Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/bridge bridge status controller env config #5465

Merged
merged 5 commits into from
Mar 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions packages/bridge-controller/src/bridge-controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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': {
Expand Down Expand Up @@ -117,7 +117,7 @@ describe('BridgeController', function () {
'534352': 2.4,
},
});
nock(getBridgeApiBaseUrl())
nock(BRIDGE_PROD_API_BASE_URL)
.get('/getTokens?chainId=10')
.reply(200, [
{
Expand Down Expand Up @@ -338,6 +338,7 @@ describe('BridgeController', function () {
expect.any(AbortSignal),
BridgeClientId.EXTENSION,
mockFetchFn,
BRIDGE_PROD_API_BASE_URL,
);
expect(bridgeController.state.quotesLastFetched).toBeNull();

Expand Down Expand Up @@ -488,6 +489,7 @@ describe('BridgeController', function () {
expect.any(AbortSignal),
BridgeClientId.EXTENSION,
mockFetchFn,
BRIDGE_PROD_API_BASE_URL,
);
expect(bridgeController.state.quotesLastFetched).toBeNull();

Expand Down Expand Up @@ -715,6 +717,7 @@ describe('BridgeController', function () {
expect.any(AbortSignal),
BridgeClientId.EXTENSION,
mockFetchFn,
BRIDGE_PROD_API_BASE_URL,
);
expect(bridgeController.state.quotesLastFetched).toBeNull();

Expand Down
12 changes: 12 additions & 0 deletions packages/bridge-controller/src/bridge-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -95,12 +96,17 @@ export class BridgeController extends StaticIntervalPollingController<BridgePoll

readonly #fetchFn: FetchFunction;

readonly #config: {
customBridgeApiBaseUrl?: string;
};

constructor({
messenger,
state,
clientId,
getLayer1GasFee,
fetchFn,
config,
}: {
messenger: BridgeControllerMessenger;
state?: Partial<BridgeControllerState>;
Expand All @@ -110,6 +116,9 @@ export class BridgeController extends StaticIntervalPollingController<BridgePoll
chainId: ChainId;
}) => Promise<string>;
fetchFn: FetchFunction;
config?: {
customBridgeApiBaseUrl?: string;
};
}) {
super({
name: BRIDGE_CONTROLLER_NAME,
Expand All @@ -127,6 +136,7 @@ export class BridgeController extends StaticIntervalPollingController<BridgePoll
this.#getLayer1GasFee = getLayer1GasFee;
this.#clientId = clientId;
this.#fetchFn = fetchFn;
this.#config = config ?? {};

// Register action handlers
this.messagingSystem.registerActionHandler(
Expand Down Expand Up @@ -242,6 +252,7 @@ export class BridgeController extends StaticIntervalPollingController<BridgePoll
const bridgeFeatureFlags = await fetchBridgeFeatureFlags(
this.#clientId,
this.#fetchFn,
this.#config.customBridgeApiBaseUrl ?? BRIDGE_PROD_API_BASE_URL,
);
this.update((state) => {
state.bridgeFeatureFlags = bridgeFeatureFlags;
Expand Down Expand Up @@ -278,6 +289,7 @@ export class BridgeController extends StaticIntervalPollingController<BridgePoll
this.#abortController!.signal as AbortSignal,
this.#clientId,
this.#fetchFn,
this.#config.customBridgeApiBaseUrl ?? BRIDGE_PROD_API_BASE_URL,
);

const quotesWithL1GasFees = await this.#appendL1GasFees(quotes);
Expand Down
8 changes: 3 additions & 5 deletions packages/bridge-controller/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export {
DEFAULT_MAX_REFRESH_COUNT,
DEFAULT_BRIDGE_CONTROLLER_STATE,
METABRIDGE_CHAIN_TO_ADDRESS_MAP,
BRIDGE_DEV_API_BASE_URL,
BRIDGE_PROD_API_BASE_URL,
} from './constants/bridge';

export type { AllowedBridgeChainIds } from './constants/bridge';
Expand All @@ -55,8 +57,4 @@ export type { SwapsTokenObject } from './constants/tokens';

export { SWAPS_API_V2_BASE_URL } from './constants/swaps';

export {
getEthUsdtResetData,
isEthUsdt,
getBridgeApiBaseUrl,
} from './utils/bridge';
export { getEthUsdtResetData, isEthUsdt } from './utils/bridge';
32 changes: 0 additions & 32 deletions packages/bridge-controller/src/utils/bridge.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable n/no-process-env */
import { Contract } from '@ethersproject/contracts';
import { abiERC20 } from '@metamask/metamask-eth-abis';
import type { Hex } from '@metamask/utils';
Expand All @@ -9,16 +8,11 @@ import {
isSwapsDefaultTokenAddress,
isSwapsDefaultTokenSymbol,
sumHexes,
getBridgeApiBaseUrl,
} from './bridge';
import {
ETH_USDT_ADDRESS,
METABRIDGE_ETHEREUM_ADDRESS,
} from '../constants/bridge';
import {
BRIDGE_DEV_API_BASE_URL,
BRIDGE_PROD_API_BASE_URL,
} from '../constants/bridge';
import { CHAIN_IDS } from '../constants/chains';
import { SWAPS_CHAINID_DEFAULT_TOKEN_MAP } from '../constants/tokens';

Expand Down Expand Up @@ -141,30 +135,4 @@ describe('Bridge utils', () => {
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);
});
});
});
13 changes: 0 additions & 13 deletions packages/bridge-controller/src/utils/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -17,17 +15,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
*
Expand Down
21 changes: 18 additions & 3 deletions packages/bridge-controller/src/utils/fetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -56,6 +56,7 @@ describe('fetch', () => {
const result = await fetchBridgeFeatureFlags(
BridgeClientId.EXTENSION,
mockFetchFn,
BRIDGE_PROD_API_BASE_URL,
);

expect(mockFetchFn).toHaveBeenCalledWith(
Expand Down Expand Up @@ -129,6 +130,7 @@ describe('fetch', () => {
const result = await fetchBridgeFeatureFlags(
BridgeClientId.EXTENSION,
mockFetchFn,
BRIDGE_PROD_API_BASE_URL,
);

expect(mockFetchFn).toHaveBeenCalledWith(
Expand Down Expand Up @@ -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);
});
});
Expand Down Expand Up @@ -196,6 +202,7 @@ describe('fetch', () => {
'0xa',
BridgeClientId.EXTENSION,
mockFetchFn,
BRIDGE_PROD_API_BASE_URL,
);

expect(mockFetchFn).toHaveBeenCalledWith(
Expand Down Expand Up @@ -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);
});
});
Expand All @@ -256,6 +268,7 @@ describe('fetch', () => {
signal,
BridgeClientId.EXTENSION,
mockFetchFn,
BRIDGE_PROD_API_BASE_URL,
);

expect(mockFetchFn).toHaveBeenCalledWith(
Expand Down Expand Up @@ -290,6 +303,7 @@ describe('fetch', () => {
signal,
BridgeClientId.EXTENSION,
mockFetchFn,
BRIDGE_PROD_API_BASE_URL,
);

expect(mockFetchFn).toHaveBeenCalledWith(
Expand Down Expand Up @@ -343,6 +357,7 @@ describe('fetch', () => {
signal,
BridgeClientId.EXTENSION,
mockFetchFn,
BRIDGE_PROD_API_BASE_URL,
);

expect(mockFetchFn).toHaveBeenCalledWith(
Expand Down
15 changes: 9 additions & 6 deletions packages/bridge-controller/src/utils/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { hexToNumber, numberToHex } from '@metamask/utils';
import {
isSwapsDefaultTokenAddress,
isSwapsDefaultTokenSymbol,
getBridgeApiBaseUrl,
} from './bridge';
import {
validateFeatureFlagsResponse,
Expand Down Expand Up @@ -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<BridgeFeatureFlags> {
const url = `${getBridgeApiBaseUrl()}/getAllFeatureFlags`;
const url = `${bridgeApiBaseUrl}/getAllFeatureFlags`;
const rawFeatureFlags: unknown = await fetchFn(url, {
headers: getClientIdHeader(clientId),
});
Expand Down Expand Up @@ -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<Record<string, SwapsTokenObject>> {
// 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,
Expand Down Expand Up @@ -132,13 +133,15 @@ 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(
request: QuoteRequest,
signal: AbortSignal,
clientId: string,
fetchFn: FetchFunction,
bridgeApiBaseUrl: string,
): Promise<QuoteResponse[]> {
const queryParams = new URLSearchParams({
walletAddress: request.walletAddress,
Expand All @@ -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,
Expand Down
Loading
Loading