pyth-crosschain/target-chains/cosmwasm/tools/src/deployer/terra.ts

161 lines
4.0 KiB
TypeScript

import {
LCDClient,
MnemonicKey,
Msg,
MsgInstantiateContract,
MsgMigrateContract,
MsgStoreCode,
WaitTxBroadcastResult,
Wallet,
isTxError,
} from "@terra-money/terra.js";
import { readFileSync } from "fs";
import { Bech32, toHex } from "@cosmjs/encoding";
import { zeroPad } from "ethers/lib/utils.js";
import assert from "assert";
import { Deployer } from ".";
export type TerraHost = {
URL: string;
chainID: string;
name: string;
};
export class TerraDeployer implements Deployer {
wallet: Wallet;
feeDenoms: [string];
constructor(wallet: Wallet) {
this.wallet = wallet;
this.feeDenoms = ["uluna"];
}
private async signAndBroadcastMsg(msg: Msg): Promise<WaitTxBroadcastResult> {
const tx = await this.wallet.createAndSignTx({
msgs: [msg],
feeDenoms: this.feeDenoms,
});
const res = await this.wallet.lcd.tx.broadcast(tx);
if (isTxError(res)) {
console.error(`Transaction failed: ${res.raw_log}`);
} else {
console.log(
`Broadcasted transaction hash: ${JSON.stringify(res.txhash)}`
);
}
return res;
}
async deployArtifact(artifact: string): Promise<number> {
const contract_bytes = readFileSync(artifact);
console.log(`Storing WASM: ${artifact} (${contract_bytes.length} bytes)`);
const store_code = new MsgStoreCode(
this.wallet.key.accAddress,
contract_bytes.toString("base64")
);
const rs = await this.signAndBroadcastMsg(store_code);
var codeId: number;
try {
// {"key":"code_id","value":"14"}
const ci = extractFromRawLog(rs.raw_log, "code_id");
codeId = parseInt(ci);
} catch (e) {
console.error(
"Encountered an error in parsing deploy code result. Printing raw log"
);
console.error(rs.raw_log);
throw e;
}
return codeId;
}
async instantiate(
codeId: number,
inst_msg: string | object,
label: string
): Promise<string> {
const instMsg = new MsgInstantiateContract(
this.wallet.key.accAddress,
this.wallet.key.accAddress,
codeId,
inst_msg,
undefined,
label
);
const rs = await this.signAndBroadcastMsg(instMsg);
var address: string = "";
try {
// {"key":"_contract_address","value":"terra1xxx3ps3gm3wceg4g300hvggdv7ga0hmsk64srccffmfy4wvcrugqnlvt8w"}
address = extractFromRawLog(rs.raw_log, "_contract_address");
} catch (e) {
console.error(
"Encountered an error in parsing instantiation result. Printing raw log"
);
console.error(rs.raw_log);
throw e;
}
console.log(
`Instantiated ${label} at ${address} (${convert_terra_address_to_hex(
address
)})`
);
return address;
}
async migrate(contract: string, codeId: number): Promise<void> {
const migrateMsg = new MsgMigrateContract(
this.wallet.key.accAddress,
contract,
codeId,
{
action: "",
}
);
const rs = await this.signAndBroadcastMsg(migrateMsg);
try {
// {"key":"code_id","value":"13"}
let resultCodeId = parseInt(extractFromRawLog(rs.raw_log, "code_id"));
assert.strictEqual(codeId, resultCodeId);
} catch (e) {
console.error(
"Encountered an error in parsing migration result. Printing raw log"
);
console.error(rs.raw_log);
throw e;
}
}
static fromHostAndMnemonic(host: TerraHost, mnemonic: string) {
const lcd = new LCDClient(host);
const wallet = lcd.wallet(
new MnemonicKey({
mnemonic,
})
);
return new TerraDeployer(wallet);
}
}
// Terra addresses are "human-readable", but for cross-chain registrations, we
// want the "canonical" version
function convert_terra_address_to_hex(human_addr: string) {
return "0x" + toHex(zeroPad(Bech32.decode(human_addr).data, 32));
}
// enter key of what to extract
function extractFromRawLog(rawLog: string, key: string): string {
const rx = new RegExp(`"${key}","value":"([^"]+)`, "gm");
return rx.exec(rawLog)![1];
}