From 9cf8d833f504c0a75b62c75d3ec8831f65e944d1 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 12 Mar 2025 18:26:35 -0400 Subject: [PATCH 1/4] chore: allow the bridge controller to accept a custom bridge API url in the constructor --- .../src/bridge-controller.test.ts | 9 ++++-- .../src/bridge-controller.ts | 12 +++++++ packages/bridge-controller/src/index.ts | 8 ++--- .../src/utils/bridge.test.ts | 32 ------------------- .../bridge-controller/src/utils/bridge.ts | 11 ------- .../bridge-controller/src/utils/fetch.test.ts | 21 ++++++++++-- packages/bridge-controller/src/utils/fetch.ts | 15 +++++---- 7 files changed, 48 insertions(+), 60 deletions(-) diff --git a/packages/bridge-controller/src/bridge-controller.test.ts b/packages/bridge-controller/src/bridge-controller.test.ts index 05d4a758ed5..63b9c8d335e 100644 --- a/packages/bridge-controller/src/bridge-controller.test.ts +++ b/packages/bridge-controller/src/bridge-controller.test.ts @@ -6,13 +6,13 @@ import nock from 'nock'; import { BridgeController } from './bridge-controller'; import { BridgeClientId, + BRIDGE_PROD_API_BASE_URL, DEFAULT_BRIDGE_CONTROLLER_STATE, } from './constants/bridge'; import { CHAIN_IDS } from './constants/chains'; import { SWAPS_API_V2_BASE_URL } from './constants/swaps'; import type { BridgeControllerMessenger, QuoteResponse } from './types'; import * as balanceUtils from './utils/balance'; -import { getBridgeApiBaseUrl } from './utils/bridge'; import * as fetchUtils from './utils/fetch'; import { flushPromises } from '../../../tests/helpers'; import { handleFetch } from '../../controller-utils/src'; @@ -55,7 +55,7 @@ describe('BridgeController', function () { jest.clearAllMocks(); jest.clearAllTimers(); - nock(getBridgeApiBaseUrl()) + nock(BRIDGE_PROD_API_BASE_URL) .get('/getAllFeatureFlags') .reply(200, { 'extension-config': { @@ -117,7 +117,7 @@ describe('BridgeController', function () { '534352': 2.4, }, }); - nock(getBridgeApiBaseUrl()) + nock(BRIDGE_PROD_API_BASE_URL) .get('/getTokens?chainId=10') .reply(200, [ { @@ -338,6 +338,7 @@ describe('BridgeController', function () { expect.any(AbortSignal), BridgeClientId.EXTENSION, mockFetchFn, + BRIDGE_PROD_API_BASE_URL, ); expect(bridgeController.state.quotesLastFetched).toBeNull(); @@ -488,6 +489,7 @@ describe('BridgeController', function () { expect.any(AbortSignal), BridgeClientId.EXTENSION, mockFetchFn, + BRIDGE_PROD_API_BASE_URL, ); expect(bridgeController.state.quotesLastFetched).toBeNull(); @@ -715,6 +717,7 @@ describe('BridgeController', function () { expect.any(AbortSignal), BridgeClientId.EXTENSION, mockFetchFn, + BRIDGE_PROD_API_BASE_URL, ); expect(bridgeController.state.quotesLastFetched).toBeNull(); diff --git a/packages/bridge-controller/src/bridge-controller.ts b/packages/bridge-controller/src/bridge-controller.ts index 98e74ffc983..3b78be46355 100644 --- a/packages/bridge-controller/src/bridge-controller.ts +++ b/packages/bridge-controller/src/bridge-controller.ts @@ -13,6 +13,7 @@ import type { Hex } from '@metamask/utils'; import type { BridgeClientId } from './constants/bridge'; import { BRIDGE_CONTROLLER_NAME, + BRIDGE_PROD_API_BASE_URL, DEFAULT_BRIDGE_CONTROLLER_STATE, METABRIDGE_CHAIN_TO_ADDRESS_MAP, REFRESH_INTERVAL_MS, @@ -95,12 +96,17 @@ export class BridgeController extends StaticIntervalPollingController; @@ -110,6 +116,9 @@ export class BridgeController extends StaticIntervalPollingController Promise; fetchFn: FetchFunction; + config?: { + customBridgeApiBaseUrl?: string; + }; }) { super({ name: BRIDGE_CONTROLLER_NAME, @@ -127,6 +136,7 @@ export class BridgeController extends StaticIntervalPollingController { state.bridgeFeatureFlags = bridgeFeatureFlags; @@ -278,6 +289,7 @@ export class BridgeController extends StaticIntervalPollingController { expect(isSwapsDefaultTokenSymbol('ETH', '' as Hex)).toBe(false); }); }); - - describe('getBridgeApiBaseUrl', () => { - const originalEnv = process.env; - - beforeEach(() => { - process.env = { ...originalEnv }; - }); - - afterEach(() => { - process.env = originalEnv; - }); - - it('returns custom API URL when BRIDGE_CUSTOM_API_BASE_URL is set', () => { - process.env.BRIDGE_CUSTOM_API_BASE_URL = 'https://custom-api.example.com'; - expect(getBridgeApiBaseUrl()).toBe('https://custom-api.example.com'); - }); - - it('returns dev API URL when BRIDGE_USE_DEV_APIS is set', () => { - process.env.BRIDGE_USE_DEV_APIS = 'true'; - expect(getBridgeApiBaseUrl()).toBe(BRIDGE_DEV_API_BASE_URL); - }); - - it('returns prod API URL by default', () => { - expect(getBridgeApiBaseUrl()).toBe(BRIDGE_PROD_API_BASE_URL); - }); - }); }); diff --git a/packages/bridge-controller/src/utils/bridge.ts b/packages/bridge-controller/src/utils/bridge.ts index 666e8ebb841..897fa11c317 100644 --- a/packages/bridge-controller/src/utils/bridge.ts +++ b/packages/bridge-controller/src/utils/bridge.ts @@ -17,17 +17,6 @@ export const getDefaultBridgeControllerState = (): BridgeControllerState => { return DEFAULT_BRIDGE_CONTROLLER_STATE; }; -export const getBridgeApiBaseUrl = () => { - if (process.env.BRIDGE_CUSTOM_API_BASE_URL) { - return process.env.BRIDGE_CUSTOM_API_BASE_URL; - } - - if (process.env.BRIDGE_USE_DEV_APIS) { - return BRIDGE_DEV_API_BASE_URL; - } - - return BRIDGE_PROD_API_BASE_URL; -}; /** * A function to return the txParam data for setting allowance to 0 for USDT on Ethereum * diff --git a/packages/bridge-controller/src/utils/fetch.test.ts b/packages/bridge-controller/src/utils/fetch.test.ts index 469c900883f..4c38695d30a 100644 --- a/packages/bridge-controller/src/utils/fetch.test.ts +++ b/packages/bridge-controller/src/utils/fetch.test.ts @@ -7,7 +7,7 @@ import { } from './fetch'; import mockBridgeQuotesErc20Erc20 from '../../tests/mock-quotes-erc20-erc20.json'; import mockBridgeQuotesNativeErc20 from '../../tests/mock-quotes-native-erc20.json'; -import { BridgeClientId } from '../constants/bridge'; +import { BridgeClientId, BRIDGE_PROD_API_BASE_URL } from '../constants/bridge'; import { CHAIN_IDS } from '../constants/chains'; const mockFetchFn = jest.fn(); @@ -56,6 +56,7 @@ describe('fetch', () => { const result = await fetchBridgeFeatureFlags( BridgeClientId.EXTENSION, mockFetchFn, + BRIDGE_PROD_API_BASE_URL, ); expect(mockFetchFn).toHaveBeenCalledWith( @@ -129,6 +130,7 @@ describe('fetch', () => { const result = await fetchBridgeFeatureFlags( BridgeClientId.EXTENSION, mockFetchFn, + BRIDGE_PROD_API_BASE_URL, ); expect(mockFetchFn).toHaveBeenCalledWith( @@ -156,7 +158,11 @@ describe('fetch', () => { mockFetchFn.mockRejectedValue(mockError); await expect( - fetchBridgeFeatureFlags(BridgeClientId.EXTENSION, mockFetchFn), + fetchBridgeFeatureFlags( + BridgeClientId.EXTENSION, + mockFetchFn, + BRIDGE_PROD_API_BASE_URL, + ), ).rejects.toThrow(mockError); }); }); @@ -196,6 +202,7 @@ describe('fetch', () => { '0xa', BridgeClientId.EXTENSION, mockFetchFn, + BRIDGE_PROD_API_BASE_URL, ); expect(mockFetchFn).toHaveBeenCalledWith( @@ -233,7 +240,12 @@ describe('fetch', () => { mockFetchFn.mockRejectedValue(mockError); await expect( - fetchBridgeTokens('0xa', BridgeClientId.EXTENSION, mockFetchFn), + fetchBridgeTokens( + '0xa', + BridgeClientId.EXTENSION, + mockFetchFn, + BRIDGE_PROD_API_BASE_URL, + ), ).rejects.toThrow(mockError); }); }); @@ -256,6 +268,7 @@ describe('fetch', () => { signal, BridgeClientId.EXTENSION, mockFetchFn, + BRIDGE_PROD_API_BASE_URL, ); expect(mockFetchFn).toHaveBeenCalledWith( @@ -290,6 +303,7 @@ describe('fetch', () => { signal, BridgeClientId.EXTENSION, mockFetchFn, + BRIDGE_PROD_API_BASE_URL, ); expect(mockFetchFn).toHaveBeenCalledWith( @@ -343,6 +357,7 @@ describe('fetch', () => { signal, BridgeClientId.EXTENSION, mockFetchFn, + BRIDGE_PROD_API_BASE_URL, ); expect(mockFetchFn).toHaveBeenCalledWith( diff --git a/packages/bridge-controller/src/utils/fetch.ts b/packages/bridge-controller/src/utils/fetch.ts index 3e9d993cdbb..33bc3e7533e 100644 --- a/packages/bridge-controller/src/utils/fetch.ts +++ b/packages/bridge-controller/src/utils/fetch.ts @@ -4,7 +4,6 @@ import { hexToNumber, numberToHex } from '@metamask/utils'; import { isSwapsDefaultTokenAddress, isSwapsDefaultTokenSymbol, - getBridgeApiBaseUrl, } from './bridge'; import { validateFeatureFlagsResponse, @@ -35,13 +34,15 @@ export const getClientIdHeader = (clientId: string) => ({ * * @param clientId - The client ID for metrics * @param fetchFn - The fetch function to use + * @param bridgeApiBaseUrl - The base URL for the bridge API * @returns The bridge feature flags */ export async function fetchBridgeFeatureFlags( clientId: string, fetchFn: FetchFunction, + bridgeApiBaseUrl: string, ): Promise { - const url = `${getBridgeApiBaseUrl()}/getAllFeatureFlags`; + const url = `${bridgeApiBaseUrl}/getAllFeatureFlags`; const rawFeatureFlags: unknown = await fetchFn(url, { headers: getClientIdHeader(clientId), }); @@ -82,17 +83,17 @@ export async function fetchBridgeFeatureFlags( * @param chainId - The chain ID to fetch tokens for * @param clientId - The client ID for metrics * @param fetchFn - The fetch function to use + * @param bridgeApiBaseUrl - The base URL for the bridge API * @returns A list of enabled (unblocked) tokens */ export async function fetchBridgeTokens( chainId: Hex, clientId: string, fetchFn: FetchFunction, + bridgeApiBaseUrl: string, ): Promise> { // TODO make token api v2 call - const url = `${getBridgeApiBaseUrl()}/getTokens?chainId=${hexToNumber( - chainId, - )}`; + const url = `${bridgeApiBaseUrl}/getTokens?chainId=${hexToNumber(chainId)}`; // TODO we will need to cache these. In Extension fetchWithCache is used. This is due to the following: // If we allow selecting dest networks which the user has not imported, @@ -132,6 +133,7 @@ export async function fetchBridgeTokens( * @param signal - The abort signal * @param clientId - The client ID for metrics * @param fetchFn - The fetch function to use + * @param bridgeApiBaseUrl - The base URL for the bridge API * @returns A list of bridge tx quotes */ export async function fetchBridgeQuotes( @@ -139,6 +141,7 @@ export async function fetchBridgeQuotes( signal: AbortSignal, clientId: string, fetchFn: FetchFunction, + bridgeApiBaseUrl: string, ): Promise { const queryParams = new URLSearchParams({ walletAddress: request.walletAddress, @@ -151,7 +154,7 @@ export async function fetchBridgeQuotes( insufficientBal: request.insufficientBal ? 'true' : 'false', resetApproval: request.resetApproval ? 'true' : 'false', }); - const url = `${getBridgeApiBaseUrl()}/getQuote?${queryParams}`; + const url = `${bridgeApiBaseUrl}/getQuote?${queryParams}`; const quotes: unknown[] = await fetchFn(url, { headers: getClientIdHeader(clientId), signal, From c2657b87b9f363004a3fa284bcf49cad7e173193 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 12 Mar 2025 18:31:54 -0400 Subject: [PATCH 2/4] chore: allow the bridge status controller to accept a custom bridge API url in the constructor --- .../src/bridge-status-controller.ts | 11 ++++++++ .../src/utils/bridge-status.test.ts | 25 +++++++++++++++---- .../src/utils/bridge-status.ts | 8 +++--- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index cd82ec0b488..1b7ef7f94c8 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -1,5 +1,6 @@ import type { StateMetadata } from '@metamask/base-controller'; import type { BridgeClientId } from '@metamask/bridge-controller'; +import { BRIDGE_PROD_API_BASE_URL } from '@metamask/bridge-controller'; import { StaticIntervalPollingController } from '@metamask/polling-controller'; import { numberToHex, type Hex } from '@metamask/utils'; @@ -46,16 +47,24 @@ export class BridgeStatusController extends StaticIntervalPollingController; clientId: BridgeClientId; fetchFn: FetchFunction; + config?: { + customBridgeApiBaseUrl?: string; + }; }) { super({ name: BRIDGE_STATUS_CONTROLLER_NAME, @@ -70,6 +79,7 @@ export class BridgeStatusController extends StaticIntervalPollingController { mockStatusRequest, mockClientId, mockFetch, + BRIDGE_PROD_API_BASE_URL, ); // Verify the fetch was called with correct parameters expect(mockFetch).toHaveBeenCalledWith( - expect.stringContaining(BRIDGE_STATUS_BASE_URL), + expect.stringContaining(getBridgeStatusUrl(BRIDGE_PROD_API_BASE_URL)), { headers: { 'X-Client-Id': mockClientId }, }, @@ -125,7 +130,12 @@ describe('utils', () => { .mockResolvedValue(invalidResponse); await expect( - fetchBridgeTxStatus(mockStatusRequest, mockClientId, mockFetch), + fetchBridgeTxStatus( + mockStatusRequest, + mockClientId, + mockFetch, + BRIDGE_PROD_API_BASE_URL, + ), // eslint-disable-next-line jest/require-to-throw-message ).rejects.toThrow(); }); @@ -136,7 +146,12 @@ describe('utils', () => { .mockRejectedValue(new Error('Network error')); await expect( - fetchBridgeTxStatus(mockStatusRequest, mockClientId, mockFetch), + fetchBridgeTxStatus( + mockStatusRequest, + mockClientId, + mockFetch, + BRIDGE_PROD_API_BASE_URL, + ), ).rejects.toThrow('Network error'); }); }); diff --git a/packages/bridge-status-controller/src/utils/bridge-status.ts b/packages/bridge-status-controller/src/utils/bridge-status.ts index 5c32f54e93d..510a8df55cc 100644 --- a/packages/bridge-status-controller/src/utils/bridge-status.ts +++ b/packages/bridge-status-controller/src/utils/bridge-status.ts @@ -1,5 +1,5 @@ import type { Quote } from '@metamask/bridge-controller'; -import { getBridgeApiBaseUrl } from '@metamask/bridge-controller'; +import { BRIDGE_PROD_API_BASE_URL } from '@metamask/bridge-controller'; import { validateBridgeStatusResponse } from './validators'; import type { @@ -13,7 +13,8 @@ export const getClientIdHeader = (clientId: string) => ({ 'X-Client-Id': clientId, }); -export const BRIDGE_STATUS_BASE_URL = `${getBridgeApiBaseUrl()}/getTxStatus`; +export const getBridgeStatusUrl = (bridgeApiBaseUrl: string) => + `${bridgeApiBaseUrl}/getTxStatus`; export const getStatusRequestDto = ( statusRequest: StatusRequestWithSrcTxHash, @@ -40,12 +41,13 @@ export const fetchBridgeTxStatus = async ( statusRequest: StatusRequestWithSrcTxHash, clientId: string, fetchFn: FetchFunction, + bridgeStatusApiBaseUrl: string, ): Promise => { const statusRequestDto = getStatusRequestDto(statusRequest); const params = new URLSearchParams(statusRequestDto); // Fetch - const url = `${BRIDGE_STATUS_BASE_URL}?${params.toString()}`; + const url = `${getBridgeStatusUrl(bridgeStatusApiBaseUrl)}?${params.toString()}`; const rawTxStatus: unknown = await fetchFn(url, { headers: getClientIdHeader(clientId), From fb726e9721f79a7f5788026cd43a95a86fec5dee Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 12 Mar 2025 18:36:36 -0400 Subject: [PATCH 3/4] chore: make var name more semantic --- packages/bridge-status-controller/src/utils/bridge-status.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/bridge-status-controller/src/utils/bridge-status.ts b/packages/bridge-status-controller/src/utils/bridge-status.ts index 510a8df55cc..21236f873ce 100644 --- a/packages/bridge-status-controller/src/utils/bridge-status.ts +++ b/packages/bridge-status-controller/src/utils/bridge-status.ts @@ -1,5 +1,4 @@ import type { Quote } from '@metamask/bridge-controller'; -import { BRIDGE_PROD_API_BASE_URL } from '@metamask/bridge-controller'; import { validateBridgeStatusResponse } from './validators'; import type { @@ -41,13 +40,13 @@ export const fetchBridgeTxStatus = async ( statusRequest: StatusRequestWithSrcTxHash, clientId: string, fetchFn: FetchFunction, - bridgeStatusApiBaseUrl: string, + bridgeApiBaseUrl: string, ): Promise => { const statusRequestDto = getStatusRequestDto(statusRequest); const params = new URLSearchParams(statusRequestDto); // Fetch - const url = `${getBridgeStatusUrl(bridgeStatusApiBaseUrl)}?${params.toString()}`; + const url = `${getBridgeStatusUrl(bridgeApiBaseUrl)}?${params.toString()}`; const rawTxStatus: unknown = await fetchFn(url, { headers: getClientIdHeader(clientId), From 269e002ad3a1ea110ef81d2951e0d323326b372c Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 12 Mar 2025 18:42:22 -0400 Subject: [PATCH 4/4] fix: lint error --- packages/bridge-controller/src/utils/bridge.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/bridge-controller/src/utils/bridge.ts b/packages/bridge-controller/src/utils/bridge.ts index 897fa11c317..124bd396e3d 100644 --- a/packages/bridge-controller/src/utils/bridge.ts +++ b/packages/bridge-controller/src/utils/bridge.ts @@ -4,8 +4,6 @@ import type { Hex } from '@metamask/utils'; import { DEFAULT_BRIDGE_CONTROLLER_STATE, - BRIDGE_DEV_API_BASE_URL, - BRIDGE_PROD_API_BASE_URL, ETH_USDT_ADDRESS, METABRIDGE_ETHEREUM_ADDRESS, } from '../constants/bridge';