Skip to content

Commit 7ebc32a

Browse files
authored
Improve portfolio fetch concurrency and error handling (#8572)
1 parent 5f0e25b commit 7ebc32a

File tree

1 file changed

+29
-29
lines changed

1 file changed

+29
-29
lines changed

apps/dashboard/src/@/hooks/useWalletPortfolio.ts

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,12 @@ export type WalletPortfolioData = {
1212
tokens: WalletPortfolioToken[];
1313
};
1414

15-
// Retry helper with exponential backoff
15+
// Retry helper with exponential backoff - returns null on failure instead of throwing
1616
async function fetchWithRetry(
1717
url: string,
1818
options: RequestInit,
1919
maxRetries = 3,
20-
): Promise<Response> {
21-
let lastError: Error | null = null;
22-
20+
): Promise<Response | null> {
2321
for (let attempt = 0; attempt < maxRetries; attempt++) {
2422
try {
2523
const response = await fetch(url, options);
@@ -32,15 +30,15 @@ async function fetchWithRetry(
3230
}
3331

3432
return response;
35-
} catch (e) {
36-
lastError = e instanceof Error ? e : new Error(String(e));
33+
} catch (_e) {
3734
// Network error - retry with backoff
3835
const delay = Math.min(1000 * 2 ** attempt, 10000);
3936
await new Promise((resolve) => setTimeout(resolve, delay));
4037
}
4138
}
4239

43-
throw lastError || new Error("Max retries exceeded");
40+
// All retries failed - return null instead of throwing
41+
return null;
4442
}
4543

4644
// Fetch tokens for a single address on a single chain
@@ -74,7 +72,8 @@ async function fetchTokensForChain(
7472
},
7573
});
7674

77-
if (!response.ok) {
75+
// If all retries failed or response not ok, return empty (balance = 0)
76+
if (!response || !response.ok) {
7877
return [];
7978
}
8079

@@ -158,15 +157,18 @@ async function fetchAllPortfolios(
158157
onProgress?: (completed: number, total: number) => void,
159158
): Promise<Map<string, WalletPortfolioData>> {
160159
const results = new Map<string, WalletPortfolioData>();
161-
const batchSize = 10; // Process 10 addresses at a time for better throughput
160+
const concurrency = 50; // Process up to 50 addresses concurrently
162161
let completed = 0;
162+
let index = 0;
163163

164-
for (let i = 0; i < addresses.length; i += batchSize) {
165-
const batch = addresses.slice(i, i + batchSize);
164+
// Worker function that processes one address at a time
165+
async function worker() {
166+
while (index < addresses.length) {
167+
const currentIndex = index++;
168+
const address = addresses[currentIndex];
169+
if (!address) continue;
166170

167-
// Process batch concurrently with individual error handling
168-
const batchResults = await Promise.allSettled(
169-
batch.map(async (address) => {
171+
try {
170172
const data = await fetchWalletPortfolio(
171173
address,
172174
chainIds,
@@ -175,26 +177,24 @@ async function fetchAllPortfolios(
175177
clientId,
176178
ecosystemSlug,
177179
);
178-
return { address, data };
179-
}),
180-
);
181-
182-
// Only add successful results
183-
for (const result of batchResults) {
184-
if (result.status === "fulfilled") {
185-
results.set(result.value.address, result.value.data);
180+
results.set(address, data);
181+
} catch (e) {
182+
// Silent fail - continue with others
183+
console.warn(`Failed to fetch portfolio for ${address}:`, e);
186184
}
187-
}
188-
189-
completed = Math.min(i + batchSize, addresses.length);
190-
onProgress?.(completed, addresses.length);
191185

192-
// Small delay between batches to avoid overwhelming the API
193-
if (i + batchSize < addresses.length) {
194-
await new Promise((resolve) => setTimeout(resolve, 100));
186+
completed++;
187+
onProgress?.(completed, addresses.length);
195188
}
196189
}
197190

191+
// Start concurrent workers
192+
const workers = Array.from(
193+
{ length: Math.min(concurrency, addresses.length) },
194+
() => worker(),
195+
);
196+
await Promise.all(workers);
197+
198198
return results;
199199
}
200200

0 commit comments

Comments
 (0)