From 458d4d03cd3a3b8d8942978acb2c8874fdf0354f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 8 Mar 2022 21:49:26 +0100 Subject: [PATCH 1/3] Add importWasmMessages --- .../src/modules/wasm/messages.spec.ts | 131 ++++++++++++++++++ .../src/modules/wasm/messages.ts | 58 ++++++++ 2 files changed, 189 insertions(+) create mode 100644 packages/cosmwasm-stargate/src/modules/wasm/messages.spec.ts diff --git a/packages/cosmwasm-stargate/src/modules/wasm/messages.spec.ts b/packages/cosmwasm-stargate/src/modules/wasm/messages.spec.ts new file mode 100644 index 0000000000..b7eb2540b5 --- /dev/null +++ b/packages/cosmwasm-stargate/src/modules/wasm/messages.spec.ts @@ -0,0 +1,131 @@ +import { toUtf8 } from "@cosmjs/encoding"; +import Long from "long"; + +import { importWasmMessages } from "./messages"; + +describe("messages", () => { + describe("importWasmMessages", () => { + it("works for MsgExecuteContract", () => { + // wasmd tx wasm execute wasm1hsm76p4ahyhl5yh3ve9ur49r5kemhp2r93f89d '{"contract":"specific"}' --amount 123ucosm --from wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd --chain-id test123 --offline --generate-only | jq .body.messages -c + const msgs = importWasmMessages( + `[{"@type":"/cosmwasm.wasm.v1.MsgExecuteContract","sender":"wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd","contract":"wasm1hsm76p4ahyhl5yh3ve9ur49r5kemhp2r93f89d","msg":{"contract":"specific"},"funds":[{"denom":"ucosm","amount":"123"}]}]`, + ); + expect(msgs).toEqual([ + { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: { + sender: "wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd", + contract: "wasm1hsm76p4ahyhl5yh3ve9ur49r5kemhp2r93f89d", + msg: toUtf8(`{"contract":"specific"}`), + funds: [ + { + denom: "ucosm", + amount: "123", + }, + ], + }, + }, + ]); + }); + + it("works for two MsgExecuteContract", () => { + // same message as above, but 2x (with different amounts) + const msgs = importWasmMessages( + `[{"@type":"/cosmwasm.wasm.v1.MsgExecuteContract","sender":"wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd","contract":"wasm1hsm76p4ahyhl5yh3ve9ur49r5kemhp2r93f89d","msg":{"contract":"specific"},"funds":[{"denom":"ucosm","amount":"123"}]},{"@type":"/cosmwasm.wasm.v1.MsgExecuteContract","sender":"wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd","contract":"wasm1hsm76p4ahyhl5yh3ve9ur49r5kemhp2r93f89d","msg":{"contract":"specific"},"funds":[{"denom":"ucosm","amount":"456"}]}]`, + ); + expect(msgs).toEqual([ + { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: { + sender: "wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd", + contract: "wasm1hsm76p4ahyhl5yh3ve9ur49r5kemhp2r93f89d", + msg: toUtf8(`{"contract":"specific"}`), + funds: [ + { + denom: "ucosm", + amount: "123", + }, + ], + }, + }, + { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: { + sender: "wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd", + contract: "wasm1hsm76p4ahyhl5yh3ve9ur49r5kemhp2r93f89d", + msg: toUtf8(`{"contract":"specific"}`), + funds: [ + { + denom: "ucosm", + amount: "456", + }, + ], + }, + }, + ]); + }); + + it("works for MsgInstantiateContract", () => { + // wasmd tx wasm instantiate 55 '{"contract":"specific"}' --label "unique instance" --admin wasm1hhg2rlu9jscacku2wwckws7932qqqu8xm5ca8y --amount 123ucosm --from wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd --chain-id test123 --offline --generate-only | jq .body.messages -c + const msgs = importWasmMessages( + `[{"@type":"/cosmwasm.wasm.v1.MsgInstantiateContract","sender":"wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd","admin":"wasm1hhg2rlu9jscacku2wwckws7932qqqu8xm5ca8y","code_id":"55","label":"unique instance","msg":{"contract":"specific"},"funds":[{"denom":"ucosm","amount":"123"}]}]`, + ); + expect(msgs).toEqual([ + { + typeUrl: "/cosmwasm.wasm.v1.MsgInstantiateContract", + value: { + sender: "wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd", + codeId: Long.fromNumber(55, true), + admin: "wasm1hhg2rlu9jscacku2wwckws7932qqqu8xm5ca8y", + label: "unique instance", + msg: toUtf8(`{"contract":"specific"}`), + funds: [ + { + denom: "ucosm", + amount: "123", + }, + ], + }, + }, + ]); + }); + + it("works for mixed MsgExecuteContract and MsgInstantiateContract", () => { + const msgs = importWasmMessages( + `[{"@type":"/cosmwasm.wasm.v1.MsgExecuteContract","sender":"wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd","contract":"wasm1hsm76p4ahyhl5yh3ve9ur49r5kemhp2r93f89d","msg":{"contract":"specific"},"funds":[{"denom":"ucosm","amount":"123"}]},{"@type":"/cosmwasm.wasm.v1.MsgInstantiateContract","sender":"wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd","admin":"wasm1hhg2rlu9jscacku2wwckws7932qqqu8xm5ca8y","code_id":"55","label":"unique instance","msg":{"contract":"specific"},"funds":[{"denom":"ucosm","amount":"123"}]}]`, + ); + expect(msgs).toEqual([ + { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: { + sender: "wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd", + contract: "wasm1hsm76p4ahyhl5yh3ve9ur49r5kemhp2r93f89d", + msg: toUtf8(`{"contract":"specific"}`), + funds: [ + { + denom: "ucosm", + amount: "123", + }, + ], + }, + }, + { + typeUrl: "/cosmwasm.wasm.v1.MsgInstantiateContract", + value: { + sender: "wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd", + codeId: Long.fromNumber(55, true), + admin: "wasm1hhg2rlu9jscacku2wwckws7932qqqu8xm5ca8y", + label: "unique instance", + msg: toUtf8(`{"contract":"specific"}`), + funds: [ + { + denom: "ucosm", + amount: "123", + }, + ], + }, + }, + ]); + }); + }); +}); diff --git a/packages/cosmwasm-stargate/src/modules/wasm/messages.ts b/packages/cosmwasm-stargate/src/modules/wasm/messages.ts index e9a1ea0278..e40902eef5 100644 --- a/packages/cosmwasm-stargate/src/modules/wasm/messages.ts +++ b/packages/cosmwasm-stargate/src/modules/wasm/messages.ts @@ -1,4 +1,6 @@ +import { toUtf8 } from "@cosmjs/encoding"; import { EncodeObject, GeneratedType } from "@cosmjs/proto-signing"; +import { isNonNullObject } from "@cosmjs/utils"; import { MsgClearAdmin, MsgExecuteContract, @@ -7,6 +9,7 @@ import { MsgStoreCode, MsgUpdateAdmin, } from "cosmjs-types/cosmwasm/wasm/v1/tx"; +import Long from "long"; export const wasmTypes: ReadonlyArray<[string, GeneratedType]> = [ ["/cosmwasm.wasm.v1.MsgClearAdmin", MsgClearAdmin], @@ -74,3 +77,58 @@ export interface MsgExecuteContractEncodeObject extends EncodeObject { export function isMsgExecuteEncodeObject(object: EncodeObject): object is MsgExecuteContractEncodeObject { return (object as MsgExecuteContractEncodeObject).typeUrl === "/cosmwasm.wasm.v1.MsgExecuteContract"; } + +/** + * Takes a JSON representation of the Go implementation and imports it into CosmJS types + * + * The Go JSON and the ts-proto JSON are not compatible, i.e. we have to implement every + * message individually. + */ +export function importWasmMessages( + json: string, +): Array { + const doc = JSON.parse(json); + if (!Array.isArray(doc)) throw new Error("Array expected"); + const out = []; + for (const element of doc) { + if (!isNonNullObject(element)) throw new Error("Element must be an object"); + if (!("@type" in element)) throw new Error("Element is missing a @type field"); + const typeUrl = (element as any)["@type"]; + if (typeof typeUrl !== "string") throw new Error("Element's @type fiels must be a string"); + switch (typeUrl) { + case "/cosmwasm.wasm.v1.MsgExecuteContract": { + // console.log(element); + const msg: MsgExecuteContractEncodeObject = { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: MsgExecuteContract.fromPartial({ + sender: (element as any).sender, + contract: (element as any).contract, + msg: toUtf8(JSON.stringify((element as any).msg)), + funds: (element as any).funds, + }), + }; + out.push(msg); + break; + } + case "/cosmwasm.wasm.v1.MsgInstantiateContract": { + // console.log(element); + const msg: MsgInstantiateContractEncodeObject = { + typeUrl: "/cosmwasm.wasm.v1.MsgInstantiateContract", + value: MsgInstantiateContract.fromPartial({ + sender: (element as any).sender, + admin: (element as any).admin, + codeId: Long.fromString((element as any).code_id, true, 10), + label: (element as any).label, + msg: toUtf8(JSON.stringify((element as any).msg)), + funds: (element as any).funds, + }), + }; + out.push(msg); + break; + } + default: + throw new Error(`Unsupported message type '${typeUrl}' found.`); + } + } + return out; +} From 1ade2ad431d2a96ca514c6bd48d1066f1d02dddb Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 9 Mar 2022 02:00:14 +0100 Subject: [PATCH 2/3] Pull out importMsgExecuteContractEncodeObject/importMsgInstantiateContract --- .../src/modules/wasm/messages.ts | 87 ++++++++++++------- 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/packages/cosmwasm-stargate/src/modules/wasm/messages.ts b/packages/cosmwasm-stargate/src/modules/wasm/messages.ts index e40902eef5..b775f5b05f 100644 --- a/packages/cosmwasm-stargate/src/modules/wasm/messages.ts +++ b/packages/cosmwasm-stargate/src/modules/wasm/messages.ts @@ -1,6 +1,6 @@ import { toUtf8 } from "@cosmjs/encoding"; import { EncodeObject, GeneratedType } from "@cosmjs/proto-signing"; -import { isNonNullObject } from "@cosmjs/utils"; +import { assert, isNonNullObject } from "@cosmjs/utils"; import { MsgClearAdmin, MsgExecuteContract, @@ -78,6 +78,55 @@ export function isMsgExecuteEncodeObject(object: EncodeObject): object is MsgExe return (object as MsgExecuteContractEncodeObject).typeUrl === "/cosmwasm.wasm.v1.MsgExecuteContract"; } +function getField(object: any, key: string | number): any { + if (!(key in object)) throw new Error(`Missing key '${key}' in object`); + return object[key]; +} + +function getStringField(object: any, key: string | number): string { + const value = getField(object, key); + if (typeof value !== "string") { + throw new Error(`Wrong type for key '${key}' in object: ${typeof value}. Must be a string.`); + } + return value; +} + +function getArrayField(object: any, key: string | number): any[] { + const value = getField(object, key); + if (!Array.isArray(value)) { + throw new Error(`Wrong type for key '${key}' in object: ${typeof value}. Must be an array.`); + } + return value; +} + +function importMsgExecuteContractEncodeObject(object: unknown): MsgExecuteContractEncodeObject { + assert(isNonNullObject(object)); + return { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: MsgExecuteContract.fromPartial({ + sender: getStringField(object, "sender"), + contract: getStringField(object, "contract"), + msg: toUtf8(JSON.stringify(getField(object, "msg"))), + funds: getArrayField(object, "funds"), + }), + }; +} + +function importMsgInstantiateContract(object: unknown): MsgInstantiateContractEncodeObject { + assert(isNonNullObject(object)); + return { + typeUrl: "/cosmwasm.wasm.v1.MsgInstantiateContract", + value: MsgInstantiateContract.fromPartial({ + sender: getStringField(object, "sender"), + admin: getStringField(object, "admin"), + codeId: Long.fromString(getStringField(object, "code_id"), true, 10), + label: getStringField(object, "label"), + msg: toUtf8(JSON.stringify(getField(object, "msg"))), + funds: getArrayField(object, "funds"), + }), + }; +} + /** * Takes a JSON representation of the Go implementation and imports it into CosmJS types * @@ -92,40 +141,14 @@ export function importWasmMessages( const out = []; for (const element of doc) { if (!isNonNullObject(element)) throw new Error("Element must be an object"); - if (!("@type" in element)) throw new Error("Element is missing a @type field"); - const typeUrl = (element as any)["@type"]; - if (typeof typeUrl !== "string") throw new Error("Element's @type fiels must be a string"); + const typeUrl = getStringField(element, "@type"); switch (typeUrl) { - case "/cosmwasm.wasm.v1.MsgExecuteContract": { - // console.log(element); - const msg: MsgExecuteContractEncodeObject = { - typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", - value: MsgExecuteContract.fromPartial({ - sender: (element as any).sender, - contract: (element as any).contract, - msg: toUtf8(JSON.stringify((element as any).msg)), - funds: (element as any).funds, - }), - }; - out.push(msg); + case "/cosmwasm.wasm.v1.MsgExecuteContract": + out.push(importMsgExecuteContractEncodeObject(element)); break; - } - case "/cosmwasm.wasm.v1.MsgInstantiateContract": { - // console.log(element); - const msg: MsgInstantiateContractEncodeObject = { - typeUrl: "/cosmwasm.wasm.v1.MsgInstantiateContract", - value: MsgInstantiateContract.fromPartial({ - sender: (element as any).sender, - admin: (element as any).admin, - codeId: Long.fromString((element as any).code_id, true, 10), - label: (element as any).label, - msg: toUtf8(JSON.stringify((element as any).msg)), - funds: (element as any).funds, - }), - }; - out.push(msg); + case "/cosmwasm.wasm.v1.MsgInstantiateContract": + out.push(importMsgInstantiateContract(element)); break; - } default: throw new Error(`Unsupported message type '${typeUrl}' found.`); } From 7a8af1918bbbe628bbd80f0c742ec8aaec8ae5ce Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 10 Mar 2022 22:59:14 +0100 Subject: [PATCH 3/3] Add test with no admin --- .../src/modules/wasm/messages.spec.ts | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/cosmwasm-stargate/src/modules/wasm/messages.spec.ts b/packages/cosmwasm-stargate/src/modules/wasm/messages.spec.ts index b7eb2540b5..286646007c 100644 --- a/packages/cosmwasm-stargate/src/modules/wasm/messages.spec.ts +++ b/packages/cosmwasm-stargate/src/modules/wasm/messages.spec.ts @@ -66,11 +66,12 @@ describe("messages", () => { }); it("works for MsgInstantiateContract", () => { + // All fields set // wasmd tx wasm instantiate 55 '{"contract":"specific"}' --label "unique instance" --admin wasm1hhg2rlu9jscacku2wwckws7932qqqu8xm5ca8y --amount 123ucosm --from wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd --chain-id test123 --offline --generate-only | jq .body.messages -c - const msgs = importWasmMessages( + const msgs1 = importWasmMessages( `[{"@type":"/cosmwasm.wasm.v1.MsgInstantiateContract","sender":"wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd","admin":"wasm1hhg2rlu9jscacku2wwckws7932qqqu8xm5ca8y","code_id":"55","label":"unique instance","msg":{"contract":"specific"},"funds":[{"denom":"ucosm","amount":"123"}]}]`, ); - expect(msgs).toEqual([ + expect(msgs1).toEqual([ { typeUrl: "/cosmwasm.wasm.v1.MsgInstantiateContract", value: { @@ -88,6 +89,30 @@ describe("messages", () => { }, }, ]); + + // No admin + // wasmd tx wasm instantiate 55 '{"contract":"specific"}' --label "unique instance" --no-admin --amount 123ucosm --from wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd --chain-id test123 --offline --generate-only | jq .body.messages -c + const msgs2 = importWasmMessages( + `[{"@type":"/cosmwasm.wasm.v1.MsgInstantiateContract","sender":"wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd","admin":"","code_id":"55","label":"unique instance","msg":{"contract":"specific"},"funds":[{"denom":"ucosm","amount":"123"}]}]`, + ); + expect(msgs2).toEqual([ + { + typeUrl: "/cosmwasm.wasm.v1.MsgInstantiateContract", + value: { + sender: "wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd", + codeId: Long.fromNumber(55, true), + admin: "", + label: "unique instance", + msg: toUtf8(`{"contract":"specific"}`), + funds: [ + { + denom: "ucosm", + amount: "123", + }, + ], + }, + }, + ]); }); it("works for mixed MsgExecuteContract and MsgInstantiateContract", () => {