upgrade_evm_contracts.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import yargs from "yargs";
  2. import { hideBin } from "yargs/helpers";
  3. import { DefaultStore, EvmChain, loadHotWallet, toPrivateKey } from "../src";
  4. import { existsSync, readFileSync, writeFileSync } from "fs";
  5. const CACHE_FILE = ".cache-upgrade-evm";
  6. const parser = yargs(hideBin(process.argv))
  7. .usage(
  8. "Deploys a new PythUpgradable contract to a set of chains and creates a governance proposal for it.\n" +
  9. `Uses a cache file (${CACHE_FILE}) to avoid deploying contracts twice\n` +
  10. "Usage: $0 --chain <chain_1> --chain <chain_2> --private-key <private_key> --ops-key-path <ops_key_path> --std-output <std_output>"
  11. )
  12. .options({
  13. testnet: {
  14. type: "boolean",
  15. default: false,
  16. desc: "Upgrade testnet contracts instead of mainnet",
  17. },
  18. "all-chains": {
  19. type: "boolean",
  20. default: false,
  21. desc: "Upgrade the contract on all chains. Use with --testnet flag to upgrade all testnet contracts",
  22. },
  23. chain: {
  24. type: "array",
  25. string: true,
  26. desc: "Chains to upgrade the contract on",
  27. },
  28. "private-key": {
  29. type: "string",
  30. demandOption: true,
  31. desc: "Private key to use for the deployment",
  32. },
  33. "ops-key-path": {
  34. type: "string",
  35. demandOption: true,
  36. desc: "Path to the private key of the proposer to use for the operations multisig governance proposal",
  37. },
  38. "std-output": {
  39. type: "string",
  40. demandOption: true,
  41. desc: "Path to the standard JSON output of the pyth contract (build artifact)",
  42. },
  43. });
  44. async function run_if_not_cached(
  45. cache_key: string,
  46. fn: () => Promise<string>
  47. ): Promise<string> {
  48. const cache = existsSync(CACHE_FILE)
  49. ? JSON.parse(readFileSync(CACHE_FILE, "utf8"))
  50. : {};
  51. if (cache[cache_key]) {
  52. return cache[cache_key];
  53. }
  54. const result = await fn();
  55. cache[cache_key] = result;
  56. writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
  57. return result;
  58. }
  59. async function main() {
  60. const argv = await parser.argv;
  61. const selectedChains: EvmChain[] = [];
  62. if (argv.allChains && argv.chain)
  63. throw new Error("Cannot use both --all-chains and --chain");
  64. if (!argv.allChains && !argv.chain)
  65. throw new Error("Must use either --all-chains or --chain");
  66. for (const chain of Object.values(DefaultStore.chains)) {
  67. if (!(chain instanceof EvmChain)) continue;
  68. if (
  69. (argv.allChains && chain.isMainnet() !== argv.testnet) ||
  70. argv.chain?.includes(chain.getId())
  71. )
  72. selectedChains.push(chain);
  73. }
  74. if (argv.chain && selectedChains.length !== argv.chain.length)
  75. throw new Error(
  76. `Some chains were not found ${selectedChains
  77. .map((chain) => chain.getId())
  78. .toString()}`
  79. );
  80. for (const chain of selectedChains) {
  81. if (chain.isMainnet() != selectedChains[0].isMainnet())
  82. throw new Error("All chains must be either mainnet or testnet");
  83. }
  84. const vault =
  85. DefaultStore.vaults[
  86. "mainnet-beta_FVQyHcooAtThJ83XFrNnv74BcinbRH3bRmfFamAHBfuj"
  87. ];
  88. console.log("Using cache file", CACHE_FILE);
  89. console.log(
  90. "Upgrading on chains",
  91. selectedChains.map((c) => c.getId())
  92. );
  93. const payloads: Buffer[] = [];
  94. for (const chain of selectedChains) {
  95. const artifact = JSON.parse(readFileSync(argv["std-output"], "utf8"));
  96. console.log("Deploying contract to", chain.getId());
  97. const address = await run_if_not_cached(`deploy-${chain.getId()}`, () => {
  98. return chain.deploy(
  99. toPrivateKey(argv["private-key"]),
  100. artifact["abi"],
  101. artifact["bytecode"],
  102. []
  103. );
  104. });
  105. console.log(`Deployed contract at ${address} on ${chain.getId()}`);
  106. payloads.push(
  107. chain.generateGovernanceUpgradePayload(address.replace("0x", ""))
  108. );
  109. }
  110. console.log("Using vault at for proposal", vault.getId());
  111. const wallet = await loadHotWallet(argv["ops-key-path"]);
  112. console.log("Using wallet ", wallet.publicKey.toBase58());
  113. await vault.connect(wallet);
  114. const proposal = await vault.proposeWormholeMessage(payloads);
  115. console.log("Proposal address", proposal.address.toBase58());
  116. }
  117. main();