Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transferable vs available balance! address #1071 #1078

Merged
merged 9 commits into from
Aug 18, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 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,9 +225,9 @@ 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')}
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';
Comment on lines +21 to +22
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid shadowing restricted global names.

The Proxy type imported here shadows the global Proxy object in JavaScript, which can lead to confusion. Consider renaming this type to avoid potential issues.

- import { Proxy, ProxyItem, TxInfo } from '../../../../util/types';
+ import { Proxy as CustomProxy, ProxyItem, TxInfo } from '../../../../util/types';
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { getValue } from '../../../../popup/account/util';
import { Proxy, ProxyItem, TxInfo } from '../../../../util/types';
import { getValue } from '../../../../popup/account/util';
import { Proxy as CustomProxy, ProxyItem, TxInfo } from '../../../../util/types';
Tools
Biome

[error] 22-22: Do not shadow the global "Proxy" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

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
29 changes: 16 additions & 13 deletions packages/extension-polkagate/src/fullscreen/sendFund/InputPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { decodeAddress, encodeAddress } from '@polkadot/util-crypto';

import { AmountWithOptions, FullscreenChain, Infotip2, InputAccount, ShowBalance, TwoButtons, Warning } from '../../components';
import { useTranslation } from '../../components/translate';
import { getValue } from '../../popup/account/util';
import { useInfo, useTeleport } from '../../hooks';
import { ASSET_HUBS } from '../../util/constants';
import { amountToHuman, amountToMachine } from '../../util/utils';
Expand Down Expand Up @@ -112,22 +113,24 @@ export default function InputPage({ address, assetId, balances, inputs, setInput
? balances?.ED
: api && api.consts['balances']['existentialDeposit'] as unknown as BN;

const transferableBalance = useMemo(() => getValue('transferable', balances), [balances]);

const amountAsBN = useMemo(
() =>
balances?.decimal ? amountToMachine(amount, balances.decimal) : undefined
,
[amount, balances]);

const warningMessage = useMemo(() => {
if (transferType !== 'All' && amountAsBN && balances?.decimal && ED) {
if (transferType !== 'All' && amountAsBN && balances?.decimal && ED && transferableBalance) {
const totalBalance = balances.freeBalance.add(balances.reservedBalance);
const toTransferBalance = assetId
? amountAsBN
: amountAsBN.add(estimatedFee || BN_ZERO).add(estimatedCrossChainFee || BN_ZERO);

const remainingBalanceAfterTransfer = totalBalance.sub(toTransferBalance);

if (balances.availableBalance.isZero() || balances.availableBalance.lt(toTransferBalance)) {
if (transferableBalance.isZero() || transferableBalance.lt(toTransferBalance)) {
return t('There is no sufficient transferable balance!');
}

Expand All @@ -137,7 +140,7 @@ export default function InputPage({ address, assetId, balances, inputs, setInput
}

return undefined;
}, [ED, amountAsBN, assetId, balances, estimatedCrossChainFee, estimatedFee, t, transferType]);
}, [ED, amountAsBN, assetId, balances, transferableBalance, estimatedCrossChainFee, estimatedFee, t, transferType]);

const destinationGenesisHashes = useMemo((): DropdownOption[] => {
const currentChainOption = chain ? [{ text: chain.name, value: chain.genesisHash as string }] : [];
Expand Down Expand Up @@ -197,11 +200,11 @@ export default function InputPage({ address, assetId, balances, inputs, setInput
!recipientChainGenesisHash ||
!(recipientAddress && inputs?.recipientAddress) ||
Number(amount) <= 0 ||
amountAsBN?.gt(new BN(balances?.availableBalance || BN_ZERO)) ||
amountAsBN?.gt(new BN(transferableBalance || BN_ZERO)) ||
!inputs?.totalFee ||
!!warningMessage
,
[address, amount, amountAsBN, balances?.availableBalance, inputs?.recipientAddress, inputs?.totalFee, recipientAddress, recipientChainGenesisHash, warningMessage]);
[address, amount, amountAsBN, inputs?.recipientAddress, inputs?.totalFee, recipientAddress, recipientChainGenesisHash, transferableBalance, warningMessage]);

const calculateFee = useCallback((amount: Balance | BN, setFeeCall: React.Dispatch<React.SetStateAction<Balance | undefined>>) => {
/** to set Maximum fee which will be used to estimate and show max transferable amount */
Expand Down Expand Up @@ -297,12 +300,12 @@ export default function InputPage({ address, assetId, balances, inputs, setInput
}, [amountAsBN, estimatedFee, estimatedCrossChainFee, setInputs, call, recipientAddress, isCrossChain, crossChainParams, assetId, formatted, amount, recipientChainName, recipientChainGenesisHash, transferType, onChainCall?.section, balances]);

useEffect(() => {
if (!api || !balances) {
if (!api || !transferableBalance) {
return;
}

calculateFee(balances?.availableBalance, setMaxFee);
}, [api, balances, calculateFee]);
calculateFee(transferableBalance, setMaxFee);
}, [api, calculateFee, transferableBalance]);

useEffect(() => {
if (!api || amountAsBN === undefined || !balances) {
Expand Down Expand Up @@ -336,18 +339,18 @@ export default function InputPage({ address, assetId, balances, inputs, setInput
}, [call, formatted, isCrossChain, crossChainParams]);

const setWholeAmount = useCallback(() => {
if (!api || !balances?.availableBalance || !maxFee || !balances || !ED) {
if (!api || !transferableBalance || !maxFee || !balances || !ED) {
return;
}

setTransferType('All');

const _isAvailableZero = balances.availableBalance.isZero();
const _isAvailableZero = transferableBalance.isZero();

const _maxFee = assetId === undefined ? maxFee : BN_ZERO;

const _canNotTransfer = _isAvailableZero || _maxFee.gte(balances.availableBalance);
const allAmount = _canNotTransfer ? '0' : amountToHuman(balances.availableBalance.sub(_maxFee).toString(), balances.decimal);
const _canNotTransfer = _isAvailableZero || _maxFee.gte(transferableBalance);
const allAmount = _canNotTransfer ? '0' : amountToHuman(transferableBalance.sub(_maxFee).toString(), balances.decimal);

setAmount(allAmount);
}, [api, assetId, balances, ED, maxFee]);
Expand Down Expand Up @@ -406,7 +409,7 @@ export default function InputPage({ address, assetId, balances, inputs, setInput
</Typography>
</Infotip2>
<ShowBalance
balance={balances?.availableBalance}
balance={transferableBalance}
decimal={balances?.decimal}
decimalPoint={4}
token={balances?.token}
Expand Down
5 changes: 4 additions & 1 deletion packages/extension-polkagate/src/hooks/useAssetsBalances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export const BN_MEMBERS = [
'vestedClaimable',
'vestingTotal',
'freeBalance',
'frozenBalance',
'frozenFee',
'frozenMisc',
'reservedBalance',
Expand Down Expand Up @@ -112,7 +113,9 @@ function allHexToBN (balances: object | string | undefined): BalancesDetails | {
Object.keys(parsedBalances).forEach((item) => {
const key = item as keyof BalancesDetails;

_balances[key] = isHexToBn(parsedBalances[key] as unknown as string);
if (parsedBalances[key] !== 'undefined') {
_balances[key] = isHexToBn(parsedBalances[key] as unknown as string);
}
});

return _balances;
Expand Down
37 changes: 22 additions & 15 deletions packages/extension-polkagate/src/hooks/useBalances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,21 +98,26 @@ export default function useBalances(address: string | undefined, refresh?: boole

const ED = api.consts['balances']['existentialDeposit'] as unknown as BN;

formatted && api.derive.balances?.all(formatted).then((b) => {
setNewBalances({
...b,
ED,
assetId: NATIVE_TOKEN_ASSET_ID,
chainName,
date: Date.now(),
decimal,
genesisHash: api.genesisHash.toString(),
token
});
formatted && api.derive.balances?.all(formatted).then((allBalances) => {
//@ts-ignore
api.query['system']['account'](formatted).then(({ data: systemBalance }) => {
const frozenBalance = systemBalance.frozen as BN;

setRefresh && setRefresh(false);
isFetching.fetching[String(formatted)]['balances'] = false;
isFetching.set(isFetching.fetching);
setNewBalances({
ED,
assetId: NATIVE_TOKEN_ASSET_ID,
...allBalances,
chainName,
date: Date.now(),
decimal,
frozenBalance,
genesisHash: api.genesisHash.toString(),
token
});
setRefresh && setRefresh(false);
isFetching.fetching[String(formatted)]['balances'] = false;
isFetching.set(isFetching.fetching);
}).catch(console.error);
}).catch(console.error);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [api, chain?.genesisHash, chainName, formatted, isFetching.fetching[String(formatted)]?.['length'], setRefresh]);
Expand Down Expand Up @@ -196,6 +201,7 @@ export default function useBalances(address: string | undefined, refresh?: boole
assetId: overall.assetId,
availableBalance: overall.availableBalance.toString(),
freeBalance: overall.freeBalance.toString(),
frozenBalance: overall.frozenBalance.toString(),
genesisHash: overall.genesisHash,
lockedBalance: overall.lockedBalance.toString(),
pooledBalance: overall.pooledBalance && overall.pooledBalance.toString(),
Expand Down Expand Up @@ -231,6 +237,7 @@ export default function useBalances(address: string | undefined, refresh?: boole
date: savedBalances[chainName].date,
decimal: savedBalances[chainName].decimal,
freeBalance: new BN(sb['freeBalance']),
frozenBalance: new BN(sb['frozenBalance'] || '0'),
genesisHash: sb['genesisHash'],
lockedBalance: new BN(sb['lockedBalance']),
pooledBalance: new BN(sb['pooledBalance']),
Expand All @@ -239,7 +246,7 @@ export default function useBalances(address: string | undefined, refresh?: boole
vestedBalance: new BN(sb['vestedBalance']),
vestedClaimable: new BN(sb['vestedClaimable']),
votingBalance: new BN(sb['votingBalance'])
} as any;
} as BalancesInfo;

setBalances({
...lastBalances,
Expand Down
15 changes: 9 additions & 6 deletions packages/extension-polkagate/src/popup/account/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ import type { BalancesInfo } from '../../util/types';

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

function isEmptyObject (obj: object): boolean {
return Object.keys(obj).length === 0;
}

export const getValue = (type: string, balances: BalancesInfo | null | undefined): BN | undefined => {
if (!balances) {
if (!balances || isEmptyObject(balances)) {
return;
}

Expand All @@ -29,9 +33,12 @@ export const getValue = (type: string, balances: BalancesInfo | null | undefined
return balances?.soloTotal ?? BN_ZERO;
case ('balance'):
case ('available'):
case ('transferable'):
case ('available balance'):
return balances.availableBalance;
case ('transferable'):
return balances.reservedBalance.gte(balances.frozenBalance || BN_ZERO)
? balances.freeBalance
: balances.freeBalance.sub(balances.frozenBalance.sub(balances.reservedBalance));
case ('reserved'):
return balances.reservedBalance;
case ('others'):
Expand All @@ -41,10 +48,6 @@ export const getValue = (type: string, balances: BalancesInfo | null | undefined
return balances.freeBalance;
case ('reserved balance'):
return balances.reservedBalance;
// case ('frozen misc'):
// return balances.frozenMisc;
// case ('frozen fee'):
// return balances.frozenFee;
case ('locked'):
case ('locked balance'):
return balances.lockedBalance;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { amountToHuman, amountToMachine } from '../../../util/utils';
import ParachainInfo from '../partials/ParachainInfo';
import ShowParachain from '../partials/ShowParachain';
import Review from './Review';
import { getValue } from '../../account/util';

interface Props {
api?: ApiPromise;
Expand Down Expand Up @@ -55,6 +56,7 @@ export default function Contribute({ api, chain, crowdloan, crowdloansId, curren
const [contributionAmount, setContributionAmount] = useState<string>();

const amountAsBN = useMemo(() => amountToMachine(contributionAmount, decimal), [contributionAmount, decimal]);
const transferableBalance = useMemo(() => getValue('transferable', balances), [balances]);

useEffect(() => {
if (!formatted || !tx || !minContribution) {
Expand All @@ -66,38 +68,39 @@ export default function Contribute({ api, chain, crowdloan, crowdloansId, curren
}

const feeDummyParams = ['2000', amountAsBN ?? new BN(minContribution), null];
const maxFeeDummyParams = ['2000', balances?.availableBalance, null];
const maxFeeDummyParams = ['2000', transferableBalance, null];

tx(...feeDummyParams).paymentInfo(formatted).then((i) => setEstimatedFee(i?.partialFee)).catch(console.error);

tx(...maxFeeDummyParams).paymentInfo(formatted).then((i) => setEstimatedMaxFee(i?.partialFee)).catch(console.error);
}, [amountAsBN, api, balances?.availableBalance, contributionAmount, formatted, minContribution, tx]);
}, [amountAsBN, api, transferableBalance, contributionAmount, formatted, minContribution, tx]);

const nextBtnDisabled = useMemo(() => {
if (!contributionAmount || !amountAsBN || !minContribution) {
return true;
}

const isAmountInRange = amountAsBN.gt(balances?.availableBalance?.sub(estimatedMaxFee ?? BN_ZERO) ?? BN_ZERO) || !amountAsBN.gte(new BN(minContribution));
const isAmountInRange = amountAsBN.gt(transferableBalance?.sub(estimatedMaxFee ?? BN_ZERO) ?? BN_ZERO) || !amountAsBN.gte(new BN(minContribution));

return (!(contributionAmount !== '0' && !isAmountInRange));
}, [amountAsBN, balances?.availableBalance, contributionAmount, estimatedMaxFee, minContribution]);
}, [amountAsBN, transferableBalance, contributionAmount, estimatedMaxFee, minContribution]);

const onMinAmount = useCallback(() => {
minContribution && decimal && setContributionAmount(amountToHuman(minContribution, decimal));
}, [decimal, minContribution]);

const onMaxAmount = useCallback(() => {
if (!api || !balances?.availableBalance || !estimatedMaxFee || !decimal) {
if (!api || !transferableBalance || !estimatedMaxFee || !decimal) {
return;
}

const ED = api.consts['balances']['existentialDeposit'] as unknown as BN;
const max = new BN(balances.availableBalance.toString()).sub(ED.muln(2)).sub(new BN(estimatedMaxFee));
const max = new BN(transferableBalance.toString()).sub(ED.muln(2)).sub(new BN(estimatedMaxFee));

const maxToHuman = amountToHuman(max.toString(), decimal);

maxToHuman && setContributionAmount(maxToHuman);
}, [api, balances?.availableBalance, decimal, estimatedMaxFee]);
}, [api, transferableBalance, decimal, estimatedMaxFee]);

const backToActives = useCallback(() => {
setShowContribute(false);
Expand Down Expand Up @@ -148,7 +151,7 @@ export default function Contribute({ api, chain, crowdloan, crowdloansId, curren
<Asset
address={String(formatted)}
api={api}
balance={balances?.availableBalance}
balance={transferableBalance}
balanceLabel={t<string>('Available balance')}
fee={estimatedFee}
style={{ m: '15px auto 0', width: '92%' }}
Expand Down
Loading
Loading