Merge pull request #856 from cosmos/add-gov-extension
Add GovExtension and add gov message types
This commit is contained in:
commit
7ef4ee30aa
@ -25,6 +25,8 @@ and this project adheres to
|
||||
- @cosmjs/faucet: Add new configuration variable `FAUCET_PATH_PATTERN` 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`.
|
||||
|
||||
[#832]: https://github.com/cosmos/cosmjs/issues/832
|
||||
[#836]: https://github.com/cosmos/cosmjs/issues/836
|
||||
|
||||
@ -85,10 +85,10 @@ export function setupWasmExtension(base: QueryClient): WasmExtension {
|
||||
return queryService.Code(request);
|
||||
},
|
||||
listContractsByCodeId: async (id: number, paginationKey?: Uint8Array) => {
|
||||
const pagination = {
|
||||
const request = {
|
||||
codeId: Long.fromNumber(id),
|
||||
pagination: createPagination(paginationKey),
|
||||
};
|
||||
const request = { ...pagination, codeId: Long.fromNumber(id) };
|
||||
return queryService.ContractsByCode(request);
|
||||
},
|
||||
getContractInfo: async (address: string) => {
|
||||
@ -97,18 +97,18 @@ export function setupWasmExtension(base: QueryClient): WasmExtension {
|
||||
},
|
||||
|
||||
getContractCodeHistory: async (address: string, paginationKey?: Uint8Array) => {
|
||||
const pagination = {
|
||||
const request = {
|
||||
address: address,
|
||||
pagination: createPagination(paginationKey),
|
||||
};
|
||||
const request = { ...pagination, address: address };
|
||||
return queryService.ContractHistory(request);
|
||||
},
|
||||
|
||||
getAllContractState: async (address: string, paginationKey?: Uint8Array) => {
|
||||
const pagination = {
|
||||
const request = {
|
||||
address: address,
|
||||
pagination: createPagination(paginationKey),
|
||||
};
|
||||
const request = { ...pagination, address: address };
|
||||
return queryService.AllContractState(request);
|
||||
},
|
||||
|
||||
|
||||
@ -1,6 +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 { MsgDelegate, MsgUndelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx";
|
||||
import { MsgTransfer } from "cosmjs-types/ibc/applications/transfer/v1/tx";
|
||||
|
||||
@ -59,3 +60,23 @@ export function isMsgTransferEncodeObject(
|
||||
): encodeObject is MsgTransferEncodeObject {
|
||||
return (encodeObject as MsgTransferEncodeObject).typeUrl === "/ibc.applications.transfer.v1.MsgTransfer";
|
||||
}
|
||||
|
||||
export interface MsgSubmitProposalEncodeObject extends EncodeObject {
|
||||
readonly typeUrl: "/cosmos.gov.v1beta1.MsgSubmitProposal";
|
||||
readonly value: Partial<MsgSubmitProposal>;
|
||||
}
|
||||
|
||||
export function isMsgSubmitProposalEncodeObject(
|
||||
encodeObject: EncodeObject,
|
||||
): encodeObject is MsgSubmitProposalEncodeObject {
|
||||
return (encodeObject as MsgSubmitProposalEncodeObject).typeUrl === "/cosmos.gov.v1beta1.MsgSubmitProposal";
|
||||
}
|
||||
|
||||
export interface MsgVoteEncodeObject extends EncodeObject {
|
||||
readonly typeUrl: "/cosmos.gov.v1beta1.MsgVote";
|
||||
readonly value: Partial<MsgVote>;
|
||||
}
|
||||
|
||||
export function isMsgVoteEncodeObject(encodeObject: EncodeObject): encodeObject is MsgVoteEncodeObject {
|
||||
return (encodeObject as MsgVoteEncodeObject).typeUrl === "/cosmos.gov.v1beta1.MsgVote";
|
||||
}
|
||||
|
||||
@ -42,13 +42,17 @@ export { AminoConverter, AminoTypes } from "./aminotypes";
|
||||
export {
|
||||
isMsgDelegateEncodeObject,
|
||||
isMsgSendEncodeObject,
|
||||
isMsgSubmitProposalEncodeObject,
|
||||
isMsgTransferEncodeObject,
|
||||
isMsgUndelegateEncodeObject,
|
||||
isMsgVoteEncodeObject,
|
||||
isMsgWithdrawDelegatorRewardEncodeObject,
|
||||
MsgDelegateEncodeObject,
|
||||
MsgSendEncodeObject,
|
||||
MsgSubmitProposalEncodeObject,
|
||||
MsgTransferEncodeObject,
|
||||
MsgUndelegateEncodeObject,
|
||||
MsgVoteEncodeObject,
|
||||
MsgWithdrawDelegatorRewardEncodeObject,
|
||||
} from "./encodeobjects";
|
||||
export { calculateFee, GasPrice } from "./fee";
|
||||
@ -60,12 +64,16 @@ export {
|
||||
createPagination,
|
||||
createProtobufRpcClient,
|
||||
DistributionExtension,
|
||||
GovExtension,
|
||||
GovParamsType,
|
||||
GovProposalId,
|
||||
IbcExtension,
|
||||
ProtobufRpcClient,
|
||||
QueryClient,
|
||||
setupAuthExtension,
|
||||
setupBankExtension,
|
||||
setupDistributionExtension,
|
||||
setupGovExtension,
|
||||
setupIbcExtension,
|
||||
setupStakingExtension,
|
||||
StakingExtension,
|
||||
|
||||
355
packages/stargate/src/queries/gov.spec.ts
Normal file
355
packages/stargate/src/queries/gov.spec.ts
Normal file
@ -0,0 +1,355 @@
|
||||
import { coin, coins, makeCosmoshubPath } from "@cosmjs/amino";
|
||||
import { toAscii } from "@cosmjs/encoding";
|
||||
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
|
||||
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
|
||||
import { assert, sleep } from "@cosmjs/utils";
|
||||
import { ProposalStatus, TextProposal, VoteOption } from "cosmjs-types/cosmos/gov/v1beta1/gov";
|
||||
import { Any } from "cosmjs-types/google/protobuf/any";
|
||||
import Long from "long";
|
||||
|
||||
import {
|
||||
MsgDelegateEncodeObject,
|
||||
MsgSubmitProposalEncodeObject,
|
||||
MsgVoteEncodeObject,
|
||||
} from "../encodeobjects";
|
||||
import { SigningStargateClient } from "../signingstargateclient";
|
||||
import { assertIsBroadcastTxSuccess } from "../stargateclient";
|
||||
import {
|
||||
defaultSigningClientOptions,
|
||||
faucet,
|
||||
nonNegativeIntegerMatcher,
|
||||
pendingWithoutSimapp,
|
||||
simapp,
|
||||
simappEnabled,
|
||||
validator,
|
||||
} from "../testutils.spec";
|
||||
import { GovExtension, setupGovExtension } from "./gov";
|
||||
import { QueryClient } from "./queryclient";
|
||||
import { longify } from "./utils";
|
||||
|
||||
async function makeClientWithGov(rpcUrl: string): Promise<[QueryClient & GovExtension, Tendermint34Client]> {
|
||||
const tmClient = await Tendermint34Client.connect(rpcUrl);
|
||||
return [QueryClient.withExtensions(tmClient, setupGovExtension), tmClient];
|
||||
}
|
||||
|
||||
describe("GovExtension", () => {
|
||||
const defaultFee = {
|
||||
amount: coins(25000, "ucosm"),
|
||||
gas: "1500000", // 1.5 million
|
||||
};
|
||||
const textProposal = TextProposal.fromPartial({
|
||||
title: "Test Proposal",
|
||||
description: "This proposal proposes to test whether this proposal passes",
|
||||
});
|
||||
const initialDeposit = coins(12300000, "ustake");
|
||||
const delegationVoter1 = coin(424242, "ustake");
|
||||
const delegationVoter2 = coin(777, "ustake");
|
||||
const voter1Address = faucet.address1;
|
||||
const voter2Address = faucet.address2;
|
||||
let proposalId: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
if (simappEnabled()) {
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
|
||||
// Use address 1 and 2 instead of 0 to avoid conflicts with other delegation tests
|
||||
// This must match `voterAddress` above.
|
||||
hdPaths: [makeCosmoshubPath(1), makeCosmoshubPath(2)],
|
||||
});
|
||||
const client = await SigningStargateClient.connectWithSigner(
|
||||
simapp.tendermintUrl,
|
||||
wallet,
|
||||
defaultSigningClientOptions,
|
||||
);
|
||||
|
||||
const proposalMsg: MsgSubmitProposalEncodeObject = {
|
||||
typeUrl: "/cosmos.gov.v1beta1.MsgSubmitProposal",
|
||||
value: {
|
||||
content: Any.fromPartial({
|
||||
typeUrl: "/cosmos.gov.v1beta1.TextProposal",
|
||||
value: Uint8Array.from(TextProposal.encode(textProposal).finish()),
|
||||
}),
|
||||
proposer: voter1Address,
|
||||
initialDeposit: initialDeposit,
|
||||
},
|
||||
};
|
||||
const proposalResult = await client.signAndBroadcast(
|
||||
voter1Address,
|
||||
[proposalMsg],
|
||||
defaultFee,
|
||||
"Test proposal for simd",
|
||||
);
|
||||
assertIsBroadcastTxSuccess(proposalResult);
|
||||
const logs = JSON.parse(proposalResult.rawLog || "");
|
||||
proposalId = logs[0].events
|
||||
.find(({ type }: any) => type === "submit_proposal")
|
||||
.attributes.find(({ key }: any) => key === "proposal_id").value;
|
||||
assert(proposalId.match(nonNegativeIntegerMatcher));
|
||||
|
||||
// Voter 1
|
||||
{
|
||||
// My vote only counts when I delegate
|
||||
if (!(await client.getDelegation(voter1Address, validator.validatorAddress))) {
|
||||
const msgDelegate: MsgDelegateEncodeObject = {
|
||||
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
|
||||
value: {
|
||||
delegatorAddress: voter1Address,
|
||||
validatorAddress: validator.validatorAddress,
|
||||
amount: delegationVoter1,
|
||||
},
|
||||
};
|
||||
const result = await client.signAndBroadcast(voter1Address, [msgDelegate], defaultFee);
|
||||
assertIsBroadcastTxSuccess(result);
|
||||
}
|
||||
|
||||
const voteMsg: MsgVoteEncodeObject = {
|
||||
typeUrl: "/cosmos.gov.v1beta1.MsgVote",
|
||||
value: {
|
||||
proposalId: longify(proposalId),
|
||||
voter: voter1Address,
|
||||
option: VoteOption.VOTE_OPTION_YES,
|
||||
},
|
||||
};
|
||||
const voteResult = await client.signAndBroadcast(voter1Address, [voteMsg], defaultFee);
|
||||
assertIsBroadcastTxSuccess(voteResult);
|
||||
}
|
||||
|
||||
// Voter 2
|
||||
{
|
||||
// My vote only counts when I delegate
|
||||
if (!(await client.getDelegation(voter2Address, validator.validatorAddress))) {
|
||||
const msgDelegate: MsgDelegateEncodeObject = {
|
||||
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
|
||||
value: {
|
||||
delegatorAddress: voter2Address,
|
||||
validatorAddress: validator.validatorAddress,
|
||||
amount: delegationVoter2,
|
||||
},
|
||||
};
|
||||
const result = await client.signAndBroadcast(voter2Address, [msgDelegate], defaultFee);
|
||||
assertIsBroadcastTxSuccess(result);
|
||||
}
|
||||
|
||||
const voteMsg: MsgVoteEncodeObject = {
|
||||
typeUrl: "/cosmos.gov.v1beta1.MsgVote",
|
||||
value: {
|
||||
proposalId: longify(proposalId),
|
||||
voter: voter2Address,
|
||||
option: VoteOption.VOTE_OPTION_NO_WITH_VETO,
|
||||
},
|
||||
};
|
||||
const voteResult = await client.signAndBroadcast(voter2Address, [voteMsg], defaultFee);
|
||||
assertIsBroadcastTxSuccess(voteResult);
|
||||
}
|
||||
|
||||
await sleep(75); // wait until transactions are indexed
|
||||
|
||||
client.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
describe("params", () => {
|
||||
it("works for deposit", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
|
||||
|
||||
const response = await client.gov.params("deposit");
|
||||
expect(response).toEqual(
|
||||
jasmine.objectContaining({
|
||||
depositParams: {
|
||||
minDeposit: simapp.govMinDeposit,
|
||||
maxDepositPeriod: {
|
||||
seconds: Long.fromNumber(172800, false),
|
||||
nanos: 0,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
tmClient.disconnect();
|
||||
});
|
||||
|
||||
it("works for tallying", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
|
||||
|
||||
const response = await client.gov.params("tallying");
|
||||
expect(response).toEqual(
|
||||
jasmine.objectContaining({
|
||||
tallyParams: {
|
||||
// Why the f*** are we getting binary values here?
|
||||
quorum: toAscii("334000000000000000"), // 0.334
|
||||
threshold: toAscii("500000000000000000"), // 0.5
|
||||
vetoThreshold: toAscii("334000000000000000"), // 0.334
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
tmClient.disconnect();
|
||||
});
|
||||
|
||||
it("works for voting", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
|
||||
|
||||
const response = await client.gov.params("voting");
|
||||
expect(response).toEqual(
|
||||
jasmine.objectContaining({
|
||||
votingParams: {
|
||||
votingPeriod: {
|
||||
seconds: Long.fromNumber(172800, false),
|
||||
nanos: 0,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
tmClient.disconnect();
|
||||
});
|
||||
});
|
||||
|
||||
describe("proposals", () => {
|
||||
it("works", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
|
||||
|
||||
const response = await client.gov.proposals(
|
||||
ProposalStatus.PROPOSAL_STATUS_VOTING_PERIOD,
|
||||
voter1Address,
|
||||
voter1Address,
|
||||
);
|
||||
expect(response.proposals.length).toBeGreaterThanOrEqual(1);
|
||||
expect(response.proposals[response.proposals.length - 1]).toEqual({
|
||||
content: Any.fromPartial({
|
||||
typeUrl: "/cosmos.gov.v1beta1.TextProposal",
|
||||
value: Uint8Array.from(TextProposal.encode(textProposal).finish()),
|
||||
}),
|
||||
proposalId: longify(proposalId),
|
||||
status: ProposalStatus.PROPOSAL_STATUS_VOTING_PERIOD,
|
||||
finalTallyResult: { yes: "0", abstain: "0", no: "0", noWithVeto: "0" },
|
||||
submitTime: jasmine.any(Date),
|
||||
depositEndTime: jasmine.any(Date),
|
||||
totalDeposit: initialDeposit,
|
||||
votingStartTime: jasmine.any(Date),
|
||||
votingEndTime: jasmine.any(Date),
|
||||
});
|
||||
|
||||
tmClient.disconnect();
|
||||
});
|
||||
});
|
||||
|
||||
describe("proposal", () => {
|
||||
it("works", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
|
||||
|
||||
const response = await client.gov.proposal(proposalId);
|
||||
expect(response.proposal).toEqual({
|
||||
content: Any.fromPartial({
|
||||
typeUrl: "/cosmos.gov.v1beta1.TextProposal",
|
||||
value: Uint8Array.from(TextProposal.encode(textProposal).finish()),
|
||||
}),
|
||||
proposalId: longify(proposalId),
|
||||
status: ProposalStatus.PROPOSAL_STATUS_VOTING_PERIOD,
|
||||
finalTallyResult: { yes: "0", abstain: "0", no: "0", noWithVeto: "0" },
|
||||
submitTime: jasmine.any(Date),
|
||||
depositEndTime: jasmine.any(Date),
|
||||
totalDeposit: initialDeposit,
|
||||
votingStartTime: jasmine.any(Date),
|
||||
votingEndTime: jasmine.any(Date),
|
||||
});
|
||||
|
||||
tmClient.disconnect();
|
||||
});
|
||||
});
|
||||
|
||||
describe("deposits", () => {
|
||||
it("works", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
|
||||
|
||||
const response = await client.gov.deposits(proposalId);
|
||||
expect(response.deposits).toEqual([
|
||||
{
|
||||
proposalId: longify(proposalId),
|
||||
depositor: voter1Address,
|
||||
amount: initialDeposit,
|
||||
},
|
||||
]);
|
||||
|
||||
tmClient.disconnect();
|
||||
});
|
||||
});
|
||||
|
||||
describe("deposit", () => {
|
||||
it("works", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
|
||||
|
||||
const response = await client.gov.deposit(proposalId, voter1Address);
|
||||
expect(response.deposit).toEqual({
|
||||
proposalId: longify(proposalId),
|
||||
depositor: voter1Address,
|
||||
amount: initialDeposit,
|
||||
});
|
||||
|
||||
tmClient.disconnect();
|
||||
});
|
||||
});
|
||||
|
||||
describe("tally", () => {
|
||||
it("works", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
|
||||
|
||||
const response = await client.gov.tally(proposalId);
|
||||
expect(response.tally).toEqual({
|
||||
yes: delegationVoter1.amount,
|
||||
abstain: "0",
|
||||
no: "0",
|
||||
noWithVeto: delegationVoter2.amount,
|
||||
});
|
||||
|
||||
tmClient.disconnect();
|
||||
});
|
||||
});
|
||||
|
||||
describe("votes", () => {
|
||||
it("works", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
|
||||
|
||||
const response = await client.gov.votes(proposalId);
|
||||
expect(response.votes).toEqual([
|
||||
// why is vote 2 first?
|
||||
{
|
||||
proposalId: longify(proposalId),
|
||||
voter: voter2Address,
|
||||
option: VoteOption.VOTE_OPTION_NO_WITH_VETO,
|
||||
},
|
||||
{
|
||||
proposalId: longify(proposalId),
|
||||
voter: voter1Address,
|
||||
option: VoteOption.VOTE_OPTION_YES,
|
||||
},
|
||||
]);
|
||||
|
||||
tmClient.disconnect();
|
||||
});
|
||||
});
|
||||
|
||||
describe("vote", () => {
|
||||
it("works", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const [client, tmClient] = await makeClientWithGov(simapp.tendermintUrl);
|
||||
|
||||
const response = await client.gov.vote(proposalId, voter1Address);
|
||||
expect(response.vote).toEqual({
|
||||
voter: voter1Address,
|
||||
proposalId: longify(proposalId),
|
||||
option: VoteOption.VOTE_OPTION_YES,
|
||||
});
|
||||
|
||||
tmClient.disconnect();
|
||||
});
|
||||
});
|
||||
});
|
||||
107
packages/stargate/src/queries/gov.ts
Normal file
107
packages/stargate/src/queries/gov.ts
Normal file
@ -0,0 +1,107 @@
|
||||
import { Uint64 } from "@cosmjs/math";
|
||||
import { ProposalStatus } from "cosmjs-types/cosmos/gov/v1beta1/gov";
|
||||
import {
|
||||
QueryClientImpl,
|
||||
QueryDepositResponse,
|
||||
QueryDepositsResponse,
|
||||
QueryParamsResponse,
|
||||
QueryProposalResponse,
|
||||
QueryProposalsResponse,
|
||||
QueryTallyResultResponse,
|
||||
QueryVoteResponse,
|
||||
QueryVotesResponse,
|
||||
} from "cosmjs-types/cosmos/gov/v1beta1/query";
|
||||
import Long from "long";
|
||||
|
||||
import { QueryClient } from "./queryclient";
|
||||
import { createPagination, createProtobufRpcClient, longify } from "./utils";
|
||||
|
||||
export type GovParamsType = "deposit" | "tallying" | "voting";
|
||||
|
||||
export type GovProposalId = string | number | Long | Uint64;
|
||||
|
||||
export interface GovExtension {
|
||||
readonly gov: {
|
||||
readonly params: (parametersType: GovParamsType) => Promise<QueryParamsResponse>;
|
||||
readonly proposals: (
|
||||
proposalStatus: ProposalStatus,
|
||||
depositor: string,
|
||||
voter: string,
|
||||
) => Promise<QueryProposalsResponse>;
|
||||
readonly proposal: (proposalId: GovProposalId) => Promise<QueryProposalResponse>;
|
||||
readonly deposits: (proposalId: GovProposalId) => Promise<QueryDepositsResponse>;
|
||||
readonly deposit: (proposalId: GovProposalId, depositorAddress: string) => Promise<QueryDepositResponse>;
|
||||
readonly tally: (proposalId: GovProposalId) => Promise<QueryTallyResultResponse>;
|
||||
readonly votes: (proposalId: GovProposalId) => Promise<QueryVotesResponse>;
|
||||
readonly vote: (proposalId: GovProposalId, voterAddress: string) => Promise<QueryVoteResponse>;
|
||||
};
|
||||
}
|
||||
|
||||
export function setupGovExtension(base: QueryClient): GovExtension {
|
||||
const rpc = createProtobufRpcClient(base);
|
||||
|
||||
// Use this service to get easy typed access to query methods
|
||||
// This cannot be used for proof verification
|
||||
const queryService = new QueryClientImpl(rpc);
|
||||
|
||||
return {
|
||||
gov: {
|
||||
params: async (parametersType: GovParamsType) => {
|
||||
const response = await queryService.Params({ paramsType: parametersType });
|
||||
return response;
|
||||
},
|
||||
proposals: async (
|
||||
proposalStatus: ProposalStatus,
|
||||
depositorAddress: string,
|
||||
voterAddress: string,
|
||||
paginationKey?: Uint8Array,
|
||||
) => {
|
||||
const response = await queryService.Proposals({
|
||||
proposalStatus,
|
||||
depositor: depositorAddress,
|
||||
voter: voterAddress,
|
||||
pagination: createPagination(paginationKey),
|
||||
});
|
||||
return response;
|
||||
},
|
||||
proposal: async (proposalId: GovProposalId) => {
|
||||
const response = await queryService.Proposal({ proposalId: longify(proposalId) });
|
||||
return response;
|
||||
},
|
||||
deposits: async (proposalId: GovProposalId, paginationKey?: Uint8Array) => {
|
||||
const response = await queryService.Deposits({
|
||||
proposalId: longify(proposalId),
|
||||
pagination: createPagination(paginationKey),
|
||||
});
|
||||
return response;
|
||||
},
|
||||
deposit: async (proposalId: GovProposalId, depositorAddress: string) => {
|
||||
const response = await queryService.Deposit({
|
||||
proposalId: longify(proposalId),
|
||||
depositor: depositorAddress,
|
||||
});
|
||||
return response;
|
||||
},
|
||||
tally: async (proposalId: GovProposalId) => {
|
||||
const response = await queryService.TallyResult({
|
||||
proposalId: longify(proposalId),
|
||||
});
|
||||
return response;
|
||||
},
|
||||
votes: async (proposalId: GovProposalId, paginationKey?: Uint8Array) => {
|
||||
const response = await queryService.Votes({
|
||||
proposalId: longify(proposalId),
|
||||
pagination: createPagination(paginationKey),
|
||||
});
|
||||
return response;
|
||||
},
|
||||
vote: async (proposalId: GovProposalId, voterAddress: string) => {
|
||||
const response = await queryService.Vote({
|
||||
proposalId: longify(proposalId),
|
||||
voter: voterAddress,
|
||||
});
|
||||
return response;
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -7,6 +7,7 @@ export { QueryClient } from "./queryclient";
|
||||
export { AuthExtension, setupAuthExtension } from "./auth";
|
||||
export { BankExtension, setupBankExtension } from "./bank";
|
||||
export { DistributionExtension, setupDistributionExtension } from "./distribution";
|
||||
export { setupGovExtension, GovExtension, GovProposalId, GovParamsType } from "./gov";
|
||||
export { IbcExtension, setupIbcExtension } from "./ibc";
|
||||
export { setupStakingExtension, StakingExtension } from "./staking";
|
||||
export { createPagination, createProtobufRpcClient, ProtobufRpcClient } from "./utils";
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Bech32 } from "@cosmjs/encoding";
|
||||
import { Uint64 } from "@cosmjs/math";
|
||||
import { PageRequest } from "cosmjs-types/cosmos/base/query/v1beta1/pagination";
|
||||
import Long from "long";
|
||||
|
||||
@ -13,6 +14,13 @@ export function toAccAddress(address: string): Uint8Array {
|
||||
return Bech32.decode(address).data;
|
||||
}
|
||||
|
||||
/**
|
||||
* If paginationKey is set, return a `PageRequest` with the given key.
|
||||
* If paginationKey is unset, return `undefined`.
|
||||
*
|
||||
* Use this with a query response's pagination next key to
|
||||
* request the next page.
|
||||
*/
|
||||
export function createPagination(paginationKey?: Uint8Array): PageRequest | undefined {
|
||||
return paginationKey
|
||||
? {
|
||||
@ -36,3 +44,12 @@ export function createProtobufRpcClient(base: QueryClient): ProtobufRpcClient {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a uint64 value as string, number, Long or Uint64 and returns an unsigned Long instance
|
||||
* of it.
|
||||
*/
|
||||
export function longify(value: string | number | Long | Uint64): Long {
|
||||
const checkedValue = Uint64.fromString(value.toString());
|
||||
return Long.fromBytesBE([...checkedValue.toBytesBigEndian()], true);
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ import {
|
||||
MsgWithdrawDelegatorReward,
|
||||
MsgWithdrawValidatorCommission,
|
||||
} from "cosmjs-types/cosmos/distribution/v1beta1/tx";
|
||||
import { MsgSubmitProposal, MsgVote } from "cosmjs-types/cosmos/gov/v1beta1/tx";
|
||||
import {
|
||||
MsgBeginRedelegate,
|
||||
MsgCreateValidator,
|
||||
@ -75,6 +76,8 @@ export const defaultRegistryTypes: ReadonlyArray<[string, GeneratedType]> = [
|
||||
["/cosmos.distribution.v1beta1.MsgSetWithdrawAddress", MsgSetWithdrawAddress],
|
||||
["/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", MsgWithdrawDelegatorReward],
|
||||
["/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission", MsgWithdrawValidatorCommission],
|
||||
["/cosmos.gov.v1beta1.MsgSubmitProposal", MsgSubmitProposal],
|
||||
["/cosmos.gov.v1beta1.MsgVote", MsgVote],
|
||||
["/cosmos.staking.v1beta1.MsgBeginRedelegate", MsgBeginRedelegate],
|
||||
["/cosmos.staking.v1beta1.MsgCreateValidator", MsgCreateValidator],
|
||||
["/cosmos.staking.v1beta1.MsgDelegate", MsgDelegate],
|
||||
|
||||
@ -7,7 +7,15 @@ import { MsgData } from "cosmjs-types/cosmos/base/abci/v1beta1/abci";
|
||||
import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin";
|
||||
|
||||
import { Account, accountFromAny } from "./accounts";
|
||||
import { AuthExtension, BankExtension, QueryClient, setupAuthExtension, setupBankExtension } from "./queries";
|
||||
import {
|
||||
AuthExtension,
|
||||
BankExtension,
|
||||
QueryClient,
|
||||
setupAuthExtension,
|
||||
setupBankExtension,
|
||||
setupStakingExtension,
|
||||
StakingExtension,
|
||||
} from "./queries";
|
||||
import {
|
||||
isSearchByHeightQuery,
|
||||
isSearchBySentFromOrToQuery,
|
||||
@ -138,7 +146,7 @@ export interface PrivateStargateClient {
|
||||
|
||||
export class StargateClient {
|
||||
private readonly tmClient: Tendermint34Client | undefined;
|
||||
private readonly queryClient: (QueryClient & AuthExtension & BankExtension) | undefined;
|
||||
private readonly queryClient: (QueryClient & AuthExtension & BankExtension & StakingExtension) | undefined;
|
||||
private chainId: string | undefined;
|
||||
|
||||
public static async connect(endpoint: string): Promise<StargateClient> {
|
||||
@ -149,7 +157,12 @@ export class StargateClient {
|
||||
protected constructor(tmClient: Tendermint34Client | undefined) {
|
||||
if (tmClient) {
|
||||
this.tmClient = tmClient;
|
||||
this.queryClient = QueryClient.withExtensions(tmClient, setupAuthExtension, setupBankExtension);
|
||||
this.queryClient = QueryClient.withExtensions(
|
||||
tmClient,
|
||||
setupAuthExtension,
|
||||
setupBankExtension,
|
||||
setupStakingExtension,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,11 +179,11 @@ export class StargateClient {
|
||||
return this.tmClient;
|
||||
}
|
||||
|
||||
protected getQueryClient(): (QueryClient & AuthExtension & BankExtension) | undefined {
|
||||
protected getQueryClient(): (QueryClient & AuthExtension & BankExtension & StakingExtension) | undefined {
|
||||
return this.queryClient;
|
||||
}
|
||||
|
||||
protected forceGetQueryClient(): QueryClient & AuthExtension & BankExtension {
|
||||
protected forceGetQueryClient(): QueryClient & AuthExtension & BankExtension & StakingExtension {
|
||||
if (!this.queryClient) {
|
||||
throw new Error("Query client not available. You cannot use online functionality in offline mode.");
|
||||
}
|
||||
@ -254,6 +267,22 @@ export class StargateClient {
|
||||
return this.forceGetQueryClient().bank.allBalances(address);
|
||||
}
|
||||
|
||||
public async getDelegation(delegatorAddress: string, validatorAddress: string): Promise<Coin | null> {
|
||||
let delegatedAmount: Coin | undefined;
|
||||
try {
|
||||
delegatedAmount = (
|
||||
await this.forceGetQueryClient().staking.delegation(delegatorAddress, validatorAddress)
|
||||
).delegationResponse?.balance;
|
||||
} catch (e) {
|
||||
if (e.toString().includes("key not found")) {
|
||||
// ignore, `delegatedAmount` remains undefined
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return delegatedAmount || null;
|
||||
}
|
||||
|
||||
public async getTx(id: string): Promise<IndexedTx | null> {
|
||||
const results = await this.txsQuery(`tx.hash='${id}'`);
|
||||
return results[0] ?? null;
|
||||
|
||||
@ -61,6 +61,7 @@ export const simapp = {
|
||||
denomFee: "ucosm",
|
||||
blockTime: 1_000, // ms
|
||||
totalSupply: 21000000000, // ucosm
|
||||
govMinDeposit: coins(10000000, "ustake"),
|
||||
};
|
||||
|
||||
export const slowSimapp = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user