get and display token allocation values

This commit is contained in:
fmtabbara
2022-02-09 11:27:09 +00:00
parent 2612cca708
commit a72264c685
9 changed files with 238 additions and 160 deletions
+2 -2
View File
@@ -6,8 +6,8 @@ use crate::ValidatorDetails;
pub(crate) const BECH32_PREFIX: &str = "nymt";
pub const DENOM: &str = "unymt";
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt14hj2tavq8fpesdwxxcu44rty3hh90vhuysqrsr";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt1fp8wa64e2aukznmrs8n9v743uurh5usnvapw09";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt1xwqrvxj8efs93a6l7rzu43tx7e53924hrpfatk";
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
"nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "nymt1dn52nx8wv9wkqmrvj6tcmdzh4es6jt8tr7f6j9";
+53 -3
View File
@@ -1,18 +1,26 @@
import { useCallback, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { invoke } from '@tauri-apps/api'
import { Balance } from '../types'
import { Balance, Coin } from '../types'
import { getVestingCoins, getVestedCoins, getLockedCoins, minorToMajor, getSpendableCoins } from '../requests'
type TTokenAllocation = {
[key in 'vesting' | 'vested' | 'locked' | 'spendable']: Coin['amount']
}
export type TUseuserBalance = {
error?: string
balance?: Balance
tokenAllocation?: TTokenAllocation
isLoading: boolean
fetchBalance: () => void
clearBalance: () => void
fetchTokenAllocation: (address: string) => Promise<void>
}
export const useGetBalance = (): TUseuserBalance => {
export const useGetBalance = (address?: string): TUseuserBalance => {
const [balance, setBalance] = useState<Balance>()
const [error, setError] = useState<string>()
const [tokenAllocation, setTokenAllocation] = useState<TTokenAllocation>()
const [isLoading, setIsLoading] = useState(false)
const fetchBalance = useCallback(async () => {
@@ -28,13 +36,55 @@ export const useGetBalance = (): TUseuserBalance => {
}, 1000)
}, [])
const fetchTokenAllocation = async (address: string) => {
console.log(address)
try {
const [vestingCoins, vestedCoins, lockedCoins, spendableCoins] = await Promise.all([
getVestingCoins(address),
getVestedCoins(address),
getLockedCoins(address),
getSpendableCoins(address),
])
const [vestingCoinsMajor, vestedCoinsMajor, lockedCoinsMajor, spendableCoinsMajor] = await Promise.all([
minorToMajor(vestingCoins.amount),
minorToMajor(vestedCoins.amount),
minorToMajor(lockedCoins.amount),
minorToMajor(spendableCoins.amount),
])
setTokenAllocation({
vesting: vestingCoinsMajor.amount,
vested: vestedCoinsMajor.amount,
locked: lockedCoinsMajor.amount,
spendable: spendableCoinsMajor.amount,
})
} catch (e) {
console.log(e)
clearTokenAllocation()
}
}
const clearBalance = () => setBalance(undefined)
const clearTokenAllocation = () => setTokenAllocation(undefined)
useEffect(() => {
if (address) {
fetchBalance()
fetchTokenAllocation(address)
} else {
clearBalance()
clearTokenAllocation()
}
}, [address])
return {
error,
isLoading,
balance,
tokenAllocation,
fetchBalance,
fetchTokenAllocation,
clearBalance,
}
}
+48
View File
@@ -0,0 +1,48 @@
import React, { useContext, useEffect } from 'react'
import { Alert, Button, Grid, Link, Typography } from '@mui/material'
import { AccountBalanceWalletOutlined, OpenInNew } from '@mui/icons-material'
import { NymCard } from '../../components'
import { ClientContext, urls } from '../../context/main'
export const BalanceCard = () => {
const { userBalance, clientDetails, network } = useContext(ClientContext)
useEffect(() => {
userBalance.fetchBalance()
}, [])
return (
<NymCard title="Balance" data-testid="check-balance" Icon={AccountBalanceWalletOutlined}>
<Grid container direction="column" spacing={2}>
<Grid item>
{userBalance.error && (
<Alert severity="error" data-testid="error-refresh" sx={{ p: 2 }}>
{userBalance.error}
</Alert>
)}
{!userBalance.error && (
<Typography
data-testid="refresh-success"
sx={{ p: 2, color: 'nym.background.dark' }}
variant="h5"
fontWeight="700"
>
{userBalance.balance?.printable_balance}
</Typography>
)}
</Grid>
{network && (
<Grid item>
<Link
sx={{ pl: 1 }}
href={`${urls(network).blockExplorer}/account/${clientDetails?.client_address}`}
target="_blank"
>
<Button endIcon={<OpenInNew />}>Last transactions</Button>
</Link>
</Grid>
)}
</Grid>
</NymCard>
)
}
+9 -36
View File
@@ -1,13 +1,12 @@
import React, { useContext, useEffect } from 'react'
import { Alert, Button, Grid, Link, Typography } from '@mui/material'
import { AccountBalanceWalletOutlined, OpenInNew } from '@mui/icons-material'
import { NymCard } from '../../components'
import { Box } from '@mui/material'
import { BalanceCard } from './balance'
import { VestingCard } from './vesting'
import { ClientContext, urls } from '../../context/main'
import { Layout } from '../../layouts'
import { ClientContext, urls } from '../../context/main'
export const Balance = () => {
const { userBalance, clientDetails, network } = useContext(ClientContext)
const { userBalance } = useContext(ClientContext)
useEffect(() => {
userBalance.fetchBalance()
@@ -15,36 +14,10 @@ export const Balance = () => {
return (
<Layout>
<NymCard title="Balance" data-testid="check-balance" Icon={AccountBalanceWalletOutlined}>
<Grid container direction="column" spacing={2}>
<Grid item>
{userBalance.error && (
<Alert severity="error" data-testid="error-refresh" sx={{ p: 2 }}>
{userBalance.error}
</Alert>
)}
{!userBalance.error && (
<Typography
data-testid="refresh-success"
sx={{ p: 2, color: 'nym.background.dark' }}
variant="h5"
fontWeight="700"
>
{userBalance.balance?.printable_balance}
</Typography>
)}
</Grid>
<Grid item>
<Link
sx={{ pl: 1 }}
href={`${urls(network).blockExplorer}/account/${clientDetails?.client_address}`}
target="_blank"
>
<Button endIcon={<OpenInNew />}>Last transactions</Button>
</Link>
</Grid>
</Grid>
</NymCard>
<Box display="flex" flexDirection="column" gap={2}>
<BalanceCard />
<VestingCard />
</Box>
</Layout>
)
}
+110
View File
@@ -0,0 +1,110 @@
import React, { useEffect, useContext, useState } from 'react'
import {
Alert,
Grid,
LinearProgress,
Table,
TableCell,
TableContainer,
TableHead,
TableRow,
Typography,
Box,
} from '@mui/material'
import { InfoOutlined } from '@mui/icons-material'
import { NymCard } from '../../components'
import { ClientContext } from '../../context/main'
export const VestingCard = () => {
const { userBalance, currency } = useContext(ClientContext)
return (
<NymCard title="Unvested tokens" data-testid="check-unvested-tokens" Icon={InfoOutlined}>
<Grid container direction="column" spacing={2}>
<Grid item>
{userBalance.error && (
<Alert severity="error" data-testid="error-refresh" sx={{ p: 2 }}>
{userBalance.error}
</Alert>
)}
{!userBalance.error && (
<>
<Typography variant="subtitle2" sx={{ color: 'grey.500', ml: 2, mb: 1 }}>
Amount of unvested tokens
</Typography>
<Typography
data-testid="refresh-success"
sx={{ ml: 2, color: 'nym.background.dark' }}
variant="h5"
fontWeight="700"
>
{userBalance.tokenAllocation?.vested || 'n/a'} {currency?.major}
</Typography>
</>
)}
</Grid>
<Grid item>
<VestingTable />
</Grid>
</Grid>
</NymCard>
)
}
const columnsHeaders = ['Unvested', 'Period', 'Amount', 'Vested']
const VestingTable = () => {
const { userBalance, currency } = useContext(ClientContext)
const [vestedPercentage, setVestedPercentage] = useState(0)
const calculatPercentage = () => {
const { tokenAllocation } = userBalance
if (tokenAllocation?.vesting && tokenAllocation.vested) {
const percentage = Math.round((+tokenAllocation.vesting / +tokenAllocation.vested) * 100)
setVestedPercentage(percentage)
} else {
setVestedPercentage(0)
}
}
useEffect(() => {
calculatPercentage()
}, [userBalance.tokenAllocation, calculatPercentage])
return (
<TableContainer>
<Table>
<TableHead>
<TableRow>
{columnsHeaders.map((header) => (
<TableCell key={header} sx={{ color: 'grey.500' }}>
{header}
</TableCell>
))}
</TableRow>
<TableRow>
<TableCell sx={{ borderBottom: 'none' }}>
{userBalance.tokenAllocation?.vested || 'n/a'} {currency?.major}
</TableCell>
<TableCell sx={{ borderBottom: 'none' }}></TableCell>
<TableCell sx={{ borderBottom: 'none' }}>
<Box display="flex" alignItems="center" gap={1}>
<Typography
variant="caption"
sx={{ color: 'nym.fee', fontWeight: 600 }}
>{`${vestedPercentage}%`}</Typography>
<LinearProgress
sx={{ flexBasis: '99%', color: 'nym.fee' }}
variant="determinate"
value={vestedPercentage}
color="inherit"
/>
</Box>
</TableCell>
<TableCell sx={{ borderBottom: 'none' }}>
{userBalance.tokenAllocation?.vesting || 'n/a'} {currency?.major}
</TableCell>
</TableRow>
</TableHead>
</Table>
</TableContainer>
)
}
+7 -109
View File
@@ -1,109 +1,7 @@
import { invoke } from '@tauri-apps/api'
import {
Account,
Balance,
Coin,
DelegationResult,
EnumNodeType,
Gateway,
InclusionProbabilityResponse,
MixNode,
MixnodeStatusResponse,
Network,
Operation,
RewardEstimationResponse,
StakeSaturationResponse,
TauriContractStateParams,
TauriTxResult,
TCreateAccount,
TMixnodeBondDetails,
TPagedDelegations,
} from '../types'
export const createAccount = async (): Promise<TCreateAccount> => await invoke('create_new_account')
export const signInWithMnemonic = async (mnemonic: string): Promise<Account> =>
await invoke('connect_with_mnemonic', { mnemonic })
export const signOut = async () => await invoke('logout')
export const minorToMajor = async (amount: string): Promise<Coin> => await invoke('minor_to_major', { amount })
export const majorToMinor = async (amount: string): Promise<Coin> => await invoke('major_to_minor', { amount })
// NOTE: this uses OUTDATED defaults that might have no resemblance with the reality
// as for the actual transaction, the gas cost is being simulated beforehand
export const getGasFee = async (operation: Operation): Promise<Coin> =>
await invoke('outdated_get_approximate_fee', { operation })
export const delegate = async ({
type,
identity,
amount,
}: {
type: EnumNodeType
identity: string
amount: Coin
}): Promise<DelegationResult> => await invoke(`delegate_to_${type}`, { identity, amount })
export const undelegate = async ({
type,
identity,
}: {
type: EnumNodeType
identity: string
}): Promise<DelegationResult> => await invoke(`undelegate_from_${type}`, { identity })
export const send = async (args: { amount: Coin; address: string; memo: string }): Promise<TauriTxResult> =>
await invoke('send', args)
export const checkMixnodeOwnership = async (): Promise<boolean> => await invoke('owns_mixnode')
export const checkGatewayOwnership = async (): Promise<boolean> => await invoke('owns_gateway')
export const bond = async ({
type,
data,
pledge,
ownerSignature,
}: {
type: EnumNodeType
data: MixNode | Gateway
pledge: Coin
ownerSignature: string
}): Promise<any> => await invoke(`bond_${type}`, { [type]: data, ownerSignature, pledge })
export const unbond = async (type: EnumNodeType) => await invoke(`unbond_${type}`)
export const userBalance = async (): Promise<Balance> => await invoke('get_balance')
export const getContractParams = async (): Promise<TauriContractStateParams> => await invoke('get_contract_settings')
export const setContractParams = async (params: TauriContractStateParams): Promise<TauriContractStateParams> =>
await invoke('update_contract_settings', { params })
export const getReverseMixDelegations = async (): Promise<TPagedDelegations> =>
await invoke('get_reverse_mix_delegations_paged')
export const getReverseGatewayDelegations = async (): Promise<TPagedDelegations> =>
await invoke('get_reverse_gateway_delegations_paged')
export const getMixnodeBondDetails = async (): Promise<TMixnodeBondDetails | null> =>
await invoke('mixnode_bond_details')
export const getMixnodeStakeSaturation = async (identity: string): Promise<StakeSaturationResponse> =>
await invoke('mixnode_stake_saturation', { identity })
export const getMixnodeRewardEstimation = async (identity: string): Promise<RewardEstimationResponse> =>
await invoke('mixnode_reward_estimation', { identity })
export const getMixnodeStatus = async (identity: string): Promise<MixnodeStatusResponse> =>
await invoke('mixnode_status', { identity })
export const updateMixnode = async ({ profitMarginPercent }: { profitMarginPercent: number }) =>
await invoke('update_mixnode', { profitMarginPercent })
export const getInclusionProbability = async (identity: string): Promise<InclusionProbabilityResponse> =>
await invoke('mixnode_inclusion_probability', { identity })
export const selectNetwork = async (network: Network): Promise<Account> => await invoke('switch_network', { network })
export * from './account'
export * from './actions'
export * from './coin'
export * from './contract'
export * from './network'
export * from './queries'
export * from './vesting'
-1
View File
@@ -7,7 +7,6 @@ import {
createTheme,
NymPaletteVariant,
} from '@mui/material/styles'
import { number } from 'yup'
//-----------------------------------------------------------------------------------------------
// Nym palette type definitions
+2 -2
View File
@@ -55,6 +55,6 @@ export type TMixnodeBondDetails = {
}
export type TCurrency = {
minor: 'unym' | 'unymt'
major: 'nym' | 'nymt'
minor: 'UNYM' | 'UNYMT'
major: 'NYM' | 'NYMT'
}
+7 -7
View File
@@ -105,20 +105,20 @@ export const randomNumberBetween = (min: number, max: number) => {
return Math.floor(Math.random() * (max - min + 1) + min)
}
export const currencyMap = (network: Network) => {
export const currencyMap = (network?: Network) => {
let currency = {
minor: 'unym',
major: 'nym',
minor: 'UNYM',
major: 'NYM',
} as TCurrency
switch (network) {
case 'MAINNET':
currency.minor = 'unym'
currency.major = 'nym'
currency.minor = 'UNYM'
currency.major = 'NYM'
break
case 'SANDBOX':
currency.minor = 'unymt'
currency.major = 'nymt'
currency.minor = 'UNYMT'
currency.major = 'NYMT'
break
}