diff --git a/CHANGELOG.md b/CHANGELOG.md index 90cf46dd..f0134e9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to ## [Unreleased] +### Added + +- @cosmjs/cosmwasm-stargate: Add `SigningCosmWasmClient.instantiate2` ([#1407]). + +[#1407]: https://github.com/cosmos/cosmjs/pull/1407 + ### Changed - all: upgrade cosmjs-types to 0.8.0 to include Cosmos SDK 0.46/0.47 and IBC v7 diff --git a/packages/cosmwasm-stargate/src/index.ts b/packages/cosmwasm-stargate/src/index.ts index 91c5af34..602f8115 100644 --- a/packages/cosmwasm-stargate/src/index.ts +++ b/packages/cosmwasm-stargate/src/index.ts @@ -5,6 +5,7 @@ export { createWasmAminoConverters, isMsgClearAdminEncodeObject, isMsgExecuteEncodeObject, + isMsgInstantiateContract2EncodeObject, isMsgInstantiateContractEncodeObject, isMsgMigrateEncodeObject, isMsgStoreCodeEncodeObject, @@ -12,6 +13,7 @@ export { JsonObject, MsgClearAdminEncodeObject, MsgExecuteContractEncodeObject, + MsgInstantiateContract2EncodeObject, MsgInstantiateContractEncodeObject, MsgMigrateContractEncodeObject, MsgStoreCodeEncodeObject, diff --git a/packages/cosmwasm-stargate/src/modules/index.ts b/packages/cosmwasm-stargate/src/modules/index.ts index 04cb2a79..2b3114cb 100644 --- a/packages/cosmwasm-stargate/src/modules/index.ts +++ b/packages/cosmwasm-stargate/src/modules/index.ts @@ -10,12 +10,14 @@ export { export { isMsgClearAdminEncodeObject, isMsgExecuteEncodeObject, + isMsgInstantiateContract2EncodeObject, isMsgInstantiateContractEncodeObject, isMsgMigrateEncodeObject, isMsgStoreCodeEncodeObject, isMsgUpdateAdminEncodeObject, MsgClearAdminEncodeObject, MsgExecuteContractEncodeObject, + MsgInstantiateContract2EncodeObject, MsgInstantiateContractEncodeObject, MsgMigrateContractEncodeObject, MsgStoreCodeEncodeObject, diff --git a/packages/cosmwasm-stargate/src/modules/wasm/messages.ts b/packages/cosmwasm-stargate/src/modules/wasm/messages.ts index e9a1ea02..bf3fc00c 100644 --- a/packages/cosmwasm-stargate/src/modules/wasm/messages.ts +++ b/packages/cosmwasm-stargate/src/modules/wasm/messages.ts @@ -3,6 +3,7 @@ import { MsgClearAdmin, MsgExecuteContract, MsgInstantiateContract, + MsgInstantiateContract2, MsgMigrateContract, MsgStoreCode, MsgUpdateAdmin, @@ -14,6 +15,7 @@ export const wasmTypes: ReadonlyArray<[string, GeneratedType]> = [ ["/cosmwasm.wasm.v1.MsgMigrateContract", MsgMigrateContract], ["/cosmwasm.wasm.v1.MsgStoreCode", MsgStoreCode], ["/cosmwasm.wasm.v1.MsgInstantiateContract", MsgInstantiateContract], + ["/cosmwasm.wasm.v1.MsgInstantiateContract2", MsgInstantiateContract2], ["/cosmwasm.wasm.v1.MsgUpdateAdmin", MsgUpdateAdmin], ]; @@ -39,6 +41,19 @@ export function isMsgInstantiateContractEncodeObject( ); } +export interface MsgInstantiateContract2EncodeObject extends EncodeObject { + readonly typeUrl: "/cosmwasm.wasm.v1.MsgInstantiateContract2"; + readonly value: Partial; +} + +export function isMsgInstantiateContract2EncodeObject( + object: EncodeObject, +): object is MsgInstantiateContract2EncodeObject { + return ( + (object as MsgInstantiateContract2EncodeObject).typeUrl === "/cosmwasm.wasm.v1.MsgInstantiateContract2" + ); +} + export interface MsgUpdateAdminEncodeObject extends EncodeObject { readonly typeUrl: "/cosmwasm.wasm.v1.MsgUpdateAdmin"; readonly value: Partial; diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts index 22ae12b0..11f49958 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts @@ -24,6 +24,7 @@ import Long from "long"; import pako from "pako"; import protobuf from "protobufjs/minimal"; +import { instantiate2Address } from "./instantiate2"; import { MsgExecuteContractEncodeObject, MsgStoreCodeEncodeObject } from "./modules"; import { SigningCosmWasmClient } from "./signingcosmwasmclient"; import { @@ -252,6 +253,50 @@ describe("SigningCosmWasmClient", () => { }); }); + describe("instantiate2", () => { + it("can instantiate with predictable address", async () => { + pendingWithoutWasmd(); + const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, { + prefix: wasmd.prefix, + }); + const options = { ...defaultSigningClientOptions, prefix: wasmd.prefix }; + + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); + const { codeId } = await client.upload(alice.address0, getHackatom().data, defaultUploadFee); + const funds = [coin(1234, "ucosm"), coin(321, "ustake")]; + const beneficiaryAddress = makeRandomAddress(); + const salt = Uint8Array.from([0x01]); + const wasm = getHackatom().data; + const msg = { + verifier: alice.address0, + beneficiary: beneficiaryAddress, + }; + const expectedAddress = instantiate2Address(sha256(wasm), alice.address0, salt, wasmd.prefix); + + const { contractAddress } = await client.instantiate2( + alice.address0, + codeId, + salt, + msg, + "My cool label--", + defaultInstantiateFee, + { + memo: "Let's see if the memo is used", + funds: funds, + }, + ); + + const wasmClient = await makeWasmClient(wasmd.endpoint); + const ucosmBalance = await wasmClient.bank.balance(contractAddress, "ucosm"); + const ustakeBalance = await wasmClient.bank.balance(contractAddress, "ustake"); + expect(ucosmBalance).toEqual(funds[0]); + expect(ustakeBalance).toEqual(funds[1]); + + expect(contractAddress).toEqual(expectedAddress); + client.disconnect(); + }); + }); + describe("updateAdmin", () => { it("can update an admin", async () => { pendingWithoutWasmd(); diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts index 0373ac3a..b3c15aca 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts @@ -46,6 +46,7 @@ import { MsgClearAdmin, MsgExecuteContract, MsgInstantiateContract, + MsgInstantiateContract2, MsgMigrateContract, MsgStoreCode, MsgUpdateAdmin, @@ -59,6 +60,7 @@ import { JsonObject, MsgClearAdminEncodeObject, MsgExecuteContractEncodeObject, + MsgInstantiateContract2EncodeObject, MsgInstantiateContractEncodeObject, MsgMigrateContractEncodeObject, MsgStoreCodeEncodeObject, @@ -86,7 +88,7 @@ export interface UploadResult { } /** - * The options of an .instantiate() call. + * The options of .instantiate() and .instantiate2() call. * All properties are optional. */ export interface InstantiateOptions { @@ -348,6 +350,45 @@ export class SigningCosmWasmClient extends CosmWasmClient { }; } + public async instantiate2( + senderAddress: string, + codeId: number, + salt: Uint8Array, + msg: JsonObject, + label: string, + fee: StdFee | "auto" | number, + options: InstantiateOptions = {}, + ): Promise { + const instantiateContract2Msg: MsgInstantiateContract2EncodeObject = { + typeUrl: "/cosmwasm.wasm.v1.MsgInstantiateContract2", + value: MsgInstantiateContract2.fromPartial({ + sender: senderAddress, + codeId: Long.fromString(new Uint53(codeId).toString()), + label: label, + msg: toUtf8(JSON.stringify(msg)), + funds: [...(options.funds || [])], + admin: options.admin, + salt: salt, + fixMsg: false, + }), + }; + const result = await this.signAndBroadcast(senderAddress, [instantiateContract2Msg], fee, options.memo); + if (isDeliverTxFailure(result)) { + throw new Error(createDeliverTxResponseErrorMessage(result)); + } + const parsedLogs = logs.parseRawLog(result.rawLog); + const contractAddressAttr = logs.findAttribute(parsedLogs, "instantiate", "_contract_address"); + return { + contractAddress: contractAddressAttr.value, + logs: parsedLogs, + height: result.height, + transactionHash: result.transactionHash, + events: result.events, + gasWanted: result.gasWanted, + gasUsed: result.gasUsed, + }; + } + public async updateAdmin( senderAddress: string, contractAddress: string,