deploy_evm_pulse_contracts.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /* eslint-disable @typescript-eslint/no-unsafe-call */
  2. /* eslint-disable @typescript-eslint/no-floating-promises */
  3. /* eslint-disable @typescript-eslint/no-unsafe-member-access */
  4. /* eslint-disable @typescript-eslint/no-unsafe-assignment */
  5. /* eslint-disable unicorn/prefer-top-level-await */
  6. /* eslint-disable no-console */
  7. import fs from "node:fs";
  8. import path from "node:path";
  9. import yargs from "yargs";
  10. import { hideBin } from "yargs/helpers";
  11. import type { BaseDeployConfig, DefaultAddresses } from "./common";
  12. import {
  13. COMMON_DEPLOY_OPTIONS,
  14. deployIfNotCached,
  15. getWeb3Contract,
  16. getOrDeployWormholeContract,
  17. topupAccountsIfNecessary,
  18. } from "./common";
  19. import type { DeploymentType } from "../src/core/base";
  20. import { toDeploymentType, toPrivateKey } from "../src/core/base";
  21. import { EvmChain } from "../src/core/chains";
  22. import {
  23. PULSE_DEFAULT_PROVIDER,
  24. PULSE_DEFAULT_KEEPER,
  25. EvmPulseContract,
  26. } from "../src/core/contracts";
  27. import { DefaultStore } from "../src/node/utils/store";
  28. type DeploymentConfig = {
  29. type: DeploymentType;
  30. saveContract: boolean;
  31. } & BaseDeployConfig;
  32. const CACHE_FILE = ".cache-deploy-evm-pulse-contracts";
  33. const parser = yargs(hideBin(process.argv))
  34. .scriptName("deploy_evm_pulse_contracts.ts")
  35. .usage(
  36. "Usage: $0 --std-output-dir <path/to/std-output-dir/> --private-key <private-key> --chain <chain> --default-provider <default-provider> --wormhole-addr <wormhole-addr>",
  37. )
  38. .options({
  39. ...COMMON_DEPLOY_OPTIONS,
  40. chain: {
  41. type: "string",
  42. demandOption: true,
  43. desc: "Chain to upload the contract on. Can be one of the evm chains available in the store",
  44. },
  45. });
  46. async function deployPulseContracts(
  47. chain: EvmChain,
  48. config: DeploymentConfig,
  49. executorAddr: string,
  50. ): Promise<string> {
  51. console.log("Deploying PulseUpgradeable on", chain.getId(), "...");
  52. // Get the artifact and ensure bytecode is properly formatted
  53. const pulseArtifact = JSON.parse(
  54. fs.readFileSync(
  55. path.join(config.jsonOutputDir, "PulseUpgradeable.json"),
  56. "utf8",
  57. ),
  58. );
  59. console.log("PulseArtifact bytecode type:", typeof pulseArtifact.bytecode);
  60. const pulseImplAddr = await deployIfNotCached(
  61. CACHE_FILE,
  62. chain,
  63. config,
  64. "PulseUpgradeable",
  65. [],
  66. );
  67. console.log("PulseUpgradeable implementation deployed at:", pulseImplAddr);
  68. const pulseImplContract = getWeb3Contract(
  69. config.jsonOutputDir,
  70. "PulseUpgradeable",
  71. pulseImplAddr,
  72. );
  73. console.log("Preparing initialization data...");
  74. const pulseInitData = pulseImplContract.methods
  75. .initialize(
  76. executorAddr, // owner
  77. executorAddr, // admin
  78. "1", // pythFeeInWei
  79. executorAddr, // pythAddress - using executor as a placeholder
  80. chain.isMainnet()
  81. ? PULSE_DEFAULT_PROVIDER.mainnet
  82. : PULSE_DEFAULT_PROVIDER.testnet,
  83. true, // prefillRequestStorage
  84. 3600, // exclusivityPeriodSeconds - 1 hour
  85. )
  86. .encodeABI();
  87. console.log("Deploying ERC1967Proxy for Pulse...");
  88. return await deployIfNotCached(
  89. CACHE_FILE,
  90. chain,
  91. config,
  92. "ERC1967Proxy",
  93. [pulseImplAddr, pulseInitData],
  94. // NOTE: we are deploying a ERC1967Proxy when deploying executor
  95. // we need to provide a different cache key. As the `artifactname`
  96. // is same in both case which means the cache key will be same
  97. `${chain.getId()}-ERC1967Proxy-PULSE1`,
  98. );
  99. }
  100. async function topupPulseAccountsIfNecessary(
  101. chain: EvmChain,
  102. deploymentConfig: DeploymentConfig,
  103. ) {
  104. const accounts: [string, DefaultAddresses][] = [
  105. ["keeper", PULSE_DEFAULT_KEEPER],
  106. ["provider", PULSE_DEFAULT_PROVIDER],
  107. ];
  108. await topupAccountsIfNecessary(chain, deploymentConfig, accounts);
  109. }
  110. async function main() {
  111. const argv = await parser.argv;
  112. const chainName = argv.chain;
  113. const chain = DefaultStore.chains[chainName];
  114. if (!chain) {
  115. throw new Error(`Chain ${chainName} not found`);
  116. } else if (!(chain instanceof EvmChain)) {
  117. throw new TypeError(`Chain ${chainName} is not an EVM chain`);
  118. }
  119. const deploymentConfig: DeploymentConfig = {
  120. type: toDeploymentType(argv.deploymentType),
  121. gasMultiplier: argv.gasMultiplier,
  122. gasPriceMultiplier: argv.gasPriceMultiplier,
  123. privateKey: toPrivateKey(argv.privateKey),
  124. jsonOutputDir: argv.stdOutputDir,
  125. saveContract: argv.saveContract,
  126. };
  127. const wormholeContract = await getOrDeployWormholeContract(
  128. chain,
  129. deploymentConfig,
  130. CACHE_FILE,
  131. );
  132. await topupPulseAccountsIfNecessary(chain, deploymentConfig);
  133. const maskedDeploymentConfig = {
  134. ...deploymentConfig,
  135. privateKey: deploymentConfig.privateKey ? `<REDACTED>` : undefined,
  136. };
  137. console.log(
  138. `Deployment config: ${JSON.stringify(maskedDeploymentConfig, undefined, 2)}\n`,
  139. );
  140. console.log(`Deploying pulse contracts on ${chain.getId()}...`);
  141. const executorAddr = wormholeContract.address; // Using wormhole contract as executor for Pulse
  142. const pulseAddr = await deployPulseContracts(
  143. chain,
  144. deploymentConfig,
  145. executorAddr,
  146. );
  147. if (deploymentConfig.saveContract) {
  148. console.log("Saving the contract in the store...");
  149. const contract = new EvmPulseContract(chain, pulseAddr);
  150. DefaultStore.pulse_contracts[contract.getId()] = contract;
  151. DefaultStore.saveAllContracts();
  152. }
  153. console.log(
  154. `✅ Deployed pulse contracts on ${chain.getId()} at ${pulseAddr}\n\n`,
  155. );
  156. }
  157. main();