Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create JSON importer for CosmWasm messages #1083

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions packages/cosmwasm-stargate/src/modules/wasm/messages.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
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", () => {
// 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 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(msgs1).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",
},
],
},
},
]);

// 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", () => {
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",
},
],
},
},
]);
});
});
});
81 changes: 81 additions & 0 deletions packages/cosmwasm-stargate/src/modules/wasm/messages.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { toUtf8 } from "@cosmjs/encoding";
import { EncodeObject, GeneratedType } from "@cosmjs/proto-signing";
import { assert, isNonNullObject } from "@cosmjs/utils";
import {
MsgClearAdmin,
MsgExecuteContract,
Expand All @@ -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],
Expand Down Expand Up @@ -74,3 +77,81 @@ export interface MsgExecuteContractEncodeObject extends EncodeObject {
export function isMsgExecuteEncodeObject(object: EncodeObject): object is MsgExecuteContractEncodeObject {
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
*
* 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<MsgExecuteContractEncodeObject | MsgInstantiateContractEncodeObject> {
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");
const typeUrl = getStringField(element, "@type");
switch (typeUrl) {
case "/cosmwasm.wasm.v1.MsgExecuteContract":
out.push(importMsgExecuteContractEncodeObject(element));
break;
case "/cosmwasm.wasm.v1.MsgInstantiateContract":
out.push(importMsgInstantiateContract(element));
break;
default:
throw new Error(`Unsupported message type '${typeUrl}' found.`);
}
}
return out;
}