diff --git a/common/network-defaults/src/qa.rs b/common/network-defaults/src/qa.rs index 206971b6a1..1d0fb36bea 100644 --- a/common/network-defaults/src/qa.rs +++ b/common/network-defaults/src/qa.rs @@ -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"; diff --git a/nym-wallet/src/hooks/useGetBalance.tsx b/nym-wallet/src/hooks/useGetBalance.tsx index 6c322cf7d2..0aae41a583 100644 --- a/nym-wallet/src/hooks/useGetBalance.tsx +++ b/nym-wallet/src/hooks/useGetBalance.tsx @@ -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 } -export const useGetBalance = (): TUseuserBalance => { +export const useGetBalance = (address?: string): TUseuserBalance => { const [balance, setBalance] = useState() const [error, setError] = useState() + const [tokenAllocation, setTokenAllocation] = useState() 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, } } diff --git a/nym-wallet/src/pages/balance/balance.tsx b/nym-wallet/src/pages/balance/balance.tsx new file mode 100644 index 0000000000..8010a27fd4 --- /dev/null +++ b/nym-wallet/src/pages/balance/balance.tsx @@ -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 ( + + + + {userBalance.error && ( + + {userBalance.error} + + )} + {!userBalance.error && ( + + {userBalance.balance?.printable_balance} + + )} + + {network && ( + + + + + + )} + + + ) +} diff --git a/nym-wallet/src/pages/balance/index.tsx b/nym-wallet/src/pages/balance/index.tsx index 8c1bde2a21..957765a0e1 100644 --- a/nym-wallet/src/pages/balance/index.tsx +++ b/nym-wallet/src/pages/balance/index.tsx @@ -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 ( - - - - {userBalance.error && ( - - {userBalance.error} - - )} - {!userBalance.error && ( - - {userBalance.balance?.printable_balance} - - )} - - - - - - - - + + + + ) } diff --git a/nym-wallet/src/pages/balance/vesting.tsx b/nym-wallet/src/pages/balance/vesting.tsx new file mode 100644 index 0000000000..4f5979a031 --- /dev/null +++ b/nym-wallet/src/pages/balance/vesting.tsx @@ -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 ( + + + + {userBalance.error && ( + + {userBalance.error} + + )} + {!userBalance.error && ( + <> + + Amount of unvested tokens + + + {userBalance.tokenAllocation?.vested || 'n/a'} {currency?.major} + + + )} + + + + + + + ) +} + +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 ( + + + + + {columnsHeaders.map((header) => ( + + {header} + + ))} + + + + {userBalance.tokenAllocation?.vested || 'n/a'} {currency?.major} + + + + + {`${vestedPercentage}%`} + + + + + {userBalance.tokenAllocation?.vesting || 'n/a'} {currency?.major} + + + +
+
+ ) +} diff --git a/nym-wallet/src/requests/index.ts b/nym-wallet/src/requests/index.ts index 37a62854dd..05f4b051a0 100644 --- a/nym-wallet/src/requests/index.ts +++ b/nym-wallet/src/requests/index.ts @@ -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 => await invoke('create_new_account') - -export const signInWithMnemonic = async (mnemonic: string): Promise => - await invoke('connect_with_mnemonic', { mnemonic }) - -export const signOut = async () => await invoke('logout') - -export const minorToMajor = async (amount: string): Promise => await invoke('minor_to_major', { amount }) - -export const majorToMinor = async (amount: string): Promise => 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 => - await invoke('outdated_get_approximate_fee', { operation }) - -export const delegate = async ({ - type, - identity, - amount, -}: { - type: EnumNodeType - identity: string - amount: Coin -}): Promise => await invoke(`delegate_to_${type}`, { identity, amount }) - -export const undelegate = async ({ - type, - identity, -}: { - type: EnumNodeType - identity: string -}): Promise => await invoke(`undelegate_from_${type}`, { identity }) - -export const send = async (args: { amount: Coin; address: string; memo: string }): Promise => - await invoke('send', args) - -export const checkMixnodeOwnership = async (): Promise => await invoke('owns_mixnode') - -export const checkGatewayOwnership = async (): Promise => await invoke('owns_gateway') - -export const bond = async ({ - type, - data, - pledge, - ownerSignature, -}: { - type: EnumNodeType - data: MixNode | Gateway - pledge: Coin - ownerSignature: string -}): Promise => await invoke(`bond_${type}`, { [type]: data, ownerSignature, pledge }) - -export const unbond = async (type: EnumNodeType) => await invoke(`unbond_${type}`) - -export const userBalance = async (): Promise => await invoke('get_balance') - -export const getContractParams = async (): Promise => await invoke('get_contract_settings') - -export const setContractParams = async (params: TauriContractStateParams): Promise => - await invoke('update_contract_settings', { params }) - -export const getReverseMixDelegations = async (): Promise => - await invoke('get_reverse_mix_delegations_paged') - -export const getReverseGatewayDelegations = async (): Promise => - await invoke('get_reverse_gateway_delegations_paged') - -export const getMixnodeBondDetails = async (): Promise => - await invoke('mixnode_bond_details') - -export const getMixnodeStakeSaturation = async (identity: string): Promise => - await invoke('mixnode_stake_saturation', { identity }) - -export const getMixnodeRewardEstimation = async (identity: string): Promise => - await invoke('mixnode_reward_estimation', { identity }) - -export const getMixnodeStatus = async (identity: string): Promise => - await invoke('mixnode_status', { identity }) - -export const updateMixnode = async ({ profitMarginPercent }: { profitMarginPercent: number }) => - await invoke('update_mixnode', { profitMarginPercent }) - -export const getInclusionProbability = async (identity: string): Promise => - await invoke('mixnode_inclusion_probability', { identity }) - -export const selectNetwork = async (network: Network): Promise => 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' diff --git a/nym-wallet/src/theme/theme.tsx b/nym-wallet/src/theme/theme.tsx index 02e4f92b9f..b6b0d70024 100644 --- a/nym-wallet/src/theme/theme.tsx +++ b/nym-wallet/src/theme/theme.tsx @@ -7,7 +7,6 @@ import { createTheme, NymPaletteVariant, } from '@mui/material/styles' -import { number } from 'yup' //----------------------------------------------------------------------------------------------- // Nym palette type definitions diff --git a/nym-wallet/src/types/global.ts b/nym-wallet/src/types/global.ts index d5c5af7535..4fddaecf7a 100644 --- a/nym-wallet/src/types/global.ts +++ b/nym-wallet/src/types/global.ts @@ -55,6 +55,6 @@ export type TMixnodeBondDetails = { } export type TCurrency = { - minor: 'unym' | 'unymt' - major: 'nym' | 'nymt' + minor: 'UNYM' | 'UNYMT' + major: 'NYM' | 'NYMT' } diff --git a/nym-wallet/src/utils/index.ts b/nym-wallet/src/utils/index.ts index 16b35b6860..10d5d0d2f4 100644 --- a/nym-wallet/src/utils/index.ts +++ b/nym-wallet/src/utils/index.ts @@ -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 }