diff --git a/packages/stargate/src/aminoconverters.ts b/packages/stargate/src/aminoconverters.ts new file mode 100644 index 00000000..8657992b --- /dev/null +++ b/packages/stargate/src/aminoconverters.ts @@ -0,0 +1,8 @@ +export interface AminoConverter { + readonly aminoType: string; + readonly toAmino: (value: any) => any; + readonly fromAmino: (value: any) => any; +} + +/** A map from protobuf type URL to the AminoConverter implementation if supported on chain */ +export type AminoConverters = Record; diff --git a/packages/stargate/src/aminomsgs.ts b/packages/stargate/src/aminomsgs.ts index f39c5600..05c0022c 100644 --- a/packages/stargate/src/aminomsgs.ts +++ b/packages/stargate/src/aminomsgs.ts @@ -1,50 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { AminoMsg, Coin } from "@cosmjs/amino"; - -// auth (no messages) - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/auth/auth.proto - -// bank - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/bank/bank.proto - -/** A high level transaction of the coin module */ -export interface AminoMsgSend extends AminoMsg { - readonly type: "cosmos-sdk/MsgSend"; - readonly value: { - /** Bech32 account address */ - readonly from_address: string; - /** Bech32 account address */ - readonly to_address: string; - readonly amount: readonly Coin[]; - }; -} - -export function isAminoMsgSend(msg: AminoMsg): msg is AminoMsgSend { - return msg.type === "cosmos-sdk/MsgSend"; -} - -interface Input { - /** Bech32 account address */ - readonly address: string; - readonly coins: readonly Coin[]; -} - -interface Output { - /** Bech32 account address */ - readonly address: string; - readonly coins: readonly Coin[]; -} - -/** A high level transaction of the coin module */ -export interface AminoMsgMultiSend extends AminoMsg { - readonly type: "cosmos-sdk/MsgMultiSend"; - readonly value: { - readonly inputs: readonly Input[]; - readonly outputs: readonly Output[]; - }; -} - -export function isAminoMsgMultiSend(msg: AminoMsg): msg is AminoMsgMultiSend { - return msg.type === "cosmos-sdk/MsgMultiSend"; -} +import { AminoMsg } from "@cosmjs/amino"; // crisis - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/crisis/crisis.proto @@ -65,69 +20,6 @@ export function isAminoMsgVerifyInvariant(msg: AminoMsg): msg is AminoMsgVerifyI // distribution - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/distribution/distribution.proto -/** Changes the withdraw address for a delegator (or validator self-delegation) */ -export interface AminoMsgSetWithdrawAddress extends AminoMsg { - // NOTE: Type string and names diverge here! - readonly type: "cosmos-sdk/MsgModifyWithdrawAddress"; - readonly value: { - /** Bech32 account address */ - readonly delegator_address: string; - /** Bech32 account address */ - readonly withdraw_address: string; - }; -} - -export function isAminoMsgSetWithdrawAddress(msg: AminoMsg): msg is AminoMsgSetWithdrawAddress { - // NOTE: Type string and names diverge here! - return msg.type === "cosmos-sdk/MsgModifyWithdrawAddress"; -} - -/** Message for delegation withdraw from a single validator */ -export interface AminoMsgWithdrawDelegatorReward extends AminoMsg { - // NOTE: Type string and names diverge here! - readonly type: "cosmos-sdk/MsgWithdrawDelegationReward"; - readonly value: { - /** Bech32 account address */ - readonly delegator_address: string; - /** Bech32 account address */ - readonly validator_address: string; - }; -} - -export function isAminoMsgWithdrawDelegatorReward(msg: AminoMsg): msg is AminoMsgWithdrawDelegatorReward { - // NOTE: Type string and names diverge here! - return msg.type === "cosmos-sdk/MsgWithdrawDelegationReward"; -} - -/** Message for validator withdraw */ -export interface AminoMsgWithdrawValidatorCommission extends AminoMsg { - readonly type: "cosmos-sdk/MsgWithdrawValidatorCommission"; - readonly value: { - /** Bech32 account address */ - readonly validator_address: string; - }; -} - -export function isAminoMsgWithdrawValidatorCommission( - msg: AminoMsg, -): msg is AminoMsgWithdrawValidatorCommission { - return msg.type === "cosmos-sdk/MsgWithdrawValidatorCommission"; -} - -/** Allows an account to directly fund the community pool. */ -export interface AminoMsgFundCommunityPool extends AminoMsg { - readonly type: "cosmos-sdk/MsgFundCommunityPool"; - readonly value: { - readonly amount: readonly Coin[]; - /** Bech32 account address */ - readonly depositor: string; - }; -} - -export function isAminoMsgFundCommunityPool(msg: AminoMsg): msg is AminoMsgFundCommunityPool { - return msg.type === "cosmos-sdk/MsgFundCommunityPool"; -} - // evidence - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/evidence/evidence.proto interface Any { @@ -151,72 +43,6 @@ export function isAminoMsgSubmitEvidence(msg: AminoMsg): msg is AminoMsgSubmitEv // gov - https://github.com/cosmos/cosmos-sdk/blob/efa73c7edb31a7bd65786501da213b294f89267a/proto/cosmos/gov/gov.proto -/** Supports submitting arbitrary proposal content. */ -export interface AminoMsgSubmitProposal extends AminoMsg { - readonly type: "cosmos-sdk/MsgSubmitProposal"; - readonly value: { - /** - * 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; - }; -} - -export function isAminoMsgSubmitProposal(msg: AminoMsg): msg is AminoMsgSubmitProposal { - return msg.type === "cosmos-sdk/MsgSubmitProposal"; -} - -/** Casts a vote */ -export interface AminoMsgVote extends AminoMsg { - readonly type: "cosmos-sdk/MsgVote"; - readonly value: { - readonly proposal_id: string; - /** Bech32 account address */ - readonly voter: string; - /** - * 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; - }; -} - -export function isAminoMsgVote(msg: AminoMsg): msg is AminoMsgVote { - return msg.type === "cosmos-sdk/MsgVote"; -} - -/** Submits a deposit to an existing proposal */ -export interface AminoMsgDeposit extends AminoMsg { - readonly type: "cosmos-sdk/MsgDeposit"; - readonly value: { - readonly proposal_id: string; - /** Bech32 account address */ - readonly depositor: string; - readonly amount: readonly Coin[]; - }; -} - -export function isAminoMsgDeposit(msg: AminoMsg): msg is AminoMsgDeposit { - return msg.type === "cosmos-sdk/MsgDeposit"; -} - // mint (no messages) - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/mint/mint.proto // params (no messages) - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/params/params.proto @@ -235,156 +61,3 @@ export interface AminoMsgUnjail extends AminoMsg { export function isAminoMsgUnjail(msg: AminoMsg): msg is AminoMsgUnjail { return msg.type === "cosmos-sdk/MsgUnjail"; } - -// staking - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/staking/staking.proto - -/** The initial commission rates to be used for creating a validator */ -interface CommissionRates { - readonly rate: string; - readonly max_rate: string; - readonly max_change_rate: string; -} - -/** A validator description. */ -interface Description { - readonly moniker: string; - readonly identity: string; - readonly website: string; - readonly security_contact: string; - readonly details: string; -} - -/** Creates a new validator. */ -export interface AminoMsgCreateValidator extends AminoMsg { - readonly type: "cosmos-sdk/MsgCreateValidator"; - readonly value: { - readonly description: Description; - readonly commission: CommissionRates; - readonly min_self_delegation: string; - /** Bech32 encoded delegator address */ - readonly delegator_address: string; - /** Bech32 encoded validator address */ - readonly validator_address: string; - /** Bech32 encoded public key */ - readonly pubkey: string; - readonly value: Coin; - }; -} - -export function isAminoMsgCreateValidator(msg: AminoMsg): msg is AminoMsgCreateValidator { - return msg.type === "cosmos-sdk/MsgCreateValidator"; -} - -/** Edits an existing validator. */ -export interface AminoMsgEditValidator extends AminoMsg { - readonly type: "cosmos-sdk/MsgEditValidator"; - readonly value: { - readonly description: Description; - /** Bech32 encoded validator address */ - readonly validator_address: string; - readonly commission_rate: string; - readonly min_self_delegation: string; - }; -} - -export function isAminoMsgEditValidator(msg: AminoMsg): msg is AminoMsgEditValidator { - return msg.type === "cosmos-sdk/MsgEditValidator"; -} - -/** - * Performs a delegation from a delegate to a validator. - * - * @see https://docs.cosmos.network/master/modules/staking/03_messages.html#msgdelegate - */ -export interface AminoMsgDelegate extends AminoMsg { - readonly type: "cosmos-sdk/MsgDelegate"; - readonly value: { - /** Bech32 encoded delegator address */ - readonly delegator_address: string; - /** Bech32 encoded validator address */ - readonly validator_address: string; - readonly amount: Coin; - }; -} - -export function isAminoMsgDelegate(msg: AminoMsg): msg is AminoMsgDelegate { - return msg.type === "cosmos-sdk/MsgDelegate"; -} - -/** Performs a redelegation from a delegate and source validator to a destination validator */ -export interface AminoMsgBeginRedelegate extends AminoMsg { - readonly type: "cosmos-sdk/MsgBeginRedelegate"; - readonly value: { - /** Bech32 encoded delegator address */ - readonly delegator_address: string; - /** Bech32 encoded source validator address */ - readonly validator_src_address: string; - /** Bech32 encoded destination validator address */ - readonly validator_dst_address: string; - readonly amount: Coin; - }; -} - -export function isAminoMsgBeginRedelegate(msg: AminoMsg): msg is AminoMsgBeginRedelegate { - return msg.type === "cosmos-sdk/MsgBeginRedelegate"; -} - -/** Performs an undelegation from a delegate and a validator */ -export interface AminoMsgUndelegate extends AminoMsg { - readonly type: "cosmos-sdk/MsgUndelegate"; - readonly value: { - /** Bech32 encoded delegator address */ - readonly delegator_address: string; - /** Bech32 encoded validator address */ - readonly validator_address: string; - readonly amount: Coin; - }; -} - -export function isAminoMsgUndelegate(msg: AminoMsg): msg is AminoMsgUndelegate { - return msg.type === "cosmos-sdk/MsgUndelegate"; -} - -// upgrade (no messages) - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/upgrade/upgrade.proto - -// ibc - -// https://github.com/cosmos/ibc-go/blob/07b6a97b67d17fd214a83764cbdb2c2c3daef445/modules/core/02-client/types/client.pb.go#L297-L312 -interface AminoHeight { - /** 0 values must be omitted (https://github.com/cosmos/cosmos-sdk/blob/v0.42.7/x/ibc/core/02-client/types/client.pb.go#L252). */ - readonly revision_number?: string; - /** 0 values must be omitted (https://github.com/cosmos/cosmos-sdk/blob/v0.42.7/x/ibc/core/02-client/types/client.pb.go#L254). */ - readonly revision_height?: string; -} - -// https://github.com/cosmos/ibc-go/blob/07b6a97b67d17fd214a83764cbdb2c2c3daef445/modules/apps/transfer/types/tx.pb.go#L33-L53 -/** Transfers fungible tokens (i.e Coins) between ICS20 enabled chains */ -export interface AminoMsgTransfer extends AminoMsg { - readonly type: "cosmos-sdk/MsgTransfer"; - readonly value: { - readonly source_port: string; - readonly source_channel: string; - readonly token?: Coin; - /** Bech32 account address */ - readonly sender: string; - /** Bech32 account address */ - readonly receiver: string; - /** - * The timeout as a (revision_number, revision_height) pair. - * - * This fied is is non-optional (https://github.com/cosmos/cosmos-sdk/blob/v0.42.7/x/ibc/applications/transfer/types/tx.pb.go#L49). - * In order to not set the timeout height, set it to {}. - */ - readonly timeout_height: AminoHeight; - /** - * Timeout timestamp in nanoseconds since Unix epoch. The timeout is disabled when set to 0. - * - * 0 values must be omitted (https://github.com/cosmos/cosmos-sdk/blob/v0.42.7/x/ibc/applications/transfer/types/tx.pb.go#L52). - */ - readonly timeout_timestamp?: string; - }; -} - -export function isAminoMsgTransfer(msg: AminoMsg): msg is AminoMsgTransfer { - return msg.type === "cosmos-sdk/MsgTransfer"; -} diff --git a/packages/stargate/src/aminotypes.spec.ts b/packages/stargate/src/aminotypes.spec.ts index 82fa6d62..aef24686 100644 --- a/packages/stargate/src/aminotypes.spec.ts +++ b/packages/stargate/src/aminotypes.spec.ts @@ -21,6 +21,7 @@ import { import { MsgTransfer } from "cosmjs-types/ibc/applications/transfer/v1/tx"; import Long from "long"; +import { AminoTypes } from "./aminotypes"; import { AminoMsgBeginRedelegate, AminoMsgCreateValidator, @@ -37,8 +38,7 @@ import { AminoMsgVote, AminoMsgWithdrawDelegatorReward, AminoMsgWithdrawValidatorCommission, -} from "./aminomsgs"; -import { AminoTypes } from "./aminotypes"; +} from "./modules"; describe("AminoTypes", () => { describe("constructor", () => { diff --git a/packages/stargate/src/aminotypes.ts b/packages/stargate/src/aminotypes.ts index ac0fafa1..236705ba 100644 --- a/packages/stargate/src/aminotypes.ts +++ b/packages/stargate/src/aminotypes.ts @@ -1,519 +1,27 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { AminoMsg, decodeBech32Pubkey, encodeBech32Pubkey } from "@cosmjs/amino"; -import { fromBase64, toBase64 } from "@cosmjs/encoding"; +import { AminoMsg } from "@cosmjs/amino"; import { EncodeObject } from "@cosmjs/proto-signing"; -import { assert, assertDefinedAndNotNull, isNonNullObject } from "@cosmjs/utils"; -import { MsgMultiSend, MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx"; + +import { AminoConverter, AminoConverters } from "./aminoconverters"; import { - MsgFundCommunityPool, - MsgSetWithdrawAddress, - 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, - MsgDelegate, - 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"; + createAuthzAminoConverters, + createBankAminoConverters, + createDistributionAminoConverters, + createFreegrantAminoConverters, + createGovAminoConverters, + createIbcAminoConverters, + createStakingAminoConverters, +} from "./modules"; -import { - AminoMsgBeginRedelegate, - AminoMsgCreateValidator, - AminoMsgDelegate, - AminoMsgDeposit, - AminoMsgEditValidator, - AminoMsgFundCommunityPool, - AminoMsgMultiSend, - AminoMsgSend, - AminoMsgSetWithdrawAddress, - AminoMsgSubmitProposal, - AminoMsgTransfer, - AminoMsgUndelegate, - AminoMsgVote, - AminoMsgWithdrawDelegatorReward, - AminoMsgWithdrawValidatorCommission, -} from "./aminomsgs"; - -export interface AminoConverter { - readonly aminoType: string; - readonly toAmino: (value: any) => any; - readonly fromAmino: (value: any) => any; -} - -function omitDefault(input: T): T | undefined { - if (typeof input === "string") { - return input === "" ? undefined : input; - } - - if (typeof input === "number") { - return input === 0 ? undefined : input; - } - - if (Long.isLong(input)) { - return input.isZero() ? undefined : input; - } - - throw new Error(`Got unsupported type '${typeof input}'`); -} - -function createDefaultTypes(prefix: string): Record { +function createDefaultTypes(prefix: string): AminoConverters { return { - // authz - "/cosmos.authz.v1beta1.MsgGrant": "not_supported_by_chain", - "/cosmos.authz.v1beta1.MsgExec": "not_supported_by_chain", - "/cosmos.authz.v1beta1.MsgRevoke": "not_supported_by_chain", - - // bank - "/cosmos.bank.v1beta1.MsgSend": { - aminoType: "cosmos-sdk/MsgSend", - toAmino: ({ fromAddress, toAddress, amount }: MsgSend): AminoMsgSend["value"] => ({ - from_address: fromAddress, - to_address: toAddress, - amount: [...amount], - }), - fromAmino: ({ from_address, to_address, amount }: AminoMsgSend["value"]): MsgSend => ({ - fromAddress: from_address, - toAddress: to_address, - amount: [...amount], - }), - }, - "/cosmos.bank.v1beta1.MsgMultiSend": { - aminoType: "cosmos-sdk/MsgMultiSend", - toAmino: ({ inputs, outputs }: MsgMultiSend): AminoMsgMultiSend["value"] => ({ - inputs: inputs.map((input) => ({ - address: input.address, - coins: [...input.coins], - })), - outputs: outputs.map((output) => ({ - address: output.address, - coins: [...output.coins], - })), - }), - fromAmino: ({ inputs, outputs }: AminoMsgMultiSend["value"]): MsgMultiSend => ({ - inputs: inputs.map((input) => ({ - address: input.address, - coins: [...input.coins], - })), - outputs: outputs.map((output) => ({ - address: output.address, - coins: [...output.coins], - })), - }), - }, - - // distribution - - "/cosmos.distribution.v1beta1.MsgFundCommunityPool": { - aminoType: "cosmos-sdk/MsgFundCommunityPool", - toAmino: ({ amount, depositor }: MsgFundCommunityPool): AminoMsgFundCommunityPool["value"] => ({ - amount: [...amount], - depositor: depositor, - }), - fromAmino: ({ amount, depositor }: AminoMsgFundCommunityPool["value"]): MsgFundCommunityPool => ({ - amount: [...amount], - depositor: depositor, - }), - }, - "/cosmos.distribution.v1beta1.MsgSetWithdrawAddress": { - aminoType: "cosmos-sdk/MsgModifyWithdrawAddress", - toAmino: ({ - delegatorAddress, - withdrawAddress, - }: MsgSetWithdrawAddress): AminoMsgSetWithdrawAddress["value"] => ({ - delegator_address: delegatorAddress, - withdraw_address: withdrawAddress, - }), - fromAmino: ({ - delegator_address, - withdraw_address, - }: AminoMsgSetWithdrawAddress["value"]): MsgSetWithdrawAddress => ({ - delegatorAddress: delegator_address, - withdrawAddress: withdraw_address, - }), - }, - "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward": { - aminoType: "cosmos-sdk/MsgWithdrawDelegationReward", - toAmino: ({ - delegatorAddress, - validatorAddress, - }: MsgWithdrawDelegatorReward): AminoMsgWithdrawDelegatorReward["value"] => ({ - delegator_address: delegatorAddress, - validator_address: validatorAddress, - }), - fromAmino: ({ - delegator_address, - validator_address, - }: AminoMsgWithdrawDelegatorReward["value"]): MsgWithdrawDelegatorReward => ({ - delegatorAddress: delegator_address, - validatorAddress: validator_address, - }), - }, - "/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission": { - aminoType: "cosmos-sdk/MsgWithdrawValidatorCommission", - toAmino: ({ - validatorAddress, - }: MsgWithdrawValidatorCommission): AminoMsgWithdrawValidatorCommission["value"] => ({ - validator_address: validatorAddress, - }), - fromAmino: ({ - validator_address, - }: AminoMsgWithdrawValidatorCommission["value"]): MsgWithdrawValidatorCommission => ({ - 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: ({ - delegatorAddress, - validatorSrcAddress, - validatorDstAddress, - amount, - }: MsgBeginRedelegate): AminoMsgBeginRedelegate["value"] => { - assertDefinedAndNotNull(amount, "missing amount"); - return { - delegator_address: delegatorAddress, - validator_src_address: validatorSrcAddress, - validator_dst_address: validatorDstAddress, - amount: amount, - }; - }, - fromAmino: ({ - delegator_address, - validator_src_address, - validator_dst_address, - amount, - }: AminoMsgBeginRedelegate["value"]): MsgBeginRedelegate => ({ - 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, - }: MsgCreateValidator): AminoMsgCreateValidator["value"] => { - assertDefinedAndNotNull(description, "missing description"); - assertDefinedAndNotNull(commission, "missing commission"); - assertDefinedAndNotNull(pubkey, "missing pubkey"); - assertDefinedAndNotNull(value, "missing value"); - 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: value, - }; - }, - fromAmino: ({ - description, - commission, - min_self_delegation, - delegator_address, - validator_address, - pubkey, - value, - }: AminoMsgCreateValidator["value"]): MsgCreateValidator => { - 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: { - typeUrl: "/cosmos.crypto.secp256k1.PubKey", - value: fromBase64(decodedPubkey.value), - }, - value: value, - }; - }, - }, - "/cosmos.staking.v1beta1.MsgDelegate": { - aminoType: "cosmos-sdk/MsgDelegate", - toAmino: ({ delegatorAddress, validatorAddress, amount }: MsgDelegate): AminoMsgDelegate["value"] => { - assertDefinedAndNotNull(amount, "missing amount"); - return { - delegator_address: delegatorAddress, - validator_address: validatorAddress, - amount: amount, - }; - }, - fromAmino: ({ - delegator_address, - validator_address, - amount, - }: AminoMsgDelegate["value"]): MsgDelegate => ({ - delegatorAddress: delegator_address, - validatorAddress: validator_address, - amount: amount, - }), - }, - "/cosmos.staking.v1beta1.MsgEditValidator": { - aminoType: "cosmos-sdk/MsgEditValidator", - toAmino: ({ - description, - commissionRate, - minSelfDelegation, - validatorAddress, - }: MsgEditValidator): AminoMsgEditValidator["value"] => { - assertDefinedAndNotNull(description, "missing description"); - 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, - }: AminoMsgEditValidator["value"]): MsgEditValidator => ({ - 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, - }: MsgUndelegate): AminoMsgUndelegate["value"] => { - assertDefinedAndNotNull(amount, "missing amount"); - return { - delegator_address: delegatorAddress, - validator_address: validatorAddress, - amount: amount, - }; - }, - fromAmino: ({ - delegator_address, - validator_address, - amount, - }: AminoMsgUndelegate["value"]): MsgUndelegate => ({ - delegatorAddress: delegator_address, - validatorAddress: validator_address, - amount: amount, - }), - }, - - // ibc - - "/ibc.applications.transfer.v1.MsgTransfer": { - aminoType: "cosmos-sdk/MsgTransfer", - toAmino: ({ - sourcePort, - sourceChannel, - token, - sender, - receiver, - timeoutHeight, - timeoutTimestamp, - }: MsgTransfer): AminoMsgTransfer["value"] => ({ - source_port: sourcePort, - source_channel: sourceChannel, - token: token, - sender: sender, - receiver: receiver, - timeout_height: timeoutHeight - ? { - revision_height: omitDefault(timeoutHeight.revisionHeight)?.toString(), - revision_number: omitDefault(timeoutHeight.revisionNumber)?.toString(), - } - : {}, - timeout_timestamp: omitDefault(timeoutTimestamp)?.toString(), - }), - fromAmino: ({ - source_port, - source_channel, - token, - sender, - receiver, - timeout_height, - timeout_timestamp, - }: AminoMsgTransfer["value"]): MsgTransfer => ({ - sourcePort: source_port, - sourceChannel: source_channel, - token: token, - sender: sender, - receiver: receiver, - timeoutHeight: timeout_height - ? { - revisionHeight: Long.fromString(timeout_height.revision_height || "0", true), - revisionNumber: Long.fromString(timeout_height.revision_number || "0", true), - } - : undefined, - timeoutTimestamp: Long.fromString(timeout_timestamp || "0", true), - }), - }, - "/cosmos.feegrant.v1beta1.MsgGrantAllowance": "not_supported_by_chain", - "/cosmos.feegrant.v1beta1.MsgRevokeAllowance": "not_supported_by_chain", + ...createAuthzAminoConverters(), + ...createBankAminoConverters(), + ...createDistributionAminoConverters(), + ...createGovAminoConverters(), + ...createStakingAminoConverters(prefix), + ...createIbcAminoConverters(), + ...createFreegrantAminoConverters(), }; } @@ -522,7 +30,7 @@ export interface AminoTypesOptions { * The Bech32 address prefix of the chain you work with (also called Bech32 human-readable part). */ readonly prefix: string; - readonly additions?: Record; + readonly additions?: AminoConverters; } function isAminoConverter( diff --git a/packages/stargate/src/index.ts b/packages/stargate/src/index.ts index 34cb49ff..32f72bf3 100644 --- a/packages/stargate/src/index.ts +++ b/packages/stargate/src/index.ts @@ -1,4 +1,16 @@ export { Account, accountFromAny } from "./accounts"; +export { AminoConverter } from "./aminoconverters"; +export { + AminoMsgSubmitEvidence, + AminoMsgUnjail, + AminoMsgVerifyInvariant, + isAminoMsgSubmitEvidence, + isAminoMsgUnjail, + isAminoMsgVerifyInvariant, +} from "./aminomsgs"; +export { AminoTypes, AminoTypesOptions } from "./aminotypes"; +export { calculateFee, GasPrice } from "./fee"; +export * as logs from "./logs"; export { AminoMsgBeginRedelegate, AminoMsgCreateValidator, @@ -9,14 +21,18 @@ export { AminoMsgMultiSend, AminoMsgSend, AminoMsgSetWithdrawAddress, - AminoMsgSubmitEvidence, AminoMsgSubmitProposal, AminoMsgUndelegate, - AminoMsgUnjail, - AminoMsgVerifyInvariant, AminoMsgVote, AminoMsgWithdrawDelegatorReward, AminoMsgWithdrawValidatorCommission, + AuthExtension, + BankExtension, + DistributionExtension, + GovExtension, + GovParamsType, + GovProposalId, + IbcExtension, isAminoMsgBeginRedelegate, isAminoMsgCreateValidator, isAminoMsgDelegate, @@ -26,26 +42,11 @@ export { isAminoMsgMultiSend, isAminoMsgSend, isAminoMsgSetWithdrawAddress, - isAminoMsgSubmitEvidence, isAminoMsgSubmitProposal, isAminoMsgUndelegate, - isAminoMsgUnjail, - isAminoMsgVerifyInvariant, isAminoMsgVote, isAminoMsgWithdrawDelegatorReward, isAminoMsgWithdrawValidatorCommission, -} from "./aminomsgs"; -export { AminoConverter, AminoTypes, AminoTypesOptions } from "./aminotypes"; -export { calculateFee, GasPrice } from "./fee"; -export * as logs from "./logs"; -export { - AuthExtension, - BankExtension, - DistributionExtension, - GovExtension, - GovParamsType, - GovProposalId, - IbcExtension, isMsgDelegateEncodeObject, isMsgDepositEncodeObject, isMsgSendEncodeObject, diff --git a/packages/stargate/src/modules/authz/aminomessages.ts b/packages/stargate/src/modules/authz/aminomessages.ts new file mode 100644 index 00000000..149fdf6d --- /dev/null +++ b/packages/stargate/src/modules/authz/aminomessages.ts @@ -0,0 +1,9 @@ +import { AminoConverters } from "../../aminoconverters"; + +export function createAuthzAminoConverters(): AminoConverters { + return { + "/cosmos.authz.v1beta1.MsgGrant": "not_supported_by_chain", + "/cosmos.authz.v1beta1.MsgExec": "not_supported_by_chain", + "/cosmos.authz.v1beta1.MsgRevoke": "not_supported_by_chain", + }; +} diff --git a/packages/stargate/src/modules/bank/aminomessages.ts b/packages/stargate/src/modules/bank/aminomessages.ts new file mode 100644 index 00000000..f74530be --- /dev/null +++ b/packages/stargate/src/modules/bank/aminomessages.ts @@ -0,0 +1,88 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { AminoMsg, Coin } from "@cosmjs/amino"; +import { MsgMultiSend, MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx"; + +// eslint-disable-next-line import/no-cycle +import { AminoConverters } from "../../aminoconverters"; + +/** A high level transaction of the coin module */ +export interface AminoMsgSend extends AminoMsg { + readonly type: "cosmos-sdk/MsgSend"; + readonly value: { + /** Bech32 account address */ + readonly from_address: string; + /** Bech32 account address */ + readonly to_address: string; + readonly amount: readonly Coin[]; + }; +} + +export function isAminoMsgSend(msg: AminoMsg): msg is AminoMsgSend { + return msg.type === "cosmos-sdk/MsgSend"; +} + +interface Input { + /** Bech32 account address */ + readonly address: string; + readonly coins: readonly Coin[]; +} + +interface Output { + /** Bech32 account address */ + readonly address: string; + readonly coins: readonly Coin[]; +} + +/** A high level transaction of the coin module */ +export interface AminoMsgMultiSend extends AminoMsg { + readonly type: "cosmos-sdk/MsgMultiSend"; + readonly value: { + readonly inputs: readonly Input[]; + readonly outputs: readonly Output[]; + }; +} + +export function isAminoMsgMultiSend(msg: AminoMsg): msg is AminoMsgMultiSend { + return msg.type === "cosmos-sdk/MsgMultiSend"; +} + +export function createBankAminoConverters(): AminoConverters { + return { + "/cosmos.bank.v1beta1.MsgSend": { + aminoType: "cosmos-sdk/MsgSend", + toAmino: ({ fromAddress, toAddress, amount }: MsgSend): AminoMsgSend["value"] => ({ + from_address: fromAddress, + to_address: toAddress, + amount: [...amount], + }), + fromAmino: ({ from_address, to_address, amount }: AminoMsgSend["value"]): MsgSend => ({ + fromAddress: from_address, + toAddress: to_address, + amount: [...amount], + }), + }, + "/cosmos.bank.v1beta1.MsgMultiSend": { + aminoType: "cosmos-sdk/MsgMultiSend", + toAmino: ({ inputs, outputs }: MsgMultiSend): AminoMsgMultiSend["value"] => ({ + inputs: inputs.map((input) => ({ + address: input.address, + coins: [...input.coins], + })), + outputs: outputs.map((output) => ({ + address: output.address, + coins: [...output.coins], + })), + }), + fromAmino: ({ inputs, outputs }: AminoMsgMultiSend["value"]): MsgMultiSend => ({ + inputs: inputs.map((input) => ({ + address: input.address, + coins: [...input.coins], + })), + outputs: outputs.map((output) => ({ + address: output.address, + coins: [...output.coins], + })), + }), + }, + }; +} diff --git a/packages/stargate/src/modules/distribution/aminomessages.ts b/packages/stargate/src/modules/distribution/aminomessages.ts new file mode 100644 index 00000000..f9844f55 --- /dev/null +++ b/packages/stargate/src/modules/distribution/aminomessages.ts @@ -0,0 +1,139 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { AminoMsg, Coin } from "@cosmjs/amino"; +import { + MsgFundCommunityPool, + MsgSetWithdrawAddress, + MsgWithdrawDelegatorReward, + MsgWithdrawValidatorCommission, +} from "cosmjs-types/cosmos/distribution/v1beta1/tx"; + +import { AminoConverter } from "../.."; + +/** Changes the withdraw address for a delegator (or validator self-delegation) */ +export interface AminoMsgSetWithdrawAddress extends AminoMsg { + // NOTE: Type string and names diverge here! + readonly type: "cosmos-sdk/MsgModifyWithdrawAddress"; + readonly value: { + /** Bech32 account address */ + readonly delegator_address: string; + /** Bech32 account address */ + readonly withdraw_address: string; + }; +} + +export function isAminoMsgSetWithdrawAddress(msg: AminoMsg): msg is AminoMsgSetWithdrawAddress { + // NOTE: Type string and names diverge here! + return msg.type === "cosmos-sdk/MsgModifyWithdrawAddress"; +} + +/** Message for delegation withdraw from a single validator */ +export interface AminoMsgWithdrawDelegatorReward extends AminoMsg { + // NOTE: Type string and names diverge here! + readonly type: "cosmos-sdk/MsgWithdrawDelegationReward"; + readonly value: { + /** Bech32 account address */ + readonly delegator_address: string; + /** Bech32 account address */ + readonly validator_address: string; + }; +} + +export function isAminoMsgWithdrawDelegatorReward(msg: AminoMsg): msg is AminoMsgWithdrawDelegatorReward { + // NOTE: Type string and names diverge here! + return msg.type === "cosmos-sdk/MsgWithdrawDelegationReward"; +} + +/** Message for validator withdraw */ +export interface AminoMsgWithdrawValidatorCommission extends AminoMsg { + readonly type: "cosmos-sdk/MsgWithdrawValidatorCommission"; + readonly value: { + /** Bech32 account address */ + readonly validator_address: string; + }; +} + +export function isAminoMsgWithdrawValidatorCommission( + msg: AminoMsg, +): msg is AminoMsgWithdrawValidatorCommission { + return msg.type === "cosmos-sdk/MsgWithdrawValidatorCommission"; +} + +/** Allows an account to directly fund the community pool. */ +export interface AminoMsgFundCommunityPool extends AminoMsg { + readonly type: "cosmos-sdk/MsgFundCommunityPool"; + readonly value: { + readonly amount: readonly Coin[]; + /** Bech32 account address */ + readonly depositor: string; + }; +} + +export function isAminoMsgFundCommunityPool(msg: AminoMsg): msg is AminoMsgFundCommunityPool { + return msg.type === "cosmos-sdk/MsgFundCommunityPool"; +} + +export function createDistributionAminoConverters(): Record< + string, + AminoConverter | "not_supported_by_chain" +> { + return { + "/cosmos.distribution.v1beta1.MsgFundCommunityPool": { + aminoType: "cosmos-sdk/MsgFundCommunityPool", + toAmino: ({ amount, depositor }: MsgFundCommunityPool): AminoMsgFundCommunityPool["value"] => ({ + amount: [...amount], + depositor: depositor, + }), + fromAmino: ({ amount, depositor }: AminoMsgFundCommunityPool["value"]): MsgFundCommunityPool => ({ + amount: [...amount], + depositor: depositor, + }), + }, + "/cosmos.distribution.v1beta1.MsgSetWithdrawAddress": { + aminoType: "cosmos-sdk/MsgModifyWithdrawAddress", + toAmino: ({ + delegatorAddress, + withdrawAddress, + }: MsgSetWithdrawAddress): AminoMsgSetWithdrawAddress["value"] => ({ + delegator_address: delegatorAddress, + withdraw_address: withdrawAddress, + }), + fromAmino: ({ + delegator_address, + withdraw_address, + }: AminoMsgSetWithdrawAddress["value"]): MsgSetWithdrawAddress => ({ + delegatorAddress: delegator_address, + withdrawAddress: withdraw_address, + }), + }, + "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward": { + aminoType: "cosmos-sdk/MsgWithdrawDelegationReward", + toAmino: ({ + delegatorAddress, + validatorAddress, + }: MsgWithdrawDelegatorReward): AminoMsgWithdrawDelegatorReward["value"] => ({ + delegator_address: delegatorAddress, + validator_address: validatorAddress, + }), + fromAmino: ({ + delegator_address, + validator_address, + }: AminoMsgWithdrawDelegatorReward["value"]): MsgWithdrawDelegatorReward => ({ + delegatorAddress: delegator_address, + validatorAddress: validator_address, + }), + }, + "/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission": { + aminoType: "cosmos-sdk/MsgWithdrawValidatorCommission", + toAmino: ({ + validatorAddress, + }: MsgWithdrawValidatorCommission): AminoMsgWithdrawValidatorCommission["value"] => ({ + validator_address: validatorAddress, + }), + fromAmino: ({ + validator_address, + }: AminoMsgWithdrawValidatorCommission["value"]): MsgWithdrawValidatorCommission => ({ + validatorAddress: validator_address, + }), + }, + }; +} diff --git a/packages/stargate/src/modules/feegrant/aminomessages.ts b/packages/stargate/src/modules/feegrant/aminomessages.ts new file mode 100644 index 00000000..7f6f8db1 --- /dev/null +++ b/packages/stargate/src/modules/feegrant/aminomessages.ts @@ -0,0 +1,8 @@ +import { AminoConverters } from "../../aminoconverters"; + +export function createFreegrantAminoConverters(): AminoConverters { + return { + "/cosmos.feegrant.v1beta1.MsgGrantAllowance": "not_supported_by_chain", + "/cosmos.feegrant.v1beta1.MsgRevokeAllowance": "not_supported_by_chain", + }; +} diff --git a/packages/stargate/src/modules/gov/aminomessages.ts b/packages/stargate/src/modules/gov/aminomessages.ts new file mode 100644 index 00000000..cb48cfd0 --- /dev/null +++ b/packages/stargate/src/modules/gov/aminomessages.ts @@ -0,0 +1,178 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { AminoMsg, Coin } from "@cosmjs/amino"; +import { assert, assertDefinedAndNotNull, isNonNullObject } from "@cosmjs/utils"; +import { TextProposal, voteOptionFromJSON } from "cosmjs-types/cosmos/gov/v1beta1/gov"; +import { MsgDeposit, MsgSubmitProposal, MsgVote } from "cosmjs-types/cosmos/gov/v1beta1/tx"; +import { Any } from "cosmjs-types/google/protobuf/any"; +import Long from "long"; + +import { AminoConverters } from "../../aminoconverters"; + +/** Supports submitting arbitrary proposal content. */ +export interface AminoMsgSubmitProposal extends AminoMsg { + readonly type: "cosmos-sdk/MsgSubmitProposal"; + readonly value: { + /** + * 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; + }; +} + +export function isAminoMsgSubmitProposal(msg: AminoMsg): msg is AminoMsgSubmitProposal { + return msg.type === "cosmos-sdk/MsgSubmitProposal"; +} + +/** Casts a vote */ +export interface AminoMsgVote extends AminoMsg { + readonly type: "cosmos-sdk/MsgVote"; + readonly value: { + readonly proposal_id: string; + /** Bech32 account address */ + readonly voter: string; + /** + * 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; + }; +} + +export function isAminoMsgVote(msg: AminoMsg): msg is AminoMsgVote { + return msg.type === "cosmos-sdk/MsgVote"; +} + +/** Submits a deposit to an existing proposal */ +export interface AminoMsgDeposit extends AminoMsg { + readonly type: "cosmos-sdk/MsgDeposit"; + readonly value: { + readonly proposal_id: string; + /** Bech32 account address */ + readonly depositor: string; + readonly amount: readonly Coin[]; + }; +} + +export function isAminoMsgDeposit(msg: AminoMsg): msg is AminoMsgDeposit { + return msg.type === "cosmos-sdk/MsgDeposit"; +} + +export function createGovAminoConverters(): AminoConverters { + return { + "/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, + }; + }, + }, + }; +} diff --git a/packages/stargate/src/modules/ibc/aminomessages.ts b/packages/stargate/src/modules/ibc/aminomessages.ts new file mode 100644 index 00000000..6fdb71b9 --- /dev/null +++ b/packages/stargate/src/modules/ibc/aminomessages.ts @@ -0,0 +1,114 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { AminoMsg, Coin } from "@cosmjs/amino"; +import { MsgTransfer } from "cosmjs-types/ibc/applications/transfer/v1/tx"; +import Long from "long"; + +import { AminoConverters } from "../../aminoconverters"; + +// https://github.com/cosmos/ibc-go/blob/07b6a97b67d17fd214a83764cbdb2c2c3daef445/modules/core/02-client/types/client.pb.go#L297-L312 +interface AminoHeight { + /** 0 values must be omitted (https://github.com/cosmos/cosmos-sdk/blob/v0.42.7/x/ibc/core/02-client/types/client.pb.go#L252). */ + readonly revision_number?: string; + /** 0 values must be omitted (https://github.com/cosmos/cosmos-sdk/blob/v0.42.7/x/ibc/core/02-client/types/client.pb.go#L254). */ + readonly revision_height?: string; +} + +// https://github.com/cosmos/ibc-go/blob/07b6a97b67d17fd214a83764cbdb2c2c3daef445/modules/apps/transfer/types/tx.pb.go#L33-L53 +/** Transfers fungible tokens (i.e Coins) between ICS20 enabled chains */ +export interface AminoMsgTransfer extends AminoMsg { + readonly type: "cosmos-sdk/MsgTransfer"; + readonly value: { + readonly source_port: string; + readonly source_channel: string; + readonly token?: Coin; + /** Bech32 account address */ + readonly sender: string; + /** Bech32 account address */ + readonly receiver: string; + /** + * The timeout as a (revision_number, revision_height) pair. + * + * This fied is is non-optional (https://github.com/cosmos/cosmos-sdk/blob/v0.42.7/x/ibc/applications/transfer/types/tx.pb.go#L49). + * In order to not set the timeout height, set it to {}. + */ + readonly timeout_height: AminoHeight; + /** + * Timeout timestamp in nanoseconds since Unix epoch. The timeout is disabled when set to 0. + * + * 0 values must be omitted (https://github.com/cosmos/cosmos-sdk/blob/v0.42.7/x/ibc/applications/transfer/types/tx.pb.go#L52). + */ + readonly timeout_timestamp?: string; + }; +} + +export function isAminoMsgTransfer(msg: AminoMsg): msg is AminoMsgTransfer { + return msg.type === "cosmos-sdk/MsgTransfer"; +} + +function omitDefault(input: T): T | undefined { + if (typeof input === "string") { + return input === "" ? undefined : input; + } + + if (typeof input === "number") { + return input === 0 ? undefined : input; + } + + if (Long.isLong(input)) { + return input.isZero() ? undefined : input; + } + + throw new Error(`Got unsupported type '${typeof input}'`); +} + +export function createIbcAminoConverters(): AminoConverters { + return { + "/ibc.applications.transfer.v1.MsgTransfer": { + aminoType: "cosmos-sdk/MsgTransfer", + toAmino: ({ + sourcePort, + sourceChannel, + token, + sender, + receiver, + timeoutHeight, + timeoutTimestamp, + }: MsgTransfer): AminoMsgTransfer["value"] => ({ + source_port: sourcePort, + source_channel: sourceChannel, + token: token, + sender: sender, + receiver: receiver, + timeout_height: timeoutHeight + ? { + revision_height: omitDefault(timeoutHeight.revisionHeight)?.toString(), + revision_number: omitDefault(timeoutHeight.revisionNumber)?.toString(), + } + : {}, + timeout_timestamp: omitDefault(timeoutTimestamp)?.toString(), + }), + fromAmino: ({ + source_port, + source_channel, + token, + sender, + receiver, + timeout_height, + timeout_timestamp, + }: AminoMsgTransfer["value"]): MsgTransfer => ({ + sourcePort: source_port, + sourceChannel: source_channel, + token: token, + sender: sender, + receiver: receiver, + timeoutHeight: timeout_height + ? { + revisionHeight: Long.fromString(timeout_height.revision_height || "0", true), + revisionNumber: Long.fromString(timeout_height.revision_number || "0", true), + } + : undefined, + timeoutTimestamp: Long.fromString(timeout_timestamp || "0", true), + }), + }, + }; +} diff --git a/packages/stargate/src/modules/index.ts b/packages/stargate/src/modules/index.ts index c828e3f9..6404bcd8 100644 --- a/packages/stargate/src/modules/index.ts +++ b/packages/stargate/src/modules/index.ts @@ -1,14 +1,43 @@ export { AuthExtension, setupAuthExtension } from "./auth/queries"; +export { createAuthzAminoConverters } from "./authz/aminomessages"; export { authzTypes } from "./authz/messages"; +export { + AminoMsgMultiSend, + AminoMsgSend, + createBankAminoConverters, + isAminoMsgMultiSend, + isAminoMsgSend, +} from "./bank/aminomessages"; export { bankTypes, isMsgSendEncodeObject, MsgSendEncodeObject } from "./bank/messages"; export { BankExtension, setupBankExtension } from "./bank/queries"; +export { + AminoMsgFundCommunityPool, + AminoMsgSetWithdrawAddress, + AminoMsgWithdrawDelegatorReward, + AminoMsgWithdrawValidatorCommission, + createDistributionAminoConverters, + isAminoMsgFundCommunityPool, + isAminoMsgSetWithdrawAddress, + isAminoMsgWithdrawDelegatorReward, + isAminoMsgWithdrawValidatorCommission, +} from "./distribution/aminomessages"; export { distributionTypes, isMsgWithdrawDelegatorRewardEncodeObject, MsgWithdrawDelegatorRewardEncodeObject, } from "./distribution/messages"; export { DistributionExtension, setupDistributionExtension } from "./distribution/queries"; +export { createFreegrantAminoConverters } from "./feegrant/aminomessages"; export { feegrantTypes } from "./feegrant/messages"; +export { + AminoMsgDeposit, + AminoMsgSubmitProposal, + AminoMsgVote, + createGovAminoConverters, + isAminoMsgDeposit, + isAminoMsgSubmitProposal, + isAminoMsgVote, +} from "./gov/aminomessages"; export { govTypes, isMsgDepositEncodeObject, @@ -19,10 +48,24 @@ export { MsgVoteEncodeObject, } from "./gov/messages"; export { GovExtension, GovParamsType, GovProposalId, setupGovExtension } from "./gov/queries"; +export { AminoMsgTransfer, createIbcAminoConverters, isAminoMsgTransfer } from "./ibc/aminomessages"; export { ibcTypes, isMsgTransferEncodeObject, MsgTransferEncodeObject } from "./ibc/messages"; export { IbcExtension, setupIbcExtension } from "./ibc/queries"; export { MintExtension, MintParams, setupMintExtension } from "./mint/queries"; export { setupSlashingExtension, SlashingExtension } from "./slashing/queries"; +export { + AminoMsgBeginRedelegate, + AminoMsgCreateValidator, + AminoMsgDelegate, + AminoMsgEditValidator, + AminoMsgUndelegate, + createStakingAminoConverters, + isAminoMsgBeginRedelegate, + isAminoMsgCreateValidator, + isAminoMsgDelegate, + isAminoMsgEditValidator, + isAminoMsgUndelegate, +} from "./staking/aminomessages"; export { isMsgDelegateEncodeObject, isMsgUndelegateEncodeObject, diff --git a/packages/stargate/src/modules/staking/aminomessages.ts b/packages/stargate/src/modules/staking/aminomessages.ts new file mode 100644 index 00000000..97904b02 --- /dev/null +++ b/packages/stargate/src/modules/staking/aminomessages.ts @@ -0,0 +1,317 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { AminoMsg, Coin, decodeBech32Pubkey, encodeBech32Pubkey } from "@cosmjs/amino"; +import { fromBase64, toBase64 } from "@cosmjs/encoding"; +import { assertDefinedAndNotNull } from "@cosmjs/utils"; +import { + MsgBeginRedelegate, + MsgCreateValidator, + MsgDelegate, + MsgEditValidator, + MsgUndelegate, +} from "cosmjs-types/cosmos/staking/v1beta1/tx"; + +import { AminoConverter } from "../.."; + +/** The initial commission rates to be used for creating a validator */ +interface CommissionRates { + readonly rate: string; + readonly max_rate: string; + readonly max_change_rate: string; +} + +/** A validator description. */ +interface Description { + readonly moniker: string; + readonly identity: string; + readonly website: string; + readonly security_contact: string; + readonly details: string; +} + +/** Creates a new validator. */ +export interface AminoMsgCreateValidator extends AminoMsg { + readonly type: "cosmos-sdk/MsgCreateValidator"; + readonly value: { + readonly description: Description; + readonly commission: CommissionRates; + readonly min_self_delegation: string; + /** Bech32 encoded delegator address */ + readonly delegator_address: string; + /** Bech32 encoded validator address */ + readonly validator_address: string; + /** Bech32 encoded public key */ + readonly pubkey: string; + readonly value: Coin; + }; +} + +export function isAminoMsgCreateValidator(msg: AminoMsg): msg is AminoMsgCreateValidator { + return msg.type === "cosmos-sdk/MsgCreateValidator"; +} + +/** Edits an existing validator. */ +export interface AminoMsgEditValidator extends AminoMsg { + readonly type: "cosmos-sdk/MsgEditValidator"; + readonly value: { + readonly description: Description; + /** Bech32 encoded validator address */ + readonly validator_address: string; + readonly commission_rate: string; + readonly min_self_delegation: string; + }; +} + +export function isAminoMsgEditValidator(msg: AminoMsg): msg is AminoMsgEditValidator { + return msg.type === "cosmos-sdk/MsgEditValidator"; +} + +/** + * Performs a delegation from a delegate to a validator. + * + * @see https://docs.cosmos.network/master/modules/staking/03_messages.html#msgdelegate + */ +export interface AminoMsgDelegate extends AminoMsg { + readonly type: "cosmos-sdk/MsgDelegate"; + readonly value: { + /** Bech32 encoded delegator address */ + readonly delegator_address: string; + /** Bech32 encoded validator address */ + readonly validator_address: string; + readonly amount: Coin; + }; +} + +export function isAminoMsgDelegate(msg: AminoMsg): msg is AminoMsgDelegate { + return msg.type === "cosmos-sdk/MsgDelegate"; +} + +/** Performs a redelegation from a delegate and source validator to a destination validator */ +export interface AminoMsgBeginRedelegate extends AminoMsg { + readonly type: "cosmos-sdk/MsgBeginRedelegate"; + readonly value: { + /** Bech32 encoded delegator address */ + readonly delegator_address: string; + /** Bech32 encoded source validator address */ + readonly validator_src_address: string; + /** Bech32 encoded destination validator address */ + readonly validator_dst_address: string; + readonly amount: Coin; + }; +} + +export function isAminoMsgBeginRedelegate(msg: AminoMsg): msg is AminoMsgBeginRedelegate { + return msg.type === "cosmos-sdk/MsgBeginRedelegate"; +} + +/** Performs an undelegation from a delegate and a validator */ +export interface AminoMsgUndelegate extends AminoMsg { + readonly type: "cosmos-sdk/MsgUndelegate"; + readonly value: { + /** Bech32 encoded delegator address */ + readonly delegator_address: string; + /** Bech32 encoded validator address */ + readonly validator_address: string; + readonly amount: Coin; + }; +} + +export function isAminoMsgUndelegate(msg: AminoMsg): msg is AminoMsgUndelegate { + return msg.type === "cosmos-sdk/MsgUndelegate"; +} + +export function createStakingAminoConverters( + prefix: string, +): Record { + return { + "/cosmos.staking.v1beta1.MsgBeginRedelegate": { + aminoType: "cosmos-sdk/MsgBeginRedelegate", + toAmino: ({ + delegatorAddress, + validatorSrcAddress, + validatorDstAddress, + amount, + }: MsgBeginRedelegate): AminoMsgBeginRedelegate["value"] => { + assertDefinedAndNotNull(amount, "missing amount"); + return { + delegator_address: delegatorAddress, + validator_src_address: validatorSrcAddress, + validator_dst_address: validatorDstAddress, + amount: amount, + }; + }, + fromAmino: ({ + delegator_address, + validator_src_address, + validator_dst_address, + amount, + }: AminoMsgBeginRedelegate["value"]): MsgBeginRedelegate => ({ + 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, + }: MsgCreateValidator): AminoMsgCreateValidator["value"] => { + assertDefinedAndNotNull(description, "missing description"); + assertDefinedAndNotNull(commission, "missing commission"); + assertDefinedAndNotNull(pubkey, "missing pubkey"); + assertDefinedAndNotNull(value, "missing value"); + 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: value, + }; + }, + fromAmino: ({ + description, + commission, + min_self_delegation, + delegator_address, + validator_address, + pubkey, + value, + }: AminoMsgCreateValidator["value"]): MsgCreateValidator => { + 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: { + typeUrl: "/cosmos.crypto.secp256k1.PubKey", + value: fromBase64(decodedPubkey.value), + }, + value: value, + }; + }, + }, + "/cosmos.staking.v1beta1.MsgDelegate": { + aminoType: "cosmos-sdk/MsgDelegate", + toAmino: ({ delegatorAddress, validatorAddress, amount }: MsgDelegate): AminoMsgDelegate["value"] => { + assertDefinedAndNotNull(amount, "missing amount"); + return { + delegator_address: delegatorAddress, + validator_address: validatorAddress, + amount: amount, + }; + }, + fromAmino: ({ + delegator_address, + validator_address, + amount, + }: AminoMsgDelegate["value"]): MsgDelegate => ({ + delegatorAddress: delegator_address, + validatorAddress: validator_address, + amount: amount, + }), + }, + "/cosmos.staking.v1beta1.MsgEditValidator": { + aminoType: "cosmos-sdk/MsgEditValidator", + toAmino: ({ + description, + commissionRate, + minSelfDelegation, + validatorAddress, + }: MsgEditValidator): AminoMsgEditValidator["value"] => { + assertDefinedAndNotNull(description, "missing description"); + 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, + }: AminoMsgEditValidator["value"]): MsgEditValidator => ({ + 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, + }: MsgUndelegate): AminoMsgUndelegate["value"] => { + assertDefinedAndNotNull(amount, "missing amount"); + return { + delegator_address: delegatorAddress, + validator_address: validatorAddress, + amount: amount, + }; + }, + fromAmino: ({ + delegator_address, + validator_address, + amount, + }: AminoMsgUndelegate["value"]): MsgUndelegate => ({ + delegatorAddress: delegator_address, + validatorAddress: validator_address, + amount: amount, + }), + }, + }; +} diff --git a/packages/stargate/src/signingstargateclient.spec.ts b/packages/stargate/src/signingstargateclient.spec.ts index 7a9108dc..1b600748 100644 --- a/packages/stargate/src/signingstargateclient.spec.ts +++ b/packages/stargate/src/signingstargateclient.spec.ts @@ -9,9 +9,8 @@ import { AuthInfo, TxBody, TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx"; import Long from "long"; import protobuf from "protobufjs/minimal"; -import { AminoMsgDelegate } from "./aminomsgs"; import { AminoTypes } from "./aminotypes"; -import { MsgDelegateEncodeObject, MsgSendEncodeObject } from "./modules"; +import { AminoMsgDelegate, MsgDelegateEncodeObject, MsgSendEncodeObject } from "./modules"; import { PrivateSigningStargateClient, SigningStargateClient } from "./signingstargateclient"; import { assertIsDeliverTxFailure, assertIsDeliverTxSuccess, isDeliverTxFailure } from "./stargateclient"; import {