Skip to content

Commit

Permalink
Merge pull request #5475 from LiskHQ/5473-fix-deeplinking-issue-to-se…
Browse files Browse the repository at this point in the history
…nd-token

Fix issue with balance validation on request token deep linking
  • Loading branch information
eniolam1000752 authored Nov 21, 2023
2 parents ca8980a + 6e414ef commit 418263d
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 16 deletions.
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) {
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

0 comments on commit 418263d

Please sign in to comment.