Skip to content

Commit

Permalink
Merge pull request #1078 from PolkaGate/closes#1071
Browse files Browse the repository at this point in the history
Transferable vs available balance! address #1071
  • Loading branch information
Nick-1979 authored Aug 18, 2024
2 parents 579c8fd + 13ac15d commit d73d625
Show file tree
Hide file tree
Showing 23 changed files with 298 additions and 363 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,38 @@

// @ts-ignore
import type { PalletBalancesBalanceLock } from '@polkadot/types/lookup';
import type { BN } from '@polkadot/util';
import type { Lock } from '../../../hooks/useAccountLocks';
import type { UnlockInformationType } from '..';

import { faUnlockAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Divider, Grid, IconButton, Typography, useTheme } from '@mui/material';
import React, { useCallback, useEffect, useMemo, useState } from 'react';

import { BN_MAX_INTEGER, BN_ZERO } from '@polkadot/util';
import { BN_MAX_INTEGER } from '@polkadot/util';

import { FormatPrice, ShowBalance } from '../../../components';
import { useAccountLocks, useCurrentBlockNumber, useHasDelegated, useInfo, useTranslation } from '../../../hooks';
import blockToDate from '../../../popup/crowdloans/partials/blockToDate';
import { useAccountLocks, useCurrentBlockNumber, useHasDelegated, useInfo, useTimeToUnlock, useTranslation } from '../../../hooks';
import { TIME_TO_SHAKE_ICON } from '../../../util/constants';
import { popupNumbers } from '..';

interface DisplayBalanceProps {
address: string | undefined;
title: string;
token: string | undefined;
decimal: number | undefined;
price: number | undefined;
refreshNeeded?: boolean;
setDisplayPopup: React.Dispatch<React.SetStateAction<number | undefined>>;
setUnlockInformation: React.Dispatch<React.SetStateAction<UnlockInformationType | undefined>>;
}

