From 72a89b145d49e80771f6987a8e2f05cfa2c06766 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Sun, 16 Aug 2026 20:52:22 +0200 Subject: [PATCH 1/2] feat(cosmwasm): surface contract set_data on ExecuteResult ExecuteResult only exposed events, dropping the binary data a contract returns via Response::set_data (cosmos/cosmjs#1648). Add a `data: readonly Uint8Array[]` field decoded from the tx's MsgExecuteContractResponse msgResponses, one entry per instruction in order. Empty for Cosmos SDK < 0.46, which does not expose message responses. Verified the decode round-trips the data; extended the execute and executeMultiple specs to assert one data entry per instruction. Co-Authored-By: Claude Opus 4.8 --- packages/cosmwasm/src/signingcosmwasmclient.spec.ts | 4 ++++ packages/cosmwasm/src/signingcosmwasmclient.ts | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts index 1a0e07099d..e7d5629050 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts @@ -673,6 +673,8 @@ import { expect(result.height).toBeGreaterThan(0); expect(result.gasWanted).toBeGreaterThan(0); expect(result.gasUsed).toBeGreaterThan(0); + // One MsgExecuteContractResponse (and thus one `data` entry) per instruction. + expect(result.data.length).toEqual(1); const wasmEvent = result.events.find((e) => e.type === "wasm"); assert(wasmEvent, "Event of type wasm expected"); expect(wasmEvent.attributes).toContain({ key: "action", value: "release" }); @@ -794,6 +796,8 @@ import { const { events } = result; const wasmEvents = events.filter((e) => e.type == "wasm"); expect(wasmEvents.length).toEqual(2); + // One `data` entry per instruction, in order. + expect(result.data.length).toEqual(2); const [wasmEvent1, wasmEvent2] = wasmEvents; expect(wasmEvent1.type).toEqual("wasm"); expect(wasmEvent1.attributes).toContain({ key: "action", value: "release" }); diff --git a/packages/cosmwasm/src/signingcosmwasmclient.ts b/packages/cosmwasm/src/signingcosmwasmclient.ts index 4d8ec7d791..5008c4d152 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.ts @@ -44,6 +44,7 @@ import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx"; import { MsgClearAdmin, MsgExecuteContract, + MsgExecuteContractResponse, MsgInstantiateContract, MsgInstantiateContract2, MsgMigrateContract, @@ -163,6 +164,12 @@ export interface ExecuteResult { /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ readonly transactionHash: string; readonly events: readonly Event[]; + /** + * The `data` bytes each executed contract returned via `Response::set_data`, + * one entry per instruction in the same order. Empty for chains running + * Cosmos SDK < 0.46, which do not expose message responses. + */ + readonly data: readonly Uint8Array[]; readonly gasWanted: bigint; readonly gasUsed: bigint; } @@ -540,6 +547,11 @@ export class SigningCosmWasmClient extends CosmWasmClient { height: result.height, transactionHash: result.transactionHash, events: result.events, + // Each MsgExecuteContract produces a MsgExecuteContractResponse whose + // `data` field carries what the contract set via `Response::set_data`. + data: result.msgResponses.map( + (msgResponse) => MsgExecuteContractResponse.decode(msgResponse.value).data, + ), gasWanted: result.gasWanted, gasUsed: result.gasUsed, }; From ae845005d5e45bef3d8d3d22b6cc888f96a563a7 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:54:16 +0200 Subject: [PATCH 2/2] test(proto-signing): cover multisig LegacyAminoPubKey in decodePubkey decodePubkey's /cosmos.crypto.multisig.LegacyAminoPubKey branch had no test coverage. Add a case that builds a 2-of-2 multisig over the existing secp256k1 and ed25519 keys, encodes it, and checks decodePubkey reconstructs it, exercising the LegacyAminoPubKey branch on both encode and decode. closes #1344 --- packages/proto-signing/src/pubkey.spec.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/proto-signing/src/pubkey.spec.ts b/packages/proto-signing/src/pubkey.spec.ts index 8a4cab11ce..458b30ad81 100644 --- a/packages/proto-signing/src/pubkey.spec.ts +++ b/packages/proto-signing/src/pubkey.spec.ts @@ -64,6 +64,25 @@ describe("pubkey", () => { }); }); + it("works for multisig threshold pubkeys", () => { + // Build a 2-of-2 multisig over the secp256k1 and ed25519 keys above, + // encode it to protobuf, then check decodePubkey reconstructs it. This + // covers the LegacyAminoPubKey branch on both encode and decode. + const multisigPubkey = { + type: "tendermint/PubKeyMultisigThreshold", + value: { + threshold: "2", + pubkeys: [ + { type: "tendermint/PubKeySecp256k1", value: defaultPubkeyBase64 }, + { type: "tendermint/PubKeyEd25519", value: ed25519PubkeyBase64 }, + ], + }, + }; + const encoded = encodePubkey(multisigPubkey); + expect(encoded.typeUrl).toEqual("/cosmos.crypto.multisig.LegacyAminoPubKey"); + expect(decodePubkey(encoded)).toEqual(multisigPubkey); + }); + it("throws for unsupported pubkey types", () => { const pubkey = { typeUrl: "/cosmos.crypto.unknown.PubKey",