Merge pull request #871 from cosmos/MsgDeposit-and-amino

Add MsgDeposit support as well as Amino JSON signing for gov messages
This commit is contained in:
Simon Warta 2021-08-24 17:22:29 +02:00 committed by GitHub
commit 0ee110bef3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 353 additions and 15 deletions

View File

@ -26,7 +26,8 @@ and this project adheres to
configure the HD path of the faucet accounts ([#832]).
- @cosmjs/cosmwasm-stargate: Add field `ibcPortId` to `Contract` ([#836]).
- @cosmjs/stargate: Add `GovExtension` for query client.
- @cosmjs/stargate: Add support for `MsgSubmitProposal` and `MsgVote`.
- @cosmjs/stargate: Add support for `MsgDeposit`, `MsgSubmitProposal` and
`MsgVote`.
[#832]: https://github.com/cosmos/cosmjs/issues/832
[#836]: https://github.com/cosmos/cosmjs/issues/836

View File

@ -155,7 +155,23 @@ export function isAminoMsgSubmitEvidence(msg: AminoMsg): msg is AminoMsgSubmitEv
export interface AminoMsgSubmitProposal extends AminoMsg {
readonly type: "cosmos-sdk/MsgSubmitProposal";
readonly value: {
readonly content: Any;
/**
* A proposal structure, e.g.
*
* ```
* {
* type: 'cosmos-sdk/TextProposal',
* value: {
* description: 'This proposal proposes to test whether this proposal passes',
* title: 'Test Proposal'
* }
* }
* ```
*/
readonly content: {
readonly type: string;
readonly value: any;
};
readonly initial_deposit: readonly Coin[];
/** Bech32 account address */
readonly proposer: string;
@ -166,14 +182,6 @@ export function isAminoMsgSubmitProposal(msg: AminoMsg): msg is AminoMsgSubmitPr
return msg.type === "cosmos-sdk/MsgSubmitProposal";
}
enum VoteOption {
VoteOptionUnspecified,
VoteOptionYes,
VoteOptionAbstain,
VoteOptionNo,
VoteOptionNoWithVeto,
}
/** Casts a vote */
export interface AminoMsgVote extends AminoMsg {
readonly type: "cosmos-sdk/MsgVote";
@ -181,7 +189,12 @@ export interface AminoMsgVote extends AminoMsg {
readonly proposal_id: string;
/** Bech32 account address */
readonly voter: string;
readonly option: VoteOption;
/**
* VoteOption as integer from 0 to 4 🤷
*
* @see https://github.com/cosmos/cosmos-sdk/blob/v0.42.9/x/gov/types/gov.pb.go#L38-L49
*/
readonly option: number;
};
}

View File

@ -9,6 +9,8 @@ import {
MsgWithdrawDelegatorReward,
MsgWithdrawValidatorCommission,
} from "cosmjs-types/cosmos/distribution/v1beta1/tx";
import { TextProposal, VoteOption } from "cosmjs-types/cosmos/gov/v1beta1/gov";
import { MsgDeposit, MsgSubmitProposal, MsgVote } from "cosmjs-types/cosmos/gov/v1beta1/tx";
import {
MsgBeginRedelegate,
MsgCreateValidator,
@ -23,13 +25,16 @@ import {
AminoMsgBeginRedelegate,
AminoMsgCreateValidator,
AminoMsgDelegate,
AminoMsgDeposit,
AminoMsgEditValidator,
AminoMsgFundCommunityPool,
AminoMsgMultiSend,
AminoMsgSend,
AminoMsgSetWithdrawAddress,
AminoMsgSubmitProposal,
AminoMsgTransfer,
AminoMsgUndelegate,
AminoMsgVote,
AminoMsgWithdrawDelegatorReward,
AminoMsgWithdrawValidatorCommission,
} from "./aminomsgs";
@ -37,6 +42,8 @@ import { AminoTypes } from "./aminotypes";
describe("AminoTypes", () => {
describe("toAmino", () => {
// bank
it("works for MsgSend", () => {
const msg: MsgSend = {
fromAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
@ -89,6 +96,85 @@ describe("AminoTypes", () => {
expect(aminoMsg).toEqual(expected);
});
// gov
it("works for MsgDeposit", () => {
const msg: MsgDeposit = {
amount: [{ amount: "12300000", denom: "ustake" }],
depositor: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
proposalId: Long.fromNumber(5),
};
const aminoMsg = new AminoTypes().toAmino({
typeUrl: "/cosmos.gov.v1beta1.MsgDeposit",
value: msg,
});
const expected: AminoMsgDeposit = {
type: "cosmos-sdk/MsgDeposit",
value: {
amount: [{ amount: "12300000", denom: "ustake" }],
depositor: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
proposal_id: "5",
},
};
expect(aminoMsg).toEqual(expected);
});
it("works for MsgSubmitProposal", () => {
const msg: MsgSubmitProposal = {
initialDeposit: [{ amount: "12300000", denom: "ustake" }],
proposer: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
content: {
typeUrl: "/cosmos.gov.v1beta1.TextProposal",
value: TextProposal.encode({
description: "This proposal proposes to test whether this proposal passes",
title: "Test Proposal",
}).finish(),
},
};
const aminoMsg = new AminoTypes().toAmino({
typeUrl: "/cosmos.gov.v1beta1.MsgSubmitProposal",
value: msg,
});
const expected: AminoMsgSubmitProposal = {
type: "cosmos-sdk/MsgSubmitProposal",
value: {
initial_deposit: [{ amount: "12300000", denom: "ustake" }],
proposer: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
content: {
type: "cosmos-sdk/TextProposal",
value: {
description: "This proposal proposes to test whether this proposal passes",
title: "Test Proposal",
},
},
},
};
expect(aminoMsg).toEqual(expected);
});
it("works for MsgVote", () => {
const msg: MsgVote = {
option: VoteOption.VOTE_OPTION_NO_WITH_VETO,
proposalId: Long.fromNumber(5),
voter: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
};
const aminoMsg = new AminoTypes().toAmino({
typeUrl: "/cosmos.gov.v1beta1.MsgVote",
value: msg,
});
const expected: AminoMsgVote = {
type: "cosmos-sdk/MsgVote",
value: {
option: 4,
proposal_id: "5",
voter: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
},
};
expect(aminoMsg).toEqual(expected);
});
// distribution
it("works for MsgFundCommunityPool", async () => {
const msg: MsgFundCommunityPool = {
amount: coins(1234, "ucosm"),
@ -163,6 +249,8 @@ describe("AminoTypes", () => {
expect(aminoMsg).toEqual(expected);
});
// staking
it("works for MsgBeginRedelegate", () => {
const msg: MsgBeginRedelegate = {
delegatorAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
@ -318,6 +406,8 @@ describe("AminoTypes", () => {
expect(aminoMsg).toEqual(expected);
});
// ibc
it("works for MsgTransfer", () => {
const msg: MsgTransfer = {
sourcePort: "testport",
@ -417,6 +507,8 @@ describe("AminoTypes", () => {
expect(aminoMsg).toEqual(expected);
});
// other
it("works with custom type url", () => {
const msg = {
foo: "bar",
@ -483,6 +575,8 @@ describe("AminoTypes", () => {
});
describe("fromAmino", () => {
// bank
it("works for MsgSend", () => {
const aminoMsg: AminoMsgSend = {
type: "cosmos-sdk/MsgSend",
@ -535,6 +629,92 @@ describe("AminoTypes", () => {
});
});
// gov
it("works for MsgDeposit", () => {
const aminoMsg: AminoMsgDeposit = {
type: "cosmos-sdk/MsgDeposit",
value: {
amount: [{ amount: "12300000", denom: "ustake" }],
depositor: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
proposal_id: "5",
},
};
const msg = new AminoTypes().fromAmino(aminoMsg);
const expectedValue: MsgDeposit = {
amount: [{ amount: "12300000", denom: "ustake" }],
depositor: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
proposalId: Long.fromNumber(5),
};
expect(msg).toEqual({
typeUrl: "/cosmos.gov.v1beta1.MsgDeposit",
value: expectedValue,
});
});
it("works for MsgSubmitProposal", () => {
const aminoMsg: AminoMsgSubmitProposal = {
type: "cosmos-sdk/MsgSubmitProposal",
value: {
initial_deposit: [{ amount: "12300000", denom: "ustake" }],
proposer: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
content: {
type: "cosmos-sdk/TextProposal",
value: {
description: "This proposal proposes to test whether this proposal passes",
title: "Test Proposal",
},
},
},
};
const msg = new AminoTypes().fromAmino(aminoMsg);
const expectedValue: MsgSubmitProposal = {
initialDeposit: [{ amount: "12300000", denom: "ustake" }],
proposer: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
content: {
typeUrl: "/cosmos.gov.v1beta1.TextProposal",
value: TextProposal.encode({
description: "This proposal proposes to test whether this proposal passes",
title: "Test Proposal",
}).finish(),
},
};
expect(msg).toEqual({
typeUrl: "/cosmos.gov.v1beta1.MsgSubmitProposal",
value: expectedValue,
});
});
it("works for MsgVote", () => {
const aminoMsg: AminoMsgVote = {
type: "cosmos-sdk/MsgVote",
value: {
option: 4,
proposal_id: "5",
voter: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
},
};
const msg = new AminoTypes().fromAmino(aminoMsg);
const expectedValue: MsgVote = {
option: VoteOption.VOTE_OPTION_NO_WITH_VETO,
proposalId: Long.fromNumber(5),
voter: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
};
expect(msg).toEqual({
typeUrl: "/cosmos.gov.v1beta1.MsgVote",
value: expectedValue,
});
});
// distribution
// TODO: MsgFundCommunityPool
// TODO: MsgSetWithdrawAddress
// TODO: MsgWithdrawDelegatorReward
// TODO: MsgWithdrawValidatorCommission
// staking
it("works for MsgBeginRedelegate", () => {
const aminoMsg: AminoMsgBeginRedelegate = {
type: "cosmos-sdk/MsgBeginRedelegate",
@ -690,6 +870,8 @@ describe("AminoTypes", () => {
});
});
// ibc
it("works for MsgTransfer", () => {
const aminoMsg: AminoMsgTransfer = {
type: "cosmos-sdk/MsgTransfer",
@ -760,6 +942,8 @@ describe("AminoTypes", () => {
});
});
// other
it("works for custom type url", () => {
const aminoMsg = {
type: "my-sdk/CustomType",

View File

@ -2,7 +2,7 @@
import { AminoMsg, decodeBech32Pubkey, encodeBech32Pubkey } from "@cosmjs/amino";
import { fromBase64, toBase64 } from "@cosmjs/encoding";
import { EncodeObject } from "@cosmjs/proto-signing";
import { assertDefinedAndNotNull } from "@cosmjs/utils";
import { assert, assertDefinedAndNotNull, isNonNullObject } from "@cosmjs/utils";
import { MsgMultiSend, MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx";
import {
MsgFundCommunityPool,
@ -10,6 +10,8 @@ import {
MsgWithdrawDelegatorReward,
MsgWithdrawValidatorCommission,
} from "cosmjs-types/cosmos/distribution/v1beta1/tx";
import { TextProposal, voteOptionFromJSON } from "cosmjs-types/cosmos/gov/v1beta1/gov";
import { MsgDeposit, MsgSubmitProposal, MsgVote } from "cosmjs-types/cosmos/gov/v1beta1/tx";
import {
MsgBeginRedelegate,
MsgCreateValidator,
@ -17,6 +19,7 @@ import {
MsgEditValidator,
MsgUndelegate,
} from "cosmjs-types/cosmos/staking/v1beta1/tx";
import { Any } from "cosmjs-types/google/protobuf/any";
import { MsgTransfer } from "cosmjs-types/ibc/applications/transfer/v1/tx";
import Long from "long";
@ -24,13 +27,16 @@ import {
AminoMsgBeginRedelegate,
AminoMsgCreateValidator,
AminoMsgDelegate,
AminoMsgDeposit,
AminoMsgEditValidator,
AminoMsgFundCommunityPool,
AminoMsgMultiSend,
AminoMsgSend,
AminoMsgSetWithdrawAddress,
AminoMsgSubmitProposal,
AminoMsgTransfer,
AminoMsgUndelegate,
AminoMsgVote,
AminoMsgWithdrawDelegatorReward,
AminoMsgWithdrawValidatorCommission,
} from "./aminomsgs";
@ -59,6 +65,8 @@ function omitDefault<T extends string | number | Long>(input: T): T | undefined
function createDefaultTypes(prefix: string): Record<string, AminoConverter> {
return {
// bank
"/cosmos.bank.v1beta1.MsgSend": {
aminoType: "cosmos-sdk/MsgSend",
toAmino: ({ fromAddress, toAddress, amount }: MsgSend): AminoMsgSend["value"] => ({
@ -95,6 +103,9 @@ function createDefaultTypes(prefix: string): Record<string, AminoConverter> {
})),
}),
},
// distribution
"/cosmos.distribution.v1beta1.MsgFundCommunityPool": {
aminoType: "cosmos-sdk/MsgFundCommunityPool",
toAmino: ({ amount, depositor }: MsgFundCommunityPool): AminoMsgFundCommunityPool["value"] => ({
@ -153,6 +164,110 @@ function createDefaultTypes(prefix: string): Record<string, AminoConverter> {
validatorAddress: validator_address,
}),
},
// gov
"/cosmos.gov.v1beta1.MsgDeposit": {
aminoType: "cosmos-sdk/MsgDeposit",
toAmino: ({ amount, depositor, proposalId }: MsgDeposit): AminoMsgDeposit["value"] => {
return {
amount,
depositor,
proposal_id: proposalId.toString(),
};
},
fromAmino: ({ amount, depositor, proposal_id }: AminoMsgDeposit["value"]): MsgDeposit => {
return {
amount: Array.from(amount),
depositor,
proposalId: Long.fromString(proposal_id),
};
},
},
"/cosmos.gov.v1beta1.MsgVote": {
aminoType: "cosmos-sdk/MsgVote",
toAmino: ({ option, proposalId, voter }: MsgVote): AminoMsgVote["value"] => {
return {
option: option,
proposal_id: proposalId.toString(),
voter: voter,
};
},
fromAmino: ({ option, proposal_id, voter }: AminoMsgVote["value"]): MsgVote => {
return {
option: voteOptionFromJSON(option),
proposalId: Long.fromString(proposal_id),
voter: voter,
};
},
},
"/cosmos.gov.v1beta1.MsgSubmitProposal": {
aminoType: "cosmos-sdk/MsgSubmitProposal",
toAmino: ({
initialDeposit,
proposer,
content,
}: MsgSubmitProposal): AminoMsgSubmitProposal["value"] => {
assertDefinedAndNotNull(content);
let proposal: any;
switch (content.typeUrl) {
case "/cosmos.gov.v1beta1.TextProposal": {
const textProposal = TextProposal.decode(content.value);
proposal = {
type: "cosmos-sdk/TextProposal",
value: {
description: textProposal.description,
title: textProposal.title,
},
};
break;
}
default:
throw new Error(`Unsupported proposal type: '${content.typeUrl}'`);
}
return {
initial_deposit: initialDeposit,
proposer: proposer,
content: proposal,
};
},
fromAmino: ({
initial_deposit,
proposer,
content,
}: AminoMsgSubmitProposal["value"]): MsgSubmitProposal => {
let any_content: Any;
switch (content.type) {
case "cosmos-sdk/TextProposal": {
const { value } = content;
assert(isNonNullObject(value));
const { title, description } = value as any;
assert(typeof title === "string");
assert(typeof description === "string");
any_content = Any.fromPartial({
typeUrl: "/cosmos.gov.v1beta1.TextProposal",
value: TextProposal.encode(
TextProposal.fromPartial({
title: title,
description: description,
}),
).finish(),
});
break;
}
default:
throw new Error(`Unsupported proposal type: '${content.type}'`);
}
return {
initialDeposit: Array.from(initial_deposit),
proposer: proposer,
content: any_content,
};
},
},
// staking
"/cosmos.staking.v1beta1.MsgBeginRedelegate": {
aminoType: "cosmos-sdk/MsgBeginRedelegate",
toAmino: ({
@ -343,6 +458,9 @@ function createDefaultTypes(prefix: string): Record<string, AminoConverter> {
amount: amount,
}),
},
// ibc
"/ibc.applications.transfer.v1.MsgTransfer": {
aminoType: "cosmos-sdk/MsgTransfer",
toAmino: ({

View File

@ -1,7 +1,7 @@
import { EncodeObject } from "@cosmjs/proto-signing";
import { MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx";
import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx";
import { MsgSubmitProposal, MsgVote } from "cosmjs-types/cosmos/gov/v1beta1/tx";
import { MsgDeposit, MsgSubmitProposal, MsgVote } from "cosmjs-types/cosmos/gov/v1beta1/tx";
import { MsgDelegate, MsgUndelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx";
import { MsgTransfer } from "cosmjs-types/ibc/applications/transfer/v1/tx";
@ -61,6 +61,17 @@ export function isMsgTransferEncodeObject(
return (encodeObject as MsgTransferEncodeObject).typeUrl === "/ibc.applications.transfer.v1.MsgTransfer";
}
export interface MsgDepositEncodeObject extends EncodeObject {
readonly typeUrl: "/cosmos.gov.v1beta1.MsgDeposit";
readonly value: Partial<MsgDeposit>;
}
export function isMsgDepositEncodeObject(
encodeObject: EncodeObject,
): encodeObject is MsgSubmitProposalEncodeObject {
return (encodeObject as MsgDepositEncodeObject).typeUrl === "/cosmos.gov.v1beta1.MsgDeposit";
}
export interface MsgSubmitProposalEncodeObject extends EncodeObject {
readonly typeUrl: "/cosmos.gov.v1beta1.MsgSubmitProposal";
readonly value: Partial<MsgSubmitProposal>;

View File

@ -41,6 +41,7 @@ export {
export { AminoConverter, AminoTypes } from "./aminotypes";
export {
isMsgDelegateEncodeObject,
isMsgDepositEncodeObject,
isMsgSendEncodeObject,
isMsgSubmitProposalEncodeObject,
isMsgTransferEncodeObject,
@ -48,6 +49,7 @@ export {
isMsgVoteEncodeObject,
isMsgWithdrawDelegatorRewardEncodeObject,
MsgDelegateEncodeObject,
MsgDepositEncodeObject,
MsgSendEncodeObject,
MsgSubmitProposalEncodeObject,
MsgTransferEncodeObject,

View File

@ -46,7 +46,7 @@ describe("GovExtension", () => {
const delegationVoter2 = coin(777, "ustake");
const voter1Address = faucet.address1;
const voter2Address = faucet.address2;
let proposalId: string;
let proposalId: string | undefined;
beforeAll(async () => {
if (simappEnabled()) {
@ -83,6 +83,7 @@ describe("GovExtension", () => {
proposalId = logs[0].events
.find(({ type }: any) => type === "submit_proposal")
.attributes.find(({ key }: any) => key === "proposal_id").value;
assert(proposalId, "Proposal ID not found in events");
assert(proposalId.match(nonNegativeIntegerMatcher));
// Voter 1
@ -210,6 +211,7 @@ describe("GovExtension", () => {
describe("proposals", () => {
it("works", async () => {
pendingWithoutSimapp();
assert(proposalId, "Missing proposal ID");
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
const response = await client.gov.proposals(
@ -240,6 +242,7 @@ describe("GovExtension", () => {
describe("proposal", () => {
it("works", async () => {
pendingWithoutSimapp();
assert(proposalId, "Missing proposal ID");
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
const response = await client.gov.proposal(proposalId);
@ -265,6 +268,7 @@ describe("GovExtension", () => {
describe("deposits", () => {
it("works", async () => {
pendingWithoutSimapp();
assert(proposalId, "Missing proposal ID");
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
const response = await client.gov.deposits(proposalId);
@ -283,6 +287,7 @@ describe("GovExtension", () => {
describe("deposit", () => {
it("works", async () => {
pendingWithoutSimapp();
assert(proposalId, "Missing proposal ID");
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
const response = await client.gov.deposit(proposalId, voter1Address);
@ -299,6 +304,7 @@ describe("GovExtension", () => {
describe("tally", () => {
it("works", async () => {
pendingWithoutSimapp();
assert(proposalId, "Missing proposal ID");
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
const response = await client.gov.tally(proposalId);
@ -316,6 +322,7 @@ describe("GovExtension", () => {
describe("votes", () => {
it("works", async () => {
pendingWithoutSimapp();
assert(proposalId, "Missing proposal ID");
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
const response = await client.gov.votes(proposalId);
@ -340,6 +347,7 @@ describe("GovExtension", () => {
describe("vote", () => {
it("works", async () => {
pendingWithoutSimapp();
assert(proposalId, "Missing proposal ID");
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
const response = await client.gov.vote(proposalId, voter1Address);

View File

@ -22,7 +22,7 @@ import {
MsgWithdrawDelegatorReward,
MsgWithdrawValidatorCommission,
} from "cosmjs-types/cosmos/distribution/v1beta1/tx";
import { MsgSubmitProposal, MsgVote } from "cosmjs-types/cosmos/gov/v1beta1/tx";
import { MsgDeposit, MsgSubmitProposal, MsgVote } from "cosmjs-types/cosmos/gov/v1beta1/tx";
import {
MsgBeginRedelegate,
MsgCreateValidator,
@ -76,6 +76,7 @@ export const defaultRegistryTypes: ReadonlyArray<[string, GeneratedType]> = [
["/cosmos.distribution.v1beta1.MsgSetWithdrawAddress", MsgSetWithdrawAddress],
["/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", MsgWithdrawDelegatorReward],
["/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission", MsgWithdrawValidatorCommission],
["/cosmos.gov.v1beta1.MsgDeposit", MsgDeposit],
["/cosmos.gov.v1beta1.MsgSubmitProposal", MsgSubmitProposal],
["/cosmos.gov.v1beta1.MsgVote", MsgVote],
["/cosmos.staking.v1beta1.MsgBeginRedelegate", MsgBeginRedelegate],