diff --git a/public/icons.svg b/public/icons.svg index f708c1db..37362bba 100644 --- a/public/icons.svg +++ b/public/icons.svg @@ -96,4 +96,27 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/App.vue b/src/App.vue index cd644601..7c912bd2 100644 --- a/src/App.vue +++ b/src/App.vue @@ -2,21 +2,9 @@ diff --git a/src/common/b64.ts b/src/common/b64.ts index 715a332c..cb2f3615 100644 --- a/src/common/b64.ts +++ b/src/common/b64.ts @@ -1,3 +1,3 @@ export const urlSafeEncode = (value: string) => { - return value.replace('+/gi', '_').replace('\//gi', '-'); + return value.replace(/\+/gi, '_').replace(/\//g, '-'); } diff --git a/src/common/chart.ts b/src/common/chart.ts index c710540d..224219a6 100644 --- a/src/common/chart.ts +++ b/src/common/chart.ts @@ -36,10 +36,12 @@ export const getMappedVotes = (details: DetailedVote) => { export const getMappedTimeline = (details: DetailedVote): TimelineData[] => { const timeline = details.timeline.map((el) => { if (el) { + const postfix = el.time && !compareNow(el.time) ? 'ago' : 'left'; + return ({ label: el.title, active: el.time ? !compareNow(el.time) : false, - subtitle: el.time ? `${fromNow(el.time)} ago` : '--' + subtitle: el.time ? `${fromNow(el.time)} ${postfix}` : 'N/A' }); } }); diff --git a/src/common/cosmos-reducer.ts b/src/common/cosmos-reducer.ts index 34e55178..6dc809a4 100644 --- a/src/common/cosmos-reducer.ts +++ b/src/common/cosmos-reducer.ts @@ -390,7 +390,7 @@ const proposalStatusMap = (status: ProposalRawStatus) => { } } -export const proposalReducer = (proposal: ProposalRaw, totalBondedTokens: string, detailedVotes: DetailedVote): Proposal => { +export const proposalReducer = (proposal: ProposalRaw, totalBondedTokens: string, detailedVotes: DetailedVote | null): Proposal => { const typeStringArray = proposal.content['@type'].split('.') const typeString = typeStringArray[typeStringArray.length - 1]; const type = proposalTypeEnumDictionary[typeString] as ProposalType; @@ -405,10 +405,10 @@ export const proposalReducer = (proposal: ProposalRaw, totalBondedTokens: string status: proposalStatusMap(proposal.status), statusBeginTime: proposalBeginTime(proposal), statusEndTime: proposalEndTime(proposal), - tally: tallyReducer(proposal, detailedVotes.tally, totalBondedTokens), + tally: detailedVotes ? tallyReducer(proposal, detailedVotes.tally, totalBondedTokens) : null, deposit: getDeposit(proposal), summary: getProposalSummary(type), - detailedVotes, + detailedVotes: detailedVotes ? detailedVotes : null, } } diff --git a/src/components/BalanceSummary.vue b/src/components/BalanceSummary.vue index c04613d0..88d8eed9 100644 --- a/src/components/BalanceSummary.vue +++ b/src/components/BalanceSummary.vue @@ -7,12 +7,12 @@

- APR + TOTAL ({{ network.stakingDenom }})

- @@ -86,29 +57,27 @@ import { defineComponent, computed } from 'vue'; import { useQuasar } from 'quasar'; import { useStore } from 'src/store'; -import { SupplyResponse, Validator, Balance } from 'src/models'; +import { Validator } from 'src/models'; import BalanceSummary from 'src/components/BalanceSummary.vue'; import ValidatorsSummary from 'src/components/ValidatorsSummary.vue'; import ValidatorsTable from 'src/components/ValidatorsTable.vue'; import ClaimDialog from 'src/components/ClaimDialog.vue'; -import ChainStats from 'src/components/ChainStats.vue'; export default defineComponent({ name: 'Portfolio', components: { BalanceSummary, ValidatorsSummary, - ValidatorsTable, - ChainStats + ValidatorsTable }, setup() { const store = useStore(); const quasar = useQuasar(); - const network = computed(() => store.state.authentication.network); - const rewards = computed(() => store.state.data.rewards); + const session = computed(() => store.state.authentication.session); + const loading = computed(() => store.state.authentication.loading || store.state.authentication.changing); const validatorsOfDelegations = computed(() => store.getters['data/validatorsOfDelegations'] as Validator[]); const delegationsLoaded = computed(() => store.state.data.delegationsLoaded); @@ -116,12 +85,6 @@ export default defineComponent({ const validatorsOfUndelegations = computed(() => store.getters['data/validatorsOfUndelegations'] as Validator[]); const undelegationsLoaded = computed(() => store.state.data.undelegationsLoaded); - const supplyInfo = computed(() => store.getters['data/supplyInfo'] as SupplyResponse | null); - const loadingSupplyInfo = computed(() => store.state.data.loadingSupplyInfo); - - const balance = computed(() => store.getters['data/currentBalance'] as Balance | undefined); - const loadingBalance = computed(() => !store.state.data.balancesLoaded || store.state.data.loading); - const openClaimDialog = () => { quasar.dialog({ component: ClaimDialog, @@ -131,11 +94,8 @@ export default defineComponent({ } return { - network, - balance, - loadingBalance, - supplyInfo, - loadingSupplyInfo, + loading, + session, rewards, validatorsOfDelegations, delegationsLoaded, diff --git a/src/modules/wallet/views/Proposal.vue b/src/modules/wallet/views/Proposal.vue index 2fa66e76..63269bce 100644 --- a/src/modules/wallet/views/Proposal.vue +++ b/src/modules/wallet/views/Proposal.vue @@ -3,7 +3,7 @@
-
+

{{ proposal?.title }}

@@ -12,6 +12,12 @@ {{ proposal?.summary }}

+
+ + + + +
@@ -103,11 +122,13 @@ export default defineComponent({ const session = computed(() => store.state.authentication.session); const proposal = computed(() => store.state.data.proposals.find(el => el.id === proposalID)); + const loadingAuth = computed(() => store.state.authentication.loading || store.state.authentication.changing); + const loading = computed(() => !store.state.data.proposalsLoaded || store.state.data.loading || loadingAuth.value); - const description = computed(() => marked(sanitizeHtml(proposal.value?.description ?? ''))); + const description = computed(() => marked(sanitizeHtml(proposal.value?.description ?? '').replace(/\\n/gm, '\n'))); const dataset = computed(() => { - if (proposal.value) { + if (proposal.value && proposal.value.detailedVotes) { return getMappedVotes(proposal.value.detailedVotes); } @@ -115,7 +136,7 @@ export default defineComponent({ }); const entries = computed(() => { - if (proposal.value) { + if (proposal.value && proposal.value.detailedVotes) { return getMappedTimeline(proposal.value.detailedVotes); } @@ -151,6 +172,8 @@ export default defineComponent({ }); return { + loadingAuth, + loading, session, proposal, description, diff --git a/src/modules/wallet/views/Proposals.vue b/src/modules/wallet/views/Proposals.vue index e7b31d61..8c14d6ff 100644 --- a/src/modules/wallet/views/Proposals.vue +++ b/src/modules/wallet/views/Proposals.vue @@ -24,7 +24,14 @@ @@ -38,11 +45,13 @@ import { useStore } from 'src/store'; import { ProposalStatus } from 'src/models'; import ProposalItem from 'src/components/ProposalItem.vue'; +import ProposalsSummary from 'src/components/ProposalsSummary.vue'; export default defineComponent({ name: 'Proposals', components: { ProposalItem, + ProposalsSummary }, setup() { const store = useStore(); diff --git a/src/modules/wallet/views/Stats.vue b/src/modules/wallet/views/Stats.vue new file mode 100644 index 00000000..1c633bc0 --- /dev/null +++ b/src/modules/wallet/views/Stats.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/src/modules/wallet/views/Validator.vue b/src/modules/wallet/views/Validator.vue index 1b8536b7..4ac41e3b 100644 --- a/src/modules/wallet/views/Validator.vue +++ b/src/modules/wallet/views/Validator.vue @@ -2,15 +2,15 @@
- - + +
@@ -48,6 +48,8 @@ export default defineComponent({ const validator = computed(() => store.state.data.validators.find(el => el.id === props.address)); const validatorDelegations = computed(() => store.state.data.validatorDelegations); const selfStakeValidator = computed(() => store.state.data.selfStakeValidator); + const loadingAuth = computed(() => store.state.authentication.loading || store.state.authentication.changing); + const loading = computed(() => !store.state.data.validatorsLoaded || store.state.data.loading || loadingAuth.value); onMounted(async () => { if (validator.value === undefined) { @@ -59,6 +61,7 @@ export default defineComponent({ }); return { + loading, validator, validatorDelegations, selfStakeValidator diff --git a/src/router/index.ts b/src/router/index.ts index 0c132af9..da5faa3f 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,4 +1,4 @@ -/* import Store from 'src/store'; */ +import Store from 'src/store'; import { createMemoryHistory, createRouter, @@ -23,12 +23,10 @@ const Router = createRouter({ ) }); -/* Router.beforeEach((to, _, next) => { +Router.beforeEach((to, _, next) => { const logged = Store.state.authentication.session !== undefined; const match = to.matched.find(el => el.name === 'login'); - console.log(to); - if (!logged && !match) { if (to.fullPath !== '/') { return next({ name: 'authentication', query: { r: to.fullPath } }); @@ -37,19 +35,19 @@ const Router = createRouter({ return next({ name: 'authentication' }); } - const defaultRoute = { name: 'wallet' }; + /* const defaultRoute = { name: 'wallet' }; if (logged && (match || to.path === '/')) { return next(defaultRoute); - } + } */ return next(); -}); */ +}); Router.afterEach(to => { const title = Array.isArray(to.meta.title) ? to.meta.title.join(' - ') : to.meta.title as string; window.document.documentElement.scrollTop = 0; - window.document.title = `${title}`; + window.document.title = `Bitsong – ${title}`; }); export default Router; diff --git a/src/services/cosmos.ts b/src/services/cosmos.ts index a5c1bea5..bc5372d7 100644 --- a/src/services/cosmos.ts +++ b/src/services/cosmos.ts @@ -1,21 +1,21 @@ +import { Vote, Deposit } from './../models/proposals'; +import { Coin } from '@cosmjs/stargate'; +import { DelegationWithBalance } from './../models/delegations'; import { api } from 'src/boot/axios'; import { BigNumber } from 'bignumber.js'; import { BlockResponse } from '@cosmjs/launchpad'; import { blockReducer, coinReducer, delegationReducer, rewardReducer, undelegationReducer, balanceReducer, validatorReducer, depositReducer, voteReducer, proposalReducer, getStakingCoinViewAmount, topVoterReducer } from 'src/common/cosmos-reducer'; import { PoolResponse, - BalanceResponse, PaginationResponse, BlockReduced, ValidatorStatus, Validator, - DelegationResponse, DenomTraceResponse, ClientStateResponse, RewardsResponse, Reward, BalanceCoin, - UnbondingDelegationResponse, UnbondingDelegationFlat, UnbondingDelegationRaw, Delegation, @@ -23,14 +23,11 @@ import { ValidatorStatusRequest, AnnualProvisionsResponse, ValidatorResponse, - ProposalResponse, GovParamsResponse, TallyParams, DepositParams, ProposalRaw, ProposalRawStatus, - VoteResponse, - DepositResponse, ValidatorMap, DetailedVote, CommunityPoolResponse, @@ -39,7 +36,8 @@ import { ValidatorsDelegationResponse, AccountResponse, AccountInfo, - InflationResponse + InflationResponse, + BankSupplyDenomResponse } from 'src/models'; import { chunk, compact, orderBy, reduce } from 'lodash'; import Store from 'src/store'; @@ -47,6 +45,7 @@ import { Tally } from '@cosmjs/launchpad/build/lcdapi/gov'; import { percentage, setDecimalLength } from 'src/common/numbers'; import { getCoinLookup } from 'src/common/network'; import { decodeB32, encodeB32 } from 'src/common/address'; +import { urlSafeEncode } from 'src/common/b64'; const GOLANG_NULL_TIME = '0001-01-01T00:00:00Z'; // time that gets serialized from null in golang @@ -60,28 +59,33 @@ export const getBlock = async (blockHeight: number | undefined = undefined): Pro } }; -export const queryAutoPaginate = async (url: string) => { +export const queryAutoPaginate = async (url: string) => { try { - const response = await api.get(url); - const paginatedData = response.data; + let data: K[] = []; + let response = await api.get(url); + const keys = Object.keys(response.data); + const fieldIndex = keys.indexOf('pagination') ? 0 : 1; + const fieldName = keys[fieldIndex]; + data = response.data[fieldName] as K[]; - /* while (response.data.pagination !== null && response.data.pagination.next_key !== null) { - const data = await api.get(url + `?pagination.key=${urlSafeEncode(response.data.pagination.next_key)}`); - paginatedData = paginatedData.concat(data.data.pagination); - } */ + while (response.data.pagination && response.data.pagination.next_key !== null) { + response = await api.get(url + `?pagination.key=${urlSafeEncode(response.data.pagination.next_key)}`); - return paginatedData; + data.concat(response.data[fieldName] as K[]); + } + + return data; } catch (error) { throw error; } } export const getDelegationsForDelegator = async (address: string, validatorsDictionary: { [key: string]: Validator }) => { - const delegations = await queryAutoPaginate( + const delegations = await queryAutoPaginate( `cosmos/staking/v1beta1/delegations/${address}` ); - const delegationsReduced = delegations.delegation_responses.length ? delegations.delegation_responses.map((delegation) => + const delegationsReduced = delegations.length ? delegations.map((delegation) => delegationReducer( delegation, validatorsDictionary[delegation.delegation.validator_address], @@ -93,9 +97,9 @@ export const getDelegationsForDelegator = async (address: string, validatorsDict } export const getUndelegationsForDelegator = async (address: string, validatorsDictionary: { [key: string]: Validator }) => { - const response = await queryAutoPaginate(`cosmos/staking/v1beta1/delegators/${address}/unbonding_delegations`); + const response = await queryAutoPaginate(`cosmos/staking/v1beta1/delegators/${address}/unbonding_delegations`); - const undelegations = response ? response.unbonding_responses : []; + const undelegations = response ? response : []; // undelegations come in a nested format { validator_address, delegator_address, entries } // we flatten the format to be able to easier iterate over the list @@ -164,6 +168,18 @@ export const getAnnualProvision = async () => { return response.data.annual_provisions; } +export const getSupply = async () => { + const response = await queryAutoPaginate('cosmos/bank/v1beta1/supply/'); + + return response; +} + +export const getSupplyByDenom = async (denom: string) => { + const response = await api.get(`cosmos/bank/v1beta1/supply/${denom}`); + + return response.data.amount; +} + export const getPool = async () => { const response = await api.get('cosmos/staking/v1beta1/pool'); @@ -231,9 +247,9 @@ export const getBalances = async (address: string, validatorsDictionary: { [key: } try { - const balancesResponse = await queryAutoPaginate(`cosmos/bank/v1beta1/balances/${address}`); + const balancesResponse = await queryAutoPaginate(`cosmos/bank/v1beta1/balances/${address}`); - const balances = balancesResponse ? balancesResponse.balances : []; + const balances = balancesResponse ? balancesResponse : []; const coins = await Promise.all( balances.map((balance) => { @@ -303,15 +319,15 @@ const dataExistsInThisChain = (timestamp: string | number) => { } const getVotes = async (proposal: ProposalRaw) => { - const response = await queryAutoPaginate(`/cosmos/gov/v1beta1/proposals/${proposal.proposal_id}/votes`); + const response = await queryAutoPaginate(`/cosmos/gov/v1beta1/proposals/${proposal.proposal_id}/votes`); - return response.votes; + return response; } const getDeposits = async (proposal: ProposalRaw) => { - const response = await queryAutoPaginate(`/cosmos/gov/v1beta1/proposals/${proposal.proposal_id}/deposits`); + const response = await queryAutoPaginate(`/cosmos/gov/v1beta1/proposals/${proposal.proposal_id}/deposits`); - return response.deposits; + return response; } export const getDetailedVotes = async (proposal: ProposalRaw, tallyParams: TallyParams, depositParams: DepositParams, validators: ValidatorMap): Promise => { @@ -437,19 +453,25 @@ export const getProposals = async (validators: ValidatorMap) => { { tally_params: tallyParams }, { deposit_params: depositParams }, ] = await Promise.all([ - queryAutoPaginate('cosmos/gov/v1beta1/proposals'), + queryAutoPaginate('cosmos/gov/v1beta1/proposals'), getPool(), getTallying(), getDeposit() ]); - if (!Array.isArray(proposalsResponse.proposals)) { + if (!Array.isArray(proposalsResponse)) { return []; } const proposals = await Promise.all( - proposalsResponse.proposals.map(async (proposal) => { - const detailedVotes = await getDetailedVotes(proposal, tallyParams, depositParams, validators); + proposalsResponse.map(async (proposal) => { + let detailedVotes: DetailedVote | null = null; + + try { + detailedVotes = await getDetailedVotes(proposal, tallyParams, depositParams, validators); + } catch (error) { + console.error(error); + } return proposalReducer( proposal, diff --git a/src/signing/messages.ts b/src/signing/messages.ts index e22d3fd2..68378bb8 100644 --- a/src/signing/messages.ts +++ b/src/signing/messages.ts @@ -105,6 +105,13 @@ export function Coin({ amount, denom }: StargateCoin, coinLookup: CoinLookUp[]) .toFixed(), denom: lookup.chainDenom, }; + } else { + return { + amount: new BigNumber(amount) + .dividedBy('1e-6') + .toFixed(), + denom + }; } } diff --git a/src/store/authentication/actions.ts b/src/store/authentication/actions.ts index 52fe668d..f8d37daa 100644 --- a/src/store/authentication/actions.ts +++ b/src/store/authentication/actions.ts @@ -18,15 +18,18 @@ const actions: ActionTree = { throw error; } finally { commit('setLoading', false); + commit('setChanging', false); } }, - async changeNetwork({ commit, dispatch }, network: NetworkConfig) { + async changeNetwork({ commit, dispatch }, { network, refresh }: { network: NetworkConfig, refresh: boolean }) { try { commit('setChanging', true); commit('setNetwork', network); api.defaults.baseURL = network.apiURL; - await dispatch('init'); + if (refresh) { + await dispatch('init'); + } } catch (error) { console.error(error); throw error; @@ -36,6 +39,8 @@ const actions: ActionTree = { }, async init({ dispatch, commit, state, rootState }) { try { + await dispatch('data/resetSessionData', undefined, { root: true }); + if (state.session && state.session.sessionType === SessionType.KEPLR) { await dispatch('keplr/init', 0, { root: true }); await dispatch('signIn', { diff --git a/src/store/authentication/mutations.ts b/src/store/authentication/mutations.ts index 8e531a4a..9cf786cc 100644 --- a/src/store/authentication/mutations.ts +++ b/src/store/authentication/mutations.ts @@ -1,3 +1,4 @@ +import { cloneDeep } from 'lodash'; import { Session, NetworkConfig } from 'src/models'; import { MutationTree } from 'vuex' import { AuthenticationStateInterface } from './state' @@ -7,7 +8,7 @@ const mutation: MutationTree = { state.session = session; }, setNetwork(state, network: NetworkConfig) { - state.network = network; + state.network = cloneDeep(network); }, setLoading(state, loading: boolean) { state.loading = loading; diff --git a/src/store/data/actions.ts b/src/store/data/actions.ts index 7d9ee9b9..37401fe0 100644 --- a/src/store/data/actions.ts +++ b/src/store/data/actions.ts @@ -15,13 +15,17 @@ import { getAccountInfo, getSupplyInfo, getPool, - getInflation + getInflation, + getCommunityPool, + getSupplyByDenom } from 'src/services'; import { keyBy } from 'lodash'; import { updateValidatorImages } from 'src/common/keybase'; import { AccountInfo, BlockReduced, TransactionRequest, Validator } from 'src/models'; import { createSignBroadcast, pollTxInclusion } from 'src/signing/transaction-manager'; import { getAPR } from 'src/common/numbers'; +import { getCoinLookup } from 'src/common/network'; +import { getStakingCoinViewAmount } from 'src/common/cosmos-reducer'; const actions: ActionTree = { resetSessionData({ commit }) { @@ -95,17 +99,6 @@ const actions: ActionTree = { commit('setLoadingSupplyInfo', true); const supplyInfo = await getSupplyInfo(); commit('setSupplyInfo', supplyInfo); - - if (!supplyInfo) { - commit( - 'notifications/add', - { - type: 'danger', - message: 'Getting supply info failed: endpoint undefined', - }, - { root: true } - ); - } } catch (err) { if (err instanceof Error) { commit( @@ -125,18 +118,38 @@ const actions: ActionTree = { await dispatch('getAPR'); } }, - async getAPR({ commit, state }) { + async getAPR({ commit, rootState }) { try { commit('setLoadingAPR', true); - if (state.supplyInfo) { - const pool = await getPool(); - const inflation = await getInflation(); - const apr = getAPR(state.supplyInfo.chainSupply, inflation.inflation, pool.pool.bonded_tokens); + const supplyCoin = getCoinLookup( + rootState.authentication.network.stakingDenom, + 'viewDenom' + ); + + const chainSupplyTotal = await getSupplyByDenom(supplyCoin ? supplyCoin.chainDenom : ''); + const communityPool = await getCommunityPool(); + + commit('setSupply', chainSupplyTotal); + commit('setCommunityPool', communityPool.pool); + + const chainSupplyCoin = getStakingCoinViewAmount(chainSupplyTotal ? chainSupplyTotal.amount : '0') + + const chainSupply = chainSupplyCoin.toString(); + + const pool = await getPool(); + const inflation = await getInflation(); + + if (chainSupplyTotal) { + const apr = getAPR(chainSupply, inflation.inflation, pool.pool.bonded_tokens); commit('setApr', apr.toString()); } + + commit('setPool', pool.pool); + commit('setInflation', inflation.inflation); } catch (err) { + console.error(err); if (err instanceof Error) { commit( 'notifications/add', @@ -442,17 +455,6 @@ const actions: ActionTree = { return hash; } } catch (err) { - if (err instanceof Error) { - commit( - 'notifications/add', - { - type: 'danger', - message: 'Getting validator self stake failed:' + err.message, - }, - { root: true } - ); - } - throw err; } finally { commit('setLoadingSignTransaction', false); diff --git a/src/store/data/getters.ts b/src/store/data/getters.ts index df2409bc..c47bc464 100644 --- a/src/store/data/getters.ts +++ b/src/store/data/getters.ts @@ -4,7 +4,8 @@ import { StateInterface } from '../index'; import { DataStateInterface } from './state'; import { bigFigureOrShortDecimals, percent } from 'src/common/numbers'; import { Dictionary, keyBy, reduce, reverse, sortBy, take } from 'lodash'; -import { Validator, ValidatorMap, Reward, ValidatorStatus } from 'src/models'; +import { Validator, ValidatorMap, Reward, ValidatorStatus, ProposalStatus } from 'src/models'; +import { getStakingCoinViewAmount } from 'src/common/cosmos-reducer'; const getters: GetterTree = { totalRewardsPerDenom({ rewards }) { @@ -31,6 +32,26 @@ const getters: GetterTree = { }, {}); }; }, + balances({ balances }, _getters, { authentication }) { + return sortBy(balances, (balance) => balance.denom === authentication.network.stakingDenom ? 0 : 1).map( + balance => { + if (balance.denom === authentication.network.stakingDenom) { + return ({ + ...balance, + }); + } + + const total = getStakingCoinViewAmount(new BigNumber(balance.total).toString()); + const available = getStakingCoinViewAmount(new BigNumber(balance.available).toString()); + + return ({ + ...balance, + total: bigFigureOrShortDecimals(total), + available: bigFigureOrShortDecimals(available), + }); + } + ); + }, currentBalance({ balances }) { const balance = [...balances].pop(); @@ -77,7 +98,34 @@ const getters: GetterTree = { activeValidators({ validators }) { return validators.filter(el => el.status === ValidatorStatus.ACTIVE); }, - supplyInfo({ supplyInfo }) { + votingProposalsCount({ proposals }) { + return proposals.filter(el => el.status === ProposalStatus.VOTING).length; + }, + getTotalSupply({ supply }) { + if (supply) { + const amount = getStakingCoinViewAmount(supply ? supply.amount : '0'); + const total = new BigNumber(amount); + + return total.toString(); + } + + return null; + }, + getCommunityPool({ communityPool }) { + if (communityPool.length > 0) { + let total = new BigNumber('0'); + + communityPool.forEach(coin => { + const amount = getStakingCoinViewAmount(coin ? coin.amount : '0'); + total = total.plus(new BigNumber(amount)); + }); + + return total.toString(); + } + + return null; + }, + supplyInfo({ supplyInfo }, getters, { authentication }) { if (supplyInfo) { return { ...supplyInfo, @@ -85,13 +133,34 @@ const getters: GetterTree = { communityPool: `${bigFigureOrShortDecimals(new BigNumber(supplyInfo.communityPool).toString()) ?? ''} ${supplyInfo.denom}`, totalSupply: `${bigFigureOrShortDecimals(new BigNumber(supplyInfo.totalSupply).toString()) ?? ''} ${supplyInfo.denom}` }; + } else { + const totalSupply = getters['getTotalSupply'] as string | null; + const communityPool = getters['getCommunityPool'] as string | null; + + return { + totalSupply: totalSupply ? `${bigFigureOrShortDecimals(totalSupply) ?? ''} ${authentication.network.stakingDenom}` : null, + communityPool: communityPool ? `${bigFigureOrShortDecimals(communityPool) ?? ''} ${authentication.network.stakingDenom}` : null, + } + } + }, + getAprInfo({ apr }) { + return apr ? percent(new BigNumber(apr).toFixed(4)) : null; + }, + getInflation({ inflation }) { + if (inflation) { + return percent(new BigNumber(inflation).toFixed(4)); + } + + return null; + }, + getBondedTokens({ pool }, _getters, { authentication }) { + if (pool) { + const bondedTokensNumber = new BigNumber(getStakingCoinViewAmount(pool.bonded_tokens)); + return `${bigFigureOrShortDecimals(bondedTokensNumber.toString()) ?? ''} ${authentication.network.stakingDenom}`; } return null; }, - getAprInfo({ apr }) { - return percent(new BigNumber(apr).toFixed(4)); - } } export default getters diff --git a/src/store/data/mutations.ts b/src/store/data/mutations.ts index 52da13e5..633ab64f 100644 --- a/src/store/data/mutations.ts +++ b/src/store/data/mutations.ts @@ -1,4 +1,5 @@ -import { Balance, BlockReduced, Delegation, GovernanceOverview, Proposal, Reward, SupplyResponse, UnbondingDelegation, Validator } from 'src/models'; +import { Coin } from '@cosmjs/stargate'; +import { Balance, BlockReduced, Delegation, GovernanceOverview, Pool, Proposal, Reward, SupplyResponse, UnbondingDelegation, Validator } from 'src/models'; import { MutationTree } from 'vuex'; import { DataStateInterface } from './state'; @@ -81,9 +82,25 @@ const mutation: MutationTree = { setApr(state, apr: string) { state.apr = apr; }, + setPool(state, pool: Pool) { + state.pool = pool; + }, + setInflation(state, inflation: string) { + state.inflation = inflation; + }, + setSupply(state, supply: Coin) { + state.supply = supply; + }, + setCommunityPool(state, communityPool: Coin[]) { + state.communityPool = communityPool; + }, resetSessionData(state) { state.supplyInfo = null; - state.apr = '0'; + state.inflation = null; + state.supply = null; + state.communityPool = []; + state.pool = null; + state.apr = null; state.balances = []; state.rewards = []; state.delegations = [] diff --git a/src/store/data/state.ts b/src/store/data/state.ts index cb115e00..e9279326 100644 --- a/src/store/data/state.ts +++ b/src/store/data/state.ts @@ -1,4 +1,5 @@ -import { Balance, BlockReduced, Delegation, GovernanceOverview, Proposal, Reward, SupplyResponse, UnbondingDelegation, Validator } from 'src/models'; +import { Coin } from '@cosmjs/stargate'; +import { Balance, BlockReduced, Delegation, GovernanceOverview, Pool, Proposal, Reward, SupplyResponse, UnbondingDelegation, Validator } from 'src/models'; export interface DataStateInterface { block: BlockReduced | undefined; @@ -26,8 +27,12 @@ export interface DataStateInterface { loadingSignTransaction: boolean; loading: boolean; loadingDataDetails: boolean; - apr: string; + supply: Coin | null; + communityPool: Coin[]; + apr: string | null; loadingApr: boolean; + inflation: string | null; + pool: Pool | null; } function state (): DataStateInterface { @@ -57,8 +62,12 @@ function state (): DataStateInterface { loadingDataDetails: false, supplyInfo: null, loadingSupplyInfo: false, - apr: '0', - loadingApr: false + supply: null, + communityPool: [], + apr: null, + loadingApr: false, + inflation: null, + pool: null } }