@miradorlabs/parallax-web 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PARALLAX_SERVICE_USAGE.md +306 -0
- package/README.md +300 -134
- package/dist/index.d.ts +120 -1
- package/dist/index.esm.js +421 -25
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +422 -24
- package/dist/index.umd.js.map +1 -1
- package/index.ts +3 -0
- package/package.json +2 -2
- package/src/parallax/ParallaxService.ts +414 -0
package/index.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export * from './src/parallax';
|
|
2
2
|
export { GrpcWebRpc } from './src/grpc';
|
|
3
3
|
|
|
4
|
+
// Export ParallaxService
|
|
5
|
+
export { ParallaxService, parallaxService } from './src/parallax/ParallaxService';
|
|
6
|
+
|
|
4
7
|
// Re-export proto types for convenience
|
|
5
8
|
export {
|
|
6
9
|
CreateTraceRequest,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miradorlabs/parallax-web",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Parallax Web Client Side SDK ",
|
|
5
5
|
"main": "dist/index.umd.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"google-protobuf": "^3.21.4",
|
|
48
48
|
"grpc-web": "^2.0.2",
|
|
49
|
-
"mirador-gateway-parallax-web": "https://storage.googleapis.com/mirador-shd-packages/gateway/parallax/grpc-web/mirador-gateway-parallax-grpc-web-1.0.
|
|
49
|
+
"mirador-gateway-parallax-web": "https://storage.googleapis.com/mirador-shd-packages/gateway/parallax/grpc-web/mirador-gateway-parallax-grpc-web-1.0.10.tgz",
|
|
50
50
|
"rxjs": "^7.8.2"
|
|
51
51
|
},
|
|
52
52
|
"author": "@mirador",
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parallax Service - Transaction Tracing for Web Applications
|
|
3
|
+
* Creates individual traces for each transaction and tracks them through their lifecycle
|
|
4
|
+
*
|
|
5
|
+
* This service provides a simplified interface for tracking transactions with automatic
|
|
6
|
+
* client metadata collection and lifecycle management.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ParallaxClient } from './index';
|
|
10
|
+
import type {
|
|
11
|
+
CreateTraceResponse,
|
|
12
|
+
StartSpanResponse,
|
|
13
|
+
FinishSpanResponse,
|
|
14
|
+
AddSpanEventResponse,
|
|
15
|
+
AddSpanErrorResponse,
|
|
16
|
+
AddSpanHintResponse,
|
|
17
|
+
} from 'mirador-gateway-parallax-web/proto/gateway/parallax/v1/parallax_gateway_pb';
|
|
18
|
+
|
|
19
|
+
interface TransactionInfo {
|
|
20
|
+
traceId: string;
|
|
21
|
+
rootSpanId: string;
|
|
22
|
+
spanId: string | null;
|
|
23
|
+
timestamp: string;
|
|
24
|
+
txHash: string | null;
|
|
25
|
+
from?: string;
|
|
26
|
+
to?: string;
|
|
27
|
+
network?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface TransactionData {
|
|
31
|
+
from: string;
|
|
32
|
+
to: string;
|
|
33
|
+
value: string;
|
|
34
|
+
network?: string;
|
|
35
|
+
walletAddress?: string;
|
|
36
|
+
additionalData?: Record<string, any>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface FinishOptions {
|
|
40
|
+
success: boolean;
|
|
41
|
+
error?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export class ParallaxService {
|
|
45
|
+
private client: ParallaxClient | null = null;
|
|
46
|
+
private activeTransactions: Map<string, TransactionInfo> = new Map();
|
|
47
|
+
|
|
48
|
+
constructor() {
|
|
49
|
+
// Client will be initialized lazily
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Initialize the Parallax client (lazy initialization)
|
|
54
|
+
* @param apiKey - Optional API key for authentication
|
|
55
|
+
* @param gatewayUrl - Optional custom gateway URL
|
|
56
|
+
*/
|
|
57
|
+
private _ensureClient(apiKey?: string, gatewayUrl?: string): void {
|
|
58
|
+
if (this.client) return;
|
|
59
|
+
|
|
60
|
+
// Determine gateway URL based on environment if not provided
|
|
61
|
+
let url = gatewayUrl;
|
|
62
|
+
if (!url && typeof window !== 'undefined') {
|
|
63
|
+
const isDevelopment =
|
|
64
|
+
window.location.hostname === 'localhost' ||
|
|
65
|
+
window.location.hostname === '127.0.0.1';
|
|
66
|
+
|
|
67
|
+
url = isDevelopment
|
|
68
|
+
? `${window.location.protocol}//${window.location.host}/parallax-gateway`
|
|
69
|
+
: 'https://gateway-parallax-dev.mirador.org';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.client = new ParallaxClient(apiKey || '', url);
|
|
73
|
+
console.log('[ParallaxService] Client initialized with URL:', url);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Start a new transaction trace
|
|
78
|
+
* Called when user initiates a transaction
|
|
79
|
+
*
|
|
80
|
+
* Note: Since createTrace now automatically creates a root span, we don't need
|
|
81
|
+
* to call startSpan separately. The rootSpanId from the response is the parent span.
|
|
82
|
+
*
|
|
83
|
+
* @param txData - Transaction data
|
|
84
|
+
* @param name - Name for the trace (e.g., 'SendingTrace', 'SwappingTrace')
|
|
85
|
+
* @param options - Optional configuration (apiKey, gatewayUrl, includeClientMeta)
|
|
86
|
+
* @returns Promise with traceId, rootSpanId, and txId
|
|
87
|
+
*/
|
|
88
|
+
async startTransactionTrace(
|
|
89
|
+
txData: TransactionData,
|
|
90
|
+
name: string = 'WalletTransaction',
|
|
91
|
+
options?: { apiKey?: string; gatewayUrl?: string; includeClientMeta?: boolean }
|
|
92
|
+
): Promise<{ traceId: string; rootSpanId: string; txId: string }> {
|
|
93
|
+
this._ensureClient(options?.apiKey, options?.gatewayUrl);
|
|
94
|
+
|
|
95
|
+
if (!this.client) {
|
|
96
|
+
throw new Error('Failed to initialize Parallax client');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const txId = `tx_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
101
|
+
const timestamp = new Date().toISOString();
|
|
102
|
+
|
|
103
|
+
// Create trace attributes
|
|
104
|
+
const traceAttributes: Record<string, string> = {
|
|
105
|
+
transactionId: txId,
|
|
106
|
+
walletAddress: txData.walletAddress || txData.from,
|
|
107
|
+
network: txData.network || 'Unknown',
|
|
108
|
+
transactionStart: timestamp,
|
|
109
|
+
from: txData.from,
|
|
110
|
+
to: txData.to,
|
|
111
|
+
value: txData.value,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Add any additional transaction data
|
|
115
|
+
if (txData.additionalData) {
|
|
116
|
+
Object.entries(txData.additionalData).forEach(([key, value]) => {
|
|
117
|
+
traceAttributes[key] = typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Create a trace for this transaction
|
|
122
|
+
const createTraceReq = await this.client.createTraceRequest({
|
|
123
|
+
name: name,
|
|
124
|
+
attr: traceAttributes,
|
|
125
|
+
tags: ['transaction', 'wallet', txData.network || 'unknown'],
|
|
126
|
+
includeClientMeta: options?.includeClientMeta ?? true,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const traceResponse: CreateTraceResponse = await this.client.createTrace(createTraceReq);
|
|
130
|
+
const traceId = traceResponse.getTraceId();
|
|
131
|
+
const rootSpanId = traceResponse.getRootSpanId();
|
|
132
|
+
|
|
133
|
+
// Store transaction info
|
|
134
|
+
this.activeTransactions.set(txId, {
|
|
135
|
+
traceId,
|
|
136
|
+
rootSpanId,
|
|
137
|
+
spanId: rootSpanId, // Use rootSpanId as the main span for this transaction
|
|
138
|
+
timestamp,
|
|
139
|
+
txHash: null,
|
|
140
|
+
from: txData.from,
|
|
141
|
+
to: txData.to,
|
|
142
|
+
network: txData.network,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
console.log('[ParallaxService] Transaction trace started:', {
|
|
146
|
+
txId,
|
|
147
|
+
traceId,
|
|
148
|
+
rootSpanId,
|
|
149
|
+
from: txData.from,
|
|
150
|
+
to: txData.to,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Add initial event to the root span
|
|
154
|
+
await this.addTransactionEvent(txId, 'transaction_initiated', {
|
|
155
|
+
from: txData.from,
|
|
156
|
+
to: txData.to,
|
|
157
|
+
value: txData.value,
|
|
158
|
+
timestamp,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return { traceId, rootSpanId, txId };
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error('[ParallaxService] Failed to start transaction trace:', error);
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Associate a transaction hash with an existing trace
|
|
170
|
+
* Called when the transaction hash is available after signing/sending
|
|
171
|
+
*
|
|
172
|
+
* @param txId - Transaction identifier returned from startTransactionTrace
|
|
173
|
+
* @param txHash - Blockchain transaction hash
|
|
174
|
+
* @param chainId - Chain ID
|
|
175
|
+
*/
|
|
176
|
+
async associateTransactionHash(txId: string, txHash: string, chainId: number): Promise<void> {
|
|
177
|
+
const txInfo = this.activeTransactions.get(txId);
|
|
178
|
+
if (!txInfo) {
|
|
179
|
+
console.warn(`[ParallaxService] Transaction ${txId} not found in active transactions`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!this.client) {
|
|
184
|
+
console.warn('[ParallaxService] Client not initialized');
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
// Update stored tx info
|
|
190
|
+
txInfo.txHash = txHash;
|
|
191
|
+
this.activeTransactions.set(txId, txInfo);
|
|
192
|
+
|
|
193
|
+
// Add chain hint to correlate with blockchain events
|
|
194
|
+
const hintReq = this.client.createAddSpanHintRequest({
|
|
195
|
+
traceId: txInfo.traceId,
|
|
196
|
+
parentSpanId: txInfo.rootSpanId,
|
|
197
|
+
txHash: txHash,
|
|
198
|
+
chainId: chainId,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
await this.client.addSpanHint(hintReq);
|
|
202
|
+
|
|
203
|
+
// Add event for transaction sent
|
|
204
|
+
await this.addTransactionEvent(txId, 'transaction_hash_available', {
|
|
205
|
+
txHash,
|
|
206
|
+
chainId: chainId.toString(),
|
|
207
|
+
timestamp: new Date().toISOString(),
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
console.log('[ParallaxService] Transaction hash associated with trace:', {
|
|
211
|
+
txId,
|
|
212
|
+
txHash,
|
|
213
|
+
traceId: txInfo.traceId,
|
|
214
|
+
});
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error('[ParallaxService] Failed to associate transaction hash:', error);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Add an event to a transaction trace
|
|
222
|
+
*
|
|
223
|
+
* @param txId - Transaction identifier
|
|
224
|
+
* @param eventName - Event name
|
|
225
|
+
* @param attributes - Event attributes
|
|
226
|
+
*/
|
|
227
|
+
async addTransactionEvent(
|
|
228
|
+
txId: string,
|
|
229
|
+
eventName: string,
|
|
230
|
+
attributes: Record<string, any> = {}
|
|
231
|
+
): Promise<void> {
|
|
232
|
+
const txInfo = this.activeTransactions.get(txId);
|
|
233
|
+
if (!txInfo) {
|
|
234
|
+
console.warn(`[ParallaxService] Transaction ${txId} not found. Cannot add event '${eventName}'`);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (!this.client || !txInfo.spanId) {
|
|
239
|
+
console.warn('[ParallaxService] Client not initialized or span not available');
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
// Convert attributes to plain object with string values
|
|
245
|
+
const eventAttributes: Record<string, string> = {};
|
|
246
|
+
Object.entries(attributes).forEach(([key, value]) => {
|
|
247
|
+
eventAttributes[key] = typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const request = this.client.createAddSpanEventRequest({
|
|
251
|
+
traceId: txInfo.traceId,
|
|
252
|
+
spanId: txInfo.spanId,
|
|
253
|
+
eventName: eventName,
|
|
254
|
+
attr: eventAttributes,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
await this.client.addSpanEvent(request);
|
|
258
|
+
} catch (error) {
|
|
259
|
+
console.error(`[ParallaxService] Failed to add transaction event '${eventName}':`, error);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Add an error to a transaction trace
|
|
265
|
+
* Creates a proper span error event in Parallax
|
|
266
|
+
*
|
|
267
|
+
* @param txId - Transaction identifier
|
|
268
|
+
* @param error - Error object or message
|
|
269
|
+
* @param errorType - Type/category of error (e.g., 'TransactionError', 'NetworkError', 'UserRejection')
|
|
270
|
+
*/
|
|
271
|
+
async addTransactionError(
|
|
272
|
+
txId: string,
|
|
273
|
+
error: Error | string,
|
|
274
|
+
errorType: string = 'TransactionError'
|
|
275
|
+
): Promise<void> {
|
|
276
|
+
const txInfo = this.activeTransactions.get(txId);
|
|
277
|
+
if (!txInfo) {
|
|
278
|
+
console.warn(`[ParallaxService] Transaction ${txId} not found. Cannot add error.`);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (!this.client || !txInfo.spanId) {
|
|
283
|
+
console.warn('[ParallaxService] Client not initialized or span not available');
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
// Extract error message and stack trace
|
|
289
|
+
let errorMessage = '';
|
|
290
|
+
let stackTrace: string | undefined;
|
|
291
|
+
|
|
292
|
+
if (error instanceof Error) {
|
|
293
|
+
errorMessage = error.message;
|
|
294
|
+
stackTrace = error.stack;
|
|
295
|
+
} else {
|
|
296
|
+
errorMessage = String(error);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const request = this.client.createAddSpanErrorRequest({
|
|
300
|
+
traceId: txInfo.traceId,
|
|
301
|
+
spanId: txInfo.spanId,
|
|
302
|
+
errorMessage: errorMessage,
|
|
303
|
+
errorType: errorType,
|
|
304
|
+
stackTrace: stackTrace,
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
await this.client.addSpanError(request);
|
|
308
|
+
|
|
309
|
+
console.log('[ParallaxService] Transaction error added to trace:', {
|
|
310
|
+
txId,
|
|
311
|
+
errorType,
|
|
312
|
+
message: error instanceof Error ? error.message : String(error),
|
|
313
|
+
});
|
|
314
|
+
} catch (err) {
|
|
315
|
+
console.error('[ParallaxService] Failed to add transaction error:', err);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Finish a transaction trace
|
|
321
|
+
* Called when transaction is confirmed or permanently failed
|
|
322
|
+
*
|
|
323
|
+
* @param txId - Transaction identifier
|
|
324
|
+
* @param options - Finish options (success, error message)
|
|
325
|
+
*/
|
|
326
|
+
async finishTransactionTrace(txId: string, options: FinishOptions = { success: true }): Promise<void> {
|
|
327
|
+
const txInfo = this.activeTransactions.get(txId);
|
|
328
|
+
if (!txInfo) {
|
|
329
|
+
console.warn(`[ParallaxService] Transaction ${txId} not found. Cannot finish.`);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (!this.client || !txInfo.spanId) {
|
|
334
|
+
console.warn('[ParallaxService] Client not initialized or span not available');
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
// Add final event
|
|
340
|
+
await this.addTransactionEvent(
|
|
341
|
+
txId,
|
|
342
|
+
options.success ? 'transaction_completed' : 'transaction_failed',
|
|
343
|
+
{
|
|
344
|
+
success: options.success.toString(),
|
|
345
|
+
error: options.error || '',
|
|
346
|
+
duration: (Date.now() - new Date(txInfo.timestamp).getTime()).toString(),
|
|
347
|
+
timestamp: new Date().toISOString(),
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
// Finish the span
|
|
352
|
+
const request = this.client.createFinishSpanRequest({
|
|
353
|
+
traceId: txInfo.traceId,
|
|
354
|
+
spanId: txInfo.spanId,
|
|
355
|
+
status: {
|
|
356
|
+
success: options.success,
|
|
357
|
+
errorMessage: options.error || '',
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
await this.client.finishSpan(request);
|
|
362
|
+
|
|
363
|
+
console.log('[ParallaxService] Transaction trace finished:', {
|
|
364
|
+
txId,
|
|
365
|
+
traceId: txInfo.traceId,
|
|
366
|
+
success: options.success,
|
|
367
|
+
txHash: txInfo.txHash,
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Remove from active transactions
|
|
371
|
+
this.activeTransactions.delete(txId);
|
|
372
|
+
} catch (error) {
|
|
373
|
+
console.error('[ParallaxService] Failed to finish transaction trace:', error);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Get info about an active transaction
|
|
379
|
+
*
|
|
380
|
+
* @param txId - Transaction identifier
|
|
381
|
+
* @returns Transaction info or null if not found
|
|
382
|
+
*/
|
|
383
|
+
getTransactionInfo(txId: string): TransactionInfo | null {
|
|
384
|
+
return this.activeTransactions.get(txId) || null;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Get all active transactions
|
|
389
|
+
*
|
|
390
|
+
* @returns Array of active transaction info
|
|
391
|
+
*/
|
|
392
|
+
getAllActiveTransactions(): Array<TransactionInfo & { txId: string }> {
|
|
393
|
+
return Array.from(this.activeTransactions.entries()).map(([txId, info]) => ({
|
|
394
|
+
txId,
|
|
395
|
+
...info,
|
|
396
|
+
}));
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Get the ParallaxClient instance for advanced usage
|
|
401
|
+
* @param apiKey - Optional API key
|
|
402
|
+
* @param gatewayUrl - Optional gateway URL
|
|
403
|
+
*/
|
|
404
|
+
getClient(apiKey?: string, gatewayUrl?: string): ParallaxClient {
|
|
405
|
+
this._ensureClient(apiKey, gatewayUrl);
|
|
406
|
+
if (!this.client) {
|
|
407
|
+
throw new Error('Failed to initialize Parallax client');
|
|
408
|
+
}
|
|
409
|
+
return this.client;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Export singleton instance
|
|
414
|
+
export const parallaxService = new ParallaxService();
|