deploy_evm_pulse_contracts.ts 4.9 KB

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