diff --git a/src/App/hooks/useHandleRangeButtonMessage.ts b/src/App/hooks/useHandleRangeButtonMessage.ts index fcf014ebe0..bc1c15fb80 100644 --- a/src/App/hooks/useHandleRangeButtonMessage.ts +++ b/src/App/hooks/useHandleRangeButtonMessage.ts @@ -38,7 +38,8 @@ export function useHandleRangeButtonMessage( } else if ( (tokenAmount === '0' || tokenAmount === '0.00' || - tokenAmount === '') && + tokenAmount === '' || + parseFloat(tokenAmount) < 0) && !isTokenInputDisabled ) { rangeButtonErrorMessage = 'Enter an Amount'; diff --git a/src/components/Form/TokenInputQuantity.tsx b/src/components/Form/TokenInputQuantity.tsx index 6c83fa3aca..86f81f95cd 100644 --- a/src/components/Form/TokenInputQuantity.tsx +++ b/src/components/Form/TokenInputQuantity.tsx @@ -114,12 +114,25 @@ function TokenInputQuantity(props: propsIF) { const onChange = (event: ChangeEvent) => { let inputStringNoCommas = event.target.value - .replace(/,/g, '.') - .replace(/\s+/g, ''); + .replace(/,/g, '.') // Replace commas with dots + .replace(/\s+/g, ''); // Remove any spaces + + if (inputStringNoCommas === '.') inputStringNoCommas = '0.'; + + const inputStringNoUnfinishedExponent = isNaN(+inputStringNoCommas) + ? inputStringNoCommas.replace( + /e[+-]?(?!\d)/gi, // Match 'e', 'e-' or 'e+' only if NOT followed by a number + '', + ) + : inputStringNoCommas; + const isPrecisionGreaterThanDecimals = precisionOfInput(inputStringNoCommas) > token.decimals; - if (inputStringNoCommas === '.') inputStringNoCommas = '0.'; - if (!isPrecisionGreaterThanDecimals && !isNaN(+inputStringNoCommas)) { + + if ( + !isPrecisionGreaterThanDecimals && + !isNaN(+inputStringNoUnfinishedExponent) + ) { handleTokenInputEvent(inputStringNoCommas); setDisplayValue(inputStringNoCommas); } diff --git a/src/components/Swap/SwapTokenInput/SwapTokenInput.tsx b/src/components/Swap/SwapTokenInput/SwapTokenInput.tsx index dd3f8b6827..ae335c521f 100644 --- a/src/components/Swap/SwapTokenInput/SwapTokenInput.tsx +++ b/src/components/Swap/SwapTokenInput/SwapTokenInput.tsx @@ -16,7 +16,10 @@ import { PoolContext } from '../../../contexts/PoolContext'; import { TradeTableContext } from '../../../contexts/TradeTableContext'; import { TradeTokenContext } from '../../../contexts/TradeTokenContext'; import { FlexContainer } from '../../../styled/Common'; -import { truncateDecimals } from '../../../ambient-utils/dataLayer'; +import { + precisionOfInput, + truncateDecimals, +} from '../../../ambient-utils/dataLayer'; import { linkGenMethodsIF, useLinkGen } from '../../../utils/hooks/useLinkGen'; import { formatTokenInput } from '../../../utils/numbers'; import TokenInputWithWalletBalance from '../../Form/TokenInputWithWalletBalance'; @@ -152,7 +155,7 @@ function SwapTokenInput(props: propsIF) { input: string, sellToken: boolean, ): Promise { - if (isNaN(parseFloat(input)) || parseFloat(input) === 0 || !crocEnv) { + if (isNaN(parseFloat(input)) || parseFloat(input) <= 0 || !crocEnv) { setIsLiquidityInsufficient(false); return undefined; @@ -175,10 +178,24 @@ function SwapTokenInput(props: propsIF) { }); } if (sellToken) { + let precisionForTruncation = 6; + const rawTokenBQty = impact?.buyQty ? parseFloat(impact.buyQty) : 0; + + // find the largest precision that doesn't exceed the token's decimal places + while ( + precisionOfInput( + rawTokenBQty.toPrecision(precisionForTruncation), + ) > tokenB.decimals && + precisionForTruncation > 1 + ) { + precisionForTruncation--; + } const truncatedTokenBQty = rawTokenBQty ? rawTokenBQty < 2 - ? rawTokenBQty.toPrecision(6) + ? rawTokenBQty + .toPrecision(precisionForTruncation) + .replace(/\.?0+$/, '') : truncateDecimals(rawTokenBQty, rawTokenBQty < 100 ? 3 : 2) : ''; @@ -188,12 +205,27 @@ function SwapTokenInput(props: propsIF) { setIsBuyLoading(false); } } else { + let precisionForTruncation = 6; + const rawTokenAQty = impact?.sellQty ? parseFloat(impact.sellQty) : 0; + + // find the largest precision that doesn't exceed the token's decimal places + while ( + precisionOfInput( + rawTokenAQty.toPrecision(precisionForTruncation), + ) > tokenA.decimals && + precisionForTruncation > 1 + ) { + precisionForTruncation--; + } + const truncatedTokenAQty = rawTokenAQty ? rawTokenAQty < 2 - ? rawTokenAQty.toPrecision(6) + ? rawTokenAQty + .toPrecision(precisionForTruncation) + .replace(/\.?0+$/, '') : truncateDecimals(rawTokenAQty, rawTokenAQty < 100 ? 3 : 2) : ''; diff --git a/src/components/Trade/Limit/LimitTokenInput/LimitTokenInput.tsx b/src/components/Trade/Limit/LimitTokenInput/LimitTokenInput.tsx index 45ab5d8702..21d01d1bf0 100644 --- a/src/components/Trade/Limit/LimitTokenInput/LimitTokenInput.tsx +++ b/src/components/Trade/Limit/LimitTokenInput/LimitTokenInput.tsx @@ -5,7 +5,10 @@ import { PoolContext } from '../../../../contexts/PoolContext'; import { TradeTableContext } from '../../../../contexts/TradeTableContext'; import { TradeTokenContext } from '../../../../contexts/TradeTokenContext'; import { FlexContainer } from '../../../../styled/Common'; -import { truncateDecimals } from '../../../../ambient-utils/dataLayer'; +import { + precisionOfInput, + truncateDecimals, +} from '../../../../ambient-utils/dataLayer'; import { limitParamsIF, linkGenMethodsIF, @@ -155,9 +158,34 @@ function LimitTokenInput(props: propsIF) { ); } + let precisionForTruncation = 6; + + const fixedTokenBQty = parseFloat( + rawTokenBQty.toFixed(tokenB.decimals), + ); + + const precisionOfTruncatedTokenBQty = precisionOfInput( + fixedTokenBQty.toPrecision(precisionForTruncation), + ); + + // find the largest precision that doesn't exceed the token's decimal places + while ( + precisionOfTruncatedTokenBQty > tokenB.decimals && + precisionForTruncation > 1 + ) { + precisionForTruncation--; + } + + const truncatedTokenBQtyIsZero = + fixedTokenBQty.toPrecision(precisionForTruncation) === '0.00000'; + const truncatedTokenBQty = rawTokenBQty ? rawTokenBQty < 2 - ? rawTokenBQty.toPrecision(6) + ? truncatedTokenBQtyIsZero + ? '0' + : fixedTokenBQty + .toPrecision(precisionForTruncation) + .replace(/\.?0+$/, '') // Remove trailing zeros : truncateDecimals(rawTokenBQty, 2) : ''; @@ -220,9 +248,34 @@ function LimitTokenInput(props: propsIF) { fromDisplayQty(formattedRawTokenAQty, tokenA.decimals), ); } + let precisionForTruncation = 6; + + const fixedTokenAQty = parseFloat( + rawTokenAQty.toFixed(tokenA.decimals), + ); + + const precisionOfTruncatedTokenAQty = precisionOfInput( + fixedTokenAQty.toPrecision(precisionForTruncation), + ); + + // find the largest precision that doesn't exceed the token's decimal places + while ( + precisionOfTruncatedTokenAQty > tokenA.decimals && + precisionForTruncation > 1 + ) { + precisionForTruncation--; + } + + const truncatedTokenAQtyIsZero = + fixedTokenAQty.toPrecision(precisionForTruncation) === '0.00000'; + const truncatedTokenAQty = rawTokenAQty ? rawTokenAQty < 2 - ? rawTokenAQty.toPrecision(6) + ? truncatedTokenAQtyIsZero + ? '0' + : fixedTokenAQty + .toPrecision(precisionForTruncation) + .replace(/\.?0+$/, '') : truncateDecimals(rawTokenAQty, 2) : ''; diff --git a/src/pages/platformAmbient/Trade/Limit/Limit.tsx b/src/pages/platformAmbient/Trade/Limit/Limit.tsx index 037e277de1..0d0a802be9 100644 --- a/src/pages/platformAmbient/Trade/Limit/Limit.tsx +++ b/src/pages/platformAmbient/Trade/Limit/Limit.tsx @@ -123,6 +123,30 @@ export default function Limit() { const [tokenBInputQty, setTokenBInputQty] = useState( !isTokenAPrimary ? primaryQuantity : '', ); + + const tokenAInputQtyNoExponentString = useMemo(() => { + return tokenAInputQty.includes('e') + ? toDisplayQty( + fromDisplayQty(tokenAInputQty || '0', tokenA.decimals), + tokenA.decimals, + ) + : tokenAInputQty; + }, [tokenAInputQty, tokenA.decimals]); + + const tokenBInputQtyNoExponentString = useMemo(() => { + try { + return tokenBInputQty.includes('e') + ? toDisplayQty( + fromDisplayQty(tokenBInputQty || '0', tokenB.decimals), + tokenB.decimals, + ) + : tokenBInputQty; + } catch (error) { + console.log({ error }); + return tokenBInputQty; + } + }, [tokenBInputQty, tokenB.decimals]); + const [isWithdrawFromDexChecked, setIsWithdrawFromDexChecked] = useState(false); const [isSaveAsDexSurplusChecked, setIsSaveAsDexSurplusChecked] = useState( @@ -187,14 +211,17 @@ export default function Limit() { : quoteTokenDexBalance; const tokenASurplusMinusTokenARemainderNum = fromDisplayQty(tokenADexBalance || '0', tokenA.decimals) - - fromDisplayQty(tokenAInputQty || '0', tokenA.decimals); + fromDisplayQty(tokenAInputQtyNoExponentString || '0', tokenA.decimals); const isTokenADexSurplusSufficient = tokenASurplusMinusTokenARemainderNum >= 0; const tokenAQtyCoveredByWalletBalance = isWithdrawFromDexChecked ? tokenASurplusMinusTokenARemainderNum < 0 ? tokenASurplusMinusTokenARemainderNum * -1n : 0n - : fromDisplayQty(tokenAInputQty || '0', tokenA.decimals); + : fromDisplayQty( + tokenAInputQtyNoExponentString || '0', + tokenA.decimals, + ); const isTokenAAllowanceSufficient = tokenAAllowance === undefined ? true @@ -493,11 +520,14 @@ export default function Limit() { useEffect(() => { handleLimitButtonMessage( - fromDisplayQty(tokenAInputQty || '0', tokenA.decimals), + fromDisplayQty( + tokenAInputQtyNoExponentString || '0', + tokenA.decimals, + ), ); }, [ isOrderValid, - tokenAInputQty, + tokenAInputQtyNoExponentString, isPoolInitialized, poolPriceNonDisplay, limitTick, @@ -601,8 +631,8 @@ export default function Limit() { const sellToken = tokenA.address; const buyToken = tokenB.address; - const sellQty = tokenAInputQty; - const buyQty = tokenBInputQty; + const sellQty = tokenAInputQtyNoExponentString; + const buyQty = tokenBInputQtyNoExponentString; const qty = isTokenAPrimary ? sellQty : buyQty; const type = isTokenAPrimary ? 'sell' : 'buy'; @@ -898,8 +928,8 @@ export default function Limit() { const percentDiffUsdValue = usdValueTokenA && usdValueTokenB - ? ((usdValueTokenB * parseFloat(tokenBInputQty) - - usdValueTokenA * parseFloat(tokenAInputQty)) / + ? ((usdValueTokenB * parseFloat(tokenBInputQtyNoExponentString) - + usdValueTokenA * parseFloat(tokenAInputQtyNoExponentString)) / (usdValueTokenA * parseFloat(tokenAInputQty))) * 100 : 0; @@ -921,7 +951,10 @@ export default function Limit() { set: setTokenAInputQty, }} tokenBInputQty={{ - value: tokenBInputQty, + value: + tokenBInputQtyNoExponentString !== '0.0' + ? tokenBInputQty + : '0', set: setTokenBInputQty, }} isSaveAsDexSurplusChecked={isSaveAsDexSurplusChecked} @@ -964,8 +997,8 @@ export default function Limit() { 0 ? ( + parseFloat(tokenAInputQtyNoExponentString) > 0 ? (