export default function LockedBalanceDisplay ({ address, decimal, price, refreshNeeded, setDisplayPopup, setUnlockInformation, title, token }: DisplayBalanceProps): React.ReactElement {
export default function LockedInReferendaFS ({ address, price, refreshNeeded, setDisplayPopup, setUnlockInformation }: DisplayBalanceProps): React.ReactElement {
const { t } = useTranslation();
const theme = useTheme();

const { api, chain, formatted } = useInfo(address);

const { api, decimal, token } = useInfo(address);
const delegatedBalance = useHasDelegated(address, refreshNeeded);
const referendaLocks = useAccountLocks(address, 'referenda', 'convictionVoting', false, refreshNeeded);
const currentBlock = useCurrentBlockNumber(address);
const { timeToUnlock, totalLocked, unlockableAmount } = useTimeToUnlock(address, referendaLocks, refreshNeeded);

const [unlockableAmount, setUnlockableAmount] = useState<BN>();
const [lockedInRef, setLockedInReferenda] = useState<BN>();
const [totalLocked, setTotalLocked] = useState<BN | null>();
const [timeToUnlock, setTimeToUnlock] = useState<string | null>();
const [miscRefLock, setMiscRefLock] = useState<BN>();
const [shake, setShake] = useState<boolean>();

const classToUnlock = currentBlock ? referendaLocks?.filter((ref) => ref.endBlock.ltn(currentBlock) && ref.classId.lt(BN_MAX_INTEGER)) : undefined;
Expand All @@ -64,108 +53,6 @@ export default function LockedBalanceDisplay ({ address, decimal, price, refresh
}
}, [unlockableAmount]);

const biggestOngoingLock = useCallback((sortedLocks: Lock[]) => {
const maybeFound = sortedLocks.find(({ endBlock }) => endBlock.eq(BN_MAX_INTEGER));

return maybeFound ? maybeFound.total : BN_ZERO;
}, []);

useEffect(() => {
if (refreshNeeded) {
setLockedInReferenda(undefined); // TODO: needs double check
setUnlockableAmount(undefined);
setTotalLocked(undefined);
setMiscRefLock(undefined);
}
}, [refreshNeeded]);

useEffect(() => {
if (referendaLocks === null) {
setLockedInReferenda(BN_ZERO);
setTimeToUnlock(null);

return;
}

if (!referendaLocks?.length || !currentBlock) {
setLockedInReferenda(undefined);
setTimeToUnlock(undefined);

return;
}

referendaLocks.sort((a, b) => { // sort locks based on total and endblock desc
if (a.total.gt(b.total)) {
return -1;
}

if (a.total.lt(b.total)) {
return 1;
}

if (a.endBlock.gt(b.endBlock)) {
return -1;
}

if (a.endBlock.lt(b.endBlock)) {
return 1;
}

return 0;
});
const biggestVote = referendaLocks[0].total;

setLockedInReferenda(biggestVote);
const indexOfBiggestNotLockable = referendaLocks.findIndex(({ endBlock }) => endBlock.gtn(currentBlock));

if (indexOfBiggestNotLockable === -1) { // all is unlockable
return setUnlockableAmount(biggestVote);
}

if (biggestVote.eq(biggestOngoingLock(referendaLocks))) { // The biggest vote is already ongoing
setUnlockableAmount(BN_ZERO);

return setTimeToUnlock(t('Locked in ongoing referenda'));
}

if (indexOfBiggestNotLockable === 0 || biggestVote.eq(referendaLocks[indexOfBiggestNotLockable].total)) { // nothing is unlockable
const dateString = blockToDate(Number(referendaLocks[indexOfBiggestNotLockable].endBlock), currentBlock);

setUnlockableAmount(BN_ZERO);

return setTimeToUnlock(t('Unlockable on {{dateString}}', { replace: { dateString } }));
}

const amountStillLocked = referendaLocks[indexOfBiggestNotLockable].total;

setUnlockableAmount(biggestVote.sub(amountStillLocked));
}, [api, biggestOngoingLock, currentBlock, referendaLocks, t]);

useEffect(() => {
if (!api?.query?.['balances'] || !formatted || api?.genesisHash?.toString() !== chain?.genesisHash) {
return setMiscRefLock(undefined);
}

// eslint-disable-next-line no-void
void api.query['balances']['locks'](formatted).then((_locks) => {
const locks = _locks as unknown as PalletBalancesBalanceLock[];

if (locks?.length) {
const foundRefLock = locks.find((l) => l.id.toHuman() === 'pyconvot');

setMiscRefLock(foundRefLock?.amount);
}
});
}, [api, chain?.genesisHash, formatted, refreshNeeded]);

useEffect(() => {
if (!lockedInRef && !delegatedBalance && !miscRefLock) {
return setTotalLocked(undefined);
}

setTotalLocked(miscRefLock || lockedInRef || delegatedBalance);
}, [delegatedBalance, lockedInRef, miscRefLock]);

const onUnlock = useCallback(() => {
if (isDisable) {
return;
Expand All @@ -182,7 +69,7 @@ export default function LockedBalanceDisplay ({ address, decimal, price, refresh
return (
<Grid alignItems='center' container item justifyContent='space-between' sx={{ bgcolor: 'background.paper', borderRadius: '5px', boxShadow: '2px 3px 4px 0px rgba(0, 0, 0, 0.1)', height: hasDescription ? '85px' : '70px', p: '15px 40px' }}>
<Typography fontSize='18px' fontWeight={400}>
{title}
{t('Locked in Referenda')}
</Typography>
<Grid alignItems='center' container item width='fit-content'>
<Grid alignItems='flex-end' container direction='column' item width='fit-content'>
Expand All @@ -193,7 +80,6 @@ export default function LockedBalanceDisplay ({ address, decimal, price, refresh
decimal={decimal}
decimalPoint={2}
token={token}
withCurrency={false}
/>
</Grid>
<Divider orientation='vertical' sx={{ backgroundColor: 'text.primary', height: '35px', mx: '10px', my: 'auto' }} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0
// @ts-nocheck

export { default as AccountInformationForDetails } from './AccountInformationForDetails';
export { default as AccountSetting } from './AccountSetting';
export { default as AssetSelect } from './AssetSelect';
export { default as CommonTasks } from './CommonTasks';
export { default as DisplayBalance } from './DisplayBalance';
export { default as ExternalLinks } from './ExternalLinks';
export { default as LockedBalanceDisplay } from './LockedBalanceDisplay';
export { default as LockedInReferendaFS } from './LockedInReferendaFS';
export { default as TotalChart } from './TotalChart';
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import type { Lock } from '../../hooks/useAccountLocks';
import type { FetchedBalance } from '../../hooks/useAssetsBalances';
import type { BalancesInfo } from '../../util/types';

import { faFileInvoice } from '@fortawesome/free-solid-svg-icons';
import { Grid, useTheme } from '@mui/material';
Expand All @@ -15,6 +16,7 @@ import { BN } from '@polkadot/util';

import { AccountContext, ActionContext, Warning } from '../../components';
import { useAccountAssets, useBalances, useCurrency, useFullscreen, useInfo, usePrices, useTranslation } from '../../hooks';
import { getValue } from '../../popup/account/util';
import ExportAccountModal from '../../popup/export/ExportAccountModal';
import ForgetAccountModal from '../../popup/forgetAccount/ForgetAccountModal';
import HistoryModal from '../../popup/history/modal/HistoryModal';
Expand All @@ -30,7 +32,7 @@ import { Title } from '../sendFund/InputPage';
import { openOrFocusTab } from './components/CommonTasks';
import ReservedDisplayBalance from './components/ReservedDisplayBalance';
import LockedInReferenda from './unlock/Review';
import { AccountInformationForDetails, AccountSetting, AssetSelect, CommonTasks, DisplayBalance, ExternalLinks, LockedBalanceDisplay, TotalChart } from './components';
import { AccountInformationForDetails, AccountSetting, AssetSelect, CommonTasks, DisplayBalance, ExternalLinks, LockedInReferendaFS, TotalChart } from './components';

export enum popupNumbers {
LOCKED_IN_REFERENDA,
Expand Down Expand Up @@ -59,7 +61,7 @@ export default function AccountDetails (): React.ReactElement {
const onAction = useContext(ActionContext);
const accountAssets = useAccountAssets(address);
const pricesInCurrency = usePrices();

const [refreshNeeded, setRefreshNeeded] = useState<boolean>(false);
const [assetIdOnAssetHub, setAssetIdOnAssetHub] = useState<number>();
const [selectedAsset, setSelectedAsset] = useState<FetchedBalance>();
Expand Down Expand Up @@ -97,6 +99,7 @@ export default function AccountDetails (): React.ReactElement {
: { ...(balances || {}), ...(selectedAsset || {}) };
}, [assetId, balances, chainName, selectedAsset]);

const transferableBalance = useMemo(() => getValue('transferable', balancesToShow as BalancesInfo), [balancesToShow]);
const isDualStaking = useMemo(() =>
balancesToShow?.soloTotal && balancesToShow?.pooledBalance && !balancesToShow.soloTotal.isZero() && !balancesToShow.pooledBalance.isZero()
, [balancesToShow?.pooledBalance, balancesToShow?.soloTotal]);
Expand Down Expand Up @@ -222,15 +225,15 @@ export default function AccountDetails (): React.ReactElement {
/>
}
<DisplayBalance
amount={balancesToShow?.availableBalance}
amount={transferableBalance}
decimal={balancesToShow?.decimal}
disabled={!balancesToShow?.availableBalance || balancesToShow?.availableBalance.isZero()}
disabled={!transferableBalance || transferableBalance.isZero()}
onClick={goToSend}
price={currentPrice}
title={t('Transferable')}
token={balancesToShow?.token}
/>
{isOnAssetHub &&
{(isOnAssetHub || (!supportGov && !supportStaking && balancesToShow?.lockedBalance && !balancesToShow.lockedBalance.isZero())) &&
<DisplayBalance
amount={balancesToShow?.lockedBalance}
decimal={balancesToShow?.decimal}
Expand Down Expand Up @@ -259,15 +262,12 @@ export default function AccountDetails (): React.ReactElement {
token={balancesToShow?.token}
/>}
{supportGov &&
<LockedBalanceDisplay
<LockedInReferendaFS
address={address}
decimal={balancesToShow?.decimal}
price={currentPrice}
refreshNeeded={refreshNeeded}
setDisplayPopup={setDisplayPopup}
setUnlockInformation={setUnlockInformation}
title={t('Locked in Referenda')}
token={balancesToShow?.token}
/>
}
<ReservedDisplayBalance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { cryptoWaitReady } from '@polkadot/util-crypto';
import { Identity, ShowBalance, SignArea2, Warning } from '../../../../components';
import { useAccountDisplay, useBalances, useInfo, useProxies, useTranslation } from '../../../../hooks';
import { ThroughProxy } from '../../../../partials';
import { getValue } from '../../../../popup/account/util';
import { Proxy, ProxyItem, TxInfo } from '../../../../util/types';
import { PROXY_TYPE } from '../../../../util/constants';
import type { Proxy, ProxyItem, TxInfo } from '../../../../util/types';
import { DraggableModal } from '../../components/DraggableModal';
Expand All @@ -44,7 +46,7 @@ const STEPS = {
SIGN_QR: 200
};

export default function DecisionDeposit({ address, open, refIndex, setOpen, track }: Props): React.ReactElement {
export default function DecisionDeposit ({ address, open, refIndex, setOpen, track }: Props): React.ReactElement {
const { t } = useTranslation();
const { api, chain, decimal, formatted, token } = useInfo(address);
const theme = useTheme();
Expand All @@ -54,7 +56,7 @@ export default function DecisionDeposit({ address, open, refIndex, setOpen, trac

const proxyItems = useMemo(() =>
proxies?.map((p: Proxy) => ({ proxy: p, status: 'current' })) as ProxyItem[]
, [proxies]);
, [proxies]);

const [step, setStep] = useState<number>(STEPS.REVIEW);
const [txInfo, setTxInfo] = useState<TxInfo | undefined>();
Expand Down Expand Up @@ -123,7 +125,7 @@ export default function DecisionDeposit({ address, open, refIndex, setOpen, trac

const HEIGHT = 550;

const notEnoughBalance = useMemo(() => amount && estimatedFee && balances?.availableBalance?.lt(amount.add(estimatedFee)), [amount, balances, estimatedFee]);
const notEnoughBalance = useMemo(() => amount && estimatedFee && getValue('transferable', balances)?.lt(amount.add(estimatedFee)), [amount, balances, estimatedFee]);

return (
<DraggableModal onClose={handleClose} open={open} width={500}>
Expand Down
Loading

0 comments on commit d73d625

Please sign in to comment.