Merge pull request #292 from CosmWasm/276-sdk38-gov-module

Add gov extension to Cosmos SDK 0.38 LCD
This commit is contained in:
Simon Warta 2020-07-16 11:26:26 +02:00 committed by GitHub
commit cf375d396c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 617 additions and 0 deletions

View File

@ -28,6 +28,16 @@ export {
BlockResponse,
BroadcastMode,
EncodeTxResponse,
GovExtension,
GovParametersResponse,
GovProposalsResponse,
GovProposalResponse,
GovProposerResponse,
GovDepositsResponse,
GovDepositResponse,
GovTallyResponse,
GovVotesResponse,
GovVoteResponse,
LcdApiArray,
LcdClient,
MintAnnualProvisionsResponse,
@ -40,6 +50,7 @@ export {
SearchTxsResponse,
setupAuthExtension,
setupBankExtension,
setupGovExtension,
setupMintExtension,
setupSlashingExtension,
setupSupplyExtension,

View File

@ -0,0 +1,304 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { assert, sleep } from "@cosmjs/utils";
import { coins } from "../coins";
import { isPostTxFailure } from "../cosmosclient";
import { makeSignBytes } from "../encoding";
import { SigningCosmosClient } from "../signingcosmosclient";
import {
dateTimeStampMatcher,
faucet,
nonNegativeIntegerMatcher,
pendingWithoutWasmd,
wasmd,
wasmdEnabled,
} from "../testutils.spec";
import { Secp256k1Wallet } from "../wallet";
import { GovExtension, GovParametersType, setupGovExtension } from "./gov";
import { LcdClient } from "./lcdclient";
function makeGovClient(apiUrl: string): LcdClient & GovExtension {
return LcdClient.withExtensions({ apiUrl }, setupGovExtension);
}
describe("GovExtension", () => {
const defaultFee = {
amount: coins(25000, "ucosm"),
gas: "1500000", // 1.5 million
};
let proposalId: string;
beforeAll(async () => {
if (wasmdEnabled()) {
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, wallet, {});
const chainId = await client.getChainId();
const proposalMsg = {
type: "cosmos-sdk/MsgSubmitProposal",
value: {
content: {
type: "cosmos-sdk/TextProposal",
value: {
description: "This proposal proposes to test whether this proposal passes",
title: "Test Proposal",
},
},
proposer: faucet.address,
initial_deposit: coins(25000000, "ustake"),
},
};
const proposalMemo = "Test proposal for wasmd";
const { accountNumber: proposalAccountNumber, sequence: proposalSequence } = await client.getNonce();
const proposalSignBytes = makeSignBytes(
[proposalMsg],
defaultFee,
chainId,
proposalMemo,
proposalAccountNumber,
proposalSequence,
);
const proposalSignature = await wallet.sign(faucet.address, proposalSignBytes);
const proposalTx = {
msg: [proposalMsg],
fee: defaultFee,
memo: proposalMemo,
signatures: [proposalSignature],
};
const proposalReceipt = await client.postTx(proposalTx);
assert(!isPostTxFailure(proposalReceipt));
proposalId = proposalReceipt.logs[0].events
.find(({ type }) => type === "submit_proposal")!
.attributes.find(({ key }) => key === "proposal_id")!.value;
const voteMsg = {
type: "cosmos-sdk/MsgVote",
value: {
proposal_id: proposalId,
voter: faucet.address,
option: "Yes",
},
};
const voteMemo = "Test vote for wasmd";
const { accountNumber: voteAccountNumber, sequence: voteSequence } = await client.getNonce();
const voteSignBytes = makeSignBytes(
[voteMsg],
defaultFee,
chainId,
voteMemo,
voteAccountNumber,
voteSequence,
);
const voteSignature = await wallet.sign(faucet.address, voteSignBytes);
const voteTx = {
msg: [voteMsg],
fee: defaultFee,
memo: voteMemo,
signatures: [voteSignature],
};
await client.postTx(voteTx);
await sleep(75); // wait until transactions are indexed
}
});
describe("parameters", () => {
it("works for deposit", async () => {
pendingWithoutWasmd();
const client = makeGovClient(wasmd.endpoint);
const paramsType = GovParametersType.Deposit;
const response = await client.gov.parameters(paramsType);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
min_deposit: [{ denom: "ustake", amount: "10000000" }],
max_deposit_period: "172800000000000",
},
});
});
it("works for tallying", async () => {
pendingWithoutWasmd();
const client = makeGovClient(wasmd.endpoint);
const paramsType = GovParametersType.Tallying;
const response = await client.gov.parameters(paramsType);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
quorum: "0.334000000000000000",
threshold: "0.500000000000000000",
veto: "0.334000000000000000",
},
});
});
it("works for voting", async () => {
pendingWithoutWasmd();
const client = makeGovClient(wasmd.endpoint);
const paramsType = GovParametersType.Voting;
const response = await client.gov.parameters(paramsType);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
voting_period: "172800000000000",
},
});
});
});
describe("proposals", () => {
it("works", async () => {
pendingWithoutWasmd();
const client = makeGovClient(wasmd.endpoint);
const response = await client.gov.proposals();
expect(response.height).toMatch(nonNegativeIntegerMatcher);
expect(response.result.length).toBeGreaterThanOrEqual(1);
expect(response.result[response.result.length - 1]).toEqual({
content: {
type: "cosmos-sdk/TextProposal",
value: {
title: "Test Proposal",
description: "This proposal proposes to test whether this proposal passes",
},
},
id: proposalId,
proposal_status: "VotingPeriod",
final_tally_result: { yes: "0", abstain: "0", no: "0", no_with_veto: "0" },
submit_time: jasmine.stringMatching(dateTimeStampMatcher),
deposit_end_time: jasmine.stringMatching(dateTimeStampMatcher),
total_deposit: [{ denom: "ustake", amount: "25000000" }],
voting_start_time: jasmine.stringMatching(dateTimeStampMatcher),
voting_end_time: jasmine.stringMatching(dateTimeStampMatcher),
});
});
});
describe("proposal", () => {
it("works", async () => {
pendingWithoutWasmd();
const client = makeGovClient(wasmd.endpoint);
const response = await client.gov.proposal(proposalId);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
content: {
type: "cosmos-sdk/TextProposal",
value: {
title: "Test Proposal",
description: "This proposal proposes to test whether this proposal passes",
},
},
id: proposalId,
proposal_status: "VotingPeriod",
final_tally_result: { yes: "0", abstain: "0", no: "0", no_with_veto: "0" },
submit_time: jasmine.stringMatching(dateTimeStampMatcher),
deposit_end_time: jasmine.stringMatching(dateTimeStampMatcher),
total_deposit: [{ denom: "ustake", amount: "25000000" }],
voting_start_time: jasmine.stringMatching(dateTimeStampMatcher),
voting_end_time: jasmine.stringMatching(dateTimeStampMatcher),
},
});
});
});
describe("proposer", () => {
it("works", async () => {
pendingWithoutWasmd();
const client = makeGovClient(wasmd.endpoint);
const response = await client.gov.proposer(proposalId);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
proposal_id: proposalId,
proposer: faucet.address,
},
});
});
});
describe("deposits", () => {
it("works", async () => {
pendingWithoutWasmd();
const client = makeGovClient(wasmd.endpoint);
const response = await client.gov.deposits(proposalId);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
proposal_id: proposalId,
depositor: faucet.address,
amount: [{ denom: "ustake", amount: "25000000" }],
},
],
});
});
});
describe("deposit", () => {
it("works", async () => {
pendingWithoutWasmd();
const client = makeGovClient(wasmd.endpoint);
const response = await client.gov.deposit(proposalId, faucet.address);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
proposal_id: proposalId,
depositor: faucet.address,
amount: [{ denom: "ustake", amount: "25000000" }],
},
});
});
});
describe("tally", () => {
it("works", async () => {
pendingWithoutWasmd();
const client = makeGovClient(wasmd.endpoint);
const response = await client.gov.tally(proposalId);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
yes: "0",
abstain: "0",
no: "0",
no_with_veto: "0",
},
});
});
});
describe("votes", () => {
it("works", async () => {
pendingWithoutWasmd();
const client = makeGovClient(wasmd.endpoint);
const response = await client.gov.votes(proposalId);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
proposal_id: proposalId,
voter: faucet.address,
option: "Yes",
},
],
});
});
});
describe("vote", () => {
it("works", async () => {
pendingWithoutWasmd();
const client = makeGovClient(wasmd.endpoint);
const response = await client.gov.vote(proposalId, faucet.address);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
voter: faucet.address,
proposal_id: proposalId,
option: "Yes",
},
});
});
});
});

