deploy_evm_pulse_contracts.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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. "default-provider": {
  42. type: "string",
  43. desc: "Address of the default provider for the Pulse contract",
  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: Array<[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 Error(`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. console.log(
  134. `Deployment config: ${JSON.stringify(deploymentConfig, null, 2)}\n`
  135. );
  136. console.log(`Deploying pulse contracts on ${chain.getId()}...`);
  137. const executorAddr = wormholeContract.address; // Using wormhole contract as executor for Pulse
  138. const pulseAddr = await deployPulseContracts(
  139. chain,
  140. deploymentConfig,
  141. executorAddr
  142. );
  143. if (deploymentConfig.saveContract) {
  144. console.log("Saving the contract in the store...");
  145. const contract = new EvmPulseContract(chain, pulseAddr);
  146. DefaultStore.pulse_contracts[contract.getId()] = contract;
  147. DefaultStore.saveAllContracts();
  148. }
  149. console.log(
  150. `✅ Deployed pulse contracts on ${chain.getId()} at ${pulseAddr}\n\n`
  151. );
  152. }
  153. main();