deploy.js 5.6 KB

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