get and display token allocation values
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
createTheme,
|
||||
NymPaletteVariant,
|
||||
} from '@mui/material/styles'
|
||||
import { number } from 'yup'
|
||||
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
// Nym palette type definitions
|
||||
|
||||
@@ -55,6 +55,6 @@ export type TMixnodeBondDetails = {
|
||||
}
|
||||
|
||||
export type TCurrency = {
|
||||
minor: 'unym' | 'unymt'
|
||||
major: 'nym' | 'nymt'
|
||||
minor: 'UNYM' | 'UNYMT'
|
||||
major: 'NYM' | 'NYMT'
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user