Skip to content

Commit

Permalink
wip(gov): gov slice
Browse files Browse the repository at this point in the history
  • Loading branch information
Hemanthghs committed Nov 24, 2023
1 parent 441be72 commit 6e3bfd0
Show file tree
Hide file tree
Showing 4 changed files with 381 additions and 0 deletions.
72 changes: 72 additions & 0 deletions frontend/src/store/features/gov/govService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import Axios, { AxiosResponse } from 'axios';
import { convertPaginationToParams, cleanURL } from '../../../utils/util';

const proposalsURL = '/cosmos/gov/v1beta1/proposals';
const proposalTallyURL = (id: string): string =>
`/cosmos/gov/v1beta1/proposals/${id}/tally`;

const voterVoteURL = (id: string, voter: string): string =>
`/cosmos/gov/v1beta1/proposals/${id}/votes/${voter}`;

const depositParamsURL = `/cosmos/gov/v1beta1/params/deposit`;

const fetchProposals = (
baseURL: string,
key: string | undefined,
limit: number | undefined,
status: number
): Promise<AxiosResponse<GetProposalsInVotingResponse>> => {
let uri = `${cleanURL(baseURL)}${proposalsURL}`;
uri += `?proposal_status=${status}`;

const params = convertPaginationToParams({
key: key,
limit: limit,
});

if (params !== '') uri += `&${params}`;
return Axios.get(uri);
};

const fetchProposalTally = (
baseURL: string,
proposalId: string
): Promise<AxiosResponse> => {
let uri = `${cleanURL(baseURL)}${proposalTallyURL(proposalId)}`;
return Axios.get(uri);
};

const fetchVoterVote = (
baseURL: string,
proposalId: string,
voter: string,
key: string | undefined,
limit: number | undefined
): Promise<AxiosResponse<ProposalVote>> => {
let uri = `${cleanURL(baseURL)}${voterVoteURL(proposalId, voter)}`;
const params = convertPaginationToParams({
key: key,
limit: limit,
});
if (params !== '') uri += `?${params}`;
return Axios.get(uri);
};

const fetchProposal = (
baseURL: string,
proposalId: number
): Promise<AxiosResponse> =>
Axios.get(`${cleanURL(baseURL)}${proposalsURL}/${proposalId}`);

const fetchDepositParams = (baseURL: string): Promise<AxiosResponse> =>
Axios.get(`${cleanURL(baseURL)}${depositParamsURL}`);

const result = {
proposals: fetchProposals,
tally: fetchProposalTally,
votes: fetchVoterVote,
proposal: fetchProposal,
depositParams: fetchDepositParams,
};

export default result;
191 changes: 191 additions & 0 deletions frontend/src/store/features/gov/govSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
'use client';

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import govService from './govService';
import { cloneDeep } from 'lodash';
import { AxiosError } from 'axios';
import { ERR_UNKNOWN } from '@/utils/errors';
import { TxStatus } from '@/types/enums';

interface Chain {
active: {
status: string;
errMsg: string;
proposals: ActiveProposal[];
};
votes: VotesData;
tally: ProposalTallyData;
}

interface Chains {
[key: string]: Chain;
}

interface GovState {
chains: Chains;
defaultState: Chain;
}

const initialState: GovState = {
chains: {},
defaultState: {
active: {
status: TxStatus.INIT,
errMsg: '',
proposals: [],
},
votes: {
status: TxStatus.INIT,
errMsg: '',
proposals: {},
},
tally: {
status: TxStatus.INIT,
errMsg: '',
proposalTally: {},
},
},
};

export const getProposalsInVoting = createAsyncThunk(
'gov/active-proposals',
async (data: GetProposalsInVotingInputs, { rejectWithValue, dispatch }) => {
try {
const response = await govService.proposals(
data.baseURL,
data.key,
data.limit,
2
);

if (response?.data?.proposals?.length > 0) {
const proposals = response?.data?.proposals;
for (let i = 0; i < proposals.length; i++) {
dispatch(
getProposalTally({
baseURL: data.baseURL,
proposalId: proposals[i].proposal_id,
chainID: data.chainID,
})
);

dispatch(
getVotes({
baseURL: data.baseURL,
proposalId: proposals[i].proposal_id,
voter: data.voter,
chainID: data.chainID,
})
);
}
}

return {
chainID: data.chainID,
data: response.data,
};
} catch (error) {
if (error instanceof AxiosError) return rejectWithValue(error.message);
return rejectWithValue(ERR_UNKNOWN);
}
}
);

export const getVotes = createAsyncThunk(
'gov/voter-votes',
async (data: GetVotesInputs) => {
const response = await govService.votes(
data.baseURL,
data.proposalId,
data.voter,
data.key,
data.limit
);

response.data.vote.proposal_id = data.proposalId;

return {
chainID: data.chainID,
data: response.data,
};
}
);

