221 lines
6.4 KiB
TypeScript
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);
|
|
}
|
|
}
|