diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a9eed7e..d96c6ae9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,10 @@ and this project adheres to not connect to Tendermint. This allows offline signing. - @cosmjs/stargate: Add `makeMultisignedTx` which allows you to assemble a transaction signed by a multisig account. +- @cosmjs/stargate: Add `delegateTokens`, `undelegateTokens` and + `withdrawRewards` methods to `SigningStargateClient`. +- @cosmjs/stargate: Export `defaultGasLimits` and `defaultGasPrice`. +- @cosmjs/cosmwasm-stargate: Export `defaultGasLimits`. ### Changed diff --git a/packages/cosmwasm-stargate/src/index.ts b/packages/cosmwasm-stargate/src/index.ts index 523a85c1..ab7e80ac 100644 --- a/packages/cosmwasm-stargate/src/index.ts +++ b/packages/cosmwasm-stargate/src/index.ts @@ -1,3 +1,7 @@ export { cosmWasmTypes } from "./aminotypes"; export { CosmWasmClient } from "./cosmwasmclient"; -export { SigningCosmWasmClient, SigningCosmWasmClientOptions } from "./signingcosmwasmclient"; +export { + defaultGasLimits, + SigningCosmWasmClient, + SigningCosmWasmClientOptions, +} from "./signingcosmwasmclient"; diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts index 46ff1524..6cc493a1 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts @@ -91,6 +91,18 @@ describe("SigningCosmWasmClient", () => { amount: coins(251200, "utest"), gas: "80000", }, + delegate: { + amount: coins(502400, "utest"), + gas: "160000", + }, + undelegate: { + amount: coins(502400, "utest"), + gas: "160000", + }, + withdraw: { + amount: coins(502400, "utest"), + gas: "160000", + }, }); }); @@ -130,6 +142,18 @@ describe("SigningCosmWasmClient", () => { amount: coins(2000, "ucosm"), gas: "80000", }, + delegate: { + amount: coins(4000, "ucosm"), + gas: "160000", + }, + undelegate: { + amount: coins(4000, "ucosm"), + gas: "160000", + }, + withdraw: { + amount: coins(4000, "ucosm"), + gas: "160000", + }, }); }); @@ -170,6 +194,18 @@ describe("SigningCosmWasmClient", () => { amount: coins(251200, "utest"), gas: "80000", }, + delegate: { + amount: coins(502400, "utest"), + gas: "160000", + }, + undelegate: { + amount: coins(502400, "utest"), + gas: "160000", + }, + withdraw: { + amount: coins(502400, "utest"), + gas: "160000", + }, }); }); }); diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts index 5162cf1c..02b0c765 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts @@ -2,7 +2,6 @@ import { encodeSecp256k1Pubkey, makeSignDoc as makeSignDocAmino } from "@cosmjs/amino"; import { ChangeAdminResult, - CosmWasmFeeTable, ExecuteResult, InstantiateOptions, InstantiateResult, @@ -30,6 +29,8 @@ import { buildFeeTable, Coin, CosmosFeeTable, + defaultGasLimits as defaultStargateGasLimits, + defaultGasPrice, defaultRegistryTypes, GasLimits, GasPrice, @@ -37,6 +38,8 @@ import { logs, StdFee, } from "@cosmjs/stargate"; +import { MsgWithdrawDelegatorReward } from "@cosmjs/stargate/build/codec/cosmos/distribution/v1beta1/tx"; +import { MsgDelegate, MsgUndelegate } from "@cosmjs/stargate/build/codec/cosmos/staking/v1beta1/tx"; import { SignMode } from "@cosmjs/stargate/build/codec/cosmos/tx/signing/v1beta1/signing"; import { TxRaw } from "@cosmjs/stargate/build/codec/cosmos/tx/v1beta1/tx"; import { Tendermint34Client } from "@cosmjs/tendermint-rpc"; @@ -54,6 +57,18 @@ import { } from "./codec/x/wasm/internal/types/tx"; import { CosmWasmClient } from "./cosmwasmclient"; +/** + * These fees are used by the higher level methods of SigningCosmWasmClient + */ +export interface CosmWasmFeeTable extends CosmosFeeTable { + readonly upload: StdFee; + readonly init: StdFee; + readonly exec: StdFee; + readonly migrate: StdFee; + /** Paid when setting the contract admin to a new address or unsetting it */ + readonly changeAdmin: StdFee; +} + function prepareBuilder(builder: string | undefined): string { if (builder === undefined) { return ""; // normalization needed by backend @@ -63,13 +78,12 @@ function prepareBuilder(builder: string | undefined): string { } } -const defaultGasPrice = GasPrice.fromString("0.025ucosm"); -const defaultGasLimits: GasLimits = { +export const defaultGasLimits: GasLimits = { + ...defaultStargateGasLimits, upload: 1_500_000, init: 500_000, migrate: 200_000, exec: 200_000, - send: 80_000, changeAdmin: 80_000, }; @@ -320,6 +334,44 @@ export class SigningCosmWasmClient extends CosmWasmClient { return this.signAndBroadcast(senderAddress, [sendMsg], this.fees.send, memo); } + public async delegateTokens( + delegatorAddress: string, + validatorAddress: string, + amount: Coin, + memo = "", + ): Promise { + const delegateMsg = { + typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", + value: MsgDelegate.fromPartial({ delegatorAddress: delegatorAddress, validatorAddress, amount }), + }; + return this.signAndBroadcast(delegatorAddress, [delegateMsg], this.fees.delegate, memo); + } + + public async undelegateTokens( + delegatorAddress: string, + validatorAddress: string, + amount: Coin, + memo = "", + ): Promise { + const undelegateMsg = { + typeUrl: "/cosmos.staking.v1beta1.MsgUndelegate", + value: MsgUndelegate.fromPartial({ delegatorAddress: delegatorAddress, validatorAddress, amount }), + }; + return this.signAndBroadcast(delegatorAddress, [undelegateMsg], this.fees.undelegate, memo); + } + + public async withdrawRewards( + delegatorAddress: string, + validatorAddress: string, + memo = "", + ): Promise { + const withdrawMsg = { + typeUrl: "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", + value: MsgWithdrawDelegatorReward.fromPartial({ delegatorAddress: delegatorAddress, validatorAddress }), + }; + return this.signAndBroadcast(delegatorAddress, [withdrawMsg], this.fees.withdraw, memo); + } + /** * Creates a transaction with the given messages, fee and memo. Then signs and broadcasts the transaction. * diff --git a/packages/stargate/src/index.ts b/packages/stargate/src/index.ts index 5d7a42ec..729bdac7 100644 --- a/packages/stargate/src/index.ts +++ b/packages/stargate/src/index.ts @@ -84,6 +84,8 @@ export { } from "./stargateclient"; export { CosmosFeeTable, + defaultGasLimits, + defaultGasPrice, defaultRegistryTypes, SignerData, SigningStargateClient, diff --git a/packages/stargate/src/signingstargateclient.spec.ts b/packages/stargate/src/signingstargateclient.spec.ts index 5cace77d..7caaa979 100644 --- a/packages/stargate/src/signingstargateclient.spec.ts +++ b/packages/stargate/src/signingstargateclient.spec.ts @@ -40,6 +40,33 @@ describe("SigningStargateClient", () => { ], gas: "80000", }, + delegate: { + amount: [ + { + amount: "4000", + denom: "ucosm", + }, + ], + gas: "160000", + }, + undelegate: { + amount: [ + { + amount: "4000", + denom: "ucosm", + }, + ], + gas: "160000", + }, + withdraw: { + amount: [ + { + amount: "4000", + denom: "ucosm", + }, + ], + gas: "160000", + }, }); }); @@ -71,6 +98,33 @@ describe("SigningStargateClient", () => { ], gas: "80000", }, + delegate: { + amount: [ + { + amount: "502400", // 3.14 * 160_000 + denom: "utest", + }, + ], + gas: "160000", + }, + undelegate: { + amount: [ + { + amount: "502400", + denom: "utest", + }, + ], + gas: "160000", + }, + withdraw: { + amount: [ + { + amount: "502400", + denom: "utest", + }, + ], + gas: "160000", + }, }); }); @@ -79,6 +133,7 @@ describe("SigningStargateClient", () => { const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic); const gasLimits = { send: 160000, + delegate: 120000, }; const options = { gasLimits: gasLimits }; const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options); @@ -93,6 +148,33 @@ describe("SigningStargateClient", () => { ], gas: "160000", }, + delegate: { + amount: [ + { + amount: "3000", // 0.025 * 120_000 + denom: "ucosm", + }, + ], + gas: "120000", + }, + undelegate: { + amount: [ + { + amount: "4000", + denom: "ucosm", + }, + ], + gas: "160000", + }, + withdraw: { + amount: [ + { + amount: "4000", + denom: "ucosm", + }, + ], + gas: "160000", + }, }); }); @@ -116,6 +198,33 @@ describe("SigningStargateClient", () => { ], gas: "160000", }, + delegate: { + amount: [ + { + amount: "502400", // 3.14 * 160_000 + denom: "utest", + }, + ], + gas: "160000", + }, + undelegate: { + amount: [ + { + amount: "502400", + denom: "utest", + }, + ], + gas: "160000", + }, + withdraw: { + amount: [ + { + amount: "502400", + denom: "utest", + }, + ], + gas: "160000", + }, }); }); }); diff --git a/packages/stargate/src/signingstargateclient.ts b/packages/stargate/src/signingstargateclient.ts index 380047fb..b12e65d5 100644 --- a/packages/stargate/src/signingstargateclient.ts +++ b/packages/stargate/src/signingstargateclient.ts @@ -66,10 +66,18 @@ import { BroadcastTxResponse, StargateClient } from "./stargateclient"; */ export interface CosmosFeeTable extends FeeTable { readonly send: StdFee; + readonly delegate: StdFee; + readonly undelegate: StdFee; + readonly withdraw: StdFee; } -const defaultGasPrice = GasPrice.fromString("0.025ucosm"); -const defaultGasLimits: GasLimits = { send: 80000 }; +export const defaultGasPrice = GasPrice.fromString("0.025ucosm"); +export const defaultGasLimits: GasLimits = { + send: 80_000, + delegate: 160_000, + undelegate: 160_000, + withdraw: 160_000, +}; export const defaultRegistryTypes: ReadonlyArray<[string, GeneratedType]> = [ ["/cosmos.bank.v1beta1.MsgMultiSend", MsgMultiSend], @@ -198,6 +206,44 @@ export class SigningStargateClient extends StargateClient { return this.signAndBroadcast(senderAddress, [sendMsg], this.fees.send, memo); } + public async delegateTokens( + delegatorAddress: string, + validatorAddress: string, + amount: Coin, + memo = "", + ): Promise { + const delegateMsg = { + typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", + value: MsgDelegate.fromPartial({ delegatorAddress: delegatorAddress, validatorAddress, amount }), + }; + return this.signAndBroadcast(delegatorAddress, [delegateMsg], this.fees.delegate, memo); + } + + public async undelegateTokens( + delegatorAddress: string, + validatorAddress: string, + amount: Coin, + memo = "", + ): Promise { + const undelegateMsg = { + typeUrl: "/cosmos.staking.v1beta1.MsgUndelegate", + value: MsgUndelegate.fromPartial({ delegatorAddress: delegatorAddress, validatorAddress, amount }), + }; + return this.signAndBroadcast(delegatorAddress, [undelegateMsg], this.fees.undelegate, memo); + } + + public async withdrawRewards( + delegatorAddress: string, + validatorAddress: string, + memo = "", + ): Promise { + const withdrawMsg = { + typeUrl: "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", + value: MsgWithdrawDelegatorReward.fromPartial({ delegatorAddress: delegatorAddress, validatorAddress }), + }; + return this.signAndBroadcast(delegatorAddress, [withdrawMsg], this.fees.withdraw, memo); + } + public async signAndBroadcast( signerAddress: string, messages: readonly EncodeObject[],