From c4fc87c2bb875dde41db2b02b977d22f2d9aa882 Mon Sep 17 00:00:00 2001 From: micaelae Date: Thu, 30 Jul 2026 20:02:24 -0700 Subject: [PATCH] chore: calcQuoteMetadata V1/V2 --- packages/bridge-controller/CHANGELOG.md | 6 + .../bridge-controller/src/selectors.test.ts | 362 +++++++----------- .../src/utils/number-formatters.ts | 17 +- .../utils/quote-metadata/calculators.test.ts | 129 ++++--- .../src/utils/quote-metadata/calculators.ts | 154 ++++---- .../src/utils/quote-metadata/merge.ts | 8 +- .../tests/mock-quotes-native-erc20.ts | 9 +- .../tests/mock-quotes-sol-erc20.ts | 2 +- 8 files changed, 315 insertions(+), 372 deletions(-) diff --git a/packages/bridge-controller/CHANGELOG.md b/packages/bridge-controller/CHANGELOG.md index 4b8fc861fd6..10264f4bb25 100644 --- a/packages/bridge-controller/CHANGELOG.md +++ b/packages/bridge-controller/CHANGELOG.md @@ -16,6 +16,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `toQuoteResponseV2` and `toQuoteResponseV1` convert quote responses to required schema when needed - `toQuoteMetadataV2` and `toQuoteMetadataV1` convert quote metadata to required schema when needed - `toNormalizedAmounts`converts atomic amounts to human-readable values +- Selector compatibility + - deprecate cost and use priceImpact + +### Removed + +- Clean up unused unsupporteed `XlmScope.Testnet` chainId mapping ([#9085](https://github.com/MetaMask/core/pull/9085)) ### Changed diff --git a/packages/bridge-controller/src/selectors.test.ts b/packages/bridge-controller/src/selectors.test.ts index 638dd3c1901..772f3f79778 100644 --- a/packages/bridge-controller/src/selectors.test.ts +++ b/packages/bridge-controller/src/selectors.test.ts @@ -2,12 +2,19 @@ import { getAddress } from '@ethersproject/address'; import type { MarketDataDetails } from '@metamask/assets-controllers'; import { toHex } from '@metamask/controller-utils'; import { SolScope } from '@metamask/keyring-api'; -import { parseCaipAssetType } from '@metamask/utils'; +import { + KnownCaipNamespace, + parseCaipAssetType, + parseCaipChainId, +} from '@metamask/utils'; import { BigNumber } from 'bignumber.js'; import { merge } from 'lodash'; import { mockBridgeQuotesErc20Erc20V1 } from '../tests/mock-quotes-erc20-erc20.js'; -import { mockBridgeQuotesNativeErc20V1 } from '../tests/mock-quotes-native-erc20.js'; +import { + getMockBridgeQuotesNativeErc20V1, + mockBridgeQuotesNativeErc20V1, +} from '../tests/mock-quotes-native-erc20.js'; import { DEFAULT_CHAIN_RANKING, ETH_USDT_ADDRESS } from './constants/bridge.js'; import type { BridgeAppState } from './selectors.js'; import { @@ -23,19 +30,23 @@ import { selectBatchSellTrades, } from './selectors.js'; import { SortOrder, RequestStatus, ChainId, NonEvmFees } from './types.js'; +import type { DeepPartial } from './types.js'; import { getNativeAssetForChainId, isNativeAddress } from './utils/bridge.js'; import { formatAddressToAssetId, formatAddressToCaipReference, + formatChainIdToCaip, formatChainIdToDec, formatChainIdToHex, } from './utils/caip-formatters.js'; import { calcQuoteMetadata } from './utils/quote-metadata/calculators.js'; import { mergeQuoteMetadata } from './utils/quote-metadata/merge.js'; +import { toQuoteMetadataV1 } from './utils/quote-metadata/to-quote-metadata-v1.js'; import { BatchSellTransactionType } from './validators/batch-sell.js'; import type { BridgeAsset } from './validators/bridge-asset.js'; -import type { QuoteResponseV1 } from './validators/quote-response-v1.js'; import { validateQuoteResponseV1 } from './validators/quote-response-v1.js'; +import type { QuoteResponseV1 } from './validators/quote-response-v1.js'; +import type { QuoteResponse } from './validators/quote-response.js'; const MOCK_USDC_ADDRESS = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; const MOCK_MUSD_ADDRESS = '0x12345A7890123456789012345678901234567890'; @@ -509,12 +520,12 @@ describe('Bridge Selectors', () => { describe('selectBridgeQuotes', () => { const getMockState = ( chainId: ChainId, - quoteOverrides?: Partial, + quoteOverrides: DeepPartial = {}, stateOverrides?: Partial, ): BridgeAppState => { const decChainId = formatChainIdToDec(chainId); - - const mockQuote = { + const caipChainId = formatChainIdToCaip(chainId); + const mockQuoteV1 = { quote: { requestId: '123', srcChainId: decChainId, @@ -526,7 +537,9 @@ describe('Bridge Selectors', () => { chainId: decChainId, address: '0x0000000000000000000000000000000000000000', decimals: 18, - assetId: getNativeAssetForChainId(chainId).assetId.toLowerCase(), + assetId: getNativeAssetForChainId( + chainId, + ).assetId.toLowerCase() as `${string}:${string}/${string}:${string}`, symbol: 'ETH', name: 'Ethereum', }, @@ -534,7 +547,9 @@ describe('Bridge Selectors', () => { chainId: 137, address: '0x0000000000000000000000000000000000000000', decimals: 18, - assetId: getNativeAssetForChainId(137).assetId.toLowerCase(), + assetId: getNativeAssetForChainId( + 137, + ).assetId.toLowerCase() as `${string}:${string}/${string}:${string}`, symbol: 'POL', name: 'Polygon', }, @@ -557,51 +572,69 @@ describe('Bridge Selectors', () => { }, }, estimatedProcessingTimeInSeconds: 300, - trade: { - value: '0x0', - gasLimit: 24000, - effectiveGas: 21000, - chainId: decChainId, - from: '0x0000000000000000000000000000000000000000', - to: '0x0000000000000000000000000000000000000000', - data: '0x0', - }, - approval: { - gasLimit: 49000, - effectiveGas: 46000, - chainId: decChainId, - from: '0x0000000000000000000000000000000000000000', - to: '0x0000000000000000000000000000000000000000', - data: '0x0', - value: '0x0', - }, + ...(parseCaipChainId(caipChainId).namespace === + KnownCaipNamespace.Eip155 + ? { + trade: { + value: '0x0', + gasLimit: 24000, + effectiveGas: 21000, + chainId: decChainId, + from: '0x0000000000000000000000000000000000000000', + to: '0x0000000000000000000000000000000000000000', + data: '0x0', + }, + } + : { trade: 'SOLANATRADE' }), + ...(parseCaipChainId(caipChainId).namespace === + KnownCaipNamespace.Eip155 + ? { + approval: { + gasLimit: 49000, + effectiveGas: 46000, + chainId: decChainId, + from: '0x0000000000000000000000000000000000000000', + to: '0x0000000000000000000000000000000000000000', + data: '0x0', + value: '0x0', + }, + } + : {}), }; + const mockQuoteV2 = [ + mockQuoteV1, + { + ...mockQuoteV1, + quote: { + ...mockQuoteV1.quote, + requestId: '456', + destTokenAmount: '2100000000000000000', + }, + }, + ].map((quote) => ({ + ...merge({}, quote, quoteOverrides), + quote: merge({}, quote.quote, quoteOverrides?.quote ?? {}), + })); + + const srcChainId = parseCaipAssetType( + mockQuoteV2[0].quote.srcAsset.assetId, + ).chainId; + const destChainId = parseCaipAssetType( + mockQuoteV2[0].quote.destAsset.assetId, + ).chainId; + return { - quotes: [ - merge({}, mockQuote, quoteOverrides), - merge( - {}, - { - ...mockQuote, - quote: { - ...mockQuote.quote, - requestId: '456', - destTokenAmount: '2100000000000000000', - }, - }, - quoteOverrides, - ), - ], + quotes: mockQuoteV2, quoteRequest: [ { - srcChainId: quoteOverrides?.quote?.srcAsset?.chainId ?? decChainId, - destChainId: quoteOverrides?.quote?.destAsset?.chainId ?? 137, + srcChainId: srcChainId ?? decChainId, + destChainId: destChainId ?? 137, srcTokenAddress: - quoteOverrides?.quote?.srcAsset?.address ?? + mockQuoteV2[0].quote.srcAsset.assetId ?? '0x0000000000000000000000000000000000000000', destTokenAddress: - quoteOverrides?.quote?.destAsset?.address ?? + mockQuoteV2[0].quote.destAsset.assetId ?? '0x0000000000000000000000000000000000000000', insufficientBal: false, }, @@ -668,21 +701,11 @@ describe('Bridge Selectors', () => { { ...mockState, assetExchangeRates: { - [mockQuote.quote.srcAsset.assetId.toLowerCase() ?? - formatAddressToAssetId( - mockQuote.quote.srcAsset.address, - mockQuote.quote.srcChainId, - ) ?? - '']: { + [mockQuote.quote.srcAsset.assetId]: { exchangeRate: '1980', usdExchangeRate: '10', }, - [mockQuote.quote.destAsset.assetId.toLowerCase() ?? - formatAddressToAssetId( - mockQuote.quote.destAsset.address, - mockQuote.quote.destChainId, - ) ?? - '']: { + [mockQuote.quote.destAsset.assetId]: { exchangeRate: '200', usdExchangeRate: '1', }, @@ -690,57 +713,28 @@ describe('Bridge Selectors', () => { }, mockClientParams, ); - - const expectedQuoteMetadata = { - adjustedReturn: { - usd: '2.099927', - valueInCurrency: '419.985546', - }, - cost: { - usd: '8.900073', - valueInCurrency: '1758.014454', - }, - gasFee: { - total: { - amount: '0.0000073', - usd: '0.000073', - valueInCurrency: '0.014454', - }, - }, - minToTokenAmount: { - amount: '1.8', - usd: '1.8', - valueInCurrency: '360', - }, - priceImpact: { - usd: '8.9', - valueInCurrency: '1758', - }, - sentAmount: { - amount: '1.1', - usd: '11', - valueInCurrency: '2178', - }, - swapRate: '1.90909090909090909091', - toTokenAmount: { - amount: '2.1', - usd: '2.1', - valueInCurrency: '420', - }, - totalNetworkFee: { - amount: '0.0000073', - usd: '0.000073', - valueInCurrency: '0.014454', + const quote = mockState.quotes[1]; + const expectedQuoteMetadata = calcQuoteMetadata(quote, { + srcTokenExchangeRate: { exchangeRate: '1980', usdExchangeRate: '10' }, + bridgeFeesPerGas: { + estimatedBaseFeeInDecGwei: '0', + feePerGasInDecGwei: '.1', }, - }; + destTokenExchangeRate: { exchangeRate: '200', usdExchangeRate: '1' }, + nativeExchangeRate: { exchangeRate: '1980', usdExchangeRate: '10' }, + }); + const expectedQuoteV2 = mergeQuoteMetadata(quote, expectedQuoteMetadata); + + expect(result.sortedQuotes[0]).toStrictEqual(expectedQuoteV2); - const quoteResponseV1 = mergeQuoteMetadata( - mockState.quotes[1], + expect(result.recommendedQuote?.priceImpact?.valueInCurrency).toBe( + expectedQuoteV2.priceImpact?.valueInCurrency, + ); + expect(toQuoteMetadataV1(result.recommendedQuote)).toStrictEqual( expectedQuoteMetadata, ); - validateQuoteResponseV1(quoteResponseV1); - expect(result.sortedQuotes[0]).toStrictEqual(quoteResponseV1); + expect(result.sortedQuotes[0]).toStrictEqual(expectedQuoteV2); expect(result.sortedQuotes[0].cost?.valueInCurrency).toBe('1758.014454'); }); @@ -753,17 +747,11 @@ describe('Bridge Selectors', () => { ...mockState, quotes: [], assetExchangeRates: { - [formatAddressToAssetId( - mockQuote.quote.srcAsset.address, - mockQuote.quote.srcChainId, - ) ?? '']: { + [mockQuote.quote.srcAsset.assetId]: { exchangeRate: '1980', usdExchangeRate: '10', }, - [formatAddressToAssetId( - mockQuote.quote.destAsset.address, - mockQuote.quote.destChainId, - ) ?? '']: { + [mockQuote.quote.destAsset.assetId]: { exchangeRate: '200', usdExchangeRate: '1', }, @@ -795,14 +783,6 @@ describe('Bridge Selectors', () => { ); const expectedQuoteMetadata = { - adjustedReturn: { - usd: undefined, - valueInCurrency: undefined, - }, - cost: { - usd: undefined, - valueInCurrency: undefined, - }, gasFee: { total: { amount: '0.0000073', @@ -837,7 +817,12 @@ describe('Bridge Selectors', () => { expect(result.sortedQuotes[0]).toStrictEqual( mergeQuoteMetadata(expectedQuoteV2, expectedQuoteMetadata), ); - expect(result.sortedQuotes[0]?.cost?.valueInCurrency).toBeUndefined(); + expect( + result.sortedQuotes[0].quote.priceData?.priceImpact, + ).toBeUndefined(); + expect(result.recommendedQuote?.quote.destTokenAmount).toBe( + '2100000000000000000', + ); expect(result.recommendedQuote?.toTokenAmount?.amount).toBe('2.1'); }); @@ -871,14 +856,6 @@ describe('Bridge Selectors', () => { ); const expectedQuoteMetadata = { - adjustedReturn: { - usd: undefined, - valueInCurrency: undefined, - }, - cost: { - usd: undefined, - valueInCurrency: undefined, - }, minToTokenAmount: { amount: '1.8', usd: undefined, @@ -922,11 +899,11 @@ describe('Bridge Selectors', () => { describe('returns swap metadata', () => { const getMockSwapState = ( - srcAsset: BridgeAsset, - destAsset: BridgeAsset, + srcAsset: Omit, + destAsset: Omit, txFee?: { amount: string; - asset: BridgeAsset; + asset: Omit; }, gasIncluded7702?: boolean, gasEstimatesChainId?: number, @@ -1094,8 +1071,6 @@ describe('Bridge Selectors', () => { assetId: getNativeAssetForChainId(1).assetId, symbol: 'ETH', name: 'Ethereum', - address: '0x0000000000000000000000000000000000000000', - chainId: 1, }; const destAsset = { decimals: 18, @@ -1103,8 +1078,6 @@ describe('Bridge Selectors', () => { 'eip155:1/erc20:0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d' as const, symbol: 'USDC', name: 'USD Coin', - address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', - chainId: 1, }; const newState = getMockSwapState(srcAsset, destAsset); @@ -1167,8 +1140,6 @@ describe('Bridge Selectors', () => { assetId: 'eip155:1/erc20:0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', decimals: 18, - address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', - chainId: 1, }, { decimals: 18, @@ -1176,8 +1147,6 @@ describe('Bridge Selectors', () => { 'eip155:1/erc20:0x0000000000000000000000000000000000000000', symbol: 'ETH', name: 'Ethereum', - address: '0x0000000000000000000000000000000000000000', - chainId: 1, }, ); @@ -1235,22 +1204,18 @@ describe('Bridge Selectors', () => { it('erc20 -> native but gas estimates are not available', () => { const newState = getMockSwapState( { - address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', decimals: 18, assetId: 'eip155:1/erc20:0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', symbol: 'USDC', name: 'USD Coin', - chainId: 1, }, { - address: '0x0000000000000000000000000000000000000000', decimals: 18, assetId: 'eip155:1/erc20:0x0000000000000000000000000000000000000000', symbol: 'ETH', name: 'Ethereum', - chainId: 1, }, undefined, undefined, @@ -1311,33 +1276,27 @@ describe('Bridge Selectors', () => { it('when gas is included and is taken from dest token', () => { const newState = getMockSwapState( { - address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', decimals: 18, assetId: 'eip155:1/erc20:0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', symbol: 'USDC', name: 'USD Coin', - chainId: 1, }, { - address: '0x0000000000000000000000000000000000000000', decimals: 18, assetId: 'eip155:1/erc20:0x0000000000000000000000000000000000000000', symbol: 'ETH', name: 'Ethereum', - chainId: 1, }, { amount: '1000000000000000', asset: { - address: '0x0000000000000000000000000000000000000000', decimals: 18, assetId: 'eip155:1/erc20:0x0000000000000000000000000000000000000000', symbol: 'ETH', name: 'Ethereum', - chainId: 1, }, }, ); @@ -1406,27 +1365,21 @@ describe('Bridge Selectors', () => { assetId: 'eip155:1/erc20:0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', decimals: 6, - chainId: 1, - address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', }, { - address: '0x0000000000000000000000000000000000000000', decimals: 18, assetId: 'eip155:1/slip44:60', symbol: 'ETH', name: 'Ethereum', - chainId: 1, }, { amount: '3000000', asset: { - address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', decimals: 6, assetId: 'eip155:1/erc20:0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', symbol: 'ETH', name: 'Ethereum', - chainId: 1, }, }, ); @@ -1451,53 +1404,24 @@ describe('Bridge Selectors', () => { }; const { sortedQuotes } = selectBridgeQuotes(newState, mockClientParams); - const expectedQuoteMetadata = { - adjustedReturn: { - usd: '10.51342489434187625472', - valueInCurrency: '8.99553613774000008538', + const expectedQuoteMetadata = calcQuoteMetadata(newState.quotes[0], { + srcTokenExchangeRate: { + exchangeRate: '551.98', + usdExchangeRate: '645.12', }, - cost: { - usd: '1936.53421414565812374528', - valueInCurrency: '1656.94468552225999991462', + bridgeFeesPerGas: { + estimatedBaseFeeInDecGwei: '0', + feePerGasInDecGwei: '.1', }, - gasFee: { - total: { - amount: '0.000008087', - usd: '0.00521708544', - valueInCurrency: '0.00446386226', - }, - }, - includedTxFees: { - amount: '3', - usd: '1935.36', - valueInCurrency: '1655.94', - }, - minToTokenAmount: { - amount: '0.015489691655494764', - usd: '9.99270988079278215168', - valueInCurrency: '8.54999999999999983272', + destTokenExchangeRate: { + exchangeRate: '551.98', + usdExchangeRate: '645.12', }, - sentAmount: { - amount: '3.018117', - usd: '1947.04763904', - valueInCurrency: '1665.94022166', - }, - priceImpact: { - usd: '1936.52899706021812374528', - valueInCurrency: '1656.94022165999999991462', + nativeExchangeRate: { + exchangeRate: '551.98', + usdExchangeRate: '645.12', }, - swapRate: '0.00540235470816119156', - toTokenAmount: { - amount: '0.016304938584731331', - usd: '10.51864197978187625472', - valueInCurrency: '9.00000000000000008538', - }, - totalNetworkFee: { - amount: '0.000008087', - usd: '0.00521708544', - valueInCurrency: '0.00446386226', - }, - }; + }); expect(sortedQuotes[0].quote.feeData.txFee).toStrictEqual({ amount: '3000000', @@ -1524,22 +1448,18 @@ describe('Bridge Selectors', () => { it('when gasIncluded7702=true and is taken from dest token', () => { const newState = getMockSwapState( { - address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', decimals: 18, assetId: 'eip155:1/erc20:0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', symbol: 'USDC', name: 'USD Coin', - chainId: 1, }, { - address: '0x0000000000000000000000000000000000000001', decimals: 18, assetId: 'eip155:1/erc20:0x0000000000000000000000000000000000000001', symbol: 'WETH', name: 'Ethereum', - chainId: 1, }, { amount: '1000000000000000000', @@ -1549,8 +1469,6 @@ describe('Bridge Selectors', () => { 'eip155:1/erc20:0x0000000000000000000000000000000000000001', symbol: 'WETH', name: 'Ethereum', - chainId: 1, - address: '0x0000000000000000000000000000000000000001', }, }, true, @@ -1689,14 +1607,6 @@ describe('Bridge Selectors', () => { valueInCurrency: undefined, }, swapRate: '1.90909090909090909091', - cost: { - usd: undefined, - valueInCurrency: undefined, - }, - adjustedReturn: { - usd: undefined, - valueInCurrency: undefined, - }, totalNetworkFee: { amount: '0.0000073', usd: '0.01314', @@ -1746,14 +1656,6 @@ describe('Bridge Selectors', () => { valueInCurrency: undefined, }, swapRate: '1.90909090909090909091', - cost: { - usd: undefined, - valueInCurrency: undefined, - }, - adjustedReturn: { - usd: undefined, - valueInCurrency: undefined, - }, totalNetworkFee: { amount: '0.0000073', usd: '0.01314', @@ -1854,6 +1756,7 @@ describe('Bridge Selectors', () => { ); const solanaQuote = solanaState.quotes[1]; + expect(solanaQuote.quote.destTokenAmount).toBe('2100000000000000000'); const expectedQuoteMetadata = calcQuoteMetadata(solanaQuote, { srcTokenExchangeRate: { exchangeRate: '0.5', usdExchangeRate: '10' }, @@ -1924,12 +1827,6 @@ describe('Bridge Selectors', () => { }, }, assetExchangeRates: {}, - currencyRates: { - ETH: { - conversionRate: 1800, - usdConversionRate: 1800, - }, - }, marketData: {}, conversionRates: {}, participateInMetaMetrics: true, @@ -1962,13 +1859,6 @@ describe('Bridge Selectors', () => { selectBatchSellQuotes( { ...mockState, - currencyRates: { - ETH: { - conversionRate: 1800, - usdConversionRate: 10, - conversionDate: Date.now(), - }, - }, assetExchangeRates: { 'eip155:10/erc20:0x0b2c639c533813f4aa9d7837caf62653d097ff85': { exchangeRate: '1980', @@ -1978,6 +1868,10 @@ describe('Bridge Selectors', () => { exchangeRate: '200', usdExchangeRate: '1', }, + 'eip155:10/slip44:60': { + exchangeRate: '1800', + usdExchangeRate: '10', + }, }, }, { ...mockClientParams, requestCount: 2 }, @@ -2085,7 +1979,7 @@ describe('Bridge Selectors', () => { const { recommendedQuotes } = selectBatchSellQuotes( { ...mockState, - quotes: mockBridgeQuotesNativeErc20V1.map((quote) => ({ + quotes: getMockBridgeQuotesNativeErc20V1().map((quote) => ({ ...quote, quoteRequestIndex: undefined, })), diff --git a/packages/bridge-controller/src/utils/number-formatters.ts b/packages/bridge-controller/src/utils/number-formatters.ts index b6b2b6d76f9..219831d253b 100644 --- a/packages/bridge-controller/src/utils/number-formatters.ts +++ b/packages/bridge-controller/src/utils/number-formatters.ts @@ -79,18 +79,31 @@ export const sumAmounts = ( return undefined; } + /** + * Fees and prices can be denominated in different assets, so we need to check if all fees have the same units + */ const isSameAssetForAllFees = new Set(fees.map((fee) => fee.asset?.assetId?.toLowerCase())).size === 1; /** * Keys that require the asset to be the same for all fees */ - const AMOUNT_KEYS = ['amount' as const]; + const AMOUNT_KEYS = [ + 'amount' as const, + 'normalizedAmount' as const, + 'minAmount' as const, + 'minAmountNormalized' as const, + ]; /** * Keys that can be aggregated across all fees */ - const FIAT_OR_USD_KEYS = ['valueInCurrency' as const, 'usd' as const]; + const FIAT_OR_USD_KEYS = [ + 'valueInCurrency' as const, + 'usd' as const, + 'minAmountValueInCurrency' as const, + 'minAmountUsd' as const, + ]; return fees.reduce((acc, fee) => { const newAcc = { ...acc }; diff --git a/packages/bridge-controller/src/utils/quote-metadata/calculators.test.ts b/packages/bridge-controller/src/utils/quote-metadata/calculators.test.ts index ec23cf5af25..b29c8df1b6b 100644 --- a/packages/bridge-controller/src/utils/quote-metadata/calculators.test.ts +++ b/packages/bridge-controller/src/utils/quote-metadata/calculators.test.ts @@ -1,11 +1,10 @@ import { AddressZero } from '@ethersproject/constants'; import { convertHexToDecimal } from '@metamask/controller-utils'; import { BigNumber } from 'bignumber.js'; -import { merge } from 'lodash'; -import { mockBridgeQuotesErc20Erc20V1 } from '../../../tests/mock-quotes-erc20-erc20.js'; -import { mockBridgeQuotesNativeErc20V1 } from '../../../tests/mock-quotes-native-erc20.js'; -import { getMockBridgeQuotesSolErc20V2 } from '../../../tests/mock-quotes-sol-erc20.js'; +import { getMockBridgeQuotesErc20Erc20V1 } from '../../../tests/mock-quotes-erc20-erc20.js'; +import { getMockBridgeQuotesNativeErc20V1 } from '../../../tests/mock-quotes-native-erc20.js'; +import { getMockBridgeQuotesSolErc20V1 } from '../../../tests/mock-quotes-sol-erc20.js'; import type { GenericQuoteRequest, L1GasFees } from '../../types.js'; import { isValidQuoteRequest } from '../../validators/quote-request.js'; import { QuoteResponseV1 } from '../../validators/quote-response-v1.js'; @@ -155,10 +154,14 @@ describe('Quote Utils', () => { describe('Quote Metadata Utils', () => { describe('calcSentAmount', () => { it('should calculate sent amount correctly with exchange rates', () => { - const mockQuote = merge({}, mockBridgeQuotesErc20Erc20V1[0], { + const mockQuote = getMockBridgeQuotesErc20Erc20V1({ quote: { srcTokenAmount: '2555423', - srcAsset: { decimals: 6 }, + srcAsset: { + decimals: 6, + assetId: + 'eip155:10/erc20:0x0b2c639c533813f4aa9d7837caf62653d097ff85', + }, feeData: { metabridge: { amount: '110000000', @@ -169,7 +172,7 @@ describe('Quote Metadata Utils', () => { }, }, }, - }).quote; + })[0].quote; expect(mockQuote.feeData.metabridge.asset?.assetId).toBe( mockQuote.srcAsset.assetId, ); @@ -189,7 +192,7 @@ describe('Quote Metadata Utils', () => { }); it('should handle missing exchange rates', () => { - const mockQuote = merge({}, mockBridgeQuotesErc20Erc20V1[0], { + const mockQuote = getMockBridgeQuotesErc20Erc20V1({ quote: { srcTokenAmount: '1000000000', srcAsset: { decimals: 6 }, @@ -197,7 +200,7 @@ describe('Quote Metadata Utils', () => { metabridge: { amount: '100000000' }, }, }, - }).quote; + })[0].quote; const result = calcSentAmount(mockQuote, {}); expect(result.amount).toBe('1100'); @@ -206,7 +209,7 @@ describe('Quote Metadata Utils', () => { }); it('should handle zero values', () => { - const zeroQuote = merge({}, mockBridgeQuotesErc20Erc20V1[0], { + const zeroQuote = getMockBridgeQuotesErc20Erc20V1({ quote: { srcTokenAmount: '0', srcAsset: { decimals: 6 }, @@ -215,7 +218,7 @@ describe('Quote Metadata Utils', () => { metabridge: { amount: '0' }, }, }, - }).quote; + })[0].quote; const result = calcSentAmount(zeroQuote, { exchangeRate: '2', @@ -228,7 +231,7 @@ describe('Quote Metadata Utils', () => { }); it('should handle large numbers', () => { - const largeQuote = merge({}, mockBridgeQuotesErc20Erc20V1[0], { + const largeQuote = getMockBridgeQuotesErc20Erc20V1({ quote: { srcTokenAmount: '1000000000000000000', srcAsset: { @@ -248,7 +251,7 @@ describe('Quote Metadata Utils', () => { }, }, }, - }).quote; + })[0].quote; const result = calcSentAmount(largeQuote, { exchangeRate: '2', @@ -266,7 +269,7 @@ describe('Quote Metadata Utils', () => { // the total fixed commitment including protocol fees. Adding feeData fees // on top would double-count them. - const intentQuote = merge({}, mockBridgeQuotesErc20Erc20V1[0], { + const intentQuote = getMockBridgeQuotesErc20Erc20V1({ quote: { srcTokenAmount: '10000000', // 10 USDT (6 decimals), fee already included srcAsset: { @@ -306,7 +309,7 @@ describe('Quote Metadata Utils', () => { }, }, }, - }).quote; + })[0].quote; const result = calcSentAmount(intentQuote, { exchangeRate: '1', @@ -321,7 +324,7 @@ describe('Quote Metadata Utils', () => { }); describe('calcNonEvmTotalNetworkFee', () => { - const mockBridgeQuote = getMockBridgeQuotesSolErc20V2({ + const mockBridgeQuote = getMockBridgeQuotesSolErc20V1({ nonEvmFeesInNative: '1', })[0]; @@ -337,7 +340,7 @@ describe('Quote Metadata Utils', () => { }); it('should calculate Bitcoin fees correctly with exchange rates', () => { - const btcQuote = getMockBridgeQuotesSolErc20V2({ + const btcQuote = getMockBridgeQuotesSolErc20V1({ nonEvmFeesInNative: '0.00005', // BTC fee in native units })[0]; @@ -407,7 +410,7 @@ describe('Quote Metadata Utils', () => { }); describe('calcRelayerFee', () => { - const mockBridgeQuote = merge({}, mockBridgeQuotesNativeErc20V1[0], { + const mockBridgeQuote = getMockBridgeQuotesNativeErc20V1({ quote: { srcAsset: { address: '0x123', decimals: 18 }, srcTokenAmount: '1000000000000000000', @@ -418,13 +421,24 @@ describe('Quote Metadata Utils', () => { }, }, trade: { value: '0x10A741A462780000' }, - }) as unknown as QuoteResponseV1; + })[0]; it('should calculate relayer fee correctly with exchange rates', () => { - const result = calcRelayerFee(mockBridgeQuote, { - exchangeRate: '2', - usdExchangeRate: '1.5', - }); + const result = calcRelayerFee( + calcSentAmount( + mockBridgeQuote.quote, + { + exchangeRate: '2', + usdExchangeRate: '1.5', + }, + false, + ), + mockBridgeQuote, + { + exchangeRate: '2', + usdExchangeRate: '1.5', + }, + ); expect(new BigNumber(mockBridgeQuote.trade.value, 16).toFixed()).toBe( '1200000000000000000', @@ -445,11 +459,21 @@ describe('Quote Metadata Utils', () => { }); it('should calculate relayer fee correctly with no trade.value', () => { + const mockQuote = getMockBridgeQuotesNativeErc20V1({ + // @ts-expect-error - trade.value is an object + trade: { ...mockBridgeQuote.trade, value: '0x0' }, + })[0]; + const result = calcRelayerFee( - merge({}, mockBridgeQuotesNativeErc20V1[0], { - // @ts-expect-error - trade.value is an object - trade: { ...mockBridgeQuote.trade, value: '0x0' }, - }), + calcSentAmount( + mockQuote.quote, + { + exchangeRate: '2', + usdExchangeRate: '1.5', + }, + false, + ), + mockQuote, { exchangeRate: '2', usdExchangeRate: '1.5', @@ -460,7 +484,7 @@ describe('Quote Metadata Utils', () => { }); it('should handle native token address', () => { - const nativeBridgeQuote = merge({}, mockBridgeQuotesNativeErc20V1[0], { + const nativeBridgeQuote = getMockBridgeQuotesNativeErc20V1({ quote: { srcTokenAmount: '1000000000000000000', feeData: { @@ -482,12 +506,23 @@ describe('Quote Metadata Utils', () => { trade: { value: '0x10A741A462780000', }, - }) as QuoteResponseV1; + })[0]; - const result = calcRelayerFee(nativeBridgeQuote, { - exchangeRate: '2', - usdExchangeRate: '1.5', - }); + const result = calcRelayerFee( + calcSentAmount( + nativeBridgeQuote.quote, + { + exchangeRate: '2', + usdExchangeRate: '1.5', + }, + false, + ), + nativeBridgeQuote, + { + exchangeRate: '2', + usdExchangeRate: '1.5', + }, + ); expect( convertHexToDecimal(nativeBridgeQuote.trade.value).toString(), @@ -934,12 +969,7 @@ describe('Quote Metadata Utils', () => { sentAmount: { valueInCurrency: undefined }, toTokenAmount: { valueInCurrency: '900' }, }), - ).toMatchInlineSnapshot(` - { - "usd": undefined, - "valueInCurrency": undefined, - } - `); + ).toMatchInlineSnapshot(`undefined`); }); it('returns undefined when toTokenAmount.valueInCurrency is undefined', () => { @@ -948,12 +978,7 @@ describe('Quote Metadata Utils', () => { sentAmount: { valueInCurrency: '1000' }, toTokenAmount: { valueInCurrency: undefined }, }), - ).toMatchInlineSnapshot(` - { - "usd": undefined, - "valueInCurrency": undefined, - } - `); + ).toMatchInlineSnapshot(`undefined`); }); it('returns undefined when sentAmount is missing', () => { @@ -962,12 +987,7 @@ describe('Quote Metadata Utils', () => { sentAmount: {}, toTokenAmount: { valueInCurrency: '900' }, }), - ).toMatchInlineSnapshot(` - { - "usd": undefined, - "valueInCurrency": undefined, - } - `); + ).toBeUndefined(); }); it('returns undefined when toTokenAmount is missing', () => { @@ -976,12 +996,7 @@ describe('Quote Metadata Utils', () => { sentAmount: { valueInCurrency: '1000' }, toTokenAmount: {}, }), - ).toMatchInlineSnapshot(` - { - "usd": undefined, - "valueInCurrency": undefined, - } - `); + ).toBeUndefined(); }); it('formats the absolute difference between source and destination fiat amounts', () => { diff --git a/packages/bridge-controller/src/utils/quote-metadata/calculators.ts b/packages/bridge-controller/src/utils/quote-metadata/calculators.ts index eaaf762c7e3..7e02e6a56f2 100644 --- a/packages/bridge-controller/src/utils/quote-metadata/calculators.ts +++ b/packages/bridge-controller/src/utils/quote-metadata/calculators.ts @@ -7,6 +7,7 @@ import { import { is } from '@metamask/superstruct'; import { BigNumber } from 'bignumber.js'; +import { toQuoteResponseV1 } from '../../index.js'; import type { L1GasFees, ExchangeRate, @@ -15,14 +16,16 @@ import type { } from '../../types.js'; import type { BridgeAsset } from '../../validators/bridge-asset.js'; import { FloatStringSchema } from '../../validators/number.js'; -import type { QuoteResponseV1 as QuoteResponse } from '../../validators/quote-response-v1.js'; -import { TxData } from '../../validators/trade.js'; +import type { QuoteResponseV1 } from '../../validators/quote-response-v1.js'; +import { QuoteResponseSchemaV2 } from '../../validators/quote-response.js'; +import type { QuoteResponse } from '../../validators/quote-response.js'; +import type { TxData } from '../../validators/trade.js'; import { isEvmQuoteResponse, isNativeAddress } from '../bridge.js'; import { calcTokenAmount } from '../number-formatters.js'; import type { QuoteMetadata, TokenAmountValues } from './types.js'; export const calcNonEvmTotalNetworkFee = ( - bridgeQuote: QuoteResponse & NonEvmFees, + bridgeQuote: QuoteResponseV1 & NonEvmFees, { exchangeRate, usdExchangeRate }: ExchangeRate, ) => { const { nonEvmFeesInNative } = bridgeQuote; @@ -57,35 +60,36 @@ export const calcToAmount = ( }; export const calcSentAmount = ( - { srcTokenAmount, srcAsset, feeData, intent }: QuoteResponse['quote'], + { srcTokenAmount, srcAsset, feeData, intent }: QuoteResponseV1['quote'], { exchangeRate, usdExchangeRate }: ExchangeRate, + isQuoteV2: boolean = false, ) => { - // For intent-based swaps (e.g. CoW Protocol), srcTokenAmount is the total + // For intent-based swaps or converted V2 quote responses, srcTokenAmount is the total // fixed commitment the user makes to the protocol — the protocol fee is // already baked in. Adding feeData fees on top would double-count them. // For conventional swaps, srcTokenAmount is the net routing amount (fees // excluded), so the src-token fees must be added to get the wallet deduction. - const sentAmount = intent - ? new BigNumber(srcTokenAmount) - : Object.values(feeData) - .filter( - (fee) => - fee?.amount && - fee.asset?.assetId?.toLowerCase() === - srcAsset.assetId?.toLowerCase(), - ) - .reduce( - (acc, { amount }) => acc.plus(amount), - new BigNumber(srcTokenAmount), - ); + const sentAmount = + intent || isQuoteV2 + ? new BigNumber(srcTokenAmount) + : Object.values(feeData) + .filter( + (fee) => + fee?.amount && + fee.asset?.assetId?.toLowerCase() === + srcAsset.assetId?.toLowerCase(), + ) + .reduce( + (acc, { amount }) => acc.plus(amount), + new BigNumber(srcTokenAmount), + ); const normalizedSentAmount = calcTokenAmount(sentAmount, srcAsset.decimals); return { - amount: normalizedSentAmount?.toString(), + amount: normalizedSentAmount?.toFixed(), valueInCurrency: - exchangeRate && normalizedSentAmount?.times(exchangeRate).toString(), + exchangeRate && normalizedSentAmount?.times(exchangeRate).toFixed(), usd: - usdExchangeRate && - normalizedSentAmount?.times(usdExchangeRate).toString(), + usdExchangeRate && normalizedSentAmount?.times(usdExchangeRate).toFixed(), }; }; @@ -97,19 +101,20 @@ export const calcBatchFees = ( const normalizedAmount = calcTokenAmount(amount, asset.decimals); return { - amount: normalizedAmount?.toString(), + amount: normalizedAmount?.toFixed(), valueInCurrency: exchangeRate - ? normalizedAmount?.times(exchangeRate).toString() + ? normalizedAmount?.times(exchangeRate).toFixed() : null, usd: usdExchangeRate - ? normalizedAmount?.times(usdExchangeRate).toString() + ? normalizedAmount?.times(usdExchangeRate).toFixed() : null, asset, }; }; export const calcRelayerFee = ( - quoteResponse: QuoteResponse, + sentAmount: ReturnType, + quoteResponse: QuoteResponseV1, { exchangeRate, usdExchangeRate }: ExchangeRate, ) => { const { quote, trade } = quoteResponse; @@ -122,11 +127,7 @@ export const calcRelayerFee = ( // Subtract srcAmount and other fees from trade value if srcAsset is native if (isNativeAddress(quote.srcAsset.assetId)) { - const sentAmountInNative = calcSentAmount(quote, { - exchangeRate, - usdExchangeRate, - }).amount; - relayerFeeInNative = relayerFeeInNative?.minus(sentAmountInNative ?? '0'); + relayerFeeInNative = relayerFeeInNative?.minus(sentAmount.amount ?? '0'); } if (relayerFeeInNative?.lte(0)) { @@ -161,9 +162,9 @@ const calcTotalGasFee = ({ }) => { const totalGasLimitInDec = tradeGasLimit || approvalGasLimit || resetApprovalGasLimit - ? new BigNumber(tradeGasLimit?.toString() ?? '0') - .plus(approvalGasLimit?.toString() ?? '0') - .plus(resetApprovalGasLimit?.toString() ?? '0') + ? new BigNumber(tradeGasLimit?.toFixed() ?? '0') + .plus(approvalGasLimit?.toFixed() ?? '0') + .plus(resetApprovalGasLimit?.toFixed() ?? '0') : undefined; const l1GasFeesInDecGWei = l1GasFeesInHexWei @@ -178,10 +179,10 @@ const calcTotalGasFee = ({ const gasFeesInDecEth = gasFeesInDecGwei?.times(new BigNumber(10).pow(-9)); const gasFeesInDisplayCurrency = nativeToDisplayCurrencyExchangeRate - ? gasFeesInDecEth?.times(nativeToDisplayCurrencyExchangeRate.toString()) + ? gasFeesInDecEth?.times(nativeToDisplayCurrencyExchangeRate) : undefined; const gasFeesInUSD = nativeToUsdExchangeRate - ? gasFeesInDecEth?.times(nativeToUsdExchangeRate.toString()) + ? gasFeesInDecEth?.times(nativeToUsdExchangeRate) : undefined; return { @@ -197,7 +198,7 @@ export const calcEstimatedAndMaxTotalGasFee = ({ exchangeRate: nativeToDisplayCurrencyExchangeRate, usdExchangeRate: nativeToUsdExchangeRate, }: { - bridgeQuote: QuoteResponse & L1GasFees; + bridgeQuote: QuoteResponseV1 & L1GasFees; feePerGasInDecGwei?: string; } & ExchangeRate) => { // Estimated total gas fee, including refunded fees (medium) @@ -259,7 +260,7 @@ export const calcIncludedTxFees = ( gasIncluded7702, srcAsset, feeData: { txFee }, - }: QuoteResponse['quote'], + }: QuoteResponseV1['quote'], srcTokenExchangeRate: ExchangeRate, destTokenExchangeRate: ExchangeRate, ) => { @@ -292,7 +293,7 @@ export const calcAdjustedReturn = ( { feeData: { txFee }, destAsset: { assetId: destAssetId }, - }: QuoteResponse['quote'], + }: QuoteResponseV1['quote'], ) => { // If gas is included and is taken from the dest token, don't subtract network fee from return if (txFee?.asset?.assetId?.toLowerCase() === destAssetId.toLowerCase()) { @@ -394,15 +395,22 @@ export const calcPriceImpact = ( const isSourceFiatValid = (value: unknown): value is string[] => is(value, FloatStringSchema); + const valueInCurrency = + isSourceFiatValid(sourceFiat) && isSourceFiatValid(destFiat) + ? new BigNumber(sourceFiat).minus(destFiat).abs().toFixed() + : undefined; + const usd = + isSourceFiatValid(sourceUsd) && isSourceFiatValid(destUsd) + ? new BigNumber(sourceUsd).minus(destUsd).abs().toFixed() + : undefined; + + if (!valueInCurrency && !usd) { + return undefined; + } + return { - valueInCurrency: - isSourceFiatValid(sourceFiat) && isSourceFiatValid(destFiat) - ? new BigNumber(sourceFiat).minus(destFiat).abs().toFixed() - : undefined, - usd: - isSourceFiatValid(sourceUsd) && isSourceFiatValid(destUsd) - ? new BigNumber(sourceUsd).minus(destUsd).abs().toFixed() - : undefined, + valueInCurrency, + usd, }; }; @@ -419,7 +427,7 @@ export const calcPriceImpact = ( * @returns The calculated metadata */ export const calcQuoteMetadata = ( - quote: QuoteResponse, + quote: QuoteResponseV1 | QuoteResponse, options: { bridgeFeesPerGas: null | { estimatedBaseFeeInDecGwei: string | null; @@ -437,39 +445,47 @@ export const calcQuoteMetadata = ( nativeExchangeRate = {}, } = options; - const sentAmount = calcSentAmount(quote.quote, srcTokenExchangeRate); + const isQuoteV2 = is(quote, QuoteResponseSchemaV2); + const quoteV1 = isQuoteV2 ? toQuoteResponseV1(quote) : quote; + + const sentAmount = calcSentAmount( + quoteV1.quote, + srcTokenExchangeRate, + isQuoteV2, + ); + const toTokenAmount = calcToAmount( - quote.quote.destTokenAmount, - quote.quote.destAsset, + quoteV1.quote.destTokenAmount, + quoteV1.quote.destAsset, destTokenExchangeRate, ); const minToTokenAmount = calcToAmount( - quote.quote.minDestTokenAmount, - quote.quote.destAsset, + quoteV1.quote.minDestTokenAmount ?? quoteV1.quote.destTokenAmount, + quoteV1.quote.destAsset, destTokenExchangeRate, ); const includedTxFees = calcIncludedTxFees( - quote.quote, + quoteV1.quote, srcTokenExchangeRate, destTokenExchangeRate, ); let totalEstimatedNetworkFee, relayerFee, gasFee; - if (isEvmQuoteResponse(quote)) { - relayerFee = calcRelayerFee(quote, nativeExchangeRate); + if (isEvmQuoteResponse(quoteV1)) { + relayerFee = calcRelayerFee(sentAmount, quoteV1, nativeExchangeRate); gasFee = calcEstimatedAndMaxTotalGasFee({ - bridgeQuote: quote, + bridgeQuote: quoteV1, ...bridgeFeesPerGas, ...nativeExchangeRate, }); - // Uses effectiveGasFee to calculate the total estimated network fee + // Uses total gasFee to calculate the total estimated network fee totalEstimatedNetworkFee = calcTotalEstimatedNetworkFee(gasFee, relayerFee); } else { // Use the new generic function for all non-EVM chains totalEstimatedNetworkFee = calcNonEvmTotalNetworkFee( - quote, + quoteV1, nativeExchangeRate, ); gasFee = { @@ -480,7 +496,7 @@ export const calcQuoteMetadata = ( const adjustedReturn = calcAdjustedReturn( toTokenAmount, totalEstimatedNetworkFee, - quote.quote, + quoteV1.quote, ); const cost = calcCost(adjustedReturn, sentAmount); @@ -504,12 +520,16 @@ export const calcQuoteMetadata = ( Should only be used for display purposes. */ gasFee, - ...(adjustedReturn && { adjustedReturn }), - ...(cost && { cost }), - ...(includedTxFees && { includedTxFees }), - ...(relayerFee && { relayerFee }), - ...((priceImpact?.valueInCurrency ?? priceImpact?.usd) && { - priceImpact, - }), + ...(adjustedReturn && + Object.values(adjustedReturn).some(Boolean) && { adjustedReturn }), + ...(cost && Object.values(cost).some(Boolean) && { cost }), + ...(includedTxFees && + Object.values(includedTxFees).some(Boolean) && { includedTxFees }), + ...(relayerFee && + Object.values(relayerFee).some(Boolean) && { relayerFee }), + ...(priceImpact && + Object.values(priceImpact).some(Boolean) && { + priceImpact, + }), }; }; diff --git a/packages/bridge-controller/src/utils/quote-metadata/merge.ts b/packages/bridge-controller/src/utils/quote-metadata/merge.ts index 7c7c8e2e5f6..40a4c91cbd0 100644 --- a/packages/bridge-controller/src/utils/quote-metadata/merge.ts +++ b/packages/bridge-controller/src/utils/quote-metadata/merge.ts @@ -1,13 +1,9 @@ import { is } from '@metamask/superstruct'; import { merge } from 'lodash'; -import { - QuoteResponseSchemaV1, - validateQuoteResponseV1, -} from '../../validators/quote-response-v1.js'; +import { QuoteResponseSchemaV1 } from '../../validators/quote-response-v1.js'; import type { QuoteResponseV1 } from '../../validators/quote-response-v1.js'; import type { QuoteResponse } from '../../validators/quote-response.js'; -import { toNormalizedAmounts } from './to-normalized-amounts.js'; import { toQuoteMetadataV2 } from './to-quote-metadata-v2.js'; import type { QuoteMetadata } from './types.js'; @@ -32,13 +28,11 @@ export function mergeQuoteMetadata< legacyQuoteMetadata, quoteResponse, ); - // const normalizedAmountsV2 = toNormalizedAmounts(quoteResponse); // Phase 1 of migration uses calcQuoteMetadata's results return merge( {}, quoteResponse, - // normalizedAmountsV2, legacyQuoteMetadataV2, legacyQuoteMetadata, // return for client testing ); diff --git a/packages/bridge-controller/tests/mock-quotes-native-erc20.ts b/packages/bridge-controller/tests/mock-quotes-native-erc20.ts index 186df0663b7..11cbd1575f2 100644 --- a/packages/bridge-controller/tests/mock-quotes-native-erc20.ts +++ b/packages/bridge-controller/tests/mock-quotes-native-erc20.ts @@ -4,6 +4,7 @@ import type { DeepPartial } from '../src/types.js'; import type { QuoteResponseV1 } from '../src/validators/quote-response-v1.js'; import { validateQuoteResponseV1 } from '../src/validators/quote-response-v1.js'; import { ActionTypes } from '../src/validators/step.js'; +import type { TxData } from '../src/validators/trade.js'; export const mockBridgeQuotesNativeErc20V1: QuoteResponseV1[] = [ { @@ -260,12 +261,12 @@ export const mockBridgeQuotesNativeErc20V1: QuoteResponseV1[] = [ }, ]; -export const getMockBridgeQuotesNativeErc20V2 = ( - quoteOverrides?: DeepPartial, -): QuoteResponseV1[] => { +export const getMockBridgeQuotesNativeErc20V1 = ( + quoteOverrides?: DeepPartial>, +): QuoteResponseV1[] => { return mockBridgeQuotesNativeErc20V1.map((quote) => { const mergedQuote = merge({}, quote, quoteOverrides); validateQuoteResponseV1(mergedQuote); - return mergedQuote; + return mergedQuote as QuoteResponseV1; }); }; diff --git a/packages/bridge-controller/tests/mock-quotes-sol-erc20.ts b/packages/bridge-controller/tests/mock-quotes-sol-erc20.ts index fc317b2891b..8aff2cea101 100644 --- a/packages/bridge-controller/tests/mock-quotes-sol-erc20.ts +++ b/packages/bridge-controller/tests/mock-quotes-sol-erc20.ts @@ -180,7 +180,7 @@ export const mockBridgeQuotesSolErc20V1: QuoteResponseV1[] = [ }, ]; -export const getMockBridgeQuotesSolErc20V2 = ( +export const getMockBridgeQuotesSolErc20V1 = ( quoteOverrides?: DeepPartial, ): QuoteResponseV1[] => { return mockBridgeQuotesSolErc20V1.map((quote) => {