Skip to content

Commit 948f19f

Browse files
authored
Merge pull request #145 from MeshJS/feature/get-TVL
Feature/get-TVL
2 parents 7c71c52 + 03fac37 commit 948f19f

File tree

3 files changed

+194
-23
lines changed

3 files changed

+194
-23
lines changed

.github/workflows/daily-balance-snapshots.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ name: Daily Balance Snapshots
44
# API requests require SNAPSHOT_AUTH_TOKEN secret to be set in GitHub repository settings.
55

66
on:
7-
#schedule:
8-
# Run at midnight UTC every day
9-
#- cron: '0 0 * * *'
7+
schedule:
8+
# Run at midnight UTC every day
9+
- cron: '0 0 * * *'
1010
# Allow manual triggering for testing
1111
workflow_dispatch:
1212

@@ -41,7 +41,7 @@ jobs:
4141
API_BASE_URL: "https://multisig.meshjs.dev"
4242
SNAPSHOT_AUTH_TOKEN: ${{ secrets.SNAPSHOT_AUTH_TOKEN }}
4343
BATCH_SIZE: 10
44-
DELAY_BETWEEN_BATCHES: 5
44+
DELAY_BETWEEN_BATCHES: 10
4545
MAX_RETRIES: 3
4646

4747
- name: Notify on failure

scripts/batch-snapshot-orchestrator.ts

