From b2d27da0a58a851b950e9364bfdc299debd87357 Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Tue, 5 Sep 2023 12:18:14 +0100 Subject: [PATCH 1/8] chore: allow enough balance check to return unknown state --- .../src/common/hooks/useNeedsApproval.ts | 3 +- .../updaters/UnfillableOrdersUpdater.ts | 3 +- .../src/legacy/state/price/updater.ts | 2 +- .../OrdersTableContainer/OrderRow/index.tsx | 6 +-- .../utils/getOrderParams.ts | 30 ++++++++------- .../modules/tokens/hooks/useEnoughBalance.ts | 37 +++++++++++++------ .../tradeQuote/hooks/useQuoteParams.ts | 2 +- .../src/utils/isEnoughAmount.ts | 8 ++-- 8 files changed, 54 insertions(+), 37 deletions(-) diff --git a/apps/cowswap-frontend/src/common/hooks/useNeedsApproval.ts b/apps/cowswap-frontend/src/common/hooks/useNeedsApproval.ts index 6b5404cd1f..4912c96e6d 100644 --- a/apps/cowswap-frontend/src/common/hooks/useNeedsApproval.ts +++ b/apps/cowswap-frontend/src/common/hooks/useNeedsApproval.ts @@ -39,5 +39,6 @@ export function useNeedsApproval(amount: Nullish>): boo return false } - return !isEnoughAmount(amount, allowance) + const enoughBalance = isEnoughAmount(amount, allowance) + return enoughBalance === false } diff --git a/apps/cowswap-frontend/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts b/apps/cowswap-frontend/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts index 291e1a7de5..c0b0049c1a 100644 --- a/apps/cowswap-frontend/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts +++ b/apps/cowswap-frontend/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts @@ -145,8 +145,9 @@ export function UnfillableOrdersUpdater(): null { const currencyAmount = CurrencyAmount.fromRawAmount(order.inputToken, order.sellAmount) const enoughBalance = hasEnoughBalanceAndAllowance({ account, amount: currencyAmount, balances }) + const verifiedQuote = verifiedQuotesEnabled && enoughBalance === true - _getOrderPrice(chainId, order, enoughBalance && verifiedQuotesEnabled, strategy) + _getOrderPrice(chainId, order, verifiedQuote, strategy) .then((quote) => { if (quote) { const [promisedPrice, promisedFee] = quote diff --git a/apps/cowswap-frontend/src/legacy/state/price/updater.ts b/apps/cowswap-frontend/src/legacy/state/price/updater.ts index d331312936..4489050092 100644 --- a/apps/cowswap-frontend/src/legacy/state/price/updater.ts +++ b/apps/cowswap-frontend/src/legacy/state/price/updater.ts @@ -207,7 +207,7 @@ export default function FeesUpdater(): null { userAddress: account, validTo, isEthFlow, - priceQuality: getPriceQuality({ verifyQuote: enoughBalance && verifiedQuotesEnabled }), + priceQuality: getPriceQuality({ verifyQuote: verifiedQuotesEnabled && enoughBalance === true }), } // Don't refetch if offline. diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx index a4988e7098..0fbfe8a87e 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx @@ -174,7 +174,7 @@ export function OrderRow({ const showCancellationModal = orderActions.getShowCancellationModal(order) const withWarning = - (!hasEnoughBalance || !hasEnoughAllowance) && + (hasEnoughBalance === false || hasEnoughAllowance === false) && // show the warning only for pending and scheduled orders (status === OrderStatus.PENDING || status === OrderStatus.SCHEDULED) const theme = useContext(ThemeContext) @@ -353,10 +353,10 @@ export function OrderRow({ bgColor={theme.alert} content={ - {!hasEnoughBalance && ( + {hasEnoughBalance === false && ( )} - {!hasEnoughAllowance && ( + {hasEnoughAllowance === false && ( )} diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts index ff97c09181..f5a555abc7 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts @@ -12,8 +12,8 @@ export interface OrderParams { sellAmount: CurrencyAmount buyAmount: CurrencyAmount rateInfoParams: RateInfoParams - hasEnoughBalance: boolean - hasEnoughAllowance: boolean + hasEnoughBalance: boolean | undefined + hasEnoughAllowance: boolean | undefined } const PERCENTAGE_FOR_PARTIAL_FILLS = new Percent(5, 10000) // 0.05% @@ -38,18 +38,20 @@ export function getOrderParams( const balance = balances[order.inputToken.address]?.value const allowance = allowances[order.inputToken.address]?.value - let hasEnoughBalance, hasEnoughAllowance - - if (order.partiallyFillable) { - // When balance or allowance are undefined (loading state), show as true - // When loaded, check there's at least PERCENTAGE_FOR_PARTIAL_FILLS of balance/allowance to consider it as enough - const amount = sellAmount.multiply(PERCENTAGE_FOR_PARTIAL_FILLS) - hasEnoughBalance = balance === undefined || isEnoughAmount(amount, balance) - hasEnoughAllowance = allowance === undefined || isEnoughAmount(amount, allowance) - } else { - hasEnoughBalance = isEnoughAmount(sellAmount, balance) - hasEnoughAllowance = isEnoughAmount(sellAmount, allowance) - } + const [hasEnoughBalance, hasEnoughAllowance] = (() => { + if (order.partiallyFillable) { + // When balance or allowance are undefined (loading state), show as true + // When loaded, check there's at least PERCENTAGE_FOR_PARTIAL_FILLS of balance/allowance to consider it as enough + const amount = sellAmount.multiply(PERCENTAGE_FOR_PARTIAL_FILLS) + const hasEnoughBalance = isEnoughAmount(amount, balance) + const hasEnoughAllowance = isEnoughAmount(amount, allowance) + return [hasEnoughBalance, hasEnoughAllowance] + } else { + const hasEnoughBalance = isEnoughAmount(sellAmount, balance) + const hasEnoughAllowance = isEnoughAmount(sellAmount, allowance) + return [hasEnoughBalance, hasEnoughAllowance] + } + })() return { chainId, diff --git a/apps/cowswap-frontend/src/modules/tokens/hooks/useEnoughBalance.ts b/apps/cowswap-frontend/src/modules/tokens/hooks/useEnoughBalance.ts index c5935ee38b..820b538922 100644 --- a/apps/cowswap-frontend/src/modules/tokens/hooks/useEnoughBalance.ts +++ b/apps/cowswap-frontend/src/modules/tokens/hooks/useEnoughBalance.ts @@ -31,7 +31,7 @@ export interface UseEnoughBalanceParams { * @param params Parameters to check balance and optionally the allowance * @returns true if the account has enough balance (and allowance if it applies) */ -export function useEnoughBalanceAndAllowance(params: UseEnoughBalanceParams): boolean { +export function useEnoughBalanceAndAllowance(params: UseEnoughBalanceParams): boolean | undefined { const { account, amount, checkAllowanceAddress } = params const isNativeCurrency = amount?.currency.isNative const token = amount?.currency.wrapped @@ -75,22 +75,37 @@ export interface EnoughBalanceParams extends Omit { + const balance = tokenAddress ? balances[tokenAddress]?.value : undefined + const balanceAmount = isNativeCurrency ? nativeBalance : balance || undefined + return isEnoughAmount(amount, balanceAmount) + })() + + const enoughAllowance = (() => { + if (!tokenAddress || !allowances) { + return undefined + } + if (isNativeCurrency) { + return true + } + const allowance = allowances[tokenAddress]?.value + return allowance && isEnoughAmount(amount, allowance) + })() + + if (enoughBalance === undefined || enoughAllowance === undefined) { + return undefined } - const balanceAmount = isNativeCurrency ? nativeBalance : balance || undefined - const enoughBalance = isEnoughAmount(amount, balanceAmount) - const enoughAllowance = !allowances || isNativeCurrency || (allowance && isEnoughAmount(amount, allowance)) || false - return enoughBalance && enoughAllowance } diff --git a/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useQuoteParams.ts b/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useQuoteParams.ts index bbcd8d948c..e0e9ceca5f 100644 --- a/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useQuoteParams.ts +++ b/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useQuoteParams.ts @@ -45,7 +45,7 @@ export function useQuoteParams(amount: string | null): LegacyFeeQuoteParams | un toDecimals, fromDecimals, isEthFlow: false, - priceQuality: getPriceQuality({ verifyQuote: enoughBalance && verifiedQuotesEnabled }), + priceQuality: getPriceQuality({ verifyQuote: verifiedQuotesEnabled && enoughBalance === true }), } return params diff --git a/apps/cowswap-frontend/src/utils/isEnoughAmount.ts b/apps/cowswap-frontend/src/utils/isEnoughAmount.ts index 910263fde1..ed29513919 100644 --- a/apps/cowswap-frontend/src/utils/isEnoughAmount.ts +++ b/apps/cowswap-frontend/src/utils/isEnoughAmount.ts @@ -3,10 +3,8 @@ import { Currency, CurrencyAmount } from '@uniswap/sdk-core' export function isEnoughAmount( sellAmount: CurrencyAmount, targetAmount: CurrencyAmount | undefined -): boolean { - if (!targetAmount) return true +): boolean | undefined { + if (!targetAmount) return undefined - if (targetAmount.equalTo(sellAmount)) return true - - return sellAmount.lessThan(targetAmount) + return sellAmount.equalTo(targetAmount) || sellAmount.lessThan(targetAmount) } From c56eb65324c720e6d272ac10a92456bd16450eee Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Tue, 5 Sep 2023 12:28:58 +0100 Subject: [PATCH 2/8] chore: allow undefined in the getPriceQuality fn --- apps/cowswap-frontend/src/api/gnosisProtocol/api.ts | 4 ++-- .../state/orders/updaters/UnfillableOrdersUpdater.ts | 9 +++++++-- apps/cowswap-frontend/src/legacy/state/price/updater.ts | 2 +- .../src/modules/tradeQuote/hooks/useQuoteParams.ts | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/cowswap-frontend/src/api/gnosisProtocol/api.ts b/apps/cowswap-frontend/src/api/gnosisProtocol/api.ts index 77b0b9c398..0a22504317 100644 --- a/apps/cowswap-frontend/src/api/gnosisProtocol/api.ts +++ b/apps/cowswap-frontend/src/api/gnosisProtocol/api.ts @@ -14,7 +14,7 @@ import { PriceQuality, TotalSurplus, OrderQuoteSideKindBuy, - OrderQuoteSideKindSell + OrderQuoteSideKindSell, } from '@cowprotocol/cow-sdk' import { orderBookApi } from 'cowSdk' @@ -225,7 +225,7 @@ export async function getProfileData(chainId: ChainId, address: string): Promise } } -export function getPriceQuality(props: { fast?: boolean; verifyQuote: boolean }): PriceQuality { +export function getPriceQuality(props: { fast?: boolean; verifyQuote: boolean | undefined }): PriceQuality { const { fast = false, verifyQuote } = props if (fast) { return PriceQuality.FAST diff --git a/apps/cowswap-frontend/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts b/apps/cowswap-frontend/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts index c0b0049c1a..2ee794f560 100644 --- a/apps/cowswap-frontend/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts +++ b/apps/cowswap-frontend/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts @@ -145,7 +145,7 @@ export function UnfillableOrdersUpdater(): null { const currencyAmount = CurrencyAmount.fromRawAmount(order.inputToken, order.sellAmount) const enoughBalance = hasEnoughBalanceAndAllowance({ account, amount: currencyAmount, balances }) - const verifiedQuote = verifiedQuotesEnabled && enoughBalance === true + const verifiedQuote = verifiedQuotesEnabled && enoughBalance _getOrderPrice(chainId, order, verifiedQuote, strategy) .then((quote) => { @@ -208,7 +208,12 @@ export function UnfillableOrdersUpdater(): null { /** * Thin wrapper around `getBestPrice` that builds the params and returns null on failure */ -async function _getOrderPrice(chainId: ChainId, order: Order, verifyQuote: boolean, strategy: GpPriceStrategy) { +async function _getOrderPrice( + chainId: ChainId, + order: Order, + verifyQuote: boolean | undefined, + strategy: GpPriceStrategy +) { let baseToken, quoteToken const amount = getRemainderAmount(order.kind, order) diff --git a/apps/cowswap-frontend/src/legacy/state/price/updater.ts b/apps/cowswap-frontend/src/legacy/state/price/updater.ts index 4489050092..dd7b2553f0 100644 --- a/apps/cowswap-frontend/src/legacy/state/price/updater.ts +++ b/apps/cowswap-frontend/src/legacy/state/price/updater.ts @@ -207,7 +207,7 @@ export default function FeesUpdater(): null { userAddress: account, validTo, isEthFlow, - priceQuality: getPriceQuality({ verifyQuote: verifiedQuotesEnabled && enoughBalance === true }), + priceQuality: getPriceQuality({ verifyQuote: verifiedQuotesEnabled && enoughBalance }), } // Don't refetch if offline. diff --git a/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useQuoteParams.ts b/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useQuoteParams.ts index e0e9ceca5f..d0d48b150e 100644 --- a/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useQuoteParams.ts +++ b/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useQuoteParams.ts @@ -45,7 +45,7 @@ export function useQuoteParams(amount: string | null): LegacyFeeQuoteParams | un toDecimals, fromDecimals, isEthFlow: false, - priceQuality: getPriceQuality({ verifyQuote: verifiedQuotesEnabled && enoughBalance === true }), + priceQuality: getPriceQuality({ verifyQuote: verifiedQuotesEnabled && enoughBalance }), } return params From cbce0ad874c26eb19294146336314268f41823f7 Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Tue, 5 Sep 2023 12:45:23 +0100 Subject: [PATCH 3/8] chore: refactor repetitive code --- .../utils/getOrderParams.ts | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts index f5a555abc7..c58f9c2021 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts @@ -39,18 +39,12 @@ export function getOrderParams( const allowance = allowances[order.inputToken.address]?.value const [hasEnoughBalance, hasEnoughAllowance] = (() => { - if (order.partiallyFillable) { - // When balance or allowance are undefined (loading state), show as true - // When loaded, check there's at least PERCENTAGE_FOR_PARTIAL_FILLS of balance/allowance to consider it as enough - const amount = sellAmount.multiply(PERCENTAGE_FOR_PARTIAL_FILLS) - const hasEnoughBalance = isEnoughAmount(amount, balance) - const hasEnoughAllowance = isEnoughAmount(amount, allowance) - return [hasEnoughBalance, hasEnoughAllowance] - } else { - const hasEnoughBalance = isEnoughAmount(sellAmount, balance) - const hasEnoughAllowance = isEnoughAmount(sellAmount, allowance) - return [hasEnoughBalance, hasEnoughAllowance] - } + // Check there's at least PERCENTAGE_FOR_PARTIAL_FILLS of balance/allowance to consider it as enough + const amount = order.partiallyFillable ? sellAmount.multiply(PERCENTAGE_FOR_PARTIAL_FILLS) : sellAmount + const hasEnoughBalance = isEnoughAmount(amount, balance) + const hasEnoughAllowance = isEnoughAmount(amount, allowance) + + return [hasEnoughBalance, hasEnoughAllowance] })() return { From 5a17533f99614d840e55dde9fbbfbd3dd9a944df Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Tue, 5 Sep 2023 12:50:20 +0100 Subject: [PATCH 4/8] chore: add nit to simplify expresssion Co-authored-by: Leandro --- apps/cowswap-frontend/src/common/hooks/useNeedsApproval.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/cowswap-frontend/src/common/hooks/useNeedsApproval.ts b/apps/cowswap-frontend/src/common/hooks/useNeedsApproval.ts index 4912c96e6d..6f4622707d 100644 --- a/apps/cowswap-frontend/src/common/hooks/useNeedsApproval.ts +++ b/apps/cowswap-frontend/src/common/hooks/useNeedsApproval.ts @@ -39,6 +39,5 @@ export function useNeedsApproval(amount: Nullish>): boo return false } - const enoughBalance = isEnoughAmount(amount, allowance) - return enoughBalance === false + return isEnoughAmount(amount, allowance) === false } From 741e0124e6cfff872970adaa2c6ac09b8d9be97b Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Tue, 5 Sep 2023 12:57:44 +0100 Subject: [PATCH 5/8] chore: move logic to private function --- .../utils/getOrderParams.ts | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts index c58f9c2021..8968e26a34 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts @@ -1,5 +1,5 @@ import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' +import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core' import { BalancesAndAllowances } from 'modules/tokens' @@ -38,14 +38,12 @@ export function getOrderParams( const balance = balances[order.inputToken.address]?.value const allowance = allowances[order.inputToken.address]?.value - const [hasEnoughBalance, hasEnoughAllowance] = (() => { - // Check there's at least PERCENTAGE_FOR_PARTIAL_FILLS of balance/allowance to consider it as enough - const amount = order.partiallyFillable ? sellAmount.multiply(PERCENTAGE_FOR_PARTIAL_FILLS) : sellAmount - const hasEnoughBalance = isEnoughAmount(amount, balance) - const hasEnoughAllowance = isEnoughAmount(amount, allowance) - - return [hasEnoughBalance, hasEnoughAllowance] - })() + const { hasEnoughBalance, hasEnoughAllowance } = _hasEnoughBalanceAndAllowance({ + partiallyFillable: order.partiallyFillable, + sellAmount, + balance, + allowance, + }) return { chainId, @@ -56,3 +54,21 @@ export function getOrderParams( hasEnoughAllowance, } } + +function _hasEnoughBalanceAndAllowance(params: { + balance: CurrencyAmount | undefined + partiallyFillable: boolean + sellAmount: CurrencyAmount + allowance: CurrencyAmount | undefined +}): { + hasEnoughBalance: boolean | undefined + hasEnoughAllowance: boolean | undefined +} { + const { allowance, balance, partiallyFillable, sellAmount } = params + // Check there's at least PERCENTAGE_FOR_PARTIAL_FILLS of balance/allowance to consider it as enough + const amount = partiallyFillable ? sellAmount.multiply(PERCENTAGE_FOR_PARTIAL_FILLS) : sellAmount + const hasEnoughBalance = isEnoughAmount(amount, balance) + const hasEnoughAllowance = isEnoughAmount(amount, allowance) + + return { hasEnoughBalance, hasEnoughAllowance } +} From 4d4b932c6263699dfd416b71cd58552c70b37967 Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Tue, 5 Sep 2023 13:18:53 +0100 Subject: [PATCH 6/8] chore: don't allow to check quotes with same sellToken and buyToken --- .../cowswap-frontend/src/api/gnosisProtocol/api.ts | 14 ++++++++++++-- .../src/api/gnosisProtocol/errors/QuoteError.ts | 9 +++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/apps/cowswap-frontend/src/api/gnosisProtocol/api.ts b/apps/cowswap-frontend/src/api/gnosisProtocol/api.ts index 0a22504317..b08515a235 100644 --- a/apps/cowswap-frontend/src/api/gnosisProtocol/api.ts +++ b/apps/cowswap-frontend/src/api/gnosisProtocol/api.ts @@ -25,8 +25,8 @@ import { toErc20Address, toNativeBuyAddress } from 'legacy/utils/tokens' import { getAppData } from 'modules/appData' -import { ApiErrorObject } from 'api/gnosisProtocol/errors/OperatorError' -import GpQuoteError, { mapOperatorErrorToQuoteError } from 'api/gnosisProtocol/errors/QuoteError' +import { ApiErrorCodes, ApiErrorObject } from 'api/gnosisProtocol/errors/OperatorError' +import GpQuoteError, { GpQuoteErrorDetails, mapOperatorErrorToQuoteError } from 'api/gnosisProtocol/errors/QuoteError' import { LegacyFeeQuoteParams as FeeQuoteParams } from './legacy/types' @@ -141,6 +141,16 @@ function _mapNewToLegacyParams(params: FeeQuoteParams): OrderQuoteRequest { export async function getQuote(params: FeeQuoteParams): Promise { const { chainId } = params const quoteParams = _mapNewToLegacyParams(params) + const { sellToken, buyToken } = quoteParams + + if (sellToken === buyToken) { + return Promise.reject( + mapOperatorErrorToQuoteError({ + errorType: ApiErrorCodes.SameBuyAndSellToken, + description: GpQuoteErrorDetails.SameBuyAndSellToken, + }) + ) + } return orderBookApi.getQuote(quoteParams, { chainId }).catch((error) => { if (isOrderbookTypedError(error)) { diff --git a/apps/cowswap-frontend/src/api/gnosisProtocol/errors/QuoteError.ts b/apps/cowswap-frontend/src/api/gnosisProtocol/errors/QuoteError.ts index 87f5f76b2a..168462a811 100644 --- a/apps/cowswap-frontend/src/api/gnosisProtocol/errors/QuoteError.ts +++ b/apps/cowswap-frontend/src/api/gnosisProtocol/errors/QuoteError.ts @@ -14,6 +14,7 @@ export enum GpQuoteErrorCodes { FeeExceedsFrom = 'FeeExceedsFrom', ZeroPrice = 'ZeroPrice', TransferEthToContract = 'TransferEthToContract', + SameBuyAndSellToken = 'SameBuyAndSellToken', UNHANDLED_ERROR = 'UNHANDLED_ERROR', } @@ -25,6 +26,7 @@ export enum GpQuoteErrorDetails { FeeExceedsFrom = 'Current fee exceeds entered "from" amount.', ZeroPrice = 'Quoted price is zero. This is likely due to a significant price difference between the two tokens. Please try increasing amounts.', TransferEthToContract = 'Buying native currencies using smart contract wallets is not currently supported.', + SameBuyAndSellToken = 'You are trying to buy and sell the same token.', SellAmountDoesNotCoverFee = 'The selling amount for the order is lower than the fee.', UNHANDLED_ERROR = 'Quote fetch failed. This may be due to a server or network connectivity issue. Please try again later.', } @@ -55,6 +57,13 @@ export function mapOperatorErrorToQuoteError(error?: ApiErrorObject): GpQuoteErr errorType: GpQuoteErrorCodes.TransferEthToContract, description: error.description, } + + case ApiErrorCodes.SameBuyAndSellToken: + return { + errorType: GpQuoteErrorCodes.SameBuyAndSellToken, + description: GpQuoteErrorDetails.SameBuyAndSellToken, + } + default: return { errorType: GpQuoteErrorCodes.UNHANDLED_ERROR, description: GpQuoteErrorDetails.UNHANDLED_ERROR } } From 16f15cded907718e96b438595529f125473f7066 Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Thu, 7 Sep 2023 11:18:34 +0100 Subject: [PATCH 7/8] chore: add private function --- .../modules/tokens/hooks/useEnoughBalance.ts | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/tokens/hooks/useEnoughBalance.ts b/apps/cowswap-frontend/src/modules/tokens/hooks/useEnoughBalance.ts index 820b538922..cdadded320 100644 --- a/apps/cowswap-frontend/src/modules/tokens/hooks/useEnoughBalance.ts +++ b/apps/cowswap-frontend/src/modules/tokens/hooks/useEnoughBalance.ts @@ -86,22 +86,8 @@ export function hasEnoughBalanceAndAllowance(params: EnoughBalanceParams): boole const token = amount?.currency.wrapped const tokenAddress = getAddress(token) - const enoughBalance = (() => { - const balance = tokenAddress ? balances[tokenAddress]?.value : undefined - const balanceAmount = isNativeCurrency ? nativeBalance : balance || undefined - return isEnoughAmount(amount, balanceAmount) - })() - - const enoughAllowance = (() => { - if (!tokenAddress || !allowances) { - return undefined - } - if (isNativeCurrency) { - return true - } - const allowance = allowances[tokenAddress]?.value - return allowance && isEnoughAmount(amount, allowance) - })() + const enoughBalance = _enoughBalance(tokenAddress, amount, balances, isNativeCurrency, nativeBalance) + const enoughAllowance = _enoughAllowance(tokenAddress, amount, allowances, isNativeCurrency) if (enoughBalance === undefined || enoughAllowance === undefined) { return undefined @@ -109,3 +95,31 @@ export function hasEnoughBalanceAndAllowance(params: EnoughBalanceParams): boole return enoughBalance && enoughAllowance } + +function _enoughBalance( + tokenAddress: string | null, + amount: CurrencyAmount, + balances: TokenAmounts, + isNativeCurrency: boolean, + nativeBalance: CurrencyAmount | undefined +): boolean | undefined { + const balance = tokenAddress ? balances[tokenAddress]?.value : undefined + const balanceAmount = isNativeCurrency ? nativeBalance : balance || undefined + return isEnoughAmount(amount, balanceAmount) +} + +function _enoughAllowance( + tokenAddress: string | null, + amount: CurrencyAmount, + allowances: TokenAmounts | undefined, + isNativeCurrency: boolean +): boolean | undefined { + if (!tokenAddress || !allowances) { + return undefined + } + if (isNativeCurrency) { + return true + } + const allowance = allowances[tokenAddress]?.value + return allowance && isEnoughAmount(amount, allowance) +} From e24c9939c6926aeaa5961ad541b0b552f6a00d1d Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Thu, 7 Sep 2023 11:25:37 +0100 Subject: [PATCH 8/8] fix: add mapping for error in the buttons map --- .../pure/TradeFormButtons/tradeButtonsMap.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/cowswap-frontend/src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx b/apps/cowswap-frontend/src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx index afc25d6d93..fac973ae41 100644 --- a/apps/cowswap-frontend/src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx +++ b/apps/cowswap-frontend/src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx @@ -34,6 +34,7 @@ const quoteErrorTexts: Record = { [GpQuoteErrorCodes.InsufficientLiquidity]: 'Insufficient liquidity for this trade.', [GpQuoteErrorCodes.FeeExceedsFrom]: 'Sell amount is too small', [GpQuoteErrorCodes.ZeroPrice]: 'Invalid price. Try increasing input/output amount.', + [GpQuoteErrorCodes.SameBuyAndSellToken]: 'Tokens must be different', } const unsupportedTokenButton = (context: TradeFormButtonContext) => {