diff --git a/packages/bridge-controller/CHANGELOG.md b/packages/bridge-controller/CHANGELOG.md index 4315bd64365..6129014db6e 100644 --- a/packages/bridge-controller/CHANGELOG.md +++ b/packages/bridge-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Define `QuoteResponse` v2 and `validateQuoteResponse` ([#9724](https://github.com/MetaMask/core/pull/9724)) +- Export `BridgeAsset` and `validateBridgeAsset` used by QuoteResponse v2, and token endpoints ([#9724](https://github.com/MetaMask/core/pull/9724)) + ### Changed - Bump `@metamask/assets-controllers` from `^110.0.0` to `^110.0.1` ([#9693](https://github.com/MetaMask/core/pull/9693), [#9706](https://github.com/MetaMask/core/pull/9706)) diff --git a/packages/bridge-controller/src/__snapshots__/bridge-controller.sse.batch.test.ts.snap b/packages/bridge-controller/src/__snapshots__/bridge-controller.sse.batch.test.ts.snap index a3e3ffbe8c1..157a704c069 100644 --- a/packages/bridge-controller/src/__snapshots__/bridge-controller.sse.batch.test.ts.snap +++ b/packages/bridge-controller/src/__snapshots__/bridge-controller.sse.batch.test.ts.snap @@ -28,13 +28,13 @@ exports[`BridgeController BatchSell (multiple quote requests) SSE fetch quotes s }, { "destChainId": "137", - "destTokenAddress": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", + "destTokenAddress": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", "destWalletAddress": "SolanaWalletAddres1234", "insufficientBal": false, "resetApproval": false, "slippage": 0.5, "srcChainId": "10", - "srcTokenAddress": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", + "srcTokenAddress": "0x0b2c639c533813f4aa9d7837caf62653d097ff85", "srcTokenAmount": "1000000000000000000", "walletAddress": "0x30E8ccaD5A980BDF30447f8c2C48e70989D9d294", }, diff --git a/packages/bridge-controller/src/index.ts b/packages/bridge-controller/src/index.ts index b5216dc7e2f..a8555ced3c8 100644 --- a/packages/bridge-controller/src/index.ts +++ b/packages/bridge-controller/src/index.ts @@ -43,7 +43,6 @@ export type { NonEvmFees, GasMultiplierByChainId, FeatureFlagResponse, - BridgeAsset, GenericQuoteRequest, BatchSellTradesResponse, GaslessProperties, @@ -118,6 +117,7 @@ export type { QuoteResponseV1 as QuoteResponse, QuoteResponseV1, } from './validators/quote-response-v1.js'; +export { validateQuoteResponse } from './validators/quote-response.js'; export type { Quote } from './validators/quote.js'; export { FeeType, DiscountType } from './validators/quote.js'; export { ActionTypes } from './validators/step.js'; @@ -127,9 +127,18 @@ export { } from './validators/quote-stream-complete.js'; export { BatchSellTransactionType } from './validators/batch-sell.js'; export { TokenFeatureType } from './validators/token-feature.js'; +export type { + BridgeAsset, + BridgeAssetV2, + MinimalAsset, +} from './validators/bridge-asset.js'; export { BridgeAssetSchema, validateBridgeAsset, + validateBridgeAssetV2, + MinimalAssetSchema, + BridgeAssetV2Schema, + BridgeAssetSecurityDataType, } from './validators/bridge-asset.js'; export { FeatureId } from './validators/feature-flags.js'; diff --git a/packages/bridge-controller/src/types.ts b/packages/bridge-controller/src/types.ts index b4fb0ea4212..23e1421c4b8 100644 --- a/packages/bridge-controller/src/types.ts +++ b/packages/bridge-controller/src/types.ts @@ -28,7 +28,6 @@ import type { BridgeController } from './bridge-controller.js'; import type { BRIDGE_CONTROLLER_NAME } from './constants/bridge.js'; import type { SimulatedGasFeeLimitsSchema } from './validators/batch-sell.js'; import type { BatchSellTradesResponseSchema } from './validators/batch-sell.js'; -import type { BridgeAssetSchema } from './validators/bridge-asset.js'; import type { ChainConfigurationSchema, ChainRankingSchema, @@ -99,12 +98,6 @@ export enum SortOrder { ETA_ASC = 'time_descending', } -/** - * This is the interface for the asset object returned by the bridge-api - * This type is used in the QuoteResponse and in the fetchBridgeTokens response - */ -export type BridgeAsset = Infer; - /** * This is the interface for the token object used in the extension client * In addition to the {@link BridgeAsset} fields, it includes balance information diff --git a/packages/bridge-controller/src/utils/bridge.ts b/packages/bridge-controller/src/utils/bridge.ts index 821cf883043..cc4fb259300 100644 --- a/packages/bridge-controller/src/utils/bridge.ts +++ b/packages/bridge-controller/src/utils/bridge.ts @@ -18,12 +18,9 @@ import { SYMBOL_TO_SLIP44_MAP, } from '../constants/tokens.js'; import type { SupportedSwapsNativeCurrencySymbols } from '../constants/tokens.js'; -import type { - BridgeAsset, - BridgeControllerState, - GenericQuoteRequest, -} from '../types.js'; +import type { BridgeControllerState, GenericQuoteRequest } from '../types.js'; import { ChainId } from '../types.js'; +import type { BridgeAsset } from '../validators/bridge-asset.js'; import type { QuoteResponseV1 } from '../validators/quote-response-v1.js'; import type { TxData } from '../validators/trade.js'; import { diff --git a/packages/bridge-controller/src/utils/caip-formatters.ts b/packages/bridge-controller/src/utils/caip-formatters.ts index c3626eb79c2..377b731540d 100644 --- a/packages/bridge-controller/src/utils/caip-formatters.ts +++ b/packages/bridge-controller/src/utils/caip-formatters.ts @@ -54,9 +54,6 @@ export const formatChainIdToCaip = ( return TrxScope.Mainnet; } if (isStellarChainId(chainId)) { - if (chainId === XlmScope.Testnet) { - return XlmScope.Testnet; - } return XlmScope.Pubnet; } return toEvmCaipChainId(numberToHex(Number(chainId))); diff --git a/packages/bridge-controller/src/utils/fetch.ts b/packages/bridge-controller/src/utils/fetch.ts index d8f60476941..9fca0814ef5 100644 --- a/packages/bridge-controller/src/utils/fetch.ts +++ b/packages/bridge-controller/src/utils/fetch.ts @@ -6,13 +6,13 @@ import type { FetchFunction, GenericQuoteRequest, QuoteRequest, - BridgeAsset, TokenFeature, QuoteStreamCompleteData, BatchSellTradesRequest, BatchSellTradesResponse, } from '../types.js'; import { validateBatchSellTradesResponse } from '../validators/batch-sell.js'; +import type { BridgeAsset } from '../validators/bridge-asset.js'; import { validateBridgeAsset } from '../validators/bridge-asset.js'; import type { FeatureId } from '../validators/feature-flags.js'; import type { QuoteResponseV1 } from '../validators/quote-response-v1.js'; diff --git a/packages/bridge-controller/src/utils/quote-metadata/calculators.ts b/packages/bridge-controller/src/utils/quote-metadata/calculators.ts index 5aa42e24a23..eaaf762c7e3 100644 --- a/packages/bridge-controller/src/utils/quote-metadata/calculators.ts +++ b/packages/bridge-controller/src/utils/quote-metadata/calculators.ts @@ -12,8 +12,8 @@ import type { ExchangeRate, NonEvmFees, DeepPartial, - BridgeAsset, } 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'; diff --git a/packages/bridge-controller/src/validators/amount-and-asset.ts b/packages/bridge-controller/src/validators/amount-and-asset.ts new file mode 100644 index 00000000000..8a084919112 --- /dev/null +++ b/packages/bridge-controller/src/validators/amount-and-asset.ts @@ -0,0 +1,33 @@ +import { + type, + optional, + nullable, + union, + string, + Infer, +} from '@metamask/superstruct'; + +import { BridgeAssetV2Schema } from './bridge-asset.js'; +import { NumberStringSchema, FloatStringSchema } from './number.js'; + +export const AmountsAndAssetSchema = type({ + /* + * The atomic amount of the asset + * @example "1000000000000000000" + */ + amount: NumberStringSchema, + /* + * The normalized amount of the asset + * @example "1.5" + */ + normalizedAmount: optional(FloatStringSchema), + asset: BridgeAssetV2Schema, + // TODO remove string and fix usd in backend + usd: optional(nullable(union([FloatStringSchema, string()]))), + /* + * The value of the asset in the currency, calculated based on usd value + * @example "0.15" + */ + valueInCurrency: optional(nullable(FloatStringSchema)), +}); +export type AmountsAndAsset = Infer; diff --git a/packages/bridge-controller/src/validators/bridge-asset.ts b/packages/bridge-controller/src/validators/bridge-asset.ts index 0e8092a691b..686b23dc9f3 100644 --- a/packages/bridge-controller/src/validators/bridge-asset.ts +++ b/packages/bridge-controller/src/validators/bridge-asset.ts @@ -5,12 +5,81 @@ import { optional, nullable, is, + intersection, + enums, + array, + boolean, } from '@metamask/superstruct'; import type { Infer } from '@metamask/superstruct'; import { CaipAssetTypeStruct } from '@metamask/utils'; export const ChainIdSchema = number(); +export const MinimalAssetSchema = type({ + /** + * Case-sensitive for non-EVM chains, case-insensitive for EVM chains + */ + assetId: CaipAssetTypeStruct, + /** + * The symbol of token object + */ + symbol: string(), + /** + * The name for the network + */ + name: string(), + decimals: number(), +}); + +export type MinimalAsset = Infer; + +export enum BridgeAssetSecurityDataType { + INFO = 'Info', + BENIGN = 'Benign', + VERIFIED = 'Verified', + WARNING = 'Warning', + SPAM = 'Spam', + MALICIOUS = 'Malicious', +} + +const BridgeAssetSecurityData = type({ + isVerified: optional(boolean()), + securityData: optional( + type({ + type: enums(Object.values(BridgeAssetSecurityDataType)), + metadata: optional( + type({ + features: array( + type({ + featureId: string(), + type: enums(Object.values(BridgeAssetSecurityDataType)), + description: string(), + }), + ), + }), + ), + }), + ), +}); + +export const BridgeAssetV2Schema = intersection([ + MinimalAssetSchema, + BridgeAssetSecurityData, + type({ + /** + * URL for token icon + */ + iconUrl: nullable(optional(string())), + noFee: optional( + type({ + isDestination: nullable(optional(boolean())), + isSource: nullable(optional(boolean())), + }), + ), + }), +]); +export type BridgeAssetV2 = Infer; + export const BridgeAssetSchema = type({ /** * The chainId of the token @@ -43,8 +112,31 @@ export const BridgeAssetSchema = type({ iconUrl: optional(nullable(string())), }); +/** + * This is the interface for the asset object returned by the bridge-api + * This type is used in the QuoteResponse and in the fetchBridgeTokens response + * + * @deprecated Avoid introducing new code that uses this function. Use BridgeAssetV2 instead + */ +export type BridgeAsset = Infer; + +/** + * Validates a token object from the bridge-api + * + * @deprecated Avoid introducing new code that uses this function. Use BridgeAssetV2 instead + * + * @param data - The data to validate + * @returns Whether the data satisfies the {@link BridgeAssetSchema} + */ export const validateBridgeAsset = ( data: unknown, ): data is Infer => { return is(data, BridgeAssetSchema); }; + +/* istanbul ignore next */ +export const validateBridgeAssetV2 = ( + data: unknown, +): data is Infer => { + return is(data, BridgeAssetV2Schema); +}; diff --git a/packages/bridge-controller/src/validators/quote-response.test.ts b/packages/bridge-controller/src/validators/quote-response.test.ts new file mode 100644 index 00000000000..5f15b86e29c --- /dev/null +++ b/packages/bridge-controller/src/validators/quote-response.test.ts @@ -0,0 +1,33 @@ +import { KnownCaipNamespace } from '@metamask/utils'; + +import { mockBridgeQuotesErc20Erc20V2Migration } from '../../tests/mock-quotes-erc20-erc20-migration-v2.js'; +import { validateQuoteResponse } from './quote-response.js'; + +describe('quote-response-v2', () => { + describe('validateQuoteResponse', () => { + it('should return a validation error for an invalid quote response', () => { + const quoteResponse = { + quote: { + requestId: '123', + }, + }; + + expect(() => + validateQuoteResponse(quoteResponse), + ).toThrowErrorMatchingInlineSnapshot( + `"At path: quote.src -- Expected an object, but received: undefined"`, + ); + }); + + it('should validate a valid quote response', () => { + const quoteResponse = mockBridgeQuotesErc20Erc20V2Migration[0]; + expect(validateQuoteResponse(quoteResponse)).toBe(true); + }); + + it('should return false when quote namespace is not supported', () => { + const quoteResponse = mockBridgeQuotesErc20Erc20V2Migration[0]; + quoteResponse.quote.src.asset.assetId = `${KnownCaipNamespace.Wallet}:123/token:test`; + expect(validateQuoteResponse(quoteResponse)).toBe(false); + }); + }); +}); diff --git a/packages/bridge-controller/src/validators/quote-response.ts b/packages/bridge-controller/src/validators/quote-response.ts new file mode 100644 index 00000000000..9b5b40fcf47 --- /dev/null +++ b/packages/bridge-controller/src/validators/quote-response.ts @@ -0,0 +1,214 @@ +import { + assert, + Infer, + number, + optional, + string, + type, + union, + intersection, + Describe, + nullable, + enums, + literal, + AnyStruct, +} from '@metamask/superstruct'; +import { + CaipChainId, + CaipChainIdStruct, + KnownCaipNamespace, + parseCaipAssetType, + StrictHexStruct, +} from '@metamask/utils'; + +import { FeatureId } from './feature-flags.js'; +import { FloatStringSchema } from './number.js'; +import { QuoteSchemaV2 } from './quote.js'; +import { + BitcoinTradeData, + BitcoinTradeDataSchema, + StellarTradeData, + StellarTradeDataSchema, + TronTradeData, + TronTradeDataSchema, + TxData, + TxDataSchema, +} from './trade.js'; + +const CommonQuoteResponseSchema = type({ + quoteId: optional(string()), + quote: QuoteSchemaV2, + estimatedProcessingTimeInSeconds: number(), + /** + * Appended to the quote if there are multiple quote requests in a batch. This + * indicates which quoteRequest the quote is for + */ + quoteRequestIndex: optional(number()), + /** + * Appended to the quote response based on the quote requested featureId + */ + featureId: optional(enums(Object.values(FeatureId))), + /** + * Appended to the quote response based on the quote request nonEvmFeesInNative flag + * + * @deprecated Use network feeData + */ + nonEvmFeesInNative: optional(FloatStringSchema), + /** + * Appended to the quote response based on the quote request l1GasFeesInHexWei flag + * + * @deprecated Use network feeData + */ + l1GasFeesInHexWei: optional(StrictHexStruct), +}); + +const EvmQuoteResponseSchema = intersection([ + CommonQuoteResponseSchema, + type({ + namespace: literal(KnownCaipNamespace.Eip155), + chainId: CaipChainIdStruct, + trade: TxDataSchema, + approval: optional(TxDataSchema), + resetApproval: optional(TxDataSchema), + }), +]); + +const TronQuoteResponseSchema = intersection([ + CommonQuoteResponseSchema, + type({ + namespace: literal(KnownCaipNamespace.Tron), + chainId: CaipChainIdStruct, + trade: TronTradeDataSchema, + approval: optional(TronTradeDataSchema), + }), +]); + +const SolanaQuoteResponseSchema = intersection([ + CommonQuoteResponseSchema, + type({ + namespace: literal(KnownCaipNamespace.Solana), + chainId: CaipChainIdStruct, + trade: string(), + approval: optional(TxDataSchema), + }), +]); + +const BitcoinQuoteResponseSchema = intersection([ + CommonQuoteResponseSchema, + type({ + namespace: literal(KnownCaipNamespace.Bip122), + chainId: CaipChainIdStruct, + trade: BitcoinTradeDataSchema, + approval: optional(TxDataSchema), + }), +]); + +const StellarQuoteResponseSchema = intersection([ + CommonQuoteResponseSchema, + type({ + namespace: literal(KnownCaipNamespace.Stellar), + chainId: CaipChainIdStruct, + trade: StellarTradeDataSchema, + approval: optional(TxDataSchema), + }), +]); + +export const QuoteResponseSchemaV2 = nullable( + union([ + EvmQuoteResponseSchema, + SolanaQuoteResponseSchema, + TronQuoteResponseSchema, + BitcoinQuoteResponseSchema, + StellarQuoteResponseSchema, + ]), +); + +/** + * This is the V2 QuoteResponse type, including metadata calculated after quote fetch + */ +export type QuoteResponse = Omit< + NonNullable>, + 'trade' | 'approval' | 'resetApproval' +> & + ( + | { + namespace: KnownCaipNamespace.Eip155; + chainId: CaipChainId; + trade: TxData & { + data: string; + }; + approval?: TxData; + /** + * Appended to the quote response based on the quote request resetApproval flag + * If defined, the quote's total network fee will include the reset approval's gas limit. + */ + resetApproval?: TxData; + } + | { + namespace: KnownCaipNamespace.Solana; + chainId: CaipChainId; + trade: string; + approval?: TxData; + } + | { + namespace: KnownCaipNamespace.Tron; + chainId: CaipChainId; + trade: TronTradeData & { + // eslint-disable-next-line @typescript-eslint/naming-convention + raw_data_hex: string; + }; + approval?: TronTradeData; + } + | { + namespace: KnownCaipNamespace.Bip122; + chainId: CaipChainId; + trade: BitcoinTradeData; + approval?: TxData; + } + | { + namespace: KnownCaipNamespace.Stellar; + chainId: CaipChainId; + trade: StellarTradeData; + approval?: TxData; + } + ); +// This ensures the QuoteResponse type is in sync with the QuoteResponseSchemaV2 +const QuoteResponse: Describe = QuoteResponseSchemaV2; + +const NAMESPACE_TO_TRADE_SCHEMA: Record = + { + [KnownCaipNamespace.Eip155]: EvmQuoteResponseSchema, + [KnownCaipNamespace.Tron]: TronQuoteResponseSchema, + [KnownCaipNamespace.Solana]: SolanaQuoteResponseSchema, + [KnownCaipNamespace.Bip122]: BitcoinQuoteResponseSchema, + [KnownCaipNamespace.Stellar]: StellarQuoteResponseSchema, + }; + +export const validateQuoteResponse = ( + quoteResponse: unknown, +): quoteResponse is QuoteResponse => { + // Validate common fields first + assert(quoteResponse, CommonQuoteResponseSchema); + + // Extract the namespace and chainId from the src asset + const { + chain: { namespace: namespaceString }, + chainId, + } = parseCaipAssetType(quoteResponse.quote.src.asset.assetId); + const namespace = namespaceString as QuoteResponse['namespace']; + + if (!NAMESPACE_TO_TRADE_SCHEMA[namespace]) { + return false; + } + + // Validate the trade and approval fields based on the src chain's namespace + assert( + { + ...quoteResponse, + namespace, + chainId, + }, + NAMESPACE_TO_TRADE_SCHEMA[namespace], + ); + return true; +}; diff --git a/packages/bridge-controller/src/validators/quote.ts b/packages/bridge-controller/src/validators/quote.ts index 66bd8106497..5b07a7e8581 100644 --- a/packages/bridge-controller/src/validators/quote.ts +++ b/packages/bridge-controller/src/validators/quote.ts @@ -10,10 +10,15 @@ import { } from '@metamask/superstruct'; import type { Infer } from '@metamask/superstruct'; +import { AmountsAndAssetSchema } from './amount-and-asset.js'; import { ChainIdSchema, BridgeAssetSchema } from './bridge-asset.js'; import { IntentSchema } from './intent.js'; -import { TruthyDigitStringSchema, NumberStringSchema } from './number.js'; -import { RefuelDataSchema, StepSchema } from './step.js'; +import { + TruthyDigitStringSchema, + NumberStringSchema, + FloatStringSchema, +} from './number.js'; +import { RefuelDataSchema, StepSchema, StepSchemaV2 } from './step.js'; export enum FeeType { METABRIDGE = 'metabridge', @@ -114,3 +119,77 @@ export const QuoteSchema = intersection([ ]); export type Quote = Infer; + +export const QuoteSchemaV2 = intersection([ + GaslessPropertiesSchema, + type({ + requestId: string(), + src: intersection([ + AmountsAndAssetSchema, + type({ + walletAddress: optional(string()), + }), + ]), + dest: intersection([ + AmountsAndAssetSchema, + type({ + minAmount: optional(string()), + minAmountUsd: optional(string()), + minAmountValueInCurrency: optional(string()), + minAmountNormalized: optional(string()), + walletAddress: optional(string()), + }), + ]), + priceData: optional( + type({ + swapRate: optional(FloatStringSchema), + priceImpact: optional( + type({ + usd: optional(nullable(FloatStringSchema)), + amount: optional(nullable(FloatStringSchema)), + valueInCurrency: optional(FloatStringSchema), + }), + ), + adjustedReturn: optional( + type({ + usd: nullable(optional(FloatStringSchema)), + valueInCurrency: nullable(optional(FloatStringSchema)), + }), + ), + }), + ), + feeData: type({ + [FeeType.METABRIDGE]: array( + intersection([ + AmountsAndAssetSchema, + type({ + quoteBpsFee: optional(number()), + baseBpsFee: optional(number()), + discountType: optional(nullable(string())), + }), + ]), + ), + [FeeType.REFUEL]: optional(array(AmountsAndAssetSchema)), + /** + * The tx fees included in the quote for gasless execution + */ + [FeeType.TX_FEE]: optional( + array(intersection([AmountsAndAssetSchema, TxFeeGasLimitsSchema])), + ), + /** + * The gas fees for the quote, excluding any provider or relayer fees + */ + [FeeType.NETWORK]: optional(array(AmountsAndAssetSchema)), + /** + * The relayer or provider fees for the quote, + */ + [FeeType.RELAYER]: optional(array(AmountsAndAssetSchema)), + }), + aggregator: string(), + protocols: array(string()), + steps: optional(array(StepSchemaV2)), + refuel: optional(StepSchema), + intent: optional(IntentSchema), + slippage: optional(number()), + }), +]); diff --git a/packages/bridge-controller/src/validators/step.ts b/packages/bridge-controller/src/validators/step.ts index cf3fff44b72..64b185fca76 100644 --- a/packages/bridge-controller/src/validators/step.ts +++ b/packages/bridge-controller/src/validators/step.ts @@ -1,5 +1,6 @@ -import { type, enums, optional } from '@metamask/superstruct'; +import { type, enums, optional, pick } from '@metamask/superstruct'; +import { AmountsAndAssetSchema } from './amount-and-asset.js'; import { BridgeAssetSchema, ChainIdSchema } from './bridge-asset.js'; export enum ActionTypes { @@ -17,3 +18,9 @@ export const StepSchema = type({ }); export const RefuelDataSchema = StepSchema; + +export const StepSchemaV2 = type({ + action: enums(Object.values(ActionTypes)), + src: pick(AmountsAndAssetSchema, ['asset']), + dest: pick(AmountsAndAssetSchema, ['asset']), +}); diff --git a/packages/bridge-controller/tests/mock-quotes-erc20-erc20-migration-v2.ts b/packages/bridge-controller/tests/mock-quotes-erc20-erc20-migration-v2.ts new file mode 100644 index 00000000000..1befe07bd44 --- /dev/null +++ b/packages/bridge-controller/tests/mock-quotes-erc20-erc20-migration-v2.ts @@ -0,0 +1,114 @@ +import type { QuoteResponse } from '../src/validators/quote-response.js'; +import { ActionTypes } from '../src/validators/step.js'; + +/** + * This is the V2 QuoteResponse + */ +export const mockBridgeQuotesErc20Erc20V2Migration: Omit< + QuoteResponse, + 'chainId' | 'namespace' +>[] = [ + { + approval: { + chainId: 10, + data: '0x095ea7b3000000000000000000000000b90357f2b86dbfd59c3502215d4060f71df8ca0e0000000000000000000000000000000000000000000000000000000000d59f80', + from: '0x141d32a89a1e0a5ef360034a2f60a4b917c18838', + gasLimit: 61865, + to: '0x0b2c639c533813f4aa9d7837caf62653d097ff85', + value: '0x00', + }, + estimatedProcessingTimeInSeconds: 60, + quote: { + dest: { + amount: '13984280', + asset: { + assetId: + 'eip155:137/erc20:0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', + decimals: 6, + name: 'Native USD Coin (POS)', + symbol: 'USDC', + iconUrl: 'https://media.socket.tech/tokens/all/USDC', + }, + minAmount: '13700000', + }, + feeData: { + metabridge: [ + { + amount: '0', + asset: { + assetId: + 'eip155:10/erc20:0x0b2c639c533813f4aa9d7837caf62653d097ff85', + decimals: 6, + name: 'USD Coin', + symbol: 'USDC', + }, + }, + ], + network: [ + { + amount: '150000000', + asset: { + assetId: 'eip155:10/slip44:60', + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, + usd: undefined, + valueInCurrency: undefined, + }, + ], + }, + gasIncluded: false, + gasIncluded7702: false, + gasSponsored: false, + priceData: {}, + protocols: ['across'], + aggregator: 'socket', + requestId: '90ae8e69-f03a-4cf6-bab7-ed4e3431eb37', + slippage: 2, + src: { + amount: '14000000', + asset: { + assetId: 'eip155:10/erc20:0x0b2c639c533813f4aa9d7837caf62653d097ff85', + decimals: 6, + name: 'USD Coin', + symbol: 'USDC', + iconUrl: 'https://media.socket.tech/tokens/all/USDC', + }, + }, + steps: [ + { + action: ActionTypes.BRIDGE, + src: { + asset: { + symbol: 'USDC', + assetId: + 'eip155:10/erc20:0x0b2c639c533813f4aa9d7837caf62653d097ff85', + name: 'USD Coin', + decimals: 6, + iconUrl: 'https://assets.polygon.technology/tokenAssets/usdc.svg', + }, + }, + dest: { + asset: { + symbol: 'USDC', + assetId: + 'eip155:137/erc20:0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', + name: 'Native USD Coin (POS)', + decimals: 6, + iconUrl: 'https://assets.polygon.technology/tokenAssets/usdc.svg', + }, + }, + }, + ], + }, + trade: { + chainId: 10, + data: '0x3ce33bff00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000000000000000000000000000000000000000d59f8000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000f736f636b6574416461707465725632000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e00000000000000000000000003a23f943181408eac424116af7b7790c94cb97a50000000000000000000000003a23f943181408eac424116af7b7790c94cb97a500000000000000000000000000000000000000000000000000000000000000890000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c33590000000000000000000000000000000000000000000000000000000000d59f8000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000716a8b9dd056055c84b7a2ba0a016099465a518700000000000000000000000000000000000000000000000000000000000004a0c3540448000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000019d0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000084ad69fa4f00000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000141d32a89a1e0a5ef360034a2f60a4b917c1883800000000000000000000000000000000000000000000000000000000000000890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000284792ebcb90000000000000000000000000000000000000000000000000000000000d59f80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000454000000000000000000000000000000000000000000000000000000000000000c40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000141d32a89a1e0a5ef360034a2f60a4b917c18838000000000000000000000000141d32a89a1e0a5ef360034a2f60a4b917c1883800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c335900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000d55a40000000000000000000000000000000000000000000000000000000000000008900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000067041c47000000000000000000000000000000000000000000000000000000006704704d00000000000000000000000000000000000000000000000000000000d00dfeeddeadbeef765753be7f7a64d5509974b0d678e1e3149b02f42c7402906f9888136205038026f20b3f6df2899044cab41d632bc7a6c35debd40516df85de6f194aeb05b72cb9ea4d5ce0f7c56c91a79536331112f1a846dc641c', + from: '0x141d32a89a1e0a5ef360034a2f60a4b917c18838', + gasLimit: 287227, + to: '0xB90357f2b86dbfD59c3502215d4060f71DF8ca0e', + value: '0x038d7ea4c68000', + }, + }, +]; diff --git a/packages/bridge-controller/tests/mock-quotes-erc20-erc20.ts b/packages/bridge-controller/tests/mock-quotes-erc20-erc20.ts index cf705916861..2f1fd15ad5e 100644 --- a/packages/bridge-controller/tests/mock-quotes-erc20-erc20.ts +++ b/packages/bridge-controller/tests/mock-quotes-erc20-erc20.ts @@ -18,7 +18,7 @@ export const mockBridgeQuotesErc20Erc20V1: QuoteResponseV1[] = [ srcChainId: 10, srcAsset: { chainId: 10, - address: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', + address: '0x0b2c639c533813f4aa9d7837caf62653d097ff85', assetId: 'eip155:10/erc20:0x0b2c639c533813f4aa9d7837caf62653d097ff85', symbol: 'USDC', name: 'USD Coin', @@ -29,7 +29,7 @@ export const mockBridgeQuotesErc20Erc20V1: QuoteResponseV1[] = [ destChainId: 137, destAsset: { chainId: 137, - address: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', + address: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', assetId: 'eip155:137/erc20:0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', symbol: 'USDC', name: 'Native USD Coin (POS)', @@ -43,7 +43,7 @@ export const mockBridgeQuotesErc20Erc20V1: QuoteResponseV1[] = [ amount: '0', asset: { chainId: 10, - address: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', + address: '0x0b2c639c533813f4aa9d7837caf62653d097ff85', assetId: 'eip155:10/erc20:0x0b2c639c533813f4aa9d7837caf62653d097ff85', symbol: 'USDC',