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

221 lines
6.4 KiB
TypeScript

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<string> {
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<DeliverTxResponse> {
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<number> {
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<string> {
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<void> {
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<void> {
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<ContractInfo> {
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);
}
}