deploy.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import { LCDClient, MnemonicKey } from "@terra-money/terra.js";
  2. import {
  3. StdFee,
  4. MsgInstantiateContract,
  5. MsgExecuteContract,
  6. MsgStoreCode,
  7. } from "@terra-money/terra.js";
  8. import { readFileSync, readdirSync } from "fs";
  9. import { Bech32, toHex } from "@cosmjs/encoding";
  10. import { zeroPad } from "ethers/lib/utils.js";
  11. /*
  12. NOTE: Only append to this array: keeping the ordering is crucial, as the
  13. contracts must be imported in a deterministic order so their addresses remain
  14. deterministic.
  15. */
  16. const artifacts = [
  17. "wormhole.wasm",
  18. "cw20_wrapped.wasm",
  19. "cw20_base.wasm",
  20. "pyth_bridge.wasm",
  21. ];
  22. /* Check that the artifact folder contains all the wasm files we expect and nothing else */
  23. const actual_artifacts = readdirSync("../artifacts/").filter((a) =>
  24. a.endsWith(".wasm")
  25. );
  26. const missing_artifacts = artifacts.filter(
  27. (a) => !actual_artifacts.includes(a)
  28. );
  29. if (missing_artifacts.length) {
  30. console.log(
  31. "Error during terra deployment. The following files are expected to be in the artifacts folder:"
  32. );
  33. missing_artifacts.forEach((file) => console.log(` - ${file}`));
  34. console.log(
  35. "Hint: the deploy script needs to run after the contracts have been built."
  36. );
  37. console.log(
  38. "External binary blobs need to be manually added in tools/Dockerfile."
  39. );
  40. process.exit(1);
  41. }
  42. const unexpected_artifacts = actual_artifacts.filter(
  43. (a) => !artifacts.includes(a)
  44. );
  45. if (unexpected_artifacts.length) {
  46. console.log(
  47. "Error during terra deployment. The following files are not expected to be in the artifacts folder:"
  48. );
  49. unexpected_artifacts.forEach((file) => console.log(` - ${file}`));
  50. console.log("Hint: you might need to modify tools/deploy.js");
  51. process.exit(1);
  52. }
  53. /* Set up terra client & wallet */
  54. const terra = new LCDClient({
  55. URL: "http://localhost:1317",
  56. chainID: "localterra",
  57. });
  58. const wallet = terra.wallet(
  59. new MnemonicKey({
  60. mnemonic:
  61. "notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius",
  62. })
  63. );
  64. await wallet.sequence();
  65. /* Deploy artifacts */
  66. const codeIds = {};
  67. for (const file of artifacts) {
  68. const contract_bytes = readFileSync(`../artifacts/${file}`);
  69. console.log(`Storing WASM: ${file} (${contract_bytes.length} bytes)`);
  70. const store_code = new MsgStoreCode(
  71. wallet.key.accAddress,
  72. contract_bytes.toString("base64")
  73. );
  74. try {
  75. const tx = await wallet.createAndSignTx({
  76. msgs: [store_code],
  77. memo: "",
  78. });
  79. const rs = await terra.tx.broadcast(tx);
  80. const ci = /"code_id","value":"([^"]+)/gm.exec(rs.raw_log)[1];
  81. codeIds[file] = parseInt(ci);
  82. } catch (e) {
  83. console.log(`${e}`);
  84. }
  85. }
  86. console.log(codeIds);
  87. /* Instantiate contracts.
  88. *
  89. * We instantiate the core contracts here (i.e. wormhole itself and the bridge contracts).
  90. * The wrapped asset contracts don't need to be instantiated here, because those
  91. * will be instantiated by the on-chain bridge contracts on demand.
  92. * */
  93. // Governance constants defined by the Wormhole spec.
  94. const govChain = 1;
  95. const govAddress =
  96. "0000000000000000000000000000000000000000000000000000000000000004";
  97. async function instantiate(contract, inst_msg) {
  98. var address;
  99. await wallet
  100. .createAndSignTx({
  101. msgs: [
  102. new MsgInstantiateContract(
  103. wallet.key.accAddress,
  104. wallet.key.accAddress,
  105. codeIds[contract],
  106. inst_msg
  107. ),
  108. ],
  109. memo: "",
  110. })
  111. .then((tx) => terra.tx.broadcast(tx))
  112. .then((rs) => {
  113. address = /"contract_address","value":"([^"]+)/gm.exec(rs.raw_log)[1];
  114. });
  115. console.log(`Instantiated ${contract} at ${address} (${convert_terra_address_to_hex(address)})`);
  116. return address;
  117. }
  118. // Instantiate contracts. NOTE: Only append at the end, the ordering must be
  119. // deterministic for the addresses to work
  120. const addresses = {};
  121. addresses["wormhole.wasm"] = await instantiate("wormhole.wasm", {
  122. gov_chain: govChain,
  123. gov_address: Buffer.from(govAddress, "hex").toString("base64"),
  124. guardian_set_expirity: 86400,
  125. initial_guardian_set: {
  126. addresses: [
  127. {
  128. bytes: Buffer.from(
  129. "beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe",
  130. "hex"
  131. ).toString("base64"),
  132. },
  133. ],
  134. expiration_time: 0,
  135. },
  136. });
  137. const pythEmitterAddress =
  138. "71f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b";
  139. const pythChain = 1;
  140. addresses["pyth_bridge.wasm"] = await instantiate("pyth_bridge.wasm", {
  141. wormhole_contract: addresses["wormhole.wasm"],
  142. pyth_emitter: Buffer.from(pythEmitterAddress, "hex").toString(
  143. "base64"
  144. ),
  145. pyth_emitter_chain: pythChain,
  146. });
  147. // Terra addresses are "human-readable", but for cross-chain registrations, we
  148. // want the "canonical" version
  149. function convert_terra_address_to_hex(human_addr) {
  150. return "0x" + toHex(zeroPad(Bech32.decode(human_addr).data, 32));
  151. }