mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Add token decimals validation in Swaps (#11587)
* Add token decimals validation in Swaps * Use camelCase
This commit is contained in:
parent
c1d96676b5
commit
a17c4462b4
@ -2302,6 +2302,10 @@
|
||||
"message": "Verified on $1 sources.",
|
||||
"description": "Indicates the number of token information sources that recognize the symbol + address. $1 is a decimal number."
|
||||
},
|
||||
"swapTooManyDecimalsError": {
|
||||
"message": "$1 allows up to $2 decimals",
|
||||
"description": "$1 is a token symbol and $2 is the max. number of decimals allowed for the token"
|
||||
},
|
||||
"swapTransactionComplete": {
|
||||
"message": "Transaction complete"
|
||||
},
|
||||
|
@ -74,6 +74,7 @@ export default function BuildQuote({
|
||||
maxSlippage,
|
||||
selectedAccountAddress,
|
||||
isFeatureFlagLoaded,
|
||||
tokenFromError,
|
||||
}) {
|
||||
const t = useContext(I18nContext);
|
||||
const dispatch = useDispatch();
|
||||
@ -367,6 +368,11 @@ export default function BuildQuote({
|
||||
}
|
||||
}
|
||||
|
||||
const swapYourTokenBalance = t('swapYourTokenBalance', [
|
||||
fromTokenString || '0',
|
||||
fromTokenSymbol,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="build-quote">
|
||||
<div className="build-quote__content">
|
||||
@ -406,28 +412,35 @@ export default function BuildQuote({
|
||||
/>
|
||||
<div
|
||||
className={classnames('build-quote__balance-message', {
|
||||
'build-quote__balance-message--error': balanceError,
|
||||
'build-quote__balance-message--error':
|
||||
balanceError || tokenFromError,
|
||||
})}
|
||||
>
|
||||
{!balanceError &&
|
||||
{!tokenFromError &&
|
||||
!balanceError &&
|
||||
fromTokenSymbol &&
|
||||
t('swapYourTokenBalance', [
|
||||
fromTokenString || '0',
|
||||
fromTokenSymbol,
|
||||
])}
|
||||
{balanceError && fromTokenSymbol && (
|
||||
swapYourTokenBalance}
|
||||
{!tokenFromError && balanceError && fromTokenSymbol && (
|
||||
<div className="build-quite__insufficient-funds">
|
||||
<div className="build-quite__insufficient-funds-first">
|
||||
{t('swapsNotEnoughForTx', [fromTokenSymbol])}
|
||||
</div>
|
||||
<div className="build-quite__insufficient-funds-second">
|
||||
{t('swapYourTokenBalance', [
|
||||
fromTokenString || '0',
|
||||
fromTokenSymbol,
|
||||
])}
|
||||
{swapYourTokenBalance}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{tokenFromError && (
|
||||
<>
|
||||
<div className="build-quote__form-error">
|
||||
{t('swapTooManyDecimalsError', [
|
||||
fromTokenSymbol,
|
||||
fromTokenDecimals,
|
||||
])}
|
||||
</div>
|
||||
<div>{swapYourTokenBalance}</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="build-quote__swap-arrows-row">
|
||||
<button
|
||||
@ -560,6 +573,7 @@ export default function BuildQuote({
|
||||
}}
|
||||
submitText={t('swapReviewSwap')}
|
||||
disabled={
|
||||
tokenFromError ||
|
||||
!isFeatureFlagLoaded ||
|
||||
!Number(inputValue) ||
|
||||
!selectedToToken?.address ||
|
||||
@ -582,4 +596,5 @@ BuildQuote.propTypes = {
|
||||
setMaxSlippage: PropTypes.func,
|
||||
selectedAccountAddress: PropTypes.string,
|
||||
isFeatureFlagLoaded: PropTypes.bool.isRequired,
|
||||
tokenFromError: PropTypes.string,
|
||||
};
|
||||
|
@ -87,6 +87,11 @@
|
||||
color: $Black-100;
|
||||
}
|
||||
|
||||
.build-quote__form-error:first-of-type {
|
||||
font-weight: bold;
|
||||
color: $Red-500;
|
||||
}
|
||||
|
||||
div:last-of-type {
|
||||
font-weight: normal;
|
||||
color: $Grey-500;
|
||||
|
@ -34,6 +34,7 @@ import {
|
||||
fetchAndSetSwapsGasPriceInfo,
|
||||
fetchSwapsLiveness,
|
||||
getUseNewSwapsApi,
|
||||
getFromToken,
|
||||
} from '../../ducks/swaps/swaps';
|
||||
import {
|
||||
AWAITING_SIGNATURES_ROUTE,
|
||||
@ -70,6 +71,7 @@ import {
|
||||
fetchTopAssets,
|
||||
getSwapsTokensReceivedFromTxMeta,
|
||||
fetchAggregatorMetadata,
|
||||
countDecimals,
|
||||
} from './swaps.util';
|
||||
import AwaitingSignatures from './awaiting-signatures';
|
||||
import AwaitingSwap from './awaiting-swap';
|
||||
@ -94,6 +96,7 @@ export default function Swap() {
|
||||
const [inputValue, setInputValue] = useState(fetchParams?.value || '');
|
||||
const [maxSlippage, setMaxSlippage] = useState(fetchParams?.slippage || 3);
|
||||
const [isFeatureFlagLoaded, setIsFeatureFlagLoaded] = useState(false);
|
||||
const [tokenFromError, setTokenFromError] = useState(null);
|
||||
|
||||
const routeState = useSelector(getBackgroundSwapRouteState);
|
||||
const selectedAccount = useSelector(getSelectedAccount);
|
||||
@ -108,6 +111,7 @@ export default function Swap() {
|
||||
const chainId = useSelector(getCurrentChainId);
|
||||
const isSwapsChain = useSelector(getIsSwapsChain);
|
||||
const useNewSwapsApi = useSelector(getUseNewSwapsApi);
|
||||
const fromToken = useSelector(getFromToken);
|
||||
|
||||
const {
|
||||
balance: ethBalance,
|
||||
@ -288,10 +292,15 @@ export default function Swap() {
|
||||
|
||||
const onInputChange = (newInputValue, balance) => {
|
||||
setInputValue(newInputValue);
|
||||
dispatch(
|
||||
setBalanceError(
|
||||
new BigNumber(newInputValue || 0).gt(balance || 0),
|
||||
),
|
||||
const balanceError = new BigNumber(newInputValue || 0).gt(
|
||||
balance || 0,
|
||||
);
|
||||
// "setBalanceError" is just a warning, a user can still click on the "Review Swap" button.
|
||||
dispatch(setBalanceError(balanceError));
|
||||
setTokenFromError(
|
||||
countDecimals(newInputValue) > fromToken.decimals
|
||||
? 'tooManyDecimals'
|
||||
: null,
|
||||
);
|
||||
};
|
||||
|
||||
@ -304,6 +313,7 @@ export default function Swap() {
|
||||
selectedAccountAddress={selectedAccountAddress}
|
||||
maxSlippage={maxSlippage}
|
||||
isFeatureFlagLoaded={isFeatureFlagLoaded}
|
||||
tokenFromError={tokenFromError}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
@ -826,3 +826,12 @@ export const getSwapsLivenessForNetwork = (swapsFeatureFlags = {}, chainId) => {
|
||||
useNewSwapsApi: false,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} value
|
||||
* @returns number
|
||||
*/
|
||||
export const countDecimals = (value) => {
|
||||
if (!value || Math.floor(value) === value) return 0;
|
||||
return value.toString().split('.')[1]?.length || 0;
|
||||
};
|
||||
|
@ -31,6 +31,7 @@ import {
|
||||
isContractAddressValid,
|
||||
getNetworkNameByChainId,
|
||||
getSwapsLivenessForNetwork,
|
||||
countDecimals,
|
||||
} from './swaps.util';
|
||||
|
||||
jest.mock('../../helpers/utils/storage-helpers.js', () => ({
|
||||
@ -450,4 +451,26 @@ describe('Swaps Util', () => {
|
||||
).toMatchObject(expectedSwapsLiveness);
|
||||
});
|
||||
});
|
||||
|
||||
describe('countDecimals', () => {
|
||||
it('returns 0 decimals for an undefined value', () => {
|
||||
expect(countDecimals()).toBe(0);
|
||||
});
|
||||
|
||||
it('returns 0 decimals for number: 1', () => {
|
||||
expect(countDecimals(1)).toBe(0);
|
||||
});
|
||||
|
||||
it('returns 1 decimals for number: 1.1', () => {
|
||||
expect(countDecimals(1.1)).toBe(1);
|
||||
});
|
||||
|
||||
it('returns 3 decimals for number: 1.123', () => {
|
||||
expect(countDecimals(1.123)).toBe(3);
|
||||
});
|
||||
|
||||
it('returns 9 decimals for number: 1.123456789', () => {
|
||||
expect(countDecimals(1.123456789)).toBe(9);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user