deploy_evm_pricefeed_contracts.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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. } from "./common";
  10. import { HermesClient } from "@pythnetwork/hermes-client";
  11. import {
  12. DeploymentType,
  13. getDefaultDeploymentConfig,
  14. toDeploymentType,
  15. toPrivateKey,
  16. } from "../src/core/base";
  17. import { EvmChain } from "../src/core/chains";
  18. import { EvmPriceFeedContract } from "../src/core/contracts";
  19. import { DefaultStore } from "../src/node/utils/store";
  20. interface DeploymentConfig extends BaseDeployConfig {
  21. type: DeploymentType;
  22. validTimePeriodSeconds: number;
  23. singleUpdateFeeInWei: number;
  24. saveContract: boolean;
  25. }
  26. const CACHE_FILE = ".cache-deploy-evm";
  27. const parser = yargs(hideBin(process.argv))
  28. .scriptName("deploy_evm_pricefeed_contracts.ts")
  29. .usage(
  30. "Usage: $0 --std-output-dir <path/to/std-output-dir/> --private-key <private-key> --chain <chain0> --chain <chain1>",
  31. )
  32. .options({
  33. ...COMMON_DEPLOY_OPTIONS,
  34. "valid-time-period-seconds": {
  35. type: "number",
  36. demandOption: false,
  37. default: 60,
  38. desc: "Valid time period in seconds for the price feed staleness",
  39. },
  40. "single-update-fee-in-wei": {
  41. type: "number",
  42. demandOption: false,
  43. default: 1,
  44. desc: "Single update fee in wei for the price feed",
  45. },
  46. "single-update-fee-in-usd": {
  47. type: "number",
  48. demandOption: false,
  49. desc: "Single update fee in USD for the price feed. (This overrides the single-update-fee-in-wei option) ",
  50. },
  51. "native-token-price-feed-id": {
  52. type: "string",
  53. demandOption: false,
  54. desc: "Pyth Price Feed ID to fetch the current price of the native token (This will be used to calculate the single-update-fee-in-usd)",
  55. },
  56. "native-token-decimals": {
  57. type: "number",
  58. demandOption: false,
  59. desc: "Number of decimals of the native token",
  60. },
  61. });
  62. async function deployPriceFeedContracts(
  63. chain: EvmChain,
  64. config: DeploymentConfig,
  65. wormholeAddr: string,
  66. ): Promise<string> {
  67. const pythImplAddr = await deployIfNotCached(
  68. CACHE_FILE,
  69. chain,
  70. config,
  71. "PythUpgradable",
  72. [],
  73. );
  74. // Craft the init data for the proxy contract
  75. const { dataSources, governanceDataSource } = getDefaultDeploymentConfig(
  76. config.type,
  77. );
  78. const pythImplContract = getWeb3Contract(
  79. config.jsonOutputDir,
  80. "PythUpgradable",
  81. pythImplAddr,
  82. );
  83. const pythInitData = pythImplContract.methods
  84. .initialize(
  85. wormholeAddr,
  86. dataSources.map((ds) => ds.emitterChain),
  87. dataSources.map((ds) => "0x" + ds.emitterAddress),
  88. governanceDataSource.emitterChain,
  89. "0x" + governanceDataSource.emitterAddress,
  90. 0, // governanceInitialSequence
  91. config.validTimePeriodSeconds,
  92. config.singleUpdateFeeInWei,
  93. )
  94. .encodeABI();
  95. return await deployIfNotCached(CACHE_FILE, chain, config, "ERC1967Proxy", [
  96. pythImplAddr,
  97. pythInitData,
  98. ]);
  99. }
  100. async function main() {
  101. const argv = await parser.argv;
  102. let singleUpdateFeeInWei = argv.singleUpdateFeeInWei;
  103. const singleUpdateFeeInUsd = argv["single-update-fee-in-usd"];
  104. const nativeTokenPriceFeedId = argv["native-token-price-feed-id"];
  105. const nativeTokenDecimals = argv["native-token-decimals"];
  106. if (
  107. singleUpdateFeeInUsd &&
  108. (nativeTokenPriceFeedId == null || nativeTokenDecimals == null)
  109. ) {
  110. throw new Error(
  111. "native-token-price-feed-id and native-token-decimals are required when single-update-fee-in-usd is provided",
  112. );
  113. }
  114. if (nativeTokenPriceFeedId && singleUpdateFeeInUsd && nativeTokenDecimals) {
  115. const hermesClient = new HermesClient("https://hermes.pyth.network");
  116. const priceObject = await hermesClient.getLatestPriceUpdates(
  117. [nativeTokenPriceFeedId],
  118. {
  119. parsed: true,
  120. },
  121. );
  122. const price = priceObject.parsed?.[0].price;
  123. if (price == null) {
  124. throw new Error("Failed to get price of the native token");
  125. }
  126. const priceInUsd = Number(price.price);
  127. const exponent = price.expo;
  128. singleUpdateFeeInWei = Math.round(
  129. Math.pow(10, nativeTokenDecimals) *
  130. (singleUpdateFeeInUsd / (priceInUsd * Math.pow(10, exponent))),
  131. );
  132. console.log(`Single update fee in wei: ${singleUpdateFeeInWei}`);
  133. }
  134. const deploymentConfig: DeploymentConfig = {
  135. type: toDeploymentType(argv.deploymentType),
  136. validTimePeriodSeconds: argv.validTimePeriodSeconds,
  137. singleUpdateFeeInWei: singleUpdateFeeInWei,
  138. gasMultiplier: argv.gasMultiplier,
  139. gasPriceMultiplier: argv.gasPriceMultiplier,
  140. privateKey: toPrivateKey(argv.privateKey),
  141. jsonOutputDir: argv.stdOutputDir,
  142. saveContract: argv.saveContract,
  143. };
  144. console.log(
  145. `Deployment config: ${JSON.stringify(deploymentConfig, null, 2)}\n`,
  146. );
  147. const chainNames = argv.chain;
  148. for (const chainName of chainNames) {
  149. const chain = DefaultStore.getChainOrThrow(chainName, EvmChain);
  150. console.log(`Deploying price feed contracts on ${chain.getId()}...`);
  151. const wormholeContract = await getOrDeployWormholeContract(
  152. chain,
  153. deploymentConfig,
  154. CACHE_FILE,
  155. );
  156. const priceFeedAddr = await deployPriceFeedContracts(
  157. chain,
  158. deploymentConfig,
  159. wormholeContract.address,
  160. );
  161. if (deploymentConfig.saveContract) {
  162. console.log("Saving the contract in the store...");
  163. const contract = new EvmPriceFeedContract(chain, priceFeedAddr);
  164. DefaultStore.contracts[contract.getId()] = contract;
  165. DefaultStore.saveAllContracts();
  166. }
  167. console.log(
  168. `✅ Deployed price feed contracts on ${chain.getId()} at ${priceFeedAddr}\n\n`,
  169. );
  170. }
  171. }
  172. main();