1+ import './setup.test' ;
2+
3+ import { Asset } from '@chain-registry/types' ;
4+ import { generateMnemonic } from '@confio/relayer/build/lib/helpers' ;
5+ import { assertIsDeliverTxSuccess } from '@cosmjs/stargate' ;
6+ import {
7+ AminoGenericOfflineSigner ,
8+ DirectGenericOfflineSigner ,
9+ OfflineAminoSigner ,
10+ OfflineDirectSigner ,
11+ } from '@interchainjs/cosmos/types/wallet' ;
12+ import { Secp256k1HDWallet } from '@interchainjs/cosmos/wallets/secp256k1hd' ;
13+ import {
14+ ProposalStatus ,
15+ TextProposal ,
16+ VoteOption ,
17+ } from 'interchainjs/cosmos/gov/v1beta1/gov' ;
18+ import {
19+ BondStatus ,
20+ bondStatusToJSON ,
21+ } from 'interchainjs/cosmos/staking/v1beta1/staking' ;
22+ import { fromBase64 , toUtf8 } from '@interchainjs/utils' ;
23+ import { BigNumber } from 'bignumber.js' ;
24+ import { SigningClient as CosmosSigningClient } from '@interchainjs/cosmos/signing-client' ;
25+ import { useChain } from 'starshipjs' ;
26+
27+ import { waitUntil } from '../src' ;
28+
29+ import { createDelegate } from "interchainjs/cosmos/staking/v1beta1/tx.rpc.func" ;
30+ import { createSubmitProposal , createVote } from "interchainjs/cosmos/gov/v1beta1/tx.rpc.func" ;
31+
32+ import { createGetBalance } from "interchainjs/cosmos/bank/v1beta1/query.rpc.func" ;
33+ import { createGetProposal , createGetVote } from "interchainjs/cosmos/gov/v1beta1/query.rpc.func" ;
34+ import { createGetValidators } from "interchainjs/cosmos/staking/v1beta1/query.rpc.func" ;
35+ import { QueryBalanceRequest , QueryBalanceResponse } from 'interchainjs/cosmos/bank/v1beta1/query' ;
36+ import { QueryProposalRequest , QueryProposalResponse , QueryVoteRequest , QueryVoteResponse } from 'interchainjs/cosmos/gov/v1beta1/query' ;
37+ import { QueryValidatorsRequest , QueryValidatorsResponse } from 'interchainjs/cosmos/staking/v1beta1/query' ;
38+
39+ const cosmosHdPath = "m/44'/118'/0'/0/0" ;
40+
41+ describe ( 'Governance tests for osmosis' , ( ) => {
42+ let directSigner : OfflineDirectSigner ,
43+ aminoSigner : OfflineAminoSigner ,
44+ denom : string ,
45+ directAddress : string ,
46+ aminoAddress : string ;
47+ let commonPrefix : string ,
48+ chainInfo ,
49+ getCoin : ( ) => Promise < Asset > ,
50+ getRpcEndpoint : ( ) => Promise < string > ,
51+ creditFromFaucet ;
52+
53+ let getBalance : ( request : QueryBalanceRequest ) => Promise < QueryBalanceResponse > ;
54+ let getProposal : ( request : QueryProposalRequest ) => Promise < QueryProposalResponse > ;
55+ let getVote : ( request : QueryVoteRequest ) => Promise < QueryVoteResponse > ;
56+ let getValidators : ( request : QueryValidatorsRequest ) => Promise < QueryValidatorsResponse > ;
57+
58+ // Variables used accross testcases
59+ let proposalId : string ;
60+ let validatorAddress : string ;
61+
62+ beforeAll ( async ( ) => {
63+ ( { chainInfo, getCoin, getRpcEndpoint, creditFromFaucet } =
64+ useChain ( 'osmosis' ) ) ;
65+ denom = ( await getCoin ( ) ) . base ;
66+
67+ commonPrefix = chainInfo ?. chain ?. bech32_prefix ;
68+
69+ // Initialize wallet
70+ const directWallet = Secp256k1HDWallet . fromMnemonic ( generateMnemonic ( ) , [
71+ {
72+ prefix : commonPrefix ,
73+ hdPath : cosmosHdPath ,
74+ } ,
75+ ] ) ;
76+ const aminoWallet = Secp256k1HDWallet . fromMnemonic ( generateMnemonic ( ) , [
77+ {
78+ prefix : commonPrefix ,
79+ hdPath : cosmosHdPath ,
80+ } ,
81+ ] ) ;
82+ directSigner = directWallet . toOfflineDirectSigner ( ) ;
83+ aminoSigner = aminoWallet . toOfflineAminoSigner ( ) ;
84+ directAddress = ( await directSigner . getAccounts ( ) ) [ 0 ] . address ;
85+ aminoAddress = ( await aminoSigner . getAccounts ( ) ) [ 0 ] . address ;
86+
87+ // Create custom cosmos interchain client
88+ const rpcEndpoint = await getRpcEndpoint ( ) ;
89+ getBalance = createGetBalance ( rpcEndpoint ) ;
90+ getProposal = createGetProposal ( rpcEndpoint ) ;
91+ getVote = createGetVote ( rpcEndpoint ) ;
92+ getValidators = createGetValidators ( rpcEndpoint ) ;
93+
94+ // Transfer osmosis to address
95+ await creditFromFaucet ( directAddress ) ;
96+ await creditFromFaucet ( aminoAddress ) ;
97+ } , 200000 ) ;
98+
99+ it ( 'check address has tokens' , async ( ) => {
100+ const { balance } = await getBalance ( {
101+ address : directAddress ,
102+ denom,
103+ } ) ;
104+
105+ expect ( balance ! . amount ) . toEqual ( '10000000000' ) ;
106+ } , 10000 ) ;
107+
108+ it ( 'query validator address' , async ( ) => {
109+ const { validators } = await getValidators ( {
110+ status : bondStatusToJSON ( BondStatus . BOND_STATUS_BONDED ) ,
111+ } ) ;
112+ let allValidators = validators ;
113+ if ( validators . length > 1 ) {
114+ allValidators = validators . sort ( ( a , b ) =>
115+ new BigNumber ( b . tokens ) . minus ( new BigNumber ( a . tokens ) ) . toNumber ( )
116+ ) ;
117+ }
118+
119+ expect ( allValidators . length ) . toBeGreaterThan ( 0 ) ;
120+
121+ // set validator address to the first one
122+ validatorAddress = allValidators [ 0 ] . operatorAddress ;
123+ } ) ;
124+
125+ it ( 'stake tokens to genesis validator' , async ( ) => {
126+ const signingClient = await CosmosSigningClient . connectWithSigner (
127+ await getRpcEndpoint ( ) ,
128+ new DirectGenericOfflineSigner ( directSigner ) ,
129+ {
130+ broadcast : {
131+ checkTx : true ,
132+ deliverTx : true ,
133+ useLegacyBroadcastTxCommit : true ,
134+ } ,
135+ }
136+ ) ;
137+
138+ const delegate = createDelegate ( signingClient ) ;
139+
140+ const { balance } = await getBalance ( {
141+ address : directAddress ,
142+ denom,
143+ } ) ;
144+
145+ // Stake half of the tokens
146+ // eslint-disable-next-line no-undef
147+ const delegationAmount = ( BigInt ( balance ! . amount ) / BigInt ( 2 ) ) . toString ( ) ;
148+
149+ const fee = {
150+ amount : [
151+ {
152+ denom,
153+ amount : '100000' ,
154+ } ,
155+ ] ,
156+ gas : '550000' ,
157+ } ;
158+
159+ const result = await delegate (
160+ directAddress ,
161+ {
162+ delegatorAddress : directAddress ,
163+ validatorAddress : validatorAddress ,
164+ amount : {
165+ amount : delegationAmount ,
166+ denom : balance ! . denom ,
167+ } ,
168+ } ,
169+ fee ,
170+ "delegate"
171+ ) ;
172+ assertIsDeliverTxSuccess ( result ) ;
173+ } , 10000 ) ;
174+
175+ it ( 'submit a txt proposal' , async ( ) => {
176+ const signingClient = await CosmosSigningClient . connectWithSigner (
177+ await getRpcEndpoint ( ) ,
178+ new DirectGenericOfflineSigner ( directSigner ) ,
179+ {
180+ broadcast : {
181+ checkTx : true ,
182+ deliverTx : true ,
183+ } ,
184+ }
185+ ) ;
186+
187+ const submitProposal = createSubmitProposal ( signingClient ) ;
188+
189+ const contentMsg = TextProposal . fromPartial ( {
190+ title : 'Test Proposal' ,
191+ description : 'Test text proposal for the e2e testing' ,
192+ } ) ;
193+
194+ // Stake half of the tokens
195+ const fee = {
196+ amount : [
197+ {
198+ denom,
199+ amount : '100000' ,
200+ } ,
201+ ] ,
202+ gas : '550000' ,
203+ } ;
204+
205+ const result = await submitProposal (
206+ directAddress ,
207+ {
208+ proposer : directAddress ,
209+ initialDeposit : [
210+ {
211+ amount : '1000000' ,
212+ denom : denom ,
213+ } ,
214+ ] ,
215+ content : {
216+ typeUrl : '/cosmos.gov.v1beta1.TextProposal' ,
217+ value : TextProposal . encode ( contentMsg ) . finish ( ) ,
218+ } ,
219+ } ,
220+ fee ,
221+ "submit proposal"
222+ ) ;
223+ assertIsDeliverTxSuccess ( result ) ;
224+
225+ // Get proposal id from log events
226+ const proposalIdEvent = result . events . find (
227+ ( event ) => event . type === 'submit_proposal'
228+ ) ;
229+ const proposalIdEncoded = proposalIdEvent ! . attributes . find (
230+ ( attr ) => toUtf8 ( fromBase64 ( attr . key ) ) === 'proposal_id'
231+ ) ! . value ;
232+ proposalId = toUtf8 ( fromBase64 ( proposalIdEncoded ) ) ;
233+
234+ // eslint-disable-next-line no-undef
235+ expect ( BigInt ( proposalId ) ) . toBeGreaterThan ( BigInt ( 0 ) ) ;
236+ } , 200000 ) ;
237+
238+ it ( 'query proposal' , async ( ) => {
239+ const result = await getProposal ( {
240+ proposalId : BigInt ( proposalId ) ,
241+ } ) ;
242+
243+ expect ( result . proposal . proposalId . toString ( ) ) . toEqual ( proposalId ) ;
244+ } , 10000 ) ;
245+
246+ it ( 'vote on proposal using direct' , async ( ) => {
247+ // create direct address signing client
248+ const signingClient = await CosmosSigningClient . connectWithSigner (
249+ await getRpcEndpoint ( ) ,
250+ new DirectGenericOfflineSigner ( directSigner ) ,
251+ {
252+ broadcast : {
253+ checkTx : true ,
254+ deliverTx : true ,
255+ useLegacyBroadcastTxCommit : true ,
256+ } ,
257+ }
258+ ) ;
259+
260+ const vote = createVote ( signingClient ) ;
261+
262+ // Vote on proposal from genesis mnemonic address
263+ const fee = {
264+ amount : [
265+ {
266+ denom,
267+ amount : '100000' ,
268+ } ,
269+ ] ,
270+ gas : '550000' ,
271+ } ;
272+
273+ const result = await vote (
274+ directAddress ,
275+ {
276+ proposalId : BigInt ( proposalId ) ,
277+ voter : directAddress ,
278+ option : VoteOption . VOTE_OPTION_YES ,
279+ } ,
280+ fee ,
281+ "vote"
282+ ) ;
283+ assertIsDeliverTxSuccess ( result ) ;
284+ } , 10000 ) ;
285+
286+ it ( 'verify direct vote' , async ( ) => {
287+ const { vote } = await getVote ( {
288+ proposalId : BigInt ( proposalId ) ,
289+ voter : directAddress ,
290+ } ) ;
291+
292+ expect ( vote . proposalId . toString ( ) ) . toEqual ( proposalId ) ;
293+ expect ( vote . voter ) . toEqual ( directAddress ) ;
294+ expect ( vote . option ) . toEqual ( VoteOption . VOTE_OPTION_YES ) ;
295+ } , 10000 ) ;
296+
297+ it ( 'vote on proposal using amino' , async ( ) => {
298+ // create amino address signing client
299+ const signingClient = await CosmosSigningClient . connectWithSigner (
300+ await getRpcEndpoint ( ) ,
301+ new AminoGenericOfflineSigner ( aminoSigner ) ,
302+ {
303+ broadcast : {
304+ checkTx : true ,
305+ deliverTx : true ,
306+ useLegacyBroadcastTxCommit : true ,
307+ } ,
308+ }
309+ ) ;
310+
311+ const vote = createVote ( signingClient ) ;
312+
313+ // Vote on proposal from genesis mnemonic address
314+ const fee = {
315+ amount : [
316+ {
317+ denom,
318+ amount : '100000' ,
319+ } ,
320+ ] ,
321+ gas : '550000' ,
322+ } ;
323+
324+ const result = await vote (
325+ aminoAddress ,
326+ {
327+ proposalId : BigInt ( proposalId ) ,
328+ voter : aminoAddress ,
329+ option : VoteOption . VOTE_OPTION_NO ,
330+ } ,
331+ fee ,
332+ "vote"
333+ ) ;
334+ assertIsDeliverTxSuccess ( result ) ;
335+ } , 10000 ) ;
336+
337+ it ( 'verify amino vote' , async ( ) => {
338+ const { vote } = await getVote ( {
339+ proposalId : BigInt ( proposalId ) ,
340+ voter : aminoAddress ,
341+ } ) ;
342+
343+ expect ( vote . proposalId . toString ( ) ) . toEqual ( proposalId ) ;
344+ expect ( vote . voter ) . toEqual ( aminoAddress ) ;
345+ expect ( vote . option ) . toEqual ( VoteOption . VOTE_OPTION_NO ) ;
346+ } , 10000 ) ;
347+
348+ it ( 'wait for voting period to end' , async ( ) => {
349+ // wait for the voting period to end
350+ const { proposal } = await getProposal ( {
351+ proposalId : BigInt ( proposalId ) ,
352+ } ) ;
353+
354+ await expect ( waitUntil ( proposal . votingEndTime ) ) . resolves . not . toThrow ( ) ;
355+ } , 200000 ) ;
356+
357+ it ( 'verify proposal passed' , async ( ) => {
358+ const { proposal } = await getProposal ( {
359+ proposalId : BigInt ( proposalId ) ,
360+ } ) ;
361+
362+ expect ( proposal . status ) . toEqual ( ProposalStatus . PROPOSAL_STATUS_PASSED ) ;
363+ } , 10000 ) ;
364+ } ) ;
0 commit comments