@thesight/sdk 0.3.8 → 0.4.0
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/dist/index.cjs +133 -166
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -15
- package/dist/index.d.ts +0 -15
- package/dist/index.js +119 -152
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5,11 +5,89 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
trace as trace3,
|
|
7
7
|
context as context2,
|
|
8
|
-
SpanStatusCode as
|
|
8
|
+
SpanStatusCode as SpanStatusCode3
|
|
9
9
|
} from "@opentelemetry/api";
|
|
10
|
-
import { parseLogs as parseLogs2, IdlResolver as IdlResolver2, enrichTree as enrichTree2, flatAttributions as flatAttributions2 } from "@thesight/core";
|
|
11
10
|
import { IdlResolver as IdlResolver3 } from "@thesight/core";
|
|
12
11
|
|
|
12
|
+
// src/enrichment.ts
|
|
13
|
+
import { SpanStatusCode } from "@opentelemetry/api";
|
|
14
|
+
import { parseLogs, enrichTree, flatAttributions } from "@thesight/core";
|
|
15
|
+
var SOLANA_CU_BUDGET = 2e5;
|
|
16
|
+
var DEFAULT_ENRICHMENT_TIMEOUT_MS = 3e4;
|
|
17
|
+
var DEFAULT_ENRICHMENT_POLL_MS = 500;
|
|
18
|
+
async function pollForTransaction(getTransaction, signature, commitment, deadline, basePollMs) {
|
|
19
|
+
let attempt = 0;
|
|
20
|
+
while (Date.now() < deadline) {
|
|
21
|
+
try {
|
|
22
|
+
const tx = await getTransaction(signature, {
|
|
23
|
+
commitment,
|
|
24
|
+
maxSupportedTransactionVersion: 0
|
|
25
|
+
});
|
|
26
|
+
if (tx) return tx;
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
attempt++;
|
|
30
|
+
const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2e3);
|
|
31
|
+
await sleep(waitMs);
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function attachTxDetailsToSpan(span, txDetails) {
|
|
36
|
+
span.setAttribute("solana.tx.status", txDetails.meta?.err ? "failed" : "confirmed");
|
|
37
|
+
span.setAttribute("solana.tx.slot", txDetails.slot);
|
|
38
|
+
const fee = txDetails.meta?.fee;
|
|
39
|
+
if (fee !== void 0) span.setAttribute("solana.tx.fee_lamports", fee);
|
|
40
|
+
const cuUsed = txDetails.meta?.computeUnitsConsumed;
|
|
41
|
+
if (cuUsed !== void 0 && cuUsed !== null) {
|
|
42
|
+
span.setAttribute("solana.tx.cu_used", Number(cuUsed));
|
|
43
|
+
span.setAttribute("solana.tx.cu_budget", SOLANA_CU_BUDGET);
|
|
44
|
+
span.setAttribute(
|
|
45
|
+
"solana.tx.cu_utilization",
|
|
46
|
+
parseFloat((Number(cuUsed) / SOLANA_CU_BUDGET * 100).toFixed(1))
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function attachParsedLogsToSpan(span, logs, resolver) {
|
|
51
|
+
const { cpiTree } = parseLogs({ logs });
|
|
52
|
+
await enrichTree(cpiTree, resolver);
|
|
53
|
+
const attributions = flatAttributions(cpiTree);
|
|
54
|
+
for (const attr of attributions) {
|
|
55
|
+
span.addEvent("cpi.invoke", {
|
|
56
|
+
"cpi.program": attr.programName ?? attr.programId,
|
|
57
|
+
"cpi.instruction": attr.instructionName ?? "unknown",
|
|
58
|
+
"cpi.depth": attr.depth,
|
|
59
|
+
"cpi.cu_consumed": attr.cuConsumed,
|
|
60
|
+
"cpi.cu_self": attr.cuSelf,
|
|
61
|
+
"cpi.percentage": parseFloat(attr.percentage.toFixed(2))
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const root = cpiTree.roots[0];
|
|
65
|
+
if (root) {
|
|
66
|
+
if (root.programName) span.setAttribute("solana.tx.program", root.programName);
|
|
67
|
+
if (root.instructionName) span.setAttribute("solana.tx.instruction", root.instructionName);
|
|
68
|
+
}
|
|
69
|
+
return cpiTree;
|
|
70
|
+
}
|
|
71
|
+
function finalizeSpan(span, txDetails, startTime) {
|
|
72
|
+
if (!txDetails) {
|
|
73
|
+
span.setAttribute("solana.tx.status", "timeout");
|
|
74
|
+
span.setAttribute("solana.tx.enrichment_ms", Date.now() - startTime);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
span.setAttribute("solana.tx.enrichment_ms", Date.now() - startTime);
|
|
78
|
+
if (txDetails.meta?.err) {
|
|
79
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
80
|
+
} else {
|
|
81
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function sleep(ms) {
|
|
85
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/index.ts
|
|
89
|
+
import { IdlResolver as IdlResolver4 } from "@thesight/core";
|
|
90
|
+
|
|
13
91
|
// src/init.ts
|
|
14
92
|
import { trace } from "@opentelemetry/api";
|
|
15
93
|
|
|
@@ -311,68 +389,40 @@ var SightInlineProcessor = class {
|
|
|
311
389
|
};
|
|
312
390
|
|
|
313
391
|
// src/track.ts
|
|
314
|
-
import { trace as trace2, context, SpanStatusCode } from "@opentelemetry/api";
|
|
315
|
-
import {
|
|
392
|
+
import { trace as trace2, context, SpanStatusCode as SpanStatusCode2 } from "@opentelemetry/api";
|
|
393
|
+
import { IdlResolver as IdlResolver2 } from "@thesight/core";
|
|
316
394
|
async function trackSolanaTransaction(opts) {
|
|
317
395
|
const tracerName = opts.serviceName ?? "@thesight/sdk";
|
|
318
396
|
const tracer = opts.tracer ?? trace2.getTracer(tracerName);
|
|
319
397
|
const span = tracer.startSpan("solana.trackTransaction", {}, context.active());
|
|
320
398
|
span.setAttribute("solana.tx.signature", opts.signature);
|
|
321
399
|
const start = Date.now();
|
|
322
|
-
const deadline = start + (opts.timeoutMs ??
|
|
400
|
+
const deadline = start + (opts.timeoutMs ?? DEFAULT_ENRICHMENT_TIMEOUT_MS);
|
|
323
401
|
const commitment = opts.commitment ?? "confirmed";
|
|
324
|
-
const basePoll = opts.pollIntervalMs ??
|
|
402
|
+
const basePoll = opts.pollIntervalMs ?? DEFAULT_ENRICHMENT_POLL_MS;
|
|
325
403
|
const resolver = opts.idlResolver ?? buildResolverFromIdls(opts.idls);
|
|
326
404
|
try {
|
|
327
|
-
const txDetails = await
|
|
405
|
+
const txDetails = await pollForTransaction(
|
|
406
|
+
(sig, pollOpts) => opts.connection.getTransaction(sig, pollOpts),
|
|
407
|
+
opts.signature,
|
|
408
|
+
commitment,
|
|
409
|
+
deadline,
|
|
410
|
+
basePoll
|
|
411
|
+
);
|
|
328
412
|
if (!txDetails) {
|
|
329
|
-
span
|
|
330
|
-
span.setAttribute("solana.tx.enrichment_ms", Date.now() - start);
|
|
413
|
+
finalizeSpan(span, null, start);
|
|
331
414
|
return;
|
|
332
415
|
}
|
|
333
|
-
span
|
|
334
|
-
span.setAttribute("solana.tx.slot", txDetails.slot);
|
|
335
|
-
const fee = txDetails.meta?.fee;
|
|
336
|
-
if (fee !== void 0) span.setAttribute("solana.tx.fee_lamports", fee);
|
|
337
|
-
const cuUsed = txDetails.meta?.computeUnitsConsumed;
|
|
338
|
-
if (cuUsed !== void 0 && cuUsed !== null) {
|
|
339
|
-
span.setAttribute("solana.tx.cu_used", Number(cuUsed));
|
|
340
|
-
span.setAttribute("solana.tx.cu_budget", 2e5);
|
|
341
|
-
span.setAttribute(
|
|
342
|
-
"solana.tx.cu_utilization",
|
|
343
|
-
parseFloat((Number(cuUsed) / 2e5 * 100).toFixed(1))
|
|
344
|
-
);
|
|
345
|
-
}
|
|
416
|
+
attachTxDetailsToSpan(span, txDetails);
|
|
346
417
|
const logs = txDetails.meta?.logMessages ?? [];
|
|
347
418
|
if (logs.length > 0) {
|
|
348
|
-
|
|
349
|
-
await enrichTree(cpiTree, resolver);
|
|
350
|
-
for (const attr of flatAttributions(cpiTree)) {
|
|
351
|
-
span.addEvent("cpi.invoke", {
|
|
352
|
-
"cpi.program": attr.programName ?? attr.programId,
|
|
353
|
-
"cpi.instruction": attr.instructionName ?? "unknown",
|
|
354
|
-
"cpi.depth": attr.depth,
|
|
355
|
-
"cpi.cu_consumed": attr.cuConsumed,
|
|
356
|
-
"cpi.cu_self": attr.cuSelf,
|
|
357
|
-
"cpi.percentage": parseFloat(attr.percentage.toFixed(2))
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
const root = cpiTree.roots[0];
|
|
361
|
-
if (root) {
|
|
362
|
-
if (root.programName) span.setAttribute("solana.tx.program", root.programName);
|
|
363
|
-
if (root.instructionName) span.setAttribute("solana.tx.instruction", root.instructionName);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
span.setAttribute("solana.tx.enrichment_ms", Date.now() - start);
|
|
367
|
-
if (txDetails.meta?.err) {
|
|
368
|
-
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
369
|
-
} else {
|
|
370
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
419
|
+
await attachParsedLogsToSpan(span, logs, resolver);
|
|
371
420
|
}
|
|
421
|
+
finalizeSpan(span, txDetails, start);
|
|
372
422
|
} catch (err) {
|
|
373
423
|
span.recordException(err instanceof Error ? err : new Error(String(err)));
|
|
374
424
|
span.setStatus({
|
|
375
|
-
code:
|
|
425
|
+
code: SpanStatusCode2.ERROR,
|
|
376
426
|
message: err instanceof Error ? err.message : String(err)
|
|
377
427
|
});
|
|
378
428
|
} finally {
|
|
@@ -380,27 +430,10 @@ async function trackSolanaTransaction(opts) {
|
|
|
380
430
|
}
|
|
381
431
|
}
|
|
382
432
|
function buildResolverFromIdls(idls) {
|
|
383
|
-
const resolver = new
|
|
433
|
+
const resolver = new IdlResolver2();
|
|
384
434
|
if (idls) resolver.registerMany(idls);
|
|
385
435
|
return resolver;
|
|
386
436
|
}
|
|
387
|
-
async function pollGetTransaction(connection, signature, commitment, deadline, basePollMs) {
|
|
388
|
-
let attempt = 0;
|
|
389
|
-
while (Date.now() < deadline) {
|
|
390
|
-
try {
|
|
391
|
-
const tx = await connection.getTransaction(signature, {
|
|
392
|
-
commitment,
|
|
393
|
-
maxSupportedTransactionVersion: 0
|
|
394
|
-
});
|
|
395
|
-
if (tx) return tx;
|
|
396
|
-
} catch {
|
|
397
|
-
}
|
|
398
|
-
attempt++;
|
|
399
|
-
const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2e3);
|
|
400
|
-
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
401
|
-
}
|
|
402
|
-
return null;
|
|
403
|
-
}
|
|
404
437
|
|
|
405
438
|
// src/index.ts
|
|
406
439
|
var InstrumentedConnection = class extends Connection {
|
|
@@ -417,7 +450,9 @@ var InstrumentedConnection = class extends Connection {
|
|
|
417
450
|
enrichmentTimeoutMs,
|
|
418
451
|
enrichmentPollIntervalMs,
|
|
419
452
|
disableAutoSpan,
|
|
420
|
-
commitment
|
|
453
|
+
// Note: commitment is intentionally NOT destructured out. It must
|
|
454
|
+
// pass through to super() via connectionConfig so the parent
|
|
455
|
+
// Connection uses it for RPC calls (preflight, blockhash, etc.).
|
|
421
456
|
...connectionConfig
|
|
422
457
|
} = config2 ?? {};
|
|
423
458
|
super(endpoint, connectionConfig);
|
|
@@ -426,12 +461,12 @@ var InstrumentedConnection = class extends Connection {
|
|
|
426
461
|
idlRpcEndpoint,
|
|
427
462
|
skipIdlResolution,
|
|
428
463
|
allowOnChainIdlFetch,
|
|
429
|
-
enrichmentTimeoutMs: enrichmentTimeoutMs ??
|
|
430
|
-
enrichmentPollIntervalMs: enrichmentPollIntervalMs ??
|
|
464
|
+
enrichmentTimeoutMs: enrichmentTimeoutMs ?? DEFAULT_ENRICHMENT_TIMEOUT_MS,
|
|
465
|
+
enrichmentPollIntervalMs: enrichmentPollIntervalMs ?? DEFAULT_ENRICHMENT_POLL_MS,
|
|
431
466
|
disableAutoSpan,
|
|
432
|
-
commitment
|
|
467
|
+
commitment: config2?.commitment
|
|
433
468
|
};
|
|
434
|
-
this.idlResolver = new
|
|
469
|
+
this.idlResolver = new IdlResolver3({
|
|
435
470
|
rpcEndpoint: idlRpcEndpoint ?? endpoint,
|
|
436
471
|
allowOnChainFetch: allowOnChainIdlFetch ?? false
|
|
437
472
|
});
|
|
@@ -479,7 +514,7 @@ var InstrumentedConnection = class extends Connection {
|
|
|
479
514
|
span.setAttribute("solana.tx.status", "failed");
|
|
480
515
|
span.recordException(err instanceof Error ? err : new Error(String(err)));
|
|
481
516
|
span.setStatus({
|
|
482
|
-
code:
|
|
517
|
+
code: SpanStatusCode3.ERROR,
|
|
483
518
|
message: err instanceof Error ? err.message : String(err)
|
|
484
519
|
});
|
|
485
520
|
span.end();
|
|
@@ -503,30 +538,30 @@ var InstrumentedConnection = class extends Connection {
|
|
|
503
538
|
*/
|
|
504
539
|
async enrichSpanInBackground(span, signature, submitStart) {
|
|
505
540
|
const enrichStart = Date.now();
|
|
506
|
-
const deadline = enrichStart + (this.sightConfig.enrichmentTimeoutMs ??
|
|
541
|
+
const deadline = enrichStart + (this.sightConfig.enrichmentTimeoutMs ?? DEFAULT_ENRICHMENT_TIMEOUT_MS);
|
|
507
542
|
const commitment = this.sightConfig.commitment ?? "confirmed";
|
|
508
|
-
const basePollMs = this.sightConfig.enrichmentPollIntervalMs ??
|
|
543
|
+
const basePollMs = this.sightConfig.enrichmentPollIntervalMs ?? DEFAULT_ENRICHMENT_POLL_MS;
|
|
509
544
|
try {
|
|
510
|
-
const txDetails = await
|
|
545
|
+
const txDetails = await pollForTransaction(
|
|
546
|
+
(sig, opts) => super.getTransaction(sig, opts),
|
|
547
|
+
signature,
|
|
548
|
+
commitment,
|
|
549
|
+
deadline,
|
|
550
|
+
basePollMs
|
|
551
|
+
);
|
|
511
552
|
if (!txDetails) {
|
|
512
|
-
span
|
|
513
|
-
span.setAttribute("solana.tx.enrichment_ms", Date.now() - submitStart);
|
|
553
|
+
finalizeSpan(span, null, submitStart);
|
|
514
554
|
span.end();
|
|
515
555
|
return;
|
|
516
556
|
}
|
|
517
|
-
|
|
557
|
+
attachTxDetailsToSpan(span, txDetails);
|
|
518
558
|
if (!this.sightConfig.skipIdlResolution) {
|
|
519
559
|
const logs = txDetails.meta?.logMessages ?? [];
|
|
520
560
|
if (logs.length > 0) {
|
|
521
|
-
await
|
|
561
|
+
await attachParsedLogsToSpan(span, logs, this.idlResolver);
|
|
522
562
|
}
|
|
523
563
|
}
|
|
524
|
-
span
|
|
525
|
-
if (txDetails.meta?.err) {
|
|
526
|
-
span.setStatus({ code: SpanStatusCode2.ERROR });
|
|
527
|
-
} else {
|
|
528
|
-
span.setStatus({ code: SpanStatusCode2.OK });
|
|
529
|
-
}
|
|
564
|
+
finalizeSpan(span, txDetails, submitStart);
|
|
530
565
|
} catch (err) {
|
|
531
566
|
span.recordException(err instanceof Error ? err : new Error(String(err)));
|
|
532
567
|
span.setAttribute(
|
|
@@ -537,77 +572,9 @@ var InstrumentedConnection = class extends Connection {
|
|
|
537
572
|
span.end();
|
|
538
573
|
}
|
|
539
574
|
}
|
|
540
|
-
/**
|
|
541
|
-
* Poll `getTransaction(signature)` until either the on-chain record is
|
|
542
|
-
* returned or the deadline passes. Exponential backoff (1.5x) capped at
|
|
543
|
-
* 2 seconds to balance responsiveness against RPC load.
|
|
544
|
-
*/
|
|
545
|
-
async pollForTransaction(signature, commitment, deadline, basePollMs) {
|
|
546
|
-
let attempt = 0;
|
|
547
|
-
while (Date.now() < deadline) {
|
|
548
|
-
try {
|
|
549
|
-
const tx = await super.getTransaction(signature, {
|
|
550
|
-
commitment,
|
|
551
|
-
maxSupportedTransactionVersion: 0
|
|
552
|
-
});
|
|
553
|
-
if (tx) return tx;
|
|
554
|
-
} catch {
|
|
555
|
-
}
|
|
556
|
-
attempt++;
|
|
557
|
-
const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2e3);
|
|
558
|
-
await sleep(waitMs);
|
|
559
|
-
}
|
|
560
|
-
return null;
|
|
561
|
-
}
|
|
562
|
-
/** Attach the flat fields (slot, fee, CU, status) from a tx response to a span. */
|
|
563
|
-
attachTxDetailsToSpan(span, txDetails) {
|
|
564
|
-
span.setAttribute("solana.tx.status", txDetails.meta?.err ? "failed" : "confirmed");
|
|
565
|
-
span.setAttribute("solana.tx.slot", txDetails.slot);
|
|
566
|
-
const fee = txDetails.meta?.fee;
|
|
567
|
-
if (fee !== void 0) span.setAttribute("solana.tx.fee_lamports", fee);
|
|
568
|
-
const cuUsed = txDetails.meta?.computeUnitsConsumed;
|
|
569
|
-
if (cuUsed !== void 0 && cuUsed !== null) {
|
|
570
|
-
span.setAttribute("solana.tx.cu_used", Number(cuUsed));
|
|
571
|
-
span.setAttribute("solana.tx.cu_budget", 2e5);
|
|
572
|
-
span.setAttribute(
|
|
573
|
-
"solana.tx.cu_utilization",
|
|
574
|
-
parseFloat((Number(cuUsed) / 2e5 * 100).toFixed(1))
|
|
575
|
-
);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
/**
|
|
579
|
-
* Parse program logs into a CPI tree, enrich with registered IDLs, and
|
|
580
|
-
* emit one `cpi.invoke` event per invocation onto the span. Root
|
|
581
|
-
* program/instruction names are copied onto span attributes so dashboards
|
|
582
|
-
* can filter by them without walking events.
|
|
583
|
-
*/
|
|
584
|
-
async attachParsedLogsToSpan(span, logs) {
|
|
585
|
-
const { cpiTree } = parseLogs2({ logs });
|
|
586
|
-
await enrichTree2(cpiTree, this.idlResolver);
|
|
587
|
-
const attributions = flatAttributions2(cpiTree);
|
|
588
|
-
for (const attr of attributions) {
|
|
589
|
-
span.addEvent("cpi.invoke", {
|
|
590
|
-
"cpi.program": attr.programName ?? attr.programId,
|
|
591
|
-
"cpi.instruction": attr.instructionName ?? "unknown",
|
|
592
|
-
"cpi.depth": attr.depth,
|
|
593
|
-
"cpi.cu_consumed": attr.cuConsumed,
|
|
594
|
-
"cpi.cu_self": attr.cuSelf,
|
|
595
|
-
"cpi.percentage": parseFloat(attr.percentage.toFixed(2))
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
const root = cpiTree.roots[0];
|
|
599
|
-
if (root) {
|
|
600
|
-
if (root.programName) span.setAttribute("solana.tx.program", root.programName);
|
|
601
|
-
if (root.instructionName) span.setAttribute("solana.tx.instruction", root.instructionName);
|
|
602
|
-
}
|
|
603
|
-
return cpiTree;
|
|
604
|
-
}
|
|
605
575
|
};
|
|
606
|
-
function sleep(ms) {
|
|
607
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
608
|
-
}
|
|
609
576
|
export {
|
|
610
|
-
|
|
577
|
+
IdlResolver4 as IdlResolver,
|
|
611
578
|
InstrumentedConnection,
|
|
612
579
|
SightSpanExporter,
|
|
613
580
|
buildDsn,
|