deploy_evm_pulse_contracts.ts 4.7 KB

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