diff --git a/apps/namada-interface/src/App/Staking/Staking.tsx b/apps/namada-interface/src/App/Staking/Staking.tsx index 9e0fb8eaa7..23b402cfd3 100644 --- a/apps/namada-interface/src/App/Staking/Staking.tsx +++ b/apps/namada-interface/src/App/Staking/Staking.tsx @@ -5,7 +5,11 @@ import { StakingContainer } from "./Staking.components"; import { StakingOverview } from "./StakingOverview"; import { ValidatorDetails } from "./ValidatorDetails"; import { TopLevelRoute, StakingAndGovernanceSubRoute } from "App/types"; -import { Validator } from "slices/StakingAndGovernance"; +import { + MyBalanceEntry, + Validator, + MyValidators, +} from "slices/StakingAndGovernance"; const initialTitle = "Staking"; @@ -38,8 +42,11 @@ const validatorNameFromUrl = (path: string): string | undefined => { }; type Props = { + myBalances: MyBalanceEntry[]; validators: Validator[]; + myValidators: MyValidators[]; selectedValidator: string | undefined; + fetchMyBalances: () => void; fetchValidators: () => void; fetchValidatorDetails: (validatorId: string) => void; }; @@ -50,7 +57,14 @@ export const Staking = (props: Props): JSX.Element => { const location = useLocation(); const navigate = useNavigate(); - const { fetchValidators, fetchValidatorDetails, validators } = props; + const { + fetchMyBalances, + fetchValidators, + fetchValidatorDetails, + myBalances, + validators, + myValidators, + } = props; // this is just so we can se the title/breadcrumb // in real case we do this cleanly in a callback that @@ -70,6 +84,7 @@ export const Staking = (props: Props): JSX.Element => { }); useEffect(() => { + fetchMyBalances(); fetchValidators(); }, []); @@ -110,7 +125,8 @@ export const Staking = (props: Props): JSX.Element => { element={ } diff --git a/apps/namada-interface/src/App/Staking/StakingOverview/StakingOverview.tsx b/apps/namada-interface/src/App/Staking/StakingOverview/StakingOverview.tsx index e14dca274f..94ca100f79 100644 --- a/apps/namada-interface/src/App/Staking/StakingOverview/StakingOverview.tsx +++ b/apps/namada-interface/src/App/Staking/StakingOverview/StakingOverview.tsx @@ -5,27 +5,25 @@ import { TableDimmedCell, TableConfigurations, } from "components/Table"; - -import { myBalancesData, myValidatorData } from "./fakeData"; import { - MyBalanceRow, + MyBalanceEntry, Validator, - MyStaking, + MyValidators, } from "slices/StakingAndGovernance"; // My Balances table row renderer and configuration -const myBalancesRowRenderer = (myBalanceRow: MyBalanceRow): JSX.Element => { +const myBalancesRowRenderer = (myBalanceEntry: MyBalanceEntry): JSX.Element => { return ( <> - {myBalanceRow.key} - {myBalanceRow.baseCurrency} + {myBalanceEntry.key} + {myBalanceEntry.baseCurrency} - {myBalanceRow.fiatCurrency} + {myBalanceEntry.fiatCurrency} ); }; -const myBalancesConfigurations: TableConfigurations = { +const myBalancesConfigurations: TableConfigurations = { title: "My Balances", rowRenderer: myBalancesRowRenderer, columns: [ @@ -36,7 +34,7 @@ const myBalancesConfigurations: TableConfigurations = { }; const MyValidatorsRowRenderer = ( - myValidatorRow: MyStaking, + myValidatorRow: MyValidators, callbacks?: ValidatorsCallbacks ): JSX.Element => { return ( @@ -44,7 +42,7 @@ const MyValidatorsRowRenderer = ( { - const formattedValidatorName = myValidatorRow.name + const formattedValidatorName = myValidatorRow.validator.name .replace(" ", "-") .toLowerCase(); @@ -56,7 +54,7 @@ const MyValidatorsRowRenderer = ( callbacks && callbacks.onClickValidator(formattedValidatorName); }} > - {myValidatorRow.name} + {myValidatorRow.validator.name} {myValidatorRow.stakingStatus} @@ -67,7 +65,7 @@ const MyValidatorsRowRenderer = ( const getMyValidatorsConfiguration = ( navigateToValidatorDetails: (validatorId: string) => void -): TableConfigurations => { +): TableConfigurations => { return { title: "My Validators", rowRenderer: MyValidatorsRowRenderer, @@ -134,12 +132,14 @@ const getAllValidatorsConfiguration = ( type Props = { navigateToValidatorDetails: (validatorId: string) => void; + myBalances: MyBalanceEntry[]; validators: Validator[]; - ownValidators: Validator[]; + myValidators: MyValidators[]; }; export const StakingOverview = (props: Props): JSX.Element => { - const { navigateToValidatorDetails, validators } = props; + const { navigateToValidatorDetails, myBalances, validators, myValidators } = + props; // we get the configurations for 2 tables that contain callbacks const myValidatorsConfiguration = getMyValidatorsConfiguration( @@ -154,14 +154,15 @@ export const StakingOverview = (props: Props): JSX.Element => { {/* my balances */} {/* my validators */}
diff --git a/apps/namada-interface/src/App/Staking/StakingOverview/fakeData.tsx b/apps/namada-interface/src/App/Staking/StakingOverview/fakeData.tsx deleted file mode 100644 index 1ee52dff93..0000000000 --- a/apps/namada-interface/src/App/Staking/StakingOverview/fakeData.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { RowBase } from "components/Table"; - -type Validator = RowBase & { - name: string; - homepageUrl: string; -}; - -// my balances -type MyBalanceRow = RowBase & { - key: string; - baseCurrency: string; - fiatCurrency: string; -}; - -export const myBalancesData: MyBalanceRow[] = [ - { - uuid: "1", - key: "Total Balance", - baseCurrency: "NAM 33.00", - fiatCurrency: "EUR 33.00", - }, - { - uuid: "2", - key: "Total Bonded", - baseCurrency: "NAM 10.00", - fiatCurrency: "EUR 10.00", - }, - { - uuid: "3", - key: "Pending Rewards", - baseCurrency: "NAM 23.00", - fiatCurrency: "EUR 23.00", - }, - { - uuid: "4", - key: "Available for Bonding", - baseCurrency: "NAM 10.00", - fiatCurrency: "EUR 10.00", - }, -]; - -// my validators -type MyValidatorsRow = Validator & { - stakingStatus: string; - stakedAmount: string; -}; - -export const myValidatorData: MyValidatorsRow[] = [ - { - uuid: "1", - name: "Polychain capital", - homepageUrl: "poly", - stakingStatus: "Bonded", - stakedAmount: "10.00", - }, - { - uuid: "2", - name: "Figment", - homepageUrl: "poly", - stakingStatus: "Bonded Pending", - stakedAmount: "3.00", - }, - { - uuid: "3", - name: "P2P", - homepageUrl: "poly", - stakingStatus: "Unboding (22 days left)", - stakedAmount: "20.00", - }, -]; diff --git a/apps/namada-interface/src/App/Staking/ValidatorDetails/ValidatorDetails.tsx b/apps/namada-interface/src/App/Staking/ValidatorDetails/ValidatorDetails.tsx index 9b16fe730d..32f5825154 100644 --- a/apps/namada-interface/src/App/Staking/ValidatorDetails/ValidatorDetails.tsx +++ b/apps/namada-interface/src/App/Staking/ValidatorDetails/ValidatorDetails.tsx @@ -1,7 +1,4 @@ -import { useState } from "react"; -import { MainContainerNavigation } from "App/StakingAndGovernance/MainContainerNavigation"; import { ValidatorDetailsContainer } from "./ValidatorDetails.components"; -import { Table } from "components/Table"; type Props = { validator?: string; diff --git a/apps/namada-interface/src/App/StakingAndGovernance/StakingAndGovernance.tsx b/apps/namada-interface/src/App/StakingAndGovernance/StakingAndGovernance.tsx index 7b9460aea6..e04af04ef0 100644 --- a/apps/namada-interface/src/App/StakingAndGovernance/StakingAndGovernance.tsx +++ b/apps/namada-interface/src/App/StakingAndGovernance/StakingAndGovernance.tsx @@ -13,6 +13,7 @@ import { } from "App/types"; import { + fetchMyBalances, fetchValidators, fetchValidatorDetails, } from "slices/StakingAndGovernance"; @@ -27,7 +28,9 @@ export const StakingAndGovernance = (): JSX.Element => { const stakingAndGovernance = useAppSelector( (state: RootState) => state.stakingAndGovernance ); - const { validators, selectedValidatorId } = stakingAndGovernance; + const { myBalances, validators, myValidators, selectedValidatorId } = + stakingAndGovernance; + // we need one of the sub routes, staking alone has nothing const stakingAndGovernanceSubRoute = locationToStakingAndGovernanceSubRoute(location); @@ -47,6 +50,10 @@ export const StakingAndGovernance = (): JSX.Element => { dispatch(fetchValidators()); }; + const fetchMyBalancesCallback = (): void => { + dispatch(fetchMyBalances()); + }; + // triggered by the url load or user click in const fetchValidatorDetailsCallback = (validatorId: string): void => { dispatch(fetchValidatorDetails(validatorId)); @@ -59,8 +66,11 @@ export const StakingAndGovernance = (): JSX.Element => { path={`${StakingAndGovernanceSubRoute.Staking}/*`} element={ diff --git a/apps/namada-interface/src/components/Table/Table.tsx b/apps/namada-interface/src/components/Table/Table.tsx index 61212739ed..d9d9525648 100644 --- a/apps/namada-interface/src/components/Table/Table.tsx +++ b/apps/namada-interface/src/components/Table/Table.tsx @@ -1,5 +1,3 @@ -import { useState } from "react"; -import { MainContainerNavigation } from "App/StakingAndGovernance/MainContainerNavigation"; import { TableContainer, TableElement } from "./Table.components"; import { TableConfigurations, RowBase, ColumnDefinition } from "./types"; diff --git a/apps/namada-interface/src/slices/StakingAndGovernance/README.md b/apps/namada-interface/src/slices/StakingAndGovernance/README.md index b2d454301d..6a72ba0944 100644 --- a/apps/namada-interface/src/slices/StakingAndGovernance/README.md +++ b/apps/namada-interface/src/slices/StakingAndGovernance/README.md @@ -1,3 +1,8 @@ # Staking And Governance -This part of hte state deals with the data that is mostly under Staking & Governance in the UI. It allows the user to perform all the actions regarding Staking, Governance and Public Goods Funding. +This part of the state deals with the data that is under Staking & Governance in the UI. It allows the user to perform all the actions regarding Staking, Governance and Public Goods Funding. + +These are the UIs the state is mainly serving: +* **Staking** - [https://specs.anoma.net/main/architecture/clients/web-wallet.html#stakingoverview](https://specs.anoma.net/main/architecture/clients/web-wallet.html#stakingoverview). +* **Governance** - [https://specs.anoma.net/main/architecture/clients/web-wallet.html#governanceproposals](https://specs.anoma.net/main/architecture/clients/web-wallet.html#governanceproposals). +* **Public goods funding** - [https://specs.anoma.net/main/architecture/clients/web-wallet.html#publicgoodsfundingoverview](https://specs.anoma.net/main/architecture/clients/web-wallet.html#publicgoodsfundingoverview). diff --git a/apps/namada-interface/src/slices/StakingAndGovernance/actions.ts b/apps/namada-interface/src/slices/StakingAndGovernance/actions.ts index db3a48f510..fb6a5debda 100644 --- a/apps/namada-interface/src/slices/StakingAndGovernance/actions.ts +++ b/apps/namada-interface/src/slices/StakingAndGovernance/actions.ts @@ -1,33 +1,33 @@ import { createAsyncThunk } from "@reduxjs/toolkit"; -import { FETCH_VALIDATORS, FETCH_VALIDATOR_DETAILS, Validator } from "./types"; import { - myBalancesData as _myBalancesData, - allValidatorsData, - myValidatorData as _myStakingPositions, -} from "./fakeData"; -export type ValidatorsPayload = { - chainId: string; - shieldedBalances: { - [accountId: string]: number; - }; -}; - -export type ValidatorDetailsPayload = { - name: string; - websiteUrl: string; -}; + FETCH_MY_BALANCES, + FETCH_VALIDATORS, + FETCH_VALIDATOR_DETAILS, + FETCH_MY_VALIDATORS, + Validator, + ValidatorDetailsPayload, + MyBalanceEntry, + MyValidators, + MyStaking, +} from "./types"; +import { allValidatorsData, myStakingData, myBalancesData } from "./fakeData"; +// this retrieves the validators +// this dispatches further actions that are depending on +// validators data export const fetchValidators = createAsyncThunk< { allValidators: Validator[] }, void ->(FETCH_VALIDATORS, async () => { - return Promise.resolve({ allValidators: allValidatorsData }); +>(FETCH_VALIDATORS, async (_, thunkApi) => { + const allValidators = allValidatorsData; + thunkApi.dispatch(fetchMyValidators(allValidators)); + return Promise.resolve({ allValidators }); }); export const fetchValidatorDetails = createAsyncThunk< ValidatorDetailsPayload | undefined, string ->(FETCH_VALIDATOR_DETAILS, async (validatorId: string) => { +>(FETCH_VALIDATOR_DETAILS, async (_validatorId: string) => { try { return Promise.resolve({ name: "polychain", @@ -37,3 +37,51 @@ export const fetchValidatorDetails = createAsyncThunk< return Promise.reject(); } }); + +// util to add validators to the user's staking entries +const myStakingToMyValidators = ( + myStaking: MyStaking[], + allValidators: Validator[] +): MyValidators[] => { + // try { + const myValidators: MyValidators[] = myStaking.map((myStakingEntry) => { + // let's get the validator we are going to add + const validator = allValidators.find( + (validator) => validator.uuid === myStakingEntry.validatorId + ); + + // this should not happen if the data is not corrupted + if (validator === undefined) { + throw `Validator with ID ${myStakingEntry.validatorId} not found`; + } + + return { ...myStakingEntry, validator: validator }; + }); + return myValidators; +}; + +// fetches staking data and appends the validators to it +// this needs the validators, so they are being passed in +// vs. getting them from the state +export const fetchMyValidators = createAsyncThunk< + { myValidators: MyValidators[] }, + Validator[] +>(FETCH_MY_VALIDATORS, async (allValidatorsData: Validator[]) => { + try { + const myValidators = myStakingToMyValidators( + myStakingData, + allValidatorsData + ); + return Promise.resolve({ myValidators }); + } catch (error) { + console.warn(`error: ${error}`); + return Promise.reject({}); + } +}); + +export const fetchMyBalances = createAsyncThunk< + { myBalances: MyBalanceEntry[] }, + void +>(FETCH_MY_BALANCES, async () => { + return Promise.resolve({ myBalances: myBalancesData }); +}); diff --git a/apps/namada-interface/src/slices/StakingAndGovernance/fakeData.ts b/apps/namada-interface/src/slices/StakingAndGovernance/fakeData.ts index 9e060e1831..225e647189 100644 --- a/apps/namada-interface/src/slices/StakingAndGovernance/fakeData.ts +++ b/apps/namada-interface/src/slices/StakingAndGovernance/fakeData.ts @@ -1,5 +1,5 @@ -import { Validator, MyStaking, MyBalanceRow } from "./types"; -export const myBalancesData: MyBalanceRow[] = [ +import { Validator, MyStaking, MyBalanceEntry } from "./types"; +export const myBalancesData: MyBalanceEntry[] = [ { uuid: "1", key: "Total Balance", @@ -26,96 +26,93 @@ export const myBalancesData: MyBalanceRow[] = [ }, ]; -export const myValidatorData: MyStaking[] = [ +export const myStakingData: MyStaking[] = [ { uuid: "1", - name: "Polychain capital", - homepageUrl: "poly", stakingStatus: "Bonded", stakedAmount: "10.00", + validatorId: "polychain-capital", }, { uuid: "2", - name: "Figment", - homepageUrl: "poly", stakingStatus: "Bonded Pending", stakedAmount: "3.00", + validatorId: "coinbase-custody", }, { uuid: "3", - name: "P2P", - homepageUrl: "poly", stakingStatus: "Unboding (22 days left)", stakedAmount: "20.00", + validatorId: "kraken", }, ]; export const allValidatorsData: Validator[] = [ { - uuid: "1", + uuid: "polychain-capital", name: "Polychain capital", homepageUrl: "https://polychain.capital", votingPower: "NAM 100 000", commission: "22%", }, { - uuid: "2", + uuid: "figment", name: "Figment", homepageUrl: "https://polychain.capital", votingPower: "NAM 100 000", commission: "20%", }, { - uuid: "3", + uuid: "p2p", name: "P2P", homepageUrl: "https://polychain.capital", votingPower: "NAM 100 000", commission: "20%", }, { - uuid: "4", + uuid: "coinbase-custody", name: "Coinbase Custody", homepageUrl: "https://polychain.capital", votingPower: "NAM 100 000", commission: "20%", }, { - uuid: "5", + uuid: "chorus-one", name: "Chorus One", homepageUrl: "https://polychain.capital", votingPower: "NAM 100 000", commission: "20%", }, { - uuid: "6", + uuid: "binance-staking", name: "Binance Staking", homepageUrl: "https://polychain.capital", votingPower: "NAM 100 000", commission: "20%", }, { - uuid: "7", + uuid: "dokiacapital", name: "DokiaCapital", homepageUrl: "https://polychain.capital", votingPower: "NAM 100 000", commission: "20%", }, { - uuid: "8", + uuid: "kraken", name: "Kraken", homepageUrl: "https://polychain.capital", votingPower: "NAM 100 000", commission: "20%", }, { - uuid: "9", + uuid: "zero-knowledge-validator-(ZKV)", name: "Zero Knowledge Validator (ZKV)", homepageUrl: "https://polychain.capital", votingPower: "NAM 100 000", commission: "20%", }, { - uuid: "10", + uuid: "paradigm", name: "Paradigm", homepageUrl: "https://polychain.capital", votingPower: "NAM 100 000", diff --git a/apps/namada-interface/src/slices/StakingAndGovernance/index.ts b/apps/namada-interface/src/slices/StakingAndGovernance/index.ts index 4b7f7232a7..4ce101073e 100644 --- a/apps/namada-interface/src/slices/StakingAndGovernance/index.ts +++ b/apps/namada-interface/src/slices/StakingAndGovernance/index.ts @@ -1,4 +1,12 @@ -export { fetchValidators, fetchValidatorDetails } from "./actions"; +export { + fetchMyBalances, + fetchValidators, + fetchValidatorDetails, +} from "./actions"; export { reducer as stakingAndGovernanceReducers } from "./reducers"; -export type { StakingAndGovernanceState } from "./reducers"; -export type { MyBalanceRow, Validator, MyStaking } from "./types"; +export type { + MyBalanceEntry, + Validator, + MyValidators, + StakingAndGovernanceState, +} from "./types"; diff --git a/apps/namada-interface/src/slices/StakingAndGovernance/reducers.ts b/apps/namada-interface/src/slices/StakingAndGovernance/reducers.ts index 3b65d51102..b74ad674a3 100644 --- a/apps/namada-interface/src/slices/StakingAndGovernance/reducers.ts +++ b/apps/namada-interface/src/slices/StakingAndGovernance/reducers.ts @@ -1,15 +1,17 @@ import { createSlice } from "@reduxjs/toolkit"; -import { fetchValidators, fetchValidatorDetails } from "./actions"; +import { + fetchMyBalances, + fetchValidators, + fetchMyValidators, + fetchValidatorDetails, +} from "./actions"; import { STAKING_AND_GOVERNANCE } from "./types"; -import { Validator, ValidatorId } from "./types"; - -export type StakingAndGovernanceState = { - validators: Validator[]; - selectedValidatorId?: ValidatorId; -}; +import { StakingAndGovernanceState } from "./types"; const initialState: StakingAndGovernanceState = { + myBalances: [], validators: [], + myValidators: [], }; export const stakingAndGovernanceSlice = createSlice({ @@ -18,26 +20,33 @@ export const stakingAndGovernanceSlice = createSlice({ reducers: {}, extraReducers: (builder) => { builder - .addCase(fetchValidators.pending, (_state, _action) => { - // start the loader + .addCase(fetchMyBalances.fulfilled, (state, action) => { + // stop the loader + state.myBalances = action.payload.myBalances; }) .addCase(fetchValidators.fulfilled, (state, action) => { // stop the loader state.validators = action.payload.allValidators; }) - .addCase(fetchValidators.rejected, (_state, _action) => { + .addCase(fetchValidators.rejected, (state, _action) => { + // stop the loader + state.validators = []; + }) + .addCase(fetchMyValidators.fulfilled, (state, action) => { + // stop the loader + state.myValidators = action.payload.myValidators; + }) + .addCase(fetchMyValidators.rejected, (state, _action) => { // stop the loader + state.myValidators = []; }) - .addCase(fetchValidatorDetails.pending, (state, action) => { + .addCase(fetchValidatorDetails.pending, (state, _action) => { // start the loader state.selectedValidatorId = undefined; }) .addCase(fetchValidatorDetails.fulfilled, (state, action) => { // stop the loader state.selectedValidatorId = action.payload?.name; - }) - .addCase(fetchValidatorDetails.rejected, (_state, _action) => { - // stop the loader }); }, }); diff --git a/apps/namada-interface/src/slices/StakingAndGovernance/types.ts b/apps/namada-interface/src/slices/StakingAndGovernance/types.ts index af26a0d905..2c90410185 100644 --- a/apps/namada-interface/src/slices/StakingAndGovernance/types.ts +++ b/apps/namada-interface/src/slices/StakingAndGovernance/types.ts @@ -1,5 +1,7 @@ export const STAKING_AND_GOVERNANCE = "stakingAndGovernance"; +export const FETCH_MY_BALANCES = `${STAKING_AND_GOVERNANCE}/FETCH_MY_BALANCES`; export const FETCH_VALIDATORS = `${STAKING_AND_GOVERNANCE}/FETCH_VALIDATORS`; +export const FETCH_MY_VALIDATORS = `${STAKING_AND_GOVERNANCE}/FETCH_MY_VALIDATORS`; export const FETCH_VALIDATOR_DETAILS = `${STAKING_AND_GOVERNANCE}/FETCH_VALIDATOR_DETAILS`; export enum StakingAndGovernanceErrors { @@ -9,10 +11,12 @@ export enum StakingAndGovernanceErrors { // TODO check this out, what format, do we have constrains export type ValidatorId = string; +// PLACEHOLDER type Unique = { uuid: string; }; +// represents the details of a validator export type Validator = Unique & { name: string; votingPower: string; @@ -20,25 +24,36 @@ export type Validator = Unique & { commission: string; }; -// USE THIS -// export type MyStaking = Unique & { -// stakingStatus: string; -// stakedAmount: string; -// validator: Validator; -// }; +// represents users staking position +export type MyStaking = Unique & { + stakingStatus: string; + stakedAmount: string; + validatorId: string; +}; -// PLACEHOLDERS -export type MyStaking = { - uuid: string; - name: string; - homepageUrl: string; +// represents users staking position combined with the validator +export type MyValidators = Unique & { stakingStatus: string; stakedAmount: string; + validator: Validator; }; -export type MyBalanceRow = { - uuid: string; +// represents users staking position combined with the validator +export type MyBalanceEntry = Unique & { key: string; baseCurrency: string; fiatCurrency: string; }; + +// PLACEHOLDER +export type ValidatorDetailsPayload = { + name: string; + websiteUrl: string; +}; + +export type StakingAndGovernanceState = { + myBalances: MyBalanceEntry[]; + validators: Validator[]; + myValidators: MyValidators[]; + selectedValidatorId?: ValidatorId; +}; diff --git a/apps/namada-interface/src/store/mocks.ts b/apps/namada-interface/src/store/mocks.ts index 30b489c7b4..ff2f7322b9 100644 --- a/apps/namada-interface/src/store/mocks.ts +++ b/apps/namada-interface/src/store/mocks.ts @@ -219,6 +219,8 @@ export const mockAppState: RootState = { rates: {}, }, stakingAndGovernance: { + myBalances: [], validators: [], + myValidators: [], }, };