diff --git a/packages/bridge-controller/src/bridge-controller.sse.test.ts b/packages/bridge-controller/src/bridge-controller.sse.test.ts index 37912d87b07..6414105ff7b 100644 --- a/packages/bridge-controller/src/bridge-controller.sse.test.ts +++ b/packages/bridge-controller/src/bridge-controller.sse.test.ts @@ -118,6 +118,7 @@ describe('BridgeController SSE', function () { isActiveDest: true, }, }, + chainRanking: [{ chainId: 'eip155:1' as const, name: 'Ethereum' }], }); bridgeController = new BridgeController({ diff --git a/packages/bridge-controller/src/bridge-controller.test.ts b/packages/bridge-controller/src/bridge-controller.test.ts index 545c045de33..bcc8c78478d 100644 --- a/packages/bridge-controller/src/bridge-controller.test.ts +++ b/packages/bridge-controller/src/bridge-controller.test.ts @@ -2700,6 +2700,7 @@ describe('BridgeController', function () { enabled: true, minimumVersion: '13.8.0', }, + chainRanking: [{ chainId: 'eip155:1' as const, name: 'Ethereum' }], }; const quotesByDecreasingProcessingTime = [...mockBridgeQuotesSolErc20]; diff --git a/packages/bridge-controller/src/constants/bridge.ts b/packages/bridge-controller/src/constants/bridge.ts index 3d7f9fb2b4e..f76333b24a4 100644 --- a/packages/bridge-controller/src/constants/bridge.ts +++ b/packages/bridge-controller/src/constants/bridge.ts @@ -48,12 +48,30 @@ export const DEFAULT_MAX_REFRESH_COUNT = 5; export const BRIDGE_CONTROLLER_NAME = 'BridgeController'; +export const DEFAULT_CHAIN_RANKING = [ + { chainId: 'eip155:1', name: 'Ethereum' }, + { chainId: 'eip155:56', name: 'BNB' }, + { chainId: 'bip122:000000000019d6689c085ae165831e93', name: 'BTC' }, + { chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', name: 'Solana' }, + { chainId: 'tron:728126428', name: 'Tron' }, + { chainId: 'eip155:8453', name: 'Base' }, + { chainId: 'eip155:42161', name: 'Arbitrum' }, + { chainId: 'eip155:59144', name: 'Linea' }, + { chainId: 'eip155:137', name: 'Polygon' }, + { chainId: 'eip155:43114', name: 'Avalanche' }, + { chainId: 'eip155:10', name: 'Optimism' }, + { chainId: 'eip155:143', name: 'Monad' }, + { chainId: 'eip155:1329', name: 'Sei' }, + { chainId: 'eip155:324', name: 'zkSync' }, +] as const; + export const DEFAULT_FEATURE_FLAG_CONFIG: FeatureFlagsPlatformConfig = { minimumVersion: '0.0.0', refreshRate: REFRESH_INTERVAL_MS, maxRefreshCount: DEFAULT_MAX_REFRESH_COUNT, support: false, chains: {}, + chainRanking: [...DEFAULT_CHAIN_RANKING], }; export const DEFAULT_BRIDGE_CONTROLLER_STATE: BridgeControllerState = { diff --git a/packages/bridge-controller/src/selectors.test.ts b/packages/bridge-controller/src/selectors.test.ts index 97e241a94b7..aa300c6c340 100644 --- a/packages/bridge-controller/src/selectors.test.ts +++ b/packages/bridge-controller/src/selectors.test.ts @@ -256,7 +256,7 @@ describe('Bridge Selectors', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any ...(mockState.remoteFeatureFlags.bridgeConfig as any), chains: { - '1': { + 'eip155:1': { refreshRate: 41000, isActiveSrc: true, isActiveDest: true, @@ -288,7 +288,7 @@ describe('Bridge Selectors', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any ...(mockState.remoteFeatureFlags.bridgeConfig as any), chains: { - '1': { + 'eip155:1': { refreshRate: 41000, isActiveSrc: true, isActiveDest: true, diff --git a/packages/bridge-controller/src/types.ts b/packages/bridge-controller/src/types.ts index a2acca431ef..bc7c082c3e5 100644 --- a/packages/bridge-controller/src/types.ts +++ b/packages/bridge-controller/src/types.ts @@ -30,6 +30,7 @@ import type { BitcoinTradeDataSchema, BridgeAssetSchema, ChainConfigurationSchema, + ChainRankingSchema, FeatureId, FeeDataSchema, PlatformConfigSchema, @@ -66,6 +67,8 @@ export enum AssetType { export type ChainConfiguration = Infer; +export type ChainRanking = Infer; + export type L1GasFees = { l1GasFeesInHexWei?: string; // l1 fees for approval and trade in hex wei, appended by BridgeController.#appendL1GasFees }; diff --git a/packages/bridge-controller/src/utils/feature-flags.test.ts b/packages/bridge-controller/src/utils/feature-flags.test.ts index 07bf3542190..b16543c7bd8 100644 --- a/packages/bridge-controller/src/utils/feature-flags.test.ts +++ b/packages/bridge-controller/src/utils/feature-flags.test.ts @@ -46,6 +46,7 @@ describe('feature-flags', () => { isActiveDest: true, }, }, + chainRanking: [], }; const result = formatFeatureFlags(bridgeConfig); @@ -95,6 +96,7 @@ describe('feature-flags', () => { support: true, minimumVersion: '0.0.0', chains: {}, + chainRanking: [], }; const result = formatFeatureFlags(bridgeConfig); @@ -124,6 +126,7 @@ describe('feature-flags', () => { isActiveDest: false, }, }, + chainRanking: [], }; const result = formatFeatureFlags(bridgeConfig); @@ -373,6 +376,7 @@ describe('feature-flags', () => { isActiveDest: true, }, }, + chainRanking: [], }; const bridgeConfig = { @@ -386,6 +390,7 @@ describe('feature-flags', () => { isActiveDest: true, }, }, + chainRanking: [], }; const remoteFeatureFlagControllerState = { @@ -414,6 +419,7 @@ describe('feature-flags', () => { isActiveDest: true, }, }, + chainRanking: [], }; expect(result).toStrictEqual(expectedBridgeConfig); @@ -431,6 +437,7 @@ describe('feature-flags', () => { isActiveDest: true, }, }, + chainRanking: [], }; const remoteFeatureFlagControllerState = { @@ -458,6 +465,7 @@ describe('feature-flags', () => { isActiveDest: true, }, }, + chainRanking: [], }; expect(result).toStrictEqual(expectedBridgeConfig); diff --git a/packages/bridge-controller/src/utils/validators.test.ts b/packages/bridge-controller/src/utils/validators.test.ts index 24298e11d52..06bfeab351e 100644 --- a/packages/bridge-controller/src/utils/validators.test.ts +++ b/packages/bridge-controller/src/utils/validators.test.ts @@ -28,6 +28,7 @@ describe('validators', () => { refreshRate: 30000, support: true, minimumVersion: '0.0.0', + chainRanking: [{ chainId: 'eip155:1', name: 'Ethereum' }], }, type: 'all evm chains active', expected: true, @@ -39,6 +40,7 @@ describe('validators', () => { refreshRate: 3000000, support: false, minimumVersion: '0.0.0', + chainRanking: [], }, type: 'bridge disabled', expected: true, @@ -103,6 +105,7 @@ describe('validators', () => { refreshRate: 30000, support: true, minimumVersion: '0.0.0', + chainRanking: [{ chainId: 'eip155:1', name: 'Ethereum' }], }, type: 'evm and solana chain config', expected: true, @@ -200,6 +203,7 @@ describe('validators', () => { refreshRate: 30000, support: true, minimumVersion: '0.0.0', + chainRanking: [{ chainId: 'eip155:1', name: 'Ethereum' }], }, type: 'evm and solana chain config + bip44 default pairs', expected: true, @@ -227,6 +231,7 @@ describe('validators', () => { enabled: true, minimumVersion: '13.8.0', }, + chainRanking: [{ chainId: 'eip155:1', name: 'Ethereum' }], }, type: 'sse config', expected: true, @@ -306,6 +311,7 @@ describe('validators', () => { support: true, minimumVersion: '0.0.0', extraField: 'foo', + chainRanking: [{ chainId: 'eip155:1', name: 'Ethereum' }], }, type: 'all evm chains active + an extra field not specified in the schema', expected: true, diff --git a/packages/bridge-controller/src/utils/validators.ts b/packages/bridge-controller/src/utils/validators.ts index 327a3f5eee1..b9135361a46 100644 --- a/packages/bridge-controller/src/utils/validators.ts +++ b/packages/bridge-controller/src/utils/validators.ts @@ -17,7 +17,11 @@ import { pattern, intersection, } from '@metamask/superstruct'; -import { CaipAssetTypeStruct, isStrictHexString } from '@metamask/utils'; +import { + CaipAssetTypeStruct, + CaipChainIdStruct, + isStrictHexString, +} from '@metamask/utils'; export enum FeeType { METABRIDGE = 'metabridge', @@ -101,6 +105,19 @@ const DefaultPairSchema = type({ other: record(string(), string()), }); +export const ChainRankingItemSchema = type({ + /** + * The CAIP-2 chain identifier (e.g., "eip155:1" for Ethereum mainnet) + */ + chainId: CaipChainIdStruct, + /** + * The display name of the chain (e.g., "Ethereum Mainnet") + */ + name: string(), +}); + +export const ChainRankingSchema = array(ChainRankingItemSchema); + export const ChainConfigurationSchema = type({ isActiveSrc: boolean(), isActiveDest: boolean(), @@ -154,6 +171,10 @@ export const PlatformConfigSchema = type({ minimumVersion: VersionStringSchema, }), ), + /** + * Array of chain objects ordered by preference/ranking + */ + chainRanking: ChainRankingSchema, }); export const validateFeatureFlagsResponse = (