Skip to content

Commit 9e7a79e

Browse files
leofelix077piyalbasuaristidesstaffieriCassioMG
committed
Add error handling for Search Asset results (#2389)
* Feature/move history fetch to bg (#2273) * upgrade to yarn 2 and use resolutions to block vulnerable package versions (#2239) * upgrade to yarn 2 and use resolutions to block vulnerable package versions * rm deprecated .yarnrc * rm yarnpath * try committing yarn binary to repo * try corepack enable for gha * update run tests cmd * rm yarnpath * rm npm i yarn * update all pipelines * rm superfluous history types * ensure invoke host function tx shows contract parameters (#2243) * ensure invoke host function tx shows contract parameters * add test for fallback if contract spec retrieval fails * Bugfix/rm auth param names (#2244) * ensure invoke host function tx shows contract parameters * add test for fallback if contract spec retrieval fails * do not show contract parameters for authorizations * add tests for create contract v1 and invoke contract * add issuer for changeTrust op (#2246) * add issuer for changeTrust op * programmatically disable overflow:hidden when copying a value * Revert "add issuer for changeTrust op (#2246)" (#2247) This reverts commit 19c8a68. * Bugfix/add issuer for changetrust (#2249) * ensure invoke host function tx shows contract parameters * add test for fallback if contract spec retrieval fails * do not show contract parameters for authorizations * add tests for create contract v1 and invoke contract * add issuer for changeTrust op * programmatically disable overflow:hidden when copying a value * cache account balances and poll for updates * fix CI tests * rm `force:true` which was causing action to happen too fast * do a fresh balance fetch on account/network change * first pass at async history * pr comments * allow for history caching * add more sentry tracking for Account and Wallets views (#2268) * add more sentry tracking for Account and Wallets views * adding more sentry reporting * gracefully degrade on errors from Blockaid (#2269) * gracefully degrade on errors from Blockaid * should not be necessary to skip dapp scanning on custom network * rm extra dep * add a test for persisting configurations in the send flow (#2271) * add a test for persisting configurations in the send flow * rm logs and update muxed acct; lower xlm payment * rm slow loading simulation * handle missing scan-tx result; add disabled state for Confirm Anyway (#2272) * handle missing scan-tx result; add disabled state for Confirm Anyway * assertions to show correct confirm button on Blockaid error * add cache for balances to ensure we do a fresh lookup when needed (#2275) * add cache for balances to ensure we do a fresh lookup when needed * add try...catch to token-prices polling * rm log * only dispatch saveBalancesForAccount when fresh data has been fetched * adjust test to wait for UI change * replace yarn setup with just yarn * rm unnecessary return * clear token details on redux clear action * make history row construction async and check for redux state for updates * add tests for assetdetails * increase timeout for flakey test * pr comments * refresh account history every time account balances refresh * check for updated appdata before showing password modal (#2300) * check for updated appdata before showing password modal * update error msg * rm unused redirect logic * stringify errors rather than using `cause` (#2302) * Feature/move icons to own hook (#2308) * move get icons out of critical path; rely on background's cache * add tests and comments * add comment * add comment * only dispatch if we have cached icons * PR comments * skip blockaid scan on first fetch of account-balances (#2310) * skip blockaid scan on first fetch of account-balances * rm stub change * rm more stubs * rm log * add comments and update boolean naming * Dropdown menu option to copy wallet address (#2316) * add button to copy address from dropdown * Added translations * revert translation file changes * revert translation file changes * Added translations * revert changes to translation files * move copy address button to first dropdown position * scroll on long strings; pretty print json (#2320) * scroll on long strings; pretty print json * rm log * add correct snapshot for json message * rm log * finish comment * add error case for JSON * don't use carat for lib * update yarn.lock * move scrollbar to btm of container; reduce json font size * update snapshot * re-searching so should abort any in flight API requests (#2323) * re-searching so should abort any in flight API requests * add comment * fix test name * make test more reliable * add check for correct search results * fix jest locator * [FEATURE] new send/swap navigation flow (#2353) * adds SelectionTile and AddressTile, updates nav flows to match updates. Adds query parameter for default values in send flow * Added translations * adds address tile and uses it in swap flow, tweaks selection tile styles * adds unit tests for new tile components * Added translations * updates swap navigation flow to match updates, updates tests flows to match * updates back icon for send and swap steps, fixes bad test references * tweaks locator in address tile tests * adds store state to asset tile tests, removes asset icon mock * updates SelectionTile prop name, adds isSuspicious prop for AssetTile * adds placeholder value in TokenList for missing token USD value * uses real IdenticonImg in address tile unit tests * adds query param validation for send and swap flow * Update extension/src/popup/views/SendPayment/index.tsx Co-authored-by: Cássio Marcos Goulart <3228151+CassioMG@users.noreply.github.com> * adds missing import * adds class for tile icon --------- Co-authored-by: Cássio Marcos Goulart <3228151+CassioMG@users.noreply.github.com> * [FEATURE] adds send and swap buttons to asset detail view (#2351) * adds send and swap buttons to asset detail view * uses secondary button styles * removes run snapshots job (#2355) * release/5.35.4 (#2354) * upgrade to ledger-hq/hw-transport-webhid (#2350) * upgrade to ledger-hq/hw-transport-webhid * add tests * add ledger support for new trustline flow (#2352) * upgrade to ledger-hq/hw-transport-webhid * add ledger support for new trustline flow * only re-fetch balances if we were successful * test for fetching balances on success * add reset spys * adjust spacing at top of hw wallet modal * Now that `Done` button properly shows, click it in tests (#2356) * skip flakey test * skip flakey test * renames local vars to follow convention * adds tests for LP share and tweaks LP title * adds links with query params for asset detail CTAs --------- Co-authored-by: Piyal Basu <pbasu235@gmail.com> * only fetch asset list data if needed (#2369) * only fetch asset list data if needed * correctly show icon loading state * [BUG] SAC token management improvements (#2374) * adds SAC detection when changing trust in the add and remove token flows * updates arg signature for isAssetSac * Feature/cache token prices (#2373) * cache token prices and batch loading wallets * use similar methodology for token price and account balance caching * fix loading state trigger * fix tests * use helper for cache clearing * set isFetchingTokenPrices to false in catch handler * rollback error change * load backend settings async on Account view (#2381) * load backend settings async on Account view * rm console.logs * Feature/use ledger key for home domains (#2363) * use ledger-key/accounts endpoint for home domains * fix tests * create generic ledger key account helper; add tests * rm unneeded data-test prop * rm unused import * [CHORE] git process updates (#2361) * moves the add translations hook to the pre commit stage, removes standalone translations commit * adds script to update app version, removes version update from submit production action, adds update version step to test run action for release branches * removes version input, now uses package version * fetch asset domains in one calls * fix tests * cache home domains while iterating over account history rows * fitler non-G keys * rm .only * fix test param * PR comments --------- Co-authored-by: aristides <aristides.staffieri@stellar.org> * update version numbers for release * Eadd error handling for soroswap tokenlist.json fail state * extract schema into constant * simplify fulfilled checks * remove timeout and fix errors returned * rm unnecessary calls to make flows even faster (#2391) * makes send swap buttons stay in the container in full screen mode (#2392) * makes send swap buttons stay in the container in full screen mode * add a pause to make sure flakey e2e test has time to save changes * use cached token list lookup on add asset screen * use cached assets lookup * simplify caching logic for lookup assets * simplify caching logic for lookup assets * update getTokenFromTokenList * update token list fetch parallel * update token list fetch parallel * remove manual definition of verified tokens * revert to parallel fetch on getAssets * revert to parallel fetch on getAssets * revert to parallel fetch on getAssets * update tests for error handling * update tests for error handling * remove deprecated package * add tests for cached list and comments on functions * fix tests --------- Co-authored-by: Piyal Basu <pbasu235@gmail.com> Co-authored-by: aristides <aristides.staffieri@stellar.org> Co-authored-by: Cássio Marcos Goulart <3228151+CassioMG@users.noreply.github.com>
1 parent 22463e6 commit 9e7a79e

17 files changed

Lines changed: 583 additions & 91 deletions

File tree

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
import { getCombinedAssetListData } from "../token-list";
2+
import {
3+
MAINNET_NETWORK_DETAILS,
4+
TESTNET_NETWORK_DETAILS,
5+
NETWORKS,
6+
} from "@shared/constants/stellar";
7+
import {
8+
DEFAULT_ASSETS_LISTS,
9+
AssetsListItem,
10+
} from "@shared/constants/soroban/asset-list";
11+
12+
// Mock Sentry captureException
13+
jest.mock("@sentry/browser", () => ({
14+
captureException: jest.fn(),
15+
}));
16+
17+
import { captureException } from "@sentry/browser";
18+
19+
describe("getCombinedAssetListData", () => {
20+
beforeEach(() => {
21+
jest.clearAllMocks();
22+
// Mock fetch globally
23+
global.fetch = jest.fn() as jest.Mock;
24+
});
25+
26+
it("should return cached asset lists when provided and not empty", async () => {
27+
const cachedAssetLists = [
28+
{
29+
name: "Cached Asset List",
30+
provider: "Cached Provider",
31+
description: "Cached Description",
32+
version: "1.0.0",
33+
network: "mainnet",
34+
assets: [
35+
{
36+
code: "CACHED",
37+
issuer: "GCACHED123",
38+
contract: "CCACHED456",
39+
domain: "cached.com",
40+
icon: "https://cached.com/icon.png",
41+
decimals: 7,
42+
},
43+
],
44+
},
45+
];
46+
47+
const result = await getCombinedAssetListData({
48+
networkDetails: MAINNET_NETWORK_DETAILS,
49+
assetsLists: DEFAULT_ASSETS_LISTS,
50+
cachedAssetLists,
51+
});
52+
53+
// Should return cached lists without fetching
54+
expect(result).toEqual(cachedAssetLists);
55+
expect(result.length).toBe(1);
56+
expect(global.fetch).not.toHaveBeenCalled();
57+
});
58+
59+
it("should fetch asset lists when cachedAssetLists is empty", async () => {
60+
const mockSuccessResponse = {
61+
name: "Test Asset List",
62+
provider: "Test Provider",
63+
description: "Test Description",
64+
version: "1.0.0",
65+
network: "mainnet",
66+
assets: [],
67+
};
68+
69+
(global.fetch as jest.Mock).mockResolvedValue({
70+
ok: true,
71+
status: 200,
72+
json: async () => mockSuccessResponse,
73+
});
74+
75+
const result = await getCombinedAssetListData({
76+
networkDetails: MAINNET_NETWORK_DETAILS,
77+
assetsLists: DEFAULT_ASSETS_LISTS,
78+
cachedAssetLists: [],
79+
});
80+
81+
// Should fetch lists when cache is empty
82+
expect(global.fetch).toHaveBeenCalled();
83+
expect(result.length).toBeGreaterThan(0);
84+
});
85+
86+
it("should fetch asset lists when cachedAssetLists is undefined", async () => {
87+
const mockSuccessResponse = {
88+
name: "Test Asset List",
89+
provider: "Test Provider",
90+
description: "Test Description",
91+
version: "1.0.0",
92+
network: "mainnet",
93+
assets: [],
94+
};
95+
96+
(global.fetch as jest.Mock).mockResolvedValue({
97+
ok: true,
98+
status: 200,
99+
json: async () => mockSuccessResponse,
100+
});
101+
102+
const result = await getCombinedAssetListData({
103+
networkDetails: MAINNET_NETWORK_DETAILS,
104+
assetsLists: DEFAULT_ASSETS_LISTS,
105+
cachedAssetLists: undefined,
106+
});
107+
108+
// Should fetch lists when cache is undefined
109+
expect(global.fetch).toHaveBeenCalled();
110+
expect(result.length).toBeGreaterThan(0);
111+
});
112+
113+
it("should handle 429 rate limit error gracefully and continue with other lists", async () => {
114+
const mockSuccessResponse = {
115+
name: "Test Asset List",
116+
provider: "Test Provider",
117+
description: "Test Description",
118+
version: "1.0.0",
119+
network: "mainnet",
120+
assets: [
121+
{
122+
code: "TEST",
123+
issuer: "GABCDEF123",
124+
contract: "C123456",
125+
domain: "test.com",
126+
icon: "https://test.com/icon.png",
127+
decimals: 7,
128+
},
129+
],
130+
};
131+
132+
// Mock fetch to return 429 for Soroswap URL and success for others
133+
(global.fetch as jest.Mock).mockImplementation((url: string) => {
134+
if (url.includes("soroswap/token-list")) {
135+
return Promise.resolve({
136+
ok: false,
137+
status: 429,
138+
statusText: "Too Many Requests",
139+
});
140+
}
141+
// Return success for other URLs
142+
return Promise.resolve({
143+
ok: true,
144+
status: 200,
145+
json: async () => mockSuccessResponse,
146+
});
147+
});
148+
149+
const result = await getCombinedAssetListData({
150+
networkDetails: MAINNET_NETWORK_DETAILS,
151+
assetsLists: DEFAULT_ASSETS_LISTS,
152+
});
153+
154+
// Should return successful lists (excluding the 429 one)
155+
expect(result.length).toBeGreaterThan(0);
156+
expect(result).not.toContain(null);
157+
expect(result).not.toContain(undefined);
158+
159+
// Verify that captureException was called for the 429 error
160+
expect(captureException).toHaveBeenCalledWith(
161+
expect.stringContaining("Failed to load asset list"),
162+
);
163+
expect(captureException).toHaveBeenCalledWith(
164+
expect.stringContaining("(429)"),
165+
);
166+
});
167+
168+
it("should handle network errors gracefully", async () => {
169+
const mockSuccessResponse = {
170+
name: "Test Asset List",
171+
provider: "Test Provider",
172+
description: "Test Description",
173+
version: "1.0.0",
174+
network: "mainnet",
175+
assets: [],
176+
};
177+
178+
// Mock fetch to throw network error for Soroswap URL
179+
(global.fetch as jest.Mock).mockImplementation((url: string) => {
180+
if (url.includes("soroswap/token-list")) {
181+
return Promise.reject(new Error("Network error"));
182+
}
183+
return Promise.resolve({
184+
ok: true,
185+
status: 200,
186+
json: async () => mockSuccessResponse,
187+
});
188+
});
189+
190+
const result = await getCombinedAssetListData({
191+
networkDetails: MAINNET_NETWORK_DETAILS,
192+
assetsLists: DEFAULT_ASSETS_LISTS,
193+
});
194+
195+
// Should return successful lists (excluding the failed one)
196+
expect(result.length).toBeGreaterThan(0);
197+
expect(result).not.toContain(null);
198+
199+
// Verify that captureException was called for the network error
200+
expect(captureException).toHaveBeenCalledWith(
201+
expect.stringContaining("Failed to load asset list"),
202+
);
203+
});
204+
205+
it("should handle 500 server errors gracefully", async () => {
206+
const mockSuccessResponse = {
207+
name: "Test Asset List",
208+
provider: "Test Provider",
209+
description: "Test Description",
210+
version: "1.0.0",
211+
network: "mainnet",
212+
assets: [],
213+
};
214+
215+
// Mock fetch to return 500 for Soroswap URL
216+
(global.fetch as jest.Mock).mockImplementation((url: string) => {
217+
if (url.includes("soroswap/token-list")) {
218+
return Promise.resolve({
219+
ok: false,
220+
status: 500,
221+
statusText: "Internal Server Error",
222+
});
223+
}
224+
return Promise.resolve({
225+
ok: true,
226+
status: 200,
227+
json: async () => mockSuccessResponse,
228+
});
229+
});
230+
231+
const result = await getCombinedAssetListData({
232+
networkDetails: MAINNET_NETWORK_DETAILS,
233+
assetsLists: DEFAULT_ASSETS_LISTS,
234+
});
235+
236+
// Should return successful lists (excluding the 500 one)
237+
expect(result.length).toBeGreaterThan(0);
238+
expect(result).not.toContain(null);
239+
240+
// Verify that captureException was called
241+
expect(captureException).toHaveBeenCalledWith(
242+
expect.stringContaining("Failed to load asset list"),
243+
);
244+
expect(captureException).toHaveBeenCalledWith(
245+
expect.stringContaining("(500)"),
246+
);
247+
});
248+
249+
it("should handle JSON parsing errors gracefully", async () => {
250+
const mockSuccessResponse = {
251+
name: "Test Asset List",
252+
provider: "Test Provider",
253+
description: "Test Description",
254+
version: "1.0.0",
255+
network: "mainnet",
256+
assets: [],
257+
};
258+
259+
// Mock fetch to return invalid JSON for Soroswap URL
260+
(global.fetch as jest.Mock).mockImplementation((url: string) => {
261+
if (url.includes("soroswap/token-list")) {
262+
return Promise.resolve({
263+
ok: true,
264+
status: 200,
265+
json: async () => {
266+
throw new Error("Invalid JSON");
267+
},
268+
});
269+
}
270+
return Promise.resolve({
271+
ok: true,
272+
status: 200,
273+
json: async () => mockSuccessResponse,
274+
});
275+
});
276+
277+
const result = await getCombinedAssetListData({
278+
networkDetails: MAINNET_NETWORK_DETAILS,
279+
assetsLists: DEFAULT_ASSETS_LISTS,
280+
});
281+
282+
// Should return successful lists (excluding the invalid JSON one)
283+
expect(result.length).toBeGreaterThan(0);
284+
expect(result).not.toContain(null);
285+
286+
// Verify that captureException was called for JSON parsing error
287+
expect(captureException).toHaveBeenCalledWith(
288+
expect.stringContaining("Failed to parse asset list JSON"),
289+
);
290+
});
291+
292+
it("should return all successful lists when all requests succeed", async () => {
293+
const mockSuccessResponse = {
294+
name: "Test Asset List",
295+
provider: "Test Provider",
296+
description: "Test Description",
297+
version: "1.0.0",
298+
network: "mainnet",
299+
assets: [],
300+
};
301+
302+
// Mock all fetches to succeed
303+
(global.fetch as jest.Mock).mockResolvedValue({
304+
ok: true,
305+
status: 200,
306+
json: async () => mockSuccessResponse,
307+
});
308+
309+
const result = await getCombinedAssetListData({
310+
networkDetails: MAINNET_NETWORK_DETAILS,
311+
assetsLists: DEFAULT_ASSETS_LISTS,
312+
});
313+
314+
// Should return all enabled lists
315+
const enabledLists = DEFAULT_ASSETS_LISTS[NETWORKS.PUBLIC].filter(
316+
(list: AssetsListItem) => list.isEnabled,
317+
);
318+
expect(result.length).toBe(enabledLists.length);
319+
expect(captureException).not.toHaveBeenCalled();
320+
});
321+
322+
it("should handle testnet network correctly", async () => {
323+
const mockSuccessResponse = {
324+
name: "Test Asset List",
325+
provider: "Test Provider",
326+
description: "Test Description",
327+
version: "1.0.0",
328+
network: "testnet",
329+
assets: [],
330+
};
331+
332+
(global.fetch as jest.Mock).mockResolvedValue({
333+
ok: true,
334+
status: 200,
335+
json: async () => mockSuccessResponse,
336+
});
337+
338+
const result = await getCombinedAssetListData({
339+
networkDetails: TESTNET_NETWORK_DETAILS,
340+
assetsLists: DEFAULT_ASSETS_LISTS,
341+
});
342+
343+
expect(result.length).toBeGreaterThan(0);
344+
});
345+
});

@shared/api/helpers/getIconFromTokenList.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {
22
AssetListReponseItem,
33
AssetListResponse,
44
} from "@shared/constants/soroban/asset-list";
5-
import { NetworkDetails } from "@shared/constants/stellar";
65
import { getCanonicalFromAsset } from "@shared/helpers/stellar";
76

87
import { sendMessageToBackground } from "./extensionMessaging";
@@ -14,7 +13,6 @@ export const getIconFromTokenLists = async ({
1413
code,
1514
assetsListsData,
1615
}: {
17-
networkDetails: NetworkDetails;
1816
issuerId?: string;
1917
contractId?: string;
2018
code: string;

0 commit comments

Comments
 (0)