import { getSigningOsmosisClient, cosmwasm, estimateOsmoFee } from "osmojs"; import { getOfflineSignerProto as getOfflineSigner } from "cosmjs-utils"; import { chains } from "chain-registry"; import { Chain } from "@chain-registry/types"; import { readFileSync } from "fs"; import { DeliverTxResponse, calculateFee } from "@cosmjs/stargate"; import { wasmTypes } from "@cosmjs/cosmwasm-stargate/build/modules/wasm/messages"; import assert from "assert"; import { ContractInfo, Deployer } from "."; import { convert_terra_address_to_hex, extractFromRawLog } from "./terra"; import { EncodeObject } from "@cosmjs/proto-signing"; import Long from "long"; export type OsmosisHost = { endpoint: string; }; export class OsmosisDeployer implements Deployer { private chain: Chain; constructor(private endpoint: string, private mnemonic: string) { const c = chains.find(({ chain_name }) => chain_name === "osmosis"); if (c === undefined) throw new Error("Could not find Osmosis in chain registry"); this.chain = c; } private async getAccountAddress(): Promise { const signer = await getOfflineSigner({ mnemonic: this.mnemonic, chain: this.chain, }); const accountData = await signer.getAccounts(); return accountData[0].address; } private async signAndBroadcast( msg: EncodeObject ): Promise { const signer = await getOfflineSigner({ mnemonic: this.mnemonic, chain: this.chain, }); const client = await getSigningOsmosisClient({ rpcEndpoint: this.endpoint, signer, defaultTypes: wasmTypes, }); const address = await this.getAccountAddress(); const { gas } = await estimateOsmoFee( client, address, [msg], "estimate fee" ); // libraries output more gas than simulated by multiplying with a constant // osmojs multiplies by 1.3 // which seems to be not enough // hence again multiplying by 1.3 const fee = calculateFee( parseInt((parseInt(gas) * 1.3).toFixed()), "0.025uosmo" ); const rs = await client.signAndBroadcast(address, [msg], fee); if (rs.code !== 0) { console.error(`Transaction failed: ${rs.rawLog}`); } else { console.log( `Broadcasted transaction hash: ${JSON.stringify(rs.transactionHash)}` ); } client.disconnect(); return rs; } async deployArtifact(artifact: string): Promise { const contract_bytes = readFileSync(artifact); console.log(`Storing WASM: ${artifact} (${contract_bytes.length} bytes)`); const storeCode = cosmwasm.wasm.v1.MessageComposer.withTypeUrl.storeCode({ sender: await this.getAccountAddress(), wasmByteCode: contract_bytes, }); const rs = await this.signAndBroadcast(storeCode); if (rs.rawLog === undefined) throw new Error("error parsing raw logs: rawLog undefined"); var codeId: number; try { // {"key":"code_id","value":"14"} const ci = extractFromRawLog(rs.rawLog, "code_id"); codeId = parseInt(ci); } catch (e) { console.error( "Encountered an error in parsing deploy code result. Printing raw log" ); console.error(rs.rawLog); throw e; } return codeId; } async instantiate( codeId: number, inst_msg: string | object, label: string ): Promise { const accAddress = await this.getAccountAddress(); const instMsg = cosmwasm.wasm.v1.MessageComposer.withTypeUrl.instantiateContract({ sender: accAddress, admin: accAddress, codeId: Long.fromNumber(codeId), label, msg: Buffer.from(JSON.stringify(inst_msg)), funds: [], }); const rs = await this.signAndBroadcast(instMsg); if (rs.rawLog === undefined) throw new Error("error parsing raw logs: rawLog undefined"); var address: string = ""; try { // {"key":"_contract_address","value":"terra1xxx3ps3gm3wceg4g300hvggdv7ga0hmsk64srccffmfy4wvcrugqnlvt8w"} address = extractFromRawLog(rs.rawLog, "_contract_address"); } catch (e) { console.error( "Encountered an error in parsing instantiation result. Printing raw log" ); console.error(rs.rawLog); throw e; } console.log( `Instantiated ${label} at ${address} (${convert_terra_address_to_hex( address )})` ); return address; } async migrate(contract: string, codeId: number): Promise { const migrateMsg = cosmwasm.wasm.v1.MessageComposer.withTypeUrl.migrateContract({ sender: await this.getAccountAddress(), contract, codeId: Long.fromNumber(codeId), msg: Buffer.from( JSON.stringify({ action: "", }) ), }); const rs = await this.signAndBroadcast(migrateMsg); if (rs.rawLog === undefined) throw new Error("error parsing raw logs: rawLog undefined"); try { // {"key":"code_id","value":"13"} let resultCodeId = parseInt(extractFromRawLog(rs.rawLog, "code_id")); assert.strictEqual(codeId, resultCodeId); } catch (e) { console.error( "Encountered an error in parsing migration result. Printing raw log" ); console.error(rs.rawLog); throw e; } } async updateAdmin(newAdmin: string, contract: string): Promise { const currAdmin = await this.getAccountAddress(); const updateAdminMsg = cosmwasm.wasm.v1.MessageComposer.withTypeUrl.updateAdmin({ sender: currAdmin, newAdmin, contract, }); const rs = await this.signAndBroadcast(updateAdminMsg); if (rs.rawLog === undefined) throw new Error("error parsing raw logs: rawLog undefined"); } async getContractInfo(contract: string): Promise { const { createRPCQueryClient } = cosmwasm.ClientFactory; const client = await createRPCQueryClient({ rpcEndpoint: this.endpoint }); const { address, contractInfo } = await client.cosmwasm.wasm.v1.contractInfo({ address: contract }); if (contractInfo === undefined) throw new Error("error fetching contract info"); const { codeId, creator, admin } = contractInfo; return { codeId: codeId.toNumber(), address: address, creator: creator, admin: admin, initMsg: undefined, }; } static fromHostAndMnemonic(host: OsmosisHost, mnemonic: string) { return new OsmosisDeployer(host.endpoint, mnemonic); } }