Skip to content

Commit 569facc

Browse files
authored
chore: add auth header to bridge getToken calls (#26191)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** This PR adds an authorization header to the bridge-api network calls <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: chore: add auth header to bridge getToken calls ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/SWAPS-3987 ## **Manual testing steps** ```gherkin Feature: bridge-api authorization header Scenario: user opens the swap page Given the user has sufficient balance for a bridge transaction When user opens the asset picker Then the getTokens/popular API call has an `Authorization bearer ...` header When user searches for a token in the asset picker Then the getTokens/search API call has an `Authorization bearer ...` header When user requests a quote Then the getQuoteStream API call has an `Authorization bearer ...` header When user submits a bridge transaction Then the getTxStatus API call has an `Authorization bearer ...` header ``` ## **Screenshots/Recordings** N/A - no user-facing changes <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Medium risk because it changes Bridge API request headers and upgrades/paches `@metamask/bridge(-status)-controller`, including updates to bridge submission/metrics payloads that could affect analytics attribution and bridge history/event tracking. > > **Overview** > Adds `Authorization: Bearer <token>` headers to Bridge API token discovery calls (`/getTokens/popular`, `/getTokens/search`, and the `fetchBridgeTokens` fallback in `useTopTokens`) by retrieving a bearer token from `Engine.context.AuthenticationController`. > > Upgrades patched `@metamask/bridge-controller`/`@metamask/bridge-status-controller` and extends unified swap/bridge metrics to optionally carry `ab_tests` (threaded through submit/polling/history and appended to tracked events). Tests and component-view mocks are updated to provide `AuthenticationController.getBearerToken`, and bridge controller messengers now delegate `AuthenticationController:getBearerToken`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit c4f3072. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 3d1f94d commit 569facc

15 files changed

Lines changed: 319 additions & 90 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
diff --git a/dist/utils/metrics/types.d.cts b/dist/utils/metrics/types.d.cts
2+
index b8f94476d4447cb5895a0f39e7beb1eb5e3b4e16..90cbc588e9ebbc84a761bbeaf3770572aad4d6f8 100644
3+
--- a/dist/utils/metrics/types.d.cts
4+
+++ b/dist/utils/metrics/types.d.cts
5+
@@ -153,6 +153,7 @@ type RequiredEventContextFromClientBase = {
6+
export type RequiredEventContextFromClient = {
7+
[K in keyof RequiredEventContextFromClientBase]: RequiredEventContextFromClientBase[K] & {
8+
location?: MetaMetricsSwapsEventSource;
9+
+ ab_tests?: Record<string, string>;
10+
};
11+
};
12+
/**
13+
@@ -198,6 +199,7 @@ export type EventPropertiesFromControllerState = {
14+
export type CrossChainSwapsEventProperties<T extends UnifiedSwapBridgeEventName> = {
15+
action_type: MetricsActionType;
16+
location: MetaMetricsSwapsEventSource;
17+
+ ab_tests?: Record<string, string>;
18+
} | Pick<EventPropertiesFromControllerState, T>[T] | Pick<RequiredEventContextFromClient, T>[T];
19+
export {};
20+
//# sourceMappingURL=types.d.cts.map
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
diff --git a/dist/bridge-status-controller.cjs b/dist/bridge-status-controller.cjs
2+
index c89f7e4c600ea6e710a532fc6887ed525b80f333..c4c20566a27b77dfa8b88ff56a038ad5344e0efb 100644
3+
--- a/dist/bridge-status-controller.cjs
4+
+++ b/dist/bridge-status-controller.cjs
5+
@@ -207,7 +207,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
6+
});
7+
});
8+
_BridgeStatusController_addTxToHistory.set(this, (startPollingForBridgeTxStatusArgs, actionId) => {
9+
- const { bridgeTxMeta, statusRequest, quoteResponse, startTime, slippagePercentage, initialDestAssetBalance, targetContractAddress, approvalTxId, isStxEnabled, location, accountAddress: selectedAddress, } = startPollingForBridgeTxStatusArgs;
10+
+ const { bridgeTxMeta, statusRequest, quoteResponse, startTime, slippagePercentage, initialDestAssetBalance, targetContractAddress, approvalTxId, isStxEnabled, location, abTests, accountAddress: selectedAddress, } = startPollingForBridgeTxStatusArgs;
11+
// Determine the key for this history item:
12+
// - For pre-submission (non-batch EVM): use actionId
13+
// - For post-submission or other cases: use bridgeTxMeta.id
14+
@@ -248,6 +248,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
15+
isStxEnabled: isStxEnabled ?? false,
16+
featureId: quoteResponse.featureId,
17+
location,
18+
+ ...(abTests && { abTests }),
19+
};
20+
this.update((state) => {
21+
// Use actionId as key for pre-submission, or txMeta.id for post-submission
22+
@@ -716,8 +717,8 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
23+
* @param location - The entry point from which the user initiated the swap or bridge (e.g. Main View, Token View, Trending Explore)
24+
* @returns The transaction meta
25+
*/
26+
- this.submitTx = async (accountAddress, quoteResponse, isStxEnabledOnClient, quotesReceivedContext, location = bridge_controller_1.MetaMetricsSwapsEventSource.MainView) => {
27+
- this.messenger.call('BridgeController:stopPollingForQuotes', bridge_controller_1.AbortReason.TransactionSubmitted,
28+
+ this.submitTx = async (accountAddress, quoteResponse, isStxEnabledOnClient, quotesReceivedContext, location = bridge_controller_1.MetaMetricsSwapsEventSource.MainView, abTests) => {
29+
+ this.messenger.call('BridgeController:stopPollingForQuotes', bridge_controller_1.AbortReason.TransactionSubmitted,
30+
// If trade is submitted before all quotes are loaded, the QuotesReceived event is published
31+
// If the trade has a featureId, it means it was submitted outside of the Unified Swap and Bridge experience, so no QuotesReceived event is published
32+
quoteResponse.featureId ? undefined : quotesReceivedContext);
33+
@@ -726,7 +727,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
34+
throw new Error('Failed to submit cross-chain swap transaction: undefined multichain account');
35+
}
36+
const isHardwareAccount = (0, bridge_controller_1.isHardwareWallet)(selectedAccount);
37+
- const preConfirmationProperties = (0, metrics_1.getPreConfirmationPropertiesFromQuote)(quoteResponse, isStxEnabledOnClient, isHardwareAccount, location);
38+
+ const preConfirmationProperties = (0, metrics_1.getPreConfirmationPropertiesFromQuote)(quoteResponse, isStxEnabledOnClient, isHardwareAccount, location, abTests);
39+
// Emit Submitted event after submit button is clicked
40+
!quoteResponse.featureId &&
41+
__classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.Submitted, undefined, preConfirmationProperties);
42+
@@ -848,6 +849,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
43+
startTime,
44+
approvalTxId,
45+
location,
46+
+ abTests,
47+
}, actionId);
48+
// Pass txFee when gasIncluded is true to use the quote's gas fees
49+
// instead of re-estimating (which would fail for max native token swaps)
50+
@@ -888,6 +890,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
51+
startTime,
52+
approvalTxId,
53+
location,
54+
+ abTests,
55+
});
56+
}
57+
if ((0, bridge_controller_1.isNonEvmChainId)(quoteResponse.quote.srcChainId)) {
58+
@@ -916,12 +919,12 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
59+
* @returns A lightweight TransactionMeta-like object for history linking
60+
*/
61+
this.submitIntent = async (params) => {
62+
- const { quoteResponse, signature, accountAddress, location } = params;
63+
+ const { quoteResponse, signature, accountAddress, location, abTests } = params;
64+
this.messenger.call('BridgeController:stopPollingForQuotes', bridge_controller_1.AbortReason.TransactionSubmitted);
65+
// Build pre-confirmation properties for error tracking parity with submitTx
66+
const account = __classPrivateFieldGet(this, _BridgeStatusController_instances, "m", _BridgeStatusController_getMultichainSelectedAccount).call(this, accountAddress);
67+
const isHardwareAccount = Boolean(account) && (0, bridge_controller_1.isHardwareWallet)(account);
68+
- const preConfirmationProperties = (0, metrics_1.getPreConfirmationPropertiesFromQuote)(quoteResponse, false, isHardwareAccount, location);
69+
+ const preConfirmationProperties = (0, metrics_1.getPreConfirmationPropertiesFromQuote)(quoteResponse, false, isHardwareAccount, location, abTests);
70+
try {
71+
const intent = (0, transaction_1.getIntentFromQuote)(quoteResponse);
72+
// If backend provided an approval tx for this intent quote, submit it first (on-chain),
73+
@@ -932,7 +935,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
74+
// Handle approval silently for better UX in intent flows
75+
const approvalTxMeta = await __classPrivateFieldGet(this, _BridgeStatusController_handleApprovalTx, "f").call(this, isBridgeTx, quoteResponse.quote.srcChainId, quoteResponse.approval && (0, bridge_controller_1.isEvmTxData)(quoteResponse.approval)
76+
? quoteResponse.approval
77+
- : undefined, quoteResponse.resetApproval,
78+
+ : undefined, quoteResponse.resetApproval,
79+
/* requireApproval */ false);
80+
approvalTxId = approvalTxMeta?.id;
81+
if (approvalTxId) {
82+
@@ -1017,6 +1020,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
83+
approvalTxId,
84+
startTime,
85+
location,
86+
+ abTests,
87+
});
88+
// Start polling using the orderId key to route to intent manager
89+
__classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, bridgeHistoryKey);
90+
@@ -1043,12 +1047,21 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
91+
* @param eventProperties - The properties for the event
92+
*/
93+
_BridgeStatusController_trackUnifiedSwapBridgeEvent.set(this, (eventName, txMetaId, eventProperties) => {
94+
+ const historyAbTests = txMetaId
95+
+ ? this.state.txHistory?.[txMetaId]?.abTests
96+
+ : undefined;
97+
+ const resolvedAbTests = eventProperties?.ab_tests ?? historyAbTests ?? undefined;
98+
+
99+
const baseProperties = {
100+
action_type: bridge_controller_1.MetricsActionType.SWAPBRIDGE_V1,
101+
location: eventProperties?.location ??
102+
(txMetaId ? this.state.txHistory?.[txMetaId]?.location : undefined) ??
103+
bridge_controller_1.MetaMetricsSwapsEventSource.MainView,
104+
...(eventProperties ?? {}),
105+
+ ...(resolvedAbTests &&
106+
+ Object.keys(resolvedAbTests).length > 0 && {
107+
+ ab_tests: resolvedAbTests,
108+
+ }),
109+
};
110+
// This will publish events for PERPS dropped tx failures as well
111+
if (!txMetaId) {
112+
diff --git a/dist/bridge-status-controller.d.cts b/dist/bridge-status-controller.d.cts
113+
index a71266a9c15070d9fd9242148b16ad0e454184e9..f5dc880b383ecef194a44539e6c570fbc0fa7b7a 100644
114+
--- a/dist/bridge-status-controller.d.cts
115+
+++ b/dist/bridge-status-controller.d.cts
116+
@@ -88,7 +88,7 @@ export declare class BridgeStatusController extends BridgeStatusController_base<
117+
* @param location - The entry point from which the user initiated the swap or bridge (e.g. Main View, Token View, Trending Explore)
118+
* @returns The transaction meta
119+
*/
120+
- submitTx: (accountAddress: string, quoteResponse: QuoteResponse<Trade, Trade> & QuoteMetadata, isStxEnabledOnClient: boolean, quotesReceivedContext?: RequiredEventContextFromClient[UnifiedSwapBridgeEventName.QuotesReceived], location?: MetaMetricsSwapsEventSource) => Promise<TransactionMeta & Partial<SolanaTransactionMeta>>;
121+
+ submitTx: (accountAddress: string, quoteResponse: QuoteResponse<Trade, Trade> & QuoteMetadata, isStxEnabledOnClient: boolean, quotesReceivedContext?: RequiredEventContextFromClient[UnifiedSwapBridgeEventName.QuotesReceived], location?: MetaMetricsSwapsEventSource, abTests?: Record<string, string>) => Promise<TransactionMeta & Partial<SolanaTransactionMeta>>;
122+
/**
123+
* UI-signed intent submission (fast path): the UI generates the EIP-712 signature and calls this with the raw signature.
124+
* Here we submit the order to the intent provider and create a synthetic history entry for UX.
125+
@@ -105,6 +105,7 @@ export declare class BridgeStatusController extends BridgeStatusController_base<
126+
signature: string;
127+
accountAddress: string;
128+
location?: MetaMetricsSwapsEventSource;
129+
+ abTests?: Record<string, string>;
130+
}) => Promise<Pick<TransactionMeta, 'id' | 'chainId' | 'type' | 'status'>>;
131+
}
132+
export {};
133+
diff --git a/dist/types.d.cts b/dist/types.d.cts
134+
index d0492cd8b8159bc63121174e6395f53c49aba59b..c5402866e8f13e37e535d1196d697756d2330023 100644
135+
--- a/dist/types.d.cts
136+
+++ b/dist/types.d.cts
137+
@@ -96,6 +96,11 @@ export type BridgeHistoryItem = {
138+
* Used to attribute swaps to specific flows (e.g. Trending Explore).
139+
*/
140+
location?: MetaMetricsSwapsEventSource;
141+
+ /**
142+
+ * A/B test context to attribute swap/bridge events to specific experiments.
143+
+ * Keys are test names, values are variant names (e.g. { token_details_layout: 'treatment' }).
144+
+ */
145+
+ abTests?: Record<string, string>;
146+
/**
147+
* Attempts tracking for exponential backoff on failed fetches.
148+
* We track the number of attempts and the last attempt time for each txMetaId that has failed at least once
149+
@@ -161,6 +166,7 @@ export type StartPollingForBridgeTxStatusArgs = {
150+
approvalTxId?: BridgeHistoryItem['approvalTxId'];
151+
isStxEnabled?: BridgeHistoryItem['isStxEnabled'];
152+
location?: BridgeHistoryItem['location'];
153+
+ abTests?: BridgeHistoryItem['abTests'];
154+
accountAddress: string;
155+
};
156+
/**
157+
diff --git a/dist/utils/metrics.cjs b/dist/utils/metrics.cjs
158+
index 775367bc08c8a46c19a78913903573a295d1f677..ef00bf331d866e60a23204475f18a017614fdd57 100644
159+
--- a/dist/utils/metrics.cjs
160+
+++ b/dist/utils/metrics.cjs
161+
@@ -109,7 +109,7 @@ exports.getPriceImpactFromQuote = getPriceImpactFromQuote;
162+
* @param location - The entry point from which the user initiated the swap or bridge (e.g. Main View, Token View, Trending Explore)
163+
* @returns The properties for the pre-confirmation event
164+
*/
165+
-const getPreConfirmationPropertiesFromQuote = (quoteResponse, isStxEnabledOnClient, isHardwareAccount, location = bridge_controller_1.MetaMetricsSwapsEventSource.MainView) => {
166+
+const getPreConfirmationPropertiesFromQuote = (quoteResponse, isStxEnabledOnClient, isHardwareAccount, location = bridge_controller_1.MetaMetricsSwapsEventSource.MainView, abTests) => {
167+
const { quote } = quoteResponse;
168+
return {
169+
...(0, exports.getPriceImpactFromQuote)(quote),
170+
@@ -125,6 +125,7 @@ const getPreConfirmationPropertiesFromQuote = (quoteResponse, isStxEnabledOnClie
171+
action_type: bridge_controller_1.MetricsActionType.SWAPBRIDGE_V1,
172+
custom_slippage: false, // TODO detect whether the user changed the default slippage
173+
location,
174+
+ ...(abTests && Object.keys(abTests).length > 0 && { ab_tests: abTests }),
175+
};
176+
};
177+
exports.getPreConfirmationPropertiesFromQuote = getPreConfirmationPropertiesFromQuote;

app/components/UI/Bridge/Views/BridgeView/BridgeView.view.test.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,10 @@ describeForPlatforms('BridgeView', () => {
331331

332332
// Regression for #25256: two USDT tokens on Linea must both appear in search results.
333333
it('shows two USDT when search API returns two USDT on Linea (#25256)', async () => {
334+
jest
335+
.spyOn(Engine.context.AuthenticationController, 'getBearerToken')
336+
.mockResolvedValue('mock-bearer-token');
337+
334338
const LINEA_CHAIN_ID = 59144;
335339
const verifiedUsdtAddress = '0xA219439258ca9da29E9Cc4cE5596924745e12B93';
336340
const otherUsdtAddress = '0x0000000000000000000000000000000000000001';

app/components/UI/Bridge/hooks/usePopularTokens.test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ import {
88

99
global.fetch = jest.fn();
1010

11+
jest.mock('../../../../core/Engine', () => ({
12+
context: {
13+
AuthenticationController: {
14+
getBearerToken: jest.fn().mockResolvedValue('mock-bearer-token'),
15+
},
16+
},
17+
}));
18+
1119
const mockPopularTokens = [
1220
createMockPopularToken({ symbol: 'TEST', name: 'Test Token' }),
1321
createMockPopularToken({
@@ -50,7 +58,11 @@ describe('usePopularTokens', () => {
5058
expect.stringContaining('/getTokens/popular'),
5159
expect.objectContaining({
5260
method: 'POST',
53-
headers: { 'Content-Type': 'application/json' },
61+
headers: {
62+
'Content-Type': 'application/json',
63+
// Initial fetch may not have a bearer token
64+
Authorization: 'Bearer ',
65+
},
5466
body: JSON.stringify({
5567
chainIds: [MOCK_CHAIN_IDS.ethereum],
5668
includeAssets: [],

app/components/UI/Bridge/hooks/usePopularTokens.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
22
import { CaipChainId, CaipAssetType } from '@metamask/utils';
33
import { BRIDGE_API_BASE_URL } from '../../../../constants/bridge';
44
import { TokenRwaData } from '@metamask/assets-controllers';
5+
import Engine from '../../../../core/Engine';
56

67
export interface PopularToken {
78
assetId: CaipAssetType;
@@ -109,6 +110,20 @@ export const usePopularTokens = ({
109110
}: UsePopularTokensParams): UsePopularTokensResult => {
110111
const [popularTokens, setPopularTokens] = useState<PopularToken[]>([]);
111112
const [isLoading, setIsLoading] = useState(true);
113+
const [bearerToken, setBearerToken] = useState<string | null>(null);
114+
115+
useEffect(() => {
116+
Engine.context.AuthenticationController.getBearerToken()
117+
.then((token) => {
118+
setBearerToken(token);
119+
})
120+
.catch((error) => {
121+
console.warn(
122+
'Failed to get bearer token for /getTokens/popular',
123+
error,
124+
);
125+
});
126+
}, []);
112127

113128
useEffect(() => {
114129
const abortController = new AbortController();
@@ -141,6 +156,7 @@ export const usePopularTokens = ({
141156
method: 'POST',
142157
headers: {
143158
'Content-Type': 'application/json',
159+
Authorization: `Bearer ${bearerToken ?? ''}`,
144160
},
145161
body: JSON.stringify({
146162
chainIds,
@@ -183,7 +199,7 @@ export const usePopularTokens = ({
183199
isCancelled = true;
184200
abortController.abort();
185201
};
186-
}, [chainIds, includeAssets]);
202+
}, [chainIds, includeAssets, bearerToken]);
187203

188204
return { popularTokens, isLoading };
189205
};

app/components/UI/Bridge/hooks/useSearchTokens.test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ import {
99

1010
global.fetch = jest.fn();
1111

12+
jest.mock('../../../../core/Engine', () => ({
13+
context: {
14+
AuthenticationController: {
15+
getBearerToken: jest.fn().mockResolvedValue('mock-bearer-token'),
16+
},
17+
},
18+
}));
19+
1220
describe('useSearchTokens', () => {
1321
beforeEach(() => {
1422
jest.clearAllMocks();
@@ -55,7 +63,11 @@ describe('useSearchTokens', () => {
5563
expect.stringContaining('/getTokens/search'),
5664
expect.objectContaining({
5765
method: 'POST',
58-
headers: { 'Content-Type': 'application/json' },
66+
headers: {
67+
'Content-Type': 'application/json',
68+
// Initial fetch may not have a bearer token
69+
Authorization: 'Bearer ',
70+
},
5971
body: expect.stringContaining('test query'),
6072
}),
6173
);

app/components/UI/Bridge/hooks/useSearchTokens.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { debounce } from 'lodash';
33
import { CaipChainId } from '@metamask/utils';
44
import { PopularToken, IncludeAsset } from './usePopularTokens';
55
import { BRIDGE_API_BASE_URL } from '../../../../constants/bridge';
6+
import Engine from '../../../../core/Engine';
67

78
const MIN_SEARCH_LENGTH = 3;
89

@@ -45,6 +46,7 @@ export const useSearchTokens = ({
4546
const [isSearchLoading, setIsSearchLoading] = useState(false);
4647
const [searchCursor, setSearchCursor] = useState<string | undefined>();
4748
const [isLoadingMore, setIsLoadingMore] = useState(false);
49+
const [bearerToken, setBearerToken] = useState<string | null>(null);
4850
// Consumers need to distinguish "waiting for debounce" from "search returned 0 results"
4951
const [currentSearchQuery, setCurrentSearchQuery] = useState<string>('');
5052
const currentSearchQueryRef = useRef<string>('');
@@ -62,6 +64,16 @@ export const useSearchTokens = ({
6264
includeAssetsRef.current = includeAssets;
6365
}, [includeAssets]);
6466

67+
useEffect(() => {
68+
Engine.context.AuthenticationController.getBearerToken()
69+
.then((token) => {
70+
setBearerToken(token);
71+
})
72+
.catch((error) => {
73+
console.warn('Failed to get bearer token for /getTokens/search', error);
74+
});
75+
}, []);
76+
6577
const resetSearch = useCallback(() => {
6678
setSearchResults([]);
6779
setSearchCursor(undefined);
@@ -118,6 +130,7 @@ export const useSearchTokens = ({
118130
method: 'POST',
119131
headers: {
120132
'Content-Type': 'application/json',
133+
Authorization: `Bearer ${bearerToken ?? ''}`,
121134
},
122135
body: JSON.stringify(requestBody),
123136
},
@@ -156,7 +169,7 @@ export const useSearchTokens = ({
156169
}
157170
}
158171
},
159-
[resetSearch],
172+
[resetSearch, bearerToken],
160173
);
161174

162175
// Create debounced search function

app/components/UI/Bridge/hooks/useTopTokens/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ export const useTopTokens = ({
189189
const rawBridgeAssets = await memoizedFetchBridgeTokens(
190190
chainId,
191191
BridgeClientId.MOBILE,
192+
await Engine.context.AuthenticationController.getBearerToken(),
192193
handleFetch,
193194
BRIDGE_API_BASE_URL,
194195
clientVersion,

0 commit comments

Comments
 (0)