From 7af4db111f2b9f6e69be3bc54b4589d8532ffeff Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 11:21:47 +0000 Subject: [PATCH 01/18] stargate: Export AminoConverter type --- packages/stargate/src/index.ts | 2 +- packages/stargate/types/index.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/stargate/src/index.ts b/packages/stargate/src/index.ts index 4254360a29..a278448a7f 100644 --- a/packages/stargate/src/index.ts +++ b/packages/stargate/src/index.ts @@ -1,5 +1,5 @@ export * as codec from "./codec"; -export { AminoTypes } from "./aminotypes"; +export { AminoConverter, AminoTypes } from "./aminotypes"; export { parseRawLog } from "./logs"; export { AuthExtension, diff --git a/packages/stargate/types/index.d.ts b/packages/stargate/types/index.d.ts index 4254360a29..a278448a7f 100644 --- a/packages/stargate/types/index.d.ts +++ b/packages/stargate/types/index.d.ts @@ -1,5 +1,5 @@ export * as codec from "./codec"; -export { AminoTypes } from "./aminotypes"; +export { AminoConverter, AminoTypes } from "./aminotypes"; export { parseRawLog } from "./logs"; export { AuthExtension, From 9b3ccf8396262b556ad5afdbb135f0f43972534c Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 11:22:25 +0000 Subject: [PATCH 02/18] cosmwasm-stargate: Add conversion for wasm Amino Msg types --- packages/cosmwasm-stargate/src/aminotypes.ts | 173 ++++++++++++++++++ .../cosmwasm-stargate/types/aminotypes.d.ts | 2 + 2 files changed, 175 insertions(+) create mode 100644 packages/cosmwasm-stargate/src/aminotypes.ts create mode 100644 packages/cosmwasm-stargate/types/aminotypes.d.ts diff --git a/packages/cosmwasm-stargate/src/aminotypes.ts b/packages/cosmwasm-stargate/src/aminotypes.ts new file mode 100644 index 0000000000..1d9a47b609 --- /dev/null +++ b/packages/cosmwasm-stargate/src/aminotypes.ts @@ -0,0 +1,173 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { + MsgClearAdmin, + MsgExecuteContract, + MsgInstantiateContract, + MsgMigrateContract, + MsgStoreCode, + MsgUpdateAdmin, +} from "@cosmjs/cosmwasm-launchpad"; +import { fromAscii, fromBase64, toAscii, toBase64 } from "@cosmjs/encoding"; +import { Coin } from "@cosmjs/launchpad"; +import { AminoConverter, codec } from "@cosmjs/stargate"; +import { assert } from "@cosmjs/utils"; +import Long from "long"; + +import { cosmwasm } from "./codec"; + +type ICoin = codec.cosmos.base.v1beta1.ICoin; +type IMsgStoreCode = cosmwasm.wasm.v1beta1.IMsgStoreCode; +type IMsgInstantiateContract = cosmwasm.wasm.v1beta1.IMsgInstantiateContract; +type IMsgUpdateAdmin = cosmwasm.wasm.v1beta1.IMsgUpdateAdmin; +type IMsgClearAdmin = cosmwasm.wasm.v1beta1.IMsgClearAdmin; +type IMsgExecuteContract = cosmwasm.wasm.v1beta1.IMsgExecuteContract; +type IMsgMigrateContract = cosmwasm.wasm.v1beta1.IMsgMigrateContract; + +function checkAmount(amount: readonly ICoin[] | undefined | null): readonly Coin[] { + assert(amount, "missing amount"); + return amount.map((a) => { + assert(a.amount, "missing amount"); + assert(a.denom, "missing denom"); + return { + amount: a.amount, + denom: a.denom, + }; + }); +} + +export const defaultTypes: Record = { + "/cosmwasm.wasm.v1beta1.MsgStoreCode": { + aminoType: "wasm/MsgStoreCode", + toAmino: ({ sender, wasmByteCode, source, builder }: IMsgStoreCode): MsgStoreCode["value"] => { + assert(sender, "missing sender"); + assert(wasmByteCode, "missing wasmByteCode"); + assert(typeof source === "string", "missing source"); + assert(typeof builder === "string", "missing builder"); + return { + sender: sender, + wasm_byte_code: toBase64(wasmByteCode), + source: source, + builder: builder, + }; + }, + fromAmino: ({ sender, wasm_byte_code, source, builder }: MsgStoreCode["value"]): IMsgStoreCode => ({ + sender: sender, + wasmByteCode: fromBase64(wasm_byte_code), + source: source, + builder: builder, + }), + }, + "/cosmwasm.wasm.v1beta1.MsgInstantiateContract": { + aminoType: "wasm/MsgInstantiateContract", + toAmino: ({ + sender, + codeId, + label, + initMsg, + initFunds, + admin, + }: IMsgInstantiateContract): MsgInstantiateContract["value"] => { + assert(sender, "missing sender"); + assert(codeId, "missing codeId"); + assert(label, "missing label"); + assert(initMsg, "missing initMsg"); + return { + sender: sender, + code_id: codeId.toString(), + label: label, + init_msg: JSON.parse(fromAscii(initMsg)), + init_funds: checkAmount(initFunds), + admin: admin ?? undefined, + }; + }, + fromAmino: ({ + sender, + code_id, + label, + init_msg, + init_funds, + admin, + }: MsgInstantiateContract["value"]): IMsgInstantiateContract => ({ + sender: sender, + codeId: Long.fromString(code_id), + label: label, + initMsg: toAscii(JSON.stringify(init_msg)), + initFunds: [...init_funds], + admin: admin, + }), + }, + "/cosmwasm.wasm.v1beta1.MsgUpdateAdmin": { + aminoType: "wasm/MsgUpdateAdmin", + toAmino: ({ sender, newAdmin, contract }: IMsgUpdateAdmin): MsgUpdateAdmin["value"] => { + assert(sender, "missing sender"); + assert(newAdmin, "missing newAdmin"); + assert(contract, "missing contract"); + return { + sender: sender, + new_admin: newAdmin, + contract: contract, + }; + }, + fromAmino: ({ sender, new_admin, contract }: MsgUpdateAdmin["value"]): IMsgUpdateAdmin => ({ + sender: sender, + newAdmin: new_admin, + contract: contract, + }), + }, + "/cosmwasm.wasm.v1beta1.MsgClearAdmin": { + aminoType: "wasm/MsgClearAdmin", + toAmino: ({ sender, contract }: IMsgClearAdmin): MsgClearAdmin["value"] => { + assert(sender, "missing sender"); + assert(contract, "missing contract"); + return { + sender: sender, + contract: contract, + }; + }, + fromAmino: ({ sender, contract }: MsgClearAdmin["value"]): IMsgClearAdmin => ({ + sender: sender, + contract: contract, + }), + }, + "/cosmwasm.wasm.v1beta1.MsgExecuteContract": { + aminoType: "wasm/MsgExecuteContract", + toAmino: ({ sender, contract, msg, sentFunds }: IMsgExecuteContract): MsgExecuteContract["value"] => { + assert(sender, "missing sender"); + assert(contract, "missing contract"); + assert(msg, "missing msg"); + return { + sender: sender, + contract: contract, + msg: JSON.parse(fromAscii(msg)), + sent_funds: checkAmount(sentFunds), + }; + }, + fromAmino: ({ sender, contract, msg, sent_funds }: MsgExecuteContract["value"]): IMsgExecuteContract => ({ + sender: sender, + contract: contract, + msg: toAscii(JSON.stringify(msg)), + sentFunds: [...sent_funds], + }), + }, + "/cosmwasm.wasm.v1beta1.MsgMigrateContract": { + aminoType: "wasm/MsgMigrateContract", + toAmino: ({ sender, contract, codeId, migrateMsg }: IMsgMigrateContract): MsgMigrateContract["value"] => { + assert(sender, "missing sender"); + assert(contract, "missing contract"); + assert(codeId, "missing codeId"); + assert(migrateMsg, "missing migrateMsg"); + return { + sender: sender, + contract: contract, + code_id: codeId.toString(), + msg: JSON.parse(fromAscii(migrateMsg)), + }; + }, + fromAmino: ({ sender, contract, code_id, msg }: MsgMigrateContract["value"]): IMsgMigrateContract => ({ + sender: sender, + contract: contract, + codeId: Long.fromString(code_id), + migrateMsg: toAscii(JSON.stringify(msg)), + }), + }, +}; diff --git a/packages/cosmwasm-stargate/types/aminotypes.d.ts b/packages/cosmwasm-stargate/types/aminotypes.d.ts new file mode 100644 index 0000000000..7febac2086 --- /dev/null +++ b/packages/cosmwasm-stargate/types/aminotypes.d.ts @@ -0,0 +1,2 @@ +import { AminoConverter } from "@cosmjs/stargate"; +export declare const defaultTypes: Record; From 8d1d745fc8ea45349c0c9182b606c1022a6be5dc Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 11:22:48 +0000 Subject: [PATCH 03/18] cosmwasm-stargate: Add Amino types tests --- .../cosmwasm-stargate/src/aminotypes.spec.ts | 337 ++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 packages/cosmwasm-stargate/src/aminotypes.spec.ts diff --git a/packages/cosmwasm-stargate/src/aminotypes.spec.ts b/packages/cosmwasm-stargate/src/aminotypes.spec.ts new file mode 100644 index 0000000000..4190162ffb --- /dev/null +++ b/packages/cosmwasm-stargate/src/aminotypes.spec.ts @@ -0,0 +1,337 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { + MsgClearAdmin, + MsgExecuteContract, + MsgInstantiateContract, + MsgMigrateContract, + MsgStoreCode, + MsgUpdateAdmin, +} from "@cosmjs/cosmwasm-launchpad"; +import { fromBase64, toAscii } from "@cosmjs/encoding"; +import { coins } from "@cosmjs/launchpad"; +import { AminoTypes } from "@cosmjs/stargate"; +import Long from "long"; + +import { defaultTypes } from "./aminotypes"; +import { cosmwasm } from "./codec"; + +type IMsgStoreCode = cosmwasm.wasm.v1beta1.IMsgStoreCode; +type IMsgInstantiateContract = cosmwasm.wasm.v1beta1.IMsgInstantiateContract; +type IMsgUpdateAdmin = cosmwasm.wasm.v1beta1.IMsgUpdateAdmin; +type IMsgClearAdmin = cosmwasm.wasm.v1beta1.IMsgClearAdmin; +type IMsgExecuteContract = cosmwasm.wasm.v1beta1.IMsgExecuteContract; +type IMsgMigrateContract = cosmwasm.wasm.v1beta1.IMsgMigrateContract; + +describe("AminoTypes", () => { + describe("toAmino", () => { + it("works for MsgStoreCode", () => { + const msg: IMsgStoreCode = { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + wasmByteCode: fromBase64("WUVMTE9XIFNVQk1BUklORQ=="), + source: "Arrabiata", + builder: "Bob", + }; + const aminoMsg = new AminoTypes({ additions: defaultTypes }).toAmino({ + typeUrl: "/cosmwasm.wasm.v1beta1.MsgStoreCode", + value: msg, + }); + const expected: MsgStoreCode = { + type: "wasm/MsgStoreCode", + value: { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + wasm_byte_code: "WUVMTE9XIFNVQk1BUklORQ==", + source: "Arrabiata", + builder: "Bob", + }, + }; + expect(aminoMsg).toEqual(expected); + }); + + it("works for MsgInstantiateContract", () => { + const msg: IMsgInstantiateContract = { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + codeId: Long.fromString("12345"), + label: "sticky", + initMsg: toAscii( + JSON.stringify({ + foo: "bar", + }), + ), + initFunds: coins(1234, "ucosm"), + admin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", + }; + const aminoMsg = new AminoTypes({ additions: defaultTypes }).toAmino({ + typeUrl: "/cosmwasm.wasm.v1beta1.MsgInstantiateContract", + value: msg, + }); + const expected: MsgInstantiateContract = { + type: "wasm/MsgInstantiateContract", + value: { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + code_id: "12345", + label: "sticky", + init_msg: { + foo: "bar", + }, + init_funds: coins(1234, "ucosm"), + admin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", + }, + }; + expect(aminoMsg).toEqual(expected); + }); + + it("works for MsgUpdateAdmin", () => { + const msg: IMsgUpdateAdmin = { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + newAdmin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + }; + const aminoMsg = new AminoTypes({ additions: defaultTypes }).toAmino({ + typeUrl: "/cosmwasm.wasm.v1beta1.MsgUpdateAdmin", + value: msg, + }); + const expected: MsgUpdateAdmin = { + type: "wasm/MsgUpdateAdmin", + value: { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + new_admin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + }, + }; + expect(aminoMsg).toEqual(expected); + }); + + it("works for MsgClearAdmin", () => { + const msg: IMsgClearAdmin = { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + }; + const aminoMsg = new AminoTypes({ additions: defaultTypes }).toAmino({ + typeUrl: "/cosmwasm.wasm.v1beta1.MsgClearAdmin", + value: msg, + }); + const expected: MsgClearAdmin = { + type: "wasm/MsgClearAdmin", + value: { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + }, + }; + expect(aminoMsg).toEqual(expected); + }); + + it("works for MsgExecuteContract", () => { + const msg: IMsgExecuteContract = { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + msg: toAscii( + JSON.stringify({ + foo: "bar", + }), + ), + sentFunds: coins(1234, "ucosm"), + }; + const aminoMsg = new AminoTypes({ additions: defaultTypes }).toAmino({ + typeUrl: "/cosmwasm.wasm.v1beta1.MsgExecuteContract", + value: msg, + }); + const expected: MsgExecuteContract = { + type: "wasm/MsgExecuteContract", + value: { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + msg: { + foo: "bar", + }, + sent_funds: coins(1234, "ucosm"), + }, + }; + expect(aminoMsg).toEqual(expected); + }); + + it("works for MsgMigrateContract", () => { + const msg: IMsgMigrateContract = { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + codeId: Long.fromString("98765"), + migrateMsg: toAscii( + JSON.stringify({ + foo: "bar", + }), + ), + }; + const aminoMsg = new AminoTypes({ additions: defaultTypes }).toAmino({ + typeUrl: "/cosmwasm.wasm.v1beta1.MsgMigrateContract", + value: msg, + }); + const expected: MsgMigrateContract = { + type: "wasm/MsgMigrateContract", + value: { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + code_id: "98765", + msg: { + foo: "bar", + }, + }, + }; + expect(aminoMsg).toEqual(expected); + }); + }); + + describe("fromAmino", () => { + it("works for MsgStoreCode", () => { + const aminoMsg: MsgStoreCode = { + type: "wasm/MsgStoreCode", + value: { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + wasm_byte_code: "WUVMTE9XIFNVQk1BUklORQ==", + source: "Arrabiata", + builder: "Bob", + }, + }; + const msg = new AminoTypes({ additions: defaultTypes }).fromAmino(aminoMsg); + const expectedValue: IMsgStoreCode = { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + wasmByteCode: fromBase64("WUVMTE9XIFNVQk1BUklORQ=="), + source: "Arrabiata", + builder: "Bob", + }; + expect(msg).toEqual({ + typeUrl: "/cosmwasm.wasm.v1beta1.MsgStoreCode", + value: expectedValue, + }); + }); + + it("works for MsgInstantiateContract", () => { + const aminoMsg: MsgInstantiateContract = { + type: "wasm/MsgInstantiateContract", + value: { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + code_id: "12345", + label: "sticky", + init_msg: { + foo: "bar", + }, + init_funds: coins(1234, "ucosm"), + admin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", + }, + }; + const msg = new AminoTypes({ additions: defaultTypes }).fromAmino(aminoMsg); + const expectedValue: IMsgInstantiateContract = { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + codeId: Long.fromString("12345"), + label: "sticky", + initMsg: toAscii( + JSON.stringify({ + foo: "bar", + }), + ), + initFunds: coins(1234, "ucosm"), + admin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", + }; + expect(msg).toEqual({ + typeUrl: "/cosmwasm.wasm.v1beta1.MsgInstantiateContract", + value: expectedValue, + }); + }); + + it("works for MsgUpdateAdmin", () => { + const aminoMsg: MsgUpdateAdmin = { + type: "wasm/MsgUpdateAdmin", + value: { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + new_admin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + }, + }; + const msg = new AminoTypes({ additions: defaultTypes }).fromAmino(aminoMsg); + const expectedValue: IMsgUpdateAdmin = { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + newAdmin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + }; + expect(msg).toEqual({ + typeUrl: "/cosmwasm.wasm.v1beta1.MsgUpdateAdmin", + value: expectedValue, + }); + }); + + it("works for MsgClearAdmin", () => { + const aminoMsg: MsgClearAdmin = { + type: "wasm/MsgClearAdmin", + value: { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + }, + }; + const msg = new AminoTypes({ additions: defaultTypes }).fromAmino(aminoMsg); + const expectedValue: IMsgClearAdmin = { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + }; + expect(msg).toEqual({ + typeUrl: "/cosmwasm.wasm.v1beta1.MsgClearAdmin", + value: expectedValue, + }); + }); + + it("works for MsgExecuteContract", () => { + const aminoMsg: MsgExecuteContract = { + type: "wasm/MsgExecuteContract", + value: { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + msg: { + foo: "bar", + }, + sent_funds: coins(1234, "ucosm"), + }, + }; + const msg = new AminoTypes({ additions: defaultTypes }).fromAmino(aminoMsg); + const expectedValue: IMsgExecuteContract = { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + msg: toAscii( + JSON.stringify({ + foo: "bar", + }), + ), + sentFunds: coins(1234, "ucosm"), + }; + expect(msg).toEqual({ + typeUrl: "/cosmwasm.wasm.v1beta1.MsgExecuteContract", + value: expectedValue, + }); + }); + + it("works for MsgMigrateContract", () => { + const aminoMsg: MsgMigrateContract = { + type: "wasm/MsgMigrateContract", + value: { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + code_id: "98765", + msg: { + foo: "bar", + }, + }, + }; + const msg = new AminoTypes({ additions: defaultTypes }).fromAmino(aminoMsg); + const expectedValue: IMsgMigrateContract = { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", + codeId: Long.fromString("98765"), + migrateMsg: toAscii( + JSON.stringify({ + foo: "bar", + }), + ), + }; + expect(msg).toEqual({ + typeUrl: "/cosmwasm.wasm.v1beta1.MsgMigrateContract", + value: expectedValue, + }); + }); + }); +}); From 2548ed7a818ec9496dab9370facf0bc22bf1831a Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 12:04:42 +0000 Subject: [PATCH 04/18] stargate: Add Amino signer test for sendTokens --- .../src/signingstargateclient.spec.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/stargate/src/signingstargateclient.spec.ts b/packages/stargate/src/signingstargateclient.spec.ts index 921ea5cdf5..88aca894a2 100644 --- a/packages/stargate/src/signingstargateclient.spec.ts +++ b/packages/stargate/src/signingstargateclient.spec.ts @@ -126,7 +126,7 @@ describe("SigningStargateClient", () => { }); describe("sendTokens", () => { - it("works", async () => { + it("works with direct signer", async () => { pendingWithoutSimapp(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic); const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet); @@ -149,6 +149,30 @@ describe("SigningStargateClient", () => { assert(after); expect(after).toEqual(transferAmount[0]); }); + + it("works with legacy Amino signer", async () => { + pendingWithoutSimapp(); + const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic); + const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet); + + const transferAmount = coins(7890, "ucosm"); + const beneficiaryAddress = makeRandomAddress(); + const memo = "for dinner"; + + // no tokens here + const before = await client.getBalance(beneficiaryAddress, "ucosm"); + expect(before).toBeNull(); + + // send + const result = await client.sendTokens(faucet.address0, beneficiaryAddress, transferAmount, memo); + assertIsBroadcastTxSuccess(result); + expect(result.rawLog).toBeTruthy(); + + // got tokens + const after = await client.getBalance(beneficiaryAddress, "ucosm"); + assert(after); + expect(after).toEqual(transferAmount[0]); + }); }); describe("signAndBroadcast", () => { From 114605e469680a0ce56f31786d24625b14e96fe8 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 12:18:40 +0000 Subject: [PATCH 05/18] stargate: Update signAndBroadcast param name --- packages/stargate/src/signingstargateclient.ts | 12 ++++++------ packages/stargate/types/signingstargateclient.d.ts | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/stargate/src/signingstargateclient.ts b/packages/stargate/src/signingstargateclient.ts index 263446f8a2..c982b19c28 100644 --- a/packages/stargate/src/signingstargateclient.ts +++ b/packages/stargate/src/signingstargateclient.ts @@ -57,7 +57,7 @@ export class SigningStargateClient extends StargateClient { private readonly fees: CosmosFeeTable; private readonly registry: Registry; private readonly signer: OfflineSigner; - private readonly aminoTypes; + private readonly aminoTypes: AminoTypes; public static async connectWithSigner( endpoint: string, @@ -111,19 +111,19 @@ export class SigningStargateClient extends StargateClient { } public async signAndBroadcast( - address: string, + signerAddress: string, messages: readonly EncodeObject[], fee: StdFee, memo = "", ): Promise { const accountFromSigner = (await this.signer.getAccounts()).find( - (account: AccountData) => account.address === address, + (account: AccountData) => account.address === signerAddress, ); if (!accountFromSigner) { throw new Error("Failed to retrieve account from signer"); } const pubkey = encodeSecp256k1Pubkey(accountFromSigner.pubkey); - const accountFromChain = await this.getAccount(address); + const accountFromChain = await this.getAccount(signerAddress); if (!accountFromChain) { throw new Error("Account not found"); } @@ -146,7 +146,7 @@ export class SigningStargateClient extends StargateClient { if (isOfflineDirectSigner(this.signer)) { const authInfoBytes = makeAuthInfoBytes([pubkeyAny], fee.amount, gasLimit, sequence); const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber); - const { signature, signed } = await this.signer.signDirect(address, signDoc); + const { signature, signed } = await this.signer.signDirect(signerAddress, signDoc); const txRaw = TxRaw.create({ bodyBytes: signed.bodyBytes, authInfoBytes: signed.authInfoBytes, @@ -160,7 +160,7 @@ export class SigningStargateClient extends StargateClient { const signMode = cosmos.tx.signing.v1beta1.SignMode.SIGN_MODE_LEGACY_AMINO_JSON; const msgs = messages.map((msg) => this.aminoTypes.toAmino(msg)); const signDoc = makeSignDocAmino(msgs, fee, chainId, memo, accountNumber, sequence); - const { signature, signed } = await this.signer.signAmino(address, signDoc); + const { signature, signed } = await this.signer.signAmino(signerAddress, signDoc); const signedTxBody = { messages: signed.msgs.map((msg) => this.aminoTypes.fromAmino(msg)), memo: signed.memo, diff --git a/packages/stargate/types/signingstargateclient.d.ts b/packages/stargate/types/signingstargateclient.d.ts index ea901c428c..510fe2ab3a 100644 --- a/packages/stargate/types/signingstargateclient.d.ts +++ b/packages/stargate/types/signingstargateclient.d.ts @@ -31,7 +31,7 @@ export declare class SigningStargateClient extends StargateClient { memo?: string, ): Promise; signAndBroadcast( - address: string, + signerAddress: string, messages: readonly EncodeObject[], fee: StdFee, memo?: string, From 4270e325b532095f0fa738c4f61c657510293710 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 12:24:03 +0000 Subject: [PATCH 06/18] stargate: Add prefix to SigningStargateClient options --- packages/stargate/src/signingstargateclient.ts | 3 ++- packages/stargate/types/signingstargateclient.d.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/stargate/src/signingstargateclient.ts b/packages/stargate/src/signingstargateclient.ts index c982b19c28..4f5eb72779 100644 --- a/packages/stargate/src/signingstargateclient.ts +++ b/packages/stargate/src/signingstargateclient.ts @@ -49,6 +49,7 @@ export interface PrivateSigningStargateClient { export interface SigningStargateClientOptions { readonly registry?: Registry; readonly aminoTypes?: AminoTypes; + readonly prefix?: string; readonly gasPrice?: GasPrice; readonly gasLimits?: GasLimits; } @@ -83,7 +84,7 @@ export class SigningStargateClient extends StargateClient { ["/cosmos.staking.v1beta1.MsgEditValidator", MsgEditValidator], ["/cosmos.staking.v1beta1.MsgUndelegate", MsgUndelegate], ]), - aminoTypes = new AminoTypes(), + aminoTypes = new AminoTypes({ prefix: options.prefix }), gasPrice = defaultGasPrice, gasLimits = defaultGasLimits, } = options; diff --git a/packages/stargate/types/signingstargateclient.d.ts b/packages/stargate/types/signingstargateclient.d.ts index 510fe2ab3a..58ae7ffa20 100644 --- a/packages/stargate/types/signingstargateclient.d.ts +++ b/packages/stargate/types/signingstargateclient.d.ts @@ -10,6 +10,7 @@ export interface PrivateSigningStargateClient { export interface SigningStargateClientOptions { readonly registry?: Registry; readonly aminoTypes?: AminoTypes; + readonly prefix?: string; readonly gasPrice?: GasPrice; readonly gasLimits?: GasLimits; } From 027f76ce744b8f37cde7992d97133b85fce6be2c Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 12:41:22 +0000 Subject: [PATCH 07/18] stargate: Refactor registry initialisation in SigningStargateClient --- .../stargate/src/signingstargateclient.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/stargate/src/signingstargateclient.ts b/packages/stargate/src/signingstargateclient.ts index 4f5eb72779..6a6d8f4942 100644 --- a/packages/stargate/src/signingstargateclient.ts +++ b/packages/stargate/src/signingstargateclient.ts @@ -40,6 +40,17 @@ const { TxRaw } = cosmos.tx.v1beta1; const defaultGasPrice = GasPrice.fromString("0.025ucosm"); const defaultGasLimits: GasLimits = { send: 80000 }; +function createDefaultRegistry(): Registry { + return new Registry([ + ["/cosmos.bank.v1beta1.MsgMultiSend", MsgMultiSend], + ["/cosmos.staking.v1beta1.MsgBeginRedelegate", MsgBeginRedelegate], + ["/cosmos.staking.v1beta1.MsgCreateValidator", MsgCreateValidator], + ["/cosmos.staking.v1beta1.MsgDelegate", MsgDelegate], + ["/cosmos.staking.v1beta1.MsgEditValidator", MsgEditValidator], + ["/cosmos.staking.v1beta1.MsgUndelegate", MsgUndelegate], + ]); +} + /** Use for testing only */ export interface PrivateSigningStargateClient { readonly fees: CosmosFeeTable; @@ -76,14 +87,7 @@ export class SigningStargateClient extends StargateClient { ) { super(tmClient); const { - registry = new Registry([ - ["/cosmos.bank.v1beta1.MsgMultiSend", MsgMultiSend], - ["/cosmos.staking.v1beta1.MsgBeginRedelegate", MsgBeginRedelegate], - ["/cosmos.staking.v1beta1.MsgCreateValidator", MsgCreateValidator], - ["/cosmos.staking.v1beta1.MsgDelegate", MsgDelegate], - ["/cosmos.staking.v1beta1.MsgEditValidator", MsgEditValidator], - ["/cosmos.staking.v1beta1.MsgUndelegate", MsgUndelegate], - ]), + registry = createDefaultRegistry(), aminoTypes = new AminoTypes({ prefix: options.prefix }), gasPrice = defaultGasPrice, gasLimits = defaultGasLimits, From 52a6f062536650a17a7edb2c11d651468de3c4c0 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 13:01:43 +0000 Subject: [PATCH 08/18] stargate: Add Msg types in SigningStargateClient tests --- packages/stargate/src/signingstargateclient.spec.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/stargate/src/signingstargateclient.spec.ts b/packages/stargate/src/signingstargateclient.spec.ts index 88aca894a2..3753be49d7 100644 --- a/packages/stargate/src/signingstargateclient.spec.ts +++ b/packages/stargate/src/signingstargateclient.spec.ts @@ -24,6 +24,9 @@ import { validator, } from "./testutils.spec"; +type IMsgSend = cosmos.bank.v1beta1.IMsgSend; +type IMsgDelegate = cosmos.staking.v1beta1.IMsgDelegate; + const { MsgSend } = cosmos.bank.v1beta1; const { MsgDelegate } = cosmos.staking.v1beta1; const { Tx } = cosmos.tx.v1beta1; @@ -259,7 +262,7 @@ describe("SigningStargateClient", () => { const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic); const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet); - const msgSend = { + const msgSend: IMsgSend = { fromAddress: faucet.address0, toAddress: makeRandomAddress(), amount: coins(1234, "ucosm"), @@ -282,7 +285,7 @@ describe("SigningStargateClient", () => { const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic); const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet); - const msgDelegate = { + const msgDelegate: IMsgDelegate = { delegatorAddress: faucet.address0, validatorAddress: validator.validatorAddress, amount: coin(1234, "ustake"), From 5fc678fc26b5a604a0e4520b68fc946259d265a7 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 13:20:48 +0000 Subject: [PATCH 09/18] cosmwasm-stargate: Add Amino signing support to SigningCosmWasmClient --- .../src/signingcosmwasmclient.ts | 76 ++++++++++++++++--- .../types/signingcosmwasmclient.d.ts | 10 ++- 2 files changed, 74 insertions(+), 12 deletions(-) diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts index fff6628164..833ec27548 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts @@ -21,18 +21,21 @@ import { GasLimits, GasPrice, logs, + makeSignDoc as makeSignDocAmino, StdFee, } from "@cosmjs/launchpad"; import { Int53, Uint53 } from "@cosmjs/math"; import { EncodeObject, encodePubkey, + isOfflineDirectSigner, makeAuthInfoBytes, makeSignDoc, - OfflineDirectSigner, + OfflineSigner, Registry, } from "@cosmjs/proto-signing"; import { + AminoTypes, BroadcastTxFailure, BroadcastTxResponse, codec, @@ -43,10 +46,20 @@ import { adaptor34, Client as TendermintClient } from "@cosmjs/tendermint-rpc"; import Long from "long"; import pako from "pako"; +import { defaultTypes } from "./aminotypes"; import { cosmwasm } from "./codec"; import { CosmWasmClient } from "./cosmwasmclient"; +const { SignMode } = codec.cosmos.tx.signing.v1beta1; const { TxRaw } = codec.cosmos.tx.v1beta1; +const { MsgMultiSend } = codec.cosmos.bank.v1beta1; +const { + MsgBeginRedelegate, + MsgCreateValidator, + MsgDelegate, + MsgEditValidator, + MsgUndelegate, +} = codec.cosmos.staking.v1beta1; const { MsgClearAdmin, MsgExecuteContract, @@ -81,6 +94,12 @@ function createBroadcastTxErrorMessage(result: BroadcastTxFailure): string { function createDefaultRegistry(): Registry { return new Registry([ + ["/cosmos.bank.v1beta1.MsgMultiSend", MsgMultiSend], + ["/cosmos.staking.v1beta1.MsgBeginRedelegate", MsgBeginRedelegate], + ["/cosmos.staking.v1beta1.MsgCreateValidator", MsgCreateValidator], + ["/cosmos.staking.v1beta1.MsgDelegate", MsgDelegate], + ["/cosmos.staking.v1beta1.MsgEditValidator", MsgEditValidator], + ["/cosmos.staking.v1beta1.MsgUndelegate", MsgUndelegate], ["/cosmwasm.wasm.v1beta1.MsgClearAdmin", MsgClearAdmin], ["/cosmwasm.wasm.v1beta1.MsgExecuteContract", MsgExecuteContract], ["/cosmwasm.wasm.v1beta1.MsgMigrateContract", MsgMigrateContract], @@ -92,6 +111,8 @@ function createDefaultRegistry(): Registry { export interface SigningCosmWasmClientOptions { readonly registry?: Registry; + readonly aminoTypes?: AminoTypes; + readonly prefix?: string; readonly gasPrice?: GasPrice; readonly gasLimits?: GasLimits; } @@ -99,16 +120,18 @@ export interface SigningCosmWasmClientOptions { /** Use for testing only */ export interface PrivateSigningCosmWasmClient { readonly fees: CosmWasmFeeTable; + readonly registry: Registry; } export class SigningCosmWasmClient extends CosmWasmClient { private readonly fees: CosmosFeeTable; private readonly registry: Registry; - private readonly signer: OfflineDirectSigner; + private readonly signer: OfflineSigner; + private readonly aminoTypes: AminoTypes; public static async connectWithSigner( endpoint: string, - signer: OfflineDirectSigner, + signer: OfflineSigner, options: SigningCosmWasmClientOptions = {}, ): Promise { const tmClient = await TendermintClient.connect(endpoint, adaptor34); @@ -117,17 +140,19 @@ export class SigningCosmWasmClient extends CosmWasmClient { private constructor( tmClient: TendermintClient, - signer: OfflineDirectSigner, + signer: OfflineSigner, options: SigningCosmWasmClientOptions, ) { super(tmClient); const { registry = createDefaultRegistry(), + aminoTypes = new AminoTypes({ additions: defaultTypes, prefix: options.prefix }), gasPrice = defaultGasPrice, gasLimits = defaultGasLimits, } = options; this.fees = buildFeeTable(gasPrice, defaultGasLimits, gasLimits); this.registry = registry; + this.aminoTypes = aminoTypes; this.signer = signer; } @@ -355,12 +380,45 @@ export class SigningCosmWasmClient extends CosmWasmClient { }); const gasLimit = Int53.fromString(fee.gas).toNumber(); - const authInfoBytes = makeAuthInfoBytes([pubkeyAny], fee.amount, gasLimit, sequence); - const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber); - const { signature, signed } = await this.signer.signDirect(signerAddress, signDoc); + if (isOfflineDirectSigner(this.signer)) { + const authInfoBytes = makeAuthInfoBytes([pubkeyAny], fee.amount, gasLimit, sequence); + const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber); + const { signature, signed } = await this.signer.signDirect(signerAddress, signDoc); + const txRaw = TxRaw.create({ + bodyBytes: signed.bodyBytes, + authInfoBytes: signed.authInfoBytes, + signatures: [fromBase64(signature.signature)], + }); + const signedTx = Uint8Array.from(TxRaw.encode(txRaw).finish()); + return this.broadcastTx(signedTx); + } + // Amino signer + + // Amino signer + const signMode = SignMode.SIGN_MODE_LEGACY_AMINO_JSON; + const msgs = messages.map((msg) => this.aminoTypes.toAmino(msg)); + const signDoc = makeSignDocAmino(msgs, fee, chainId, memo, accountNumber, sequence); + const { signature, signed } = await this.signer.signAmino(signerAddress, signDoc); + const signedTxBody = { + messages: signed.msgs.map((msg) => this.aminoTypes.fromAmino(msg)), + memo: signed.memo, + }; + const signedTxBodyBytes = this.registry.encode({ + typeUrl: "/cosmos.tx.v1beta1.TxBody", + value: signedTxBody, + }); + const signedGasLimit = Int53.fromString(signed.fee.gas).toNumber(); + const signedSequence = Int53.fromString(signed.sequence).toNumber(); + const signedAuthInfoBytes = makeAuthInfoBytes( + [pubkeyAny], + signed.fee.amount, + signedGasLimit, + signedSequence, + signMode, + ); const txRaw = TxRaw.create({ - bodyBytes: signed.bodyBytes, - authInfoBytes: signed.authInfoBytes, + bodyBytes: signedTxBodyBytes, + authInfoBytes: signedAuthInfoBytes, signatures: [fromBase64(signature.signature)], }); const signedTx = Uint8Array.from(TxRaw.encode(txRaw).finish()); diff --git a/packages/cosmwasm-stargate/types/signingcosmwasmclient.d.ts b/packages/cosmwasm-stargate/types/signingcosmwasmclient.d.ts index 41f160bcf8..c6d66c4e46 100644 --- a/packages/cosmwasm-stargate/types/signingcosmwasmclient.d.ts +++ b/packages/cosmwasm-stargate/types/signingcosmwasmclient.d.ts @@ -9,25 +9,29 @@ import { UploadResult, } from "@cosmjs/cosmwasm-launchpad"; import { Coin, CosmosFeeTable, GasLimits, GasPrice, StdFee } from "@cosmjs/launchpad"; -import { EncodeObject, OfflineDirectSigner, Registry } from "@cosmjs/proto-signing"; -import { BroadcastTxResponse } from "@cosmjs/stargate"; +import { EncodeObject, OfflineSigner, Registry } from "@cosmjs/proto-signing"; +import { AminoTypes, BroadcastTxResponse } from "@cosmjs/stargate"; import { CosmWasmClient } from "./cosmwasmclient"; export interface SigningCosmWasmClientOptions { readonly registry?: Registry; + readonly aminoTypes?: AminoTypes; + readonly prefix?: string; readonly gasPrice?: GasPrice; readonly gasLimits?: GasLimits; } /** Use for testing only */ export interface PrivateSigningCosmWasmClient { readonly fees: CosmWasmFeeTable; + readonly registry: Registry; } export declare class SigningCosmWasmClient extends CosmWasmClient { private readonly fees; private readonly registry; private readonly signer; + private readonly aminoTypes; static connectWithSigner( endpoint: string, - signer: OfflineDirectSigner, + signer: OfflineSigner, options?: SigningCosmWasmClientOptions, ): Promise; private constructor(); From 0af2d6739b46364345be729e5774edcd78d951fe Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 13:22:52 +0000 Subject: [PATCH 10/18] cosmwasm-stargate: Add tests for SigningCosmWasmClient Amino signing --- .../src/signingcosmwasmclient.spec.ts | 241 +++++++++++++++++- .../cosmwasm-stargate/src/testutils.spec.ts | 43 +++- 2 files changed, 279 insertions(+), 5 deletions(-) diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts index def69bad1b..8554b04b3f 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts @@ -2,12 +2,21 @@ import { UploadMeta } from "@cosmjs/cosmwasm-launchpad"; import { sha256 } from "@cosmjs/crypto"; import { toHex } from "@cosmjs/encoding"; -import { coin, coins, GasPrice } from "@cosmjs/launchpad"; -import { DirectSecp256k1HdWallet, Registry } from "@cosmjs/proto-signing"; -import { assertIsBroadcastTxSuccess, codec } from "@cosmjs/stargate"; +import { + coin, + coins, + GasPrice, + MsgDelegate as LaunchpadMsgDelegate, + Secp256k1HdWallet, +} from "@cosmjs/launchpad"; +import { Coin, cosmosField, DirectSecp256k1HdWallet, registered, Registry } from "@cosmjs/proto-signing"; +import { AminoTypes, assertIsBroadcastTxSuccess, codec } from "@cosmjs/stargate"; import { assert, sleep } from "@cosmjs/utils"; import Long from "long"; +import pako from "pako"; +import { Message } from "protobufjs"; +import { cosmwasm } from "./codec"; import { PrivateSigningCosmWasmClient, SigningCosmWasmClient } from "./signingcosmwasmclient"; import { alice, @@ -15,12 +24,18 @@ import { makeRandomAddress, makeWasmClient, ModifyingDirectSecp256k1HdWallet, + ModifyingSecp256k1HdWallet, pendingWithoutWasmd, unused, validator, wasmd, } from "./testutils.spec"; +type IMsgSend = codec.cosmos.bank.v1beta1.IMsgSend; +type IMsgDelegate = codec.cosmos.staking.v1beta1.IMsgDelegate; +type IMsgStoreCode = cosmwasm.wasm.v1beta1.IMsgStoreCode; + +const { MsgSend } = codec.cosmos.bank.v1beta1; const { MsgDelegate } = codec.cosmos.staking.v1beta1; const { Tx } = codec.cosmos.tx.v1beta1; @@ -33,6 +48,17 @@ describe("SigningCosmWasmClient", () => { expect(client).toBeTruthy(); }); + it("can be constructed with custom registry", async () => { + pendingWithoutWasmd(); + const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic); + const registry = new Registry(); + registry.register("/custom.MsgCustom", MsgSend); + const options = { registry: registry }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); + const openedClient = (client as unknown) as PrivateSigningCosmWasmClient; + expect(openedClient.registry.lookupType("/custom.MsgCustom")).toEqual(MsgSend); + }); + it("can be constructed with custom gas price", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); @@ -411,7 +437,7 @@ describe("SigningCosmWasmClient", () => { }); describe("sendTokens", () => { - it("works", async () => { + it("works with direct signer", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); @@ -434,6 +460,30 @@ describe("SigningCosmWasmClient", () => { assert(after); expect(after).toEqual(transferAmount[0]); }); + + it("works with legacy Amino signer", async () => { + pendingWithoutWasmd(); + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + + const transferAmount = coins(7890, "ucosm"); + const beneficiaryAddress = makeRandomAddress(); + const memo = "for dinner"; + + // no tokens here + const before = await client.getBalance(beneficiaryAddress, "ucosm"); + expect(before).toBeNull(); + + // send + const result = await client.sendTokens(alice.address0, beneficiaryAddress, transferAmount, memo); + assertIsBroadcastTxSuccess(result); + expect(result.rawLog).toBeTruthy(); + + // got tokens + const after = await client.getBalance(beneficiaryAddress, "ucosm"); + assert(after); + expect(after).toEqual(transferAmount[0]); + }); }); describe("signAndBroadcast", () => { @@ -506,5 +556,188 @@ describe("SigningCosmWasmClient", () => { expect(tx.authInfo!.fee!.gasLimit!.toNumber()).toEqual(333333); }); }); + + describe("legacy Amino mode", () => { + // NOTE: One custom registry shared between tests + // See https://github.com/protobufjs/protobuf.js#using-decorators + // > Decorated types reside in protobuf.roots["decorated"] using a flat structure, so no duplicate names. + const customRegistry = new Registry(); + const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate"; + + @registered(customRegistry, msgDelegateTypeUrl) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + class CustomMsgDelegate extends Message { + @cosmosField.string(1) + public readonly custom_delegator_address?: string; + @cosmosField.string(2) + public readonly custom_validator_address?: string; + @cosmosField.message(3, Coin) + public readonly custom_amount?: Coin; + } + + it("works with bank MsgSend", async () => { + pendingWithoutWasmd(); + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + + const msgSend: IMsgSend = { + fromAddress: alice.address0, + toAddress: makeRandomAddress(), + amount: coins(1234, "ucosm"), + }; + const msgAny = { + typeUrl: "/cosmos.bank.v1beta1.MsgSend", + value: msgSend, + }; + const fee = { + amount: coins(2000, "ucosm"), + gas: "200000", + }; + const memo = "Use your tokens wisely"; + const result = await client.signAndBroadcast(alice.address0, [msgAny], fee, memo); + assertIsBroadcastTxSuccess(result); + }); + + it("works with staking MsgDelegate", async () => { + pendingWithoutWasmd(); + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + + const msgDelegate: IMsgDelegate = { + delegatorAddress: alice.address0, + validatorAddress: validator.validatorAddress, + amount: coin(1234, "ustake"), + }; + const msgAny = { + typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", + value: msgDelegate, + }; + const fee = { + amount: coins(2000, "ustake"), + gas: "200000", + }; + const memo = "Use your tokens wisely"; + const result = await client.signAndBroadcast(alice.address0, [msgAny], fee, memo); + assertIsBroadcastTxSuccess(result); + }); + + it("works with wasm MsgStoreCode", async () => { + pendingWithoutWasmd(); + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const { data, builder, source } = getHackatom(); + + const msgStoreCode: IMsgStoreCode = { + sender: alice.address0, + wasmByteCode: pako.gzip(data), + source: source, + builder: builder, + }; + const msgAny = { + typeUrl: "/cosmwasm.wasm.v1beta1.MsgStoreCode", + value: msgStoreCode, + }; + const fee = { + amount: coins(2000, "ustake"), + gas: "1500000", + }; + const memo = "Use your tokens wisely"; + const result = await client.signAndBroadcast(alice.address0, [msgAny], fee, memo); + assertIsBroadcastTxSuccess(result); + }); + + it("works with a custom registry and custom message", async () => { + pendingWithoutWasmd(); + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); + const customAminoTypes = new AminoTypes({ + additions: { + "/cosmos.staking.v1beta1.MsgDelegate": { + aminoType: "cosmos-sdk/MsgDelegate", + toAmino: ({ + custom_delegator_address, + custom_validator_address, + custom_amount, + }: CustomMsgDelegate): LaunchpadMsgDelegate["value"] => { + assert(custom_delegator_address, "missing custom_delegator_address"); + assert(custom_validator_address, "missing validator_address"); + assert(custom_amount, "missing amount"); + assert(custom_amount.amount, "missing amount.amount"); + assert(custom_amount.denom, "missing amount.denom"); + return { + delegator_address: custom_delegator_address, + validator_address: custom_validator_address, + amount: { + amount: custom_amount.amount, + denom: custom_amount.denom, + }, + }; + }, + fromAmino: ({ + delegator_address, + validator_address, + amount, + }: LaunchpadMsgDelegate["value"]): CustomMsgDelegate => + CustomMsgDelegate.create({ + custom_delegator_address: delegator_address, + custom_validator_address: validator_address, + custom_amount: Coin.create(amount), + }), + }, + }, + }); + const options = { registry: customRegistry, aminoTypes: customAminoTypes }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); + + const msg = { + custom_delegator_address: alice.address0, + custom_validator_address: validator.validatorAddress, + custom_amount: coin(1234, "ustake"), + }; + const msgAny = { + typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", + value: msg, + }; + const fee = { + amount: coins(2000, "ucosm"), + gas: "200000", + }; + const memo = "Use your power wisely"; + const result = await client.signAndBroadcast(alice.address0, [msgAny], fee, memo); + assertIsBroadcastTxSuccess(result); + }); + + it("works with a modifying signer", async () => { + pendingWithoutWasmd(); + const wallet = await ModifyingSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, "wasm"); + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + + const msg = { + delegatorAddress: alice.address0, + validatorAddress: validator.validatorAddress, + amount: coin(1234, "ustake"), + }; + const msgAny = { + typeUrl: msgDelegateTypeUrl, + value: msg, + }; + const fee = { + amount: coins(2000, "ucosm"), + gas: "200000", + }; + const memo = "Use your power wisely"; + const result = await client.signAndBroadcast(alice.address0, [msgAny], fee, memo); + assertIsBroadcastTxSuccess(result); + + await sleep(1000); + + const searchResult = await client.getTx(result.transactionHash); + assert(searchResult, "Must find transaction"); + const tx = Tx.decode(searchResult.tx); + // From ModifyingSecp256k1HdWallet + expect(tx.body!.memo).toEqual("This was modified"); + expect({ ...tx.authInfo!.fee!.amount![0] }).toEqual(coin(3000, "ucosm")); + expect(tx.authInfo!.fee!.gasLimit!.toNumber()).toEqual(333333); + }); + }); }); }); diff --git a/packages/cosmwasm-stargate/src/testutils.spec.ts b/packages/cosmwasm-stargate/src/testutils.spec.ts index 5f778c7349..e29980f88d 100644 --- a/packages/cosmwasm-stargate/src/testutils.spec.ts +++ b/packages/cosmwasm-stargate/src/testutils.spec.ts @@ -1,7 +1,13 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { Bip39, EnglishMnemonic, Random, Secp256k1, Slip10, Slip10Curve } from "@cosmjs/crypto"; import { Bech32, fromBase64 } from "@cosmjs/encoding"; -import { coins, makeCosmoshubPath } from "@cosmjs/launchpad"; +import { + AminoSignResponse, + coins, + makeCosmoshubPath, + Secp256k1HdWallet, + StdSignDoc, +} from "@cosmjs/launchpad"; import { DirectSecp256k1HdWallet, DirectSignResponse, makeAuthInfoBytes } from "@cosmjs/proto-signing"; import { AuthExtension, @@ -205,6 +211,41 @@ export async function makeWasmClient( return QueryClient.withExtensions(tmClient, setupAuthExtension, setupBankExtension, setupWasmExtension); } +/** + * A class for testing clients using an Amino signer which modifies the transaction it receives before signing + */ +export class ModifyingSecp256k1HdWallet extends Secp256k1HdWallet { + public static async fromMnemonic( + mnemonic: string, + hdPath = makeCosmoshubPath(0), + prefix = "cosmos", + ): Promise { + const mnemonicChecked = new EnglishMnemonic(mnemonic); + const seed = await Bip39.mnemonicToSeed(mnemonicChecked); + const { privkey } = Slip10.derivePath(Slip10Curve.Secp256k1, seed, hdPath); + const uncompressed = (await Secp256k1.makeKeypair(privkey)).pubkey; + return new ModifyingSecp256k1HdWallet( + mnemonicChecked, + hdPath, + privkey, + Secp256k1.compressPubkey(uncompressed), + prefix, + ); + } + + public async signAmino(signerAddress: string, signDoc: StdSignDoc): Promise { + const modifiedSignDoc = { + ...signDoc, + fee: { + amount: coins(3000, "ucosm"), + gas: "333333", + }, + memo: "This was modified", + }; + return super.signAmino(signerAddress, modifiedSignDoc); + } +} + /** * A class for testing clients using a direct signer which modifies the transaction it receives before signing */ From c613186cbe84154f51d53fa20e9a15346808efc8 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 14:31:58 +0000 Subject: [PATCH 11/18] cosmwasm-stargate: Add prefix to options in SigningCosmWasmClient tests This currently only matters for MsgCreateValidator but this will be useful if people look at the tests to see how to use this class. --- .../src/signingcosmwasmclient.spec.ts | 87 ++++++++++++------- 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts index 8554b04b3f..4c4cb8a369 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts @@ -44,7 +44,8 @@ describe("SigningCosmWasmClient", () => { it("can be constructed", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); expect(client).toBeTruthy(); }); @@ -53,7 +54,7 @@ describe("SigningCosmWasmClient", () => { const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic); const registry = new Registry(); registry.register("/custom.MsgCustom", MsgSend); - const options = { registry: registry }; + const options = { prefix: wasmd.prefix, registry: registry }; const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const openedClient = (client as unknown) as PrivateSigningCosmWasmClient; expect(openedClient.registry.lookupType("/custom.MsgCustom")).toEqual(MsgSend); @@ -62,8 +63,11 @@ describe("SigningCosmWasmClient", () => { it("can be constructed with custom gas price", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const gasPrice = GasPrice.fromString("3.14utest"); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, { gasPrice }); + const options = { + prefix: wasmd.prefix, + gasPrice: GasPrice.fromString("3.14utest"), + }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const openedClient = (client as unknown) as PrivateSigningCosmWasmClient; expect(openedClient.fees).toEqual({ upload: { @@ -96,10 +100,13 @@ describe("SigningCosmWasmClient", () => { it("can be constructed with custom gas limits", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const gasLimits = { - send: 160000, + const options = { + prefix: wasmd.prefix, + gasLimits: { + send: 160000, + }, }; - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, { gasLimits }); + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const openedClient = (client as unknown) as PrivateSigningCosmWasmClient; expect(openedClient.fees).toEqual({ upload: { @@ -132,14 +139,14 @@ describe("SigningCosmWasmClient", () => { it("can be constructed with custom gas price and gas limits", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const gasPrice = GasPrice.fromString("3.14utest"); - const gasLimits = { - send: 160000, + const options = { + prefix: wasmd.prefix, + gasPrice: GasPrice.fromString("3.14utest"), + gasLimits: { + send: 160000, + }, }; - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, { - gasPrice, - gasLimits, - }); + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const openedClient = (client as unknown) as PrivateSigningCosmWasmClient; expect(openedClient.fees).toEqual({ upload: { @@ -174,7 +181,8 @@ describe("SigningCosmWasmClient", () => { it("works", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const wasm = getHackatom().data; const { codeId, @@ -193,7 +201,8 @@ describe("SigningCosmWasmClient", () => { it("can set builder and source", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const hackatom = getHackatom(); const meta: UploadMeta = { source: "https://crates.io/api/v1/crates/cw-nameservice/0.1.0/download", @@ -210,7 +219,8 @@ describe("SigningCosmWasmClient", () => { it("works with transfer amount", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const { codeId } = await client.upload(alice.address0, getHackatom().data); const transferAmount = [coin(1234, "ucosm"), coin(321, "ustake")]; const beneficiaryAddress = makeRandomAddress(); @@ -237,7 +247,8 @@ describe("SigningCosmWasmClient", () => { it("works with admin", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const { codeId } = await client.upload(alice.address0, getHackatom().data); const beneficiaryAddress = makeRandomAddress(); const { contractAddress } = await client.instantiate( @@ -259,7 +270,8 @@ describe("SigningCosmWasmClient", () => { it("can instantiate one code multiple times", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const { codeId } = await client.upload(alice.address0, getHackatom().data); const contractAddress1 = await client.instantiate( alice.address0, @@ -287,7 +299,8 @@ describe("SigningCosmWasmClient", () => { it("can update an admin", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const { codeId } = await client.upload(alice.address0, getHackatom().data); const beneficiaryAddress = makeRandomAddress(); const { contractAddress } = await client.instantiate( @@ -322,7 +335,8 @@ describe("SigningCosmWasmClient", () => { it("can clear an admin", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const { codeId } = await client.upload(alice.address0, getHackatom().data); const beneficiaryAddress = makeRandomAddress(); const { contractAddress } = await client.instantiate( @@ -357,7 +371,8 @@ describe("SigningCosmWasmClient", () => { it("can can migrate from one code ID to another", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const { codeId: codeId1 } = await client.upload(alice.address0, getHackatom().data); const { codeId: codeId2 } = await client.upload(alice.address0, getHackatom().data); const beneficiaryAddress = makeRandomAddress(); @@ -397,7 +412,8 @@ describe("SigningCosmWasmClient", () => { it("works", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const { codeId } = await client.upload(alice.address0, getHackatom().data); // instantiate const transferAmount = [coin(233444, "ucosm"), coin(5454, "ustake")]; @@ -440,7 +456,8 @@ describe("SigningCosmWasmClient", () => { it("works with direct signer", async () => { pendingWithoutWasmd(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const transferAmount = coins(7890, "ucosm"); const beneficiaryAddress = makeRandomAddress(); @@ -464,7 +481,8 @@ describe("SigningCosmWasmClient", () => { it("works with legacy Amino signer", async () => { pendingWithoutWasmd(); const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const transferAmount = coins(7890, "ucosm"); const beneficiaryAddress = makeRandomAddress(); @@ -494,7 +512,7 @@ describe("SigningCosmWasmClient", () => { const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate"; const registry = new Registry(); registry.register(msgDelegateTypeUrl, MsgDelegate); - const options = { registry: registry }; + const options = { prefix: wasmd.prefix, registry: registry }; const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const msg = MsgDelegate.create({ @@ -525,7 +543,7 @@ describe("SigningCosmWasmClient", () => { const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate"; const registry = new Registry(); registry.register(msgDelegateTypeUrl, MsgDelegate); - const options = { registry: registry }; + const options = { prefix: wasmd.prefix, registry: registry }; const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const msg = MsgDelegate.create({ @@ -578,7 +596,8 @@ describe("SigningCosmWasmClient", () => { it("works with bank MsgSend", async () => { pendingWithoutWasmd(); const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const msgSend: IMsgSend = { fromAddress: alice.address0, @@ -601,7 +620,8 @@ describe("SigningCosmWasmClient", () => { it("works with staking MsgDelegate", async () => { pendingWithoutWasmd(); const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const msgDelegate: IMsgDelegate = { delegatorAddress: alice.address0, @@ -624,7 +644,8 @@ describe("SigningCosmWasmClient", () => { it("works with wasm MsgStoreCode", async () => { pendingWithoutWasmd(); const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const { data, builder, source } = getHackatom(); const msgStoreCode: IMsgStoreCode = { @@ -650,6 +671,7 @@ describe("SigningCosmWasmClient", () => { pendingWithoutWasmd(); const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix); const customAminoTypes = new AminoTypes({ + prefix: wasmd.prefix, additions: { "/cosmos.staking.v1beta1.MsgDelegate": { aminoType: "cosmos-sdk/MsgDelegate", @@ -685,7 +707,7 @@ describe("SigningCosmWasmClient", () => { }, }, }); - const options = { registry: customRegistry, aminoTypes: customAminoTypes }; + const options = { prefix: wasmd.prefix, registry: customRegistry, aminoTypes: customAminoTypes }; const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const msg = { @@ -709,7 +731,8 @@ describe("SigningCosmWasmClient", () => { it("works with a modifying signer", async () => { pendingWithoutWasmd(); const wallet = await ModifyingSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, "wasm"); - const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet); + const options = { prefix: wasmd.prefix }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const msg = { delegatorAddress: alice.address0, From 9e2f2d31689243264d277cfc0eddba298e6bf11d Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 16:09:59 +0000 Subject: [PATCH 12/18] cosmwasm-stargate: Update for review comments --- .../cosmwasm-stargate/src/aminotypes.spec.ts | 40 +++++++++---------- packages/cosmwasm-stargate/src/aminotypes.ts | 16 ++++---- .../src/signingcosmwasmclient.ts | 5 +-- .../cosmwasm-stargate/types/aminotypes.d.ts | 2 +- 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/packages/cosmwasm-stargate/src/aminotypes.spec.ts b/packages/cosmwasm-stargate/src/aminotypes.spec.ts index 4190162ffb..38b7c56327 100644 --- a/packages/cosmwasm-stargate/src/aminotypes.spec.ts +++ b/packages/cosmwasm-stargate/src/aminotypes.spec.ts @@ -7,12 +7,12 @@ import { MsgStoreCode, MsgUpdateAdmin, } from "@cosmjs/cosmwasm-launchpad"; -import { fromBase64, toAscii } from "@cosmjs/encoding"; +import { fromBase64, toUtf8 } from "@cosmjs/encoding"; import { coins } from "@cosmjs/launchpad"; import { AminoTypes } from "@cosmjs/stargate"; import Long from "long"; -import { defaultTypes } from "./aminotypes"; +import { cosmWasmTypes } from "./aminotypes"; import { cosmwasm } from "./codec"; type IMsgStoreCode = cosmwasm.wasm.v1beta1.IMsgStoreCode; @@ -31,7 +31,7 @@ describe("AminoTypes", () => { source: "Arrabiata", builder: "Bob", }; - const aminoMsg = new AminoTypes({ additions: defaultTypes }).toAmino({ + const aminoMsg = new AminoTypes({ additions: cosmWasmTypes }).toAmino({ typeUrl: "/cosmwasm.wasm.v1beta1.MsgStoreCode", value: msg, }); @@ -52,7 +52,7 @@ describe("AminoTypes", () => { sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", codeId: Long.fromString("12345"), label: "sticky", - initMsg: toAscii( + initMsg: toUtf8( JSON.stringify({ foo: "bar", }), @@ -60,7 +60,7 @@ describe("AminoTypes", () => { initFunds: coins(1234, "ucosm"), admin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", }; - const aminoMsg = new AminoTypes({ additions: defaultTypes }).toAmino({ + const aminoMsg = new AminoTypes({ additions: cosmWasmTypes }).toAmino({ typeUrl: "/cosmwasm.wasm.v1beta1.MsgInstantiateContract", value: msg, }); @@ -86,7 +86,7 @@ describe("AminoTypes", () => { newAdmin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", }; - const aminoMsg = new AminoTypes({ additions: defaultTypes }).toAmino({ + const aminoMsg = new AminoTypes({ additions: cosmWasmTypes }).toAmino({ typeUrl: "/cosmwasm.wasm.v1beta1.MsgUpdateAdmin", value: msg, }); @@ -106,7 +106,7 @@ describe("AminoTypes", () => { sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", }; - const aminoMsg = new AminoTypes({ additions: defaultTypes }).toAmino({ + const aminoMsg = new AminoTypes({ additions: cosmWasmTypes }).toAmino({ typeUrl: "/cosmwasm.wasm.v1beta1.MsgClearAdmin", value: msg, }); @@ -124,14 +124,14 @@ describe("AminoTypes", () => { const msg: IMsgExecuteContract = { sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", - msg: toAscii( + msg: toUtf8( JSON.stringify({ foo: "bar", }), ), sentFunds: coins(1234, "ucosm"), }; - const aminoMsg = new AminoTypes({ additions: defaultTypes }).toAmino({ + const aminoMsg = new AminoTypes({ additions: cosmWasmTypes }).toAmino({ typeUrl: "/cosmwasm.wasm.v1beta1.MsgExecuteContract", value: msg, }); @@ -154,13 +154,13 @@ describe("AminoTypes", () => { sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", codeId: Long.fromString("98765"), - migrateMsg: toAscii( + migrateMsg: toUtf8( JSON.stringify({ foo: "bar", }), ), }; - const aminoMsg = new AminoTypes({ additions: defaultTypes }).toAmino({ + const aminoMsg = new AminoTypes({ additions: cosmWasmTypes }).toAmino({ typeUrl: "/cosmwasm.wasm.v1beta1.MsgMigrateContract", value: msg, }); @@ -190,7 +190,7 @@ describe("AminoTypes", () => { builder: "Bob", }, }; - const msg = new AminoTypes({ additions: defaultTypes }).fromAmino(aminoMsg); + const msg = new AminoTypes({ additions: cosmWasmTypes }).fromAmino(aminoMsg); const expectedValue: IMsgStoreCode = { sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", wasmByteCode: fromBase64("WUVMTE9XIFNVQk1BUklORQ=="), @@ -217,12 +217,12 @@ describe("AminoTypes", () => { admin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", }, }; - const msg = new AminoTypes({ additions: defaultTypes }).fromAmino(aminoMsg); + const msg = new AminoTypes({ additions: cosmWasmTypes }).fromAmino(aminoMsg); const expectedValue: IMsgInstantiateContract = { sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", codeId: Long.fromString("12345"), label: "sticky", - initMsg: toAscii( + initMsg: toUtf8( JSON.stringify({ foo: "bar", }), @@ -245,7 +245,7 @@ describe("AminoTypes", () => { contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", }, }; - const msg = new AminoTypes({ additions: defaultTypes }).fromAmino(aminoMsg); + const msg = new AminoTypes({ additions: cosmWasmTypes }).fromAmino(aminoMsg); const expectedValue: IMsgUpdateAdmin = { sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", newAdmin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", @@ -265,7 +265,7 @@ describe("AminoTypes", () => { contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", }, }; - const msg = new AminoTypes({ additions: defaultTypes }).fromAmino(aminoMsg); + const msg = new AminoTypes({ additions: cosmWasmTypes }).fromAmino(aminoMsg); const expectedValue: IMsgClearAdmin = { sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", @@ -288,11 +288,11 @@ describe("AminoTypes", () => { sent_funds: coins(1234, "ucosm"), }, }; - const msg = new AminoTypes({ additions: defaultTypes }).fromAmino(aminoMsg); + const msg = new AminoTypes({ additions: cosmWasmTypes }).fromAmino(aminoMsg); const expectedValue: IMsgExecuteContract = { sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", - msg: toAscii( + msg: toUtf8( JSON.stringify({ foo: "bar", }), @@ -317,12 +317,12 @@ describe("AminoTypes", () => { }, }, }; - const msg = new AminoTypes({ additions: defaultTypes }).fromAmino(aminoMsg); + const msg = new AminoTypes({ additions: cosmWasmTypes }).fromAmino(aminoMsg); const expectedValue: IMsgMigrateContract = { sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", codeId: Long.fromString("98765"), - migrateMsg: toAscii( + migrateMsg: toUtf8( JSON.stringify({ foo: "bar", }), diff --git a/packages/cosmwasm-stargate/src/aminotypes.ts b/packages/cosmwasm-stargate/src/aminotypes.ts index 1d9a47b609..b9c663fb18 100644 --- a/packages/cosmwasm-stargate/src/aminotypes.ts +++ b/packages/cosmwasm-stargate/src/aminotypes.ts @@ -7,7 +7,7 @@ import { MsgStoreCode, MsgUpdateAdmin, } from "@cosmjs/cosmwasm-launchpad"; -import { fromAscii, fromBase64, toAscii, toBase64 } from "@cosmjs/encoding"; +import { fromBase64, fromUtf8, toBase64, toUtf8 } from "@cosmjs/encoding"; import { Coin } from "@cosmjs/launchpad"; import { AminoConverter, codec } from "@cosmjs/stargate"; import { assert } from "@cosmjs/utils"; @@ -35,7 +35,7 @@ function checkAmount(amount: readonly ICoin[] | undefined | null): readonly Coin }); } -export const defaultTypes: Record = { +export const cosmWasmTypes: Record = { "/cosmwasm.wasm.v1beta1.MsgStoreCode": { aminoType: "wasm/MsgStoreCode", toAmino: ({ sender, wasmByteCode, source, builder }: IMsgStoreCode): MsgStoreCode["value"] => { @@ -75,7 +75,7 @@ export const defaultTypes: Record = { sender: sender, code_id: codeId.toString(), label: label, - init_msg: JSON.parse(fromAscii(initMsg)), + init_msg: JSON.parse(fromUtf8(initMsg)), init_funds: checkAmount(initFunds), admin: admin ?? undefined, }; @@ -91,7 +91,7 @@ export const defaultTypes: Record = { sender: sender, codeId: Long.fromString(code_id), label: label, - initMsg: toAscii(JSON.stringify(init_msg)), + initMsg: toUtf8(JSON.stringify(init_msg)), initFunds: [...init_funds], admin: admin, }), @@ -138,14 +138,14 @@ export const defaultTypes: Record = { return { sender: sender, contract: contract, - msg: JSON.parse(fromAscii(msg)), + msg: JSON.parse(fromUtf8(msg)), sent_funds: checkAmount(sentFunds), }; }, fromAmino: ({ sender, contract, msg, sent_funds }: MsgExecuteContract["value"]): IMsgExecuteContract => ({ sender: sender, contract: contract, - msg: toAscii(JSON.stringify(msg)), + msg: toUtf8(JSON.stringify(msg)), sentFunds: [...sent_funds], }), }, @@ -160,14 +160,14 @@ export const defaultTypes: Record = { sender: sender, contract: contract, code_id: codeId.toString(), - msg: JSON.parse(fromAscii(migrateMsg)), + msg: JSON.parse(fromUtf8(migrateMsg)), }; }, fromAmino: ({ sender, contract, code_id, msg }: MsgMigrateContract["value"]): IMsgMigrateContract => ({ sender: sender, contract: contract, codeId: Long.fromString(code_id), - migrateMsg: toAscii(JSON.stringify(msg)), + migrateMsg: toUtf8(JSON.stringify(msg)), }), }, }; diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts index 833ec27548..1d23f12c1f 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts @@ -46,7 +46,7 @@ import { adaptor34, Client as TendermintClient } from "@cosmjs/tendermint-rpc"; import Long from "long"; import pako from "pako"; -import { defaultTypes } from "./aminotypes"; +import { cosmWasmTypes } from "./aminotypes"; import { cosmwasm } from "./codec"; import { CosmWasmClient } from "./cosmwasmclient"; @@ -146,7 +146,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { super(tmClient); const { registry = createDefaultRegistry(), - aminoTypes = new AminoTypes({ additions: defaultTypes, prefix: options.prefix }), + aminoTypes = new AminoTypes({ additions: cosmWasmTypes, prefix: options.prefix }), gasPrice = defaultGasPrice, gasLimits = defaultGasLimits, } = options; @@ -392,7 +392,6 @@ export class SigningCosmWasmClient extends CosmWasmClient { const signedTx = Uint8Array.from(TxRaw.encode(txRaw).finish()); return this.broadcastTx(signedTx); } - // Amino signer // Amino signer const signMode = SignMode.SIGN_MODE_LEGACY_AMINO_JSON; diff --git a/packages/cosmwasm-stargate/types/aminotypes.d.ts b/packages/cosmwasm-stargate/types/aminotypes.d.ts index 7febac2086..413e64a244 100644 --- a/packages/cosmwasm-stargate/types/aminotypes.d.ts +++ b/packages/cosmwasm-stargate/types/aminotypes.d.ts @@ -1,2 +1,2 @@ import { AminoConverter } from "@cosmjs/stargate"; -export declare const defaultTypes: Record; +export declare const cosmWasmTypes: Record; From 76a3f5535066145ef71649c23dbb70c5efebb39e Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 16:17:56 +0000 Subject: [PATCH 13/18] utils: Replace assertDefined with assertDefinedAndNotNull --- packages/utils/src/assert.spec.ts | 42 ++++++++++++++++--------------- packages/utils/src/assert.ts | 6 ++--- packages/utils/src/index.ts | 2 +- packages/utils/types/assert.d.ts | 5 +++- packages/utils/types/index.d.ts | 2 +- 5 files changed, 31 insertions(+), 26 deletions(-) diff --git a/packages/utils/src/assert.spec.ts b/packages/utils/src/assert.spec.ts index 4b6374cfc0..d22b5c16cd 100644 --- a/packages/utils/src/assert.spec.ts +++ b/packages/utils/src/assert.spec.ts @@ -1,52 +1,54 @@ -import { assertDefined } from "./assert"; +import { assertDefinedAndNotNull } from "./assert"; describe("assert", () => { - describe("assertDefined", () => { + describe("assertDefinedAndNotNull", () => { it("passes for simple values", () => { { - const value: number | undefined = 123; - assertDefined(value); + const value: number | undefined | null = 123; + assertDefinedAndNotNull(value); expect(value).toEqual(123); } { - const value: string | undefined = "abc"; - assertDefined(value); + const value: string | undefined | null = "abc"; + assertDefinedAndNotNull(value); expect(value).toEqual("abc"); } }); it("passes for falsy values", () => { { - const value: number | undefined = 0; - assertDefined(value); + const value: number | undefined | null = 0; + assertDefinedAndNotNull(value); expect(value).toEqual(0); } { - const value: string | undefined = ""; - assertDefined(value); + const value: string | undefined | null = ""; + assertDefinedAndNotNull(value); expect(value).toEqual(""); } - { - const value: null | undefined = null; - assertDefined(value); - expect(value).toEqual(null); - } }); it("throws for undefined values", () => { { - const value: number | undefined = undefined; - expect(() => assertDefined(value)).toThrowError("value is undefined"); + const value: number | undefined | null = undefined; + expect(() => assertDefinedAndNotNull(value)).toThrowError("value is undefined or null"); } { - let value: string | undefined; - expect(() => assertDefined(value)).toThrowError("value is undefined"); + let value: string | undefined | null; + expect(() => assertDefinedAndNotNull(value)).toThrowError("value is undefined or null"); } }); + it("throws for null values", () => { + const value: number | undefined | null = null; + expect(() => assertDefinedAndNotNull(value)).toThrowError("value is undefined or null"); + }); + it("throws with custom message", () => { const value: number | undefined = undefined; - expect(() => assertDefined(value, "Bug in the data source")).toThrowError("Bug in the data source"); + expect(() => assertDefinedAndNotNull(value, "Bug in the data source")).toThrowError( + "Bug in the data source", + ); }); }); }); diff --git a/packages/utils/src/assert.ts b/packages/utils/src/assert.ts index 3c86b35378..14f37517ca 100644 --- a/packages/utils/src/assert.ts +++ b/packages/utils/src/assert.ts @@ -5,8 +5,8 @@ export function assert(condition: any, msg?: string): asserts condition { } } -export function assertDefined(value: T | undefined, msg?: string): asserts value is T { - if (value === undefined) { - throw new Error(msg || "value is undefined"); +export function assertDefinedAndNotNull(value: T | undefined | null, msg?: string): asserts value is T { + if (value === undefined || value === null) { + throw new Error(msg ?? "value is undefined or null"); } } diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 391b2471c2..596d63db78 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,4 +1,4 @@ export { arrayContentEquals } from "./arrays"; -export { assert, assertDefined } from "./assert"; +export { assert, assertDefinedAndNotNull } from "./assert"; export { sleep } from "./sleep"; export { isNonNullObject, isUint8Array } from "./typechecks"; diff --git a/packages/utils/types/assert.d.ts b/packages/utils/types/assert.d.ts index 385fadf14f..65f3dfdb86 100644 --- a/packages/utils/types/assert.d.ts +++ b/packages/utils/types/assert.d.ts @@ -1,2 +1,5 @@ export declare function assert(condition: any, msg?: string): asserts condition; -export declare function assertDefined(value: T | undefined, msg?: string): asserts value is T; +export declare function assertDefinedAndNotNull( + value: T | undefined | null, + msg?: string, +): asserts value is T; diff --git a/packages/utils/types/index.d.ts b/packages/utils/types/index.d.ts index 391b2471c2..596d63db78 100644 --- a/packages/utils/types/index.d.ts +++ b/packages/utils/types/index.d.ts @@ -1,4 +1,4 @@ export { arrayContentEquals } from "./arrays"; -export { assert, assertDefined } from "./assert"; +export { assert, assertDefinedAndNotNull } from "./assert"; export { sleep } from "./sleep"; export { isNonNullObject, isUint8Array } from "./typechecks"; From dc9b8370b27be47f71e2a5409ebca8d1c705cd64 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 16:20:20 +0000 Subject: [PATCH 14/18] root: Update CHANGELOG for utils assertDefinedAndNotNull change --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a7e13fb66..9b5aa82bc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,14 @@ - @cosmjs/tendermint-rpc: Remove types `BlockHash`, `TxBytes` and `TxHash`. Use `Uint8Array` instead. +### Added + +- @cosmjs/utils: Added `assertDefinedAndNotNull`. + +### Removed + +- @cosmjs/utils: `assertDefined` removed in favour of `assertDefinedAndNotNull`. + ## 0.23.2 (2021-01-06) ### Security From b967327c678c51095edbe50a22907b74962d1201 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 16:34:20 +0000 Subject: [PATCH 15/18] stargate: Update to assertDefinedAndNotNull --- packages/stargate/src/queries/queryclient.ts | 4 ++-- packages/stargate/src/stargateclient.ts | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/stargate/src/queries/queryclient.ts b/packages/stargate/src/queries/queryclient.ts index 31cf9ef1e6..f9d636cd2c 100644 --- a/packages/stargate/src/queries/queryclient.ts +++ b/packages/stargate/src/queries/queryclient.ts @@ -3,7 +3,7 @@ import { iavlSpec, ics23, tendermintSpec, verifyExistence, verifyNonExistence } import { toAscii, toHex } from "@cosmjs/encoding"; import { firstEvent } from "@cosmjs/stream"; import { Client as TendermintClient, Header, NewBlockHeaderEvent, ProofOp } from "@cosmjs/tendermint-rpc"; -import { arrayContentEquals, assert, assertDefined, isNonNullObject, sleep } from "@cosmjs/utils"; +import { arrayContentEquals, assert, assertDefinedAndNotNull, isNonNullObject, sleep } from "@cosmjs/utils"; import { Stream } from "xstream"; type QueryExtensionSetup

= (base: QueryClient) => P; @@ -233,7 +233,7 @@ export class QueryClient { // this must return the header for height+1 // throws an error if height is 0 or undefined private async getNextHeader(height?: number): Promise

{ - assertDefined(height); + assertDefinedAndNotNull(height); if (height === 0) { throw new Error("Query returned height 0, cannot prove it"); } diff --git a/packages/stargate/src/stargateclient.ts b/packages/stargate/src/stargateclient.ts index 9d2304bf2a..488ff28569 100644 --- a/packages/stargate/src/stargateclient.ts +++ b/packages/stargate/src/stargateclient.ts @@ -18,7 +18,7 @@ import { Client as TendermintClient, DateTime, } from "@cosmjs/tendermint-rpc"; -import { assert, assertDefined } from "@cosmjs/utils"; +import { assert, assertDefinedAndNotNull } from "@cosmjs/utils"; import Long from "long"; import { cosmos } from "./codec"; @@ -110,10 +110,8 @@ export function accountFromProto(input: IBaseAccount): Account { } export function coinFromProto(input: ICoin): Coin { - assertDefined(input.amount); - assertDefined(input.denom); - assert(input.amount !== null); - assert(input.denom !== null); + assertDefinedAndNotNull(input.amount); + assertDefinedAndNotNull(input.denom); return { amount: input.amount, denom: input.denom, From 4624e68590b8b1346f995f5ef6af4dc35ddbc8e0 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 16:34:37 +0000 Subject: [PATCH 16/18] cosmwasm-stargate: Update to assertDefinedAndNotNull --- packages/cosmwasm-stargate/src/queries/wasm.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cosmwasm-stargate/src/queries/wasm.spec.ts b/packages/cosmwasm-stargate/src/queries/wasm.spec.ts index 78b7897f67..48363469b3 100644 --- a/packages/cosmwasm-stargate/src/queries/wasm.spec.ts +++ b/packages/cosmwasm-stargate/src/queries/wasm.spec.ts @@ -9,7 +9,7 @@ import { parseRawLog, SigningStargateClient, } from "@cosmjs/stargate"; -import { assert, assertDefined } from "@cosmjs/utils"; +import { assert, assertDefinedAndNotNull } from "@cosmjs/utils"; import Long from "long"; import { cosmwasm } from "../codec"; @@ -395,7 +395,7 @@ describe("WasmExtension", () => { expect(codeId).toBeGreaterThanOrEqual(1); expect(codeId).toBeLessThanOrEqual(200); - assertDefined(result.data); + assertDefinedAndNotNull(result.data); const msgData = fromOneElementArray(result.data); expect(msgData.msgType).toEqual("store-code"); expect(MsgStoreCodeResponse.decode(msgData.data!)).toEqual( @@ -415,7 +415,7 @@ describe("WasmExtension", () => { const amountAttr = logs.findAttribute(parsedLogs, "transfer", "amount"); expect(amountAttr.value).toEqual("1234ucosm,321ustake"); - assertDefined(result.data); + assertDefinedAndNotNull(result.data); const msgData = fromOneElementArray(result.data); expect(msgData.msgType).toEqual("instantiate"); expect(MsgInstantiateContractResponse.decode(msgData.data!)).toEqual( @@ -441,7 +441,7 @@ describe("WasmExtension", () => { value: beneficiaryAddress, }); - assertDefined(result.data); + assertDefinedAndNotNull(result.data); const msgData = fromOneElementArray(result.data); expect(msgData.msgType).toEqual("execute"); expect(MsgExecuteContractResponse.decode(msgData.data!)).toEqual( From 6433173303a6c3ff8452eca85c37f1a72656a034 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 16:41:51 +0000 Subject: [PATCH 17/18] stargate: Simplify Amino type checks --- packages/stargate/src/aminotypes.ts | 136 +++++++++++----------------- 1 file changed, 53 insertions(+), 83 deletions(-) diff --git a/packages/stargate/src/aminotypes.ts b/packages/stargate/src/aminotypes.ts index d4a7e19408..8d424d72ad 100644 --- a/packages/stargate/src/aminotypes.ts +++ b/packages/stargate/src/aminotypes.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { fromBase64, toBase64 } from "@cosmjs/encoding"; import { - Coin, decodeBech32Pubkey, encodeBech32Pubkey, Msg, @@ -14,11 +13,11 @@ import { MsgUndelegate, } from "@cosmjs/launchpad"; import { EncodeObject } from "@cosmjs/proto-signing"; -import { assert } from "@cosmjs/utils"; +import { assertDefinedAndNotNull } from "@cosmjs/utils"; import { cosmos } from "./codec"; +import { coinFromProto } from "./stargateclient"; -type ICoin = cosmos.base.v1beta1.ICoin; type IMsgSend = cosmos.bank.v1beta1.IMsgSend; type IMsgMultiSend = cosmos.bank.v1beta1.IMsgMultiSend; type IMsgBeginRedelegate = cosmos.staking.v1beta1.IMsgBeginRedelegate; @@ -33,29 +32,18 @@ export interface AminoConverter { readonly fromAmino: (value: any) => any; } -function checkAmount(amount: readonly ICoin[] | undefined | null): readonly Coin[] { - assert(amount, "missing amount"); - return amount.map((a) => { - assert(a.amount, "missing amount"); - assert(a.denom, "missing denom"); - return { - amount: a.amount, - denom: a.denom, - }; - }); -} - function createDefaultTypes(prefix: string): Record { return { "/cosmos.bank.v1beta1.MsgSend": { aminoType: "cosmos-sdk/MsgSend", toAmino: ({ fromAddress, toAddress, amount }: IMsgSend): MsgSend["value"] => { - assert(fromAddress, "missing fromAddress"); - assert(toAddress, "missing toAddress"); + assertDefinedAndNotNull(fromAddress, "missing fromAddress"); + assertDefinedAndNotNull(toAddress, "missing toAddress"); + assertDefinedAndNotNull(amount, "missing amount"); return { from_address: fromAddress, to_address: toAddress, - amount: checkAmount(amount), + amount: amount.map(coinFromProto), }; }, fromAmino: ({ from_address, to_address, amount }: MsgSend["value"]): IMsgSend => ({ @@ -67,21 +55,23 @@ function createDefaultTypes(prefix: string): Record { "/cosmos.bank.v1beta1.MsgMultiSend": { aminoType: "cosmos-sdk/MsgMultiSend", toAmino: ({ inputs, outputs }: IMsgMultiSend): MsgMultiSend["value"] => { - assert(inputs, "missing inputs"); - assert(outputs, "missing outputs"); + assertDefinedAndNotNull(inputs, "missing inputs"); + assertDefinedAndNotNull(outputs, "missing outputs"); return { inputs: inputs.map((input) => { - assert(input.address, "missing input.address"); + assertDefinedAndNotNull(input.address, "missing input.address"); + assertDefinedAndNotNull(input.coins, "missing input.amount"); return { address: input.address, - coins: checkAmount(input.coins), + coins: input.coins.map(coinFromProto), }; }), outputs: outputs.map((output) => { - assert(output.address, "missing output.address"); + assertDefinedAndNotNull(output.address, "missing output.address"); + assertDefinedAndNotNull(output.coins, "missing output.coins"); return { address: output.address, - coins: checkAmount(output.coins), + coins: output.coins.map(coinFromProto), }; }), }; @@ -105,20 +95,15 @@ function createDefaultTypes(prefix: string): Record { validatorDstAddress, amount, }: IMsgBeginRedelegate): MsgBeginRedelegate["value"] => { - assert(delegatorAddress, "missing delegatorAddress"); - assert(validatorSrcAddress, "missing validatorSrcAddress"); - assert(validatorDstAddress, "missing validatorDstAddress"); - assert(amount, "missing amount"); - assert(amount.amount, "missing amount.amount"); - assert(amount.denom, "missing amount.denom"); + assertDefinedAndNotNull(delegatorAddress, "missing delegatorAddress"); + assertDefinedAndNotNull(validatorSrcAddress, "missing validatorSrcAddress"); + assertDefinedAndNotNull(validatorDstAddress, "missing validatorDstAddress"); + assertDefinedAndNotNull(amount, "missing amount"); return { delegator_address: delegatorAddress, validator_src_address: validatorSrcAddress, validator_dst_address: validatorDstAddress, - amount: { - amount: amount.amount, - denom: amount.denom, - }, + amount: coinFromProto(amount), }; }, fromAmino: ({ @@ -144,24 +129,22 @@ function createDefaultTypes(prefix: string): Record { pubkey, value, }: IMsgCreateValidator): MsgCreateValidator["value"] => { - assert(description, "missing description"); - assert(description.moniker, "missing description.moniker"); - assert(description.identity, "missing description.identity"); - assert(description.website, "missing description.website"); - assert(description.securityContact, "missing description.securityContact"); - assert(description.details, "missing description.details"); - assert(commission, "missing commission"); - assert(commission.rate, "missing commission.rate"); - assert(commission.maxRate, "missing commission.maxRate"); - assert(commission.maxChangeRate, "missing commission.maxChangeRate"); - assert(minSelfDelegation, "missing minSelfDelegation"); - assert(delegatorAddress, "missing delegatorAddress"); - assert(validatorAddress, "missing validatorAddress"); - assert(pubkey, "missing pubkey"); - assert(pubkey.value, "missing pubkey.value"); - assert(value, "missing value"); - assert(value.amount, "missing value.amount"); - assert(value.denom, "missing value.denom"); + assertDefinedAndNotNull(description, "missing description"); + assertDefinedAndNotNull(description.moniker, "missing description.moniker"); + assertDefinedAndNotNull(description.identity, "missing description.identity"); + assertDefinedAndNotNull(description.website, "missing description.website"); + assertDefinedAndNotNull(description.securityContact, "missing description.securityContact"); + assertDefinedAndNotNull(description.details, "missing description.details"); + assertDefinedAndNotNull(commission, "missing commission"); + assertDefinedAndNotNull(commission.rate, "missing commission.rate"); + assertDefinedAndNotNull(commission.maxRate, "missing commission.maxRate"); + assertDefinedAndNotNull(commission.maxChangeRate, "missing commission.maxChangeRate"); + assertDefinedAndNotNull(minSelfDelegation, "missing minSelfDelegation"); + assertDefinedAndNotNull(delegatorAddress, "missing delegatorAddress"); + assertDefinedAndNotNull(validatorAddress, "missing validatorAddress"); + assertDefinedAndNotNull(pubkey, "missing pubkey"); + assertDefinedAndNotNull(pubkey.value, "missing pubkey.value"); + assertDefinedAndNotNull(value, "missing value"); return { description: { moniker: description.moniker, @@ -185,10 +168,7 @@ function createDefaultTypes(prefix: string): Record { }, prefix, ), - value: { - amount: value.amount, - denom: value.denom, - }, + value: coinFromProto(value), }; }, fromAmino: ({ @@ -231,18 +211,13 @@ function createDefaultTypes(prefix: string): Record { "/cosmos.staking.v1beta1.MsgDelegate": { aminoType: "cosmos-sdk/MsgDelegate", toAmino: ({ delegatorAddress, validatorAddress, amount }: IMsgDelegate): MsgDelegate["value"] => { - assert(delegatorAddress, "missing delegatorAddress"); - assert(validatorAddress, "missing validatorAddress"); - assert(amount, "missing amount"); - assert(amount.amount, "missing amount.amount"); - assert(amount.denom, "missing amount.denom"); + assertDefinedAndNotNull(delegatorAddress, "missing delegatorAddress"); + assertDefinedAndNotNull(validatorAddress, "missing validatorAddress"); + assertDefinedAndNotNull(amount, "missing amount"); return { delegator_address: delegatorAddress, validator_address: validatorAddress, - amount: { - amount: amount.amount, - denom: amount.denom, - }, + amount: coinFromProto(amount), }; }, fromAmino: ({ delegator_address, validator_address, amount }: MsgDelegate["value"]): IMsgDelegate => ({ @@ -259,15 +234,15 @@ function createDefaultTypes(prefix: string): Record { minSelfDelegation, validatorAddress, }: IMsgEditValidator): MsgEditValidator["value"] => { - assert(description, "missing description"); - assert(description.moniker, "missing description.moniker"); - assert(description.identity, "missing description.identity"); - assert(description.website, "missing description.website"); - assert(description.securityContact, "missing description.securityContact"); - assert(description.details, "missing description.details"); - assert(commissionRate, "missing commissionRate"); - assert(minSelfDelegation, "missing minSelfDelegation"); - assert(validatorAddress, "missing validatorAddress"); + assertDefinedAndNotNull(description, "missing description"); + assertDefinedAndNotNull(description.moniker, "missing description.moniker"); + assertDefinedAndNotNull(description.identity, "missing description.identity"); + assertDefinedAndNotNull(description.website, "missing description.website"); + assertDefinedAndNotNull(description.securityContact, "missing description.securityContact"); + assertDefinedAndNotNull(description.details, "missing description.details"); + assertDefinedAndNotNull(commissionRate, "missing commissionRate"); + assertDefinedAndNotNull(minSelfDelegation, "missing minSelfDelegation"); + assertDefinedAndNotNull(validatorAddress, "missing validatorAddress"); return { description: { moniker: description.moniker, @@ -302,18 +277,13 @@ function createDefaultTypes(prefix: string): Record { "/cosmos.staking.v1beta1.MsgUndelegate": { aminoType: "cosmos-sdk/MsgUndelegate", toAmino: ({ delegatorAddress, validatorAddress, amount }: IMsgUndelegate): MsgUndelegate["value"] => { - assert(delegatorAddress, "missing delegatorAddress"); - assert(validatorAddress, "missing validatorAddress"); - assert(amount, "missing amount"); - assert(amount.amount, "missing amount.amount"); - assert(amount.denom, "missing amount.denom"); + assertDefinedAndNotNull(delegatorAddress, "missing delegatorAddress"); + assertDefinedAndNotNull(validatorAddress, "missing validatorAddress"); + assertDefinedAndNotNull(amount, "missing amount"); return { delegator_address: delegatorAddress, validator_address: validatorAddress, - amount: { - amount: amount.amount, - denom: amount.denom, - }, + amount: coinFromProto(amount), }; }, fromAmino: ({ From 69993c1446d8fcab1adc5159def321f5b7612411 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 13 Jan 2021 16:44:47 +0000 Subject: [PATCH 18/18] cosmwasm-stargate: Simplify Amino type checks --- packages/cosmwasm-stargate/src/aminotypes.ts | 64 ++++++++------------ 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/packages/cosmwasm-stargate/src/aminotypes.ts b/packages/cosmwasm-stargate/src/aminotypes.ts index b9c663fb18..968d7260cc 100644 --- a/packages/cosmwasm-stargate/src/aminotypes.ts +++ b/packages/cosmwasm-stargate/src/aminotypes.ts @@ -8,14 +8,12 @@ import { MsgUpdateAdmin, } from "@cosmjs/cosmwasm-launchpad"; import { fromBase64, fromUtf8, toBase64, toUtf8 } from "@cosmjs/encoding"; -import { Coin } from "@cosmjs/launchpad"; -import { AminoConverter, codec } from "@cosmjs/stargate"; -import { assert } from "@cosmjs/utils"; +import { AminoConverter, coinFromProto } from "@cosmjs/stargate"; +import { assertDefinedAndNotNull } from "@cosmjs/utils"; import Long from "long"; import { cosmwasm } from "./codec"; -type ICoin = codec.cosmos.base.v1beta1.ICoin; type IMsgStoreCode = cosmwasm.wasm.v1beta1.IMsgStoreCode; type IMsgInstantiateContract = cosmwasm.wasm.v1beta1.IMsgInstantiateContract; type IMsgUpdateAdmin = cosmwasm.wasm.v1beta1.IMsgUpdateAdmin; @@ -23,26 +21,14 @@ type IMsgClearAdmin = cosmwasm.wasm.v1beta1.IMsgClearAdmin; type IMsgExecuteContract = cosmwasm.wasm.v1beta1.IMsgExecuteContract; type IMsgMigrateContract = cosmwasm.wasm.v1beta1.IMsgMigrateContract; -function checkAmount(amount: readonly ICoin[] | undefined | null): readonly Coin[] { - assert(amount, "missing amount"); - return amount.map((a) => { - assert(a.amount, "missing amount"); - assert(a.denom, "missing denom"); - return { - amount: a.amount, - denom: a.denom, - }; - }); -} - export const cosmWasmTypes: Record = { "/cosmwasm.wasm.v1beta1.MsgStoreCode": { aminoType: "wasm/MsgStoreCode", toAmino: ({ sender, wasmByteCode, source, builder }: IMsgStoreCode): MsgStoreCode["value"] => { - assert(sender, "missing sender"); - assert(wasmByteCode, "missing wasmByteCode"); - assert(typeof source === "string", "missing source"); - assert(typeof builder === "string", "missing builder"); + assertDefinedAndNotNull(sender, "missing sender"); + assertDefinedAndNotNull(wasmByteCode, "missing wasmByteCode"); + assertDefinedAndNotNull(source, "missing source"); + assertDefinedAndNotNull(builder, "missing builder"); return { sender: sender, wasm_byte_code: toBase64(wasmByteCode), @@ -67,16 +53,17 @@ export const cosmWasmTypes: Record = { initFunds, admin, }: IMsgInstantiateContract): MsgInstantiateContract["value"] => { - assert(sender, "missing sender"); - assert(codeId, "missing codeId"); - assert(label, "missing label"); - assert(initMsg, "missing initMsg"); + assertDefinedAndNotNull(sender, "missing sender"); + assertDefinedAndNotNull(codeId, "missing codeId"); + assertDefinedAndNotNull(label, "missing label"); + assertDefinedAndNotNull(initMsg, "missing initMsg"); + assertDefinedAndNotNull(initFunds, "missing initFunds"); return { sender: sender, code_id: codeId.toString(), label: label, init_msg: JSON.parse(fromUtf8(initMsg)), - init_funds: checkAmount(initFunds), + init_funds: initFunds.map(coinFromProto), admin: admin ?? undefined, }; }, @@ -99,9 +86,9 @@ export const cosmWasmTypes: Record = { "/cosmwasm.wasm.v1beta1.MsgUpdateAdmin": { aminoType: "wasm/MsgUpdateAdmin", toAmino: ({ sender, newAdmin, contract }: IMsgUpdateAdmin): MsgUpdateAdmin["value"] => { - assert(sender, "missing sender"); - assert(newAdmin, "missing newAdmin"); - assert(contract, "missing contract"); + assertDefinedAndNotNull(sender, "missing sender"); + assertDefinedAndNotNull(newAdmin, "missing newAdmin"); + assertDefinedAndNotNull(contract, "missing contract"); return { sender: sender, new_admin: newAdmin, @@ -117,8 +104,8 @@ export const cosmWasmTypes: Record = { "/cosmwasm.wasm.v1beta1.MsgClearAdmin": { aminoType: "wasm/MsgClearAdmin", toAmino: ({ sender, contract }: IMsgClearAdmin): MsgClearAdmin["value"] => { - assert(sender, "missing sender"); - assert(contract, "missing contract"); + assertDefinedAndNotNull(sender, "missing sender"); + assertDefinedAndNotNull(contract, "missing contract"); return { sender: sender, contract: contract, @@ -132,14 +119,15 @@ export const cosmWasmTypes: Record = { "/cosmwasm.wasm.v1beta1.MsgExecuteContract": { aminoType: "wasm/MsgExecuteContract", toAmino: ({ sender, contract, msg, sentFunds }: IMsgExecuteContract): MsgExecuteContract["value"] => { - assert(sender, "missing sender"); - assert(contract, "missing contract"); - assert(msg, "missing msg"); + assertDefinedAndNotNull(sender, "missing sender"); + assertDefinedAndNotNull(contract, "missing contract"); + assertDefinedAndNotNull(msg, "missing msg"); + assertDefinedAndNotNull(sentFunds, "missing sentFunds"); return { sender: sender, contract: contract, msg: JSON.parse(fromUtf8(msg)), - sent_funds: checkAmount(sentFunds), + sent_funds: sentFunds.map(coinFromProto), }; }, fromAmino: ({ sender, contract, msg, sent_funds }: MsgExecuteContract["value"]): IMsgExecuteContract => ({ @@ -152,10 +140,10 @@ export const cosmWasmTypes: Record = { "/cosmwasm.wasm.v1beta1.MsgMigrateContract": { aminoType: "wasm/MsgMigrateContract", toAmino: ({ sender, contract, codeId, migrateMsg }: IMsgMigrateContract): MsgMigrateContract["value"] => { - assert(sender, "missing sender"); - assert(contract, "missing contract"); - assert(codeId, "missing codeId"); - assert(migrateMsg, "missing migrateMsg"); + assertDefinedAndNotNull(sender, "missing sender"); + assertDefinedAndNotNull(contract, "missing contract"); + assertDefinedAndNotNull(codeId, "missing codeId"); + assertDefinedAndNotNull(migrateMsg, "missing migrateMsg"); return { sender: sender, contract: contract,