View File

@ -0,0 +1,150 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { Coin } from "../coins";
import { LcdClient } from "./lcdclient";
export enum GovParametersType {
Deposit = "deposit",
Tallying = "tallying",
Voting = "voting",
}
export interface GovParametersDepositResponse {
readonly height: string;
readonly result: {
readonly min_deposit: readonly Coin[];
readonly max_deposit_period: string;
};
}
export interface GovParametersTallyingResponse {
readonly height: string;
readonly result: {
readonly quorum: string;
readonly threshold: string;
readonly veto: string;
};
}
export interface GovParametersVotingResponse {
readonly height: string;
readonly result: {
readonly voting_period: string;
};
}
export type GovParametersResponse =
| GovParametersDepositResponse
| GovParametersTallyingResponse
| GovParametersVotingResponse;
export interface Tally {
readonly yes: string;
readonly abstain: string;
readonly no: string;
readonly no_with_veto: string;
}
export interface Proposal {
readonly id: string;
readonly proposal_status: string;
readonly final_tally_result: Tally;
readonly submit_time: string;
readonly total_deposit: readonly Coin[];
readonly deposit_end_time: string;
readonly voting_start_time: string;
readonly voting_end_time: string;
readonly content: {
readonly type: string;
readonly value: {
readonly title: string;
readonly description: string;
};
};
}
export interface GovProposalsResponse {
readonly height: string;
readonly result: readonly Proposal[];
}
export interface GovProposalResponse {
readonly height: string;
readonly result: Proposal;
}
export interface GovProposerResponse {
readonly height: string;
readonly result: {
readonly proposal_id: string;
readonly proposer: string;
};
}
export interface Deposit {
readonly amount: readonly Coin[];
readonly proposal_id: string;
readonly depositor: string;
}
export interface GovDepositsResponse {
readonly height: string;
readonly result: readonly Deposit[];
}
export interface GovDepositResponse {
readonly height: string;
readonly result: Deposit;
}
export interface GovTallyResponse {
readonly height: string;
readonly result: Tally;
}
export interface Vote {
readonly voter: string;
readonly proposal_id: string;
readonly option: string;
}
export interface GovVotesResponse {
readonly height: string;
readonly result: readonly Vote[];
}
export interface GovVoteResponse {
readonly height: string;
readonly result: Vote;
}
export interface GovExtension {
readonly gov: {
readonly parameters: (parametersType: GovParametersType) => Promise<GovParametersResponse>;
readonly proposals: () => Promise<GovProposalsResponse>;
readonly proposal: (proposalId: string) => Promise<GovProposalResponse>;
readonly proposer: (proposalId: string) => Promise<GovProposerResponse>;
readonly deposits: (proposalId: string) => Promise<GovDepositsResponse>;
readonly deposit: (proposalId: string, depositorAddress: string) => Promise<GovDepositResponse>;
readonly tally: (proposalId: string) => Promise<GovTallyResponse>;
readonly votes: (proposalId: string) => Promise<GovVotesResponse>;
readonly vote: (proposalId: string, voterAddress: string) => Promise<GovVoteResponse>;
};
}
export function setupGovExtension(base: LcdClient): GovExtension {
return {
gov: {
parameters: async (parametersType: GovParametersType) => base.get(`/gov/parameters/${parametersType}`),
proposals: async () => base.get("/gov/proposals"),
proposal: async (proposalId: string) => base.get(`/gov/proposals/${proposalId}`),
proposer: async (proposalId: string) => base.get(`/gov/proposals/${proposalId}/proposer`),
deposits: async (proposalId: string) => base.get(`/gov/proposals/${proposalId}/deposits`),
deposit: async (proposalId: string, depositorAddress: string) =>
base.get(`/gov/proposals/${proposalId}/deposits/${depositorAddress}`),
tally: async (proposalId: string) => base.get(`/gov/proposals/${proposalId}/tally`),
votes: async (proposalId: string) => base.get(`/gov/proposals/${proposalId}/votes`),
vote: async (proposalId: string, voterAddress: string) =>
base.get(`/gov/proposals/${proposalId}/votes/${voterAddress}`),
},
};
}

