| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- import "dotenv/config";
- import { LCDClient, MnemonicKey } from "@terra-money/terra.js";
- import {
- StdFee,
- MsgInstantiateContract,
- MsgExecuteContract,
- MsgStoreCode,
- } from "@terra-money/terra.js";
- import { readFileSync, readdirSync } from "fs";
- import { Bech32, toHex } from "@cosmjs/encoding";
- import { zeroPad } from "ethers/lib/utils.js";
- function sleep(ms) {
- return new Promise((resolve) => setTimeout(resolve, ms));
- }
- async function broadcastAndWait(terra, tx) {
- const response = await terra.tx.broadcast(tx);
- let currentHeight = (await terra.tendermint.blockInfo()).block.header.height;
- while (currentHeight <= response.height) {
- await sleep(100);
- currentHeight = (await terra.tendermint.blockInfo()).block.header.height;
- }
- return response;
- }
- /*
- NOTE: Only append to this array: keeping the ordering is crucial, as the
- contracts must be imported in a deterministic order so their addresses remain
- deterministic.
- */
- const artifacts = [
- "wormhole.wasm",
- "token_bridge_terra.wasm",
- "cw20_wrapped.wasm",
- "cw20_base.wasm",
- "nft_bridge.wasm",
- "cw721_wrapped.wasm",
- "cw721_base.wasm",
- "mock_bridge_integration.wasm",
- ];
- /* Check that the artifact folder contains all the wasm files we expect and nothing else */
- const actual_artifacts = readdirSync("../artifacts/").filter((a) =>
- a.endsWith(".wasm")
- );
- const missing_artifacts = artifacts.filter(
- (a) => !actual_artifacts.includes(a)
- );
- if (missing_artifacts.length) {
- console.log(
- "Error during terra deployment. The following files are expected to be in the artifacts folder:"
- );
- missing_artifacts.forEach((file) => console.log(` - ${file}`));
- console.log(
- "Hint: the deploy script needs to run after the contracts have been built."
- );
- console.log(
- "External binary blobs need to be manually added in tools/Dockerfile."
- );
- process.exit(1);
- }
- const unexpected_artifacts = actual_artifacts.filter(
- (a) => !artifacts.includes(a)
- );
- if (unexpected_artifacts.length) {
- console.log(
- "Error during terra deployment. The following files are not expected to be in the artifacts folder:"
- );
- unexpected_artifacts.forEach((file) => console.log(` - ${file}`));
- console.log("Hint: you might need to modify tools/deploy.js");
- process.exit(1);
- }
- /* Set up terra client & wallet */
- const terra = new LCDClient({
- URL: "http://localhost:1317",
- chainID: "localterra",
- });
- const wallet = terra.wallet(
- new MnemonicKey({
- mnemonic:
- "notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius",
- })
- );
- await wallet.sequence();
- /* Deploy artifacts */
- const codeIds = {};
- for (const file of artifacts) {
- const contract_bytes = readFileSync(`../artifacts/${file}`);
- console.log(`Storing WASM: ${file} (${contract_bytes.length} bytes)`);
- const store_code = new MsgStoreCode(
- wallet.key.accAddress,
- contract_bytes.toString("base64")
- );
- try {
- const tx = await wallet.createAndSignTx({
- msgs: [store_code],
- memo: "",
- });
- const rs = await broadcastAndWait(terra, tx);
- const ci = /"code_id","value":"([^"]+)/gm.exec(rs.raw_log)[1];
- codeIds[file] = parseInt(ci);
- } catch (e) {
- console.log(`${e}`);
- }
- }
- console.log(codeIds);
- /* Instantiate contracts.
- *
- * We instantiate the core contracts here (i.e. wormhole itself and the bridge contracts).
- * The wrapped asset contracts don't need to be instantiated here, because those
- * will be instantiated by the on-chain bridge contracts on demand.
- * */
- // Governance constants defined by the Wormhole spec.
- const govChain = 1;
- const govAddress =
- "0000000000000000000000000000000000000000000000000000000000000004";
- async function instantiate(contract, inst_msg) {
- var address;
- await wallet
- .createAndSignTx({
- msgs: [
- new MsgInstantiateContract(
- wallet.key.accAddress,
- wallet.key.accAddress,
- codeIds[contract],
- inst_msg
- ),
- ],
- memo: "",
- })
- .then((tx) => broadcastAndWait(terra, tx))
- .then((rs) => {
- address = /"contract_address","value":"([^"]+)/gm.exec(rs.raw_log)[1];
- });
- console.log(
- `Instantiated ${contract} at ${address} (${convert_terra_address_to_hex(
- address
- )})`
- );
- return address;
- }
- // Instantiate contracts. NOTE: Only append at the end, the ordering must be
- // deterministic for the addresses to work
- const addresses = {};
- const init_guardians = JSON.parse(process.env.INIT_SIGNERS);
- if (!init_guardians || init_guardians.length === 0) {
- throw "failed to get initial guardians from .env file.";
- }
- addresses["wormhole.wasm"] = await instantiate("wormhole.wasm", {
- gov_chain: govChain,
- gov_address: Buffer.from(govAddress, "hex").toString("base64"),
- guardian_set_expirity: 86400,
- initial_guardian_set: {
- addresses: init_guardians.map((hex) => {
- return {
- bytes: Buffer.from(hex, "hex").toString("base64"),
- };
- }),
- expiration_time: 0,
- },
- });
- addresses["token_bridge_terra.wasm"] = await instantiate(
- "token_bridge_terra.wasm",
- {
- gov_chain: govChain,
- gov_address: Buffer.from(govAddress, "hex").toString("base64"),
- wormhole_contract: addresses["wormhole.wasm"],
- wrapped_asset_code_id: codeIds["cw20_wrapped.wasm"],
- }
- );
- addresses["mock.wasm"] = await instantiate("cw20_base.wasm", {
- name: "MOCK",
- symbol: "MCK",
- decimals: 6,
- initial_balances: [
- {
- address: wallet.key.accAddress,
- amount: "100000000",
- },
- ],
- mint: null,
- });
- addresses["nft_bridge.wasm"] = await instantiate("nft_bridge.wasm", {
- gov_chain: govChain,
- gov_address: Buffer.from(govAddress, "hex").toString("base64"),
- wormhole_contract: addresses["wormhole.wasm"],
- wrapped_asset_code_id: codeIds["cw721_wrapped.wasm"],
- });
- addresses["cw721_base.wasm"] = await instantiate("cw721_base.wasm", {
- name: "MOCK",
- symbol: "MCK",
- minter: wallet.key.accAddress,
- });
- async function mint_cw721(token_id, token_uri) {
- await wallet
- .createAndSignTx({
- msgs: [
- new MsgExecuteContract(
- wallet.key.accAddress,
- addresses["cw721_base.wasm"],
- {
- mint: {
- token_id: token_id.toString(),
- owner: wallet.key.accAddress,
- token_uri: token_uri,
- },
- },
- { uluna: 1000 }
- ),
- ],
- memo: "",
- fee: new StdFee(2000000, {
- uluna: "100000",
- }),
- })
- .then((tx) => broadcastAndWait(terra, tx));
- console.log(
- `Minted NFT with token_id ${token_id} at ${addresses["cw721_base.wasm"]}`
- );
- }
- await mint_cw721(
- 0,
- "https://ixmfkhnh2o4keek2457f2v2iw47cugsx23eynlcfpstxihsay7nq.arweave.net/RdhVHafTuKIRWud-XVdItz4qGlfWyYasRXyndB5Ax9s/"
- );
- await mint_cw721(
- 1,
- "https://portal.neondistrict.io/api/getNft/158456327500392944014123206890"
- );
- /* Registrations: tell the bridge contracts to know about each other */
- const contract_registrations = {
- "token_bridge_terra.wasm": [
- // Solana
- process.env.REGISTER_SOL_TOKEN_BRIDGE_VAA,
- // Ethereum
- process.env.REGISTER_ETH_TOKEN_BRIDGE_VAA,
- // BSC
- process.env.REGISTER_BSC_TOKEN_BRIDGE_VAA,
- // ALGO
- process.env.REGISTER_ALGO_TOKEN_BRIDGE_VAA,
- // TERRA2
- process.env.REGISTER_TERRA2_TOKEN_BRIDGE_VAA,
- // NEAR
- process.env.REGISTER_NEAR_TOKEN_BRIDGE_VAA,
- // Wormhole Chain
- process.env.REGISTER_WORMCHAIN_TOKEN_BRIDGE_VAA,
- ],
- "nft_bridge.wasm": [
- // Solana
- process.env.REGISTER_SOL_NFT_BRIDGE_VAA,
- // Ethereum
- process.env.REGISTER_ETH_NFT_BRIDGE_VAA,
- ],
- };
- for (const [contract, registrations] of Object.entries(
- contract_registrations
- )) {
- console.log(`Registering chains for ${contract}:`);
- for (const registration of registrations) {
- await wallet
- .createAndSignTx({
- msgs: [
- new MsgExecuteContract(
- wallet.key.accAddress,
- addresses[contract],
- {
- submit_vaa: {
- data: Buffer.from(registration, "hex").toString("base64"),
- },
- },
- { uluna: 1000 }
- ),
- ],
- memo: "",
- fee: new StdFee(2000000, {
- uluna: "100000",
- }),
- })
- .then((tx) => broadcastAndWait(terra, tx))
- .then((rs) => console.log(rs));
- }
- }
- // Terra addresses are "human-readable", but for cross-chain registrations, we
- // want the "canonical" version
- function convert_terra_address_to_hex(human_addr) {
- return "0x" + toHex(zeroPad(Bech32.decode(human_addr).data, 32));
- }
|