upgrade_evm_entropy_contracts.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /* eslint-disable @typescript-eslint/restrict-template-expressions */
  2. /* eslint-disable @typescript-eslint/no-unsafe-argument */
  3. /* eslint-disable @typescript-eslint/no-unsafe-member-access */
  4. /* eslint-disable @typescript-eslint/no-unsafe-assignment */
  5. /* eslint-disable no-console */
  6. import { readFileSync } from "node:fs";
  7. import type { PythCluster } from "@pythnetwork/client/lib/cluster";
  8. import yargs from "yargs";
  9. import { hideBin } from "yargs/helpers";
  10. import {
  11. COMMON_UPGRADE_OPTIONS,
  12. getSelectedChains,
  13. makeCacheFunction,
  14. } from "./common";
  15. import { toPrivateKey } from "../src/core/base";
  16. import { loadHotWallet } from "../src/node/utils/governance";
  17. import { DefaultStore } from "../src/node/utils/store";
  18. const EXECUTOR_CACHE_FILE = ".cache-upgrade-evm-executor-contract";
  19. const ENTROPY_CACHE_FILE = ".cache-upgrade-evm-entropy-contract";
  20. const parser = yargs(hideBin(process.argv))
  21. .usage(
  22. "Deploys a new Upgradeable contract for Executor or Entropy to a set of chains where Entropy is deployed and creates a governance proposal for it.\n" +
  23. `Uses a cache file to avoid deploying contracts twice\n` +
  24. "Usage: $0 --chain <chain_1> --chain <chain_2> --private-key <private_key> --ops-key-path <ops_key_path> --std-output <std_output>",
  25. )
  26. .options({
  27. ...COMMON_UPGRADE_OPTIONS,
  28. "contract-type": {
  29. type: "string",
  30. choices: ["executor", "entropy"],
  31. demandOption: true,
  32. },
  33. });
  34. // Override these URLs to use a different RPC node for mainnet / testnet.
  35. // TODO: extract these RPCs to a config file (?)
  36. const RPCS = {
  37. "mainnet-beta": "https://api.mainnet-beta.solana.com",
  38. testnet: "https://api.testnet.solana.com",
  39. devnet: "https://api.devnet.solana.com",
  40. } as Record<PythCluster, string>;
  41. function registry(cluster: PythCluster): string {
  42. return RPCS[cluster];
  43. }
  44. async function main() {
  45. const argv = await parser.argv;
  46. const cacheFile =
  47. argv["contract-type"] === "executor"
  48. ? EXECUTOR_CACHE_FILE
  49. : ENTROPY_CACHE_FILE;
  50. const runIfNotCached = makeCacheFunction(cacheFile);
  51. const selectedChains = getSelectedChains(argv);
  52. const vault =
  53. DefaultStore.vaults[
  54. "mainnet-beta_FVQyHcooAtThJ83XFrNnv74BcinbRH3bRmfFamAHBfuj"
  55. ];
  56. console.log("Using cache file", cacheFile);
  57. // Try to deploy on every chain, then collect any failures at the end. This logic makes it simpler to
  58. // identify deployment problems (e.g., not enough gas) on every chain where they occur.
  59. const payloads: Buffer[] = [];
  60. const failures: string[] = [];
  61. for (const contract of Object.values(DefaultStore.entropy_contracts)) {
  62. if (selectedChains.includes(contract.chain)) {
  63. const artifact = JSON.parse(readFileSync(argv["std-output"], "utf8"));
  64. console.log("Deploying contract to", contract.chain.getId());
  65. try {
  66. const address = await runIfNotCached(
  67. `deploy-${contract.chain.getId()}`,
  68. () => {
  69. return contract.chain.deploy(
  70. toPrivateKey(argv["private-key"]),
  71. artifact.abi,
  72. artifact.bytecode.object, // As per the artifacts generated by forge, bytecode is an object with an 'object' property
  73. [],
  74. 2,
  75. );
  76. },
  77. );
  78. console.log(
  79. `Deployed contract at ${address} on ${contract.chain.getId()}`,
  80. );
  81. const payload =
  82. argv["contract-type"] === "executor"
  83. ? await contract.generateUpgradeExecutorContractsPayload(address)
  84. : await contract.generateUpgradeEntropyContractPayload(address);
  85. console.log(payload.toString("hex"));
  86. payloads.push(payload);
  87. } catch (error) {
  88. console.log(`error deploying: ${error}`);
  89. failures.push(contract.chain.getId());
  90. }
  91. }
  92. }
  93. if (failures.length > 0) {
  94. throw new Error(
  95. `Some chains could not be deployed: ${failures.join(
  96. ", ",
  97. )}. Scroll up to see the errors from each chain.`,
  98. );
  99. }
  100. console.log("Using vault at for proposal", vault?.getId());
  101. const wallet = await loadHotWallet(argv["ops-key-path"]);
  102. console.log("Using wallet", wallet.publicKey.toBase58());
  103. vault?.connect(wallet, registry);
  104. const proposal = await vault?.proposeWormholeMessage(payloads);
  105. console.log("Proposal address", proposal?.address.toBase58());
  106. }
  107. // eslint-disable-next-line @typescript-eslint/no-floating-promises, unicorn/prefer-top-level-await
  108. main();