deploy_evm_entropy_contracts.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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. ENTROPY_DEFAULT_KEEPER,
  8. ENTROPY_DEFAULT_PROVIDER,
  9. EvmEntropyContract,
  10. getDefaultDeploymentConfig,
  11. toDeploymentType,
  12. toPrivateKey,
  13. } from "../src";
  14. import {
  15. COMMON_DEPLOY_OPTIONS,
  16. deployIfNotCached,
  17. getWeb3Contract,
  18. getOrDeployWormholeContract,
  19. BaseDeployConfig,
  20. } from "./common";
  21. import Web3 from "web3";
  22. interface DeploymentConfig extends BaseDeployConfig {
  23. type: DeploymentType;
  24. saveContract: boolean;
  25. }
  26. const CACHE_FILE = ".cache-deploy-evm-entropy-contracts";
  27. const parser = yargs(hideBin(process.argv))
  28. .scriptName("deploy_evm_entropy_contracts.ts")
  29. .usage(
  30. "Usage: $0 --std-output-dir <path/to/std-output-dir/> --private-key <private-key> --chain <chain> --wormhole-addr <wormhole-addr>"
  31. )
  32. .options({
  33. ...COMMON_DEPLOY_OPTIONS,
  34. chain: {
  35. type: "string",
  36. demandOption: true,
  37. desc: "Chain to upload the contract on. Can be one of the evm chains available in the store",
  38. },
  39. });
  40. async function deployExecutorContracts(
  41. chain: EvmChain,
  42. config: DeploymentConfig,
  43. wormholeAddr: string
  44. ): Promise<string> {
  45. const executorImplAddr = await deployIfNotCached(
  46. CACHE_FILE,
  47. chain,
  48. config,
  49. "ExecutorUpgradable",
  50. []
  51. );
  52. // Craft the init data for the proxy contract
  53. const { governanceDataSource } = getDefaultDeploymentConfig(config.type);
  54. const executorImplContract = getWeb3Contract(
  55. config.jsonOutputDir,
  56. "ExecutorUpgradable",
  57. executorImplAddr
  58. );
  59. const executorInitData = executorImplContract.methods
  60. .initialize(
  61. wormholeAddr,
  62. 0, // lastExecutedSequence,
  63. chain.getWormholeChainId(),
  64. governanceDataSource.emitterChain,
  65. `0x${governanceDataSource.emitterAddress}`
  66. )
  67. .encodeABI();
  68. return await deployIfNotCached(CACHE_FILE, chain, config, "ERC1967Proxy", [
  69. executorImplAddr,
  70. executorInitData,
  71. ]);
  72. }
  73. async function deployEntropyContracts(
  74. chain: EvmChain,
  75. config: DeploymentConfig,
  76. executorAddr: string
  77. ): Promise<string> {
  78. const entropyImplAddr = await deployIfNotCached(
  79. CACHE_FILE,
  80. chain,
  81. config,
  82. "EntropyUpgradable",
  83. []
  84. );
  85. const entropyImplContract = getWeb3Contract(
  86. config.jsonOutputDir,
  87. "EntropyUpgradable",
  88. entropyImplAddr
  89. );
  90. const entropyInitData = entropyImplContract.methods
  91. .initialize(
  92. executorAddr, // owner
  93. executorAddr, // admin
  94. 1, // pythFeeInWei
  95. chain.isMainnet()
  96. ? ENTROPY_DEFAULT_PROVIDER.mainnet
  97. : ENTROPY_DEFAULT_PROVIDER.testnet,
  98. true // prefillRequestStorage
  99. )
  100. .encodeABI();
  101. return await deployIfNotCached(
  102. CACHE_FILE,
  103. chain,
  104. config,
  105. "ERC1967Proxy",
  106. [entropyImplAddr, entropyInitData],
  107. // NOTE: we are deploying a ERC1967Proxy when deploying executor
  108. // we need to provide a different cache key. As the `artifactname`
  109. // is same in both case which means the cache key will be same
  110. `${chain.getId()}-ERC1967Proxy-ENTROPY`
  111. );
  112. }
  113. async function topupAccountsIfNecessary(
  114. chain: EvmChain,
  115. deploymentConfig: DeploymentConfig
  116. ) {
  117. for (const [accountName, defaultAddresses] of [
  118. ["keeper", ENTROPY_DEFAULT_KEEPER],
  119. ["provider", ENTROPY_DEFAULT_PROVIDER],
  120. ] as const) {
  121. const accountAddress = chain.isMainnet()
  122. ? defaultAddresses.mainnet
  123. : defaultAddresses.testnet;
  124. const web3 = chain.getWeb3();
  125. const balance = Number(
  126. web3.utils.fromWei(await web3.eth.getBalance(accountAddress), "ether")
  127. );
  128. const MIN_BALANCE = 0.01;
  129. console.log(`${accountName} balance: ${balance} ETH`);
  130. if (balance < MIN_BALANCE) {
  131. console.log(
  132. `Balance is less than ${MIN_BALANCE}. Topping up the ${accountName} address...`
  133. );
  134. const signer = web3.eth.accounts.privateKeyToAccount(
  135. deploymentConfig.privateKey
  136. );
  137. web3.eth.accounts.wallet.add(signer);
  138. const estimatedGas = await web3.eth.estimateGas({
  139. from: signer.address,
  140. to: accountAddress,
  141. value: web3.utils.toWei(`${MIN_BALANCE}`, "ether"),
  142. });
  143. const tx = await web3.eth.sendTransaction({
  144. from: signer.address,
  145. to: accountAddress,
  146. gas: estimatedGas * deploymentConfig.gasMultiplier,
  147. value: web3.utils.toWei(`${MIN_BALANCE}`, "ether"),
  148. });
  149. console.log(
  150. `Topped up the ${accountName} address. Tx: `,
  151. tx.transactionHash
  152. );
  153. }
  154. }
  155. }
  156. async function main() {
  157. const argv = await parser.argv;
  158. const chainName = argv.chain;
  159. const chain = DefaultStore.chains[chainName];
  160. if (!chain) {
  161. throw new Error(`Chain ${chainName} not found`);
  162. } else if (!(chain instanceof EvmChain)) {
  163. throw new Error(`Chain ${chainName} is not an EVM chain`);
  164. }
  165. const deploymentConfig: DeploymentConfig = {
  166. type: toDeploymentType(argv.deploymentType),
  167. gasMultiplier: argv.gasMultiplier,
  168. gasPriceMultiplier: argv.gasPriceMultiplier,
  169. privateKey: toPrivateKey(argv.privateKey),
  170. jsonOutputDir: argv.stdOutputDir,
  171. saveContract: argv.saveContract,
  172. };
  173. const wormholeContract = await getOrDeployWormholeContract(
  174. chain,
  175. deploymentConfig,
  176. CACHE_FILE
  177. );
  178. await topupAccountsIfNecessary(chain, deploymentConfig);
  179. console.log(
  180. `Deployment config: ${JSON.stringify(deploymentConfig, null, 2)}\n`
  181. );
  182. console.log(`Deploying entropy contracts on ${chain.getId()}...`);
  183. const executorAddr = await deployExecutorContracts(
  184. chain,
  185. deploymentConfig,
  186. wormholeContract.address
  187. );
  188. const entropyAddr = await deployEntropyContracts(
  189. chain,
  190. deploymentConfig,
  191. executorAddr
  192. );
  193. if (deploymentConfig.saveContract) {
  194. console.log("Saving the contract in the store...");
  195. const contract = new EvmEntropyContract(chain, entropyAddr);
  196. DefaultStore.entropy_contracts[contract.getId()] = contract;
  197. DefaultStore.saveAllContracts();
  198. }
  199. console.log(
  200. `✅ Deployed entropy contracts on ${chain.getId()} at ${entropyAddr}\n\n`
  201. );
  202. }
  203. main();