View File

@ -4,6 +4,19 @@
export { AuthExtension, AuthAccountsResponse, setupAuthExtension } from "./auth";
export { BankBalancesResponse, BankExtension, setupBankExtension } from "./bank";
export {
GovExtension,
GovParametersResponse,
GovProposalsResponse,
GovProposalResponse,
GovProposerResponse,
GovDepositsResponse,
GovDepositResponse,
GovTallyResponse,
GovVotesResponse,
GovVoteResponse,
setupGovExtension,
} from "./gov";
export {
MintAnnualProvisionsResponse,
MintExtension,

View File

@ -17,6 +17,7 @@ export const tendermintIdMatcher = /^[0-9A-F]{64}$/;
export const tendermintOptionalIdMatcher = /^([0-9A-F]{64}|)$/;
export const tendermintAddressMatcher = /^[0-9A-F]{40}$/;
export const tendermintShortHashMatcher = /^[0-9a-f]{40}$/;
export const dateTimeStampMatcher = /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]+)?Z$/;
export const semverMatcher = /^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/;
// https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32

View File

@ -26,6 +26,16 @@ export {
BlockResponse,
BroadcastMode,
EncodeTxResponse,
GovExtension,
GovParametersResponse,
GovProposalsResponse,
GovProposalResponse,
GovProposerResponse,
GovDepositsResponse,
GovDepositResponse,
GovTallyResponse,
GovVotesResponse,
GovVoteResponse,
LcdApiArray,
LcdClient,
MintAnnualProvisionsResponse,
@ -38,6 +48,7 @@ export {
SearchTxsResponse,
setupAuthExtension,
setupBankExtension,
setupGovExtension,
setupMintExtension,
setupSlashingExtension,
setupSupplyExtension,

114
packages/sdk38/types/lcdapi/gov.d.ts vendored Normal file
View File

@ -0,0 +1,114 @@
import { Coin } from "../coins";
import { LcdClient } from "./lcdclient";
export declare enum GovParametersType {
Deposit = "deposit",
Tallying = "tallying",
Voting = "voting",
}
export interface GovParametersDepositResponse {
readonly height: string;
readonly result: {
readonly min_deposit: readonly Coin[];
readonly max_deposit_period: string;
};
}
export interface GovParametersTallyingResponse {
readonly height: string;
readonly result: {
readonly quorum: string;
readonly threshold: string;
readonly veto: string;
};
}
export interface GovParametersVotingResponse {
readonly height: string;
readonly result: {
readonly voting_period: string;
};
}
export declare type GovParametersResponse =
| GovParametersDepositResponse
| GovParametersTallyingResponse
| GovParametersVotingResponse;
export interface Tally {
readonly yes: string;
readonly abstain: string;
readonly no: string;
readonly no_with_veto: string;
}
export interface Proposal {
readonly id: string;
readonly proposal_status: string;
readonly final_tally_result: Tally;
readonly submit_time: string;
readonly total_deposit: readonly Coin[];
readonly deposit_end_time: string;
readonly voting_start_time: string;
readonly voting_end_time: string;
readonly content: {
readonly type: string;
readonly value: {
readonly title: string;
readonly description: string;
};
};
}
export interface GovProposalsResponse {
readonly height: string;
readonly result: readonly Proposal[];
}
export interface GovProposalResponse {
readonly height: string;
readonly result: Proposal;
}
export interface GovProposerResponse {
readonly height: string;
readonly result: {
readonly proposal_id: string;
readonly proposer: string;
};
}
export interface Deposit {
readonly amount: readonly Coin[];
readonly proposal_id: string;
readonly depositor: string;
}
export interface GovDepositsResponse {
readonly height: string;
readonly result: readonly Deposit[];
}
export interface GovDepositResponse {
readonly height: string;
readonly result: Deposit;
}
export interface GovTallyResponse {
readonly height: string;
readonly result: Tally;
}
export interface Vote {
readonly voter: string;
readonly proposal_id: string;
readonly option: string;
}
export interface GovVotesResponse {
readonly height: string;
readonly result: readonly Vote[];
}
export interface GovVoteResponse {
readonly height: string;
readonly result: Vote;
}
export interface GovExtension {
readonly gov: {
readonly parameters: (parametersType: GovParametersType) => Promise<GovParametersResponse>;
readonly proposals: () => Promise<GovProposalsResponse>;
readonly proposal: (proposalId: string) => Promise<GovProposalResponse>;
readonly proposer: (proposalId: string) => Promise<GovProposerResponse>;
readonly deposits: (proposalId: string) => Promise<GovDepositsResponse>;
readonly deposit: (proposalId: string, depositorAddress: string) => Promise<GovDepositResponse>;
readonly tally: (proposalId: string) => Promise<GovTallyResponse>;
readonly votes: (proposalId: string) => Promise<GovVotesResponse>;
readonly vote: (proposalId: string, voterAddress: string) => Promise<GovVoteResponse>;
};
}
export declare function setupGovExtension(base: LcdClient): GovExtension;

View File

@ -1,5 +1,18 @@
export { AuthExtension, AuthAccountsResponse, setupAuthExtension } from "./auth";
export { BankBalancesResponse, BankExtension, setupBankExtension } from "./bank";
export {
GovExtension,
GovParametersResponse,
GovProposalsResponse,
GovProposalResponse,
GovProposerResponse,
GovDepositsResponse,
GovDepositResponse,
GovTallyResponse,
GovVotesResponse,
GovVoteResponse,
setupGovExtension,
} from "./gov";
export {
MintAnnualProvisionsResponse,
MintExtension,