Lines changed: 107 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* - API_BASE_URL: Base URL for the API (default: http://localhost:3000)
1616
* - SNAPSHOT_AUTH_TOKEN: Authentication token for API requests
1717
* - BATCH_SIZE: Number of wallets per batch (default: 10)
18-
* - DELAY_BETWEEN_BATCHES: Delay between batches in seconds (default: 5)
18+
* - DELAY_BETWEEN_BATCHES: Delay between batches in seconds (default: 10)
1919
* - MAX_RETRIES: Maximum retries for failed batches (default: 3)
2020
*/
2121

@@ -24,8 +24,18 @@ interface BatchProgress {
2424
walletsInBatch: number;
2525
failedInBatch: number;
2626
snapshotsStored: number;
27-
totalAdaBalance: number;
2827
totalBatches: number;
28+
// Network-specific data
29+
mainnetWallets: number;
30+
testnetWallets: number;
31+
mainnetAdaBalance: number;
32+
testnetAdaBalance: number;
33+
// Failure details
34+
failures: Array<{
35+
walletId: string;
36+
errorType: string;
37+
errorMessage: string;
38+
}>;
2939
}
3040

3141
interface BatchResponse {
@@ -40,9 +50,21 @@ interface BatchResults {
4050
failedBatches: number;
4151
totalWalletsProcessed: number;
4252
totalWalletsFailed: number;
43-
totalAdaBalance: number;
4453
totalSnapshotsStored: number;
4554
executionTime: number;
55+
// Network-specific data
56+
totalMainnetWallets: number;
57+
totalTestnetWallets: number;
58+
totalMainnetAdaBalance: number;
59+
totalTestnetAdaBalance: number;
60+
// Failure tracking
61+
allFailures: Array<{
62+
walletId: string;
63+
errorType: string;
64+
errorMessage: string;
65+
batchNumber: number;
66+
}>;
67+
failureSummary: Record<string, number>;
4668
}
4769

4870
interface BatchConfig {
@@ -70,9 +92,16 @@ class BatchSnapshotOrchestrator {
7092
failedBatches: 0,
7193
totalWalletsProcessed: 0,
7294
totalWalletsFailed: 0,
73-
totalAdaBalance: 0,
7495
totalSnapshotsStored: 0,
7596
executionTime: 0,
97+
// Network-specific data
98+
totalMainnetWallets: 0,
99+
totalTestnetWallets: 0,
100+
totalMainnetAdaBalance: 0,
101+
totalTestnetAdaBalance: 0,
102+
// Failure tracking
103+
allFailures: [],
104+
failureSummary: {},
76105
};
77106
}
78107

@@ -88,7 +117,7 @@ class BatchSnapshotOrchestrator {
88117
apiBaseUrl,
89118
authToken,
90119
batchSize: parseInt(process.env.BATCH_SIZE || '10'),
91-
delayBetweenBatches: parseInt(process.env.DELAY_BETWEEN_BATCHES || '5'),
120+
delayBetweenBatches: parseInt(process.env.DELAY_BETWEEN_BATCHES || '10'),
92121
maxRetries: parseInt(process.env.MAX_RETRIES || '3'),
93122
};
94123
}
@@ -129,6 +158,17 @@ class BatchSnapshotOrchestrator {
129158
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
130159
}
131160

161+
private getFriendlyErrorName(errorType: string): string {
162+
const errorMap: Record<string, string> = {
163+
'wallet_build_failed': 'Wallet Build Failed',
164+
'utxo_fetch_failed': 'UTxO Fetch Failed',
165+
'address_generation_failed': 'Address Generation Failed',
166+
'balance_calculation_failed': 'Balance Calculation Failed',
167+
'processing_failed': 'General Processing Failed',
168+
};
169+
return errorMap[errorType] || errorType;
170+
}
171+
132172
private async processBatch(batchNumber: number, batchId: string): Promise<BatchProgress | null> {
133173
console.log(`📦 Processing batch ${batchNumber}...`);
134174

@@ -148,7 +188,16 @@ class BatchSnapshotOrchestrator {
148188
console.log(` • Processed: ${data.progress.processedInBatch}/${data.progress.walletsInBatch} wallets`);
149189
console.log(` • Failed: ${data.progress.failedInBatch}`);
150190
console.log(` • Snapshots stored: ${data.progress.snapshotsStored}`);
151-
console.log(` • Batch ADA balance: ${Math.round(data.progress.totalAdaBalance * 100) / 100} ADA`);
191+
console.log(` • Mainnet: ${data.progress.mainnetWallets} wallets, ${Math.round(data.progress.mainnetAdaBalance * 100) / 100} ADA`);
192+
console.log(` • Testnet: ${data.progress.testnetWallets} wallets, ${Math.round(data.progress.testnetAdaBalance * 100) / 100} ADA`);
193+
194+
// Show failures for this batch
195+
if (data.progress.failures.length > 0) {
196+
console.log(` ❌ Failures in this batch:`);
197+
data.progress.failures.forEach((failure, index) => {
198+
console.log(` ${index + 1}. ${failure.walletId}... - ${failure.errorMessage}`);
199+
});
200+
}
152201

153202
return data.progress;
154203
} else {
@@ -193,8 +242,22 @@ class BatchSnapshotOrchestrator {
193242
this.results.completedBatches = 1;
194243
this.results.totalWalletsProcessed += firstBatch.processedInBatch;
195244
this.results.totalWalletsFailed += firstBatch.failedInBatch;
196-
this.results.totalAdaBalance += firstBatch.totalAdaBalance;
197245
this.results.totalSnapshotsStored += firstBatch.snapshotsStored;
246+
247+
// Accumulate network-specific data
248+
this.results.totalMainnetWallets += firstBatch.mainnetWallets;
249+
this.results.totalTestnetWallets += firstBatch.testnetWallets;
250+
this.results.totalMainnetAdaBalance += firstBatch.mainnetAdaBalance;
251+
this.results.totalTestnetAdaBalance += firstBatch.testnetAdaBalance;
252+
253+
// Accumulate failures
254+
firstBatch.failures.forEach(failure => {
255+
this.results.allFailures.push({
256+
...failure,
257+
batchNumber: 1
258+
});
259+
this.results.failureSummary[failure.errorType] = (this.results.failureSummary[failure.errorType] || 0) + 1;
260+
});
198261

199262
console.log(`📊 Total batches to process: ${this.results.totalBatches}`);
200263

@@ -210,8 +273,22 @@ class BatchSnapshotOrchestrator {
210273
this.results.completedBatches++;
211274
this.results.totalWalletsProcessed += batchProgress.processedInBatch;
212275
this.results.totalWalletsFailed += batchProgress.failedInBatch;
213-
this.results.totalAdaBalance += batchProgress.totalAdaBalance;
214276
this.results.totalSnapshotsStored += batchProgress.snapshotsStored;
277+
278+
// Accumulate network-specific data
279+
this.results.totalMainnetWallets += batchProgress.mainnetWallets;
280+
this.results.totalTestnetWallets += batchProgress.testnetWallets;
281+
this.results.totalMainnetAdaBalance += batchProgress.mainnetAdaBalance;
282+
this.results.totalTestnetAdaBalance += batchProgress.testnetAdaBalance;
283+
284+
// Accumulate failures
285+
batchProgress.failures.forEach(failure => {
286+
this.results.allFailures.push({
287+
...failure,
288+
batchNumber
289+
});
290+
this.results.failureSummary[failure.errorType] = (this.results.failureSummary[failure.errorType] || 0) + 1;
291+
});
215292
} else {
216293
this.results.failedBatches++;
217294
console.error(`❌ Batch ${batchNumber} failed completely`);
@@ -234,11 +311,31 @@ class BatchSnapshotOrchestrator {
234311
console.log(` • Wallets processed: ${this.results.totalWalletsProcessed}`);
235312
console.log(` • Wallets failed: ${this.results.totalWalletsFailed}`);
236313
console.log(` • Snapshots stored: ${this.results.totalSnapshotsStored}`);
237-
console.log(` • Total TVL: ${Math.round(this.results.totalAdaBalance * 100) / 100} ADA`);
238314
console.log(` • Execution time: ${this.results.executionTime}s`);
315+
316+
// Network-specific breakdown
317+
console.log(`\n🌐 Network Breakdown:`);
318+
console.log(` 📈 Mainnet:`);
319+
console.log(` • Wallets: ${this.results.totalMainnetWallets}`);
320+
console.log(` • TVL: ${Math.round(this.results.totalMainnetAdaBalance * 100) / 100} ADA`);
321+
console.log(` 🧪 Testnet:`);
322+
console.log(` • Wallets: ${this.results.totalTestnetWallets}`);
323+
console.log(` • TVL: ${Math.round(this.results.totalTestnetAdaBalance * 100) / 100} ADA`);
324+
325+
// Failure analysis
326+
if (this.results.totalWalletsFailed > 0) {
327+
console.log(`\n❌ Failure Summary:`);
328+
console.log(` • Total failed wallets: ${this.results.totalWalletsFailed}`);
329+
330+
// Show failure summary by type
331+
Object.entries(this.results.failureSummary).forEach(([errorType, count]) => {
332+
const friendlyName = this.getFriendlyErrorName(errorType);
333+
console.log(` • ${friendlyName}: ${count} wallets`);
334+
});
335+
}
239336

240337
if (this.results.failedBatches > 0) {
241-
console.log(`⚠️ Warning: ${this.results.failedBatches} batches failed. You may need to retry those batches manually.`);
338+
console.log(`\n⚠️ Warning: ${this.results.failedBatches} batches failed. You may need to retry those batches manually.`);
242339
}
243340

244341
return this.results;

src/pages/api/v1/stats/run-snapshots-batch.ts

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ interface WalletBalance {
1616
balance: Record<string, string>;
1717
adaBalance: number;
1818
isArchived: boolean;
19+
network: number; // 0 = testnet, 1 = mainnet
20+
}
21+
22+
interface WalletFailure {
23+
walletId: string;
24+
errorType: string; // e.g., "wallet_build_failed", "utxo_fetch_failed", "balance_calculation_failed"
25+
errorMessage: string; // sanitized error message
1926
}
2027

2128
interface BatchProgress {
@@ -27,11 +34,17 @@ interface BatchProgress {
2734
failedInBatch: number;
2835
totalProcessed: number;
2936
totalFailed: number;
30-
totalAdaBalance: number;
3137
snapshotsStored: number;
3238
isComplete: boolean;
3339
startedAt: string;
3440
lastUpdatedAt: string;
41+
// Network-specific data
42+
mainnetWallets: number;
43+
testnetWallets: number;
44+
mainnetAdaBalance: number;
45+
testnetAdaBalance: number;
46+
// Failure details
47+
failures: WalletFailure[];
3548
}
3649

3750
interface BatchResponse {
@@ -113,21 +126,31 @@ export default async function handler(
113126
failedInBatch: 0,
114127
totalProcessed: 0,
115128
totalFailed: 0,
116-
totalAdaBalance: 0,
117129
snapshotsStored: 0,
118130
isComplete: true,
119131
startedAt: startTime,
120132
lastUpdatedAt: new Date().toISOString(),
133+
// Network-specific data
134+
mainnetWallets: 0,
135+
testnetWallets: 0,
136+
mainnetAdaBalance: 0,
137+
testnetAdaBalance: 0,
138+
// Failure details
139+
failures: [],
121140
},
122141
timestamp: new Date().toISOString(),
123142
});
124143
}
125144

126145
// Step 3: Process wallets in this batch
127146
const walletBalances: WalletBalance[] = [];
147+
const failures: WalletFailure[] = [];
128148
let processedInBatch = 0;
129149
let failedInBatch = 0;
130-
let totalAdaBalance = 0;
150+
let mainnetWallets = 0;
151+
let testnetWallets = 0;
152+
let mainnetAdaBalance = 0;
153+
let testnetAdaBalance = 0;
131154

132155
for (const wallet of wallets) {
133156
try {
@@ -163,6 +186,11 @@ export default async function handler(
163186
const mWallet = buildMultisigWallet(walletData, network);
164187
if (!mWallet) {
165188
console.error(`Failed to build multisig wallet for ${wallet.id.slice(0, 8)}...`);
189+
failures.push({
190+
walletId: wallet.id.slice(0, 8),
191+
errorType: "wallet_build_failed",
192+
errorMessage: "Unable to build multisig wallet from provided data"
193+
});
166194
failedInBatch++;
167195
continue;
168196
}
@@ -239,16 +267,49 @@ export default async function handler(
239267
balance,
240268
adaBalance: roundedAdaBalance,
241269
isArchived: wallet.isArchived,
270+
network,
242271
};
243272

244273
walletBalances.push(walletBalance);
245-
totalAdaBalance += roundedAdaBalance;
274+
275+
// Track network-specific data
276+
if (network === 1) {
277+
mainnetWallets++;
278+
mainnetAdaBalance += roundedAdaBalance;
279+
} else {
280+
testnetWallets++;
281+
testnetAdaBalance += roundedAdaBalance;
282+
}
283+
246284
processedInBatch++;
247285

248-
console.log(` ✅ Balance: ${roundedAdaBalance} ADA`);
286+
console.log(` ✅ Balance: ${roundedAdaBalance} ADA (${network === 1 ? 'mainnet' : 'testnet'})`);
249287

250288
} catch (error) {
251-
console.error(`Error processing wallet ${wallet.id.slice(0, 8)}...:`, error);
289+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
290+
console.error(`Error processing wallet ${wallet.id.slice(0, 8)}...:`, errorMessage);
291+
292+
// Determine error type based on error message
293+
let errorType = "processing_failed";
294+
let sanitizedMessage = "Wallet processing failed";
295+
296+
if (errorMessage.includes("fetchAddressUTxOs") || errorMessage.includes("UTxO")) {
297+
errorType = "utxo_fetch_failed";
298+
sanitizedMessage = "Failed to fetch UTxOs from blockchain";
299+
} else if (errorMessage.includes("serializeNativeScript") || errorMessage.includes("address")) {
300+
errorType = "address_generation_failed";
301+
sanitizedMessage = "Failed to generate wallet address";
302+
} else if (errorMessage.includes("balance") || errorMessage.includes("lovelace")) {
303+
errorType = "balance_calculation_failed";
304+
sanitizedMessage = "Failed to calculate wallet balance";
305+
}
306+
307+
failures.push({
308+
walletId: wallet.id.slice(0, 8),
309+
errorType,
310+
errorMessage: sanitizedMessage
311+
});
312+
252313
failedInBatch++;
253314
}
254315
}
@@ -292,7 +353,8 @@ export default async function handler(
292353
console.log(` • Processed: ${processedInBatch}/${wallets.length}`);
293354
console.log(` • Failed: ${failedInBatch}`);
294355
console.log(` • Snapshots stored: ${snapshotsStored}`);
295-
console.log(` • Batch ADA balance: ${Math.round(totalAdaBalance * 100) / 100} ADA`);
356+
console.log(` • Mainnet: ${mainnetWallets} wallets, ${Math.round(mainnetAdaBalance * 100) / 100} ADA`);
357+
console.log(` • Testnet: ${testnetWallets} wallets, ${Math.round(testnetAdaBalance * 100) / 100} ADA`);
296358
console.log(` • Overall progress: ${totalProcessed}/${totalWallets} wallets`);
297359

298360
const progress: BatchProgress = {
@@ -304,11 +366,17 @@ export default async function handler(
304366
failedInBatch,
305367
totalProcessed,
306368
totalFailed,
307-
totalAdaBalance,
308369
snapshotsStored,
309370
isComplete,
310371
startedAt: startTime,
311372
lastUpdatedAt: new Date().toISOString(),
373+
// Network-specific data
374+
mainnetWallets,
375+
testnetWallets,
376+
mainnetAdaBalance,
377+
testnetAdaBalance,
378+
// Failure details
379+
failures,
312380
};
313381

314382
const response: BatchResponse = {
@@ -338,11 +406,17 @@ export default async function handler(
338406
failedInBatch: 0,
339407
totalProcessed: 0,
340408
totalFailed: 0,
341-
totalAdaBalance: 0,
342409
snapshotsStored: 0,
343410
isComplete: false,
344411
startedAt: startTime,
345412
lastUpdatedAt: new Date().toISOString(),
413+
// Network-specific data
414+
mainnetWallets: 0,
415+
testnetWallets: 0,
416+
mainnetAdaBalance: 0,
417+
testnetAdaBalance: 0,
418+
// Failure details
419+
failures: [],
346420
},
347421
timestamp: new Date().toISOString(),
348422
});

0 commit comments

Comments
 (0)