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

Fix issue with balance validation on request token deep linking #5475

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions src/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,7 @@
"There are no results matching your search term.": "There are no results matching your search term.",
"There are no stakes in queue, please select validators to stake.": "There are no stakes in queue, please select validators to stake.",
"There are no tokens to display for this account at this moment.": "There are no tokens to display for this account at this moment.",
"There are no tokens to send at this moment.": "There are no tokens to send at this moment.",
"There are no transactions for this account.": "There are no transactions for this account.",
"There are no transactions for this block.": "There are no transactions for this block.",
"There are no transactions for this chain.": "There are no transactions for this chain.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const AccountMenuListing = ({ className, onItemClicked }) => {
hasNetworkError,
isLoadingNetwork,
insuffientBalanceMessage: getTokenBalanceErrorMessage({
errorType: 'registerMultiSignature',
hasAvailableTokenBalance,
hasSufficientBalanceForFee,
feeTokenSymbol: feeToken?.symbol,
Expand Down
1 change: 1 addition & 0 deletions src/modules/common/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,6 @@ export const INSUFFICENT_TOKEN_BALANCE_MESSAGE = {
'Token balance is not enough to register a multisignature account.'
),
registerValidator: i18next.t('Token balance is not enough to register a validator profile.'),
sendToken: i18next.t('There are no tokens to send at this moment.'),
stakeValidator: i18next.t('Token balance is not enough to stake a validator.'),
};
3 changes: 2 additions & 1 deletion src/modules/common/utils/getTokenBalanceErrorMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ export function getTokenBalanceErrorMessage({
hasAvailableTokenBalance = true,
hasSufficientBalanceForFee = true,
feeTokenSymbol,
errorType,
t,
}) {
if (!hasAvailableTokenBalance) {
return {
message: INSUFFICENT_TOKEN_BALANCE_MESSAGE.registerMultiSignature,
message: INSUFFICENT_TOKEN_BALANCE_MESSAGE[errorType || 'sendToken'],
};
}

Expand Down
67 changes: 67 additions & 0 deletions src/modules/common/utils/getTokenBalanceErrorMessage.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { INSUFFICENT_TOKEN_BALANCE_MESSAGE } from '../constants';
import { getTokenBalanceErrorMessage } from './getTokenBalanceErrorMessage';

function interpolateFn(templateString, data) {
let result = templateString;
const keys = templateString.match(/{{[A-Za-z0-9_]+}}/g);
keys.forEach((key) => {
const normalizedKey = key.match(/(?<={{)[A-Za-z0-9_]+(?=}})/)?.[0];

if (normalizedKey && data[normalizedKey]) result = result.replace(key, data[normalizedKey]);
});

return result;
}

describe('getTokenBalanceErrorMessage', () => {
const mockTranslationFn = jest.fn((text, data) => interpolateFn(text, data));

it('Should not return an error message', () => {
const result = getTokenBalanceErrorMessage({
t: mockTranslationFn,
hasAvailableTokenBalance: true,
hasSufficientBalanceForFee: true,
feeTokenSymbol: 'LSK',
});

expect(result).toEqual({});
});
it('Should return an insufficient fee error message', () => {
const result = getTokenBalanceErrorMessage({
t: mockTranslationFn,
hasAvailableTokenBalance: true,
hasSufficientBalanceForFee: false,
feeTokenSymbol: 'LSK',
});

expect(result).toEqual({
message: 'There are no LSK tokens to pay for fees.',
});
});
it('Should return an insufficent token balance error message if error type is provided', () => {
const result = getTokenBalanceErrorMessage({
t: mockTranslationFn,
hasAvailableTokenBalance: false,
hasSufficientBalanceForFee: false,
feeTokenSymbol: 'LSK',
errorType: 'registerValidator',
});

expect(result).toEqual({
message: INSUFFICENT_TOKEN_BALANCE_MESSAGE.registerValidator,
});
});

it('Should return an insufficent token balance error message if error type is not provided', () => {
const result = getTokenBalanceErrorMessage({
t: mockTranslationFn,
hasAvailableTokenBalance: false,
hasSufficientBalanceForFee: false,
feeTokenSymbol: 'LSK',
});

expect(result).toEqual({
message: INSUFFICENT_TOKEN_BALANCE_MESSAGE.sendToken,
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ function ValidatorStakeButton({ address, isBanned, currentAddress, isDisabled, h
data={{
validatorAddress: address,
...getTokenBalanceErrorMessage({
errorType: 'stakeValidator',
hasSufficientBalanceForFee,
feeTokenSymbol: feeToken?.symbol,
hasAvailableTokenBalance: hasTokenBalance,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const ValidatorActionButton = ({ address, isValidator }) => {
<DialogLink
data={{
...getTokenBalanceErrorMessage({
errorType: 'registerValidator',
hasSufficientBalanceForFee,
feeTokenSymbol: feeToken?.symbol,
hasAvailableTokenBalance: hasTokenBalances,
Expand Down
31 changes: 31 additions & 0 deletions src/modules/token/fungible/components/SendForm/SendForm.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable max-lines */
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import Piwik from 'src/utils/piwik';
import { useHistory } from 'react-router-dom';
import { MODULE_COMMANDS_NAME_MAP } from '@transaction/configuration/moduleCommand';
import AmountField from '@common/components/amountField';
import TokenAmount from '@token/fungible/components/tokenAmount';
Expand All @@ -16,13 +17,16 @@ import {
} from '@blockchainApplication/manage/hooks';
import MenuSelect, { MenuItem } from '@wallet/components/MenuSelect';
import TxComposer from '@transaction/components/TxComposer';
import { addSearchParamsToUrl } from 'src/utils/searchParams';
import { getTokenBalanceErrorMessage } from 'src/modules/common/utils/getTokenBalanceErrorMessage';
import BookmarkAutoSuggest from './bookmarkAutoSuggest';
import useAmountField from '../../hooks/useAmountField';
import useMessageField from '../../hooks/useMessageField';
import { useTransferableTokens } from '../../hooks';
import useRecipientField from '../../hooks/useRecipientField';
import styles from './form.css';
import MessageField from '../MessageField';
import { useTokenBalances, useValidateFeeBalance } from '../../hooks/queries';

const getInitialData = (formProps, initialValue) => formProps?.params.data || initialValue || '';
const getInitialAmount = (formProps, initialValue, token) =>
Expand Down Expand Up @@ -53,6 +57,7 @@ const getInitialToken = (transactionData, initialTokenId, tokens) => {
// eslint-disable-next-line max-statements
const SendForm = (props) => {
const { prevState, t, bookmarks, nextStep } = props;
const history = useHistory();
const [recipientChain, setRecipientChain] = useState({});
const [token, setToken] = useState({});
const [maxAmount, setMaxAmount] = useState({ value: 0, error: false });
Expand All @@ -75,6 +80,12 @@ const SendForm = (props) => {
)
);
const { applications: managedApps } = useApplicationManagement();
const tokenBalanceQuery = useTokenBalances();
const {
hasSufficientBalanceForFee,
feeToken,
isLoading: isLoadingFeeBalance,
} = useValidateFeeBalance();

const mainChainApplication = useMemo(
() => managedApps.find(({ chainID }) => /0{4}$/.test(chainID)),
Expand Down Expand Up @@ -115,6 +126,26 @@ const SendForm = (props) => {
return areFieldsValid && isTokenValid;
}, [amount, recipient, reference, recipientChain, sendingChain, token]);

useEffect(() => {
if (!isLoadingFeeBalance && !tokenBalanceQuery.isLoading) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move const hasTokenWithBalance calculation here (lines 89-91). Inside this if statement since this is the only part of the component which uses that logic.

const hasTokenWithBalance = tokenBalanceQuery.data?.data?.some(
(tokenBalance) => BigInt(tokenBalance?.availableBalance || 0) > BigInt(0)
);

const tokenBalanceError = getTokenBalanceErrorMessage({
errorType: 'sendToken',
hasSufficientBalanceForFee,
feeTokenSymbol: feeToken?.symbol,
hasAvailableTokenBalance: hasTokenWithBalance,
t,
});

if (Object.values(tokenBalanceError).length) {
addSearchParamsToUrl(history, { modal: 'noTokenBalance', ...tokenBalanceError });
}
}
}, [isLoadingFeeBalance, tokenBalanceQuery.isLoading]);

useEffect(() => {
setToken(getInitialToken(prevState?.transactionData, props.initialValue?.token, tokens));
}, [prevState?.transactionData, props.initialValue?.token, tokens]);
Expand Down
7 changes: 7 additions & 0 deletions src/modules/token/fungible/components/SendForm/form.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
useGetMinimumMessageFee,
useTokenBalances,
useTokenSummary,
useValidateFeeBalance,
} from '../../hooks/queries';
import { useTransferableTokens } from '../../hooks';

Expand Down Expand Up @@ -127,6 +128,12 @@ describe('Form', () => {
toggleSetting: jest.fn(),
});

useValidateFeeBalance.mockReturnValue({
hasSufficientBalanceForFee: true,
isLoading: false,
feeToken: mockAppsTokens.data[0],
});

beforeEach(() => {
bookmarks = {
LSK: [
Expand Down
8 changes: 7 additions & 1 deletion src/modules/token/fungible/components/SendView/index.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getTransactionBaseFees } from '@transaction/api';
import { mockTokensBalance, mockTokenSummary } from '@token/fungible/__fixtures__/mockTokens';
import { mockAppsTokens, mockTokensBalance, mockTokenSummary } from '@token/fungible/__fixtures__/mockTokens';
import { mountWithRouterAndQueryClient } from 'src/utils/testHelpers';
import mockManagedApplications from '@tests/fixtures/blockchainApplicationsManage';
import mockSavedAccounts from '@tests/fixtures/accounts';
Expand All @@ -17,6 +17,7 @@ import {
useGetMinimumMessageFee,
useTokenBalances,
useTokenSummary,
useValidateFeeBalance,
} from '../../hooks/queries';
import { useTransferableTokens } from '../../hooks';

Expand Down Expand Up @@ -62,6 +63,11 @@ useGetInitializationFees.mockReturnValue({
data: { data: { escrowAccount: 165000, userAccount: 165000 } },
});
useGetMinimumMessageFee.mockReturnValue({ data: { data: { fee: 5000000 } } });
useValidateFeeBalance.mockReturnValue({
hasSufficientBalanceForFee: true,
isLoading: false,
feeToken: mockAppsTokens.data[0],
});

getTransactionBaseFees.mockResolvedValue({
Low: 0,
Expand Down
15 changes: 1 addition & 14 deletions src/modules/wallet/components/overview/overview.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ import { useCurrentAccount } from '@account/hooks';
import { useLatestBlock } from '@block/hooks/queries/useLatestBlock';
import { SecondaryButton, PrimaryButton } from '@theme/buttons';
import { useValidators } from '@pos/validator/hooks/queries';
import { useValidateFeeBalance } from '@token/fungible/hooks/queries/useValidateFeeBalance';
import { selectSearchParamValue } from 'src/utils/searchParams';
import { getTokenBalanceErrorMessage } from 'src/modules/common/utils/getTokenBalanceErrorMessage';
import { useAuth } from '@auth/hooks/queries';
import routes from 'src/routes/routes';
import styles from './overview.css';
Expand Down Expand Up @@ -63,7 +61,6 @@ const Overview = ({ isWalletRoute, history }) => {
refetch,
} = useTokenBalances({ config: { params: { address } } });
const { data: myTokenBalances } = useTokenBalances();
const { hasSufficientBalanceForFee, feeToken } = useValidateFeeBalance();
const hasTokenWithBalance = myTokenBalances?.data?.some(
(tokenBalance) => BigInt(tokenBalance?.availableBalance || 0) > BigInt(0)
);
Expand Down Expand Up @@ -130,17 +127,7 @@ const Overview = ({ isWalletRoute, history }) => {
)}
</div>
<div className={`${grid['col-xs-3']} ${grid['col-md-3']} ${grid['col-lg-3']}`}>
<DialogLink
data={getTokenBalanceErrorMessage({
hasSufficientBalanceForFee,
feeTokenSymbol: feeToken?.symbol,
hasAvailableTokenBalance: hasTokenWithBalance,
t,
})}
component={
hasTokenWithBalance && hasSufficientBalanceForFee ? 'send' : 'noTokenBalance'
}
>
<DialogLink component="send">
<PrimaryButton>{t('Send')}</PrimaryButton>
</DialogLink>
</div>
Expand Down