From f51235ba744a926ff549f3f1505696605893716a Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Fri, 20 Jan 2023 14:15:52 +0600 Subject: [PATCH 01/42] Remove formatting of trade amounts using formatSmartAmount() --- .../containers/LimitOrdersWidget/index.tsx | 6 ++---- .../swap/containers/NewSwapWidget/index.tsx | 4 ++-- .../modules/trade/utils/tokenViewAmount.ts | 18 +++--------------- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx b/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx index 066d00a2f6..f5f0b80b42 100644 --- a/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx +++ b/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx @@ -83,7 +83,7 @@ export function LimitOrdersWidget() { [settingState.showRecipient, isWrapOrUnwrap] ) const priceImpact = usePriceImpact(useLimitOrdersPriceImpactParams()) - const inputViewAmount = tokenViewAmount(inputCurrencyAmount, inputCurrencyBalance, orderKind === OrderKind.SELL) + const inputViewAmount = tokenViewAmount(inputCurrencyAmount) const inputCurrencyInfo: CurrencyInfo = { field: Field.INPUT, @@ -100,9 +100,7 @@ export function LimitOrdersWidget() { label: isWrapOrUnwrap ? undefined : isSellOrder ? 'You receive at least' : 'You receive exactly', currency: outputCurrency, rawAmount: isWrapOrUnwrap ? inputCurrencyAmount : outputCurrencyAmount, - viewAmount: isWrapOrUnwrap - ? inputViewAmount - : tokenViewAmount(outputCurrencyAmount, outputCurrencyBalance, orderKind === OrderKind.BUY), + viewAmount: isWrapOrUnwrap ? inputViewAmount : tokenViewAmount(outputCurrencyAmount), balance: outputCurrencyBalance, fiatAmount: outputCurrencyFiatAmount, receiveAmountInfo: null, diff --git a/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx b/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx index eb527c7a52..cfff93a6df 100644 --- a/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx +++ b/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx @@ -84,7 +84,7 @@ export function NewSwapWidget() { field: Field.INPUT, currency: currencies.INPUT || null, rawAmount: parsedAmounts.INPUT || null, - viewAmount: tokenViewAmount(parsedAmounts.INPUT, inputCurrencyBalance, independentField === Field.INPUT), + viewAmount: tokenViewAmount(parsedAmounts.INPUT), balance: inputCurrencyBalance, fiatAmount: useHigherUSDValue(trade?.inputAmountWithoutFee), receiveAmountInfo: independentField === Field.OUTPUT && trade ? getInputReceiveAmountInfo(trade) : null, @@ -94,7 +94,7 @@ export function NewSwapWidget() { field: Field.OUTPUT, currency: currencies.OUTPUT || null, rawAmount: parsedAmounts.OUTPUT || null, - viewAmount: tokenViewAmount(parsedAmounts.OUTPUT, outputCurrencyBalance, independentField === Field.OUTPUT), + viewAmount: tokenViewAmount(parsedAmounts.OUTPUT), balance: outputCurrencyBalance, fiatAmount: useHigherUSDValue(trade?.outputAmountWithoutFee), receiveAmountInfo: independentField === Field.INPUT && trade ? getOutputReceiveAmountInfo(trade) : null, diff --git a/src/cow-react/modules/trade/utils/tokenViewAmount.ts b/src/cow-react/modules/trade/utils/tokenViewAmount.ts index 90728ecc37..58274fa3fa 100644 --- a/src/cow-react/modules/trade/utils/tokenViewAmount.ts +++ b/src/cow-react/modules/trade/utils/tokenViewAmount.ts @@ -1,18 +1,6 @@ -import { formatSmartAmount } from 'utils/format' import { Currency, CurrencyAmount } from '@uniswap/sdk-core' -import { maxAmountSpend } from 'utils/maxAmountSpend' +import { AMOUNT_PRECISION } from 'constants/index' -export function tokenViewAmount( - amount: CurrencyAmount | undefined | null, - balance: CurrencyAmount | null, - isIndependentField: boolean -): string { - if (isIndependentField) { - return amount?.toExact() || '' - } - - const maxBalance = balance ? maxAmountSpend(balance) : undefined - const isInputCurrencyHasMaxAmount = !!(maxBalance && amount?.equalTo(maxBalance)) - - return (isInputCurrencyHasMaxAmount ? amount?.toExact() : formatSmartAmount(amount)) || '' +export function tokenViewAmount(amount: CurrencyAmount | undefined | null): string { + return amount?.toFixed(AMOUNT_PRECISION) || '' } From 7e16a0e6a3af0522df46cf211885c71b811ac4b7 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Wed, 1 Feb 2023 13:45:03 +0600 Subject: [PATCH 02/42] Fix tokenViewAmount --- .../containers/RateInput/index.tsx | 4 ++-- .../swap/helpers/tradeReceiveAmount.ts | 14 ++++++------ .../modules/trade/utils/tokenViewAmount.ts | 7 +++--- .../SwapModalHeader/SwapModalHeaderMod.tsx | 22 +++++++++---------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/cow-react/modules/limitOrders/containers/RateInput/index.tsx b/src/cow-react/modules/limitOrders/containers/RateInput/index.tsx index c74de26d35..8db59da6d1 100644 --- a/src/cow-react/modules/limitOrders/containers/RateInput/index.tsx +++ b/src/cow-react/modules/limitOrders/containers/RateInput/index.tsx @@ -9,12 +9,12 @@ import { useLimitOrdersTradeState } from '@cow/modules/limitOrders/hooks/useLimi import { toFraction } from '@cow/modules/limitOrders/utils/toFraction' import { useRateImpact } from '@cow/modules/limitOrders/hooks/useRateImpact' import { isFractionFalsy } from '@cow/utils/isFractionFalsy' -import { formatSmart } from '@cow/utils/format' import { getQuoteCurrency, getQuoteCurrencyByStableCoin } from '@cow/common/services/getQuoteCurrency' import { useWeb3React } from '@web3-react/core' import { getAddress } from '@cow/utils/getAddress' import { useUpdateActiveRate } from '@cow/modules/limitOrders/hooks/useUpdateActiveRate' import { TokenSymbol } from '@cow/common/pure/TokenSymbol' +import { tokenViewAmount } from '@cow/modules/trade/utils/tokenViewAmount' export function RateInput() { const { chainId } = useWeb3React() @@ -51,7 +51,7 @@ export function RateInput() { const rate = isInversed ? activeRate.invert() : activeRate - return formatSmart(rate) || '' + return tokenViewAmount(rate) }, [activeRate, areBothCurrencies, isInversed, isTypedValue, typedValue]) // Handle set market price diff --git a/src/cow-react/modules/swap/helpers/tradeReceiveAmount.ts b/src/cow-react/modules/swap/helpers/tradeReceiveAmount.ts index f50f57b953..edc78e1391 100644 --- a/src/cow-react/modules/swap/helpers/tradeReceiveAmount.ts +++ b/src/cow-react/modules/swap/helpers/tradeReceiveAmount.ts @@ -1,6 +1,6 @@ import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core' -import { formatSmartAmount } from '@cow/utils/format' import TradeGp from 'state/swap/TradeGp' +import { tokenViewAmount } from '@cow/modules/trade/utils/tokenViewAmount' export interface ReceiveAmountInfo { type: 'from' | 'to' @@ -16,19 +16,19 @@ export function getInputReceiveAmountInfo(trade: TradeGp): ReceiveAmountInfo { amountBeforeFees: trade.tradeType === TradeType.EXACT_INPUT && trade.inputAmountWithFee.lessThan(trade.fee.amount) ? '0' - : formatSmartAmount(trade.inputAmountWithoutFee) || '0', + : tokenViewAmount(trade.inputAmountWithoutFee) || '0', amountAfterFeesRaw: trade.inputAmountWithFee, - amountAfterFees: formatSmartAmount(trade.inputAmountWithFee) || '0', - feeAmount: formatSmartAmount(trade.fee.feeAsCurrency) || '0', + amountAfterFees: tokenViewAmount(trade.inputAmountWithFee) || '0', + feeAmount: tokenViewAmount(trade.fee.feeAsCurrency) || '0', } } export function getOutputReceiveAmountInfo(trade: TradeGp): ReceiveAmountInfo { return { type: 'to', - amountBeforeFees: formatSmartAmount(trade.outputAmountWithoutFee) || '0', + amountBeforeFees: tokenViewAmount(trade.outputAmountWithoutFee) || '0', amountAfterFeesRaw: trade.outputAmount, - amountAfterFees: formatSmartAmount(trade.outputAmount) || '0', - feeAmount: formatSmartAmount(trade.outputAmountWithoutFee?.subtract(trade.outputAmount)) || '0', + amountAfterFees: tokenViewAmount(trade.outputAmount) || '0', + feeAmount: tokenViewAmount(trade.outputAmountWithoutFee?.subtract(trade.outputAmount)) || '0', } } diff --git a/src/cow-react/modules/trade/utils/tokenViewAmount.ts b/src/cow-react/modules/trade/utils/tokenViewAmount.ts index 58274fa3fa..1293bf0ff5 100644 --- a/src/cow-react/modules/trade/utils/tokenViewAmount.ts +++ b/src/cow-react/modules/trade/utils/tokenViewAmount.ts @@ -1,6 +1,7 @@ -import { Currency, CurrencyAmount } from '@uniswap/sdk-core' +import { Fraction } from '@uniswap/sdk-core' import { AMOUNT_PRECISION } from 'constants/index' +import { Nullish } from '@cow/types' -export function tokenViewAmount(amount: CurrencyAmount | undefined | null): string { - return amount?.toFixed(AMOUNT_PRECISION) || '' +export function tokenViewAmount(amount: Nullish): string { + return amount?.toSignificant(AMOUNT_PRECISION) || '' } diff --git a/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx b/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx index 39dcf3e1bd..fa60b59342 100644 --- a/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx +++ b/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx @@ -22,10 +22,10 @@ import { SwapShowAcceptChanges, TruncatedText } from 'components/swap/styleds' // MOD imports import TradeGp from 'state/swap/TradeGp' -import { AMOUNT_PRECISION, INPUT_OUTPUT_EXPLANATION } from 'constants/index' +import { INPUT_OUTPUT_EXPLANATION } from 'constants/index' import { computeSlippageAdjustedAmounts } from 'utils/prices' import { Field } from 'state/swap/actions' -import { formatMax, formatSmart } from '@cow/utils/format' +import { formatMax } from '@cow/utils/format' import { AuxInformationContainer } from 'components/CurrencyInputPanel/CurrencyInputPanelMod' import FeeInformationTooltip from '../FeeInformationTooltip' import { LightCardType } from '.' @@ -33,6 +33,7 @@ import { transparentize } from 'polished' import { WarningProps } from 'components/SwapWarnings' import { RateInfo, RateInfoParams } from '@cow/common/pure/RateInfo' import { TokenSymbol } from '@cow/common/pure/TokenSymbol' +import { tokenViewAmount } from '@cow/modules/trade/utils/tokenViewAmount' export const ArrowWrapper = styled.div` --size: 26px; @@ -161,7 +162,7 @@ SwapModalHeaderProps) { fontWeight={500} title={`${fullInputWithoutFee} ${trade.inputAmount.currency.symbol || ''}`} > - {formatSmart(trade.inputAmountWithoutFee, AMOUNT_PRECISION)} + {tokenViewAmount(trade.inputAmountWithoutFee)} {/* @@ -184,8 +185,8 @@ SwapModalHeaderProps) { > {/* TODO: replace with */} - {formatSmart(trade.outputAmountWithoutFee, AMOUNT_PRECISION)} + {tokenViewAmount(trade.outputAmountWithoutFee)} @@ -249,8 +250,8 @@ SwapModalHeaderProps) { {!!exactOutLabel && ( {/* {trade.minimumAmountOut(allowedSlippage).toSignificant(6)} {trade.outputAmount.currency.symbol} */} - {formatSmart(slippageOut, AMOUNT_PRECISION) || '-'} {' '} - {/* // MOD */} + {tokenViewAmount(slippageOut) || '-'} {/* // MOD */} {/* {trade.outputAmount.currency.symbol} */} {' '} or the swap will not execute. {INPUT_OUTPUT_EXPLANATION} @@ -316,7 +316,7 @@ SwapModalHeaderProps) { Input is estimated. You will sell at most{' '} {/* {trade.maximumAmountIn(allowedSlippage).toSignificant(6)} {trade.inputAmount.currency.symbol} */} - {formatSmart(slippageIn, AMOUNT_PRECISION) || '-'} + {tokenViewAmount(slippageIn) || '-'} {/* {trade.inputAmount.currency.symbol} */} {/* // MOD */} {' '} From 0d14cdf271eb4497d2ad0f946422bbfc193a4c72 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Wed, 1 Feb 2023 15:01:49 +0600 Subject: [PATCH 03/42] Fix tokenViewAmount --- .../modules/trade/utils/tokenViewAmount.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/cow-react/modules/trade/utils/tokenViewAmount.ts b/src/cow-react/modules/trade/utils/tokenViewAmount.ts index 1293bf0ff5..628d9a7e6c 100644 --- a/src/cow-react/modules/trade/utils/tokenViewAmount.ts +++ b/src/cow-react/modules/trade/utils/tokenViewAmount.ts @@ -1,7 +1,13 @@ -import { Fraction } from '@uniswap/sdk-core' -import { AMOUNT_PRECISION } from 'constants/index' +import { Currency, CurrencyAmount, Fraction } from '@uniswap/sdk-core' import { Nullish } from '@cow/types' +import { FULL_PRICE_PRECISION } from 'constants/index' -export function tokenViewAmount(amount: Nullish): string { - return amount?.toSignificant(AMOUNT_PRECISION) || '' +export function tokenViewAmount(amount: Nullish | Fraction>): string { + if (!amount) return '' + + if (amount instanceof CurrencyAmount) { + return amount.toSignificant(amount.currency.decimals) || '' + } + + return amount.toFixed(FULL_PRICE_PRECISION) || '' } From 80a3c654827b4224a60bf6f7301f8064c325ba0b Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Thu, 2 Feb 2023 15:22:22 +0600 Subject: [PATCH 04/42] Fix formatSmartAmount usage --- .../modules/swap/helpers/tradeReceiveAmount.ts | 14 +++++++------- .../swap/SwapModalHeader/SwapModalHeaderMod.tsx | 4 +--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/cow-react/modules/swap/helpers/tradeReceiveAmount.ts b/src/cow-react/modules/swap/helpers/tradeReceiveAmount.ts index edc78e1391..f50f57b953 100644 --- a/src/cow-react/modules/swap/helpers/tradeReceiveAmount.ts +++ b/src/cow-react/modules/swap/helpers/tradeReceiveAmount.ts @@ -1,6 +1,6 @@ import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core' +import { formatSmartAmount } from '@cow/utils/format' import TradeGp from 'state/swap/TradeGp' -import { tokenViewAmount } from '@cow/modules/trade/utils/tokenViewAmount' export interface ReceiveAmountInfo { type: 'from' | 'to' @@ -16,19 +16,19 @@ export function getInputReceiveAmountInfo(trade: TradeGp): ReceiveAmountInfo { amountBeforeFees: trade.tradeType === TradeType.EXACT_INPUT && trade.inputAmountWithFee.lessThan(trade.fee.amount) ? '0' - : tokenViewAmount(trade.inputAmountWithoutFee) || '0', + : formatSmartAmount(trade.inputAmountWithoutFee) || '0', amountAfterFeesRaw: trade.inputAmountWithFee, - amountAfterFees: tokenViewAmount(trade.inputAmountWithFee) || '0', - feeAmount: tokenViewAmount(trade.fee.feeAsCurrency) || '0', + amountAfterFees: formatSmartAmount(trade.inputAmountWithFee) || '0', + feeAmount: formatSmartAmount(trade.fee.feeAsCurrency) || '0', } } export function getOutputReceiveAmountInfo(trade: TradeGp): ReceiveAmountInfo { return { type: 'to', - amountBeforeFees: tokenViewAmount(trade.outputAmountWithoutFee) || '0', + amountBeforeFees: formatSmartAmount(trade.outputAmountWithoutFee) || '0', amountAfterFeesRaw: trade.outputAmount, - amountAfterFees: tokenViewAmount(trade.outputAmount) || '0', - feeAmount: tokenViewAmount(trade.outputAmountWithoutFee?.subtract(trade.outputAmount)) || '0', + amountAfterFees: formatSmartAmount(trade.outputAmount) || '0', + feeAmount: formatSmartAmount(trade.outputAmountWithoutFee?.subtract(trade.outputAmount)) || '0', } } diff --git a/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx b/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx index fa60b59342..44a0ade222 100644 --- a/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx +++ b/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx @@ -316,9 +316,7 @@ SwapModalHeaderProps) { Input is estimated. You will sell at most{' '} {/* {trade.maximumAmountIn(allowedSlippage).toSignificant(6)} {trade.inputAmount.currency.symbol} */} - {tokenViewAmount(slippageIn) || '-'} - {/* {trade.inputAmount.currency.symbol} */} - {/* // MOD */} + {tokenViewAmount(slippageIn) || '-'} {/* // MOD */} {' '} {/* or the transaction will revert. */} or the swap will not execute. {INPUT_OUTPUT_EXPLANATION} From a7d2c61eaf99fab7ab771b8c58ee86faab691e51 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Thu, 2 Feb 2023 19:01:02 +0600 Subject: [PATCH 05/42] Use toSignificant() strategy for tokenViewAmount() --- .../containers/LimitOrdersWidget/index.tsx | 6 +++-- .../swap/containers/NewSwapWidget/index.tsx | 4 ++-- .../modules/trade/utils/tokenViewAmount.ts | 22 ++++++++++++++----- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx b/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx index 4f08547169..ab03373198 100644 --- a/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx +++ b/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx @@ -83,7 +83,7 @@ export function LimitOrdersWidget() { [settingState.showRecipient, isWrapOrUnwrap] ) const priceImpact = usePriceImpact(useLimitOrdersPriceImpactParams()) - const inputViewAmount = tokenViewAmount(inputCurrencyAmount) + const inputViewAmount = tokenViewAmount(inputCurrencyAmount, inputCurrencyBalance, orderKind === OrderKind.SELL) const inputCurrencyInfo: CurrencyInfo = { field: Field.INPUT, @@ -100,7 +100,9 @@ export function LimitOrdersWidget() { label: isWrapOrUnwrap ? undefined : isSellOrder ? 'You receive at least' : 'You receive exactly', currency: outputCurrency, rawAmount: isWrapOrUnwrap ? inputCurrencyAmount : outputCurrencyAmount, - viewAmount: isWrapOrUnwrap ? inputViewAmount : tokenViewAmount(outputCurrencyAmount), + viewAmount: isWrapOrUnwrap + ? inputViewAmount + : tokenViewAmount(outputCurrencyAmount, outputCurrencyBalance, orderKind === OrderKind.BUY), balance: outputCurrencyBalance, fiatAmount: outputCurrencyFiatAmount, receiveAmountInfo: null, diff --git a/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx b/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx index cfff93a6df..eb527c7a52 100644 --- a/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx +++ b/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx @@ -84,7 +84,7 @@ export function NewSwapWidget() { field: Field.INPUT, currency: currencies.INPUT || null, rawAmount: parsedAmounts.INPUT || null, - viewAmount: tokenViewAmount(parsedAmounts.INPUT), + viewAmount: tokenViewAmount(parsedAmounts.INPUT, inputCurrencyBalance, independentField === Field.INPUT), balance: inputCurrencyBalance, fiatAmount: useHigherUSDValue(trade?.inputAmountWithoutFee), receiveAmountInfo: independentField === Field.OUTPUT && trade ? getInputReceiveAmountInfo(trade) : null, @@ -94,7 +94,7 @@ export function NewSwapWidget() { field: Field.OUTPUT, currency: currencies.OUTPUT || null, rawAmount: parsedAmounts.OUTPUT || null, - viewAmount: tokenViewAmount(parsedAmounts.OUTPUT), + viewAmount: tokenViewAmount(parsedAmounts.OUTPUT, outputCurrencyBalance, independentField === Field.OUTPUT), balance: outputCurrencyBalance, fiatAmount: useHigherUSDValue(trade?.outputAmountWithoutFee), receiveAmountInfo: independentField === Field.INPUT && trade ? getOutputReceiveAmountInfo(trade) : null, diff --git a/src/cow-react/modules/trade/utils/tokenViewAmount.ts b/src/cow-react/modules/trade/utils/tokenViewAmount.ts index 628d9a7e6c..19cfc8ec56 100644 --- a/src/cow-react/modules/trade/utils/tokenViewAmount.ts +++ b/src/cow-react/modules/trade/utils/tokenViewAmount.ts @@ -1,13 +1,25 @@ import { Currency, CurrencyAmount, Fraction } from '@uniswap/sdk-core' import { Nullish } from '@cow/types' -import { FULL_PRICE_PRECISION } from 'constants/index' +import { maxAmountSpend } from 'utils/maxAmountSpend' +import { DEFAULT_PRECISION, FULL_PRICE_PRECISION } from 'constants/index' -export function tokenViewAmount(amount: Nullish | Fraction>): string { +export function tokenViewAmount( + amount: Nullish | Fraction>, + balance: CurrencyAmount | null = null, + isIndependentField = false +): string { if (!amount) return '' - if (amount instanceof CurrencyAmount) { - return amount.toSignificant(amount.currency.decimals) || '' + const maxBalance = balance ? maxAmountSpend(balance) : undefined + const isInputCurrencyHasMaxAmount = !!(maxBalance && amount.equalTo(maxBalance)) + + if (isIndependentField || isInputCurrencyHasMaxAmount) { + if (amount instanceof CurrencyAmount) { + return amount.toExact() || '' + } + + return amount.toFixed(FULL_PRICE_PRECISION) || '' } - return amount.toFixed(FULL_PRICE_PRECISION) || '' + return amount.toSignificant(DEFAULT_PRECISION) } From d446afc78edabbdbd6ff7e2bea771679d80bcf2f Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Thu, 2 Feb 2023 19:01:42 +0600 Subject: [PATCH 06/42] Fix e2e test --- cypress-custom/integration/limit-orders.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress-custom/integration/limit-orders.test.ts b/cypress-custom/integration/limit-orders.test.ts index 07f2620a99..3d0df26dcf 100644 --- a/cypress-custom/integration/limit-orders.test.ts +++ b/cypress-custom/integration/limit-orders.test.ts @@ -21,8 +21,8 @@ describe('Limit orders', () => { cy.get('#rate-limit-amount-input').clear().type(outputAmount, { force: true }) cy.get('#review-limit-order-btn').click() - cy.get('#limit-orders-currency-output .token-amount-input').should('have.value', '200B') + cy.get('#limit-orders-currency-output .token-amount-input').should('have.value', outputAmount) cy.get('#limit-orders-confirm #input-currency-preview .token-amount-input').should('have.value', inputAmount) - cy.get('#limit-orders-confirm #output-currency-preview .token-amount-input').should('have.value', '200B') + cy.get('#limit-orders-confirm #output-currency-preview .token-amount-input').should('have.value', outputAmount) }) }) From 192d99b86d0b54e6a5876257e9a759c27c8de676 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Thu, 2 Feb 2023 19:35:08 +0600 Subject: [PATCH 07/42] Fix e2e test --- .../integration/limit-orders.test.ts | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/cypress-custom/integration/limit-orders.test.ts b/cypress-custom/integration/limit-orders.test.ts index 3d0df26dcf..35b8df1c4c 100644 --- a/cypress-custom/integration/limit-orders.test.ts +++ b/cypress-custom/integration/limit-orders.test.ts @@ -8,8 +8,9 @@ function pickToken(symbol: string, role: 'input' | 'output') { describe('Limit orders', () => { it('Confirmation modal must contains values that were entered while creating', () => { - const inputAmount = '0.1' - const outputAmount = '2000000000000' + const inputAmount = 0.1 + const rate = 2000000000000 + const outputAmount = inputAmount * rate cy.visit(`/#/${CHAIN_ID}/limit-orders`) cy.get('#unlock-limit-orders-btn').click() @@ -17,12 +18,18 @@ describe('Limit orders', () => { pickToken('WETH', 'input') pickToken('DAI', 'output') - cy.get('#limit-orders-currency-input .token-amount-input').type(inputAmount) - cy.get('#rate-limit-amount-input').clear().type(outputAmount, { force: true }) + cy.get('#limit-orders-currency-input .token-amount-input').type(inputAmount.toString()) + cy.get('#rate-limit-amount-input').clear().type(rate.toString(), { force: true }) cy.get('#review-limit-order-btn').click() - cy.get('#limit-orders-currency-output .token-amount-input').should('have.value', outputAmount) - cy.get('#limit-orders-confirm #input-currency-preview .token-amount-input').should('have.value', inputAmount) - cy.get('#limit-orders-confirm #output-currency-preview .token-amount-input').should('have.value', outputAmount) + cy.get('#limit-orders-currency-output .token-amount-input').should('have.value', outputAmount.toString()) + cy.get('#limit-orders-confirm #input-currency-preview .token-amount-input').should( + 'have.value', + inputAmount.toString() + ) + cy.get('#limit-orders-confirm #output-currency-preview .token-amount-input').should( + 'have.value', + outputAmount.toString() + ) }) }) From 4ec3ba16e3a93392e91c3b08092bb6f091aff244 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Sun, 5 Feb 2023 16:14:28 +0600 Subject: [PATCH 08/42] Amounts formatting --- .../common/pure/FiatAmount/index.tsx | 22 +++++++ src/cow-react/common/pure/RateInfo/index.tsx | 13 ++-- .../common/pure/TokenAmount/index.tsx | 29 +++++++++ .../common/pure/TokenSymbol/index.tsx | 14 +++-- src/cow-react/constants/intl.ts | 1 + .../containers/LimitOrdersWidget/index.tsx | 6 +- .../containers/RateInput/index.tsx | 4 +- .../updaters/MarketPriceUpdater/index.tsx | 16 +++-- .../swap/containers/NewSwapWidget/index.tsx | 6 +- .../swap/helpers/tradeReceiveAmount.ts | 34 ---------- .../swap/helpers/tradeReceiveAmount.tsx | 41 ++++++++++++ .../RowReceivedAfterSlippageContent/index.tsx | 7 +-- .../modules/trade/utils/tokenViewAmount.ts | 25 -------- src/cow-react/types.ts | 4 ++ .../utils/amountFormat/index.test.ts | 63 +++++++++++++++++++ src/cow-react/utils/amountFormat/index.ts | 60 ++++++++++++++++++ src/cow-react/utils/amountFormat/utils.ts | 51 +++++++++++++++ src/cow-react/utils/format.ts | 24 +++---- src/cow-react/utils/fractionUtils.ts | 38 ++++++++++- src/cow-react/utils/trimTrailingZeros.ts | 8 +++ .../swap/ConfirmSwapModal/index.tsx | 16 ++--- .../components/swap/FeeInformationTooltip.tsx | 4 +- .../SwapModalHeader/SwapModalHeaderMod.tsx | 22 ++++--- 23 files changed, 382 insertions(+), 126 deletions(-) create mode 100644 src/cow-react/common/pure/FiatAmount/index.tsx create mode 100644 src/cow-react/common/pure/TokenAmount/index.tsx create mode 100644 src/cow-react/constants/intl.ts delete mode 100644 src/cow-react/modules/swap/helpers/tradeReceiveAmount.ts create mode 100644 src/cow-react/modules/swap/helpers/tradeReceiveAmount.tsx delete mode 100644 src/cow-react/modules/trade/utils/tokenViewAmount.ts create mode 100644 src/cow-react/utils/amountFormat/index.test.ts create mode 100644 src/cow-react/utils/amountFormat/index.ts create mode 100644 src/cow-react/utils/amountFormat/utils.ts create mode 100644 src/cow-react/utils/trimTrailingZeros.ts diff --git a/src/cow-react/common/pure/FiatAmount/index.tsx b/src/cow-react/common/pure/FiatAmount/index.tsx new file mode 100644 index 0000000000..e4bd38e24e --- /dev/null +++ b/src/cow-react/common/pure/FiatAmount/index.tsx @@ -0,0 +1,22 @@ +import { formatFiatAmount } from '@cow/utils/amountFormat' +import { FractionLike, Nullish } from '@cow/types' + +export interface FiatAmountProps { + amount: Nullish + defaultValue?: string + className?: string +} + +// TODO: remove after testing +const highlight = !!localStorage.getItem('amountsRefactoring') + +export function FiatAmount({ amount, defaultValue, className }: FiatAmountProps) { + const formattedAmount = formatFiatAmount(amount) + + return ( + + {formattedAmount ? '≈ $' : ''} + {formattedAmount || defaultValue} + + ) +} diff --git a/src/cow-react/common/pure/RateInfo/index.tsx b/src/cow-react/common/pure/RateInfo/index.tsx index 32731f2a5b..3568ab5ace 100644 --- a/src/cow-react/common/pure/RateInfo/index.tsx +++ b/src/cow-react/common/pure/RateInfo/index.tsx @@ -1,4 +1,3 @@ -import { formatSmart } from '@cow/utils/format' import React, { useEffect, useMemo, useState } from 'react' import styled from 'styled-components/macro' import { Trans } from '@lingui/macro' @@ -11,6 +10,8 @@ import { usePrice } from '@cow/common/hooks/usePrice' import { transparentize } from 'polished' import { DEFAULT_DECIMALS } from '@cowprotocol/cow-js' import { TokenSymbol } from '@cow/common/pure/TokenSymbol' +import { TokenAmount } from '@cow/common/pure/TokenAmount' +import { FiatAmount } from '@cow/common/pure/FiatAmount' export interface RateInfoParams { chainId: SupportedChainId | undefined @@ -173,10 +174,14 @@ export function RateInfo({ rateOutputCurrency.symbol || '' } > - 1 = {formatSmart(currentActiveRate)}{' '} - + 1 ={' '} + {' '} - {!!fiatAmount && (≈${formatSmart(fiatAmount, 2)})} + {!!fiatAmount && ( + + () + + )} diff --git a/src/cow-react/common/pure/TokenAmount/index.tsx b/src/cow-react/common/pure/TokenAmount/index.tsx new file mode 100644 index 0000000000..cd1766cfca --- /dev/null +++ b/src/cow-react/common/pure/TokenAmount/index.tsx @@ -0,0 +1,29 @@ +import { formatTokenAmount } from '@cow/utils/amountFormat' +import { FractionLike, Nullish } from '@cow/types' +import { TokenSymbol, TokenSymbolProps } from '@cow/common/pure/TokenSymbol' +import { FractionUtils } from '@cow/utils/fractionUtils' +import { LONG_PRECISION } from 'constants/index' + +export interface TokenAmountProps { + amount: Nullish + defaultValue?: string + tokenSymbol?: TokenSymbolProps['token'] + className?: string +} + +// TODO: remove after testing +const highlight = !!localStorage.getItem('amountsRefactoring') + +export function TokenAmount({ amount, defaultValue, className, tokenSymbol }: TokenAmountProps) { + const title = FractionUtils.fractionLikeToExact(amount, LONG_PRECISION) + + return ( + <> + + {formatTokenAmount(amount) || defaultValue} + {tokenSymbol ? ' ' : ''} + {tokenSymbol ? : ''} + + + ) +} diff --git a/src/cow-react/common/pure/TokenSymbol/index.tsx b/src/cow-react/common/pure/TokenSymbol/index.tsx index 61465a5ea1..805f7f77c4 100644 --- a/src/cow-react/common/pure/TokenSymbol/index.tsx +++ b/src/cow-react/common/pure/TokenSymbol/index.tsx @@ -1,12 +1,14 @@ import { formatSymbol } from '@cow/utils/format' import { Currency } from '@uniswap/sdk-core' +import { Nullish } from '@cow/types' -export type Props = { - token: Pick | undefined | null +export type TokenSymbolProps = { + token: Nullish> length?: number + className?: string } -export function TokenSymbol({ token, length }: Props) { +export function TokenSymbol({ token, length, className }: TokenSymbolProps) { const { symbol, name } = token || {} if (!symbol && !name) return null @@ -14,5 +16,9 @@ export function TokenSymbol({ token, length }: Props) { const fullSymbol = symbol || name const abbreviateSymbol = formatSymbol(fullSymbol, length) - return {abbreviateSymbol} + return ( + + {abbreviateSymbol} + + ) } diff --git a/src/cow-react/constants/intl.ts b/src/cow-react/constants/intl.ts new file mode 100644 index 0000000000..e45e728759 --- /dev/null +++ b/src/cow-react/constants/intl.ts @@ -0,0 +1 @@ +export const INTL_NUMBER_FORMAT = new Intl.NumberFormat(navigator.language) diff --git a/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx b/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx index ab03373198..0834b29860 100644 --- a/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx +++ b/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx @@ -39,11 +39,11 @@ import { useDetectNativeToken } from '@cow/modules/swap/hooks/useDetectNativeTok import { LimitOrdersProps, limitOrdersPropsChecker } from './limitOrdersPropsChecker' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import { useOnCurrencySelection } from '@cow/modules/limitOrders/hooks/useOnCurrencySelection' -import { tokenViewAmount } from '@cow/modules/trade/utils/tokenViewAmount' import { maxAmountSpend } from '@src/utils/maxAmountSpend' import { FractionUtils } from '@cow/utils/fractionUtils' import { useSetupLimitOrderAmountsFromUrl } from '@cow/modules/limitOrders/hooks/useSetupLimitOrderAmountsFromUrl' import AffiliateStatusCheck from 'components/AffiliateStatusCheck' +import { formatAmountInput } from '@cow/utils/amountFormat' export function LimitOrdersWidget() { useSetupTradeState() @@ -83,7 +83,7 @@ export function LimitOrdersWidget() { [settingState.showRecipient, isWrapOrUnwrap] ) const priceImpact = usePriceImpact(useLimitOrdersPriceImpactParams()) - const inputViewAmount = tokenViewAmount(inputCurrencyAmount, inputCurrencyBalance, orderKind === OrderKind.SELL) + const inputViewAmount = formatAmountInput(inputCurrencyAmount, inputCurrencyBalance, orderKind === OrderKind.SELL) const inputCurrencyInfo: CurrencyInfo = { field: Field.INPUT, @@ -102,7 +102,7 @@ export function LimitOrdersWidget() { rawAmount: isWrapOrUnwrap ? inputCurrencyAmount : outputCurrencyAmount, viewAmount: isWrapOrUnwrap ? inputViewAmount - : tokenViewAmount(outputCurrencyAmount, outputCurrencyBalance, orderKind === OrderKind.BUY), + : formatAmountInput(outputCurrencyAmount, outputCurrencyBalance, orderKind === OrderKind.BUY), balance: outputCurrencyBalance, fiatAmount: outputCurrencyFiatAmount, receiveAmountInfo: null, diff --git a/src/cow-react/modules/limitOrders/containers/RateInput/index.tsx b/src/cow-react/modules/limitOrders/containers/RateInput/index.tsx index ee1b0cc015..befe06bc24 100644 --- a/src/cow-react/modules/limitOrders/containers/RateInput/index.tsx +++ b/src/cow-react/modules/limitOrders/containers/RateInput/index.tsx @@ -14,7 +14,7 @@ import { useWeb3React } from '@web3-react/core' import { getAddress } from '@cow/utils/getAddress' import { useUpdateActiveRate } from '@cow/modules/limitOrders/hooks/useUpdateActiveRate' import { TokenSymbol } from '@cow/common/pure/TokenSymbol' -import { tokenViewAmount } from '@cow/modules/trade/utils/tokenViewAmount' +import { formatAmountInput } from '@cow/utils/amountFormat' export function RateInput() { const { chainId } = useWeb3React() @@ -51,7 +51,7 @@ export function RateInput() { const rate = isInversed ? activeRate.invert() : activeRate - return tokenViewAmount(rate) + return formatAmountInput(rate) }, [activeRate, areBothCurrencies, isInversed, isTypedValue, typedValue]) // Handle set market price diff --git a/src/cow-react/modules/limitOrders/updaters/MarketPriceUpdater/index.tsx b/src/cow-react/modules/limitOrders/updaters/MarketPriceUpdater/index.tsx index b0cab9d842..940e9d947d 100644 --- a/src/cow-react/modules/limitOrders/updaters/MarketPriceUpdater/index.tsx +++ b/src/cow-react/modules/limitOrders/updaters/MarketPriceUpdater/index.tsx @@ -1,11 +1,12 @@ import { useLayoutEffect, useState } from 'react' import { useAtomValue, useUpdateAtom } from 'jotai/utils' -import { limitRateAtom, updateLimitRateAtom } from '@cow/modules/limitOrders/state/limitRateAtom' +import { limitRateAtom, LimitRateState, updateLimitRateAtom } from '@cow/modules/limitOrders/state/limitRateAtom' import { useGetInitialPrice } from '@cow/modules/limitOrders/hooks/useGetInitialPrice' import { useLimitOrdersTradeState } from '../../hooks/useLimitOrdersTradeState' import { isFractionFalsy } from '@cow/utils/isFractionFalsy' import usePrevious from '@src/hooks/usePrevious' import { useUpdateActiveRate } from '@cow/modules/limitOrders/hooks/useUpdateActiveRate' +import { Writeable } from '@cow/types' // Fetch and update initial price for the selected token pair export function MarketPriceUpdater() { @@ -19,12 +20,17 @@ export function MarketPriceUpdater() { const prevPrice = usePrevious(price) useLayoutEffect(() => { - updateLimitRateState({ + const update: Partial> = { + initialRate: price, // Don't change isLoading flag when price is already set isLoading: isInitialPriceSet ? false : isLoading, - initialRate: price, - isTypedValue: false, - }) + } + + if (!isInitialPriceSet) { + update.isTypedValue = false + } + + updateLimitRateState(update) }, [isInitialPriceSet, price, isLoading, updateLimitRateState]) useLayoutEffect(() => { diff --git a/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx b/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx index eb527c7a52..c7d2ac3649 100644 --- a/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx +++ b/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx @@ -19,7 +19,6 @@ import { useTradePricesUpdate } from '@cow/modules/swap/hooks/useTradePricesUpda import { useCurrencyBalance } from 'state/connection/hooks' import { CurrencyInfo } from '@cow/common/pure/CurrencyInputPanel/types' import { Field } from 'state/swap/actions' -import { tokenViewAmount } from '@cow/modules/trade/utils/tokenViewAmount' import { useHigherUSDValue } from 'hooks/useStablecoinPrice' import { getInputReceiveAmountInfo, getOutputReceiveAmountInfo } from '@cow/modules/swap/helpers/tradeReceiveAmount' import React, { useState } from 'react' @@ -45,6 +44,7 @@ import { NetworkAlert } from 'components/NetworkAlert/NetworkAlert' import { useRateInfoParams } from '@cow/common/hooks/useRateInfoParams' import { useSetupSwapAmountsFromUrl } from '@cow/modules/swap/hooks/useSetupSwapAmountsFromUrl' import { useIsTradeUnsupported } from 'state/lists/hooks/hooksMod' +import { formatAmountInput } from '@cow/utils/amountFormat' export function NewSwapWidget() { useSetupTradeState() @@ -84,7 +84,7 @@ export function NewSwapWidget() { field: Field.INPUT, currency: currencies.INPUT || null, rawAmount: parsedAmounts.INPUT || null, - viewAmount: tokenViewAmount(parsedAmounts.INPUT, inputCurrencyBalance, independentField === Field.INPUT), + viewAmount: formatAmountInput(parsedAmounts.INPUT, inputCurrencyBalance, independentField === Field.INPUT), balance: inputCurrencyBalance, fiatAmount: useHigherUSDValue(trade?.inputAmountWithoutFee), receiveAmountInfo: independentField === Field.OUTPUT && trade ? getInputReceiveAmountInfo(trade) : null, @@ -94,7 +94,7 @@ export function NewSwapWidget() { field: Field.OUTPUT, currency: currencies.OUTPUT || null, rawAmount: parsedAmounts.OUTPUT || null, - viewAmount: tokenViewAmount(parsedAmounts.OUTPUT, outputCurrencyBalance, independentField === Field.OUTPUT), + viewAmount: formatAmountInput(parsedAmounts.OUTPUT, outputCurrencyBalance, independentField === Field.OUTPUT), balance: outputCurrencyBalance, fiatAmount: useHigherUSDValue(trade?.outputAmountWithoutFee), receiveAmountInfo: independentField === Field.INPUT && trade ? getOutputReceiveAmountInfo(trade) : null, diff --git a/src/cow-react/modules/swap/helpers/tradeReceiveAmount.ts b/src/cow-react/modules/swap/helpers/tradeReceiveAmount.ts deleted file mode 100644 index f50f57b953..0000000000 --- a/src/cow-react/modules/swap/helpers/tradeReceiveAmount.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core' -import { formatSmartAmount } from '@cow/utils/format' -import TradeGp from 'state/swap/TradeGp' - -export interface ReceiveAmountInfo { - type: 'from' | 'to' - amountBeforeFees: string - amountAfterFees: string - amountAfterFeesRaw: CurrencyAmount - feeAmount: string -} - -export function getInputReceiveAmountInfo(trade: TradeGp): ReceiveAmountInfo { - return { - type: 'from', - amountBeforeFees: - trade.tradeType === TradeType.EXACT_INPUT && trade.inputAmountWithFee.lessThan(trade.fee.amount) - ? '0' - : formatSmartAmount(trade.inputAmountWithoutFee) || '0', - amountAfterFeesRaw: trade.inputAmountWithFee, - amountAfterFees: formatSmartAmount(trade.inputAmountWithFee) || '0', - feeAmount: formatSmartAmount(trade.fee.feeAsCurrency) || '0', - } -} - -export function getOutputReceiveAmountInfo(trade: TradeGp): ReceiveAmountInfo { - return { - type: 'to', - amountBeforeFees: formatSmartAmount(trade.outputAmountWithoutFee) || '0', - amountAfterFeesRaw: trade.outputAmount, - amountAfterFees: formatSmartAmount(trade.outputAmount) || '0', - feeAmount: formatSmartAmount(trade.outputAmountWithoutFee?.subtract(trade.outputAmount)) || '0', - } -} diff --git a/src/cow-react/modules/swap/helpers/tradeReceiveAmount.tsx b/src/cow-react/modules/swap/helpers/tradeReceiveAmount.tsx new file mode 100644 index 0000000000..33e41ad885 --- /dev/null +++ b/src/cow-react/modules/swap/helpers/tradeReceiveAmount.tsx @@ -0,0 +1,41 @@ +import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core' +import TradeGp from 'state/swap/TradeGp' +import { TokenAmount } from '@cow/common/pure/TokenAmount' +import { ReactNode } from 'react' + +export interface ReceiveAmountInfo { + type: 'from' | 'to' + amountBeforeFees: ReactNode + amountAfterFees: ReactNode + amountAfterFeesRaw: CurrencyAmount + feeAmount: ReactNode +} + +export function getInputReceiveAmountInfo(trade: TradeGp): ReceiveAmountInfo { + return { + type: 'from', + amountBeforeFees: ( + + ), + amountAfterFeesRaw: trade.inputAmountWithFee, + amountAfterFees: , + feeAmount: , + } +} + +export function getOutputReceiveAmountInfo(trade: TradeGp): ReceiveAmountInfo { + return { + type: 'to', + amountBeforeFees: , + amountAfterFeesRaw: trade.outputAmount, + amountAfterFees: , + feeAmount: , + } +} diff --git a/src/cow-react/modules/swap/pure/Row/RowReceivedAfterSlippageContent/index.tsx b/src/cow-react/modules/swap/pure/Row/RowReceivedAfterSlippageContent/index.tsx index 5c7736fb07..6b46f03a23 100644 --- a/src/cow-react/modules/swap/pure/Row/RowReceivedAfterSlippageContent/index.tsx +++ b/src/cow-react/modules/swap/pure/Row/RowReceivedAfterSlippageContent/index.tsx @@ -7,11 +7,9 @@ import { RowFixed } from 'components/Row' import { MouseoverTooltipContent } from 'components/Tooltip' import { getMinimumReceivedTooltip } from 'utils/tooltips' -import { formatSmart } from '@cow/utils/format' -import { AMOUNT_PRECISION } from 'constants/index' import { StyledRowBetween, TextWrapper } from '../styled' import { RowStyleProps } from '@cow/modules/swap/pure/Row/types' -import { TokenSymbol } from '@cow/common/pure/TokenSymbol' +import { TokenAmount } from '@cow/common/pure/TokenAmount' export interface RowReceivedAfterSlippageContentProps extends RowReceivedAfterSlippageProps { isExactIn: boolean @@ -40,8 +38,7 @@ export function RowReceivedAfterSlippageContent(props: RowReceivedAfterSlippageC - {`${formatSmart(swapAmount, AMOUNT_PRECISION) || '-'} `} - + ) diff --git a/src/cow-react/modules/trade/utils/tokenViewAmount.ts b/src/cow-react/modules/trade/utils/tokenViewAmount.ts deleted file mode 100644 index 19cfc8ec56..0000000000 --- a/src/cow-react/modules/trade/utils/tokenViewAmount.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Currency, CurrencyAmount, Fraction } from '@uniswap/sdk-core' -import { Nullish } from '@cow/types' -import { maxAmountSpend } from 'utils/maxAmountSpend' -import { DEFAULT_PRECISION, FULL_PRICE_PRECISION } from 'constants/index' - -export function tokenViewAmount( - amount: Nullish | Fraction>, - balance: CurrencyAmount | null = null, - isIndependentField = false -): string { - if (!amount) return '' - - const maxBalance = balance ? maxAmountSpend(balance) : undefined - const isInputCurrencyHasMaxAmount = !!(maxBalance && amount.equalTo(maxBalance)) - - if (isIndependentField || isInputCurrencyHasMaxAmount) { - if (amount instanceof CurrencyAmount) { - return amount.toExact() || '' - } - - return amount.toFixed(FULL_PRICE_PRECISION) || '' - } - - return amount.toSignificant(DEFAULT_PRECISION) -} diff --git a/src/cow-react/types.ts b/src/cow-react/types.ts index 877a462f8f..6193ebd763 100644 --- a/src/cow-react/types.ts +++ b/src/cow-react/types.ts @@ -1,3 +1,5 @@ +import { Currency, CurrencyAmount, Fraction, Price } from '@uniswap/sdk-core' + export type Timestamp = number // Example: 1667981900 === Nov 09 2022 14:18:20 export type Milliseconds = number // Example: 30000 === 30 sec @@ -5,3 +7,5 @@ export type Milliseconds = number // Example: 30000 === 30 sec export type Writeable = { -readonly [P in keyof T]: T[P] } export type Nullish = T | null | undefined + +export type FractionLike = Fraction | Price | CurrencyAmount diff --git a/src/cow-react/utils/amountFormat/index.test.ts b/src/cow-react/utils/amountFormat/index.test.ts new file mode 100644 index 0000000000..dc007a001a --- /dev/null +++ b/src/cow-react/utils/amountFormat/index.test.ts @@ -0,0 +1,63 @@ +import { formatTokenAmount } from './index' +import { CurrencyAmount } from '@uniswap/sdk-core' +import { DAI_GOERLI } from 'utils/goerli/constants' + +describe('Amounts formatting', () => { + const decimals = DAI_GOERLI.decimals + const getAmount = (value: string, decimalsShift: number) => + CurrencyAmount.fromRawAmount(DAI_GOERLI, value + '0'.repeat(decimals + decimalsShift)) + + it('Extra small amount', () => { + const result = formatTokenAmount(getAmount('1', -decimals)) + + expect(result).toBe('< 0.000001') + }) + + it('Small amount', () => { + const result = formatTokenAmount(getAmount('1', -4)) + + expect(result).toBe('0.0001') + }) + + it('One amount', () => { + const result = formatTokenAmount(getAmount('1', 0)) + + expect(result).toBe('1') + }) + + it('Regular amount', () => { + const result = formatTokenAmount(getAmount('1', 3)) + + expect(result).toBe('1,000') + }) + + it('Thousands amount', () => { + const result = formatTokenAmount(getAmount('1', 4)) + + expect(result).toBe('10,000') + }) + + it('Hundreds thousands amount', () => { + const result = formatTokenAmount(getAmount('23', 4)) + + expect(result).toBe('230,000') + }) + + it('Millions amount', () => { + const result = formatTokenAmount(getAmount('5456', 3)) + + expect(result).toBe('5,456,000') + }) + + it('Billions amount', () => { + const result = formatTokenAmount(getAmount('9307222438', 0)) + + expect(result).toBe('9.307B') + }) + + it('Trillions amount', () => { + const result = formatTokenAmount(getAmount('45676822', 9)) + + expect(result).toBe('45,676.822T') + }) +}) diff --git a/src/cow-react/utils/amountFormat/index.ts b/src/cow-react/utils/amountFormat/index.ts new file mode 100644 index 0000000000..1fb249926c --- /dev/null +++ b/src/cow-react/utils/amountFormat/index.ts @@ -0,0 +1,60 @@ +import { FractionLike, Nullish } from '@cow/types' +import { Currency, CurrencyAmount } from '@uniswap/sdk-core' +import { maxAmountSpend } from 'utils/maxAmountSpend' +import { AMOUNT_PRECISION, FIAT_PRECISION } from 'constants/index' +import { trimTrailingZeros } from '@cow/utils/trimTrailingZeros' +import { FractionUtils } from '@cow/utils/fractionUtils' +import { getPrecisionForAmount, getSuffixForAmount, lessThanPrecisionSymbol, trimHugeAmounts } from './utils' +import { INTL_NUMBER_FORMAT } from '@cow/constants/intl' + +export function formatFiatAmount(amount: Nullish): string { + return formatAmountWithPrecision(amount, FIAT_PRECISION) +} + +export function formatTokenAmount(amount: Nullish): string { + return formatAmountWithPrecision(amount, getPrecisionForAmount(amount)) +} + +export function formatAmountWithPrecision(amount: Nullish, precision: number): string { + if (!amount) return '' + + // Align fraction-like types to Fraction + const amountAsFraction = FractionUtils.fractionLikeToFraction(amount) + // Calculate suffix (T,B or nothing) + const suffix = getSuffixForAmount(amountAsFraction) + // For cases when an amount is more than billions + const { quotient, remainder } = trimHugeAmounts(amountAsFraction) + + // Apply the language formatting for the amount + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat + const formattedQuotient = INTL_NUMBER_FORMAT.format(BigInt(quotient.toString())) + // Trim the remainder up to precision + const formattedRemainder = remainder.greaterThan(0) ? `${remainder.toFixed(precision).slice(1)}` : '' + const result = trimTrailingZeros(formattedQuotient + formattedRemainder) + suffix + + return +result === 0 ? lessThanPrecisionSymbol(precision) : result +} + +export function formatAmountInput( + amount: Nullish, + balance: CurrencyAmount | null = null, + isIndependentField = false +): string { + if (!amount) return '' + + const maxBalance = balance ? maxAmountSpend(balance) : undefined + const isAmountMatchesBalance = !!(maxBalance && amount.equalTo(maxBalance)) + + return trimTrailingZeros( + (() => { + if (isIndependentField || isAmountMatchesBalance) { + return FractionUtils.fractionLikeToExact(amount) + } + + const precision = getPrecisionForAmount(amount) + const result = amount.toFixed(precision) + + return +result === 0 ? amount.toSignificant(AMOUNT_PRECISION) : result + })() + ) +} diff --git a/src/cow-react/utils/amountFormat/utils.ts b/src/cow-react/utils/amountFormat/utils.ts new file mode 100644 index 0000000000..b2f6a086e3 --- /dev/null +++ b/src/cow-react/utils/amountFormat/utils.ts @@ -0,0 +1,51 @@ +import { FractionLike, Nullish } from '@cow/types' +import { FractionUtils } from '@cow/utils/fractionUtils' +import { CurrencyAmount, Fraction } from '@uniswap/sdk-core' +import JSBI from 'jsbi' + +const ONE = JSBI.BigInt(1) +const HUNDRED_K = JSBI.BigInt(100_000) +const MILLION = JSBI.BigInt(1_000_000) +const TEN_MILLION = JSBI.BigInt(10_000_000) +const BILLION = JSBI.BigInt(1_000_000_000) +const TRILLION = JSBI.BigInt(1_000_000_000_000) + +export function getPrecisionForAmount(amount: Nullish): number { + if (!amount) return 0 + + const fraction = FractionUtils.fractionLikeToFraction(amount) + const smartPrecision = (() => { + if (FractionUtils.gte(fraction, BILLION)) return 3 + + if (FractionUtils.lte(fraction, ONE)) return 6 + if (FractionUtils.lte(fraction, HUNDRED_K)) return 4 + if (FractionUtils.lte(fraction, MILLION)) return 3 + if (FractionUtils.lte(fraction, TEN_MILLION)) return 2 + return 1 + })() + + // Some tokens have decimals = 0 + if (amount instanceof CurrencyAmount && amount.currency.decimals < smartPrecision) { + return amount.currency.decimals + } + + return smartPrecision +} + +export function getSuffixForAmount(amount: Fraction): string { + if (FractionUtils.gte(amount, TRILLION)) return 'T' + if (FractionUtils.gte(amount, BILLION)) return 'B' + + return '' +} + +export function trimHugeAmounts(amount: Fraction): Fraction { + if (FractionUtils.gte(amount, TRILLION)) return amount.divide(TRILLION) + if (FractionUtils.gte(amount, BILLION)) return amount.divide(BILLION) + + return amount +} + +export function lessThanPrecisionSymbol(precision: number): string { + return `< 0.${'0'.repeat(precision - 1)}1` +} diff --git a/src/cow-react/utils/format.ts b/src/cow-react/utils/format.ts index 0d5fe48fe2..e60d1fce0e 100644 --- a/src/cow-react/utils/format.ts +++ b/src/cow-react/utils/format.ts @@ -107,6 +107,7 @@ function _adjustCurrencyAmountPrecision(value: CurrencyAmount): { amou * @param decimalsToShow * @param options * @returns string or undefined + * @deprecated use cow-react/utils/amountFormat */ export function formatSmart( value: CurrencyAmount | Percent | BigNumber | Fraction | null | undefined, @@ -140,11 +141,17 @@ export function formatSmart( }) } +/** + * @deprecated use cow-react/utils/amountFormat + */ export function formatSmartLocaleAware(...params: Parameters): ReturnType { const [value, decimalsToShow, options = {}] = params return formatSmart(value, decimalsToShow, { ...options, isLocaleAware: true, thousandSeparator: true }) } +/** + * @deprecated use cow-react/utils/amountFormat + */ export function formatSmartAmount( value: CurrencyAmount | Percent | BigNumber | Fraction | null | undefined ): string | undefined { @@ -167,6 +174,7 @@ export function formatSmartAmount( * * @param value * @param decimals + * @deprecated use FractionUtils.fractionLikeToExact */ export function formatMax(value?: Fraction, decimals?: number): string | undefined { if (!value) { @@ -180,21 +188,9 @@ export function formatMax(value?: Fraction, decimals?: number): string | undefin return amount } -/** - * Truncated given `value` on `decimals`. - * E.g.: value=10.001; decimals=2 => 10.00 - * - * @param value - * @param decimals - */ -export function truncateOnMaxDecimals(value: string, decimals: number): string { - const regex = new RegExp(`(\\d*\\.\\d{${decimals}})\\d*`) - return value.replace(regex, '$1') -} - const DEFAULT_MAX_SYMBOL_LENGTH = 12 -export function formatSymbol(symbol: string | undefined, length?: number): string | undefined { - const maxLength = length ? length : DEFAULT_MAX_SYMBOL_LENGTH +// TODO: move to another file +export function formatSymbol(symbol: string | undefined, maxLength = DEFAULT_MAX_SYMBOL_LENGTH): string | undefined { return symbol && symbol.length > maxLength ? symbol.slice(0, maxLength) + '...' : symbol } diff --git a/src/cow-react/utils/fractionUtils.ts b/src/cow-react/utils/fractionUtils.ts index 38bcaf962e..2f359006e3 100644 --- a/src/cow-react/utils/fractionUtils.ts +++ b/src/cow-react/utils/fractionUtils.ts @@ -1,13 +1,15 @@ -import { Fraction } from '@uniswap/sdk-core' +import { CurrencyAmount, Fraction, Price, BigintIsh } from '@uniswap/sdk-core' +import { FractionLike, Nullish } from '@cow/types' +import { FULL_PRICE_PRECISION } from 'constants/index' export class FractionUtils { - static serializeFractionToJSON(fraction: Fraction | null | undefined): string { + static serializeFractionToJSON(fraction: Nullish): string { if (!fraction) return '' const { numerator, denominator } = fraction return JSON.stringify({ numerator: numerator + '', denominator: denominator + '' }) } - static parseFractionFromJSON(s: string | null | undefined): Fraction | null { + static parseFractionFromJSON(s: Nullish): Fraction | null { if (!s) return null try { @@ -17,4 +19,34 @@ export class FractionUtils { return null } } + + static fractionLikeToExact(amount: Nullish, max = FULL_PRICE_PRECISION): string { + if (!amount) return '' + + if (amount.equalTo(0)) return '0' + + if (amount instanceof CurrencyAmount) { + return amount.toFixed(amount.currency.decimals) || '' + } + + if (amount instanceof Price) { + return amount.toFixed(amount.quoteCurrency.decimals) || '' + } + + return amount.toFixed(max) || '' + } + + static fractionLikeToFraction(amount: FractionLike): Fraction { + if (amount instanceof CurrencyAmount) return new Fraction(amount.quotient, amount.decimalScale) + if (amount instanceof Price) return amount.asFraction.multiply(amount.scalar) + return amount + } + + static gte(fraction: Fraction, value: BigintIsh): boolean { + return fraction.equalTo(value) || fraction.greaterThan(value) + } + + static lte(fraction: Fraction, value: BigintIsh): boolean { + return fraction.equalTo(value) || fraction.lessThan(value) + } } diff --git a/src/cow-react/utils/trimTrailingZeros.ts b/src/cow-react/utils/trimTrailingZeros.ts new file mode 100644 index 0000000000..72e21353ce --- /dev/null +++ b/src/cow-react/utils/trimTrailingZeros.ts @@ -0,0 +1,8 @@ +import { INTL_NUMBER_FORMAT } from '@cow/constants/intl' + +const decimalsSeparator = INTL_NUMBER_FORMAT.format(1.2)[1] +const trailingZerosRegex = new RegExp('(\\' + decimalsSeparator + '\\d*?[1-9])0*$') + +export function trimTrailingZeros(value: string): string { + return value.replace(trailingZerosRegex, '$1') +} diff --git a/src/custom/components/swap/ConfirmSwapModal/index.tsx b/src/custom/components/swap/ConfirmSwapModal/index.tsx index 243da90bcd..65a76812d6 100644 --- a/src/custom/components/swap/ConfirmSwapModal/index.tsx +++ b/src/custom/components/swap/ConfirmSwapModal/index.tsx @@ -2,28 +2,20 @@ import { Trans } from '@lingui/macro' import ConfirmSwapModalMod from './ConfirmSwapModalMod' import TradeGp from 'state/swap/TradeGp' -import { formatMax, formatSmart } from '@cow/utils/format' -import { AMOUNT_PRECISION } from 'constants/index' +import { TokenAmount } from '@cow/common/pure/TokenAmount' +import React from 'react' export * from './ConfirmSwapModalMod' function PendingText(props: { trade: TradeGp | undefined }): JSX.Element { const { trade } = props const inputAmount = trade?.inputAmount - const inputSymbol = inputAmount?.currency?.symbol const outputAmount = trade?.outputAmount - const outputSymbol = outputAmount?.currency?.symbol return ( - Swapping{' '} - - {formatSmart(inputAmount, AMOUNT_PRECISION)} {inputSymbol} - {' '} - for{' '} - - {formatSmart(outputAmount, AMOUNT_PRECISION)} {outputSymbol} - + Swapping for{' '} + ) } diff --git a/src/custom/components/swap/FeeInformationTooltip.tsx b/src/custom/components/swap/FeeInformationTooltip.tsx index a6f9697227..66e1a357d7 100644 --- a/src/custom/components/swap/FeeInformationTooltip.tsx +++ b/src/custom/components/swap/FeeInformationTooltip.tsx @@ -14,8 +14,8 @@ interface FeeInformationTooltipProps { trade?: TradeGp label: React.ReactNode showHelper: boolean - amountBeforeFees?: string - amountAfterFees?: string + amountBeforeFees?: React.ReactNode + amountAfterFees?: React.ReactNode feeAmount?: CurrencyAmount type: 'From' | 'To' fiatValue: CurrencyAmount | null diff --git a/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx b/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx index 44a0ade222..8d94cbcbe8 100644 --- a/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx +++ b/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx @@ -1,6 +1,6 @@ import { Trans } from '@lingui/macro' import { /* Currency, */ Percent, TradeType } from '@uniswap/sdk-core' -import { useContext, useMemo } from 'react' +import React, { useContext, useMemo } from 'react' import { AlertTriangle, ArrowDown } from 'react-feather' import { Text } from 'rebass' // import { InterfaceTrade } from 'state/routing/types' @@ -33,7 +33,7 @@ import { transparentize } from 'polished' import { WarningProps } from 'components/SwapWarnings' import { RateInfo, RateInfoParams } from '@cow/common/pure/RateInfo' import { TokenSymbol } from '@cow/common/pure/TokenSymbol' -import { tokenViewAmount } from '@cow/modules/trade/utils/tokenViewAmount' +import { TokenAmount } from '@cow/common/pure/TokenAmount' export const ArrowWrapper = styled.div` --size: 26px; @@ -162,7 +162,7 @@ SwapModalHeaderProps) { fontWeight={500} title={`${fullInputWithoutFee} ${trade.inputAmount.currency.symbol || ''}`} > - {tokenViewAmount(trade.inputAmountWithoutFee)} + {/* @@ -185,8 +185,8 @@ SwapModalHeaderProps) { > {/* TODO: replace with */} } + amountBeforeFees={} feeAmount={trade.fee.feeAsCurrency} allowsOffchainSigning={allowsOffchainSigning} label={exactInLabel} @@ -233,7 +233,7 @@ SwapModalHeaderProps) { fontWeight={500} title={`${fullOutputWithoutFee} ${trade.outputAmount.currency.symbol || ''}`} > - {tokenViewAmount(trade.outputAmountWithoutFee)} + {} @@ -250,8 +250,8 @@ SwapModalHeaderProps) { {!!exactOutLabel && ( } + amountBeforeFees={} feeAmount={trade.outputAmountWithoutFee?.subtract(trade.outputAmount)} label={exactOutLabel} allowsOffchainSigning={allowsOffchainSigning} @@ -304,7 +304,8 @@ SwapModalHeaderProps) { Output is estimated. You will receive at least{' '} {/* {trade.minimumAmountOut(allowedSlippage).toSignificant(6)} {trade.outputAmount.currency.symbol} */} - {tokenViewAmount(slippageOut) || '-'} {/* // MOD */} + {' '} + {/* // MOD */} {/* {trade.outputAmount.currency.symbol} */} {' '} or the swap will not execute. {INPUT_OUTPUT_EXPLANATION} @@ -316,7 +317,8 @@ SwapModalHeaderProps) { Input is estimated. You will sell at most{' '} {/* {trade.maximumAmountIn(allowedSlippage).toSignificant(6)} {trade.inputAmount.currency.symbol} */} - {tokenViewAmount(slippageIn) || '-'} {/* // MOD */} + {' '} + {/* // MOD */} {' '} {/* or the transaction will revert. */} or the swap will not execute. {INPUT_OUTPUT_EXPLANATION} From 35feae0dc27050a4d3e538fd46fe97cdf1408d0a Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Sun, 5 Feb 2023 16:24:03 +0600 Subject: [PATCH 09/42] Remove redundant deps from format.ts --- src/cow-react/pages/Account/utils.ts | 6 +++++- src/cow-react/utils/format.ts | 14 +------------- src/custom/utils/price.ts | 6 +++++- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/cow-react/pages/Account/utils.ts b/src/cow-react/pages/Account/utils.ts index e656b8e0cc..353a876cd6 100644 --- a/src/cow-react/pages/Account/utils.ts +++ b/src/cow-react/pages/Account/utils.ts @@ -1,4 +1,8 @@ -import { numberFormatter } from '@cow/utils/format' +const numberFormatter = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + minimumFractionDigits: 0, +}) export const formatDecimal = (number?: number): string => { return number ? numberFormatter.format(number) : '-' diff --git a/src/cow-react/utils/format.ts b/src/cow-react/utils/format.ts index e60d1fce0e..eb4144c404 100644 --- a/src/cow-react/utils/format.ts +++ b/src/cow-react/utils/format.ts @@ -11,19 +11,7 @@ import { LONG_PRECISION, } from 'constants/index' -const TEN = new BigNumber(10) - -export const numberFormatter = new Intl.NumberFormat('en-US', { - style: 'currency', - currency: 'USD', - minimumFractionDigits: 0, -}) - -export function formatAtoms(amount: string, decimals: number): string { - return new BigNumber(amount).div(TEN.pow(decimals)).toString(10) -} - -export interface FormatSmartOptions { +interface FormatSmartOptions { thousandSeparator?: boolean smallLimit?: string isLocaleAware?: boolean diff --git a/src/custom/utils/price.ts b/src/custom/utils/price.ts index 16ceed731b..ea5b27b313 100644 --- a/src/custom/utils/price.ts +++ b/src/custom/utils/price.ts @@ -6,7 +6,6 @@ import { Percent } from '@uniswap/sdk-core' import { getQuote, getPriceQuoteLegacy as getPriceQuoteGp } from '@cow/api/gnosisProtocol' import GpQuoteError, { GpQuoteErrorCodes } from '@cow/api/gnosisProtocol/errors/QuoteError' import { getCanonicalMarket, isPromiseFulfilled, withTimeout } from 'utils/misc' -import { formatAtoms } from '@cow/utils/format' import { PRICE_API_TIMEOUT_MS, SWR_OPTIONS } from 'constants/index' import { getPriceQuote as getPriceQuoteParaswap, @@ -243,6 +242,11 @@ function _checkFeeErrorForData(error: GpQuoteError) { } } +function formatAtoms(amount: string, decimals: number): string { + return BigNumber.from(amount) + .div(10 ** decimals) + .toString() +} /** * (LEGACY) Will be overwritten in the near future * Return the best quote considering all price feeds. The quote contains information about the price and fee From b6a6e7adbbf1fbcc5b23e563d35269c03977817b Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Sun, 5 Feb 2023 18:35:55 +0600 Subject: [PATCH 10/42] Refactor TokenAmount for Account balances --- .../common/pure/TokenAmount/index.tsx | 3 +- src/cow-react/pages/Account/Balances.tsx | 36 +++++++++---------- src/cow-react/utils/amountFormat/index.ts | 8 +++-- src/cow-react/utils/fractionUtils.ts | 19 ++++++---- src/cow-react/utils/trimTrailingZeros.ts | 5 +-- 5 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/cow-react/common/pure/TokenAmount/index.tsx b/src/cow-react/common/pure/TokenAmount/index.tsx index cd1766cfca..c914a17229 100644 --- a/src/cow-react/common/pure/TokenAmount/index.tsx +++ b/src/cow-react/common/pure/TokenAmount/index.tsx @@ -15,7 +15,8 @@ export interface TokenAmountProps { const highlight = !!localStorage.getItem('amountsRefactoring') export function TokenAmount({ amount, defaultValue, className, tokenSymbol }: TokenAmountProps) { - const title = FractionUtils.fractionLikeToExact(amount, LONG_PRECISION) + const title = + FractionUtils.fractionLikeToExact(amount, LONG_PRECISION) + (tokenSymbol ? ` ${tokenSymbol.symbol}` : '') return ( <> diff --git a/src/cow-react/pages/Account/Balances.tsx b/src/cow-react/pages/Account/Balances.tsx index bb64a3a084..0638f71981 100644 --- a/src/cow-react/pages/Account/Balances.tsx +++ b/src/cow-react/pages/Account/Balances.tsx @@ -12,7 +12,6 @@ import { } from '@cow/pages/Account/styled' import { useWeb3React } from '@web3-react/core' import { getBlockExplorerUrl } from 'utils' -import { formatMax, formatSmartLocaleAware } from '@cow/utils/format' import { MouseoverTooltipContent } from 'components/Tooltip' import { SupportedChainId as ChainId } from 'constants/chains' import { ButtonPrimary } from 'custom/components/Button' @@ -22,8 +21,8 @@ import ArrowIcon from 'assets/cow-swap/arrow.svg' import CowImage from 'assets/cow-swap/cow_v2.svg' import { useTokenBalance } from 'state/connection/hooks' import { useVCowData, useSwapVCowCallback, useSetSwapVCowStatus, useSwapVCowStatus } from 'state/cowToken/hooks' -import { V_COW_CONTRACT_ADDRESS, COW_CONTRACT_ADDRESS, AMOUNT_PRECISION } from 'constants/index' -import { COW } from 'constants/tokens' +import { V_COW_CONTRACT_ADDRESS, COW_CONTRACT_ADDRESS } from 'constants/index' +import { COW, V_COW } from 'constants/tokens' import { useErrorModal } from 'hooks/useErrorMessageAndModal' import { OperationType } from 'components/TransactionConfirmationModal' import useTransactionConfirmationModal from 'hooks/useTransactionConfirmationModal' @@ -38,8 +37,7 @@ import { useCowFromLockedGnoBalances } from '@cow/pages/Account/LockedGnoVesting import { getProviderErrorMessage } from 'utils/misc' import { MetaMask } from '@web3-react/metamask' import { HelpCircle } from '@cow/common/pure/HelpCircle' - -const COW_DECIMALS = COW[ChainId.MAINNET].decimals +import { TokenAmount } from '@cow/common/pure/TokenAmount' // Number of blocks to wait before we re-enable the swap COW -> vCOW button after confirmation const BLOCKS_TO_WAIT = 2 @@ -60,8 +58,10 @@ export default function Profile() { // Locked GNO balance const { loading: isLockedGnoLoading, ...lockedGnoBalances } = useCowFromLockedGnoBalances() + const cowToken = COW[chainId] + const vCowToken = V_COW[chainId] // Cow balance - const cow = useTokenBalance(account || undefined, chainId ? COW[chainId] : undefined) + const cow = useTokenBalance(account || undefined, chainId ? cowToken : undefined) // vCow balance values const { unvested, vested, total, isLoading: isVCowLoading } = useVCowData() @@ -88,13 +88,11 @@ export default function Profile() { return output }, [isLockedGnoLoading, isVCowLoading, provider]) - const cowBalance = formatSmartLocaleAware(cow, AMOUNT_PRECISION) || '0' - const cowBalanceMax = formatMax(cow, COW_DECIMALS) || '0' - const vCowBalanceVested = formatSmartLocaleAware(shouldUpdate ? undefined : vested, AMOUNT_PRECISION) || '0' - const vCowBalanceVestedMax = vested ? formatMax(shouldUpdate ? undefined : vested, COW_DECIMALS) : '0' - const vCowBalanceUnvested = formatSmartLocaleAware(unvested, AMOUNT_PRECISION) || '0' - const vCowBalance = formatSmartLocaleAware(total, AMOUNT_PRECISION) || '0' - const vCowBalanceMax = total ? formatMax(total, COW_DECIMALS) : '0' + const vCowBalanceVested = ( + + ) + const vCowBalanceUnvested = + const vCowBalance = // Init modal hooks const { handleSetError, handleCloseError, ErrorModal } = useErrorModal() @@ -132,10 +130,10 @@ export default function Profile() { balanceBreakdown: ( - Unvested

{vCowBalanceUnvested} vCOW

+ Unvested

{vCowBalanceUnvested}

- Vested

{vCowBalanceVested} vCOW

+ Vested

{vCowBalanceVested}

), @@ -221,7 +219,7 @@ export default function Profile() { Total vCOW balance - {vCowBalance} vCOW{' '} + {vCowBalance}{' '} @@ -236,7 +234,7 @@ export default function Profile() { - {vCowBalanceVested} + {vCowBalanceVested} {renderConvertToCowContent()} @@ -259,7 +257,9 @@ export default function Profile() { Cow Balance Available COW balance - {cowBalance} COW + + + diff --git a/src/cow-react/utils/amountFormat/index.ts b/src/cow-react/utils/amountFormat/index.ts index 1fb249926c..f73373a265 100644 --- a/src/cow-react/utils/amountFormat/index.ts +++ b/src/cow-react/utils/amountFormat/index.ts @@ -27,10 +27,12 @@ export function formatAmountWithPrecision(amount: Nullish, precisi // Apply the language formatting for the amount // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat - const formattedQuotient = INTL_NUMBER_FORMAT.format(BigInt(quotient.toString())) + const formattedQuotient = INTL_NUMBER_FORMAT.format(BigInt(trimTrailingZeros(quotient.toString()))) // Trim the remainder up to precision - const formattedRemainder = remainder.greaterThan(0) ? `${remainder.toFixed(precision).slice(1)}` : '' - const result = trimTrailingZeros(formattedQuotient + formattedRemainder) + suffix + const formattedRemainder = remainder.greaterThan(0) + ? trimTrailingZeros(`${remainder.toFixed(precision).slice(1)}`) + : '' + const result = formattedQuotient + formattedRemainder + suffix return +result === 0 ? lessThanPrecisionSymbol(precision) : result } diff --git a/src/cow-react/utils/fractionUtils.ts b/src/cow-react/utils/fractionUtils.ts index 2f359006e3..49b5c41b65 100644 --- a/src/cow-react/utils/fractionUtils.ts +++ b/src/cow-react/utils/fractionUtils.ts @@ -1,6 +1,7 @@ import { CurrencyAmount, Fraction, Price, BigintIsh } from '@uniswap/sdk-core' import { FractionLike, Nullish } from '@cow/types' import { FULL_PRICE_PRECISION } from 'constants/index' +import { trimTrailingZeros } from '@cow/utils/trimTrailingZeros' export class FractionUtils { static serializeFractionToJSON(fraction: Nullish): string { @@ -25,15 +26,19 @@ export class FractionUtils { if (amount.equalTo(0)) return '0' - if (amount instanceof CurrencyAmount) { - return amount.toFixed(amount.currency.decimals) || '' - } + return trimTrailingZeros( + (() => { + if (amount instanceof CurrencyAmount) { + return amount.toFixed(amount.currency.decimals) || '' + } - if (amount instanceof Price) { - return amount.toFixed(amount.quoteCurrency.decimals) || '' - } + if (amount instanceof Price) { + return amount.toFixed(amount.quoteCurrency.decimals) || '' + } - return amount.toFixed(max) || '' + return amount.toFixed(max) || '' + })() + ) } static fractionLikeToFraction(amount: FractionLike): Fraction { diff --git a/src/cow-react/utils/trimTrailingZeros.ts b/src/cow-react/utils/trimTrailingZeros.ts index 72e21353ce..cbc1142305 100644 --- a/src/cow-react/utils/trimTrailingZeros.ts +++ b/src/cow-react/utils/trimTrailingZeros.ts @@ -1,7 +1,4 @@ -import { INTL_NUMBER_FORMAT } from '@cow/constants/intl' - -const decimalsSeparator = INTL_NUMBER_FORMAT.format(1.2)[1] -const trailingZerosRegex = new RegExp('(\\' + decimalsSeparator + '\\d*?[1-9])0*$') +const trailingZerosRegex = /(\.\d*?[1-9])0*$/ export function trimTrailingZeros(value: string): string { return value.replace(trailingZerosRegex, '$1') From 97b91e9c53543287888d5e176e5788b45d0a8b8f Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Sun, 5 Feb 2023 18:49:59 +0600 Subject: [PATCH 11/42] Refactor TokenAmount for LockedGnoVesting --- .../common/pure/TokenSymbol/index.tsx | 3 ++- .../pages/Account/LockedGnoVesting/index.tsx | 19 ++++++++++--------- .../utils/amountFormat/index.test.ts | 6 ++++++ src/cow-react/utils/amountFormat/index.ts | 2 +- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/cow-react/common/pure/TokenSymbol/index.tsx b/src/cow-react/common/pure/TokenSymbol/index.tsx index 805f7f77c4..c6c7eabaf7 100644 --- a/src/cow-react/common/pure/TokenSymbol/index.tsx +++ b/src/cow-react/common/pure/TokenSymbol/index.tsx @@ -15,9 +15,10 @@ export function TokenSymbol({ token, length, className }: TokenSymbolProps) { const fullSymbol = symbol || name const abbreviateSymbol = formatSymbol(fullSymbol, length) + const title = fullSymbol === abbreviateSymbol ? undefined : fullSymbol return ( - + {abbreviateSymbol} ) diff --git a/src/cow-react/pages/Account/LockedGnoVesting/index.tsx b/src/cow-react/pages/Account/LockedGnoVesting/index.tsx index ff8413fa58..c08203a0d2 100644 --- a/src/cow-react/pages/Account/LockedGnoVesting/index.tsx +++ b/src/cow-react/pages/Account/LockedGnoVesting/index.tsx @@ -6,8 +6,6 @@ import { ButtonPrimary } from 'custom/components/Button' import { MouseoverTooltipContent } from 'components/Tooltip' import cowImage from 'assets/cow-swap/cow_v2.svg' import ArrowIcon from 'assets/cow-swap/arrow.svg' -import { AMOUNT_PRECISION } from 'constants/index' -import { formatSmartLocaleAware } from '@cow/utils/format' import { OperationType } from 'components/TransactionConfirmationModal' import { useErrorModal } from 'hooks/useErrorMessageAndModal' import CopyHelper from 'components/Copy' @@ -25,6 +23,7 @@ import { getProviderErrorMessage, isRejectRequestProviderError } from 'utils/mis import { claimAnalytics } from 'components/analytics' import { ButtonSize } from 'theme' import { HelpCircle } from '@cow/common/pure/HelpCircle' +import { TokenAmount } from '@cow/common/pure/TokenAmount' enum ClaimStatus { INITIAL, @@ -46,10 +45,12 @@ const LockedGnoVesting: React.FC = ({ openModal, closeModal, vested, allo const { chainId = ChainId.MAINNET, account } = useWeb3React() const [status, setStatus] = useState(ClaimStatus.INITIAL) const unvested = allocated.subtract(vested) - const allocatedFormatted = formatSmartLocaleAware(allocated, AMOUNT_PRECISION) || '0' - const vestedFormatted = formatSmartLocaleAware(vested, AMOUNT_PRECISION) || '0' - const unvestedFormatted = formatSmartLocaleAware(unvested, AMOUNT_PRECISION) || '0' - const claimableFormatted = formatSmartLocaleAware(vested.subtract(claimed), AMOUNT_PRECISION) || '0' + + const allocatedFormatted = + const vestedFormatted = + const unvestedFormatted = + const claimableFormatted = + const previousAccount = usePrevious(account) const canClaim = @@ -137,16 +138,16 @@ const LockedGnoVesting: React.FC = ({ openModal, closeModal, vested, allo COW vesting from locked GNO - {allocatedFormatted} COW{' '} + {allocatedFormatted} - Unvested

{unvestedFormatted} COW

+ Unvested

{unvestedFormatted}

- Vested

{vestedFormatted} COW

+ Vested

{vestedFormatted}

} diff --git a/src/cow-react/utils/amountFormat/index.test.ts b/src/cow-react/utils/amountFormat/index.test.ts index dc007a001a..e7b177f24e 100644 --- a/src/cow-react/utils/amountFormat/index.test.ts +++ b/src/cow-react/utils/amountFormat/index.test.ts @@ -7,6 +7,12 @@ describe('Amounts formatting', () => { const getAmount = (value: string, decimalsShift: number) => CurrencyAmount.fromRawAmount(DAI_GOERLI, value + '0'.repeat(decimals + decimalsShift)) + it('Zero amount', () => { + const result = formatTokenAmount(getAmount('0', 0)) + + expect(result).toBe('0') + }) + it('Extra small amount', () => { const result = formatTokenAmount(getAmount('1', -decimals)) diff --git a/src/cow-react/utils/amountFormat/index.ts b/src/cow-react/utils/amountFormat/index.ts index f73373a265..62a1a557c5 100644 --- a/src/cow-react/utils/amountFormat/index.ts +++ b/src/cow-react/utils/amountFormat/index.ts @@ -34,7 +34,7 @@ export function formatAmountWithPrecision(amount: Nullish, precisi : '' const result = formattedQuotient + formattedRemainder + suffix - return +result === 0 ? lessThanPrecisionSymbol(precision) : result + return remainder.greaterThan(0) && +result === 0 ? lessThanPrecisionSymbol(precision) : result } export function formatAmountInput( From 19db9a9acae8dda62f03a8fa790e7168283c24da Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Sun, 5 Feb 2023 19:22:18 +0600 Subject: [PATCH 12/42] Refactor TokenAmount for Claim page and Invest --- src/cow-react/pages/Account/Balances.tsx | 4 ++- src/cow-react/pages/Claim/ClaimSummary.tsx | 7 ++-- src/cow-react/pages/Claim/ClaimingStatus.tsx | 15 +++----- src/cow-react/pages/Claim/ClaimsTable.tsx | 23 +++++------- .../Claim/InvestmentFlow/InvestOption.tsx | 36 ++++++++----------- .../Claim/InvestmentFlow/InvestSummaryRow.tsx | 24 +++++-------- src/custom/components/CowBalance/index.tsx | 7 ++-- .../components/CowBalanceButton/index.tsx | 13 ++----- .../CowSubsidyModal/SubsidyTable.tsx | 14 ++++---- src/custom/state/claim/hooks/index.ts | 5 ++- src/custom/state/cowToken/hooks.ts | 1 + 11 files changed, 60 insertions(+), 89 deletions(-) diff --git a/src/cow-react/pages/Account/Balances.tsx b/src/cow-react/pages/Account/Balances.tsx index 0638f71981..eab8f83f65 100644 --- a/src/cow-react/pages/Account/Balances.tsx +++ b/src/cow-react/pages/Account/Balances.tsx @@ -234,7 +234,9 @@ export default function Profile() {
- {vCowBalanceVested} + + + {renderConvertToCowContent()} diff --git a/src/cow-react/pages/Claim/ClaimSummary.tsx b/src/cow-react/pages/Claim/ClaimSummary.tsx index 2c3ccb322e..c7cbaba399 100644 --- a/src/cow-react/pages/Claim/ClaimSummary.tsx +++ b/src/cow-react/pages/Claim/ClaimSummary.tsx @@ -1,16 +1,15 @@ import { Trans } from '@lingui/macro' import { CurrencyAmount, Currency, Token } from '@uniswap/sdk-core' import CowProtocolLogo from 'components/CowProtocolLogo' -import { formatMax, formatSmartLocaleAware } from '@cow/utils/format' import { useClaimState } from 'state/claim/hooks' import { ClaimSummary as ClaimSummaryWrapper, ClaimSummaryTitle, ClaimTotal } from './styled' import { ClaimCommonTypes } from './types' import { ClaimStatus } from 'state/claim/actions' -import { AMOUNT_PRECISION } from 'constants/index' import { useTokenBalance } from 'state/connection/hooks' import { V_COW } from 'constants/tokens' import { useWeb3React } from '@web3-react/core' import JSBI from 'jsbi' +import { TokenAmount } from '@cow/common/pure/TokenAmount' type ClaimSummaryProps = Pick & { unclaimedAmount: ClaimCommonTypes['tokenCurrencyAmount'] | undefined @@ -73,9 +72,9 @@ export function ClaimSummaryView({
{totalAvailableText && {totalAvailableText}} -

+

{' '} - {formatSmartLocaleAware(totalAvailableAmount, AMOUNT_PRECISION) || '0'} vCOW +

diff --git a/src/cow-react/pages/Claim/ClaimingStatus.tsx b/src/cow-react/pages/Claim/ClaimingStatus.tsx index 434f206192..857d066a7f 100644 --- a/src/cow-react/pages/Claim/ClaimingStatus.tsx +++ b/src/cow-react/pages/Claim/ClaimingStatus.tsx @@ -24,13 +24,12 @@ import twitterImage from 'assets/cow-swap/twitter.svg' import discordImage from 'assets/cow-swap/discord.svg' import CowProtocolIcon from 'assets/cow-swap/cowprotocol.svg' import { ExternalLink } from 'theme' -import { formatMax, formatSmartLocaleAware } from '@cow/utils/format' -import { AMOUNT_PRECISION } from 'constants/index' import { shortenAddress } from 'utils' import CopyHelper from 'components/Copy' import { ButtonSecondary } from 'components/Button' import { ClaimCommonTypes } from './types' import { Routes } from '@cow/constants/routes' +import { TokenAmount } from '@cow/common/pure/TokenAmount' const COW_TWEET_TEMPLATE = 'I just joined the 🐮 CoWmunity @CoWSwap and claimed my first vCOW tokens! Join me at https://swap.cow.fi/' @@ -62,8 +61,7 @@ export default function ClaimingStatus({ handleChangeAccount }: ClaimNavProps) { const vCowAmount = currency && CurrencyAmount.fromRawAmount(currency, claimedAmount) - const formattedVCowAmount = formatSmartLocaleAware(vCowAmount, AMOUNT_PRECISION) - const formattedMaxVCowAmount = vCowAmount?.greaterThan('0') ? formatMax(vCowAmount, currency?.decimals) : '' + const formattedVCowAmount = return ( @@ -77,18 +75,13 @@ export default function ClaimingStatus({ handleChangeAccount }: ClaimNavProps) { )}

{isConfirmed ? 'Claim successful!' : isFailure ? 'Failed to claim' : 'Claiming'}

- {!isConfirmed && ( - - {formattedVCowAmount} vCOW - - )} + {!isConfirmed && formattedVCowAmount} {isConfirmed && ( <>

- Congratulations on claiming{' '} - {formattedVCowAmount} vCOW! + Congratulations on claiming {formattedVCowAmount}! {isSelfClaiming ? ( ) : ( diff --git a/src/cow-react/pages/Claim/ClaimsTable.tsx b/src/cow-react/pages/Claim/ClaimsTable.tsx index ec6b4971ae..49259eb48f 100644 --- a/src/cow-react/pages/Claim/ClaimsTable.tsx +++ b/src/cow-react/pages/Claim/ClaimsTable.tsx @@ -4,7 +4,6 @@ import { ClaimTable, ClaimBreakdown, TokenLogo, BannerExplainer } from '@cow/pag import CowProtocolLogo from 'components/CowProtocolLogo' import { ClaimStatus } from 'state/claim/actions' // import { UserClaimDataDetails } from './types' TODO: fix in another PR -import { formatMax, formatSmartLocaleAware } from '@cow/utils/format' import { ClaimCommonTypes, EnhancedUserClaimData } from './types' import { useAllClaimingTransactionIndices } from 'state/enhancedTransactions/hooks' @@ -13,10 +12,10 @@ import Circle from 'assets/images/blue-loader.svg' import { Countdown } from '@cow/pages/Claim/Countdown' import { getPaidClaims, getIndexes } from 'state/claim/hooks/utils' import { useEffect } from 'react' -import { AMOUNT_PRECISION } from 'constants/index' import { ExternalLink } from 'theme/index' import SVG from 'react-inlinesvg' import CowProtocolImage from 'assets/cow-swap/cowprotocol.svg' +import { TokenAmount } from '@cow/common/pure/TokenAmount' export type ClaimsTableProps = Pick @@ -77,27 +76,23 @@ const ClaimsTableRow = ({ {!isFree && with {currencyAmount?.currency?.symbol}} - - {formatSmartLocaleAware(claimAmount, AMOUNT_PRECISION) || 0} vCOW + + {price && ( - Price:{' '} - {`${formatSmartLocaleAware(price) || 0} vCOW per ${ - currencyAmount?.currency?.symbol - }`} + Price: {/*TODO: check quoteCurrency*/} + + per {currencyAmount?.currency?.symbol} + )} Cost:{' '} - + {' '} - {isFree ? ( - Free! - ) : ( - `${formatSmartLocaleAware(cost, AMOUNT_PRECISION) || 0} ${cost?.currency?.symbol}` - )} + {isFree ? Free! : } diff --git a/src/cow-react/pages/Claim/InvestmentFlow/InvestOption.tsx b/src/cow-react/pages/Claim/InvestmentFlow/InvestOption.tsx index 5142b78f9b..9100ecdba3 100644 --- a/src/cow-react/pages/Claim/InvestmentFlow/InvestOption.tsx +++ b/src/cow-react/pages/Claim/InvestmentFlow/InvestOption.tsx @@ -1,5 +1,5 @@ import { useCallback, useMemo, useState, useEffect } from 'react' -import { CurrencyAmount, Percent } from '@uniswap/sdk-core' +import { CurrencyAmount } from '@uniswap/sdk-core' import { BigNumber } from '@ethersproject/bignumber' import SVG from 'react-inlinesvg' @@ -14,7 +14,7 @@ import { UserMessage, WarningWrapper, } from '../styled' -import { formatMax, formatSmartLocaleAware, formatSymbol } from '@cow/utils/format' +import { formatSymbol } from '@cow/utils/format' import { calculateGasMargin } from 'utils/calculateGasMargin' import Row from 'components/Row' import CheckCircle from 'assets/cow-swap/check.svg' @@ -31,7 +31,6 @@ import Loader from 'components/Loader' import { useErrorModal } from 'hooks/useErrorMessageAndModal' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import { calculateInvestmentAmounts, calculatePercentage } from 'state/claim/hooks/utils' -import { AMOUNT_PRECISION, PERCENTAGE_PRECISION } from 'constants/index' import { useGasPrices } from 'state/gas/hooks' import { AVG_APPROVE_COST_GWEI } from 'constants/index' import { EnhancedUserClaimData } from '../types' @@ -40,6 +39,8 @@ import { ONE_HUNDRED_PERCENT } from 'constants/misc' import { IS_TESTING_ENV } from '../const' import { InvestmentFlowProps } from '.' import { getProviderErrorMessage } from 'utils/misc' +import { formatTokenAmount } from '@cow/utils/amountFormat' +import { TokenAmount } from '@cow/common/pure/TokenAmount' const ErrorMessages = { NoBalance: (symbol = '') => @@ -266,7 +267,7 @@ export default function InvestOption({ claim, openModal, closeModal }: InvestOpt if (noBalance || !parsedAmount) { percentageValue = '0' } else { - percentageValue = _formatPercentage(calculatePercentage(parsedAmount, maxCost)) + percentageValue = formatTokenAmount(calculatePercentage(parsedAmount, maxCost)) || '0' } setPercentage(percentageValue) @@ -317,7 +318,7 @@ export default function InvestOption({ claim, openModal, closeModal }: InvestOpt } if (isNative && gasCost && parsedAmount.add(gasCost).greaterThan(balance)) { - warnings.push(WarningMessages.InsufficientNativeBalance(token?.symbol, formatSmartLocaleAware(gasCost))) + warnings.push(WarningMessages.InsufficientNativeBalance(token?.symbol, formatTokenAmount(gasCost))) } setInputWarnings(warnings.length ? warnings : []) @@ -338,15 +339,16 @@ export default function InvestOption({ claim, openModal, closeModal }: InvestOpt Price{' '} - - {formatSmartLocaleAware(price) || '0'} vCOW per {currencyAmount?.currency?.symbol} + + per{' '} + {currencyAmount?.currency?.symbol} Max. investment available{' '} - - {formatSmartLocaleAware(maxCost, AMOUNT_PRECISION) || '0'} {maxCost?.currency?.symbol} + + @@ -409,12 +411,8 @@ export default function InvestOption({ claim, openModal, closeModal }: InvestOpt - - Receive: {formatSmartLocaleAware(vCowAmount, AMOUNT_PRECISION) || 0} vCOW + + Receive: {/* Insufficient balance validation error */} {inputError && ( @@ -459,7 +457,3 @@ export default function InvestOption({ claim, openModal, closeModal }: InvestOpt ) } - -function _formatPercentage(percentage: Percent): string { - return formatSmartLocaleAware(percentage, PERCENTAGE_PRECISION) || '0' -} diff --git a/src/cow-react/pages/Claim/InvestmentFlow/InvestSummaryRow.tsx b/src/cow-react/pages/Claim/InvestmentFlow/InvestSummaryRow.tsx index 385cea3e2d..caf983f857 100644 --- a/src/cow-react/pages/Claim/InvestmentFlow/InvestSummaryRow.tsx +++ b/src/cow-react/pages/Claim/InvestmentFlow/InvestSummaryRow.tsx @@ -3,11 +3,10 @@ import { calculatePercentage } from 'state/claim/hooks/utils' import { TokenLogo, InvestAvailableBar, UserMessage } from '@cow/pages/Claim/styled' import { ClaimWithInvestmentData } from '@cow/pages/Claim/types' import CowProtocolLogo from 'components/CowProtocolLogo' -import { formatMax, formatSmartLocaleAware } from '@cow/utils/format' import { ONE_HUNDRED_PERCENT } from 'constants/misc' -import { AMOUNT_PRECISION } from 'constants/index' import ImportantIcon from 'assets/cow-swap/important.svg' import SVG from 'react-inlinesvg' +import { TokenAmount } from '@cow/common/pure/TokenAmount' export type Props = { claim: ClaimWithInvestmentData } @@ -18,10 +17,7 @@ export function InvestSummaryRow(props: Props): JSX.Element | null { const symbol = isFree ? '' : (currencyAmount?.currency?.symbol as string) - const formattedCost = formatSmartLocaleAware(investmentCost, AMOUNT_PRECISION) || '0' - const formattedCostMaxPrecision = investmentCost - ? `${formatMax(investmentCost, currencyAmount?.currency?.decimals)} ${symbol}` - : '' + const formattedCost = const percentage = investmentCost && cost && calculatePercentage(investmentCost, cost) @@ -48,16 +44,13 @@ export function InvestSummaryRow(props: Props): JSX.Element | null { - - {formatSmartLocaleAware(vCowAmount, AMOUNT_PRECISION) || '0'} vCOW + + {!isFree && ( - Investment amount:{' '} - - {formattedCost} {symbol} - + Investment amount: {formattedCost} {percentage?.lessThan(ONE_HUNDRED_PERCENT) && ( @@ -75,13 +68,14 @@ export function InvestSummaryRow(props: Props): JSX.Element | null { {!isFree && ( Price:{' '} - - {formatSmartLocaleAware(price) || '0'} vCOW per {symbol} + + {/*TODO: check quoteCurrency*/} + per {symbol} )} - Cost: {isFree ? 'Free!' : `${formattedCost} ${symbol}`} + Cost: {isFree ? 'Free!' : formattedCost} Vesting: diff --git a/src/custom/components/CowBalance/index.tsx b/src/custom/components/CowBalance/index.tsx index b90fd80987..c69ef98ae0 100644 --- a/src/custom/components/CowBalance/index.tsx +++ b/src/custom/components/CowBalance/index.tsx @@ -1,11 +1,10 @@ import styled from 'styled-components/macro' import { Trans } from '@lingui/macro' -import { AMOUNT_PRECISION } from 'constants/index' import { ClaimSummaryTitle, ClaimTotal, ClaimSummary as ClaimSummaryWrapper } from '@cow/pages/Claim/styled' -import { formatMax, formatSmartLocaleAware } from '@cow/utils/format' import CowProtocolLogo from 'components/CowProtocolLogo' import { CowSubsidyInfoProps } from 'components/CowSubsidyModal' +import { TokenAmount } from '@cow/common/pure/TokenAmount' const Wrapper = styled(ClaimSummaryWrapper)` border-radius: 100px; @@ -44,9 +43,9 @@ const CowBalance = ({ balance, title }: CowBalanceProps) => {
Your combined balance -

+

{' '} - {formatSmartLocaleAware(balance, AMOUNT_PRECISION) || '0'} (v)COW +

diff --git a/src/custom/components/CowBalanceButton/index.tsx b/src/custom/components/CowBalanceButton/index.tsx index 6013f72f6a..eaf93ae4f4 100644 --- a/src/custom/components/CowBalanceButton/index.tsx +++ b/src/custom/components/CowBalanceButton/index.tsx @@ -1,13 +1,11 @@ -import { Trans } from '@lingui/macro' import styled, { css } from 'styled-components/macro' import CowProtocolLogo from 'components/CowProtocolLogo' import { useCombinedBalance } from 'state/cowToken/hooks' import { ChainId } from 'state/lists/actions/actionsMod' -import { formatMax, formatSmartLocaleAware } from '@cow/utils/format' -import { COW } from 'constants/tokens' import { transparentize } from 'polished' import { useWeb3React } from '@web3-react/core' import { supportedChainId } from 'utils/supportedChainId' +import { TokenAmount } from '@cow/common/pure/TokenAmount' export const Wrapper = styled.div<{ isLoading: boolean }>` background-color: transparent; @@ -80,15 +78,10 @@ interface CowBalanceButtonProps { isUpToSmall?: boolean } -const COW_DECIMALS = COW[ChainId.MAINNET].decimals - export default function CowBalanceButton({ onClick, isUpToSmall }: CowBalanceButtonProps) { const { chainId } = useWeb3React() const { balance, isLoading } = useCombinedBalance() - const formattedBalance = formatSmartLocaleAware(balance, 0) - const formattedMaxBalance = formatMax(balance, COW_DECIMALS) - if (!supportedChainId(chainId)) { return null } @@ -97,8 +90,8 @@ export default function CowBalanceButton({ onClick, isUpToSmall }: CowBalanceBut {!isUpToSmall && ( - - {formattedBalance || 0} + + )} diff --git a/src/custom/components/CowSubsidyModal/SubsidyTable.tsx b/src/custom/components/CowSubsidyModal/SubsidyTable.tsx index 735083584c..176714dca5 100644 --- a/src/custom/components/CowSubsidyModal/SubsidyTable.tsx +++ b/src/custom/components/CowSubsidyModal/SubsidyTable.tsx @@ -1,14 +1,13 @@ import styled from 'styled-components/macro' -import { formatSmartLocaleAware } from '@cow/utils/format' import { COW_SUBSIDY_DATA } from './constants' import { CowSubsidy } from '.' import { transparentize, lighten } from 'polished' -import { BigNumber } from 'bignumber.js' -import { formatUnits } from '@ethersproject/units' import { V_COW } from 'constants/tokens' import { SupportedChainId } from 'constants/chains' import { useIsDarkMode } from 'state/user/hooks' +import { TokenAmount } from '@cow/common/pure/TokenAmount' +import { CurrencyAmount } from '@uniswap/sdk-core' const StyledSubsidyTable = styled.table` width: 100%; @@ -154,7 +153,7 @@ const SubsidyTr = styled.tr<{ selected?: boolean; darkMode?: boolean }>` `} ` -const COW_DECIMALS = V_COW[SupportedChainId.MAINNET].decimals +const vCowToken = V_COW[SupportedChainId.MAINNET] function SubsidyTable({ discount }: CowSubsidy) { const darkMode = useIsDarkMode() @@ -171,12 +170,15 @@ function SubsidyTable({ discount }: CowSubsidy) { {/* DATA IS IN ATOMS */} {COW_SUBSIDY_DATA.map(([threshold, thresholdDiscount], i) => { const selected = discount === thresholdDiscount - const formattedThreshold = new BigNumber(formatUnits(threshold, COW_DECIMALS)) + const formattedThreshold = CurrencyAmount.fromRawAmount(vCowToken, threshold) return ( - {i && '>' + formatSmartLocaleAware(formattedThreshold)} + + {i && '>'} + {i && } + {thresholdDiscount}% diff --git a/src/custom/state/claim/hooks/index.ts b/src/custom/state/claim/hooks/index.ts index 371c06a5d5..51a23b658a 100644 --- a/src/custom/state/claim/hooks/index.ts +++ b/src/custom/state/claim/hooks/index.ts @@ -15,7 +15,6 @@ import { useTransactionAdder } from 'state/enhancedTransactions/hooks' import { GpEther, V_COW } from 'constants/tokens' -import { formatSmartLocaleAware } from '@cow/utils/format' import { calculateGasMargin } from 'utils/calculateGasMargin' import { isAddress } from 'utils' @@ -55,11 +54,11 @@ import { } from '../actions' import { EnhancedUserClaimData } from '@cow/pages/Claim/types' import { supportedChainId } from 'utils/supportedChainId' -import { AMOUNT_PRECISION } from 'constants/index' import useIsMounted from 'hooks/useIsMounted' import { SupportedChainId as ChainId } from 'constants/chains' import { ClaimInfo } from 'state/claim/reducer' import { CallState } from '@uniswap/redux-multicall' +import { formatTokenAmount } from '@cow/utils/amountFormat' export { useUserClaimData, useUserHasAvailableClaim } from '@src/state/claim/hooks' @@ -572,7 +571,7 @@ export function useClaimCallback(account: string | null | undefined): { } const vCowAmount = CurrencyAmount.fromRawAmount(vCowToken, totalClaimedAmount) - const formattedVCowAmount = formatSmartLocaleAware(vCowAmount, AMOUNT_PRECISION) || '0' + const formattedVCowAmount = formatTokenAmount(vCowAmount) || '0' const extendedArgs = _extendFinalArg(args, { from: connectedAccount, // add the `from` as the connected account diff --git a/src/custom/state/cowToken/hooks.ts b/src/custom/state/cowToken/hooks.ts index cd173d3f60..95793319c5 100644 --- a/src/custom/state/cowToken/hooks.ts +++ b/src/custom/state/cowToken/hooks.ts @@ -195,6 +195,7 @@ export function useCombinedBalance() { if (cowBalance) tmpBalance = JSBI.add(tmpBalance, cowBalance.quotient) } + // TODO: check COW vs vCOW const balance = CurrencyAmount.fromRawAmount(cow, tmpBalance) return { balance, isLoading } From 877edcc3fbc5c1a231ec5fe4732e6e754c9a669b Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Sun, 5 Feb 2023 19:23:04 +0600 Subject: [PATCH 13/42] Remove formatSmartLocaleAware --- src/cow-react/utils/format.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/cow-react/utils/format.ts b/src/cow-react/utils/format.ts index eb4144c404..ac8e58b5f1 100644 --- a/src/cow-react/utils/format.ts +++ b/src/cow-react/utils/format.ts @@ -129,14 +129,6 @@ export function formatSmart( }) } -/** - * @deprecated use cow-react/utils/amountFormat - */ -export function formatSmartLocaleAware(...params: Parameters): ReturnType { - const [value, decimalsToShow, options = {}] = params - return formatSmart(value, decimalsToShow, { ...options, isLocaleAware: true, thousandSeparator: true }) -} - /** * @deprecated use cow-react/utils/amountFormat */ From 4d0ee9bcd25d649f18b55fd5af449a14d3ee353d Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Sun, 5 Feb 2023 19:48:49 +0600 Subject: [PATCH 14/42] Remove formatSmartAmount --- .../common/helpers/pendingOrderSummary.ts | 20 --------- .../CurrencyInputPanel/CurrencyInputPanel.tsx | 7 ++-- .../CurrencyInputPanel/CurrencyPreview.tsx | 7 ++-- .../LimitOrdersConfirmModal/index.tsx | 16 ++------ .../pure/ReceiptModal/FilledField.tsx | 41 ++++++++----------- .../pure/EthFlow/WrappingPreview/WrapCard.tsx | 9 ++-- src/cow-react/utils/format.ts | 10 ----- 7 files changed, 31 insertions(+), 79 deletions(-) delete mode 100644 src/cow-react/common/helpers/pendingOrderSummary.ts diff --git a/src/cow-react/common/helpers/pendingOrderSummary.ts b/src/cow-react/common/helpers/pendingOrderSummary.ts deleted file mode 100644 index d75559dbed..0000000000 --- a/src/cow-react/common/helpers/pendingOrderSummary.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Order, OrderKind } from 'state/orders/actions' -import { formatSmartAmount } from '@cow/utils/format' -import { CurrencyAmount } from '@uniswap/sdk-core' -import { formatSymbol } from '@cow/utils/format' - -export function pendingOrderSummary(order: Order): string { - const { kind, buyAmount, sellAmount, inputToken, outputToken, feeAmount } = order - - const inputPrefix = kind === OrderKind.BUY ? 'at most ' : '' - const outputPrefix = kind === OrderKind.SELL ? 'at least ' : '' - - const inputAmount = CurrencyAmount.fromRawAmount(inputToken, sellAmount.toString()).add( - CurrencyAmount.fromRawAmount(inputToken, feeAmount.toString()) - ) - const outputAmount = CurrencyAmount.fromRawAmount(outputToken, buyAmount.toString()) - - return `Swap ${inputPrefix}${formatSmartAmount(inputAmount)} ${formatSymbol( - inputAmount.currency.symbol - )} for ${outputPrefix}${formatSmartAmount(outputAmount)} ${formatSymbol(outputAmount.currency.symbol)}` -} diff --git a/src/cow-react/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx b/src/cow-react/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx index d9a15d46a2..512e4eb9d9 100644 --- a/src/cow-react/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx +++ b/src/cow-react/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx @@ -3,7 +3,6 @@ import * as styledEl from './styled' import { CurrencySelectButton } from '@cow/modules/swap/pure/CurrencySelectButton' import { Currency } from '@uniswap/sdk-core' import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal' -import { formatSmartAmount } from '@cow/utils/format' import { FiatValue } from 'components/CurrencyInputPanel/FiatValue' import { Trans } from '@lingui/macro' import { PriceImpact } from 'hooks/usePriceImpact' @@ -16,7 +15,7 @@ import { CurrencyInfo } from '@cow/common/pure/CurrencyInputPanel/types' import { isSupportedChainId } from 'lib/hooks/routing/clientSideSmartOrderRouter' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { MouseoverTooltip } from 'components/Tooltip' -import { TokenSymbol } from '@cow/common/pure/TokenSymbol' +import { TokenAmount } from '@cow/common/pure/TokenAmount' interface BuiltItProps { className: string @@ -137,8 +136,8 @@ export function CurrencyInputPanel(props: CurrencyInputPanelProps) {
{balance && !disabled && ( <> - - Balance: {formatSmartAmount(balance) || '0'} + + Balance: {showSetMax && balance.greaterThan(0) && ( Max )} diff --git a/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx b/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx index e4c5203962..c969e859ea 100644 --- a/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx +++ b/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx @@ -1,12 +1,11 @@ import React from 'react' import * as styledEl from './styled' import { CurrencySelectButton } from '@cow/modules/swap/pure/CurrencySelectButton' -import { formatSmartAmount } from '@cow/utils/format' import { FiatValue } from 'components/CurrencyInputPanel/FiatValue' import { Trans } from '@lingui/macro' import { PriceImpact } from 'hooks/usePriceImpact' import { CurrencyInfo } from '@cow/common/pure/CurrencyInputPanel/types' -import { TokenSymbol } from '@cow/common/pure/TokenSymbol' +import { TokenAmount } from '@cow/common/pure/TokenAmount' interface BuiltItProps { className: string @@ -49,8 +48,8 @@ export function CurrencyPreview(props: CurrencyPreviewProps) {
{balance && ( <> - - Balance: {formatSmartAmount(balance) || '0'} {} + + Balance: )} diff --git a/src/cow-react/modules/limitOrders/containers/LimitOrdersConfirmModal/index.tsx b/src/cow-react/modules/limitOrders/containers/LimitOrdersConfirmModal/index.tsx index bd0258ee48..ff03c28b98 100644 --- a/src/cow-react/modules/limitOrders/containers/LimitOrdersConfirmModal/index.tsx +++ b/src/cow-react/modules/limitOrders/containers/LimitOrdersConfirmModal/index.tsx @@ -11,7 +11,6 @@ import { limitOrdersConfirmState } from '../LimitOrdersConfirmModal/state' import { useWalletInfo } from 'hooks/useWalletInfo' import { GpModal } from 'components/Modal' import * as styledEl from './styled' -import { formatSmartAmount } from '@cow/utils/format' import { useRateImpact } from '@cow/modules/limitOrders/hooks/useRateImpact' import { useRateInfoParams } from '@cow/common/hooks/useRateInfoParams' import { LimitOrdersWarnings } from '@cow/modules/limitOrders/containers/LimitOrdersWarnings' @@ -21,6 +20,7 @@ import { useErrorModal } from 'hooks/useErrorMessageAndModal' import OperatorError from '@cow/api/gnosisProtocol/errors/OperatorError' import { useAtomValue } from 'jotai/utils' import { limitOrdersSettingsAtom } from '@cow/modules/limitOrders/state/limitOrdersSettingsAtom' +import { TokenAmount } from '@cow/common/pure/TokenAmount' export interface LimitOrdersConfirmModalProps { isOpen: boolean @@ -38,18 +38,8 @@ function PendingText({ inputRawAmount: CurrencyAmount | null outputRawAmount: CurrencyAmount | null }) { - const inputSymbol = inputRawAmount?.currency?.symbol - const outputSymbol = outputRawAmount?.currency?.symbol - const inputTitle = ( - - {formatSmartAmount(inputRawAmount)} {inputSymbol} - - ) - const outputTitle = ( - - {formatSmartAmount(outputRawAmount)} {outputSymbol} - - ) + const inputTitle = + const outputTitle = return ( <> Placing limit order {inputTitle} for {outputTitle} diff --git a/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx b/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx index 0c8fe8b6df..d10110e86c 100644 --- a/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx +++ b/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx @@ -1,13 +1,12 @@ // Code based on https://github.com/cowprotocol/explorer/blob/develop/src/components/orders/FilledProgress/index.tsx -import { useMemo } from 'react' +import React, { useMemo } from 'react' import * as styledEl from './styled' import { ParsedOrder } from '@cow/modules/limitOrders/containers/OrdersWidget/hooks/useLimitOrdersList' -import { formatSmartAmount } from '@cow/utils/format' import { CurrencyAmount, Token } from '@uniswap/sdk-core' import { OrderKind } from '@cowprotocol/contracts' import { BigNumber } from 'bignumber.js' import JSBI from 'jsbi' -import { TokenSymbol } from '@cow/common/pure/TokenSymbol' +import { TokenAmount } from '@cow/common/pure/TokenAmount' interface Props { order: ParsedOrder @@ -15,6 +14,11 @@ interface Props { buyAmount: CurrencyAmount } +// TODO: using .toNumber() we potentially lose accuracy +function LegacyBigNumberToCurrencyAmount(currency: Token, value: BigNumber | undefined): CurrencyAmount { + return CurrencyAmount.fromRawAmount(currency, (value?.toNumber() || 0) * 10 ** currency.decimals) +} + export function FilledField({ order, sellAmount, buyAmount }: Props) { const { inputToken, @@ -22,8 +26,6 @@ export function FilledField({ order, sellAmount, buyAmount }: Props) { filledPercentage, fullyFilled, kind, - sellToken, - buyToken, feeAmount, executedBuyAmount, executedSellAmount, @@ -34,23 +36,20 @@ export function FilledField({ order, sellAmount, buyAmount }: Props) { const touched = !!filledPercentage?.gt(0) let mainToken: Token - let mainAddress: string let mainAmount: CurrencyAmount let swappedToken: Token - let swappedAddress: string let swappedAmount: JSBI | undefined let action: string + // TODO: set types, move calculations logic to a function let filledAmountWithFee, swappedAmountWithFee if (kind === OrderKind.SELL) { action = 'sold' mainToken = inputToken - mainAddress = sellToken mainAmount = sellAmount.add(CurrencyAmount.fromRawAmount(mainToken, feeAmount.toString())) swappedToken = outputToken - swappedAddress = buyToken swappedAmount = executedBuyAmount // Sell orders, add the fee in to the sellAmount (mainAmount, in this case) @@ -60,11 +59,9 @@ export function FilledField({ order, sellAmount, buyAmount }: Props) { action = 'bought' mainToken = outputToken - mainAddress = buyToken mainAmount = buyAmount swappedToken = inputToken - swappedAddress = sellToken swappedAmount = executedSellAmount // Buy orders need to add the fee, to the sellToken too (swappedAmount in this case) @@ -72,17 +69,13 @@ export function FilledField({ order, sellAmount, buyAmount }: Props) { swappedAmountWithFee = new BigNumber(swappedAmount?.toString() || '0').plus(executedFeeAmount || '0') } - // In case the token object is empty, display the address - const mainSymbol = mainToken ? mainToken.symbol : mainAddress - const swappedSymbol = swappedToken ? swappedToken.symbol : swappedAddress // In case the token object is empty, display the raw amount (`decimals || 0` part) - const formattedMainAmount = formatSmartAmount(mainAmount) const filledAmountDecimal = filledAmountWithFee?.div(new BigNumber(10 ** mainToken.decimals)) - const formattedFilledAmount = formatSmartAmount(filledAmountDecimal) + const formattedFilledAmount = LegacyBigNumberToCurrencyAmount(mainToken, filledAmountDecimal) - const swappedAmountDecimal = swappedAmountWithFee?.div(new BigNumber(10 ** swappedToken.decimals)) - const formattedSwappedAmount = formatSmartAmount(swappedAmountDecimal) + const swappedAmountDecimal = swappedAmountWithFee.div(new BigNumber(10 ** swappedToken.decimals)) + const formattedSwappedAmount = LegacyBigNumberToCurrencyAmount(mainToken, swappedAmountDecimal) const formattedPercentage = useMemo(() => { if (!filledPercentage) { @@ -100,16 +93,16 @@ export function FilledField({ order, sellAmount, buyAmount }: Props) { - + {/* Executed part (bought/sold tokens) */} - {formattedFilledAmount} + {' '} {!fullyFilled && ( // Show the total amount to buy/sell. Only for orders that are not 100% executed <> of{' '} - - {formattedMainAmount} + + {' '} )} @@ -120,8 +113,8 @@ export function FilledField({ order, sellAmount, buyAmount }: Props) { // Total sell tokens you pay (for buy orders) <> for a total of{' '} - - {formattedSwappedAmount} + + )} diff --git a/src/cow-react/modules/swap/pure/EthFlow/WrappingPreview/WrapCard.tsx b/src/cow-react/modules/swap/pure/EthFlow/WrappingPreview/WrapCard.tsx index 6a286da456..f09e83a046 100644 --- a/src/cow-react/modules/swap/pure/EthFlow/WrappingPreview/WrapCard.tsx +++ b/src/cow-react/modules/swap/pure/EthFlow/WrappingPreview/WrapCard.tsx @@ -1,12 +1,12 @@ import { CurrencyAmount, Currency } from '@uniswap/sdk-core' import styled from 'styled-components/macro' import CurrencyLogo from 'components/CurrencyLogo' -import { formatSmartAmount } from '@cow/utils/format' import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo' import { CHAIN_INFO } from 'constants/chainInfo' import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens' import * as styledEl from '@cow/modules/swap/pure/EthFlow/WrappingPreview/styled' +import { TokenAmount } from '@cow/common/pure/TokenAmount' const BackupTokenImg = styled.img.attrs((attrs) => ({ ...attrs, width: '24px' }))` filter: invert(1); @@ -21,7 +21,6 @@ interface WrapCardProps { export function WrapCard(props: WrapCardProps) { const { balance, amountToWrap, currency, chainId } = props - const symbol = currency.symbol const hasLogoUri = currency.isNative || Boolean(currency instanceof WrappedTokenInfo && currency.logoURI) return ( @@ -35,11 +34,13 @@ export function WrapCard(props: WrapCardProps) { {/* amount to wrap/unwrap */} - {formatSmartAmount(amountToWrap) || '-'} {symbol} + {/* user balance */} - Balance: {formatSmartAmount(balance) || '-'} + + Balance: + ) } diff --git a/src/cow-react/utils/format.ts b/src/cow-react/utils/format.ts index ac8e58b5f1..1cfee33002 100644 --- a/src/cow-react/utils/format.ts +++ b/src/cow-react/utils/format.ts @@ -3,7 +3,6 @@ import BigNumber from 'bignumber.js' import { formatSmart as _formatSmart } from '@cowprotocol/cow-js' import { Currency, CurrencyAmount, Percent, Fraction } from '@uniswap/sdk-core' import { - AMOUNT_PRECISION, DEFAULT_DECIMALS, DEFAULT_PRECISION, DEFAULT_SMALL_LIMIT, @@ -129,15 +128,6 @@ export function formatSmart( }) } -/** - * @deprecated use cow-react/utils/amountFormat - */ -export function formatSmartAmount( - value: CurrencyAmount | Percent | BigNumber | Fraction | null | undefined -): string | undefined { - return formatSmart(value, AMOUNT_PRECISION) -} - /** * Formats Fraction with max precision * From ff3d36d5600e3bda05b0b12d9eb77241df533652 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Sun, 5 Feb 2023 20:26:17 +0600 Subject: [PATCH 15/42] Remove formatMax --- .../CurrencyInputPanel/CurrencyInputPanel.tsx | 2 +- .../CurrencyInputPanel/CurrencyPreview.tsx | 2 +- .../common/pure/FiatAmount/index.tsx | 11 +- .../swap/containers/Row/RowFee/index.tsx | 11 +- .../Row/RowReceivedAfterSlippage/index.tsx | 4 - .../index.cosmos.tsx | 1 - .../RowReceivedAfterSlippageContent/index.tsx | 5 +- src/cow-react/utils/format.ts | 38 +- .../CurrencyInputPanelMod.tsx | 513 +----------------- .../CurrencyInputPanel/FiatValue/index.tsx | 1 - .../components/CurrencyInputPanel/index.tsx | 36 -- src/custom/components/Tokens/BalanceCell.tsx | 7 +- .../components/Tokens/FiatBalanceCell.tsx | 7 +- .../components/Tokens/TokensTableRow.tsx | 14 +- .../components/swap/FeeInformationTooltip.tsx | 18 +- .../SwapModalHeader/SwapModalHeaderMod.tsx | 16 +- 16 files changed, 50 insertions(+), 636 deletions(-) delete mode 100644 src/custom/components/CurrencyInputPanel/FiatValue/index.tsx delete mode 100644 src/custom/components/CurrencyInputPanel/index.tsx diff --git a/src/cow-react/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx b/src/cow-react/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx index 512e4eb9d9..63757d8478 100644 --- a/src/cow-react/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx +++ b/src/cow-react/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx @@ -3,7 +3,7 @@ import * as styledEl from './styled' import { CurrencySelectButton } from '@cow/modules/swap/pure/CurrencySelectButton' import { Currency } from '@uniswap/sdk-core' import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal' -import { FiatValue } from 'components/CurrencyInputPanel/FiatValue' +import { FiatValue } from 'custom/components/CurrencyInputPanel/FiatValue/FiatValueMod' import { Trans } from '@lingui/macro' import { PriceImpact } from 'hooks/usePriceImpact' import { ReceiveAmount } from '@cow/modules/swap/pure/ReceiveAmount' diff --git a/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx b/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx index c969e859ea..86834e4a8e 100644 --- a/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx +++ b/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx @@ -1,7 +1,7 @@ import React from 'react' import * as styledEl from './styled' import { CurrencySelectButton } from '@cow/modules/swap/pure/CurrencySelectButton' -import { FiatValue } from 'components/CurrencyInputPanel/FiatValue' +import { FiatValue } from 'custom/components/CurrencyInputPanel/FiatValue/FiatValueMod' import { Trans } from '@lingui/macro' import { PriceImpact } from 'hooks/usePriceImpact' import { CurrencyInfo } from '@cow/common/pure/CurrencyInputPanel/types' diff --git a/src/cow-react/common/pure/FiatAmount/index.tsx b/src/cow-react/common/pure/FiatAmount/index.tsx index e4bd38e24e..f25ccb6504 100644 --- a/src/cow-react/common/pure/FiatAmount/index.tsx +++ b/src/cow-react/common/pure/FiatAmount/index.tsx @@ -1,8 +1,11 @@ import { formatFiatAmount } from '@cow/utils/amountFormat' import { FractionLike, Nullish } from '@cow/types' +import { FractionUtils } from '@cow/utils/fractionUtils' +import { LONG_PRECISION } from 'constants/index' export interface FiatAmountProps { amount: Nullish + accurate?: boolean defaultValue?: string className?: string } @@ -10,12 +13,14 @@ export interface FiatAmountProps { // TODO: remove after testing const highlight = !!localStorage.getItem('amountsRefactoring') -export function FiatAmount({ amount, defaultValue, className }: FiatAmountProps) { +export function FiatAmount({ amount, defaultValue, className, accurate = false }: FiatAmountProps) { const formattedAmount = formatFiatAmount(amount) + const title = FractionUtils.fractionLikeToExact(amount, LONG_PRECISION) + const accuracySymbol = accurate ? '' : '≈ ' return ( - - {formattedAmount ? '≈ $' : ''} + + {formattedAmount ? accuracySymbol + '$' : ''} {formattedAmount || defaultValue} ) diff --git a/src/cow-react/modules/swap/containers/Row/RowFee/index.tsx b/src/cow-react/modules/swap/containers/Row/RowFee/index.tsx index 291f104cf6..3e806a4af4 100644 --- a/src/cow-react/modules/swap/containers/Row/RowFee/index.tsx +++ b/src/cow-react/modules/swap/containers/Row/RowFee/index.tsx @@ -1,13 +1,14 @@ import { useMemo } from 'react' import { CurrencyAmount, Currency, TradeType, Token } from '@uniswap/sdk-core' -import { formatMax, formatSmart, formatSymbol } from '@cow/utils/format' +import { formatSymbol } from '@cow/utils/format' import TradeGp from 'state/swap/TradeGp' -import { AMOUNT_PRECISION, FIAT_PRECISION } from 'constants/index' import { RowFeeContent } from '@cow/modules/swap/pure/Row/RowFeeContent' import { RowWithShowHelpersProps } from '@cow/modules/swap/pure/Row/types' import { useIsEthFlow } from '@cow/modules/swap/hooks/useIsEthFlow' import useNativeCurrency from 'lib/hooks/useNativeCurrency' +import { formatFiatAmount, formatTokenAmount } from '@cow/utils/amountFormat' +import { FractionUtils } from '@cow/utils/fractionUtils' export const GASLESS_FEE_TOOLTIP_MSG = 'On CoW Swap you sign your order (hence no gas costs!). The fees are covering your gas costs already.' @@ -64,13 +65,13 @@ export function RowFee({ trade, fee, feeFiatValue, allowsOffchainSigning, showHe const props = useMemo(() => { const displayFee = realizedFee || fee const feeCurrencySymbol = displayFee?.currency.symbol || '-' - const smartFeeFiatValue = formatSmart(feeFiatValue, FIAT_PRECISION) - const smartFeeTokenValue = formatSmart(displayFee, AMOUNT_PRECISION) + const smartFeeFiatValue = formatFiatAmount(feeFiatValue) + const smartFeeTokenValue = formatTokenAmount(displayFee) const feeAmountWithCurrency = `${smartFeeTokenValue} ${formatSymbol(feeCurrencySymbol)} ${ isEthFLow ? ' + gas' : '' }` const feeToken = smartFeeTokenValue ? feeAmountWithCurrency : '🎉 Free!' - const fullDisplayFee = formatMax(displayFee, displayFee?.currency.decimals) || '-' + const fullDisplayFee = FractionUtils.fractionLikeToExact(displayFee) || '-' const includeGasMessage = allowsOffchainSigning && !isEthFLow ? ' (incl. gas costs)' : '' return { diff --git a/src/cow-react/modules/swap/containers/Row/RowReceivedAfterSlippage/index.tsx b/src/cow-react/modules/swap/containers/Row/RowReceivedAfterSlippage/index.tsx index 5be0a90df7..db5cb2448d 100644 --- a/src/cow-react/modules/swap/containers/Row/RowReceivedAfterSlippage/index.tsx +++ b/src/cow-react/modules/swap/containers/Row/RowReceivedAfterSlippage/index.tsx @@ -4,7 +4,6 @@ import { Percent, TradeType } from '@uniswap/sdk-core' import { RowReceivedAfterSlippageContent } from '@cow/modules/swap/pure/Row/RowReceivedAfterSlippageContent' import { Field } from 'state/swap/actions' -import { formatMax } from '@cow/utils/format' import TradeGp from 'state/swap/TradeGp' import { computeSlippageAdjustedAmounts } from 'utils/prices' import { RowWithShowHelpersProps } from '@cow/modules/swap/pure/Row/typings' @@ -28,9 +27,6 @@ export function RowReceivedAfterSlippage({ trade, allowedSlippage, showHelpers } get swapAmount() { return this.isExactIn ? this.slippageOut : this.slippageIn }, - get fullOutAmount() { - return formatMax(this.swapAmount, this.swapAmount?.currency.decimals) || '-' - }, }), [trade, allowedSlippage, slippageAdjustedAmounts, showHelpers] ) diff --git a/src/cow-react/modules/swap/pure/Row/RowReceivedAfterSlippageContent/index.cosmos.tsx b/src/cow-react/modules/swap/pure/Row/RowReceivedAfterSlippageContent/index.cosmos.tsx index 835a3da13d..8cfcbbf26c 100644 --- a/src/cow-react/modules/swap/pure/Row/RowReceivedAfterSlippageContent/index.cosmos.tsx +++ b/src/cow-react/modules/swap/pure/Row/RowReceivedAfterSlippageContent/index.cosmos.tsx @@ -30,7 +30,6 @@ const defaultProps: RowReceivedAfterSlippageProps & RowReceivedAfterSlippageCont showHelpers: true, allowedSlippage: new Percent(1, 100), isExactIn: true, - fullOutAmount: '100', swapAmount: trade.inputAmountWithFee, } diff --git a/src/cow-react/modules/swap/pure/Row/RowReceivedAfterSlippageContent/index.tsx b/src/cow-react/modules/swap/pure/Row/RowReceivedAfterSlippageContent/index.tsx index 6b46f03a23..7d288d17dc 100644 --- a/src/cow-react/modules/swap/pure/Row/RowReceivedAfterSlippageContent/index.tsx +++ b/src/cow-react/modules/swap/pure/Row/RowReceivedAfterSlippageContent/index.tsx @@ -13,13 +13,12 @@ import { TokenAmount } from '@cow/common/pure/TokenAmount' export interface RowReceivedAfterSlippageContentProps extends RowReceivedAfterSlippageProps { isExactIn: boolean - fullOutAmount: string swapAmount: CurrencyAmount | undefined styleProps?: RowStyleProps } export function RowReceivedAfterSlippageContent(props: RowReceivedAfterSlippageContentProps) { - const { trade, showHelpers, allowedSlippage, isExactIn, fullOutAmount, swapAmount, styleProps = {} } = props + const { trade, showHelpers, allowedSlippage, isExactIn, swapAmount, styleProps = {} } = props return ( @@ -37,7 +36,7 @@ export function RowReceivedAfterSlippageContent(props: RowReceivedAfterSlippageC )} - + diff --git a/src/cow-react/utils/format.ts b/src/cow-react/utils/format.ts index 1cfee33002..fdcf56bb7d 100644 --- a/src/cow-react/utils/format.ts +++ b/src/cow-react/utils/format.ts @@ -2,13 +2,7 @@ import BigNumber from 'bignumber.js' import { formatSmart as _formatSmart } from '@cowprotocol/cow-js' import { Currency, CurrencyAmount, Percent, Fraction } from '@uniswap/sdk-core' -import { - DEFAULT_DECIMALS, - DEFAULT_PRECISION, - DEFAULT_SMALL_LIMIT, - FULL_PRICE_PRECISION, - LONG_PRECISION, -} from 'constants/index' +import { DEFAULT_DECIMALS, DEFAULT_PRECISION, DEFAULT_SMALL_LIMIT, FULL_PRICE_PRECISION } from 'constants/index' interface FormatSmartOptions { thousandSeparator?: boolean @@ -128,36 +122,6 @@ export function formatSmart( }) } -/** - * Formats Fraction with max precision - * - * If value has less that `decimals` precision, show the value with 1 significant digit - * E.g.: - * Token decimals: `2`; value: `0.0014123` - * => `0.001` - * - * Token decimals: `5`; value: `0.0014123` - * => `0.00141` - * - * Token decimals: `10`; value: `412310.0014123` - * => `412310.0014123000` - * - * @param value - * @param decimals - * @deprecated use FractionUtils.fractionLikeToExact - */ -export function formatMax(value?: Fraction, decimals?: number): string | undefined { - if (!value) { - return - } - let amount = value.toFixed(decimals ?? LONG_PRECISION) - - if (+amount === 0) { - amount = value.toSignificant(1) - } - return amount -} - const DEFAULT_MAX_SYMBOL_LENGTH = 12 // TODO: move to another file diff --git a/src/custom/components/CurrencyInputPanel/CurrencyInputPanelMod.tsx b/src/custom/components/CurrencyInputPanel/CurrencyInputPanelMod.tsx index d3010239c1..740559e838 100644 --- a/src/custom/components/CurrencyInputPanel/CurrencyInputPanelMod.tsx +++ b/src/custom/components/CurrencyInputPanel/CurrencyInputPanelMod.tsx @@ -1,36 +1,9 @@ -import { Trans } from '@lingui/macro' -import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core' -import { Pair } from '@uniswap/v2-sdk' -import { useWeb3React } from '@web3-react/core' -import { AutoColumn } from 'components/Column' -import { LoadingOpacityContainer, loadingOpacityMixin } from 'components/Loader/styled' -import { lighten } from 'polished' -import { ReactNode, useCallback, useState } from 'react' -import { Lock } from 'react-feather' -import styled, { css } from 'styled-components/macro' -// import { formatCurrencyAmount } from 'utils/formatCurrencyAmount' - -import { ReactComponent as DropDown } from 'assets/images/dropdown.svg' -import useTheme from 'hooks/useTheme' -import { useCurrencyBalance } from 'state/connection/hooks' -import { ThemedText } from 'theme' -import { ButtonGray } from 'components/Button' -import CurrencyLogo, { StyledLogo } from 'components/CurrencyLogo' -import DoubleCurrencyLogo from 'components/DoubleLogo' +import { loadingOpacityMixin } from 'components/Loader/styled' +import styled from 'styled-components/macro' import { Input as NumericalInput } from 'components/NumericalInput' -import { RowBetween, RowFixed } from 'components/Row' -// import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal' -import { FiatValue } from 'components/CurrencyInputPanel/FiatValue' - -// MOD imports -import { WithClassName } from 'types' -import { formatMax, formatSmart } from '@cow/utils/format' -import { AMOUNT_PRECISION } from 'constants/index' import { FeeInformationTooltipWrapper } from 'components/swap/FeeInformationTooltip' -import { TextWrapper } from '@src/components/HoverInlineText' // mod -import { CurrencySearchModal } from '.' // mod -import { isSupportedChain } from 'utils/supportedChainId' //mod +// TODO: refactor these styles export const InputPanel = styled.div<{ hideInput?: boolean }>` ${({ theme }) => theme.flexColumnNoWrap} position: relative; @@ -42,19 +15,6 @@ export const InputPanel = styled.div<{ hideInput?: boolean }>` will-change: height; ` -const FixedContainer = styled.div` - width: 100%; - height: 100%; - position: absolute; - border-radius: 20px; - background-color: ${({ theme }) => theme.bg1}; - opacity: 0.95; - display: flex; - align-items: center; - justify-content: center; - z-index: 2; -` - export const Container = styled.div<{ hideInput: boolean; disabled?: boolean; showAux?: boolean }>` border-radius: ${({ hideInput, showAux = false }) => (showAux ? '20px 20px 0 0' : hideInput ? '16px' : '20px')}; border: 1px solid ${({ theme, hideInput }) => (hideInput ? ' transparent' : theme.bg2)}; @@ -145,473 +105,6 @@ export const AuxInformationContainer = styled(Container)<{ } ` -export const CurrencySelect = styled(ButtonGray)<{ visible: boolean; selected: boolean; hideInput?: boolean }>` - align-items: center; - background-color: ${({ selected, theme }) => (selected ? theme.bg1 : lighten(0.1, theme.bg1))}; - color: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)}; - cursor: pointer; - border-radius: 16px; - outline: none; - user-select: none; - border: none; - font-size: 24px; - font-weight: 500; - height: ${({ hideInput }) => (hideInput ? '2.8rem' : '2.4rem')}; - width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')}; - padding: 0 8px; - justify-content: space-between; - margin-right: ${({ hideInput }) => (hideInput ? '0' : '12px')}; - :focus, - :hover { - background-color: ${({ selected, theme }) => (selected ? theme.bg1 : lighten(0.1, theme.bg1))}; - } - visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')}; -` - -export const InputRow = styled.div<{ selected: boolean }>` - ${({ theme }) => theme.flexRowNoWrap} - align-items: center; - justify-content: space-between; - padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 0.75rem 1rem')}; -` - -export const LabelRow = styled.div` - ${({ theme }) => theme.flexRowNoWrap} - align-items: center; - color: ${({ theme }) => theme.text1}; - font-size: 0.75rem; - line-height: 1rem; - padding: 0 1rem 1rem; - span:hover { - cursor: pointer; - color: ${({ theme }) => lighten(0.2, theme.text2)}; - } -` - -const FiatRow = styled(LabelRow)` - justify-content: flex-end; -` - -export const Aligner = styled.span` - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; -` - -export const StyledDropDown = styled(DropDown)<{ selected: boolean }>` - margin: 0 0.25rem 0 0.35rem; - height: 35%; - - path { - stroke: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)}; - stroke-width: 1.5px; - } -` - -export const StyledTokenName = styled.span<{ active?: boolean }>` - ${({ active }) => (active ? ' margin: 0 0.25rem 0 0.25rem;' : ' margin: 0 0.25rem 0 0.25rem;')} - font-size: ${({ active }) => (active ? '18px' : '18px')}; -` - -export const StyledBalanceMax = styled.button<{ disabled?: boolean }>` - background-color: transparent; - border: none; - border-radius: 12px; - color: ${({ theme }) => theme.primary5}; - cursor: pointer; - font-size: 14px; - font-weight: 500; - margin-left: 0.25rem; - opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)}; - pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')}; - - :hover { - opacity: ${({ disabled }) => (!disabled ? 0.8 : 0.4)}; - } - - :focus { - outline: none; - } - - ${({ theme }) => theme.mediaWidth.upToExtraSmall` - margin-right: 0.5rem; - `}; -` - export const StyledNumericalInput = styled(NumericalInput)<{ $loading: boolean }>` ${loadingOpacityMixin} ` - -// mod - due to circular dependencies and lazy loading -// mod - this custom component has to be here rather than ./index.tsx -export const Wrapper = styled.div<{ selected: boolean; showLoader: boolean }>` - // CSS Override - - ${StyledTokenName} { - font-size: 19px; - } - - ${InputPanel} { - background: transparent; - color: ${({ theme }) => theme.currencyInput?.color}; - border-radius: none; - - ${({ theme }) => theme.mediaWidth.upToSmall` - flex-flow: column wrap; - - > div > div > input { - width: 100%; - text-align: left; - padding: 0; - margin: 20px 0 8px; - word-break: break-all; - } - `}; - - &:hover { - color: ${({ theme }) => theme.currencyInput?.color}; - } - } - - ${LabelRow} { - color: ${({ theme }) => theme.currencyInput?.color}; - - span:hover { - color: ${({ theme }) => theme.currencyInput?.color}; - } - } - - ${InputRow} { - background: transparent; - - ${({ theme }) => theme.mediaWidth.upToSmall` - flex-flow: column wrap; - padding: 1rem 1rem 0 1rem; - `}; - - > input, - > input::placeholder { - background: transparent; - color: inherit; - } - - > input::placeholder { - opacity: 0.5; - } - } - - ${StyledBalanceMax} { - color: ${({ theme }) => theme.primary4}; - - ${({ theme }) => theme.mediaWidth.upToSmall` - margin: 0 0 auto 0; - `}; - } - - ${Container} { - background-color: ${({ theme }) => theme.input?.bg1}; - border: none; - border-radius: 16px; - - &:hover { - } - } - - ${AuxInformationContainer} { - background-color: ${({ theme }) => lighten(0.0, theme.bg1 || theme.bg3)}; - border-top: none; - - &:hover { - background-color: ${({ theme }) => lighten(0.0, theme.bg1 || theme.bg3)}; - border-top: none; - } - } - - ${({ showLoader, theme }) => - showLoader && - css` - #swap-currency-output ${Container} { - position: relative; - display: inline-block; - - overflow: hidden; - &::before { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - transform: translateX(-100%); - ${theme.shimmer}; // shimmer effect - content: ''; - } - } - `} - - ${CurrencySelect} { - z-index: 2; - background: ${({ selected, theme }) => (selected ? theme.bg1 : theme.bg2)}; - color: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)}; - transition: background-color 0.2s ease-in-out; - - ${({ theme }) => theme.mediaWidth.upToSmall` - width: 100%; - `}; - - &:focus { - background-color: ${({ selected, theme }) => (selected ? theme.bg1 : theme.bg2)}; - } - &:hover { - background-color: ${({ selected, theme }) => (selected ? lighten(0.1, theme.bg1) : lighten(0.1, theme.bg2))}; - } - - path { - stroke: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)}; - stroke-width: 1.5px; - } - } - - ${RowBetween} { - color: ${({ theme }) => theme.currencyInput?.color}; - - ${({ theme }) => theme.mediaWidth.upToSmall` - flex-flow: column wrap; - `} - - > div > div > span, - > div { - color: ${({ theme }) => theme.currencyInput?.color}; - } - - // Balance Wrapper - > div:first-of-type { - ${({ theme }) => theme.mediaWidth.upToSmall` - margin: 10px 0 0; - width: 100%; - opacity: 0.75; - `} - } - - // USD estimation - > div:last-of-type { - ${({ theme }) => theme.mediaWidth.upToSmall` - order: -1; - width: 100%; - text-align: left; - justify-content: flex-start; - display: flex; - `} - } - - // Balance text - ${({ theme }) => theme.mediaWidth.upToSmall` - > div > div { - word-break: break-all; - } - `} - } - - ${StyledLogo} { - background: ${({ theme }) => theme.bg1}; - } - - // Reset the cursor for the FIAT estimate & price impact - ${TextWrapper} { - &:hover, - + span:hover { - cursor: initial; - } - } -` - -export interface CurrencyInputPanelProps extends WithClassName { - value: string - onUserInput: (value: string) => void - onMax?: () => void - showMaxButton: boolean - label?: ReactNode - onCurrencySelect?: (currency: Currency) => void - currency?: Currency | null - hideBalance?: boolean - pair?: Pair | null - hideInput?: boolean - otherCurrency?: Currency | null - fiatValue?: CurrencyAmount | null - priceImpact?: Percent - priceImpactLoading?: boolean - id: string - showCommonBases?: boolean - showCurrencyAmount?: boolean - disableNonToken?: boolean - renderBalance?: (amount: CurrencyAmount) => ReactNode - locked?: boolean - loading?: boolean - customBalanceText?: string - disableCurrencySelect?: boolean - balanceAmount?: CurrencyAmount -} - -export default function CurrencyInputPanel({ - value, - onUserInput, - onMax, - showMaxButton, - onCurrencySelect, - currency, - otherCurrency, - id, - showCommonBases, - showCurrencyAmount, - disableNonToken, - renderBalance, - fiatValue, - priceImpact, - priceImpactLoading, - balanceAmount, - hideBalance = false, - pair = null, // used for double token logo - hideInput = false, - locked = false, - loading = false, - label, - ...rest -}: CurrencyInputPanelProps) { - const [modalOpen, setModalOpen] = useState(false) - const { account, chainId } = useWeb3React() - const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined) || balanceAmount - const theme = useTheme() - - const handleDismissSearch = useCallback(() => { - setModalOpen(false) - }, [setModalOpen]) - - const chainAllowed = isSupportedChain(chainId) - - return ( - <> - - {locked && ( - - - - - The market price is outside your specified price range. Single-asset deposit only. - - - - )} - - - { - if (onCurrencySelect) { - setModalOpen(true) - } - }} - > - - - {pair ? ( - - - - ) : currency ? ( - - ) : null} - {pair ? ( - - {pair?.token0.symbol}:{pair?.token1.symbol} - - ) : ( - - {(currency && currency.symbol && currency.symbol.length > 20 - ? currency.symbol.slice(0, 4) + - '...' + - currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length) - : currency?.symbol) || Select a token} - - )} - - {onCurrencySelect && } - - - {!hideInput && ( - - )} - - {!hideInput && !hideBalance && currency !== undefined && ( - - - {account ? ( - - - {!hideBalance && currency && selectedCurrencyBalance ? ( - renderBalance ? ( - renderBalance(selectedCurrencyBalance) - ) : ( - - Balance: {formatSmart(selectedCurrencyBalance, AMOUNT_PRECISION) || '0'} {currency.symbol} - - ) - ) : null} - - {showMaxButton && selectedCurrencyBalance ? ( - - (Max) - - ) : null} - - ) : ( - - )} - - - - - - )} - - {onCurrencySelect && ( - - )} - - {/* Fee Information */} - {!!label && {label}} - - ) -} diff --git a/src/custom/components/CurrencyInputPanel/FiatValue/index.tsx b/src/custom/components/CurrencyInputPanel/FiatValue/index.tsx deleted file mode 100644 index fcc464f95d..0000000000 --- a/src/custom/components/CurrencyInputPanel/FiatValue/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { FiatValue } from './FiatValueMod' diff --git a/src/custom/components/CurrencyInputPanel/index.tsx b/src/custom/components/CurrencyInputPanel/index.tsx deleted file mode 100644 index 9eb8b26b7f..0000000000 --- a/src/custom/components/CurrencyInputPanel/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import styled from 'styled-components/macro' -import useLoadingWithTimeout from 'hooks/useLoadingWithTimeout' -import { useIsQuoteRefreshing } from 'state/price/hooks' - -import CurrencyInputPanelMod, { CurrencyInputPanelProps, Wrapper } from './CurrencyInputPanelMod' -import CurrencySearchModalUni from '@src/components/SearchModal/CurrencySearchModal' - -import { LONG_LOAD_THRESHOLD } from 'constants/index' - -export const CurrencySearchModal = styled(CurrencySearchModalUni)` - > [data-reach-dialog-content] { - max-width: 520px; - background-color: ${({ theme }) => theme.bg1}; - - ${({ theme }) => theme.mediaWidth.upToSmall` - width: 100%; - height: 100%; - max-height: 100%; - max-width: 100%; - border-radius: 0; - `} - } -` - -export function CurrencyInputPanel(props: CurrencyInputPanelProps) { - const { currency } = props - const isRefreshingQuote = useIsQuoteRefreshing() - const showLoader = useLoadingWithTimeout(isRefreshingQuote, LONG_LOAD_THRESHOLD) - return ( - - - - ) -} - -export default CurrencyInputPanel diff --git a/src/custom/components/Tokens/BalanceCell.tsx b/src/custom/components/Tokens/BalanceCell.tsx index 819541392f..46978b5005 100644 --- a/src/custom/components/Tokens/BalanceCell.tsx +++ b/src/custom/components/Tokens/BalanceCell.tsx @@ -1,9 +1,9 @@ import { Token, CurrencyAmount } from '@uniswap/sdk-core' import { useWeb3React } from '@web3-react/core' import { BalanceValue } from './styled' -import { formatSmart, formatMax } from '@cow/utils/format' import Loader from 'components/Loader' import useTheme from 'hooks/useTheme' +import { TokenAmount } from '@cow/common/pure/TokenAmount' type BalanceCellProps = { balance: CurrencyAmount | undefined @@ -12,7 +12,6 @@ type BalanceCellProps = { export default function BalanceCell({ balance }: BalanceCellProps) { const { account } = useWeb3React() const hasBalance = balance?.greaterThan(0) - const formattedBalance = formatSmart(balance) || 0 const theme = useTheme() if (!balance) { @@ -20,8 +19,8 @@ export default function BalanceCell({ balance }: BalanceCellProps) { } return ( - - {formattedBalance} + + ) } diff --git a/src/custom/components/Tokens/FiatBalanceCell.tsx b/src/custom/components/Tokens/FiatBalanceCell.tsx index b917bedb02..2c6ece74fb 100644 --- a/src/custom/components/Tokens/FiatBalanceCell.tsx +++ b/src/custom/components/Tokens/FiatBalanceCell.tsx @@ -3,8 +3,9 @@ import { Token, CurrencyAmount } from '@uniswap/sdk-core' import { useHigherUSDValue } from 'hooks/useStablecoinPrice' import { FIAT_PRECISION } from 'constants/index' import { BalanceValue, InfoCircle, FiatValue } from './styled' -import { formatMax, formatSmart } from '@cow/utils/format' +import { formatSmart } from '@cow/utils/format' import { MouseoverTooltip } from 'components/Tooltip' +import { FiatAmount } from '@cow/common/pure/FiatAmount' type FiatBalanceCellProps = { balance: CurrencyAmount | undefined @@ -19,9 +20,9 @@ export default function FiatBalanceCell({ balance }: FiatBalanceCellProps) { }) return ( - + {formattedFiatValue ? ( - $ {formattedFiatValue} + ) : ( $ 0.00 diff --git a/src/custom/components/Tokens/TokensTableRow.tsx b/src/custom/components/Tokens/TokensTableRow.tsx index 0779648a68..fd26866f20 100644 --- a/src/custom/components/Tokens/TokensTableRow.tsx +++ b/src/custom/components/Tokens/TokensTableRow.tsx @@ -12,7 +12,6 @@ import { TokenText, } from './styled' import FavouriteTokenButton from './FavouriteTokenButton' -import { formatMax, formatSmart } from '@cow/utils/format' import { ApprovalState, useApproveCallback } from 'hooks/useApproveCallback' import { OperationType } from 'components/TransactionConfirmationModal' import { useErrorModal } from 'hooks/useErrorMessageAndModal' @@ -20,7 +19,7 @@ import { CardsSpinner, ExtLink } from '@cow/pages/Account/styled' import usePrevious from 'hooks/usePrevious' import { useTokenAllowance } from 'hooks/useTokenAllowance' import { useWeb3React } from '@web3-react/core' -import { AMOUNT_PRECISION, GP_VAULT_RELAYER } from 'constants/index' +import { GP_VAULT_RELAYER } from 'constants/index' import BalanceCell from './BalanceCell' import FiatBalanceCell from './FiatBalanceCell' import Loader from 'components/Loader' @@ -33,6 +32,7 @@ import SVG from 'react-inlinesvg' import EtherscanImage from 'assets/cow-swap/etherscan-icon.svg' import { TokenSymbol } from '@cow/common/pure/TokenSymbol' import { useAreThereTokensWithSameSymbol } from '@cow/common/hooks/useAreThereTokensWithSameSymbol' +import { TokenAmount } from '@cow/common/pure/TokenAmount' type DataRowParams = { tokenData: Token @@ -138,11 +138,11 @@ const DataRow = ({ Approve all - - Approved: {formatSmart(currentAllowance, AMOUNT_PRECISION)} + + Approved:{' '} + + + ) diff --git a/src/custom/components/swap/FeeInformationTooltip.tsx b/src/custom/components/swap/FeeInformationTooltip.tsx index 66e1a357d7..0eb8134c7e 100644 --- a/src/custom/components/swap/FeeInformationTooltip.tsx +++ b/src/custom/components/swap/FeeInformationTooltip.tsx @@ -3,12 +3,13 @@ import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core' import TradeGp from 'state/swap/TradeGp' import QuestionHelper from 'components/QuestionHelper' import styled from 'styled-components/macro' -import { formatMax, formatSmart } from '@cow/utils/format' +import { formatSmart } from '@cow/utils/format' import useTheme from 'hooks/useTheme' -import { AMOUNT_PRECISION, FIAT_PRECISION } from 'constants/index' +import { AMOUNT_PRECISION } from 'constants/index' import useCowBalanceAndSubsidy from 'hooks/useCowBalanceAndSubsidy' import { useIsEthFlow } from '@cow/modules/swap/hooks/useIsEthFlow' import { TokenSymbol } from '@cow/common/pure/TokenSymbol' +import { FiatAmount } from '@cow/common/pure/FiatAmount' interface FeeInformationTooltipProps { trade?: TradeGp @@ -124,9 +125,9 @@ export default function FeeInformationTooltip(props: FeeInformationTooltipProps) const { subsidy } = useCowBalanceAndSubsidy() - const [symbol, fullFeeAmount] = useMemo(() => { + const symbol = useMemo(() => { const amount = trade?.[type === 'From' ? 'inputAmount' : 'outputAmount'] - return amount ? [amount.currency.symbol || '', formatMax(amount, amount.currency.decimals) || '-'] : [] + return amount?.currency.symbol }, [trade, type]) if (!trade || !showHelper) return null @@ -164,8 +165,13 @@ export default function FeeInformationTooltip(props: FeeInformationTooltipProps) } /> - - {amountAfterFees} {showFiat && fiatValue && ≈ ${formatSmart(fiatValue, FIAT_PRECISION)}} + + {amountAfterFees}{' '} + {showFiat && fiatValue && ( + + + + )} ) diff --git a/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx b/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx index 8d94cbcbe8..45d7c35c88 100644 --- a/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx +++ b/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx @@ -25,7 +25,6 @@ import TradeGp from 'state/swap/TradeGp' import { INPUT_OUTPUT_EXPLANATION } from 'constants/index' import { computeSlippageAdjustedAmounts } from 'utils/prices' import { Field } from 'state/swap/actions' -import { formatMax } from '@cow/utils/format' import { AuxInformationContainer } from 'components/CurrencyInputPanel/CurrencyInputPanelMod' import FeeInformationTooltip from '../FeeInformationTooltip' import { LightCardType } from '.' @@ -132,9 +131,6 @@ SwapModalHeaderProps) { ] }, [trade]) - const fullInputWithoutFee = formatMax(trade?.inputAmountWithoutFee, trade?.inputAmount.currency.decimals) || '-' - const fullOutputWithoutFee = formatMax(trade?.outputAmountWithoutFee, trade?.outputAmount.currency.decimals) || '-' - return ( - + @@ -228,11 +220,7 @@ SwapModalHeaderProps) { - + {} From 2f79b6c603b4108715209dcd4e84e517fd0ef08f Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Sun, 5 Feb 2023 20:45:40 +0600 Subject: [PATCH 16/42] Remove formatSmart --- .../limitOrders/pure/Orders/OrderRow.tsx | 4 +- .../pure/ReceiptModal/CurrencyField.tsx | 6 +- .../pure/ReceiptModal/FeeField.tsx | 10 +- .../pure/ReceiptModal/PriceField.tsx | 7 +- .../pure/ReceiptModal/SurplusField.tsx | 6 +- .../swap/containers/Row/RowSlippage/index.tsx | 5 +- src/cow-react/utils/format.ts | 124 ------------------ .../Transaction/ActivityDetails.tsx | 13 +- .../FiatValue/FiatValueMod.tsx | 13 +- src/custom/components/Header/index.tsx | 7 +- .../CurrencyList/CurrencyListMod.tsx | 11 +- .../SearchModal/CurrencyList/index.tsx | 11 +- .../components/Tokens/FiatBalanceCell.tsx | 8 +- .../components/swap/FeeInformationTooltip.tsx | 8 +- src/custom/hooks/useWrapCallback.ts | 7 +- src/custom/state/orders/updaters/utils.ts | 10 +- src/custom/utils/tooltips.ts | 4 +- src/custom/utils/trade.ts | 9 +- 18 files changed, 64 insertions(+), 199 deletions(-) diff --git a/src/cow-react/modules/limitOrders/pure/Orders/OrderRow.tsx b/src/cow-react/modules/limitOrders/pure/Orders/OrderRow.tsx index 9480b40be2..767ae29bca 100644 --- a/src/cow-react/modules/limitOrders/pure/Orders/OrderRow.tsx +++ b/src/cow-react/modules/limitOrders/pure/Orders/OrderRow.tsx @@ -1,5 +1,4 @@ import { useContext } from 'react' -import { formatSmart } from '@cow/utils/format' import styled, { DefaultTheme, StyledComponent, ThemeContext } from 'styled-components/macro' import { Order, OrderStatus } from 'state/orders/actions' import { Currency, CurrencyAmount } from '@uniswap/sdk-core' @@ -13,6 +12,7 @@ import { getSellAmountWithFee } from '@cow/modules/limitOrders/utils/getSellAmou import AlertTriangle from 'assets/cow-swap/alert.svg' import SVG from 'react-inlinesvg' import { TokenSymbol } from '@cow/common/pure/TokenSymbol' +import { TokenAmount } from '@cow/common/pure/TokenAmount' export const orderStatusTitleMap: { [key in OrderStatus]: string } = { [OrderStatus.PENDING]: 'Open', @@ -163,7 +163,7 @@ function CurrencyAmountItem({ amount }: { amount: CurrencyAmount }) {
- {formatSmart(amount)} + ) diff --git a/src/cow-react/modules/limitOrders/pure/ReceiptModal/CurrencyField.tsx b/src/cow-react/modules/limitOrders/pure/ReceiptModal/CurrencyField.tsx index 3e6e5dd582..681b3c1d66 100644 --- a/src/cow-react/modules/limitOrders/pure/ReceiptModal/CurrencyField.tsx +++ b/src/cow-react/modules/limitOrders/pure/ReceiptModal/CurrencyField.tsx @@ -1,7 +1,7 @@ import { CurrencyAmount, Token } from '@uniswap/sdk-core' import * as styledEl from './styled' import { CurrencySelectButton } from '@cow/modules/swap/pure/CurrencySelectButton' -import { formatSmart } from '@cow/utils/format' +import { TokenAmount } from '@cow/common/pure/TokenAmount' interface Props { label: string @@ -16,7 +16,9 @@ export function CurrencyField({ label, token, amount }: Props) {
- {formatSmart(amount)} + + +
) diff --git a/src/cow-react/modules/limitOrders/pure/ReceiptModal/FeeField.tsx b/src/cow-react/modules/limitOrders/pure/ReceiptModal/FeeField.tsx index 5884918219..589dde39aa 100644 --- a/src/cow-react/modules/limitOrders/pure/ReceiptModal/FeeField.tsx +++ b/src/cow-react/modules/limitOrders/pure/ReceiptModal/FeeField.tsx @@ -1,8 +1,7 @@ import { ParsedOrder } from '@cow/modules/limitOrders/containers/OrdersWidget/hooks/useLimitOrdersList' -import { formatSmart } from '@cow/utils/format' import { CurrencyAmount } from '@uniswap/sdk-core' import * as styledEl from './styled' -import { TokenSymbol } from '@cow/common/pure/TokenSymbol' +import { TokenAmount } from '@cow/common/pure/TokenAmount' export type Props = { order: ParsedOrder } @@ -13,16 +12,15 @@ export function FeeField({ order }: Props): JSX.Element | null { // TODO: use the value from SDK const totalFee = CurrencyAmount.fromRawAmount(inputToken, (executedSurplusFee ?? executedFeeAmount) || 0) - const formattedExecutedFee = formatSmart(totalFee) const quoteSymbol = inputToken.symbol return ( - {!quoteSymbol || !formattedExecutedFee ? ( + {!quoteSymbol || !totalFee ? ( - ) : ( - - {formattedExecutedFee} + + )} diff --git a/src/cow-react/modules/limitOrders/pure/ReceiptModal/PriceField.tsx b/src/cow-react/modules/limitOrders/pure/ReceiptModal/PriceField.tsx index 541e27c91d..8c74e0c0ac 100644 --- a/src/cow-react/modules/limitOrders/pure/ReceiptModal/PriceField.tsx +++ b/src/cow-react/modules/limitOrders/pure/ReceiptModal/PriceField.tsx @@ -1,8 +1,8 @@ import * as styledEl from './styled' import { ParsedOrder } from '@cow/modules/limitOrders/containers/OrdersWidget/hooks/useLimitOrdersList' import { Fraction } from '@uniswap/sdk-core' -import { formatSmart } from '@cow/utils/format' import { TokenSymbol } from '@cow/common/pure/TokenSymbol' +import { TokenAmount } from '@cow/common/pure/TokenAmount' interface Props { order: ParsedOrder @@ -14,10 +14,7 @@ export function PriceField({ order, price }: Props) { {price ? ( - 1 {} ={' '} - - {formatSmart(price)} - + 1 {} = ) : ( '-' diff --git a/src/cow-react/modules/limitOrders/pure/ReceiptModal/SurplusField.tsx b/src/cow-react/modules/limitOrders/pure/ReceiptModal/SurplusField.tsx index b4b8d9d890..8c49841307 100644 --- a/src/cow-react/modules/limitOrders/pure/ReceiptModal/SurplusField.tsx +++ b/src/cow-react/modules/limitOrders/pure/ReceiptModal/SurplusField.tsx @@ -1,9 +1,8 @@ import { ParsedOrder } from '@cow/modules/limitOrders/containers/OrdersWidget/hooks/useLimitOrdersList' import { OrderKind } from 'state/orders/actions' import * as styledEl from './styled' -import { formatSmart } from '@cow/utils/format' import { CurrencyAmount } from '@uniswap/sdk-core' -import { TokenSymbol } from '@cow/common/pure/TokenSymbol' +import { TokenAmount } from '@cow/common/pure/TokenAmount' interface Props { order: ParsedOrder @@ -19,7 +18,6 @@ export function SurplusField({ order }: Props) { } const parsedSurplus = CurrencyAmount.fromRawAmount(surplusToken, surplusAmount?.toNumber()) - const formattedSurplus = formatSmart(parsedSurplus) const formattedPercent = surplusPercentage?.multipliedBy(100)?.toFixed(2) return ( @@ -27,7 +25,7 @@ export function SurplusField({ order }: Props) { +{formattedPercent}% - {formattedSurplus} + diff --git a/src/cow-react/modules/swap/containers/Row/RowSlippage/index.tsx b/src/cow-react/modules/swap/containers/Row/RowSlippage/index.tsx index 6efb5b9ab2..677843aa04 100644 --- a/src/cow-react/modules/swap/containers/Row/RowSlippage/index.tsx +++ b/src/cow-react/modules/swap/containers/Row/RowSlippage/index.tsx @@ -1,12 +1,11 @@ import { useMemo } from 'react' import { Percent } from '@uniswap/sdk-core' -import { PERCENTAGE_PRECISION } from 'constants/index' import { useToggleSettingsMenu } from 'state/application/hooks' -import { formatSmart } from '@cow/utils/format' import { RowSlippageContent } from '@cow/modules/swap/pure/Row/RowSlippageContent' import { useIsEthFlow } from '@cow/modules/swap/hooks/useIsEthFlow' import { useDetectNativeToken } from '@cow/modules/swap/hooks/useDetectNativeToken' +import { formatTokenAmount } from '@cow/utils/amountFormat' export interface RowSlippageProps { allowedSlippage: Percent @@ -25,7 +24,7 @@ export function RowSlippage({ allowedSlippage, showSettingOnClick = true }: RowS symbols: [nativeCurrency.symbol], showSettingOnClick, allowedSlippage, - displaySlippage: `${formatSmart(allowedSlippage, PERCENTAGE_PRECISION)}%`, + displaySlippage: `${formatTokenAmount(allowedSlippage)}%`, }), [allowedSlippage, nativeCurrency, isEthFlow, showSettingOnClick] ) diff --git a/src/cow-react/utils/format.ts b/src/cow-react/utils/format.ts index fdcf56bb7d..1e8d8e0a3a 100644 --- a/src/cow-react/utils/format.ts +++ b/src/cow-react/utils/format.ts @@ -1,127 +1,3 @@ -import BigNumber from 'bignumber.js' - -import { formatSmart as _formatSmart } from '@cowprotocol/cow-js' -import { Currency, CurrencyAmount, Percent, Fraction } from '@uniswap/sdk-core' -import { DEFAULT_DECIMALS, DEFAULT_PRECISION, DEFAULT_SMALL_LIMIT, FULL_PRICE_PRECISION } from 'constants/index' - -interface FormatSmartOptions { - thousandSeparator?: boolean - smallLimit?: string - isLocaleAware?: boolean -} - -/** - * Gets/adjusts small limits based on the amount of decimals to show - * - * - When `smallLimit` is set, use that - * - When `decimalsToShow` < 1, return `1` - * - When `decimalsToShow` < `DEFAULT_PRECISION`, reduce `smallLimit` to match precision - * - Otherwise, use `DEFAULT_SMALL_LIMIT`, which matches `DEFAULT_PRECISION` - * - * @param smallLimit - * @param decimalsToShow - */ -function _buildSmallLimit(smallLimit: string | undefined, decimalsToShow: number): string { - if (smallLimit) { - // explicitly set, use that - return smallLimit - } - if (decimalsToShow < 1) { - // special case when there's no decimal to display - return '1' - } - if (decimalsToShow < DEFAULT_PRECISION) { - // showing less than default precision, adjust small limit to avoid something like: - // < 0.0000000001 when decimals to show =2 and the value is 0.00312 - return '0.' + '0'.repeat(decimalsToShow - 1) + '1' - } - // stick to default smallLimit (0.000001), which matches DEFAULT_PRECISION (6) - return DEFAULT_SMALL_LIMIT -} - -/** - * Gets/adjusts CurrencyAmount display amount and precision - * - * Additional adjustment might be required in case amount is smaller than 1 token atom. - * E.g.: - * Token decimals: `2`; value: `0.001` - * Without adjustment, we'll have `precision:2` and `amount:0`. - * This is formatted to `0`, which is not entirely true, but the formatter doesn't know there are more stuff. - * - * So we get the remainder of the division and add as many decimals as needed to precision: - * Remainder: `0.1`; extra decimals: `1` - * => amount: 1 - * => precision: precision + extra decimals => 2 + 1 => 3 - * - * When formatting, smallLimit will be set to 0.01, formatting the result as `< 0.01` - * - * @param value - */ -function _adjustCurrencyAmountPrecision(value: CurrencyAmount): { amount: string; precision: number } { - // Amount is in atoms, need to convert it to units by setting precision = token.decimals - let precision = value.currency.decimals - // Returns an integer value rounded down - let amount = value.quotient.toString() - - // If given amount is zero it means we have less that 1 atom - // Adjust the precision and amount to indicate value is >0, even though tiny - if (+amount === 0) { - const remainder = value.remainder.toSignificant(1) // get only the first digit of the remainder - // It can happen that remainder is `1`. - // I know, how can the rest of the division be 1 is quotient is 0? o.O - // Turns out the answer is rounding. - // Requesting toSignificant(1) can return `0` if the value is something like 0.9 - // For this reason, we only remove `0.` and increase the precision if necessary - let decimalPart = remainder - if (/^0\./.test(remainder)) { - decimalPart = remainder.slice(2) // drop `0.` part - precision += decimalPart.length // how many more digits do we have? add that to the precision - } - amount = decimalPart.replace(/^0+/, '') // remove potential leading zeros, precision already accounts for it - } - return { amount, precision } -} - -/** - * formatSmart - * @param value - * @param decimalsToShow - * @param options - * @returns string or undefined - * @deprecated use cow-react/utils/amountFormat - */ -export function formatSmart( - value: CurrencyAmount | Percent | BigNumber | Fraction | null | undefined, - decimalsToShow: number = DEFAULT_PRECISION, - options?: FormatSmartOptions -) { - if (!value) return - - let precision - let amount - let smallLimitPrecision - if (value instanceof CurrencyAmount) { - const adjustedValues = _adjustCurrencyAmountPrecision(value) - amount = adjustedValues.amount - precision = adjustedValues.precision - smallLimitPrecision = Math.min(decimalsToShow, precision ?? DEFAULT_DECIMALS) - } else { - // Amount is already at desired precision (e.g.: a price), just need to format it nicely - precision = 0 - amount = value.toFixed(FULL_PRICE_PRECISION) // To a large enough precision so very small values are not rounded to 0 - smallLimitPrecision = Math.min(decimalsToShow, DEFAULT_DECIMALS) - } - - return _formatSmart({ - amount, - precision, - decimals: decimalsToShow, - thousandSeparator: !!options?.thousandSeparator, - smallLimit: _buildSmallLimit(options?.smallLimit, smallLimitPrecision), - isLocaleAware: !!options?.isLocaleAware, - }) -} - const DEFAULT_MAX_SYMBOL_LENGTH = 12 // TODO: move to another file diff --git a/src/custom/components/AccountDetails/Transaction/ActivityDetails.tsx b/src/custom/components/AccountDetails/Transaction/ActivityDetails.tsx index e922a4315b..199a8680a7 100644 --- a/src/custom/components/AccountDetails/Transaction/ActivityDetails.tsx +++ b/src/custom/components/AccountDetails/Transaction/ActivityDetails.tsx @@ -1,9 +1,8 @@ -import React from 'react' +import React, { ReactNode } from 'react' import { CurrencyAmount } from '@uniswap/sdk-core' import { OrderStatus } from 'state/orders/actions' -import { formatSmart } from '@cow/utils/format' import { Summary, SummaryInner, @@ -28,7 +27,7 @@ import { RateInfoParams, RateInfo } from '@cow/common/pure/RateInfo' import { EthFlowStepper } from '@cow/modules/swap/containers/EthFlowStepper' import { StatusDetails } from './StatusDetails' import { useCancelOrder } from '@cow/common/hooks/useCancelOrder' -import { formatSymbol } from '@cow/utils/format' +import { TokenAmount } from '@cow/common/pure/TokenAmount' const DEFAULT_ORDER_SUMMARY = { from: '', @@ -136,8 +135,8 @@ function GnosisSafeTxDetails(props: { } interface OrderSummaryType { - from: string | undefined - to: string | undefined + from: ReactNode | undefined + to: ReactNode | undefined limitPrice: string | undefined executionPrice?: string | undefined validTo: string | undefined @@ -218,8 +217,8 @@ export function ActivityDetails(props: { orderSummary = { ...DEFAULT_ORDER_SUMMARY, - from: `${formatSmart(inputAmount.add(feeAmount))} ${formatSymbol(inputAmount.currency.symbol)}`, - to: `${formatSmart(outputAmount)} ${formatSymbol(outputAmount.currency.symbol)}`, + from: , + to: , validTo: validTo ? new Date((validTo as number) * 1000).toLocaleString(undefined, DateFormatOptions) : undefined, fulfillmentTime: fulfillmentTime ? new Date(fulfillmentTime).toLocaleString(undefined, DateFormatOptions) diff --git a/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx b/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx index 914ce56ae1..ece012e39d 100644 --- a/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx +++ b/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx @@ -11,9 +11,9 @@ import { warningSeverity } from 'utils/prices' import { MouseoverTooltip } from 'components/Tooltip' // MOD imports -import { FIAT_PRECISION, PERCENTAGE_PRECISION } from 'constants/index' // mod -import { formatSmart } from '@cow/utils/format' +import { PERCENTAGE_PRECISION } from 'constants/index' // mod import Loader from 'components/Loader' +import { formatAmountWithPrecision, formatFiatAmount } from '@cow/utils/amountFormat' export function FiatValue({ fiatValue, @@ -56,12 +56,7 @@ export function FiatValue({ {fiatValue && !isLoading ? ( ≈ $ - + ) : ( '' @@ -70,7 +65,7 @@ export function FiatValue({ {' '} - ({formatSmart(priceImpact.multiply(-1), PERCENTAGE_PRECISION)}%) + ({formatAmountWithPrecision(priceImpact.multiply(-1), PERCENTAGE_PRECISION)}%) ) : null} diff --git a/src/custom/components/Header/index.tsx b/src/custom/components/Header/index.tsx index 894a81ad01..55d1761688 100644 --- a/src/custom/components/Header/index.tsx +++ b/src/custom/components/Header/index.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback, useMemo } from 'react' +import React, { useState, useEffect, useCallback, useMemo } from 'react' import { SupportedChainId as ChainId } from 'constants/chains' import { Routes } from '@cow/constants/routes' import { useNavigate } from 'react-router-dom' @@ -6,10 +6,8 @@ import { useWeb3React } from '@web3-react/core' import { useNativeCurrencyBalances } from 'state/connection/hooks' import { useDarkModeManager } from 'state/user/hooks' import { useMediaQuery, upToSmall, upToMedium, upToLarge, LargeAndUp } from 'hooks/useMediaQuery' -import { AMOUNT_PRECISION } from 'constants/index' import { supportedChainId } from 'utils/supportedChainId' -import { formatSmart } from '@cow/utils/format' import { addBodyClass, removeBodyClass } from 'utils/toggleBodyClass' // Components @@ -40,6 +38,7 @@ import { MAIN_MENU, MainMenuContext } from '@cow/modules/mainMenu' import { MenuTree } from '@cow/modules/mainMenu/pure/MenuTree' import { getDefaultTradeState } from '@cow/modules/trade/types/TradeState' import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { TokenAmount } from '@cow/common/pure/TokenAmount' export const NETWORK_LABELS: { [chainId in ChainId]?: string } = { // [ChainId.RINKEBY]: 'Rinkeby', @@ -157,7 +156,7 @@ export default function Header() { {account && userEthBalance && chainId && ( - {formatSmart(userEthBalance, AMOUNT_PRECISION) || '0'} {nativeToken} + )} diff --git a/src/custom/components/SearchModal/CurrencyList/CurrencyListMod.tsx b/src/custom/components/SearchModal/CurrencyList/CurrencyListMod.tsx index b1816df9ad..d5c050386f 100644 --- a/src/custom/components/SearchModal/CurrencyList/CurrencyListMod.tsx +++ b/src/custom/components/SearchModal/CurrencyList/CurrencyListMod.tsx @@ -6,7 +6,7 @@ import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent' import { LightGreyCard } from 'components/Card' import QuestionHelper from 'components/QuestionHelper' import useTheme from 'hooks/useTheme' -import { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react' +import React, { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react' import { FixedSizeList } from 'react-window' import { Text } from 'rebass' import styled from 'styled-components/macro' @@ -26,10 +26,9 @@ import { LoadingRows /*, MenuItem*/ } from 'components/SearchModal/styleds' // MOD imports import { MenuItem } from '.' // mod -import { formatSmart } from '@cow/utils/format' -import { AMOUNT_PRECISION } from 'constants/index' import { useIsUnsupportedTokenGp } from 'state/lists/hooks' import { TokenSymbol } from '@cow/common/pure/TokenSymbol' +import { TokenAmount } from '@cow/common/pure/TokenAmount' function currencyKey(currency: Currency): string { return currency.isToken ? currency.address : 'ETHER' @@ -65,7 +64,11 @@ export const FixedContentRow = styled.div` ` function Balance({ balance }: { balance: CurrencyAmount }) { - return {formatSmart(balance, AMOUNT_PRECISION)} + return ( + + + + ) } export const TagContainer = styled.div` diff --git a/src/custom/components/SearchModal/CurrencyList/index.tsx b/src/custom/components/SearchModal/CurrencyList/index.tsx index d41c44d9df..1f1b048940 100644 --- a/src/custom/components/SearchModal/CurrencyList/index.tsx +++ b/src/custom/components/SearchModal/CurrencyList/index.tsx @@ -1,6 +1,6 @@ import styled from 'styled-components/macro' import { Currency, CurrencyAmount } from '@uniswap/sdk-core' -import { LONG_PRECISION, UNSUPPORTED_TOKENS_FAQ_URL } from 'constants/index' +import { UNSUPPORTED_TOKENS_FAQ_URL } from 'constants/index' import CurrencyListMod, { StyledBalanceText, Tag as TagMod, TagContainer } from './CurrencyListMod' import { StyledLogo } from 'components/CurrencyLogo' import { MouseoverTooltip } from 'components/Tooltip' @@ -10,10 +10,11 @@ import { HashLink } from 'react-router-hash-link' // eslint-disable-next-line no-restricted-imports import { t } from '@lingui/macro' import { TagInfo } from 'state/lists/wrappedTokenInfo' -import { formatSmart } from '@cow/utils/format' import Column from 'components/Column' import { MenuItem as MenuItemMod } from '@src/components/SearchModal/styleds' import { transparentize } from 'polished' +import { TokenAmount } from '@cow/common/pure/TokenAmount' +import React from 'react' const UNSUPPORTED_TOKEN_TAG = [ { @@ -145,7 +146,11 @@ function TokenTags({ /* currency, */ isUnsupported }: { /* currency: Currency; * } export function Balance({ balance }: { balance: CurrencyAmount }) { - return {formatSmart(balance, LONG_PRECISION) || '0'} + return ( + + + + ) } export default function CurrencyList( diff --git a/src/custom/components/Tokens/FiatBalanceCell.tsx b/src/custom/components/Tokens/FiatBalanceCell.tsx index 2c6ece74fb..10431dd330 100644 --- a/src/custom/components/Tokens/FiatBalanceCell.tsx +++ b/src/custom/components/Tokens/FiatBalanceCell.tsx @@ -1,9 +1,7 @@ import { Trans } from '@lingui/macro' import { Token, CurrencyAmount } from '@uniswap/sdk-core' import { useHigherUSDValue } from 'hooks/useStablecoinPrice' -import { FIAT_PRECISION } from 'constants/index' import { BalanceValue, InfoCircle, FiatValue } from './styled' -import { formatSmart } from '@cow/utils/format' import { MouseoverTooltip } from 'components/Tooltip' import { FiatAmount } from '@cow/common/pure/FiatAmount' @@ -14,14 +12,10 @@ type FiatBalanceCellProps = { export default function FiatBalanceCell({ balance }: FiatBalanceCellProps) { const hasBalance = balance?.greaterThan(0) const fiatValue = useHigherUSDValue(balance) - const formattedFiatValue = formatSmart(fiatValue, FIAT_PRECISION, { - thousandSeparator: true, - isLocaleAware: true, - }) return ( - {formattedFiatValue ? ( + {fiatValue ? ( ) : ( diff --git a/src/custom/components/swap/FeeInformationTooltip.tsx b/src/custom/components/swap/FeeInformationTooltip.tsx index 0eb8134c7e..92fbb2a204 100644 --- a/src/custom/components/swap/FeeInformationTooltip.tsx +++ b/src/custom/components/swap/FeeInformationTooltip.tsx @@ -3,13 +3,13 @@ import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core' import TradeGp from 'state/swap/TradeGp' import QuestionHelper from 'components/QuestionHelper' import styled from 'styled-components/macro' -import { formatSmart } from '@cow/utils/format' import useTheme from 'hooks/useTheme' -import { AMOUNT_PRECISION } from 'constants/index' import useCowBalanceAndSubsidy from 'hooks/useCowBalanceAndSubsidy' import { useIsEthFlow } from '@cow/modules/swap/hooks/useIsEthFlow' import { TokenSymbol } from '@cow/common/pure/TokenSymbol' import { FiatAmount } from '@cow/common/pure/FiatAmount' +import { TokenAmount } from '@cow/common/pure/TokenAmount' +import { formatTokenAmount } from '@cow/utils/amountFormat' interface FeeInformationTooltipProps { trade?: TradeGp @@ -88,7 +88,7 @@ type FeeBreakdownProps = FeeInformationTooltipProps & { const FeeBreakdownLine = ({ feeAmount, discount, type, symbol }: FeeBreakdownProps) => { const typeString = type === 'From' ? '+' : '-' - const smartFee = formatSmart(feeAmount, AMOUNT_PRECISION) + const smartFee = formatTokenAmount(feeAmount) return ( @@ -96,7 +96,7 @@ const FeeBreakdownLine = ({ feeAmount, discount, type, symbol }: FeeBreakdownPro {smartFee ? ( {typeString} - {smartFee} + ) : ( Free diff --git a/src/custom/hooks/useWrapCallback.ts b/src/custom/hooks/useWrapCallback.ts index f9241c0006..d733b8206a 100644 --- a/src/custom/hooks/useWrapCallback.ts +++ b/src/custom/hooks/useWrapCallback.ts @@ -7,8 +7,8 @@ import { BigNumber } from '@ethersproject/bignumber' import { TransactionResponse } from '@ethersproject/providers' import { Contract } from '@ethersproject/contracts' import { getChainCurrencySymbols } from 'utils/gnosis_chain/hack' -import { AMOUNT_PRECISION, RADIX_HEX } from 'constants/index' -import { formatSmart, formatSymbol } from '@cow/utils/format' +import { RADIX_HEX } from 'constants/index' +import { formatSymbol } from '@cow/utils/format' import { getOperationMessage, OperationType } from '../components/TransactionConfirmationModal' import { calculateGasMargin } from 'utils/calculateGasMargin' import { isRejectRequestProviderError } from '../utils/misc' @@ -19,6 +19,7 @@ import { useWeb3React } from '@web3-react/core' import { useCurrencyBalance } from 'state/connection/hooks' import { useTransactionConfirmModal } from '@cow/modules/swap/hooks/useTransactionConfirmModal' import { useDetectNativeToken } from '@cow/modules/swap/hooks/useDetectNativeToken' +import { formatTokenAmount } from '@cow/utils/amountFormat' // Use a 180K gas as a fallback if there's issue calculating the gas estimation (fixes some issues with some nodes failing to calculate gas costs for SC wallets) const WRAP_UNWRAP_GAS_LIMIT_DEFAULT = BigNumber.from('180000') @@ -115,7 +116,7 @@ export function useWrapUnwrapContext( const amountHex = `0x${inputAmount.quotient.toString(RADIX_HEX)}` const operationType = isWrap ? OperationType.WRAP_ETHER : OperationType.UNWRAP_WETH const baseSummarySuffix = isWrap ? `${native} to ${wrapped}` : `${wrapped} to ${native}` - const baseSummary = `${formatSmart(inputAmount, AMOUNT_PRECISION)} ${baseSummarySuffix}` + const baseSummary = `${formatTokenAmount(inputAmount)} ${baseSummarySuffix}` const summary = `${isWrap ? 'Wrap' : 'Unwrap'} ${baseSummary}` const confirmationMessage = `${isWrap ? 'Wrapping' : 'Unwrapping'} ${baseSummary}` const operationMessage = getOperationMessage(operationType, chainId) diff --git a/src/custom/state/orders/updaters/utils.ts b/src/custom/state/orders/updaters/utils.ts index 59c631a81c..5252e17317 100644 --- a/src/custom/state/orders/updaters/utils.ts +++ b/src/custom/state/orders/updaters/utils.ts @@ -1,10 +1,10 @@ import { Order, OrderFulfillmentData, OrderKind, OrderStatus } from 'state/orders/actions' import { getOrder, OrderID, OrderMetaData } from '@cow/api/gnosisProtocol' import { stringToCurrency } from 'state/swap/extension' -import { formatSmart, formatSymbol } from '@cow/utils/format' -import { AMOUNT_PRECISION } from 'constants/index' +import { formatSymbol } from '@cow/utils/format' import { classifyOrder, OrderTransitionStatus } from 'state/orders/utils' import { SupportedChainId as ChainId } from 'constants/chains' +import { formatTokenAmount } from '@cow/utils/amountFormat' export type OrderLogPopupMixData = OrderFulfillmentData | OrderID @@ -38,11 +38,9 @@ export function computeOrderSummary({ const inputPrefix = !isFulfilled && kind === OrderKind.BUY ? 'at most ' : '' const outputPrefix = !isFulfilled && kind === OrderKind.SELL ? 'at least ' : '' - summary = `Swap ${inputPrefix}${formatSmart(inputAmount, AMOUNT_PRECISION)} ${formatSymbol( + summary = `Swap ${inputPrefix}${formatTokenAmount(inputAmount)} ${formatSymbol( inputAmount.currency.symbol - )} for ${outputPrefix}${formatSmart(outputAmount, AMOUNT_PRECISION)} ${formatSymbol( - outputAmount.currency.symbol - )}` + )} for ${outputPrefix}${formatTokenAmount(outputAmount)} ${formatSymbol(outputAmount.currency.symbol)}` } else { // We only have the API order info, let's at least use that summary = `Swap ${sellToken} for ${buyToken}` diff --git a/src/custom/utils/tooltips.ts b/src/custom/utils/tooltips.ts index d575d0b659..1bed905be1 100644 --- a/src/custom/utils/tooltips.ts +++ b/src/custom/utils/tooltips.ts @@ -1,13 +1,13 @@ import { Percent } from '@uniswap/sdk-core' import { PERCENTAGE_PRECISION } from '../constants' -import { formatSmart } from '@cow/utils/format' +import { formatAmountWithPrecision } from '@cow/utils/amountFormat' export function getMinimumReceivedTooltip(allowedSlippage: Percent, isExactIn: boolean): string { const slippagePercent = allowedSlippage return `${ isExactIn ? "Minimum tokens you'll receive." : "Maximum tokens you'll sell." - } This accounts for the current price and your chosen slippage (${formatSmart( + } This accounts for the current price and your chosen slippage (${formatAmountWithPrecision( slippagePercent, PERCENTAGE_PRECISION )}%). If the price moves beyond your slippage, you won't trade but also you won't pay any fees or gas.` diff --git a/src/custom/utils/trade.ts b/src/custom/utils/trade.ts index 2d3966e3e4..0001db037d 100644 --- a/src/custom/utils/trade.ts +++ b/src/custom/utils/trade.ts @@ -6,11 +6,12 @@ import { AddUnserialisedPendingOrderParams } from 'state/orders/hooks' import { signOrder, signOrderCancellation, UnsignedOrder } from 'utils/signatures' import { OrderID, sendOrder as sendOrderApi, sendSignedOrderCancellation } from '@cow/api/gnosisProtocol' import { Signer } from '@ethersproject/abstract-signer' -import { AMOUNT_PRECISION, RADIX_DECIMAL, NATIVE_CURRENCY_BUY_ADDRESS } from 'constants/index' +import { RADIX_DECIMAL, NATIVE_CURRENCY_BUY_ADDRESS } from 'constants/index' import { SupportedChainId as ChainId } from 'constants/chains' -import { formatSmart, formatSymbol } from '@cow/utils/format' +import { formatSymbol } from '@cow/utils/format' import { SigningScheme } from '@cowprotocol/contracts' import { getProfileData, getTrades } from '@cow/api/gnosisProtocol/api' +import { formatTokenAmount } from '@cow/utils/amountFormat' export type PostOrderParams = { account: string @@ -59,8 +60,8 @@ function _getSummary(params: PostOrderParams): string { ] const inputSymbol = formatSymbol(sellToken.symbol) const outputSymbol = formatSymbol(buyToken.symbol) - const inputAmountValue = formatSmart(feeAmount ? inputAmount.add(feeAmount) : inputAmount, AMOUNT_PRECISION) - const outputAmountValue = formatSmart(outputAmount, AMOUNT_PRECISION) + const inputAmountValue = formatTokenAmount(feeAmount ? inputAmount.add(feeAmount) : inputAmount) + const outputAmountValue = formatTokenAmount(outputAmount) const base = `Swap ${inputQuantifier}${inputAmountValue} ${inputSymbol} for ${outputQuantifier}${outputAmountValue} ${outputSymbol}` From 649792de975aaf1e563109d5df8aab5705317e03 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Sun, 5 Feb 2023 20:56:13 +0600 Subject: [PATCH 17/42] Remove @cowprotocol/cow-js --- package.json | 1 - src/cow-react/common/pure/RateInfo/index.tsx | 3 +- .../limitOrders/utils/getOrderFilledAmount.ts | 2 +- .../limitOrders/utils/getOrderSurplus.ts | 2 +- .../swap/containers/EthFlowStepper/index.tsx | 4 +- src/custom/constants/index.ts | 3 + src/custom/state/orders/priceUtils.ts | 55 +++++++++++++++++++ src/custom/state/orders/utils.ts | 4 +- yarn.lock | 10 +--- 9 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 src/custom/state/orders/priceUtils.ts diff --git a/package.json b/package.json index e8179170ac..64025fa607 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,6 @@ "@babel/runtime": "^7.17.0", "@coinbase/wallet-sdk": "^3.3.0", "@cowprotocol/contracts": "^1.3.1", - "@cowprotocol/cow-js": "^1.0.0-RC.0", "@cowprotocol/cow-runner-game": "^0.2.9", "@cowprotocol/cow-sdk": "^1.0.2-RC.0", "@cowprotocol/ethflowcontract": "cowprotocol/ethflowcontract.git#v1.0.0-artifacts", diff --git a/src/cow-react/common/pure/RateInfo/index.tsx b/src/cow-react/common/pure/RateInfo/index.tsx index 3568ab5ace..662267d163 100644 --- a/src/cow-react/common/pure/RateInfo/index.tsx +++ b/src/cow-react/common/pure/RateInfo/index.tsx @@ -8,11 +8,12 @@ import { SupportedChainId } from 'constants/chains' import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { usePrice } from '@cow/common/hooks/usePrice' import { transparentize } from 'polished' -import { DEFAULT_DECIMALS } from '@cowprotocol/cow-js' import { TokenSymbol } from '@cow/common/pure/TokenSymbol' import { TokenAmount } from '@cow/common/pure/TokenAmount' import { FiatAmount } from '@cow/common/pure/FiatAmount' +const DEFAULT_DECIMALS = 4 + export interface RateInfoParams { chainId: SupportedChainId | undefined inputCurrencyAmount: CurrencyAmount | null diff --git a/src/cow-react/modules/limitOrders/utils/getOrderFilledAmount.ts b/src/cow-react/modules/limitOrders/utils/getOrderFilledAmount.ts index 9a2ae71fa9..b33656f215 100644 --- a/src/cow-react/modules/limitOrders/utils/getOrderFilledAmount.ts +++ b/src/cow-react/modules/limitOrders/utils/getOrderFilledAmount.ts @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js' import { Order } from 'state/orders/actions' -import { ZERO_BIG_NUMBER } from '@cowprotocol/cow-js' +import { ZERO_BIG_NUMBER } from 'constants/index' /** * Get order filled amount, both as raw amount (in atoms) and as percentage (from 0 to 1) diff --git a/src/cow-react/modules/limitOrders/utils/getOrderSurplus.ts b/src/cow-react/modules/limitOrders/utils/getOrderSurplus.ts index 0dfd4e2aa9..248fe44243 100644 --- a/src/cow-react/modules/limitOrders/utils/getOrderSurplus.ts +++ b/src/cow-react/modules/limitOrders/utils/getOrderSurplus.ts @@ -1,6 +1,6 @@ // Util functions that only pertain to/deal with operator API related stuff import BigNumber from 'bignumber.js' -import { ZERO_BIG_NUMBER } from '@cowprotocol/cow-js' +import { ZERO_BIG_NUMBER } from 'constants/index' import { Order } from 'state/orders/actions' import { BigNumberish } from '@ethersproject/bignumber' import { getOrderExecutedAmounts } from './getOrderExecutedAmounts' diff --git a/src/cow-react/modules/swap/containers/EthFlowStepper/index.tsx b/src/cow-react/modules/swap/containers/EthFlowStepper/index.tsx index e90d452d39..ed24d45c3d 100644 --- a/src/cow-react/modules/swap/containers/EthFlowStepper/index.tsx +++ b/src/cow-react/modules/swap/containers/EthFlowStepper/index.tsx @@ -6,10 +6,10 @@ import { import { useDetectNativeToken } from '@cow/modules/swap/hooks/useDetectNativeToken' import { Order, OrderStatus } from 'state/orders/actions' import { NATIVE_CURRENCY_BUY_ADDRESS } from 'constants/index' -import { safeTokenName } from '@cowprotocol/cow-js' import { isOrderExpired } from 'state/orders/utils' import { useAllTransactions } from 'state/enhancedTransactions/hooks' import { EnhancedTransactionDetails } from 'state/enhancedTransactions/reducer' +import { formatSymbol } from '@cow/utils/format' type EthFlowStepperProps = { order: Order | undefined @@ -42,7 +42,7 @@ export function EthFlowStepper(props: EthFlowStepperProps) { const stepperProps: PureProps = { nativeTokenSymbol: native.symbol as string, - tokenLabel: safeTokenName(order.outputToken), + tokenLabel: formatSymbol(order.outputToken.symbol) || '', order: { orderId: order.id, state, diff --git a/src/custom/constants/index.ts b/src/custom/constants/index.ts index 4a6f80552b..04e4ab4a7c 100644 --- a/src/custom/constants/index.ts +++ b/src/custom/constants/index.ts @@ -1,3 +1,4 @@ +import BigNumber from 'bignumber.js' import { Token, Fraction, Percent } from '@uniswap/sdk-core' import ethFlowBarnJson from '@cowprotocol/ethflowcontract/networks.barn.json' import ethFlowProdJson from '@cowprotocol/ethflowcontract/networks.prod.json' @@ -14,6 +15,8 @@ const { GPv2Settlement, GPv2VaultRelayer } = networksJson const EthFlowBarn = ethFlowBarnJson.CoWSwapEthFlow const EthFlowProd = ethFlowProdJson.CoWSwapEthFlow +export const ZERO_BIG_NUMBER = new BigNumber(0) + export const DEFAULT_SLIPPAGE_BPS = 50 // 0.5% export const MAX_SLIPPAGE_BPS = 5000 // 50% export const MIN_SLIPPAGE_BPS = 0 // 0% diff --git a/src/custom/state/orders/priceUtils.ts b/src/custom/state/orders/priceUtils.ts new file mode 100644 index 0000000000..8a904257d9 --- /dev/null +++ b/src/custom/state/orders/priceUtils.ts @@ -0,0 +1,55 @@ +// Copied from @cowprotocol/cow-js + +import { DEFAULT_DECIMALS } from 'constants/index' +import { BigNumber } from 'bignumber.js' + +interface Token { + amount: BigNumber | string + decimals?: number +} + +interface CalculatePriceParams { + numerator: Token + denominator: Token +} + +export const ONE_BIG_NUMBER = new BigNumber(1) +export const TEN_BIG_NUMBER = new BigNumber(10) + +/** + * Calculates price from either BN, BigNumber or string amounts + * based on decimal precision of each part. + * + * Decimals defaults to 0. + * Use case is to calculate the price when values already in the same unit + * and no adjustment is required. + * + * Returns price in BigNumber and leave formatting up to the caller + */ +export function calculatePrice(params: CalculatePriceParams): BigNumber { + const { + numerator: { amount: numeratorAmount, decimals: numeratorDecimals = DEFAULT_DECIMALS }, + denominator: { amount: denominatorAmount, decimals: denominatorDecimals = DEFAULT_DECIMALS }, + } = params + + // convert to BigNumber + const numerator = new BigNumber(numeratorAmount.toString()) + const denominator = new BigNumber(denominatorAmount.toString()) + + if (numeratorDecimals >= denominatorDecimals) { + const precisionFactor = TEN_BIG_NUMBER.exponentiatedBy(numeratorDecimals - denominatorDecimals) + return numerator.dividedBy(denominator.multipliedBy(precisionFactor)) + } else { + const precisionFactor = TEN_BIG_NUMBER.exponentiatedBy(denominatorDecimals - numeratorDecimals) + return numerator.multipliedBy(precisionFactor).dividedBy(denominator) + } +} + +/** + * Convenience function to invert price + * + * @param price Price to be inverted as BigNumber + */ +export function invertPrice(price: BigNumber): BigNumber { + return ONE_BIG_NUMBER.div(price) +} diff --git a/src/custom/state/orders/utils.ts b/src/custom/state/orders/utils.ts index fd814b9f3a..d476cbb810 100644 --- a/src/custom/state/orders/utils.ts +++ b/src/custom/state/orders/utils.ts @@ -2,11 +2,11 @@ import { OrderKind } from '@cowprotocol/contracts' import { Price } from '@uniswap/sdk-core' import { ONE_HUNDRED_PERCENT } from 'constants/misc' -import { PENDING_ORDERS_BUFFER } from 'constants/index' +import { PENDING_ORDERS_BUFFER, ZERO_BIG_NUMBER } from 'constants/index' import { OrderMetaData } from '@cow/api/gnosisProtocol' import { Order } from 'state/orders/actions' import { OUT_OF_MARKET_PRICE_DELTA_PERCENTAGE } from 'state/orders/consts' -import { calculatePrice, invertPrice, ZERO_BIG_NUMBER } from '@cowprotocol/cow-js' +import { calculatePrice, invertPrice } from './priceUtils' import { BigNumber } from 'bignumber.js' import { PriceInformation } from '@cowprotocol/cow-sdk' diff --git a/yarn.lock b/yarn.lock index 5765fbd27a..4663fa980e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1171,14 +1171,6 @@ resolved "https://registry.yarnpkg.com/@cowprotocol/contracts/-/contracts-1.3.1.tgz#a812b27bdd0c046ab730eb24911ef7c641ed0df8" integrity sha512-p93xODog3XG5eSDU5od1ERvYf7YIyXd7USknKcsWnka5VN5mDDjqjCKTLw8EoZdrCIEpf6fGd8At2nv+MsvNqA== -"@cowprotocol/cow-js@^1.0.0-RC.0": - version "1.0.0-RC.0" - resolved "https://registry.yarnpkg.com/@cowprotocol/cow-js/-/cow-js-1.0.0-RC.0.tgz#24c3139f7ea1b7837966222f8160054361c3a1ff" - integrity sha512-cxr+0ubGjRh55Qzo926nBZUTNT310syRncpXT49Fbx3kyS8BsOAszGg8pW4mX05rULrpvfch2PiSY0eEWH6k8g== - dependencies: - bignumber.js "^9.0.2" - bn.js "^5.2.0" - "@cowprotocol/cow-runner-game@^0.2.9": version "0.2.9" resolved "https://registry.yarnpkg.com/@cowprotocol/cow-runner-game/-/cow-runner-game-0.2.9.tgz#3f94b3f370bd114f77db8b1d238cba3ef4e9d644" @@ -5989,7 +5981,7 @@ bignumber.js@8.1.1: resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-8.1.1.tgz#4b072ae5aea9c20f6730e4e5d529df1271c4d885" integrity sha512-QD46ppGintwPGuL1KqmwhR0O+N2cZUg8JG/VzwI2e28sM9TqHjQB10lI4QAaMHVbLzwVLLAwEglpKPViWX+5NQ== -bignumber.js@^9.0.0, bignumber.js@^9.0.2: +bignumber.js@^9.0.0: version "9.1.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== From 55e94069e5590102da16f2ed55b3986ae91de060 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Sun, 5 Feb 2023 21:50:29 +0600 Subject: [PATCH 18/42] Fix trimTrailingZeros --- src/cow-react/utils/trimTrailingZeros.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cow-react/utils/trimTrailingZeros.ts b/src/cow-react/utils/trimTrailingZeros.ts index cbc1142305..c9f1f9a632 100644 --- a/src/cow-react/utils/trimTrailingZeros.ts +++ b/src/cow-react/utils/trimTrailingZeros.ts @@ -1,5 +1,5 @@ const trailingZerosRegex = /(\.\d*?[1-9])0*$/ export function trimTrailingZeros(value: string): string { - return value.replace(trailingZerosRegex, '$1') + return value.replace(trailingZerosRegex, '$1').replace(/\.0*$/, '') } From 1dd32a3f8b73e0c8e87939f552b4e1e844b12a47 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Sun, 5 Feb 2023 21:55:21 +0600 Subject: [PATCH 19/42] Use FiatAmount component --- .../CurrencyInputPanel/FiatValue/FiatValueMod.tsx | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx b/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx index ece012e39d..db40c4cfe8 100644 --- a/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx +++ b/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx @@ -1,8 +1,6 @@ -import { Trans } from '@lingui/macro' // eslint-disable-next-line no-restricted-imports import { t } from '@lingui/macro' import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' -import HoverInlineText from 'components/HoverInlineText' import { useMemo } from 'react' import useTheme from 'hooks/useTheme' @@ -13,7 +11,8 @@ import { MouseoverTooltip } from 'components/Tooltip' // MOD imports import { PERCENTAGE_PRECISION } from 'constants/index' // mod import Loader from 'components/Loader' -import { formatAmountWithPrecision, formatFiatAmount } from '@cow/utils/amountFormat' +import { formatAmountWithPrecision } from '@cow/utils/amountFormat' +import { FiatAmount } from '@cow/common/pure/FiatAmount' export function FiatValue({ fiatValue, @@ -53,14 +52,7 @@ export function FiatValue({ return ( - {fiatValue && !isLoading ? ( - - ≈ $ - - - ) : ( - '' - )} + {fiatValue && !isLoading ? : ''} {priceImpact ? ( {' '} From 4e180ffd001abafba101d3fc1dadbaa3d138340c Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Mon, 6 Feb 2023 14:14:26 +0600 Subject: [PATCH 20/42] Fix tests --- .../tradeReceiveAmount.test.ts.snap | 149 ++++++++++++++++++ .../swap/helpers/tradeReceiveAmount.test.ts | 23 ++- src/cow-react/utils/trimTrailingZeros.ts | 19 ++- 3 files changed, 175 insertions(+), 16 deletions(-) create mode 100644 src/cow-react/modules/swap/helpers/__snapshots__/tradeReceiveAmount.test.ts.snap diff --git a/src/cow-react/modules/swap/helpers/__snapshots__/tradeReceiveAmount.test.ts.snap b/src/cow-react/modules/swap/helpers/__snapshots__/tradeReceiveAmount.test.ts.snap new file mode 100644 index 0000000000..2a19364c54 --- /dev/null +++ b/src/cow-react/modules/swap/helpers/__snapshots__/tradeReceiveAmount.test.ts.snap @@ -0,0 +1,149 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Helpers to build ReceiveAmountInfo getInputReceiveAmountInfo() When selling, if the fee is bigger than the traded amount Then amountBeforeFees should be zero 1`] = ` +Object { + "amount": CurrencyAmount { + "currency": Token { + "address": "0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB", + "chainId": 1, + "decimals": 18, + "isNative": false, + "isToken": true, + "name": "CoW Protocol Token", + "symbol": "COW", + }, + "decimalScale": JSBI [ + 660865024, + 931322574, + ], + "denominator": JSBI [ + 1, + ], + "numerator": JSBI [], + }, + "defaultValue": "0", +} +`; + +exports[`Helpers to build ReceiveAmountInfo getInputReceiveAmountInfo() When selling, if the fee is bigger than the traded amount Then amountBeforeFees should be zero 2`] = ` +Object { + "amount": null, + "defaultValue": "0", +} +`; + +exports[`Helpers to build ReceiveAmountInfo getInputReceiveAmountInfo() When selling, if the fee is bigger than the traded amount Then amountBeforeFees should be zero 3`] = ` +Object { + "amount": CurrencyAmount { + "currency": Token { + "address": "0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB", + "chainId": 1, + "decimals": 18, + "isNative": false, + "isToken": true, + "name": "CoW Protocol Token", + "symbol": "COW", + }, + "decimalScale": JSBI [ + 660865024, + 931322574, + ], + "denominator": JSBI [ + 1, + ], + "numerator": JSBI [ + 908853248, + 646484075, + 2, + ], + }, + "defaultValue": "0", +} +`; + +exports[`Helpers to build ReceiveAmountInfo getOutputReceiveAmountInfo() Should match a snapshot 1`] = ` +Object { + "amount": CurrencyAmount { + "currency": Token { + "address": "0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB", + "chainId": 1, + "decimals": 18, + "isNative": false, + "isToken": true, + "name": "CoW Protocol Token", + "symbol": "COW", + }, + "decimalScale": JSBI [ + 660865024, + 931322574, + ], + "denominator": JSBI [ + 1, + ], + "numerator": JSBI [ + 933756928, + 902409669, + 216, + ], + }, + "defaultValue": "0", +} +`; + +exports[`Helpers to build ReceiveAmountInfo getOutputReceiveAmountInfo() Should match a snapshot 2`] = ` +Object { + "amount": CurrencyAmount { + "currency": Token { + "address": "0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB", + "chainId": 1, + "decimals": 18, + "isNative": false, + "isToken": true, + "name": "CoW Protocol Token", + "symbol": "COW", + }, + "decimalScale": JSBI [ + 660865024, + 931322574, + ], + "denominator": JSBI [ + 1, + ], + "numerator": JSBI [ + 24903680, + 255925594, + 214, + ], + }, + "defaultValue": "0", +} +`; + +exports[`Helpers to build ReceiveAmountInfo getOutputReceiveAmountInfo() Should match a snapshot 3`] = ` +Object { + "amount": CurrencyAmount { + "currency": Token { + "address": "0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB", + "chainId": 1, + "decimals": 18, + "isNative": false, + "isToken": true, + "name": "CoW Protocol Token", + "symbol": "COW", + }, + "decimalScale": JSBI [ + 660865024, + 931322574, + ], + "denominator": JSBI [ + 1, + ], + "numerator": JSBI [ + 908853248, + 646484075, + 2, + ], + }, + "defaultValue": "0", +} +`; diff --git a/src/cow-react/modules/swap/helpers/tradeReceiveAmount.test.ts b/src/cow-react/modules/swap/helpers/tradeReceiveAmount.test.ts index da0f018703..e2f1ff439c 100644 --- a/src/cow-react/modules/swap/helpers/tradeReceiveAmount.test.ts +++ b/src/cow-react/modules/swap/helpers/tradeReceiveAmount.test.ts @@ -3,6 +3,7 @@ import TradeGp from 'state/swap/TradeGp' import { COW, GNO } from 'constants/tokens' import { SupportedChainId } from 'constants/chains' import { CurrencyAmount, Price, Token, TradeType } from '@uniswap/sdk-core' +import { ReactElement } from 'react' const currency = COW[SupportedChainId.MAINNET] const currencyOut = GNO[SupportedChainId.MAINNET] @@ -32,13 +33,10 @@ describe('Helpers to build ReceiveAmountInfo', () => { ) expect(value.amountAfterFeesRaw.toExact()).toBe('0') - expect({ ...value, amountAfterFeesRaw: null }).toEqual({ - amountAfterFeesRaw: null, - amountAfterFees: '0', - amountBeforeFees: '0', - feeAmount: '3', - type: 'from', - }) + expect((value.amountAfterFees as ReactElement).props).toMatchSnapshot() + expect((value.amountBeforeFees as ReactElement).props).toMatchSnapshot() + expect((value.feeAmount as ReactElement).props).toMatchSnapshot() + expect(value.type).toBe('from') }) }) }) @@ -60,13 +58,10 @@ describe('Helpers to build ReceiveAmountInfo', () => { ) expect(value.amountAfterFeesRaw.toExact()).toBe('250') - expect({ ...value, amountAfterFeesRaw: null }).toEqual({ - amountAfterFees: '250', - amountAfterFeesRaw: null, - amountBeforeFees: '247', - feeAmount: '-3', - type: 'to', - }) + expect((value.amountAfterFees as ReactElement).props).toMatchSnapshot() + expect((value.amountBeforeFees as ReactElement).props).toMatchSnapshot() + expect((value.feeAmount as ReactElement).props).toMatchSnapshot() + expect(value.type).toBe('to') }) }) }) diff --git a/src/cow-react/utils/trimTrailingZeros.ts b/src/cow-react/utils/trimTrailingZeros.ts index c9f1f9a632..7912652093 100644 --- a/src/cow-react/utils/trimTrailingZeros.ts +++ b/src/cow-react/utils/trimTrailingZeros.ts @@ -1,5 +1,20 @@ -const trailingZerosRegex = /(\.\d*?[1-9])0*$/ +const DOT = '.' +const ZERO = '0' export function trimTrailingZeros(value: string): string { - return value.replace(trailingZerosRegex, '$1').replace(/\.0*$/, '') + if (!value.includes(DOT)) return value + + const trimmed = value.slice(0, getFirstTrailingZeroIndex(value)) + + if (trimmed[trimmed.length - 1] === DOT) return trimmed.slice(0, -1) + + return trimmed +} + +function getFirstTrailingZeroIndex(value: string): number { + for (let i = value.length - 1; i > 0; i--) { + if (value[i] !== ZERO) return i + 1 + } + + return value.length } From 3c1d13db4fc1cc04baa221ae08738f2c80826a9e Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Mon, 6 Feb 2023 15:24:50 +0600 Subject: [PATCH 21/42] More tests --- src/cow-react/abis/types/index.ts | 2 +- .../utils/amountFormat/index.test.ts | 124 ++++++++++++------ src/cow-react/utils/amountFormat/utils.ts | 5 +- 3 files changed, 88 insertions(+), 43 deletions(-) diff --git a/src/cow-react/abis/types/index.ts b/src/cow-react/abis/types/index.ts index cc22b4d63d..36c98c35c0 100644 --- a/src/cow-react/abis/types/index.ts +++ b/src/cow-react/abis/types/index.ts @@ -10,5 +10,5 @@ export { GPv2Settlement__factory } from "./factories/GPv2Settlement__factory"; export { MerkleDrop__factory } from "./factories/MerkleDrop__factory"; export { TokenDistro__factory } from "./factories/TokenDistro__factory"; export { VCow__factory } from "./factories/VCow__factory"; -export * from "@src/abis/types"; export * from "@cow/abis/types/ethflow"; +export * from "@src/abis/types"; diff --git a/src/cow-react/utils/amountFormat/index.test.ts b/src/cow-react/utils/amountFormat/index.test.ts index e7b177f24e..de5fc921f2 100644 --- a/src/cow-react/utils/amountFormat/index.test.ts +++ b/src/cow-react/utils/amountFormat/index.test.ts @@ -1,4 +1,4 @@ -import { formatTokenAmount } from './index' +import { formatFiatAmount, formatTokenAmount } from './index' import { CurrencyAmount } from '@uniswap/sdk-core' import { DAI_GOERLI } from 'utils/goerli/constants' @@ -7,63 +7,109 @@ describe('Amounts formatting', () => { const getAmount = (value: string, decimalsShift: number) => CurrencyAmount.fromRawAmount(DAI_GOERLI, value + '0'.repeat(decimals + decimalsShift)) - it('Zero amount', () => { - const result = formatTokenAmount(getAmount('0', 0)) + describe('Amounts', () => { + it('Zero amount', () => { + const result = formatTokenAmount(getAmount('0', 0)) - expect(result).toBe('0') - }) + expect(result).toBe('0') + }) - it('Extra small amount', () => { - const result = formatTokenAmount(getAmount('1', -decimals)) + it('Extra small amount', () => { + const result = formatTokenAmount(getAmount('1', -decimals)) - expect(result).toBe('< 0.000001') - }) + expect(result).toBe('< 0.000001') + }) - it('Small amount', () => { - const result = formatTokenAmount(getAmount('1', -4)) + it('Small amount', () => { + const result = formatTokenAmount(getAmount('1', -4)) - expect(result).toBe('0.0001') - }) + expect(result).toBe('0.0001') + }) - it('One amount', () => { - const result = formatTokenAmount(getAmount('1', 0)) + it('One amount', () => { + const result = formatTokenAmount(getAmount('1', 0)) - expect(result).toBe('1') - }) + expect(result).toBe('1') + }) - it('Regular amount', () => { - const result = formatTokenAmount(getAmount('1', 3)) + it('Regular amount', () => { + const result = formatTokenAmount(getAmount('1', 3)) - expect(result).toBe('1,000') - }) + expect(result).toBe('1,000') + }) - it('Thousands amount', () => { - const result = formatTokenAmount(getAmount('1', 4)) + it('Thousands amount', () => { + const result = formatTokenAmount(getAmount('1', 4)) - expect(result).toBe('10,000') - }) + expect(result).toBe('10,000') + }) - it('Hundreds thousands amount', () => { - const result = formatTokenAmount(getAmount('23', 4)) + it('Hundreds thousands amount', () => { + const result = formatTokenAmount(getAmount('23', 4)) - expect(result).toBe('230,000') - }) + expect(result).toBe('230,000') + }) - it('Millions amount', () => { - const result = formatTokenAmount(getAmount('5456', 3)) + it('Millions amount', () => { + const result = formatTokenAmount(getAmount('5456', 3)) - expect(result).toBe('5,456,000') - }) + expect(result).toBe('5,456,000') + }) + + it('Billions amount', () => { + const result = formatTokenAmount(getAmount('9307222438', 0)) + + expect(result).toBe('9.307B') + }) - it('Billions amount', () => { - const result = formatTokenAmount(getAmount('9307222438', 0)) + it('Trillions amount', () => { + const result = formatTokenAmount(getAmount('45676822', 9)) - expect(result).toBe('9.307B') + expect(result).toBe('45,676.822T') + }) }) - it('Trillions amount', () => { - const result = formatTokenAmount(getAmount('45676822', 9)) + describe('Precision', () => { + it('When the amount is less than 1, then precision is 6', () => { + const result = formatTokenAmount(getAmount('1123456', -8)) + + expect(result).toBe('0.011235') + }) + + it('When the amount is less than 100_000, then precision is 4', () => { + const result = formatTokenAmount(getAmount('7850450043', -5)) + + expect(result).toBe('78,504.5004') + }) + + it('When the amount is less than 1M, then precision is 3', () => { + const result = formatTokenAmount(getAmount('60023003367', -5)) + + expect(result).toBe('600,230.034') + }) + + it('When the amount is less than 10M, then precision is 2', () => { + const result = formatTokenAmount(getAmount('902355432336', -5)) + + expect(result).toBe('9,023,554.32') + }) + + it('When the amount is greater than 10M, then precision is 3', () => { + const result1 = formatTokenAmount(getAmount('1093393493', 1)) + const result2 = formatTokenAmount(getAmount('34392220011', 1)) + const result3 = formatTokenAmount(getAmount('3244302333422', 1)) + + expect(result1).toBe('10.934B') + expect(result2).toBe('343.922B') + expect(result3).toBe('32.443T') + }) + + it('Precision for fiat amounts is always 3', () => { + const result1 = formatFiatAmount(getAmount('734436023451', -5)) + const result2 = formatFiatAmount(getAmount('60001444', 3)) - expect(result).toBe('45,676.822T') + expect(result1).toBe('7,344,360.23') + expect(result2).toBe('60.00B') + }) }) }) diff --git a/src/cow-react/utils/amountFormat/utils.ts b/src/cow-react/utils/amountFormat/utils.ts index b2f6a086e3..248299d11b 100644 --- a/src/cow-react/utils/amountFormat/utils.ts +++ b/src/cow-react/utils/amountFormat/utils.ts @@ -15,13 +15,12 @@ export function getPrecisionForAmount(amount: Nullish): number { const fraction = FractionUtils.fractionLikeToFraction(amount) const smartPrecision = (() => { - if (FractionUtils.gte(fraction, BILLION)) return 3 - if (FractionUtils.lte(fraction, ONE)) return 6 if (FractionUtils.lte(fraction, HUNDRED_K)) return 4 if (FractionUtils.lte(fraction, MILLION)) return 3 if (FractionUtils.lte(fraction, TEN_MILLION)) return 2 - return 1 + + return 3 })() // Some tokens have decimals = 0 From 2efa6cc7a1a7aa773800a49c2fe29f356a80b04f Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Mon, 6 Feb 2023 16:45:25 +0600 Subject: [PATCH 22/42] Amounts formatting docs --- src/cow-react/utils/amountFormat/README.md | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/cow-react/utils/amountFormat/README.md diff --git a/src/cow-react/utils/amountFormat/README.md b/src/cow-react/utils/amountFormat/README.md new file mode 100644 index 0000000000..564b2a39fb --- /dev/null +++ b/src/cow-react/utils/amountFormat/README.md @@ -0,0 +1,38 @@ +# Amounts formatting + +The application needs to display amounts of different sizes, from extremely small to extremely large, while maintaining accuracy. + +There are two types of amounts: + - token amount: `100 000.54 COW` + - fiat amounts: `≈ $1 946 628.4` + +### Language-sensitive number formatting + +The solution uses [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat) API for numbers formatting. + +### Precision + +The `smart` precision algorithm: +1. When an amount is less than `1`, then precision is `6` +2. When an amount is less than `100_000`, then precision is `4` +3. When an amount is less than `1M`, then precision is `3` +4. When an amount is less than `10M`, then precision is `2` +5. When an amount is greater than `10M`, then precision is `3` +6. When an amount is greater than `1B`, then precision is `3` AND the suffix `B` adds +7. When an amount is greater than `1T`, then precision is `3` AND the suffix `T` adds + +## Token amounts + +Examples: +`` - looks like `400 DAI` +`` - looks like `6 000.11` + +The `amount` property has type `FractionLike`. It includes `Fraction`, `Price` and `CurrencyAmount` from `@uniswap/sdk-core` + +An amount could be formatted using `formatTokenAmount(amount)` function. +Or `formatAmountWithPrecision(amount, precision)` if you need custom precision. + +## Fiat amounts + +Examples: +` Date: Mon, 6 Feb 2023 16:48:05 +0600 Subject: [PATCH 23/42] Amounts formatting docs --- src/cow-react/utils/amountFormat/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cow-react/utils/amountFormat/README.md b/src/cow-react/utils/amountFormat/README.md index 564b2a39fb..b8e7694e71 100644 --- a/src/cow-react/utils/amountFormat/README.md +++ b/src/cow-react/utils/amountFormat/README.md @@ -13,13 +13,13 @@ The solution uses [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/W ### Precision The `smart` precision algorithm: -1. When an amount is less than `1`, then precision is `6` -2. When an amount is less than `100_000`, then precision is `4` -3. When an amount is less than `1M`, then precision is `3` -4. When an amount is less than `10M`, then precision is `2` -5. When an amount is greater than `10M`, then precision is `3` -6. When an amount is greater than `1B`, then precision is `3` AND the suffix `B` adds -7. When an amount is greater than `1T`, then precision is `3` AND the suffix `T` adds +1. When an amount is less than `1`, then precision is `6` (`0.003411`) +2. When an amount is less than `100_000`, then precision is `4` (`2 002.5901`) +3. When an amount is less than `1M`, then precision is `3` (`640 123.012`) +4. When an amount is less than `10M`, then precision is `2` (`2 700 000.08`) +5. When an amount is greater than `10M`, then precision is `3` (`65 200 000.102`) +6. When an amount is greater than `1B`, then precision is `3` AND plus suffix `B` (`12.502B`) +7. When an amount is greater than `1T`, then precision is `3` AND plus suffix `T` adds (`6.2T`) ## Token amounts From aaf6bf64273a3440a3bca4adb6297994b0928e2a Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Mon, 6 Feb 2023 16:48:38 +0600 Subject: [PATCH 24/42] Amounts formatting docs --- src/cow-react/utils/amountFormat/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cow-react/utils/amountFormat/README.md b/src/cow-react/utils/amountFormat/README.md index b8e7694e71..e741785eda 100644 --- a/src/cow-react/utils/amountFormat/README.md +++ b/src/cow-react/utils/amountFormat/README.md @@ -36,3 +36,5 @@ Or `formatAmountWithPrecision(amount, precision)` if you need custom precision. Examples: ` Date: Mon, 6 Feb 2023 10:51:18 +0000 Subject: [PATCH 25/42] Fix code style issues with Prettier --- src/cow-react/utils/amountFormat/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cow-react/utils/amountFormat/README.md b/src/cow-react/utils/amountFormat/README.md index e741785eda..18db2c343e 100644 --- a/src/cow-react/utils/amountFormat/README.md +++ b/src/cow-react/utils/amountFormat/README.md @@ -3,8 +3,9 @@ The application needs to display amounts of different sizes, from extremely small to extremely large, while maintaining accuracy. There are two types of amounts: - - token amount: `100 000.54 COW` - - fiat amounts: `≈ $1 946 628.4` + +- token amount: `100 000.54 COW` +- fiat amounts: `≈ $1 946 628.4` ### Language-sensitive number formatting @@ -13,6 +14,7 @@ The solution uses [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/W ### Precision The `smart` precision algorithm: + 1. When an amount is less than `1`, then precision is `6` (`0.003411`) 2. When an amount is less than `100_000`, then precision is `4` (`2 002.5901`) 3. When an amount is less than `1M`, then precision is `3` (`640 123.012`) From 913d48ff4efce04f71ae5e38363fd373173ff249 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Mon, 6 Feb 2023 16:57:38 +0600 Subject: [PATCH 26/42] Amounts formatting docs --- src/cow-react/utils/amountFormat/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cow-react/utils/amountFormat/README.md b/src/cow-react/utils/amountFormat/README.md index e741785eda..e01f419977 100644 --- a/src/cow-react/utils/amountFormat/README.md +++ b/src/cow-react/utils/amountFormat/README.md @@ -27,6 +27,8 @@ Examples: `` - looks like `400 DAI` `` - looks like `6 000.11` +The component has `title` attribute and displays the full amount on hover. + The `amount` property has type `FractionLike`. It includes `Fraction`, `Price` and `CurrencyAmount` from `@uniswap/sdk-core` An amount could be formatted using `formatTokenAmount(amount)` function. From 99f5786f461e63f204a2170ea22adc80d60c6db1 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Mon, 6 Feb 2023 17:47:41 +0600 Subject: [PATCH 27/42] Fix code-style --- .../common/pure/FiatAmount/index.tsx | 2 +- .../common/pure/TokenAmount/index.tsx | 2 +- .../containers/LimitOrdersWidget/index.tsx | 6 ++-- .../containers/RateInput/index.tsx | 4 +-- .../swap/containers/NewSwapWidget/index.tsx | 6 ++-- .../swap/containers/Row/RowFee/index.tsx | 2 +- src/cow-react/utils/amountFormat/index.ts | 28 ++++++++----------- src/cow-react/utils/amountFormat/utils.ts | 18 ++++++------ src/cow-react/utils/fractionUtils.ts | 2 +- .../swap/ConfirmSwapModal/index.tsx | 1 - 10 files changed, 33 insertions(+), 38 deletions(-) diff --git a/src/cow-react/common/pure/FiatAmount/index.tsx b/src/cow-react/common/pure/FiatAmount/index.tsx index f25ccb6504..3f057985e2 100644 --- a/src/cow-react/common/pure/FiatAmount/index.tsx +++ b/src/cow-react/common/pure/FiatAmount/index.tsx @@ -15,7 +15,7 @@ const highlight = !!localStorage.getItem('amountsRefactoring') export function FiatAmount({ amount, defaultValue, className, accurate = false }: FiatAmountProps) { const formattedAmount = formatFiatAmount(amount) - const title = FractionUtils.fractionLikeToExact(amount, LONG_PRECISION) + const title = FractionUtils.fractionLikeToExactString(amount, LONG_PRECISION) const accuracySymbol = accurate ? '' : '≈ ' return ( diff --git a/src/cow-react/common/pure/TokenAmount/index.tsx b/src/cow-react/common/pure/TokenAmount/index.tsx index c914a17229..718efea200 100644 --- a/src/cow-react/common/pure/TokenAmount/index.tsx +++ b/src/cow-react/common/pure/TokenAmount/index.tsx @@ -16,7 +16,7 @@ const highlight = !!localStorage.getItem('amountsRefactoring') export function TokenAmount({ amount, defaultValue, className, tokenSymbol }: TokenAmountProps) { const title = - FractionUtils.fractionLikeToExact(amount, LONG_PRECISION) + (tokenSymbol ? ` ${tokenSymbol.symbol}` : '') + FractionUtils.fractionLikeToExactString(amount, LONG_PRECISION) + (tokenSymbol ? ` ${tokenSymbol.symbol}` : '') return ( <> diff --git a/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx b/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx index 0834b29860..3b9eb62c5e 100644 --- a/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx +++ b/src/cow-react/modules/limitOrders/containers/LimitOrdersWidget/index.tsx @@ -43,7 +43,7 @@ import { maxAmountSpend } from '@src/utils/maxAmountSpend' import { FractionUtils } from '@cow/utils/fractionUtils' import { useSetupLimitOrderAmountsFromUrl } from '@cow/modules/limitOrders/hooks/useSetupLimitOrderAmountsFromUrl' import AffiliateStatusCheck from 'components/AffiliateStatusCheck' -import { formatAmountInput } from '@cow/utils/amountFormat' +import { formatInputAmount } from '@cow/utils/amountFormat' export function LimitOrdersWidget() { useSetupTradeState() @@ -83,7 +83,7 @@ export function LimitOrdersWidget() { [settingState.showRecipient, isWrapOrUnwrap] ) const priceImpact = usePriceImpact(useLimitOrdersPriceImpactParams()) - const inputViewAmount = formatAmountInput(inputCurrencyAmount, inputCurrencyBalance, orderKind === OrderKind.SELL) + const inputViewAmount = formatInputAmount(inputCurrencyAmount, inputCurrencyBalance, orderKind === OrderKind.SELL) const inputCurrencyInfo: CurrencyInfo = { field: Field.INPUT, @@ -102,7 +102,7 @@ export function LimitOrdersWidget() { rawAmount: isWrapOrUnwrap ? inputCurrencyAmount : outputCurrencyAmount, viewAmount: isWrapOrUnwrap ? inputViewAmount - : formatAmountInput(outputCurrencyAmount, outputCurrencyBalance, orderKind === OrderKind.BUY), + : formatInputAmount(outputCurrencyAmount, outputCurrencyBalance, orderKind === OrderKind.BUY), balance: outputCurrencyBalance, fiatAmount: outputCurrencyFiatAmount, receiveAmountInfo: null, diff --git a/src/cow-react/modules/limitOrders/containers/RateInput/index.tsx b/src/cow-react/modules/limitOrders/containers/RateInput/index.tsx index befe06bc24..dee3084816 100644 --- a/src/cow-react/modules/limitOrders/containers/RateInput/index.tsx +++ b/src/cow-react/modules/limitOrders/containers/RateInput/index.tsx @@ -14,7 +14,7 @@ import { useWeb3React } from '@web3-react/core' import { getAddress } from '@cow/utils/getAddress' import { useUpdateActiveRate } from '@cow/modules/limitOrders/hooks/useUpdateActiveRate' import { TokenSymbol } from '@cow/common/pure/TokenSymbol' -import { formatAmountInput } from '@cow/utils/amountFormat' +import { formatInputAmount } from '@cow/utils/amountFormat' export function RateInput() { const { chainId } = useWeb3React() @@ -51,7 +51,7 @@ export function RateInput() { const rate = isInversed ? activeRate.invert() : activeRate - return formatAmountInput(rate) + return formatInputAmount(rate) }, [activeRate, areBothCurrencies, isInversed, isTypedValue, typedValue]) // Handle set market price diff --git a/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx b/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx index c7d2ac3649..d66e8ccf64 100644 --- a/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx +++ b/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx @@ -44,7 +44,7 @@ import { NetworkAlert } from 'components/NetworkAlert/NetworkAlert' import { useRateInfoParams } from '@cow/common/hooks/useRateInfoParams' import { useSetupSwapAmountsFromUrl } from '@cow/modules/swap/hooks/useSetupSwapAmountsFromUrl' import { useIsTradeUnsupported } from 'state/lists/hooks/hooksMod' -import { formatAmountInput } from '@cow/utils/amountFormat' +import { formatInputAmount } from '@cow/utils/amountFormat' export function NewSwapWidget() { useSetupTradeState() @@ -84,7 +84,7 @@ export function NewSwapWidget() { field: Field.INPUT, currency: currencies.INPUT || null, rawAmount: parsedAmounts.INPUT || null, - viewAmount: formatAmountInput(parsedAmounts.INPUT, inputCurrencyBalance, independentField === Field.INPUT), + viewAmount: formatInputAmount(parsedAmounts.INPUT, inputCurrencyBalance, independentField === Field.INPUT), balance: inputCurrencyBalance, fiatAmount: useHigherUSDValue(trade?.inputAmountWithoutFee), receiveAmountInfo: independentField === Field.OUTPUT && trade ? getInputReceiveAmountInfo(trade) : null, @@ -94,7 +94,7 @@ export function NewSwapWidget() { field: Field.OUTPUT, currency: currencies.OUTPUT || null, rawAmount: parsedAmounts.OUTPUT || null, - viewAmount: formatAmountInput(parsedAmounts.OUTPUT, outputCurrencyBalance, independentField === Field.OUTPUT), + viewAmount: formatInputAmount(parsedAmounts.OUTPUT, outputCurrencyBalance, independentField === Field.OUTPUT), balance: outputCurrencyBalance, fiatAmount: useHigherUSDValue(trade?.outputAmountWithoutFee), receiveAmountInfo: independentField === Field.INPUT && trade ? getOutputReceiveAmountInfo(trade) : null, diff --git a/src/cow-react/modules/swap/containers/Row/RowFee/index.tsx b/src/cow-react/modules/swap/containers/Row/RowFee/index.tsx index 3e806a4af4..2a60a18538 100644 --- a/src/cow-react/modules/swap/containers/Row/RowFee/index.tsx +++ b/src/cow-react/modules/swap/containers/Row/RowFee/index.tsx @@ -71,7 +71,7 @@ export function RowFee({ trade, fee, feeFiatValue, allowsOffchainSigning, showHe isEthFLow ? ' + gas' : '' }` const feeToken = smartFeeTokenValue ? feeAmountWithCurrency : '🎉 Free!' - const fullDisplayFee = FractionUtils.fractionLikeToExact(displayFee) || '-' + const fullDisplayFee = FractionUtils.fractionLikeToExactString(displayFee) || '-' const includeGasMessage = allowsOffchainSigning && !isEthFLow ? ' (incl. gas costs)' : '' return { diff --git a/src/cow-react/utils/amountFormat/index.ts b/src/cow-react/utils/amountFormat/index.ts index 62a1a557c5..0f055ef0da 100644 --- a/src/cow-react/utils/amountFormat/index.ts +++ b/src/cow-react/utils/amountFormat/index.ts @@ -29,34 +29,28 @@ export function formatAmountWithPrecision(amount: Nullish, precisi // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat const formattedQuotient = INTL_NUMBER_FORMAT.format(BigInt(trimTrailingZeros(quotient.toString()))) // Trim the remainder up to precision - const formattedRemainder = remainder.greaterThan(0) - ? trimTrailingZeros(`${remainder.toFixed(precision).slice(1)}`) - : '' + const formattedRemainder = remainder.greaterThan(0) ? trimTrailingZeros(remainder.toFixed(precision).slice(1)) : '' const result = formattedQuotient + formattedRemainder + suffix return remainder.greaterThan(0) && +result === 0 ? lessThanPrecisionSymbol(precision) : result } -export function formatAmountInput( +export function formatInputAmount( amount: Nullish, - balance: CurrencyAmount | null = null, + balance: Nullish> = null, isIndependentField = false ): string { if (!amount) return '' - const maxBalance = balance ? maxAmountSpend(balance) : undefined - const isAmountMatchesBalance = !!(maxBalance && amount.equalTo(maxBalance)) + const usesMaxBalance = balance ? maxAmountSpend(balance) : undefined + const amountMatchesBalance = !!(usesMaxBalance && amount.equalTo(usesMaxBalance)) - return trimTrailingZeros( - (() => { - if (isIndependentField || isAmountMatchesBalance) { - return FractionUtils.fractionLikeToExact(amount) - } + if (isIndependentField || amountMatchesBalance) { + return trimTrailingZeros(FractionUtils.fractionLikeToExactString(amount)) + } - const precision = getPrecisionForAmount(amount) - const result = amount.toFixed(precision) + const precision = getPrecisionForAmount(amount) + const result = amount.toFixed(precision) - return +result === 0 ? amount.toSignificant(AMOUNT_PRECISION) : result - })() - ) + return trimTrailingZeros(+result === 0 ? amount.toSignificant(AMOUNT_PRECISION) : result) } diff --git a/src/cow-react/utils/amountFormat/utils.ts b/src/cow-react/utils/amountFormat/utils.ts index 248299d11b..92a6c962bc 100644 --- a/src/cow-react/utils/amountFormat/utils.ts +++ b/src/cow-react/utils/amountFormat/utils.ts @@ -10,18 +10,20 @@ const TEN_MILLION = JSBI.BigInt(10_000_000) const BILLION = JSBI.BigInt(1_000_000_000) const TRILLION = JSBI.BigInt(1_000_000_000_000) +function getPrecisionForFraction(fraction: Fraction): number { + if (FractionUtils.lte(fraction, ONE)) return 6 + if (FractionUtils.lte(fraction, HUNDRED_K)) return 4 + if (FractionUtils.lte(fraction, MILLION)) return 3 + if (FractionUtils.lte(fraction, TEN_MILLION)) return 2 + + return 3 +} + export function getPrecisionForAmount(amount: Nullish): number { if (!amount) return 0 const fraction = FractionUtils.fractionLikeToFraction(amount) - const smartPrecision = (() => { - if (FractionUtils.lte(fraction, ONE)) return 6 - if (FractionUtils.lte(fraction, HUNDRED_K)) return 4 - if (FractionUtils.lte(fraction, MILLION)) return 3 - if (FractionUtils.lte(fraction, TEN_MILLION)) return 2 - - return 3 - })() + const smartPrecision = getPrecisionForFraction(fraction) // Some tokens have decimals = 0 if (amount instanceof CurrencyAmount && amount.currency.decimals < smartPrecision) { diff --git a/src/cow-react/utils/fractionUtils.ts b/src/cow-react/utils/fractionUtils.ts index 49b5c41b65..0ec976e02d 100644 --- a/src/cow-react/utils/fractionUtils.ts +++ b/src/cow-react/utils/fractionUtils.ts @@ -21,7 +21,7 @@ export class FractionUtils { } } - static fractionLikeToExact(amount: Nullish, max = FULL_PRICE_PRECISION): string { + static fractionLikeToExactString(amount: Nullish, max = FULL_PRICE_PRECISION): string { if (!amount) return '' if (amount.equalTo(0)) return '0' diff --git a/src/custom/components/swap/ConfirmSwapModal/index.tsx b/src/custom/components/swap/ConfirmSwapModal/index.tsx index 65a76812d6..6b99a4f55c 100644 --- a/src/custom/components/swap/ConfirmSwapModal/index.tsx +++ b/src/custom/components/swap/ConfirmSwapModal/index.tsx @@ -3,7 +3,6 @@ import { Trans } from '@lingui/macro' import ConfirmSwapModalMod from './ConfirmSwapModalMod' import TradeGp from 'state/swap/TradeGp' import { TokenAmount } from '@cow/common/pure/TokenAmount' -import React from 'react' export * from './ConfirmSwapModalMod' From 3c6c4d2453e719a881cca95dc2e3aa8bd286ed74 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Mon, 6 Feb 2023 19:51:57 +0600 Subject: [PATCH 28/42] Fix legacyBigNumberToCurrencyAmount --- .../modules/limitOrders/pure/ReceiptModal/FilledField.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx b/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx index d10110e86c..d08c60db00 100644 --- a/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx +++ b/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx @@ -15,8 +15,8 @@ interface Props { } // TODO: using .toNumber() we potentially lose accuracy -function LegacyBigNumberToCurrencyAmount(currency: Token, value: BigNumber | undefined): CurrencyAmount { - return CurrencyAmount.fromRawAmount(currency, (value?.toNumber() || 0) * 10 ** currency.decimals) +function legacyBigNumberToCurrencyAmount(currency: Token, value: BigNumber | undefined): CurrencyAmount { + return CurrencyAmount.fromRawAmount(currency, Math.ceil((value?.toNumber() || 0) * 10 ** currency.decimals)) } export function FilledField({ order, sellAmount, buyAmount }: Props) { @@ -72,10 +72,10 @@ export function FilledField({ order, sellAmount, buyAmount }: Props) { // In case the token object is empty, display the raw amount (`decimals || 0` part) const filledAmountDecimal = filledAmountWithFee?.div(new BigNumber(10 ** mainToken.decimals)) - const formattedFilledAmount = LegacyBigNumberToCurrencyAmount(mainToken, filledAmountDecimal) + const formattedFilledAmount = legacyBigNumberToCurrencyAmount(mainToken, filledAmountDecimal) const swappedAmountDecimal = swappedAmountWithFee.div(new BigNumber(10 ** swappedToken.decimals)) - const formattedSwappedAmount = LegacyBigNumberToCurrencyAmount(mainToken, swappedAmountDecimal) + const formattedSwappedAmount = legacyBigNumberToCurrencyAmount(mainToken, swappedAmountDecimal) const formattedPercentage = useMemo(() => { if (!filledPercentage) { From d9988910b292a2597133e0ae55d81f19e3e26942 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Tue, 7 Feb 2023 18:36:37 +0600 Subject: [PATCH 29/42] Fix percent displaying --- src/cow-react/abis/types/index.ts | 2 +- .../swap/containers/Row/RowSlippage/index.tsx | 4 +- .../utils/amountFormat/index.test.ts | 40 ++++++++++++++++++- src/cow-react/utils/amountFormat/index.ts | 17 ++++++-- .../FiatValue/FiatValueMod.tsx | 6 +-- 5 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/cow-react/abis/types/index.ts b/src/cow-react/abis/types/index.ts index 36c98c35c0..cc22b4d63d 100644 --- a/src/cow-react/abis/types/index.ts +++ b/src/cow-react/abis/types/index.ts @@ -10,5 +10,5 @@ export { GPv2Settlement__factory } from "./factories/GPv2Settlement__factory"; export { MerkleDrop__factory } from "./factories/MerkleDrop__factory"; export { TokenDistro__factory } from "./factories/TokenDistro__factory"; export { VCow__factory } from "./factories/VCow__factory"; -export * from "@cow/abis/types/ethflow"; export * from "@src/abis/types"; +export * from "@cow/abis/types/ethflow"; diff --git a/src/cow-react/modules/swap/containers/Row/RowSlippage/index.tsx b/src/cow-react/modules/swap/containers/Row/RowSlippage/index.tsx index 677843aa04..6d83279b23 100644 --- a/src/cow-react/modules/swap/containers/Row/RowSlippage/index.tsx +++ b/src/cow-react/modules/swap/containers/Row/RowSlippage/index.tsx @@ -5,7 +5,7 @@ import { useToggleSettingsMenu } from 'state/application/hooks' import { RowSlippageContent } from '@cow/modules/swap/pure/Row/RowSlippageContent' import { useIsEthFlow } from '@cow/modules/swap/hooks/useIsEthFlow' import { useDetectNativeToken } from '@cow/modules/swap/hooks/useDetectNativeToken' -import { formatTokenAmount } from '@cow/utils/amountFormat' +import { formatPercent } from '@cow/utils/amountFormat' export interface RowSlippageProps { allowedSlippage: Percent @@ -24,7 +24,7 @@ export function RowSlippage({ allowedSlippage, showSettingOnClick = true }: RowS symbols: [nativeCurrency.symbol], showSettingOnClick, allowedSlippage, - displaySlippage: `${formatTokenAmount(allowedSlippage)}%`, + displaySlippage: `${formatPercent(allowedSlippage)}%`, }), [allowedSlippage, nativeCurrency, isEthFlow, showSettingOnClick] ) diff --git a/src/cow-react/utils/amountFormat/index.test.ts b/src/cow-react/utils/amountFormat/index.test.ts index de5fc921f2..c139144361 100644 --- a/src/cow-react/utils/amountFormat/index.test.ts +++ b/src/cow-react/utils/amountFormat/index.test.ts @@ -1,6 +1,7 @@ -import { formatFiatAmount, formatTokenAmount } from './index' -import { CurrencyAmount } from '@uniswap/sdk-core' +import { formatFiatAmount, formatPercent, formatTokenAmount } from './index' +import { CurrencyAmount, Percent } from '@uniswap/sdk-core' import { DAI_GOERLI } from 'utils/goerli/constants' +import { USDC_GNOSIS_CHAIN } from '../../../custom/utils/gnosis_chain/constants' describe('Amounts formatting', () => { const decimals = DAI_GOERLI.decimals @@ -103,6 +104,21 @@ describe('Amounts formatting', () => { expect(result2).toBe('343.922B') expect(result3).toBe('32.443T') }) + }) + + describe('Fiat amounts', () => { + it('When the amount is almost 1, it must be rounded up to 1', () => { + const result = formatFiatAmount( + // ~0.995 + CurrencyAmount.fromFractionalAmount( + USDC_GNOSIS_CHAIN, + '994582567877074269904770000000000000000000', + '999200146079960203000000000000000000' + ) + ) + + expect(result).toBe('1') + }) it('Precision for fiat amounts is always 3', () => { const result1 = formatFiatAmount(getAmount('734436023451', -5)) @@ -112,4 +128,24 @@ describe('Amounts formatting', () => { expect(result2).toBe('60.00B') }) }) + + describe('Percent', () => { + it('Regular percent', () => { + const result = formatPercent(new Percent(100, 2000)) + + expect(result).toBe('5') + }) + + it('Negative percent', () => { + const result = formatPercent(new Percent(2, 1).multiply(-1)) + + expect(result).toBe('-200') + }) + + it('With decimals', () => { + const result = formatPercent(new Percent(31500, 25634562)) + + expect(result).toBe('0.12') + }) + }) }) diff --git a/src/cow-react/utils/amountFormat/index.ts b/src/cow-react/utils/amountFormat/index.ts index 0f055ef0da..f7b5b8e555 100644 --- a/src/cow-react/utils/amountFormat/index.ts +++ b/src/cow-react/utils/amountFormat/index.ts @@ -1,7 +1,7 @@ import { FractionLike, Nullish } from '@cow/types' -import { Currency, CurrencyAmount } from '@uniswap/sdk-core' +import { Currency, CurrencyAmount, Percent, Rounding } from '@uniswap/sdk-core' import { maxAmountSpend } from 'utils/maxAmountSpend' -import { AMOUNT_PRECISION, FIAT_PRECISION } from 'constants/index' +import { AMOUNT_PRECISION, FIAT_PRECISION, PERCENTAGE_PRECISION } from 'constants/index' import { trimTrailingZeros } from '@cow/utils/trimTrailingZeros' import { FractionUtils } from '@cow/utils/fractionUtils' import { getPrecisionForAmount, getSuffixForAmount, lessThanPrecisionSymbol, trimHugeAmounts } from './utils' @@ -15,6 +15,10 @@ export function formatTokenAmount(amount: Nullish): string { return formatAmountWithPrecision(amount, getPrecisionForAmount(amount)) } +export function formatPercent(percent: Nullish): string { + return percent ? trimTrailingZeros(percent.toFixed(PERCENTAGE_PRECISION)) : '' +} + export function formatAmountWithPrecision(amount: Nullish, precision: number): string { if (!amount) return '' @@ -29,7 +33,14 @@ export function formatAmountWithPrecision(amount: Nullish, precisi // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat const formattedQuotient = INTL_NUMBER_FORMAT.format(BigInt(trimTrailingZeros(quotient.toString()))) // Trim the remainder up to precision - const formattedRemainder = remainder.greaterThan(0) ? trimTrailingZeros(remainder.toFixed(precision).slice(1)) : '' + const fixedRemainder = remainder.toFixed(precision, undefined, Rounding.ROUND_HALF_UP) + + // toFixed() could round the remainder up, and the result could be 1.00 or greater + if (+fixedRemainder >= 1) { + return trimTrailingZeros(fixedRemainder) + } + + const formattedRemainder = remainder.greaterThan(0) ? trimTrailingZeros(fixedRemainder.slice(1)) : '' const result = formattedQuotient + formattedRemainder + suffix return remainder.greaterThan(0) && +result === 0 ? lessThanPrecisionSymbol(precision) : result diff --git a/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx b/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx index db40c4cfe8..7a2ffa2683 100644 --- a/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx +++ b/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx @@ -9,9 +9,9 @@ import { warningSeverity } from 'utils/prices' import { MouseoverTooltip } from 'components/Tooltip' // MOD imports -import { PERCENTAGE_PRECISION } from 'constants/index' // mod +// mod import Loader from 'components/Loader' -import { formatAmountWithPrecision } from '@cow/utils/amountFormat' +import { formatPercent } from '@cow/utils/amountFormat' import { FiatAmount } from '@cow/common/pure/FiatAmount' export function FiatValue({ @@ -57,7 +57,7 @@ export function FiatValue({ {' '} - ({formatAmountWithPrecision(priceImpact.multiply(-1), PERCENTAGE_PRECISION)}%) + ({formatPercent(priceImpact.multiply(-1))}%) ) : null} From 1f09d6fba11488553aa5a5d3699544ad19b285f0 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Tue, 7 Feb 2023 19:37:51 +0600 Subject: [PATCH 30/42] Fix amounts formatting --- .../common/pure/CurrencyInputPanel/CurrencyPreview.tsx | 8 +++++--- src/cow-react/utils/fractionUtils.ts | 3 ++- .../components/CurrencyInputPanel/FiatValue/index.tsx | 1 + src/custom/components/Tokens/TokensTableRow.tsx | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 src/custom/components/CurrencyInputPanel/FiatValue/index.tsx diff --git a/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx b/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx index 86834e4a8e..da92b2fbdf 100644 --- a/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx +++ b/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx @@ -6,6 +6,8 @@ import { Trans } from '@lingui/macro' import { PriceImpact } from 'hooks/usePriceImpact' import { CurrencyInfo } from '@cow/common/pure/CurrencyInputPanel/types' import { TokenAmount } from '@cow/common/pure/TokenAmount' +import { FractionUtils } from '@cow/utils/fractionUtils' +import { formatTokenAmount } from '@cow/utils/amountFormat' interface BuiltItProps { className: string @@ -21,7 +23,7 @@ export interface CurrencyPreviewProps extends Partial { export function CurrencyPreview(props: CurrencyPreviewProps) { const { id, currencyInfo, className, priceImpactParams, topLabel } = props const { priceImpact, loading: priceImpactLoading } = priceImpactParams || {} - const { currency, balance, fiatAmount, viewAmount, rawAmount } = currencyInfo + const { currency, balance, fiatAmount, rawAmount } = currencyInfo return ( <> @@ -34,10 +36,10 @@ export function CurrencyPreview(props: CurrencyPreviewProps) {
void 0} $loading={false} /> diff --git a/src/cow-react/utils/fractionUtils.ts b/src/cow-react/utils/fractionUtils.ts index 0ec976e02d..d6e87bbb28 100644 --- a/src/cow-react/utils/fractionUtils.ts +++ b/src/cow-react/utils/fractionUtils.ts @@ -33,7 +33,8 @@ export class FractionUtils { } if (amount instanceof Price) { - return amount.toFixed(amount.quoteCurrency.decimals) || '' + const decimals = amount.quoteCurrency.decimals === 0 ? max : amount.quoteCurrency.decimals + return amount.toFixed(decimals) || '' } return amount.toFixed(max) || '' diff --git a/src/custom/components/CurrencyInputPanel/FiatValue/index.tsx b/src/custom/components/CurrencyInputPanel/FiatValue/index.tsx new file mode 100644 index 0000000000..fcc464f95d --- /dev/null +++ b/src/custom/components/CurrencyInputPanel/FiatValue/index.tsx @@ -0,0 +1 @@ +export { FiatValue } from './FiatValueMod' diff --git a/src/custom/components/Tokens/TokensTableRow.tsx b/src/custom/components/Tokens/TokensTableRow.tsx index fd26866f20..6e91c966ff 100644 --- a/src/custom/components/Tokens/TokensTableRow.tsx +++ b/src/custom/components/Tokens/TokensTableRow.tsx @@ -141,7 +141,7 @@ const DataRow = ({ Approved:{' '} - + From 11c52c9c5ac766529cc9e633a8ebc07d9d4ce3f8 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Tue, 7 Feb 2023 20:12:52 +0600 Subject: [PATCH 31/42] Fix code style --- src/cow-react/pages/Account/Balances.tsx | 18 +++++++-------- .../pages/Account/LockedGnoVesting/index.tsx | 22 ++++++++++--------- src/cow-react/utils/amountFormat/README.md | 6 +++++ .../CurrencyList/CurrencyListMod.tsx | 2 +- .../SearchModal/CurrencyList/index.tsx | 1 - src/custom/state/orders/priceUtils.ts | 6 ++--- src/custom/utils/price.ts | 1 + 7 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/cow-react/pages/Account/Balances.tsx b/src/cow-react/pages/Account/Balances.tsx index eab8f83f65..aa3297cdc3 100644 --- a/src/cow-react/pages/Account/Balances.tsx +++ b/src/cow-react/pages/Account/Balances.tsx @@ -88,12 +88,6 @@ export default function Profile() { return output }, [isLockedGnoLoading, isVCowLoading, provider]) - const vCowBalanceVested = ( - - ) - const vCowBalanceUnvested = - const vCowBalance = - // Init modal hooks const { handleSetError, handleCloseError, ErrorModal } = useErrorModal() const { TransactionConfirmationModal, openModal, closeModal } = useTransactionConfirmationModal( @@ -130,10 +124,16 @@ export default function Profile() { balanceBreakdown: ( - Unvested

{vCowBalanceUnvested}

+ Unvested{' '} +

+ +

- Vested

{vCowBalanceVested}

+ Vested{' '} +

+ +

), @@ -219,7 +219,7 @@ export default function Profile() { Total vCOW balance
- {vCowBalance}{' '} + {' '} diff --git a/src/cow-react/pages/Account/LockedGnoVesting/index.tsx b/src/cow-react/pages/Account/LockedGnoVesting/index.tsx index c08203a0d2..119fa81beb 100644 --- a/src/cow-react/pages/Account/LockedGnoVesting/index.tsx +++ b/src/cow-react/pages/Account/LockedGnoVesting/index.tsx @@ -45,12 +45,6 @@ const LockedGnoVesting: React.FC = ({ openModal, closeModal, vested, allo const { chainId = ChainId.MAINNET, account } = useWeb3React() const [status, setStatus] = useState(ClaimStatus.INITIAL) const unvested = allocated.subtract(vested) - - const allocatedFormatted = - const vestedFormatted = - const unvestedFormatted = - const claimableFormatted = - const previousAccount = usePrevious(account) const canClaim = @@ -138,16 +132,22 @@ const LockedGnoVesting: React.FC = ({ openModal, closeModal, vested, allo COW vesting from locked GNO - {allocatedFormatted} + - Unvested

{unvestedFormatted}

+ Unvested{' '} +

+ +

- Vested

{vestedFormatted}

+ Vested{' '} +

+ +

} @@ -176,7 +176,9 @@ const LockedGnoVesting: React.FC = ({ openModal, closeModal, vested, allo
- {claimableFormatted} + + + {status === ClaimStatus.CONFIRMED ? ( diff --git a/src/cow-react/utils/amountFormat/README.md b/src/cow-react/utils/amountFormat/README.md index 856b4eb63e..51d79717e4 100644 --- a/src/cow-react/utils/amountFormat/README.md +++ b/src/cow-react/utils/amountFormat/README.md @@ -11,6 +11,12 @@ There are two types of amounts: The solution uses [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat) API for numbers formatting. +### Trailing zeros + +All functions and components mentioned in this documents are using `trimTrailingZeros()` function to avoid values with trailing zeros. + +Example: `trimTrailingZeros('12.092000') -> 12.092` + ### Precision The `smart` precision algorithm: diff --git a/src/custom/components/SearchModal/CurrencyList/CurrencyListMod.tsx b/src/custom/components/SearchModal/CurrencyList/CurrencyListMod.tsx index d5c050386f..d6a42af61e 100644 --- a/src/custom/components/SearchModal/CurrencyList/CurrencyListMod.tsx +++ b/src/custom/components/SearchModal/CurrencyList/CurrencyListMod.tsx @@ -6,7 +6,7 @@ import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent' import { LightGreyCard } from 'components/Card' import QuestionHelper from 'components/QuestionHelper' import useTheme from 'hooks/useTheme' -import React, { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react' +import { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react' import { FixedSizeList } from 'react-window' import { Text } from 'rebass' import styled from 'styled-components/macro' diff --git a/src/custom/components/SearchModal/CurrencyList/index.tsx b/src/custom/components/SearchModal/CurrencyList/index.tsx index 1f1b048940..743eea3ef6 100644 --- a/src/custom/components/SearchModal/CurrencyList/index.tsx +++ b/src/custom/components/SearchModal/CurrencyList/index.tsx @@ -14,7 +14,6 @@ import Column from 'components/Column' import { MenuItem as MenuItemMod } from '@src/components/SearchModal/styleds' import { transparentize } from 'polished' import { TokenAmount } from '@cow/common/pure/TokenAmount' -import React from 'react' const UNSUPPORTED_TOKEN_TAG = [ { diff --git a/src/custom/state/orders/priceUtils.ts b/src/custom/state/orders/priceUtils.ts index 8a904257d9..cbc2ef1e30 100644 --- a/src/custom/state/orders/priceUtils.ts +++ b/src/custom/state/orders/priceUtils.ts @@ -3,14 +3,14 @@ import { DEFAULT_DECIMALS } from 'constants/index' import { BigNumber } from 'bignumber.js' -interface Token { +interface PriceTokenInfo { amount: BigNumber | string decimals?: number } interface CalculatePriceParams { - numerator: Token - denominator: Token + numerator: PriceTokenInfo + denominator: PriceTokenInfo } export const ONE_BIG_NUMBER = new BigNumber(1) diff --git a/src/custom/utils/price.ts b/src/custom/utils/price.ts index ea5b27b313..0220a8468b 100644 --- a/src/custom/utils/price.ts +++ b/src/custom/utils/price.ts @@ -242,6 +242,7 @@ function _checkFeeErrorForData(error: GpQuoteError) { } } +// TODO: move to state/orders/priceUtils function formatAtoms(amount: string, decimals: number): string { return BigNumber.from(amount) .div(10 ** decimals) From 19f4cd73ed5fd22b8f1c284f3278a95f79e24df7 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Tue, 7 Feb 2023 20:26:59 +0600 Subject: [PATCH 32/42] TODOs, Docs, feature flag for amounts formatting --- README.md | 5 +++++ src/cow-react/common/pure/FiatAmount/index.tsx | 14 ++++++++++---- src/cow-react/common/pure/TokenAmount/index.tsx | 14 ++++++++++---- src/cow-react/constants/featureFlags.ts | 1 + .../limitOrders/pure/ReceiptModal/FilledField.tsx | 1 + .../swap/containers/NewSwapWidget/index.tsx | 2 ++ .../modules/swap/containers/Row/RowFee/index.tsx | 1 + src/cow-react/utils/amountFormat/README.md | 3 +++ 8 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 src/cow-react/constants/featureFlags.ts diff --git a/README.md b/README.md index 1c10ffd011..9ea8e0e481 100644 --- a/README.md +++ b/README.md @@ -164,3 +164,8 @@ The plan: 2. Deploy a new version to production `emergency.js` is not cached by browser and loaded before all. + +## Nested docs + +1. [Amounts formatting](src/cow-react/utils/amountFormat/README.md) + diff --git a/src/cow-react/common/pure/FiatAmount/index.tsx b/src/cow-react/common/pure/FiatAmount/index.tsx index 3f057985e2..acb48791cd 100644 --- a/src/cow-react/common/pure/FiatAmount/index.tsx +++ b/src/cow-react/common/pure/FiatAmount/index.tsx @@ -2,6 +2,9 @@ import { formatFiatAmount } from '@cow/utils/amountFormat' import { FractionLike, Nullish } from '@cow/types' import { FractionUtils } from '@cow/utils/fractionUtils' import { LONG_PRECISION } from 'constants/index' +import { FeatureFlag } from '@cow/utils/featureFlags' +import styled from 'styled-components/macro' +import { TOKEN_AMOUNT_FEATURE_FLAG } from '@cow/constants/featureFlags' export interface FiatAmountProps { amount: Nullish @@ -10,8 +13,11 @@ export interface FiatAmountProps { className?: string } -// TODO: remove after testing -const highlight = !!localStorage.getItem('amountsRefactoring') +const highlight = !!FeatureFlag.get(TOKEN_AMOUNT_FEATURE_FLAG) + +const Wrapper = styled.span<{ highlight: boolean }>` + background: ${({ highlight }) => (highlight ? 'rgba(113,255,18,0.4)' : '')}; +` export function FiatAmount({ amount, defaultValue, className, accurate = false }: FiatAmountProps) { const formattedAmount = formatFiatAmount(amount) @@ -19,9 +25,9 @@ export function FiatAmount({ amount, defaultValue, className, accurate = false } const accuracySymbol = accurate ? '' : '≈ ' return ( - + {formattedAmount ? accuracySymbol + '$' : ''} {formattedAmount || defaultValue} - + ) } diff --git a/src/cow-react/common/pure/TokenAmount/index.tsx b/src/cow-react/common/pure/TokenAmount/index.tsx index 718efea200..2da79f5c0e 100644 --- a/src/cow-react/common/pure/TokenAmount/index.tsx +++ b/src/cow-react/common/pure/TokenAmount/index.tsx @@ -3,6 +3,9 @@ import { FractionLike, Nullish } from '@cow/types' import { TokenSymbol, TokenSymbolProps } from '@cow/common/pure/TokenSymbol' import { FractionUtils } from '@cow/utils/fractionUtils' import { LONG_PRECISION } from 'constants/index' +import { FeatureFlag } from '@cow/utils/featureFlags' +import styled from 'styled-components/macro' +import { TOKEN_AMOUNT_FEATURE_FLAG } from '@cow/constants/featureFlags' export interface TokenAmountProps { amount: Nullish @@ -11,8 +14,11 @@ export interface TokenAmountProps { className?: string } -// TODO: remove after testing -const highlight = !!localStorage.getItem('amountsRefactoring') +const highlight = !!FeatureFlag.get(TOKEN_AMOUNT_FEATURE_FLAG) + +const Wrapper = styled.span<{ highlight: boolean }>` + background: ${({ highlight }) => (highlight ? 'rgba(196,18,255,0.4)' : '')}; +` export function TokenAmount({ amount, defaultValue, className, tokenSymbol }: TokenAmountProps) { const title = @@ -20,11 +26,11 @@ export function TokenAmount({ amount, defaultValue, className, tokenSymbol }: To return ( <> - + {formatTokenAmount(amount) || defaultValue} {tokenSymbol ? ' ' : ''} {tokenSymbol ? : ''} - + ) } diff --git a/src/cow-react/constants/featureFlags.ts b/src/cow-react/constants/featureFlags.ts new file mode 100644 index 0000000000..41d839aaf2 --- /dev/null +++ b/src/cow-react/constants/featureFlags.ts @@ -0,0 +1 @@ +export const TOKEN_AMOUNT_FEATURE_FLAG = 'amountsRefactoring' diff --git a/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx b/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx index d08c60db00..bfa408d6ec 100644 --- a/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx +++ b/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx @@ -15,6 +15,7 @@ interface Props { } // TODO: using .toNumber() we potentially lose accuracy +// TODO: must be refactored with replacing bignumber.js by @ethersproject/bignumber function legacyBigNumberToCurrencyAmount(currency: Token, value: BigNumber | undefined): CurrencyAmount { return CurrencyAmount.fromRawAmount(currency, Math.ceil((value?.toNumber() || 0) * 10 ** currency.decimals)) } diff --git a/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx b/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx index d66e8ccf64..660a83089f 100644 --- a/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx +++ b/src/cow-react/modules/swap/containers/NewSwapWidget/index.tsx @@ -80,6 +80,8 @@ export function NewSwapWidget() { const inputCurrencyBalance = useCurrencyBalance(account ?? undefined, currencies.INPUT) || null const outputCurrencyBalance = useCurrencyBalance(account ?? undefined, currencies.OUTPUT) || null + // TODO: unify CurrencyInfo assembling between Swap and Limit orders + // TODO: delegate formatting to the view layer const inputCurrencyInfo: CurrencyInfo = { field: Field.INPUT, currency: currencies.INPUT || null, diff --git a/src/cow-react/modules/swap/containers/Row/RowFee/index.tsx b/src/cow-react/modules/swap/containers/Row/RowFee/index.tsx index 2a60a18538..2b777f0df1 100644 --- a/src/cow-react/modules/swap/containers/Row/RowFee/index.tsx +++ b/src/cow-react/modules/swap/containers/Row/RowFee/index.tsx @@ -65,6 +65,7 @@ export function RowFee({ trade, fee, feeFiatValue, allowsOffchainSigning, showHe const props = useMemo(() => { const displayFee = realizedFee || fee const feeCurrencySymbol = displayFee?.currency.symbol || '-' + // TODO: delegate formatting to the view layer const smartFeeFiatValue = formatFiatAmount(feeFiatValue) const smartFeeTokenValue = formatTokenAmount(displayFee) const feeAmountWithCurrency = `${smartFeeTokenValue} ${formatSymbol(feeCurrencySymbol)} ${ diff --git a/src/cow-react/utils/amountFormat/README.md b/src/cow-react/utils/amountFormat/README.md index 51d79717e4..df5e0908c4 100644 --- a/src/cow-react/utils/amountFormat/README.md +++ b/src/cow-react/utils/amountFormat/README.md @@ -7,6 +7,9 @@ There are two types of amounts: - token amount: `100 000.54 COW` - fiat amounts: `≈ $1 946 628.4` +### Feature-flags +`localStorage.setItem('amountsRefactoring', '1')` - to highlight components with formatted tokens and fiat amounts + ### Language-sensitive number formatting The solution uses [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat) API for numbers formatting. From 9f98ef1f30d9d2dcfce085c13dd59db318eaf7fb Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Tue, 7 Feb 2023 20:27:45 +0600 Subject: [PATCH 33/42] Rename FF --- src/cow-react/common/pure/FiatAmount/index.tsx | 4 ++-- src/cow-react/common/pure/TokenAmount/index.tsx | 4 ++-- src/cow-react/constants/featureFlags.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cow-react/common/pure/FiatAmount/index.tsx b/src/cow-react/common/pure/FiatAmount/index.tsx index acb48791cd..d6e0edce4e 100644 --- a/src/cow-react/common/pure/FiatAmount/index.tsx +++ b/src/cow-react/common/pure/FiatAmount/index.tsx @@ -4,7 +4,7 @@ import { FractionUtils } from '@cow/utils/fractionUtils' import { LONG_PRECISION } from 'constants/index' import { FeatureFlag } from '@cow/utils/featureFlags' import styled from 'styled-components/macro' -import { TOKEN_AMOUNT_FEATURE_FLAG } from '@cow/constants/featureFlags' +import { AMOUNTS_FORMATTING_FEATURE_FLAG } from '@cow/constants/featureFlags' export interface FiatAmountProps { amount: Nullish @@ -13,7 +13,7 @@ export interface FiatAmountProps { className?: string } -const highlight = !!FeatureFlag.get(TOKEN_AMOUNT_FEATURE_FLAG) +const highlight = !!FeatureFlag.get(AMOUNTS_FORMATTING_FEATURE_FLAG) const Wrapper = styled.span<{ highlight: boolean }>` background: ${({ highlight }) => (highlight ? 'rgba(113,255,18,0.4)' : '')}; diff --git a/src/cow-react/common/pure/TokenAmount/index.tsx b/src/cow-react/common/pure/TokenAmount/index.tsx index 2da79f5c0e..6afddbd12c 100644 --- a/src/cow-react/common/pure/TokenAmount/index.tsx +++ b/src/cow-react/common/pure/TokenAmount/index.tsx @@ -5,7 +5,7 @@ import { FractionUtils } from '@cow/utils/fractionUtils' import { LONG_PRECISION } from 'constants/index' import { FeatureFlag } from '@cow/utils/featureFlags' import styled from 'styled-components/macro' -import { TOKEN_AMOUNT_FEATURE_FLAG } from '@cow/constants/featureFlags' +import { AMOUNTS_FORMATTING_FEATURE_FLAG } from '@cow/constants/featureFlags' export interface TokenAmountProps { amount: Nullish @@ -14,7 +14,7 @@ export interface TokenAmountProps { className?: string } -const highlight = !!FeatureFlag.get(TOKEN_AMOUNT_FEATURE_FLAG) +const highlight = !!FeatureFlag.get(AMOUNTS_FORMATTING_FEATURE_FLAG) const Wrapper = styled.span<{ highlight: boolean }>` background: ${({ highlight }) => (highlight ? 'rgba(196,18,255,0.4)' : '')}; diff --git a/src/cow-react/constants/featureFlags.ts b/src/cow-react/constants/featureFlags.ts index 41d839aaf2..2ff084b7c6 100644 --- a/src/cow-react/constants/featureFlags.ts +++ b/src/cow-react/constants/featureFlags.ts @@ -1 +1 @@ -export const TOKEN_AMOUNT_FEATURE_FLAG = 'amountsRefactoring' +export const AMOUNTS_FORMATTING_FEATURE_FLAG = 'highlight-amounts-formatting' From 57177f41db7407230d432a9ff06d67a628b76efd Mon Sep 17 00:00:00 2001 From: Lint Action Date: Tue, 7 Feb 2023 14:29:51 +0000 Subject: [PATCH 34/42] Fix code style issues with Prettier --- README.md | 1 - src/cow-react/utils/amountFormat/README.md | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ea8e0e481..7a3aabf618 100644 --- a/README.md +++ b/README.md @@ -168,4 +168,3 @@ The plan: ## Nested docs 1. [Amounts formatting](src/cow-react/utils/amountFormat/README.md) - diff --git a/src/cow-react/utils/amountFormat/README.md b/src/cow-react/utils/amountFormat/README.md index df5e0908c4..d901822d7b 100644 --- a/src/cow-react/utils/amountFormat/README.md +++ b/src/cow-react/utils/amountFormat/README.md @@ -8,6 +8,7 @@ There are two types of amounts: - fiat amounts: `≈ $1 946 628.4` ### Feature-flags + `localStorage.setItem('amountsRefactoring', '1')` - to highlight components with formatted tokens and fiat amounts ### Language-sensitive number formatting From 958b2044ef7790c2304855ed237e47cf3202c68d Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Tue, 7 Feb 2023 20:49:06 +0600 Subject: [PATCH 35/42] Fix issues --- .../modules/limitOrders/pure/ReceiptModal/FilledField.tsx | 2 +- src/custom/utils/price.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx b/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx index bfa408d6ec..b6806342af 100644 --- a/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx +++ b/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx @@ -76,7 +76,7 @@ export function FilledField({ order, sellAmount, buyAmount }: Props) { const formattedFilledAmount = legacyBigNumberToCurrencyAmount(mainToken, filledAmountDecimal) const swappedAmountDecimal = swappedAmountWithFee.div(new BigNumber(10 ** swappedToken.decimals)) - const formattedSwappedAmount = legacyBigNumberToCurrencyAmount(mainToken, swappedAmountDecimal) + const formattedSwappedAmount = legacyBigNumberToCurrencyAmount(outputToken, swappedAmountDecimal) const formattedPercentage = useMemo(() => { if (!filledPercentage) { diff --git a/src/custom/utils/price.ts b/src/custom/utils/price.ts index 0220a8468b..c2b35f5160 100644 --- a/src/custom/utils/price.ts +++ b/src/custom/utils/price.ts @@ -244,10 +244,11 @@ function _checkFeeErrorForData(error: GpQuoteError) { // TODO: move to state/orders/priceUtils function formatAtoms(amount: string, decimals: number): string { - return BigNumber.from(amount) + return BigNumberJs(amount) .div(10 ** decimals) .toString() } + /** * (LEGACY) Will be overwritten in the near future * Return the best quote considering all price feeds. The quote contains information about the price and fee From 83382af1c36c750d865e895074e9666bf61a0cd9 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Tue, 7 Feb 2023 21:10:29 +0600 Subject: [PATCH 36/42] Round balance amount --- src/cow-react/utils/fractionUtils.ts | 9 ++++++++- src/custom/components/CowBalanceButton/index.tsx | 6 +++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/cow-react/utils/fractionUtils.ts b/src/cow-react/utils/fractionUtils.ts index d6e87bbb28..f02ffd9f37 100644 --- a/src/cow-react/utils/fractionUtils.ts +++ b/src/cow-react/utils/fractionUtils.ts @@ -1,7 +1,8 @@ -import { CurrencyAmount, Fraction, Price, BigintIsh } from '@uniswap/sdk-core' +import { CurrencyAmount, Fraction, Price, BigintIsh, Rounding } from '@uniswap/sdk-core' import { FractionLike, Nullish } from '@cow/types' import { FULL_PRICE_PRECISION } from 'constants/index' import { trimTrailingZeros } from '@cow/utils/trimTrailingZeros' +import JSBI from 'jsbi' export class FractionUtils { static serializeFractionToJSON(fraction: Nullish): string { @@ -48,6 +49,12 @@ export class FractionUtils { return amount } + static round(value: FractionLike, rounding: Rounding = Rounding.ROUND_UP): Fraction { + const { quotient, remainder } = FractionUtils.fractionLikeToFraction(value) + + return new Fraction(JSBI.add(quotient, JSBI.BigInt(remainder.toFixed(0, undefined, rounding))), 1) + } + static gte(fraction: Fraction, value: BigintIsh): boolean { return fraction.equalTo(value) || fraction.greaterThan(value) } diff --git a/src/custom/components/CowBalanceButton/index.tsx b/src/custom/components/CowBalanceButton/index.tsx index eaf93ae4f4..82b912daf4 100644 --- a/src/custom/components/CowBalanceButton/index.tsx +++ b/src/custom/components/CowBalanceButton/index.tsx @@ -6,6 +6,7 @@ import { transparentize } from 'polished' import { useWeb3React } from '@web3-react/core' import { supportedChainId } from 'utils/supportedChainId' import { TokenAmount } from '@cow/common/pure/TokenAmount' +import { FractionUtils } from '@cow/utils/fractionUtils' export const Wrapper = styled.div<{ isLoading: boolean }>` background-color: transparent; @@ -86,12 +87,15 @@ export default function CowBalanceButton({ onClick, isUpToSmall }: CowBalanceBut return null } + // We don't need to display decimals symbols, so, we round the balance amount + const roundedAmount = FractionUtils.round(balance) + return ( {!isUpToSmall && ( - + )} From d8b1bec4c419b9d034d4b48d95b94457199f15f4 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Tue, 7 Feb 2023 21:27:00 +0600 Subject: [PATCH 37/42] Fix issues --- src/cow-react/common/pure/TokenAmount/index.tsx | 7 +++++-- src/custom/components/CowBalanceButton/index.tsx | 6 +----- src/custom/components/Tokens/styled.ts | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/cow-react/common/pure/TokenAmount/index.tsx b/src/cow-react/common/pure/TokenAmount/index.tsx index 6afddbd12c..fd42752629 100644 --- a/src/cow-react/common/pure/TokenAmount/index.tsx +++ b/src/cow-react/common/pure/TokenAmount/index.tsx @@ -12,6 +12,7 @@ export interface TokenAmountProps { defaultValue?: string tokenSymbol?: TokenSymbolProps['token'] className?: string + round?: boolean } const highlight = !!FeatureFlag.get(AMOUNTS_FORMATTING_FEATURE_FLAG) @@ -20,14 +21,16 @@ const Wrapper = styled.span<{ highlight: boolean }>` background: ${({ highlight }) => (highlight ? 'rgba(196,18,255,0.4)' : '')}; ` -export function TokenAmount({ amount, defaultValue, className, tokenSymbol }: TokenAmountProps) { +export function TokenAmount({ amount, defaultValue, className, tokenSymbol, round }: TokenAmountProps) { const title = FractionUtils.fractionLikeToExactString(amount, LONG_PRECISION) + (tokenSymbol ? ` ${tokenSymbol.symbol}` : '') + if (!amount) return null + return ( <> - {formatTokenAmount(amount) || defaultValue} + {formatTokenAmount(round ? FractionUtils.round(amount) : amount) || defaultValue} {tokenSymbol ? ' ' : ''} {tokenSymbol ? : ''} diff --git a/src/custom/components/CowBalanceButton/index.tsx b/src/custom/components/CowBalanceButton/index.tsx index 82b912daf4..dc4d51e2d9 100644 --- a/src/custom/components/CowBalanceButton/index.tsx +++ b/src/custom/components/CowBalanceButton/index.tsx @@ -6,7 +6,6 @@ import { transparentize } from 'polished' import { useWeb3React } from '@web3-react/core' import { supportedChainId } from 'utils/supportedChainId' import { TokenAmount } from '@cow/common/pure/TokenAmount' -import { FractionUtils } from '@cow/utils/fractionUtils' export const Wrapper = styled.div<{ isLoading: boolean }>` background-color: transparent; @@ -87,15 +86,12 @@ export default function CowBalanceButton({ onClick, isUpToSmall }: CowBalanceBut return null } - // We don't need to display decimals symbols, so, we round the balance amount - const roundedAmount = FractionUtils.round(balance) - return ( {!isUpToSmall && ( - + )} diff --git a/src/custom/components/Tokens/styled.ts b/src/custom/components/Tokens/styled.ts index d54e0831eb..70b5dde075 100644 --- a/src/custom/components/Tokens/styled.ts +++ b/src/custom/components/Tokens/styled.ts @@ -396,7 +396,7 @@ export const ApproveLabel = styled.span<{ color?: string }>` ` export const CustomLimit = styled.div` - span:last-child { + > span:last-child { cursor: default; font-size: 10px; margin-top: 5px; From 36c967d707e83fa8aa06e362147eeb8bcceca291 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Tue, 7 Feb 2023 21:39:46 +0600 Subject: [PATCH 38/42] Show less than 0.0001 for tokens with 0 decimals --- src/cow-react/utils/amountFormat/index.ts | 2 +- src/cow-react/utils/amountFormat/utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cow-react/utils/amountFormat/index.ts b/src/cow-react/utils/amountFormat/index.ts index f7b5b8e555..6c2faadacf 100644 --- a/src/cow-react/utils/amountFormat/index.ts +++ b/src/cow-react/utils/amountFormat/index.ts @@ -43,7 +43,7 @@ export function formatAmountWithPrecision(amount: Nullish, precisi const formattedRemainder = remainder.greaterThan(0) ? trimTrailingZeros(fixedRemainder.slice(1)) : '' const result = formattedQuotient + formattedRemainder + suffix - return remainder.greaterThan(0) && +result === 0 ? lessThanPrecisionSymbol(precision) : result + return amount.greaterThan(0) && +result === 0 ? lessThanPrecisionSymbol(precision) : result } export function formatInputAmount( diff --git a/src/cow-react/utils/amountFormat/utils.ts b/src/cow-react/utils/amountFormat/utils.ts index 92a6c962bc..36caec0fee 100644 --- a/src/cow-react/utils/amountFormat/utils.ts +++ b/src/cow-react/utils/amountFormat/utils.ts @@ -48,5 +48,5 @@ export function trimHugeAmounts(amount: Fraction): Fraction { } export function lessThanPrecisionSymbol(precision: number): string { - return `< 0.${'0'.repeat(precision - 1)}1` + return `< 0.${'0'.repeat((precision || 4) - 1)}1` } From 8e5199fdff5043c59d65ff6fab38880fdc707f91 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Tue, 7 Feb 2023 21:41:31 +0600 Subject: [PATCH 39/42] Fix e2e test --- cypress-custom/integration/limit-orders.test.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cypress-custom/integration/limit-orders.test.ts b/cypress-custom/integration/limit-orders.test.ts index 35b8df1c4c..f341310427 100644 --- a/cypress-custom/integration/limit-orders.test.ts +++ b/cypress-custom/integration/limit-orders.test.ts @@ -27,9 +27,6 @@ describe('Limit orders', () => { 'have.value', inputAmount.toString() ) - cy.get('#limit-orders-confirm #output-currency-preview .token-amount-input').should( - 'have.value', - outputAmount.toString() - ) + cy.get('#limit-orders-confirm #output-currency-preview .token-amount-input').should('have.value', '200B') }) }) From 97df229e423b9c44bb90506c7a36134b6601dffe Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Wed, 8 Feb 2023 12:27:47 +0600 Subject: [PATCH 40/42] Fix issues --- .../integration/limit-orders.test.ts | 4 +-- .../CurrencyInputPanel/CurrencyPreview.tsx | 12 +-------- .../common/pure/CurrencyInputPanel/styled.tsx | 11 ++++++++ .../pure/ReceiptModal/FilledField.tsx | 2 +- src/cow-react/utils/amountFormat/README.md | 1 + .../utils/amountFormat/index.test.ts | 27 +++++++++++++++++-- src/cow-react/utils/amountFormat/index.ts | 13 ++++++--- .../components/CowBalanceButton/index.tsx | 2 +- 8 files changed, 52 insertions(+), 20 deletions(-) diff --git a/cypress-custom/integration/limit-orders.test.ts b/cypress-custom/integration/limit-orders.test.ts index f341310427..1fbbed0b8d 100644 --- a/cypress-custom/integration/limit-orders.test.ts +++ b/cypress-custom/integration/limit-orders.test.ts @@ -24,9 +24,9 @@ describe('Limit orders', () => { cy.get('#limit-orders-currency-output .token-amount-input').should('have.value', outputAmount.toString()) cy.get('#limit-orders-confirm #input-currency-preview .token-amount-input').should( - 'have.value', + 'contain.text', inputAmount.toString() ) - cy.get('#limit-orders-confirm #output-currency-preview .token-amount-input').should('have.value', '200B') + cy.get('#limit-orders-confirm #output-currency-preview .token-amount-input').should('contain.text', '200B') }) }) diff --git a/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx b/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx index da92b2fbdf..886ae3ccd5 100644 --- a/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx +++ b/src/cow-react/common/pure/CurrencyInputPanel/CurrencyPreview.tsx @@ -1,4 +1,3 @@ -import React from 'react' import * as styledEl from './styled' import { CurrencySelectButton } from '@cow/modules/swap/pure/CurrencySelectButton' import { FiatValue } from 'custom/components/CurrencyInputPanel/FiatValue/FiatValueMod' @@ -6,8 +5,6 @@ import { Trans } from '@lingui/macro' import { PriceImpact } from 'hooks/usePriceImpact' import { CurrencyInfo } from '@cow/common/pure/CurrencyInputPanel/types' import { TokenAmount } from '@cow/common/pure/TokenAmount' -import { FractionUtils } from '@cow/utils/fractionUtils' -import { formatTokenAmount } from '@cow/utils/amountFormat' interface BuiltItProps { className: string @@ -35,14 +32,7 @@ export function CurrencyPreview(props: CurrencyPreviewProps) {
- void 0} - $loading={false} - /> +
diff --git a/src/cow-react/common/pure/CurrencyInputPanel/styled.tsx b/src/cow-react/common/pure/CurrencyInputPanel/styled.tsx index c74298a997..dd33a2097e 100644 --- a/src/cow-react/common/pure/CurrencyInputPanel/styled.tsx +++ b/src/cow-react/common/pure/CurrencyInputPanel/styled.tsx @@ -2,6 +2,7 @@ import styled from 'styled-components/macro' import { loadingOpacityMixin } from 'components/Loader/styled' import Input from 'components/NumericalInput' import { transparentize } from 'polished' +import { TokenAmount } from '@cow/common/pure/TokenAmount' export const Wrapper = styled.div<{ withReceiveAmountInfo: boolean; disabled: boolean }>` display: flex; @@ -75,6 +76,16 @@ export const NumericalInput = styled(Input)<{ $loading: boolean }>` ${loadingOpacityMixin} ` +export const TokenAmountStyled = styled(TokenAmount)` + font-size: 28px; + font-weight: 500; + color: ${({ theme }) => theme.text1}; + + ${({ theme }) => theme.mediaWidth.upToSmall` + font-size: 26px; + `} +` + export const BalanceText = styled.span` font-weight: inherit; font-size: inherit; diff --git a/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx b/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx index b6806342af..ce06ea2875 100644 --- a/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx +++ b/src/cow-react/modules/limitOrders/pure/ReceiptModal/FilledField.tsx @@ -1,5 +1,5 @@ // Code based on https://github.com/cowprotocol/explorer/blob/develop/src/components/orders/FilledProgress/index.tsx -import React, { useMemo } from 'react' +import { useMemo } from 'react' import * as styledEl from './styled' import { ParsedOrder } from '@cow/modules/limitOrders/containers/OrdersWidget/hooks/useLimitOrdersList' import { CurrencyAmount, Token } from '@uniswap/sdk-core' diff --git a/src/cow-react/utils/amountFormat/README.md b/src/cow-react/utils/amountFormat/README.md index d901822d7b..45014a6f0e 100644 --- a/src/cow-react/utils/amountFormat/README.md +++ b/src/cow-react/utils/amountFormat/README.md @@ -20,6 +20,7 @@ The solution uses [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/W All functions and components mentioned in this documents are using `trimTrailingZeros()` function to avoid values with trailing zeros. Example: `trimTrailingZeros('12.092000') -> 12.092` +Example: `trimTrailingZeros('24.000000') -> 24` ### Precision diff --git a/src/cow-react/utils/amountFormat/index.test.ts b/src/cow-react/utils/amountFormat/index.test.ts index c139144361..18642b33b3 100644 --- a/src/cow-react/utils/amountFormat/index.test.ts +++ b/src/cow-react/utils/amountFormat/index.test.ts @@ -1,4 +1,4 @@ -import { formatFiatAmount, formatPercent, formatTokenAmount } from './index' +import { formatAmountWithPrecision, formatFiatAmount, formatPercent, formatTokenAmount } from './index' import { CurrencyAmount, Percent } from '@uniswap/sdk-core' import { DAI_GOERLI } from 'utils/goerli/constants' import { USDC_GNOSIS_CHAIN } from '../../../custom/utils/gnosis_chain/constants' @@ -120,7 +120,7 @@ describe('Amounts formatting', () => { expect(result).toBe('1') }) - it('Precision for fiat amounts is always 3', () => { + it('Precision for fiat amounts is always 2', () => { const result1 = formatFiatAmount(getAmount('734436023451', -5)) const result2 = formatFiatAmount(getAmount('60001444', 3)) @@ -148,4 +148,27 @@ describe('Amounts formatting', () => { expect(result).toBe('0.12') }) }) + + describe('Internationalization', () => { + it('ES', () => { + const numberFormat = new Intl.NumberFormat('es') + const result = formatAmountWithPrecision(getAmount('7850450043', -5), 2, numberFormat) + + expect(result).toBe('78.504,50') + }) + + it('EN', () => { + const numberFormat = new Intl.NumberFormat('en') + const result = formatAmountWithPrecision(getAmount('7850450043', -5), 2, numberFormat) + + expect(result).toBe('78,504.50') + }) + + it('RU', () => { + const numberFormat = new Intl.NumberFormat('ru') + const result = formatAmountWithPrecision(getAmount('7850450043', -5), 2, numberFormat) + + expect(result).toBe('78 504,50') + }) + }) }) diff --git a/src/cow-react/utils/amountFormat/index.ts b/src/cow-react/utils/amountFormat/index.ts index 6c2faadacf..d1538437df 100644 --- a/src/cow-react/utils/amountFormat/index.ts +++ b/src/cow-react/utils/amountFormat/index.ts @@ -19,7 +19,11 @@ export function formatPercent(percent: Nullish): string { return percent ? trimTrailingZeros(percent.toFixed(PERCENTAGE_PRECISION)) : '' } -export function formatAmountWithPrecision(amount: Nullish, precision: number): string { +export function formatAmountWithPrecision( + amount: Nullish, + precision: number, + numberFormat = INTL_NUMBER_FORMAT +): string { if (!amount) return '' // Align fraction-like types to Fraction @@ -31,7 +35,7 @@ export function formatAmountWithPrecision(amount: Nullish, precisi // Apply the language formatting for the amount // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat - const formattedQuotient = INTL_NUMBER_FORMAT.format(BigInt(trimTrailingZeros(quotient.toString()))) + const formattedQuotient = numberFormat.format(BigInt(trimTrailingZeros(quotient.toString()))) // Trim the remainder up to precision const fixedRemainder = remainder.toFixed(precision, undefined, Rounding.ROUND_HALF_UP) @@ -40,7 +44,10 @@ export function formatAmountWithPrecision(amount: Nullish, precisi return trimTrailingZeros(fixedRemainder) } - const formattedRemainder = remainder.greaterThan(0) ? trimTrailingZeros(fixedRemainder.slice(1)) : '' + const decimalsSeparator = numberFormat.format(1.1)[1] + const formattedRemainder = remainder.greaterThan(0) + ? decimalsSeparator + trimTrailingZeros(fixedRemainder.slice(2)) + : '' const result = formattedQuotient + formattedRemainder + suffix return amount.greaterThan(0) && +result === 0 ? lessThanPrecisionSymbol(precision) : result diff --git a/src/custom/components/CowBalanceButton/index.tsx b/src/custom/components/CowBalanceButton/index.tsx index dc4d51e2d9..a4b31e1012 100644 --- a/src/custom/components/CowBalanceButton/index.tsx +++ b/src/custom/components/CowBalanceButton/index.tsx @@ -91,7 +91,7 @@ export default function CowBalanceButton({ onClick, isUpToSmall }: CowBalanceBut {!isUpToSmall && ( - + )} From 149a6a4c99c6212ca7feb3781bbda4e4b98d7ee6 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Wed, 8 Feb 2023 12:31:49 +0600 Subject: [PATCH 41/42] Fix (v)COW balance displaying --- .../common/pure/TokenAmount/index.tsx | 21 ++++++++++++++++--- .../components/CowBalanceButton/index.tsx | 8 ++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/cow-react/common/pure/TokenAmount/index.tsx b/src/cow-react/common/pure/TokenAmount/index.tsx index fd42752629..2982d1628b 100644 --- a/src/cow-react/common/pure/TokenAmount/index.tsx +++ b/src/cow-react/common/pure/TokenAmount/index.tsx @@ -12,6 +12,7 @@ export interface TokenAmountProps { defaultValue?: string tokenSymbol?: TokenSymbolProps['token'] className?: string + hideTokenSymbol?: boolean round?: boolean } @@ -21,18 +22,32 @@ const Wrapper = styled.span<{ highlight: boolean }>` background: ${({ highlight }) => (highlight ? 'rgba(196,18,255,0.4)' : '')}; ` -export function TokenAmount({ amount, defaultValue, className, tokenSymbol, round }: TokenAmountProps) { +export function TokenAmount({ + amount, + defaultValue, + className, + tokenSymbol, + round, + hideTokenSymbol, +}: TokenAmountProps) { const title = FractionUtils.fractionLikeToExactString(amount, LONG_PRECISION) + (tokenSymbol ? ` ${tokenSymbol.symbol}` : '') if (!amount) return null + const tokenSymbolElement = + hideTokenSymbol || !tokenSymbol ? null : ( + <> + {' '} + + + ) + return ( <> {formatTokenAmount(round ? FractionUtils.round(amount) : amount) || defaultValue} - {tokenSymbol ? ' ' : ''} - {tokenSymbol ? : ''} + {tokenSymbolElement} ) diff --git a/src/custom/components/CowBalanceButton/index.tsx b/src/custom/components/CowBalanceButton/index.tsx index a4b31e1012..fc1c4ff7de 100644 --- a/src/custom/components/CowBalanceButton/index.tsx +++ b/src/custom/components/CowBalanceButton/index.tsx @@ -91,7 +91,13 @@ export default function CowBalanceButton({ onClick, isUpToSmall }: CowBalanceBut {!isUpToSmall && ( - + )} From be28eb02fde8945066a452a38f5d287397ad93fc Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Wed, 8 Feb 2023 12:45:23 +0600 Subject: [PATCH 42/42] Fix zero trim regarding locale --- src/cow-react/utils/amountFormat/index.test.ts | 6 +++--- src/cow-react/utils/amountFormat/index.ts | 6 +++--- src/cow-react/utils/trimTrailingZeros.ts | 7 +++---- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/cow-react/utils/amountFormat/index.test.ts b/src/cow-react/utils/amountFormat/index.test.ts index 18642b33b3..ea226ea0a2 100644 --- a/src/cow-react/utils/amountFormat/index.test.ts +++ b/src/cow-react/utils/amountFormat/index.test.ts @@ -154,21 +154,21 @@ describe('Amounts formatting', () => { const numberFormat = new Intl.NumberFormat('es') const result = formatAmountWithPrecision(getAmount('7850450043', -5), 2, numberFormat) - expect(result).toBe('78.504,50') + expect(result).toBe('78.504,5') }) it('EN', () => { const numberFormat = new Intl.NumberFormat('en') const result = formatAmountWithPrecision(getAmount('7850450043', -5), 2, numberFormat) - expect(result).toBe('78,504.50') + expect(result).toBe('78,504.5') }) it('RU', () => { const numberFormat = new Intl.NumberFormat('ru') const result = formatAmountWithPrecision(getAmount('7850450043', -5), 2, numberFormat) - expect(result).toBe('78 504,50') + expect(result).toBe('78 504,5') }) }) }) diff --git a/src/cow-react/utils/amountFormat/index.ts b/src/cow-react/utils/amountFormat/index.ts index d1538437df..23735667ee 100644 --- a/src/cow-react/utils/amountFormat/index.ts +++ b/src/cow-react/utils/amountFormat/index.ts @@ -33,9 +33,10 @@ export function formatAmountWithPrecision( // For cases when an amount is more than billions const { quotient, remainder } = trimHugeAmounts(amountAsFraction) + const decimalsSeparator = numberFormat.format(1.1)[1] // Apply the language formatting for the amount // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat - const formattedQuotient = numberFormat.format(BigInt(trimTrailingZeros(quotient.toString()))) + const formattedQuotient = numberFormat.format(BigInt(trimTrailingZeros(quotient.toString(), decimalsSeparator))) // Trim the remainder up to precision const fixedRemainder = remainder.toFixed(precision, undefined, Rounding.ROUND_HALF_UP) @@ -44,9 +45,8 @@ export function formatAmountWithPrecision( return trimTrailingZeros(fixedRemainder) } - const decimalsSeparator = numberFormat.format(1.1)[1] const formattedRemainder = remainder.greaterThan(0) - ? decimalsSeparator + trimTrailingZeros(fixedRemainder.slice(2)) + ? decimalsSeparator + trimTrailingZeros(fixedRemainder.slice(1)).slice(1) : '' const result = formattedQuotient + formattedRemainder + suffix diff --git a/src/cow-react/utils/trimTrailingZeros.ts b/src/cow-react/utils/trimTrailingZeros.ts index 7912652093..15246115d0 100644 --- a/src/cow-react/utils/trimTrailingZeros.ts +++ b/src/cow-react/utils/trimTrailingZeros.ts @@ -1,12 +1,11 @@ -const DOT = '.' const ZERO = '0' -export function trimTrailingZeros(value: string): string { - if (!value.includes(DOT)) return value +export function trimTrailingZeros(value: string, decimalsSeparator = '.'): string { + if (!value.includes(decimalsSeparator)) return value const trimmed = value.slice(0, getFirstTrailingZeroIndex(value)) - if (trimmed[trimmed.length - 1] === DOT) return trimmed.slice(0, -1) + if (trimmed[trimmed.length - 1] === decimalsSeparator) return trimmed.slice(0, -1) return trimmed }