Merge pull request #614 from cosmos/594-amino-signing

Implement Amino signing for bank/staking in Stargate
This commit is contained in:
Simon Warta 2021-01-12 19:43:46 +01:00 committed by GitHub
commit 3f996b9fc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1084 additions and 98 deletions

View File

@ -99,7 +99,23 @@ export {
uint64ToNumber,
uint64ToString,
} from "./lcdapi";
export { isMsgDelegate, isMsgSend, Msg, MsgDelegate, MsgSend } from "./msgs";
export {
isMsgBeginRedelegate,
isMsgCreateValidator,
isMsgDelegate,
isMsgEditValidator,
isMsgMultiSend,
isMsgSend,
isMsgUndelegate,
Msg,
MsgBeginRedelegate,
MsgCreateValidator,
MsgDelegate,
MsgEditValidator,
MsgMultiSend,
MsgSend,
MsgUndelegate,
} from "./msgs";
export {
decodeAminoPubkey,
decodeBech32Pubkey,

View File

@ -97,7 +97,23 @@ export {
uint64ToNumber,
uint64ToString,
} from "./lcdapi";
export { isMsgDelegate, isMsgSend, Msg, MsgDelegate, MsgSend } from "./msgs";
export {
isMsgBeginRedelegate,
isMsgCreateValidator,
isMsgDelegate,
isMsgEditValidator,
isMsgMultiSend,
isMsgSend,
isMsgUndelegate,
Msg,
MsgBeginRedelegate,
MsgCreateValidator,
MsgDelegate,
MsgEditValidator,
MsgMultiSend,
MsgSend,
MsgUndelegate,
} from "./msgs";
export {
decodeAminoPubkey,
decodeBech32Pubkey,

View File

@ -1,56 +1,573 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { fromBase64 } from "@cosmjs/encoding";
import {
coin,
coins,
encodeBech32Pubkey,
MsgBeginRedelegate,
MsgCreateValidator,
MsgDelegate,
MsgEditValidator,
MsgMultiSend,
MsgSend,
MsgUndelegate,
} from "@cosmjs/launchpad";
import { AminoTypes } from "./aminotypes";
import { cosmos } from "./codec";
type IMsgSend = cosmos.bank.v1beta1.IMsgSend;
type IMsgMultiSend = cosmos.bank.v1beta1.IMsgMultiSend;
type IMsgBeginRedelegate = cosmos.staking.v1beta1.IMsgBeginRedelegate;
type IMsgCreateValidator = cosmos.staking.v1beta1.IMsgCreateValidator;
type IMsgDelegate = cosmos.staking.v1beta1.IMsgDelegate;
type IMsgEditValidator = cosmos.staking.v1beta1.IMsgEditValidator;
type IMsgUndelegate = cosmos.staking.v1beta1.IMsgUndelegate;
describe("AminoTypes", () => {
describe("toAmino", () => {
it("works for known type url", () => {
const msgType = new AminoTypes().toAmino("/cosmos.staking.v1beta1.MsgDelegate");
expect(msgType).toEqual("cosmos-sdk/MsgDelegate");
it("works for MsgSend", () => {
const msg: IMsgSend = {
fromAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
toAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coins(1234, "ucosm"),
};
const aminoMsg = new AminoTypes().toAmino({
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
value: msg,
});
const expected: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
to_address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coins(1234, "ucosm"),
},
};
expect(aminoMsg).toEqual(expected);
});
it("works for MsgMultiSend", () => {
const msg: IMsgMultiSend = {
inputs: [
{ address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", coins: coins(1234, "ucosm") },
{ address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", coins: coins(5678, "ucosm") },
],
outputs: [
{ address: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", coins: coins(6000, "ucosm") },
{ address: "cosmos142u9fgcjdlycfcez3lw8x6x5h7rfjlnfhpw2lx", coins: coins(912, "ucosm") },
],
};
const aminoMsg = new AminoTypes().toAmino({
typeUrl: "/cosmos.bank.v1beta1.MsgMultiSend",
value: msg,
});
const expected: MsgMultiSend = {
type: "cosmos-sdk/MsgMultiSend",
value: {
inputs: [
{ address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", coins: coins(1234, "ucosm") },
{ address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", coins: coins(5678, "ucosm") },
],
outputs: [
{ address: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", coins: coins(6000, "ucosm") },
{ address: "cosmos142u9fgcjdlycfcez3lw8x6x5h7rfjlnfhpw2lx", coins: coins(912, "ucosm") },
],
},
};
expect(aminoMsg).toEqual(expected);
});
it("works for MsgBeginRedelegate", () => {
const msg: IMsgBeginRedelegate = {
delegatorAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validatorSrcAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
validatorDstAddress: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
amount: coin(1234, "ucosm"),
};
const aminoMsg = new AminoTypes().toAmino({
typeUrl: "/cosmos.staking.v1beta1.MsgBeginRedelegate",
value: msg,
});
const expected: MsgBeginRedelegate = {
type: "cosmos-sdk/MsgBeginRedelegate",
value: {
delegator_address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validator_src_address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
validator_dst_address: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
amount: coin(1234, "ucosm"),
},
};
expect(aminoMsg).toEqual(expected);
});
it("works for MsgCreateValidator", () => {
const msg: IMsgCreateValidator = {
description: {
moniker: "validator",
identity: "me",
website: "valid.com",
securityContact: "Hamburglar",
details: "...",
},
commission: {
rate: "0.2",
maxRate: "0.3",
maxChangeRate: "0.1",
},
minSelfDelegation: "123",
delegatorAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validatorAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
pubkey: {
type_url: "/cosmos.crypto.secp256k1.PubKey",
value: fromBase64("A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ"),
},
value: coin(1234, "ucosm"),
};
const aminoMsg = new AminoTypes().toAmino({
typeUrl: "/cosmos.staking.v1beta1.MsgCreateValidator",
value: msg,
});
const expected: MsgCreateValidator = {
type: "cosmos-sdk/MsgCreateValidator",
value: {
description: {
moniker: "validator",
identity: "me",
website: "valid.com",
security_contact: "Hamburglar",
details: "...",
},
commission: {
rate: "0.2",
max_rate: "0.3",
max_change_rate: "0.1",
},
min_self_delegation: "123",
delegator_address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validator_address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
pubkey: encodeBech32Pubkey(
{ type: "tendermint/PubKeySecp256k1", value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ" },
"cosmos",
),
value: coin(1234, "ucosm"),
},
};
expect(aminoMsg).toEqual(expected);
});
it("works for MsgDelegate", () => {
const msg: IMsgDelegate = {
delegatorAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validatorAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coin(1234, "ucosm"),
};
const aminoMsg = new AminoTypes().toAmino({
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
value: msg,
});
const expected: MsgDelegate = {
type: "cosmos-sdk/MsgDelegate",
value: {
delegator_address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validator_address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coin(1234, "ucosm"),
},
};
expect(aminoMsg).toEqual(expected);
});
it("works for MsgEditValidator", () => {
const msg: IMsgEditValidator = {
description: {
moniker: "validator",
identity: "me",
website: "valid.com",
securityContact: "Hamburglar",
details: "...",
},
commissionRate: "0.2",
minSelfDelegation: "123",
validatorAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
};
const aminoMsg = new AminoTypes().toAmino({
typeUrl: "/cosmos.staking.v1beta1.MsgEditValidator",
value: msg,
});
const expected: MsgEditValidator = {
type: "cosmos-sdk/MsgEditValidator",
value: {
description: {
moniker: "validator",
identity: "me",
website: "valid.com",
security_contact: "Hamburglar",
details: "...",
},
commission_rate: "0.2",
min_self_delegation: "123",
validator_address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
},
};
expect(aminoMsg).toEqual(expected);
});
it("works for MsgUndelegate", () => {
const msg: IMsgUndelegate = {
delegatorAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validatorAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coin(1234, "ucosm"),
};
const aminoMsg = new AminoTypes().toAmino({
typeUrl: "/cosmos.staking.v1beta1.MsgUndelegate",
value: msg,
});
const expected: MsgUndelegate = {
type: "cosmos-sdk/MsgUndelegate",
value: {
delegator_address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validator_address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coin(1234, "ucosm"),
},
};
expect(aminoMsg).toEqual(expected);
});
it("works with custom type url", () => {
const msgType = new AminoTypes({ "/my.CustomType": "my-sdk/CustomType" }).toAmino("/my.CustomType");
expect(msgType).toEqual("my-sdk/CustomType");
const msg = {
foo: "bar",
};
const aminoMsg = new AminoTypes({
additions: {
"/my.CustomType": {
aminoType: "my-sdk/CustomType",
toAmino: ({
foo,
}: {
readonly foo: string;
}): { readonly foo: string; readonly constant: string } => ({
foo: `amino-prefix-${foo}`,
constant: "something-for-amino",
}),
fromAmino: () => {},
},
},
}).toAmino({ typeUrl: "/my.CustomType", value: msg });
expect(aminoMsg).toEqual({
type: "my-sdk/CustomType",
value: {
foo: "amino-prefix-bar",
constant: "something-for-amino",
},
});
});
it("works with overridden type url", () => {
const msgType = new AminoTypes({
"/cosmos.staking.v1beta1.MsgDelegate": "my-override/MsgDelegate",
}).toAmino("/cosmos.staking.v1beta1.MsgDelegate");
expect(msgType).toEqual("my-override/MsgDelegate");
const msg: IMsgDelegate = {
delegatorAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validatorAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coin(1234, "ucosm"),
};
const aminoMsg = new AminoTypes({
additions: {
"/cosmos.staking.v1beta1.MsgDelegate": {
aminoType: "my-override/MsgDelegate",
toAmino: (m: IMsgDelegate): { readonly foo: string } => ({
foo: m.delegatorAddress ?? "",
}),
fromAmino: () => {},
},
},
}).toAmino({
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
value: msg,
});
const expected = {
type: "my-override/MsgDelegate",
value: {
foo: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
},
};
expect(aminoMsg).toEqual(expected);
});
it("throws for unknown type url", () => {
expect(() => new AminoTypes().toAmino("/xxx.Unknown")).toThrowError(
expect(() => new AminoTypes().toAmino({ typeUrl: "/xxx.Unknown", value: { foo: "bar" } })).toThrowError(
/Type URL does not exist in the Amino message type register./i,
);
});
});
describe("fromAmino", () => {
it("works for known type url", () => {
const msgUrl = new AminoTypes().fromAmino("cosmos-sdk/MsgDelegate");
expect(msgUrl).toEqual("/cosmos.staking.v1beta1.MsgDelegate");
it("works for MsgSend", () => {
const aminoMsg: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
to_address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coins(1234, "ucosm"),
},
};
const msg = new AminoTypes().fromAmino(aminoMsg);
const expectedValue: IMsgSend = {
fromAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
toAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coins(1234, "ucosm"),
};
expect(msg).toEqual({
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
value: expectedValue,
});
});
it("works with custom type url", () => {
const msgType = new AminoTypes({ "/my.CustomType": "my-sdk/CustomType" }).fromAmino(
"my-sdk/CustomType",
);
expect(msgType).toEqual("/my.CustomType");
it("works for MsgMultiSend", () => {
const aminoMsg: MsgMultiSend = {
type: "cosmos-sdk/MsgMultiSend",
value: {
inputs: [
{ address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", coins: coins(1234, "ucosm") },
{ address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", coins: coins(5678, "ucosm") },
],
outputs: [
{ address: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", coins: coins(6000, "ucosm") },
{ address: "cosmos142u9fgcjdlycfcez3lw8x6x5h7rfjlnfhpw2lx", coins: coins(912, "ucosm") },
],
},
};
const msg = new AminoTypes().fromAmino(aminoMsg);
const expectedValue: IMsgMultiSend = {
inputs: [
{ address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", coins: coins(1234, "ucosm") },
{ address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", coins: coins(5678, "ucosm") },
],
outputs: [
{ address: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", coins: coins(6000, "ucosm") },
{ address: "cosmos142u9fgcjdlycfcez3lw8x6x5h7rfjlnfhpw2lx", coins: coins(912, "ucosm") },
],
};
expect(msg).toEqual({
typeUrl: "/cosmos.bank.v1beta1.MsgMultiSend",
value: expectedValue,
});
});
it("works for MsgBeginRedelegate", () => {
const aminoMsg: MsgBeginRedelegate = {
type: "cosmos-sdk/MsgBeginRedelegate",
value: {
delegator_address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validator_src_address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
validator_dst_address: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
amount: coin(1234, "ucosm"),
},
};
const msg = new AminoTypes().fromAmino(aminoMsg);
const expectedValue: IMsgBeginRedelegate = {
delegatorAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validatorSrcAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
validatorDstAddress: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
amount: coin(1234, "ucosm"),
};
expect(msg).toEqual({
typeUrl: "/cosmos.staking.v1beta1.MsgBeginRedelegate",
value: expectedValue,
});
});
it("works for MsgCreateValidator", () => {
const aminoMsg: MsgCreateValidator = {
type: "cosmos-sdk/MsgCreateValidator",
value: {
description: {
moniker: "validator",
identity: "me",
website: "valid.com",
security_contact: "Hamburglar",
details: "...",
},
commission: {
rate: "0.2",
max_rate: "0.3",
max_change_rate: "0.1",
},
min_self_delegation: "123",
delegator_address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validator_address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
pubkey: encodeBech32Pubkey(
{ type: "tendermint/PubKeySecp256k1", value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ" },
"cosmos",
),
value: coin(1234, "ucosm"),
},
};
const msg = new AminoTypes().fromAmino(aminoMsg);
const expectedValue: IMsgCreateValidator = {
description: {
moniker: "validator",
identity: "me",
website: "valid.com",
securityContact: "Hamburglar",
details: "...",
},
commission: {
rate: "0.2",
maxRate: "0.3",
maxChangeRate: "0.1",
},
minSelfDelegation: "123",
delegatorAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validatorAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
pubkey: {
type_url: "/cosmos.crypto.secp256k1.PubKey",
value: fromBase64("A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ"),
},
value: coin(1234, "ucosm"),
};
expect(msg).toEqual({
typeUrl: "/cosmos.staking.v1beta1.MsgCreateValidator",
value: expectedValue,
});
});
it("works for MsgDelegate", () => {
const aminoMsg: MsgDelegate = {
type: "cosmos-sdk/MsgDelegate",
value: {
delegator_address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validator_address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coin(1234, "ucosm"),
},
};
const msg = new AminoTypes().fromAmino(aminoMsg);
const expectedValue: IMsgDelegate = {
delegatorAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validatorAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coin(1234, "ucosm"),
};
expect(msg).toEqual({
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
value: expectedValue,
});
});
it("works for MsgEditValidator", () => {
const aminoMsg: MsgEditValidator = {
type: "cosmos-sdk/MsgEditValidator",
value: {
description: {
moniker: "validator",
identity: "me",
website: "valid.com",
security_contact: "Hamburglar",
details: "...",
},
commission_rate: "0.2",
min_self_delegation: "123",
validator_address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
},
};
const msg = new AminoTypes().fromAmino(aminoMsg);
const expectedValue: IMsgEditValidator = {
description: {
moniker: "validator",
identity: "me",
website: "valid.com",
securityContact: "Hamburglar",
details: "...",
},
commissionRate: "0.2",
minSelfDelegation: "123",
validatorAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
};
expect(msg).toEqual({
typeUrl: "/cosmos.staking.v1beta1.MsgEditValidator",
value: expectedValue,
});
});
it("works for MsgUndelegate", () => {
const aminoMsg: MsgUndelegate = {
type: "cosmos-sdk/MsgUndelegate",
value: {
delegator_address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validator_address: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coin(1234, "ucosm"),
},
};
const msg = new AminoTypes().fromAmino(aminoMsg);
const expectedValue: IMsgUndelegate = {
delegatorAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validatorAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coin(1234, "ucosm"),
};
expect(msg).toEqual({
typeUrl: "/cosmos.staking.v1beta1.MsgUndelegate",
value: expectedValue,
});
});
it("works for custom type url", () => {
const aminoMsg = {
type: "my-sdk/CustomType",
value: {
foo: "amino-prefix-bar",
constant: "something-for-amino",
},
};
const msg = new AminoTypes({
additions: {
"/my.CustomType": {
aminoType: "my-sdk/CustomType",
toAmino: () => {},
fromAmino: ({ foo }: { readonly foo: string; readonly constant: string }): any => ({
foo: foo.slice(13),
}),
},
},
}).fromAmino(aminoMsg);
const expectedValue = {
foo: "bar",
};
expect(msg).toEqual({
typeUrl: "/my.CustomType",
value: expectedValue,
});
});
it("works with overridden type url", () => {
const msgType = new AminoTypes({
"/my.OverrideType": "cosmos-sdk/MsgDelegate",
}).fromAmino("cosmos-sdk/MsgDelegate");
expect(msgType).toEqual("/my.OverrideType");
const msg = new AminoTypes({
additions: {
"/my.OverrideType": {
aminoType: "cosmos-sdk/MsgDelegate",
toAmino: () => {},
fromAmino: ({ foo }: { readonly foo: string }): IMsgDelegate => ({
delegatorAddress: foo,
validatorAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coin(1234, "ucosm"),
}),
},
},
}).fromAmino({
type: "cosmos-sdk/MsgDelegate",
value: {
foo: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
},
});
const expected: { readonly typeUrl: "/my.OverrideType"; readonly value: IMsgDelegate } = {
typeUrl: "/my.OverrideType",
value: {
delegatorAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
validatorAddress: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
amount: coin(1234, "ucosm"),
},
};
expect(msg).toEqual(expected);
});
it("throws for unknown type url", () => {
expect(() => new AminoTypes().fromAmino("cosmos-sdk/MsgUnknown")).toThrowError(
/Type does not exist in the Amino message type register./i,
);
expect(() =>
new AminoTypes().fromAmino({ type: "cosmos-sdk/MsgUnknown", value: { foo: "bar" } }),
).toThrowError(/Type does not exist in the Amino message type register./i);
});
});
});

View File

@ -1,61 +1,386 @@
const defaultTypes: Record<string, string> = {
"/cosmos.bank.v1beta1.MsgSend": "cosmos-sdk/MsgSend",
"/cosmos.bank.v1beta1.MsgMultiSend": "cosmos-sdk/MsgMultiSend",
"/cosmos.crisis.v1beta1.MsgVerifyInvariant": "cosmos-sdk/MsgVerifyInvariant",
"/cosmos.distribution.v1beta1.MsgSetWithdrawAddress": "cosmos-sdk/MsgSetWithdrawAddress",
"/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward": "cosmos-sdk/MsgWithdrawDelegatorReward",
"/cosmos.distribution.v1beta1.MsgWithdrawValidatorComission": "cosmos-sdk/MsgWithdrawValidatorComission",
"/cosmos.distribution.v1beta1.MsgFundCommunityPool": "cosmos-sdk/MsgFundCommunityPool",
"/cosmos.evidence.v1beta1.MsgSubmitEvidence": "cosmos-sdk/MsgSubmitEvidence",
"/cosmos.gov.v1beta1.MsgSubmitProposal": "cosmos-sdk/MsgSubmitProposal",
"/cosmos.gov.v1beta1.MsgVote": "cosmos-sdk/MsgVote",
"/cosmos.gov.v1beta1.MsgDeposit": "cosmos-sdk/MsgDeposit",
"/cosmos.slashing.v1beta1.MsgUnjail": "cosmos-sdk/MsgUnjail",
"/cosmos.staking.v1beta1.MsgCreateValidator": "cosmos-sdk/MsgCreateValidator",
"/cosmos.staking.v1beta1.MsgEditValidator": "cosmos-sdk/MsgEditValidator",
"/cosmos.staking.v1beta1.MsgDelegate": "cosmos-sdk/MsgDelegate",
"/cosmos.staking.v1beta1.MsgBeginRedelegate": "cosmos-sdk/MsgBeginRedelegate",
"/cosmos.staking.v1beta1.MsgUndelegate": "cosmos-sdk/MsgUndelegate",
"/cosmos.vesting.v1beta1.MsgCreateVestingAccount": "cosmos-sdk/MsgCreateVestingAccount",
};
/* eslint-disable @typescript-eslint/naming-convention */
import { fromBase64, toBase64 } from "@cosmjs/encoding";
import {
Coin,
decodeBech32Pubkey,
encodeBech32Pubkey,
Msg,
MsgBeginRedelegate,
MsgCreateValidator,
MsgDelegate,
MsgEditValidator,
MsgMultiSend,
MsgSend,
MsgUndelegate,
} from "@cosmjs/launchpad";
import { EncodeObject } from "@cosmjs/proto-signing";
import { assert } from "@cosmjs/utils";
import { cosmos } from "./codec";
type ICoin = cosmos.base.v1beta1.ICoin;
type IMsgSend = cosmos.bank.v1beta1.IMsgSend;
type IMsgMultiSend = cosmos.bank.v1beta1.IMsgMultiSend;
type IMsgBeginRedelegate = cosmos.staking.v1beta1.IMsgBeginRedelegate;
type IMsgCreateValidator = cosmos.staking.v1beta1.IMsgCreateValidator;
type IMsgDelegate = cosmos.staking.v1beta1.IMsgDelegate;
type IMsgEditValidator = cosmos.staking.v1beta1.IMsgEditValidator;
type IMsgUndelegate = cosmos.staking.v1beta1.IMsgUndelegate;
export interface AminoConverter {
readonly aminoType: string;
readonly toAmino: (value: any) => any;
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<string, AminoConverter> {
return {
"/cosmos.bank.v1beta1.MsgSend": {
aminoType: "cosmos-sdk/MsgSend",
toAmino: ({ fromAddress, toAddress, amount }: IMsgSend): MsgSend["value"] => {
assert(fromAddress, "missing fromAddress");
assert(toAddress, "missing toAddress");
return {
from_address: fromAddress,
to_address: toAddress,
amount: checkAmount(amount),
};
},
fromAmino: ({ from_address, to_address, amount }: MsgSend["value"]): IMsgSend => ({
fromAddress: from_address,
toAddress: to_address,
amount: [...amount],
}),
},
"/cosmos.bank.v1beta1.MsgMultiSend": {
aminoType: "cosmos-sdk/MsgMultiSend",
toAmino: ({ inputs, outputs }: IMsgMultiSend): MsgMultiSend["value"] => {
assert(inputs, "missing inputs");
assert(outputs, "missing outputs");
return {
inputs: inputs.map((input) => {
assert(input.address, "missing input.address");
return {
address: input.address,
coins: checkAmount(input.coins),
};
}),
outputs: outputs.map((output) => {
assert(output.address, "missing output.address");
return {
address: output.address,
coins: checkAmount(output.coins),
};
}),
};
},
fromAmino: ({ inputs, outputs }: MsgMultiSend["value"]): IMsgMultiSend => ({
inputs: inputs.map((input) => ({
address: input.address,
coins: [...input.coins],
})),
outputs: outputs.map((output) => ({
address: output.address,
coins: [...output.coins],
})),
}),
},
"/cosmos.staking.v1beta1.MsgBeginRedelegate": {
aminoType: "cosmos-sdk/MsgBeginRedelegate",
toAmino: ({
delegatorAddress,
validatorSrcAddress,
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");
return {
delegator_address: delegatorAddress,
validator_src_address: validatorSrcAddress,
validator_dst_address: validatorDstAddress,
amount: {
amount: amount.amount,
denom: amount.denom,
},
};
},
fromAmino: ({
delegator_address,
validator_src_address,
validator_dst_address,
amount,
}: MsgBeginRedelegate["value"]): IMsgBeginRedelegate => ({
delegatorAddress: delegator_address,
validatorSrcAddress: validator_src_address,
validatorDstAddress: validator_dst_address,
amount: amount,
}),
},
"/cosmos.staking.v1beta1.MsgCreateValidator": {
aminoType: "cosmos-sdk/MsgCreateValidator",
toAmino: ({
description,
commission,
minSelfDelegation,
delegatorAddress,
validatorAddress,
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");
return {
description: {
moniker: description.moniker,
identity: description.identity,
website: description.website,
security_contact: description.securityContact,
details: description.details,
},
commission: {
rate: commission.rate,
max_rate: commission.maxRate,
max_change_rate: commission.maxChangeRate,
},
min_self_delegation: minSelfDelegation,
delegator_address: delegatorAddress,
validator_address: validatorAddress,
pubkey: encodeBech32Pubkey(
{
type: "tendermint/PubKeySecp256k1",
value: toBase64(pubkey.value),
},
prefix,
),
value: {
amount: value.amount,
denom: value.denom,
},
};
},
fromAmino: ({
description,
commission,
min_self_delegation,
delegator_address,
validator_address,
pubkey,
value,
}: MsgCreateValidator["value"]): IMsgCreateValidator => {
const decodedPubkey = decodeBech32Pubkey(pubkey);
if (decodedPubkey.type !== "tendermint/PubKeySecp256k1") {
throw new Error("Only Secp256k1 public keys are supported");
}
return {
description: {
moniker: description.moniker,
identity: description.identity,
website: description.website,
securityContact: description.security_contact,
details: description.details,
},
commission: {
rate: commission.rate,
maxRate: commission.max_rate,
maxChangeRate: commission.max_change_rate,
},
minSelfDelegation: min_self_delegation,
delegatorAddress: delegator_address,
validatorAddress: validator_address,
pubkey: {
type_url: "/cosmos.crypto.secp256k1.PubKey",
value: fromBase64(decodedPubkey.value),
},
value: value,
};
},
},
"/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");
return {
delegator_address: delegatorAddress,
validator_address: validatorAddress,
amount: {
amount: amount.amount,
denom: amount.denom,
},
};
},
fromAmino: ({ delegator_address, validator_address, amount }: MsgDelegate["value"]): IMsgDelegate => ({
delegatorAddress: delegator_address,
validatorAddress: validator_address,
amount: amount,
}),
},
"/cosmos.staking.v1beta1.MsgEditValidator": {
aminoType: "cosmos-sdk/MsgEditValidator",
toAmino: ({
description,
commissionRate,
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");
return {
description: {
moniker: description.moniker,
identity: description.identity,
website: description.website,
security_contact: description.securityContact,
details: description.details,
},
commission_rate: commissionRate,
min_self_delegation: minSelfDelegation,
validator_address: validatorAddress,
};
},
fromAmino: ({
description,
commission_rate,
min_self_delegation,
validator_address,
}: MsgEditValidator["value"]): IMsgEditValidator => ({
description: {
moniker: description.moniker,
identity: description.identity,
website: description.website,
securityContact: description.security_contact,
details: description.details,
},
commissionRate: commission_rate,
minSelfDelegation: min_self_delegation,
validatorAddress: validator_address,
}),
},
"/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");
return {
delegator_address: delegatorAddress,
validator_address: validatorAddress,
amount: {
amount: amount.amount,
denom: amount.denom,
},
};
},
fromAmino: ({
delegator_address,
validator_address,
amount,
}: MsgUndelegate["value"]): IMsgUndelegate => ({
delegatorAddress: delegator_address,
validatorAddress: validator_address,
amount: amount,
}),
},
};
}
interface AminoTypesOptions {
readonly additions?: Record<string, AminoConverter>;
readonly prefix?: string;
}
/**
* A map from Stargate message types as used in the messages's `Any` type
* to Amino types.
*/
export class AminoTypes {
private readonly register: Record<string, string>;
private readonly register: Record<string, AminoConverter>;
public constructor(additions: Record<string, string> = {}) {
public constructor({ additions = {}, prefix = "cosmos" }: AminoTypesOptions = {}) {
const additionalAminoTypes = Object.values(additions);
const filteredDefaultTypes = Object.entries(defaultTypes).reduce(
(acc, [key, value]) => (additionalAminoTypes.includes(value) ? acc : { ...acc, [key]: value }),
const filteredDefaultTypes = Object.entries(createDefaultTypes(prefix)).reduce(
(acc, [key, value]) =>
additionalAminoTypes.find(({ aminoType }) => value.aminoType === aminoType)
? acc
: { ...acc, [key]: value },
{},
);
this.register = { ...filteredDefaultTypes, ...additions };
}
public toAmino(typeUrl: string): string {
const type = this.register[typeUrl];
if (!type) {
public toAmino({ typeUrl, value }: EncodeObject): Msg {
const converter = this.register[typeUrl];
if (!converter) {
throw new Error(
"Type URL does not exist in the Amino message type register. " +
"If you need support for this message type, you can pass in additional entries to the AminoTypes constructor. " +
"If you think this message type should be included by default, please open an issue at https://github.com/cosmos/cosmjs/issues.",
);
}
return type;
return {
type: converter.aminoType,
value: converter.toAmino(value),
};
}
public fromAmino(type: string): string {
const [typeUrl] = Object.entries(this.register).find(([_typeUrl, value]) => value === type) ?? [];
if (!typeUrl) {
public fromAmino({ type, value }: Msg): EncodeObject {
const result = Object.entries(this.register).find(([_typeUrl, { aminoType }]) => aminoType === type);
if (!result) {
throw new Error(
"Type does not exist in the Amino message type register. " +
"If you need support for this message type, you can pass in additional entries to the AminoTypes constructor. " +
"If you think this message type should be included by default, please open an issue at https://github.com/cosmos/cosmjs/issues.",
);
}
return typeUrl;
const [typeUrl, converter] = result;
return {
typeUrl: typeUrl,
value: converter.fromAmino(value),
};
}
}

View File

@ -1,9 +1,16 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { coin, coins, GasPrice, Secp256k1HdWallet } from "@cosmjs/launchpad";
import {
coin,
coins,
GasPrice,
MsgDelegate as LaunchpadMsgDelegate,
Secp256k1HdWallet,
} from "@cosmjs/launchpad";
import { Coin, cosmosField, DirectSecp256k1HdWallet, registered, Registry } from "@cosmjs/proto-signing";
import { assert, sleep } from "@cosmjs/utils";
import { Message } from "protobufjs";
import { AminoTypes } from "./aminotypes";
import { cosmos } from "./codec";
import { PrivateSigningStargateClient, SigningStargateClient } from "./signingstargateclient";
import { assertIsBroadcastTxSuccess } from "./stargateclient";
@ -150,10 +157,7 @@ describe("SigningStargateClient", () => {
pendingWithoutSimapp();
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
const registry = new Registry();
registry.register(msgDelegateTypeUrl, MsgDelegate);
const options = { registry: registry };
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);
const msg = MsgDelegate.create({
delegatorAddress: faucet.address0,
@ -177,10 +181,7 @@ describe("SigningStargateClient", () => {
pendingWithoutSimapp();
const wallet = await ModifyingDirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
const registry = new Registry();
registry.register(msgDelegateTypeUrl, MsgDelegate);
const options = { registry: registry };
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);
const msg = MsgDelegate.create({
delegatorAddress: faucet.address0,
@ -212,36 +213,118 @@ describe("SigningStargateClient", () => {
});
describe("legacy Amino mode", () => {
// NOTE: One registry shared between tests
// 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 registry = new Registry();
const customRegistry = new Registry();
const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
@registered(registry, msgDelegateTypeUrl)
@registered(customRegistry, msgDelegateTypeUrl)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class CustomMsgDelegate extends Message {
@cosmosField.string(1)
public readonly delegator_address?: string;
public readonly custom_delegator_address?: string;
@cosmosField.string(2)
public readonly validator_address?: string;
public readonly custom_validator_address?: string;
@cosmosField.message(3, Coin)
public readonly amount?: Coin;
public readonly custom_amount?: Coin;
}
it("works", async () => {
it("works with bank MsgSend", async () => {
pendingWithoutSimapp();
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const options = { registry: registry };
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);
const msg = {
delegator_address: faucet.address0,
validator_address: validator.validatorAddress,
const msgSend = {
fromAddress: faucet.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(faucet.address0, [msgAny], fee, memo);
assertIsBroadcastTxSuccess(result);
});
it("works with staking MsgDelegate", async () => {
pendingWithoutSimapp();
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);
const msgDelegate = {
delegatorAddress: faucet.address0,
validatorAddress: validator.validatorAddress,
amount: coin(1234, "ustake"),
};
const msgAny = {
typeUrl: msgDelegateTypeUrl,
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(faucet.address0, [msgAny], fee, memo);
assertIsBroadcastTxSuccess(result);
});
it("works with a custom registry and custom message", async () => {
pendingWithoutSimapp();
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
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 SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);
const msg = {
custom_delegator_address: faucet.address0,
custom_validator_address: validator.validatorAddress,
custom_amount: coin(1234, "ustake"),
};
const msgAny = {
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
value: msg,
};
const fee = {
@ -256,12 +339,11 @@ describe("SigningStargateClient", () => {
it("works with a modifying signer", async () => {
pendingWithoutSimapp();
const wallet = await ModifyingSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const options = { registry: registry };
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);
const msg = {
delegator_address: faucet.address0,
validator_address: validator.validatorAddress,
delegatorAddress: faucet.address0,
validatorAddress: validator.validatorAddress,
amount: coin(1234, "ustake"),
};
const msgAny = {

View File

@ -27,6 +27,14 @@ import { AminoTypes } from "./aminotypes";
import { cosmos } from "./codec";
import { BroadcastTxResponse, StargateClient } from "./stargateclient";
const { MsgMultiSend } = cosmos.bank.v1beta1;
const {
MsgBeginRedelegate,
MsgCreateValidator,
MsgDelegate,
MsgEditValidator,
MsgUndelegate,
} = cosmos.staking.v1beta1;
const { TxRaw } = cosmos.tx.v1beta1;
const defaultGasPrice = GasPrice.fromString("0.025ucosm");
@ -40,6 +48,7 @@ export interface PrivateSigningStargateClient {
export interface SigningStargateClientOptions {
readonly registry?: Registry;
readonly aminoTypes?: AminoTypes;
readonly gasPrice?: GasPrice;
readonly gasLimits?: GasLimits<CosmosFeeTable>;
}
@ -48,7 +57,7 @@ export class SigningStargateClient extends StargateClient {
private readonly fees: CosmosFeeTable;
private readonly registry: Registry;
private readonly signer: OfflineSigner;
private readonly aminoTypes = new AminoTypes();
private readonly aminoTypes;
public static async connectWithSigner(
endpoint: string,
@ -65,9 +74,22 @@ export class SigningStargateClient extends StargateClient {
options: SigningStargateClientOptions,
) {
super(tmClient);
const { registry = new Registry(), gasPrice = defaultGasPrice, gasLimits = defaultGasLimits } = options;
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],
]),
aminoTypes = new AminoTypes(),
gasPrice = defaultGasPrice,
gasLimits = defaultGasLimits,
} = options;
this.fees = buildFeeTable<CosmosFeeTable>(gasPrice, defaultGasLimits, gasLimits);
this.registry = registry;
this.aminoTypes = aminoTypes;
this.signer = signer;
}
@ -136,17 +158,11 @@ export class SigningStargateClient extends StargateClient {
// Amino signer
const signMode = cosmos.tx.signing.v1beta1.SignMode.SIGN_MODE_LEGACY_AMINO_JSON;
const msgs = messages.map((msg) => ({
type: this.aminoTypes.toAmino(msg.typeUrl),
value: msg.value,
}));
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 signedTxBody = {
messages: signed.msgs.map((msg) => ({
typeUrl: this.aminoTypes.fromAmino(msg.type),
value: msg.value,
})),
messages: signed.msgs.map((msg) => this.aminoTypes.fromAmino(msg)),
memo: signed.memo,
};
const signedTxBodyBytes = this.registry.encode({

View File

@ -1,10 +1,22 @@
import { Msg } from "@cosmjs/launchpad";
import { EncodeObject } from "@cosmjs/proto-signing";
export interface AminoConverter {
readonly aminoType: string;
readonly toAmino: (value: any) => any;
readonly fromAmino: (value: any) => any;
}
interface AminoTypesOptions {
readonly additions?: Record<string, AminoConverter>;
readonly prefix?: string;
}
/**
* A map from Stargate message types as used in the messages's `Any` type
* to Amino types.
*/
export declare class AminoTypes {
private readonly register;
constructor(additions?: Record<string, string>);
toAmino(typeUrl: string): string;
fromAmino(type: string): string;
constructor({ additions, prefix }?: AminoTypesOptions);
toAmino({ typeUrl, value }: EncodeObject): Msg;
fromAmino({ type, value }: Msg): EncodeObject;
}
export {};

View File

@ -1,5 +1,6 @@
import { Coin, CosmosFeeTable, GasLimits, GasPrice, StdFee } from "@cosmjs/launchpad";
import { EncodeObject, OfflineSigner, Registry } from "@cosmjs/proto-signing";
import { AminoTypes } from "./aminotypes";
import { BroadcastTxResponse, StargateClient } from "./stargateclient";
/** Use for testing only */
export interface PrivateSigningStargateClient {
@ -8,6 +9,7 @@ export interface PrivateSigningStargateClient {
}
export interface SigningStargateClientOptions {
readonly registry?: Registry;
readonly aminoTypes?: AminoTypes;
readonly gasPrice?: GasPrice;
readonly gasLimits?: GasLimits<CosmosFeeTable>;
}