export const getProposalTally = createAsyncThunk(
'gov/proposal-tally',
async (data: GetProposalTallyInputs) => {
const response = await govService.tally(data.baseURL, data.proposalId);

response.data.tally.proposal_id = data.proposalId;

return {
chainID: data.chainID,
data: response.data,
};
}
);

export const govSlice = createSlice({
name: 'gov',
initialState,
reducers: {},
extraReducers: (builder) => {
// active proposals
builder
.addCase(getProposalsInVoting.pending, (state, action) => {
const chainID = action.meta?.arg?.chainID;
if (!state.chains[chainID])
state.chains[chainID] = cloneDeep(initialState.defaultState);
})
.addCase(getProposalsInVoting.fulfilled, (state, action) => {
const chainID = action.payload?.chainID || '';
if (chainID.length > 0) {
let result = {
status: 'idle',
errMsg: '',
proposals: action.payload?.data?.proposals,
};
state.chains[chainID].active = result;
}
})
.addCase(getProposalsInVoting.rejected, (state, action) => {});

// votes
builder
.addCase(getVotes.pending, () => {})
.addCase(getVotes.fulfilled, (state, action) => {
const chainID = action.payload.chainID;
let result: VotesData = {
status: 'idle',
errMsg: '',
proposals: state.chains[chainID].votes?.proposals || {},
};

result.proposals[action.payload?.data?.vote?.proposal_id] =
action.payload.data;

state.chains[chainID].votes = result;
})
.addCase(getVotes.rejected, () => {});

// tally
builder
.addCase(getProposalTally.pending, () => {})
.addCase(getProposalTally.fulfilled, (state, action) => {
const chainID = action.payload.chainID;
let result = {
status: 'idle',
errMsg: '',
proposalTally: state.chains[chainID].tally?.proposalTally || {},
};

result.proposalTally[action.payload?.data?.tally?.proposal_id] =
action.payload?.data.tally;
state.chains[chainID].tally = result;
})
.addCase(getProposalTally.rejected, () => {});
},
});

export const {} = govSlice.actions;
export default govSlice.reducer;
2 changes: 2 additions & 0 deletions frontend/src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import stakeSlice from './features/staking/stakeSlice';
import bankSlice from './features/bank/bankSlice';
import distributionSlice from './features/distribution/distributionSlice';
import authSlice from './features/auth/authSlice';
import govSlice from './features/gov/govSlice';

export const store = configureStore({
reducer: {
Expand All @@ -18,6 +19,7 @@ export const store = configureStore({
bank: bankSlice,
auth: authSlice,
distribution: distributionSlice,
gov: govSlice,
},
});

Expand Down
116 changes: 116 additions & 0 deletions frontend/src/types/gov.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
interface ActiveProposal {
proposal_id: string;
content: {
'@type': string;
title: string;
description: string;
changes?: {
subspace: string;
key: string;
value: string;
}[];
};
status: string;
final_tally_result: {
yes: string;
abstain: string;
no: string;
no_with_veto: string;
};
submit_time: string;
deposit_end_time: string;
total_deposit: {
denom: string;
amount: string;
}[];
voting_start_time: string;
voting_end_time: string;
}

interface CosmosHubProposal {
status: string;
errMsg: string;
proposals: ActiveProposal[];
}

interface GovPagination {
next_key: string | undefined;
total: string;
}

interface GetProposalsInVotingResponse {
proposals: ActiveProposal[];
pagination: GovPagination;
}

interface VoteOption {
option: string;
weight: string;
}

interface Vote {
proposal_id: string;
voter: string;
option: string;
options: VoteOption[];
}

interface ProposalVote {
vote: Vote;
}

interface VotesData {
status: string;
errMsg: string;
proposals: {
[key: string]: ProposalVote;
};
}

interface ProposalTally {
[key: string]: {
yes: string;
abstain: string;
no: string;
no_with_veto: string;
proposal_id: string;
};
}

interface GetProposalTallyResponse {
[key: string]: {
yes: string;
abstain: string;
no: string;
no_with_veto: string;
};
}

interface ProposalTallyData {
status: string;
errMsg: string;
proposalTally: ProposalTally;
}

interface GetProposalsInVotingInputs {
baseURL: string;
chainID: string;
voter: string;
key?: string;
limit?: number;
}

interface GetVotesInputs {
baseURL: string;
proposalId: string;
voter: string;
chainID: string;
key?: string;
limit?: number;
}

interface GetProposalTallyInputs {
baseURL: string;
proposalId: string;
chainID: string;
}

0 comments on commit 6e3bfd0

Please sign in to comment.