@hashspan/core 0.6.0 → 0.8.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.mjs CHANGED
@@ -1,11 +1,11 @@
1
- import { SpanKind, SpanStatusCode, context, diag, propagation, trace } from "@opentelemetry/api";
1
+ import { SpanKind, SpanStatusCode, context, diag, metrics, propagation, trace } from "@opentelemetry/api";
2
2
  //#region src/agent.ts
3
3
  const ATTR_GEN_AI_AGENT_ID = "gen_ai.agent.id";
4
4
  const ATTR_GEN_AI_AGENT_NAME = "gen_ai.agent.name";
5
5
  /**
6
6
  * Agent identity as GenAI attributes. A field set in the static identity always wins; Baggage, which a remote caller
7
7
  * can set, only fills fields it leaves unset, and is not read at all with `fromBaggage` false
8
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0011-agent-identity-precedence.md).
8
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0011-agent-identity-precedence.md).
9
9
  */
10
10
  function agentAttributes(ctx, identity, fromBaggage = true) {
11
11
  const baggage = fromBaggage ? propagation.getBaggage(ctx) : void 0;
@@ -21,7 +21,7 @@ function agentAttributes(ctx, identity, fromBaggage = true) {
21
21
  /**
22
22
  * Attribute keys emitted by hashspan.
23
23
  *
24
- * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md for
24
+ * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/semconv.md for
25
25
  * definitions and value types. These names are a public contract: changes follow the deprecation policy in AGENTS.md.
26
26
  */
27
27
  const ATTR_BLOCKCHAIN_SYSTEM = "blockchain.system";
@@ -45,12 +45,12 @@ const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME = "blockchain.contract.function.nam
45
45
  const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR = "blockchain.contract.function.selector";
46
46
  /**
47
47
  * Opt-in: decoded call arguments as a JSON array. See
48
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0004-privacy-defaults.md.
48
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0004-privacy-defaults.md.
49
49
  */
50
50
  const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS = "blockchain.contract.function.arguments";
51
51
  /**
52
52
  * Payments settled on chain by a party other than the agent, e.g. an x402 facilitator. See
53
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md.
53
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0013-x402-payments.md.
54
54
  */
55
55
  const ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL = "blockchain.payment.protocol";
56
56
  const ATTR_BLOCKCHAIN_PAYMENT_PAYER = "blockchain.payment.payer";
@@ -63,12 +63,33 @@ const ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT = "blockchain.payment.settled_amoun
63
63
  /**
64
64
  * Whether the settlement transaction's receipt carries the payment, as checked by the adapter; absent when no check
65
65
  * was possible. See
66
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0017-x402-payment-verification.md.
66
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0017-x402-payment-verification.md.
67
67
  */
68
68
  const ATTR_BLOCKCHAIN_PAYMENT_VERIFIED = "blockchain.payment.verified";
69
69
  /** x402's own payment fields. */
70
70
  const ATTR_X402_SCHEME = "x402.scheme";
71
71
  const ATTR_X402_RESOURCE = "x402.resource";
72
+ /**
73
+ * User operations of ERC-4337 smart accounts. See
74
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0021-user-operations.md.
75
+ */
76
+ const ATTR_BLOCKCHAIN_USER_OPERATION_HASH = "blockchain.user_operation.hash";
77
+ const ATTR_BLOCKCHAIN_USER_OPERATION_SENDER = "blockchain.user_operation.sender";
78
+ const ATTR_BLOCKCHAIN_USER_OPERATION_ENTRY_POINT = "blockchain.user_operation.entry_point";
79
+ const ATTR_BLOCKCHAIN_USER_OPERATION_CALL_COUNT = "blockchain.user_operation.call_count";
80
+ const ATTR_BLOCKCHAIN_USER_OPERATION_NONCE = "blockchain.user_operation.nonce";
81
+ const ATTR_BLOCKCHAIN_USER_OPERATION_SUCCESS = "blockchain.user_operation.success";
82
+ const ATTR_BLOCKCHAIN_USER_OPERATION_GAS_USED = "blockchain.user_operation.gas.used";
83
+ /** What the operation itself paid, in wei; the bundle transaction's fee covers every operation in the bundle. */
84
+ const ATTR_BLOCKCHAIN_USER_OPERATION_GAS_COST = "blockchain.user_operation.gas.cost";
85
+ const ATTR_BLOCKCHAIN_USER_OPERATION_PAYMASTER = "blockchain.user_operation.paymaster";
86
+ /**
87
+ * Metrics only: what a send, confirmation or fee sample is about. Recorded as `user_operation` on samples of user
88
+ * operations, and absent on those of transactions.
89
+ */
90
+ const ATTR_BLOCKCHAIN_OPERATION_SUBJECT = "blockchain.operation.subject";
91
+ /** Values for {@link ATTR_BLOCKCHAIN_OPERATION_SUBJECT}. */
92
+ const BLOCKCHAIN_OPERATION_SUBJECT_VALUE_USER_OPERATION = "user_operation";
72
93
  /** Values for {@link ATTR_BLOCKCHAIN_SYSTEM}. */
73
94
  const BLOCKCHAIN_SYSTEM_VALUE_EVM = "evm";
74
95
  /** Values for {@link ATTR_BLOCKCHAIN_OPERATION_NAME}. */
@@ -87,7 +108,7 @@ const BLOCKCHAIN_TX_STATUS_VALUE_REVERTED = "reverted";
87
108
  /**
88
109
  * @deprecated No longer recorded: a confirm span that gave up waiting records `error.type` `timeout` and no
89
110
  * `blockchain.tx.status`. The constant is removed in 1.0. See
90
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
111
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
91
112
  */
92
113
  const BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT = "timeout";
93
114
  const BLOCKCHAIN_TX_STATUS_VALUE_REPLACED = "replaced";
@@ -100,6 +121,107 @@ const ATTR_ERROR_TYPE = "error.type";
100
121
  /** Fallback {@link ATTR_ERROR_TYPE} value when the error has no name. */
101
122
  const ERROR_TYPE_VALUE_OTHER = "_OTHER";
102
123
  //#endregion
124
+ //#region src/metrics.ts
125
+ /**
126
+ * Duration of a send of a transaction or user operation: from the start of the sending call until the hash is known or
127
+ * the call failed.
128
+ */
129
+ const METRIC_BLOCKCHAIN_CLIENT_SEND_DURATION = "blockchain.client.send.duration";
130
+ /**
131
+ * Duration of a confirmation of a transaction or user operation: from the start of the wait until the receipt, a
132
+ * timeout or a failure.
133
+ */
134
+ const METRIC_BLOCKCHAIN_CLIENT_CONFIRMATION_DURATION = "blockchain.client.confirmation.duration";
135
+ /**
136
+ * Total fee of a mined transaction (execution fee plus L1 data fee), or the cost of a user operation, in the chain's
137
+ * smallest unit (wei).
138
+ */
139
+ const METRIC_BLOCKCHAIN_CLIENT_FEE = "blockchain.client.fee";
140
+ const DURATION_BUCKETS = [
141
+ .05,
142
+ .1,
143
+ .25,
144
+ .5,
145
+ 1,
146
+ 2,
147
+ 5,
148
+ 10,
149
+ 20,
150
+ 30,
151
+ 60,
152
+ 120,
153
+ 300
154
+ ];
155
+ const FEE_BUCKETS = Array.from({ length: 11 }, (_, i) => 10 ** (i + 8));
156
+ const ERROR_CLASS = /^[A-Z][A-Za-z]{0,62}Error$|^Error$/;
157
+ const ERROR_CODE = /^[a-z]{1,32}(_[a-z]{1,32}){0,7}$/;
158
+ /**
159
+ * `attributes` with `error.type` kept only if it is an error class name or a lower-case code, else `_OTHER`. Error
160
+ * names are free text (any `Error.name`), and metrics do not pass through the `redact` hook; the span keeps its own
161
+ * `error.type`.
162
+ */
163
+ function withMetricErrorType(attributes) {
164
+ const type = attributes[ATTR_ERROR_TYPE];
165
+ if (type === void 0) return attributes;
166
+ return typeof type === "string" && (type === "_OTHER" || ERROR_CLASS.test(type) || ERROR_CODE.test(type)) ? attributes : {
167
+ ...attributes,
168
+ [ATTR_ERROR_TYPE]: ERROR_TYPE_VALUE_OTHER
169
+ };
170
+ }
171
+ /** Milliseconds since the epoch of a span time, as the OpenTelemetry API accepts it. */
172
+ function toEpochMs(time) {
173
+ if (time === void 0) return Date.now();
174
+ if (time instanceof Date) return time.getTime();
175
+ if (Array.isArray(time)) return time[0] * 1e3 + time[1] / 1e6;
176
+ if (typeof time !== "number") return Date.now();
177
+ const origin = globalThis.performance?.timeOrigin;
178
+ return typeof origin === "number" && time < origin ? origin + time : time;
179
+ }
180
+ function createTxMetrics(meterProvider, name, version) {
181
+ let histograms;
182
+ const get = () => {
183
+ histograms ??= (() => {
184
+ const meter = (meterProvider ?? metrics.getMeterProvider()).getMeter(name, version);
185
+ return {
186
+ send: meter.createHistogram(METRIC_BLOCKCHAIN_CLIENT_SEND_DURATION, {
187
+ unit: "s",
188
+ description: "Duration of sending a transaction or user operation, until its hash is known",
189
+ advice: { explicitBucketBoundaries: DURATION_BUCKETS }
190
+ }),
191
+ confirmation: meter.createHistogram(METRIC_BLOCKCHAIN_CLIENT_CONFIRMATION_DURATION, {
192
+ unit: "s",
193
+ description: "Duration of waiting for a transaction or user operation receipt",
194
+ advice: { explicitBucketBoundaries: DURATION_BUCKETS }
195
+ }),
196
+ fee: meter.createHistogram(METRIC_BLOCKCHAIN_CLIENT_FEE, {
197
+ unit: "{wei}",
198
+ description: "Total fee of a mined transaction, or the cost of a user operation",
199
+ advice: { explicitBucketBoundaries: FEE_BUCKETS }
200
+ })
201
+ };
202
+ })();
203
+ return histograms;
204
+ };
205
+ const record = (what, run) => {
206
+ try {
207
+ run();
208
+ } catch (error) {
209
+ diag.error(`hashspan: failed to record the ${what} metric`, error);
210
+ }
211
+ };
212
+ return {
213
+ sendDuration: (seconds, attributes) => record("send duration", () => {
214
+ if (seconds >= 0) get().send.record(seconds, withMetricErrorType(attributes));
215
+ }),
216
+ confirmationDuration: (seconds, attributes) => record("confirmation duration", () => {
217
+ if (seconds >= 0) get().confirmation.record(seconds, withMetricErrorType(attributes));
218
+ }),
219
+ fee: (wei, attributes) => record("fee", () => {
220
+ if (wei >= 0n) get().fee.record(Number(wei), attributes);
221
+ })
222
+ };
223
+ }
224
+ //#endregion
103
225
  //#region src/confirm-registry.ts
104
226
  /**
105
227
  * Bounded registry from (chainId, tx hash) to its in-flight confirm span, or to "settled" for a while after a
@@ -385,7 +507,7 @@ function sanitizeResource(resource) {
385
507
  }
386
508
  //#endregion
387
509
  //#region src/version.ts
388
- const VERSION = "0.6.0";
510
+ const VERSION = "0.8.0";
389
511
  //#endregion
390
512
  //#region src/tracker.ts
391
513
  const INSTRUMENTATION_NAME = "@hashspan/core";
@@ -408,13 +530,20 @@ const NON_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
408
530
  ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL,
409
531
  ATTR_BLOCKCHAIN_PAYMENT_STATUS,
410
532
  ATTR_BLOCKCHAIN_PAYMENT_VERIFIED,
533
+ ATTR_BLOCKCHAIN_USER_OPERATION_HASH,
534
+ ATTR_BLOCKCHAIN_USER_OPERATION_SUCCESS,
411
535
  ATTR_ERROR_TYPE,
412
536
  ATTR_EXCEPTION_TYPE
413
537
  ]);
414
538
  const TX_HASH = /^0x[0-9a-fA-F]{64}$/;
415
539
  const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
540
+ const ZERO_ADDRESS = /^0x0{40}$/;
416
541
  /** A non-negative integer that fits in 256 bits. */
417
542
  const AMOUNT = /^(0|[1-9][0-9]{0,77})$/;
543
+ /** A `0x` hex quantity of at most 256 bits, as JSON-RPC encodes integers. */
544
+ const HEX_QUANTITY = /^0x[0-9a-fA-F]{1,64}$/;
545
+ const MAX_UINT256 = 2n ** 256n - 1n;
546
+ const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
418
547
  /** `error.type` of a wait that gave up: a confirmation or a payment whose outcome was never learned. */
419
548
  const OBSERVER_TIMEOUT = "timeout";
420
549
  const PAYMENT_STATUSES = /* @__PURE__ */ new Set([
@@ -433,6 +562,11 @@ const noopSend = (parent) => ({
433
562
  end: () => {},
434
563
  fail: () => {}
435
564
  });
565
+ const noopUserOperationSend = (parent) => ({
566
+ context: parent,
567
+ end: () => {},
568
+ fail: () => {}
569
+ });
436
570
  const NOOP_PAYMENT = {
437
571
  end: () => {},
438
572
  fail: () => {},
@@ -444,6 +578,11 @@ const NOOP_CONFIRM = {
444
578
  timeout: () => {},
445
579
  fail: () => {}
446
580
  };
581
+ const NOOP_USER_OPERATION_CONFIRM = {
582
+ end: () => {},
583
+ timeout: () => {},
584
+ fail: () => {}
585
+ };
447
586
  /** Runs `fn`, logging instead of throwing: instrumentation must never break the caller. */
448
587
  function safely(what, fn, fallback) {
449
588
  try {
@@ -466,6 +605,22 @@ const ERROR_TYPE_OVERRIDE = /^[A-Za-z0-9_.-]{1,64}$/;
466
605
  function identifier(value) {
467
606
  return typeof value === "string" && ERROR_TYPE_OVERRIDE.test(value) ? value : void 0;
468
607
  }
608
+ /**
609
+ * A non-negative integer of at most 256 bits, from a bigint, a safe integer, or a decimal or `0x` hex string, as
610
+ * bundlers return them; undefined for anything else.
611
+ */
612
+ function quantity(value) {
613
+ let parsed;
614
+ if (typeof value === "bigint") parsed = value;
615
+ else if (typeof value === "number" && Number.isSafeInteger(value)) parsed = BigInt(value);
616
+ else if (typeof value === "string" && (AMOUNT.test(value) || HEX_QUANTITY.test(value))) parsed = BigInt(value);
617
+ return parsed !== void 0 && parsed >= 0n && parsed <= MAX_UINT256 ? parsed : void 0;
618
+ }
619
+ /** A quantity as a number, or undefined when it is none or too large to be one exactly. */
620
+ function smallQuantity(value) {
621
+ const parsed = quantity(value);
622
+ return parsed !== void 0 && parsed <= MAX_SAFE_INTEGER ? Number(parsed) : void 0;
623
+ }
469
624
  /** A decimal amount, or undefined when `value` is not a non-negative integer. */
470
625
  function amount(value) {
471
626
  const text = typeof value === "bigint" ? value.toString() : value;
@@ -511,8 +666,44 @@ function reportedErrorType(error, options) {
511
666
  return errorType(error);
512
667
  }
513
668
  /**
669
+ * Joins the confirm span for `hash` in `registry`, opening it with `open` for the first handle; undefined when the
670
+ * key recently got a receipt. A receipt from any handle ends the span; a timeout or failure only ends it when it is the
671
+ * last handle still waiting.
672
+ */
673
+ function joinConfirm(registry, chainId, hash, open) {
674
+ const current = registry.get(chainId, hash);
675
+ if (current === "settled") return void 0;
676
+ let confirm = current;
677
+ if (!confirm) {
678
+ confirm = open();
679
+ registry.start(chainId, hash, confirm);
680
+ }
681
+ const shared = confirm;
682
+ shared.active += 1;
683
+ let done = false;
684
+ return {
685
+ shared,
686
+ receive: () => {
687
+ if (done || shared.ended) return false;
688
+ done = true;
689
+ shared.active -= 1;
690
+ shared.ended = true;
691
+ return true;
692
+ },
693
+ withdraw: (end) => {
694
+ if (done || shared.ended) return;
695
+ done = true;
696
+ shared.active -= 1;
697
+ if (shared.active > 0) return;
698
+ shared.ended = true;
699
+ registry.release(chainId, hash, shared);
700
+ end();
701
+ }
702
+ };
703
+ }
704
+ /**
514
705
  * Creates a tracker that records transactions as `send` and `confirm` spans, and payments as `payment` spans, with
515
- * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md). It makes
706
+ * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/semconv.md). It makes
516
707
  * no network calls; the caller passes hashes and receipts. Its methods and handles never throw: failures are logged
517
708
  * via `diag`, and a method that fails returns a handle that records nothing.
518
709
  */
@@ -528,6 +719,27 @@ function createTxTracker(options = {}) {
528
719
  const formatAddress = safely("configure address mode", () => resolveAddressFormatter(options.address), OFF_ADDRESS_FORMATTER);
529
720
  const errorMessages = safely("configure error message mode", () => resolveErrorMessageMode(options.errorMessages), "off");
530
721
  const paymentResource = safely("configure payment resource mode", () => resolvePaymentResourceMode(options.paymentResource), "off");
722
+ const userOperationLinks = new LinkStore({
723
+ ttlMs: options.linkTtlMs ?? DEFAULT_LINK_TTL_MS,
724
+ maxEntries: options.maxTrackedTransactions ?? DEFAULT_MAX_TRACKED
725
+ });
726
+ const userOperationConfirmations = new ConfirmRegistry({
727
+ ttlMs: options.linkTtlMs ?? DEFAULT_LINK_TTL_MS,
728
+ maxEntries: options.maxTrackedTransactions ?? DEFAULT_MAX_TRACKED
729
+ });
730
+ const txMetrics = createTxMetrics(options.meterProvider, INSTRUMENTATION_NAME, VERSION);
731
+ /** Attributes of a metric: low-cardinality only, never an address, hash or agent identity. */
732
+ const metricAttributes = (chainId, extra = {}) => ({
733
+ [ATTR_BLOCKCHAIN_SYSTEM]: "evm",
734
+ [ATTR_BLOCKCHAIN_CHAIN_ID]: chainId,
735
+ ...extra
736
+ });
737
+ /** Metric attributes of a user operation sample: as for transactions, plus what the sample is about. */
738
+ const userOperationMetricAttributes = (chainId, extra = {}) => metricAttributes(chainId, {
739
+ [ATTR_BLOCKCHAIN_OPERATION_SUBJECT]: BLOCKCHAIN_OPERATION_SUBJECT_VALUE_USER_OPERATION,
740
+ ...extra
741
+ });
742
+ const secondsSince = (startMs, endTime) => (toEpochMs(endTime) - startMs) / 1e3;
531
743
  let tracer;
532
744
  const getTracer = () => {
533
745
  tracer ??= (options.tracerProvider ?? trace.getTracerProvider()).getTracer(INSTRUMENTATION_NAME, VERSION);
@@ -552,7 +764,7 @@ function createTxTracker(options = {}) {
552
764
  /**
553
765
  * Exception event attributes for `error`, per the error message mode. The error object itself is never handed to
554
766
  * the SDK: its message and stack can carry addresses and calldata
555
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0006-error-privacy.md).
767
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0006-error-privacy.md).
556
768
  */
557
769
  const exceptionAttributes = (type, error) => {
558
770
  const attributes = { [ATTR_EXCEPTION_TYPE]: type };
@@ -585,6 +797,7 @@ function createTxTracker(options = {}) {
585
797
  code: SpanStatusCode.ERROR,
586
798
  ...message !== void 0 ? { message } : {}
587
799
  });
800
+ return type;
588
801
  };
589
802
  /** Ends a span exactly once; the span is always ended even if recording attributes fails. */
590
803
  const finisher = (span) => {
@@ -606,8 +819,8 @@ function createTxTracker(options = {}) {
606
819
  const formatted = formatAddress(address);
607
820
  if (formatted !== void 0) attributes[key] = formatted;
608
821
  };
609
- /** Records `address` only if it is one: payment addresses come from remote parties. */
610
- const setPaymentAddress = (attributes, key, address) => {
822
+ /** Records `address` only if it is one: payment and user operation addresses come from remote parties. */
823
+ const setRemoteAddress = (attributes, key, address) => {
611
824
  if (typeof address === "string" && ADDRESS.test(address)) setAddress(attributes, key, address);
612
825
  };
613
826
  const baseAttributes = (chainId, operation, ctx) => ({
@@ -636,6 +849,8 @@ function createTxTracker(options = {}) {
636
849
  ...input.startTime !== void 0 ? { startTime: input.startTime } : {}
637
850
  }, parent);
638
851
  const finish = finisher(span);
852
+ const startMs = toEpochMs(input.startTime);
853
+ const recordSend = (endTime, errorType) => txMetrics.sendDuration(secondsSince(startMs, endTime), metricAttributes(input.chainId, errorType === void 0 ? {} : { [ATTR_ERROR_TYPE]: errorType }));
639
854
  return {
640
855
  context: trace.setSpan(parent, span),
641
856
  end: (result, second) => finish("record transaction hash", () => {
@@ -649,10 +864,11 @@ function createTxTracker(options = {}) {
649
864
  parent
650
865
  });
651
866
  span.setAttributes(redact({ [ATTR_BLOCKCHAIN_TX_HASH]: hash }));
867
+ recordSend(handleOptions(second).endTime);
652
868
  }, handleOptions(second).endTime),
653
869
  fail: (error, second, third) => {
654
870
  const options = handleOptions(second, third);
655
- finish("record send failure", () => markError(span, reportedErrorType(error, options), error, errorType(error)), options.endTime);
871
+ finish("record send failure", () => recordSend(options.endTime, markError(span, reportedErrorType(error, options), error, errorType(error))), options.endTime);
656
872
  }
657
873
  };
658
874
  };
@@ -688,6 +904,8 @@ function createTxTracker(options = {}) {
688
904
  ...explicitStart !== void 0 ? { startTime: explicitStart } : {}
689
905
  }, parent);
690
906
  const finish = finisher(span);
907
+ const startMs = toEpochMs(explicitStart);
908
+ const recordConfirmation = (endTime, outcome) => txMetrics.confirmationDuration(secondsSince(startMs, endTime), metricAttributes(input.chainId, outcome));
691
909
  return {
692
910
  active: 0,
693
911
  ended: false,
@@ -697,11 +915,16 @@ function createTxTracker(options = {}) {
697
915
  links: [{ context: span.spanContext() }, ...sent ? [{ context: sent.spanContext }] : []]
698
916
  },
699
917
  receipt: (receipt, endTime) => finish("record receipt", () => {
700
- span.setAttributes(redact(receiptAttributes(receipt)));
918
+ const attributes = receiptAttributes(receipt);
919
+ span.setAttributes(redact(attributes));
701
920
  if (receipt.status === "reverted") markError(span, BLOCKCHAIN_TX_STATUS_VALUE_REVERTED);
921
+ const status = { [ATTR_BLOCKCHAIN_TX_STATUS]: attributes[ATTR_BLOCKCHAIN_TX_STATUS] };
922
+ recordConfirmation(endTime, status);
923
+ const fee = attributes[ATTR_BLOCKCHAIN_TX_FEE];
924
+ if (typeof fee === "string") txMetrics.fee(BigInt(fee), metricAttributes(input.chainId, status));
702
925
  }, endTime),
703
- timeout: (endTime) => finish("record confirmation timeout", () => markError(span, OBSERVER_TIMEOUT), endTime),
704
- fail: (error, endTime) => finish("record confirmation failure", () => markError(span, errorType(error), error), endTime),
926
+ timeout: (endTime) => finish("record confirmation timeout", () => recordConfirmation(endTime, { [ATTR_ERROR_TYPE]: markError(span, OBSERVER_TIMEOUT) }), endTime),
927
+ fail: (error, endTime) => finish("record confirmation failure", () => recordConfirmation(endTime, { [ATTR_ERROR_TYPE]: markError(span, errorType(error), error) }), endTime),
705
928
  replaced: (hash, reason, endTime) => finish("record replacement", () => {
706
929
  const attributes = {
707
930
  [ATTR_BLOCKCHAIN_TX_STATUS]: BLOCKCHAIN_TX_STATUS_VALUE_REPLACED,
@@ -709,8 +932,9 @@ function createTxTracker(options = {}) {
709
932
  };
710
933
  if (reason !== void 0 && REPLACEMENT_REASONS.has(reason)) attributes[ATTR_BLOCKCHAIN_TX_REPLACEMENT_REASON] = reason;
711
934
  span.setAttributes(redact(attributes));
935
+ recordConfirmation(endTime, { [ATTR_BLOCKCHAIN_TX_STATUS]: BLOCKCHAIN_TX_STATUS_VALUE_REPLACED });
712
936
  }, endTime),
713
- unattributable: (endTime) => finish("record unattributable receipt", () => markError(span, ERROR_TYPE_VALUE_OTHER), endTime)
937
+ unattributable: (endTime) => finish("record unattributable receipt", () => recordConfirmation(endTime, { [ATTR_ERROR_TYPE]: markError(span, ERROR_TYPE_VALUE_OTHER) }), endTime)
714
938
  };
715
939
  };
716
940
  /**
@@ -732,7 +956,7 @@ function createTxTracker(options = {}) {
732
956
  };
733
957
  /**
734
958
  * Ends `shared` with `receipt`, attributing it to the transaction that was mined
735
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0008-replaced-transactions.md).
959
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0008-replaced-transactions.md).
736
960
  */
737
961
  const endWithReceipt = (chainId, hash, shared, receipt, endTime) => {
738
962
  const mined = receipt.transactionHash;
@@ -762,41 +986,22 @@ function createTxTracker(options = {}) {
762
986
  */
763
987
  const startConfirm = (input, parentCtx) => {
764
988
  const { chainId, hash } = input;
765
- const current = confirmations.get(chainId, hash);
766
- if (current === "settled") return NOOP_CONFIRM;
767
- let confirm = current;
768
- if (!confirm) {
769
- confirm = openConfirm(input, parentCtx);
770
- confirmations.start(chainId, hash, confirm);
771
- }
772
- const shared = confirm;
773
- shared.active += 1;
774
- let done = false;
775
- const withdraw = (end) => {
776
- if (done || shared.ended) return;
777
- done = true;
778
- shared.active -= 1;
779
- if (shared.active > 0) return;
780
- shared.ended = true;
781
- confirmations.release(chainId, hash, shared);
782
- end();
783
- };
989
+ const claim = joinConfirm(confirmations, chainId, hash, () => openConfirm(input, parentCtx));
990
+ if (!claim) return NOOP_CONFIRM;
991
+ const { shared } = claim;
784
992
  return {
785
993
  end: (receipt, second) => {
786
994
  const { endTime } = handleOptions(second);
787
- if (done || shared.ended) return;
788
- done = true;
789
- shared.active -= 1;
790
- shared.ended = true;
995
+ if (!claim.receive()) return;
791
996
  safely("record receipt", () => endWithReceipt(chainId, hash, shared, receipt, endTime), void 0);
792
997
  },
793
998
  timeout: (second) => {
794
999
  const { endTime } = handleOptions(second);
795
- withdraw(() => shared.timeout(endTime));
1000
+ claim.withdraw(() => shared.timeout(endTime));
796
1001
  },
797
1002
  fail: (error, second) => {
798
1003
  const { endTime } = handleOptions(second);
799
- withdraw(() => shared.fail(error, endTime));
1004
+ claim.withdraw(() => shared.fail(error, endTime));
800
1005
  }
801
1006
  };
802
1007
  };
@@ -805,10 +1010,10 @@ function createTxTracker(options = {}) {
805
1010
  const attributes = baseAttributes(input.chainId, BLOCKCHAIN_OPERATION_NAME_VALUE_PAYMENT, parent);
806
1011
  const protocol = identifier(input.protocol);
807
1012
  if (protocol !== void 0) attributes[ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL] = protocol;
808
- setPaymentAddress(attributes, ATTR_BLOCKCHAIN_PAYMENT_PAYER, input.payer);
1013
+ setRemoteAddress(attributes, ATTR_BLOCKCHAIN_PAYMENT_PAYER, input.payer);
809
1014
  const knownPayer = typeof input.payer === "string" && ADDRESS.test(input.payer);
810
- setPaymentAddress(attributes, ATTR_BLOCKCHAIN_PAYMENT_RECIPIENT, input.recipient);
811
- setPaymentAddress(attributes, ATTR_BLOCKCHAIN_PAYMENT_ASSET, input.asset);
1015
+ setRemoteAddress(attributes, ATTR_BLOCKCHAIN_PAYMENT_RECIPIENT, input.recipient);
1016
+ setRemoteAddress(attributes, ATTR_BLOCKCHAIN_PAYMENT_ASSET, input.asset);
812
1017
  const paid = amount(input.amount);
813
1018
  if (paid !== void 0) attributes[ATTR_BLOCKCHAIN_PAYMENT_AMOUNT] = paid;
814
1019
  const scheme = identifier(input.x402?.scheme);
@@ -840,7 +1045,7 @@ function createTxTracker(options = {}) {
840
1045
  const settled = { [ATTR_BLOCKCHAIN_PAYMENT_STATUS]: status };
841
1046
  const hash = settlement.hash;
842
1047
  if (linkHash(hash)) settled[ATTR_BLOCKCHAIN_TX_HASH] = hash;
843
- if (!knownPayer) setPaymentAddress(settled, ATTR_BLOCKCHAIN_PAYMENT_PAYER, settlement.payer);
1048
+ if (!knownPayer) setRemoteAddress(settled, ATTR_BLOCKCHAIN_PAYMENT_PAYER, settlement.payer);
844
1049
  const settledAmount = amount(settlement.amount);
845
1050
  if (settledAmount !== void 0) {
846
1051
  settled[ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT] = settledAmount;
@@ -861,11 +1066,139 @@ function createTxTracker(options = {}) {
861
1066
  link: (hash) => safely("link the payment span", () => void linkHash(hash), void 0)
862
1067
  };
863
1068
  };
1069
+ const startUserOperationSend = (input, parentCtx) => {
1070
+ const parent = parentCtx ?? context.active();
1071
+ const { chainId } = input;
1072
+ const attributes = baseAttributes(chainId, BLOCKCHAIN_OPERATION_NAME_VALUE_SEND, parent);
1073
+ setRemoteAddress(attributes, ATTR_BLOCKCHAIN_USER_OPERATION_SENDER, input.sender);
1074
+ setRemoteAddress(attributes, ATTR_BLOCKCHAIN_USER_OPERATION_ENTRY_POINT, input.entryPoint);
1075
+ const callCount = smallQuantity(input.callCount);
1076
+ if (callCount !== void 0) attributes[ATTR_BLOCKCHAIN_USER_OPERATION_CALL_COUNT] = callCount;
1077
+ const span = getTracer().startSpan(`send ${chainId}`, {
1078
+ kind: SpanKind.CLIENT,
1079
+ attributes: redact(attributes),
1080
+ ...input.startTime !== void 0 ? { startTime: input.startTime } : {}
1081
+ }, parent);
1082
+ const finish = finisher(span);
1083
+ const startMs = toEpochMs(input.startTime);
1084
+ const recordSend = (endTime, errorType) => txMetrics.sendDuration(secondsSince(startMs, endTime), userOperationMetricAttributes(chainId, errorType === void 0 ? {} : { [ATTR_ERROR_TYPE]: errorType }));
1085
+ return {
1086
+ context: trace.setSpan(parent, span),
1087
+ end: (result, second) => {
1088
+ const { endTime } = handleOptions(second);
1089
+ finish("record user operation hash", () => {
1090
+ const hash = result?.userOpHash;
1091
+ if (typeof hash !== "string" || !TX_HASH.test(hash)) {
1092
+ diag.debug("hashspan: ending a send span without a valid user operation hash");
1093
+ return;
1094
+ }
1095
+ userOperationLinks.set(chainId, hash, {
1096
+ spanContext: span.spanContext(),
1097
+ parent
1098
+ });
1099
+ span.setAttributes(redact({ [ATTR_BLOCKCHAIN_USER_OPERATION_HASH]: hash }));
1100
+ recordSend(endTime);
1101
+ }, endTime);
1102
+ },
1103
+ fail: (error, second) => {
1104
+ const read = handleOptions(second);
1105
+ finish("record user operation send failure", () => recordSend(read.endTime, markError(span, reportedErrorType(error, read), error, errorType(error))), read.endTime);
1106
+ }
1107
+ };
1108
+ };
1109
+ /**
1110
+ * Attributes of a user operation receipt. Its values come from a bundler: what is malformed is left out. The bundle
1111
+ * transaction's status, gas and fee are not recorded: they cover every operation in the bundle (ADR 0021).
1112
+ */
1113
+ const userOperationReceiptAttributes = (receipt) => {
1114
+ const attributes = {};
1115
+ const success = receipt.success;
1116
+ if (typeof success === "boolean") attributes[ATTR_BLOCKCHAIN_USER_OPERATION_SUCCESS] = success;
1117
+ const gasUsed = smallQuantity(receipt.actualGasUsed);
1118
+ if (gasUsed !== void 0) attributes[ATTR_BLOCKCHAIN_USER_OPERATION_GAS_USED] = gasUsed;
1119
+ const gasCost = quantity(receipt.actualGasCost);
1120
+ if (gasCost !== void 0) attributes[ATTR_BLOCKCHAIN_USER_OPERATION_GAS_COST] = gasCost.toString();
1121
+ setRemoteAddress(attributes, ATTR_BLOCKCHAIN_USER_OPERATION_SENDER, receipt.sender);
1122
+ const nonce = quantity(receipt.nonce);
1123
+ if (nonce !== void 0) attributes[ATTR_BLOCKCHAIN_USER_OPERATION_NONCE] = nonce.toString();
1124
+ const paymaster = receipt.paymaster;
1125
+ if (typeof paymaster === "string" && !ZERO_ADDRESS.test(paymaster)) setRemoteAddress(attributes, ATTR_BLOCKCHAIN_USER_OPERATION_PAYMASTER, paymaster);
1126
+ setRemoteAddress(attributes, ATTR_BLOCKCHAIN_USER_OPERATION_ENTRY_POINT, receipt.entryPoint);
1127
+ const reason = receipt.revertReason;
1128
+ if (typeof reason === "string") attributes[ATTR_BLOCKCHAIN_TX_REVERT_REASON] = formatAddressesIn(reason, formatAddress);
1129
+ const bundle = receipt.transactionHash;
1130
+ if (typeof bundle === "string" && TX_HASH.test(bundle)) attributes[ATTR_BLOCKCHAIN_TX_HASH] = bundle;
1131
+ const block = smallQuantity(receipt.blockNumber);
1132
+ if (block !== void 0) attributes[ATTR_BLOCKCHAIN_BLOCK_NUMBER] = block;
1133
+ return attributes;
1134
+ };
1135
+ /** Opens the confirm span of a user operation. */
1136
+ const openUserOperationConfirm = (input, parentCtx) => {
1137
+ const { chainId, userOpHash } = input;
1138
+ const sent = userOperationLinks.get(chainId, userOpHash);
1139
+ const active = context.active();
1140
+ const parent = parentCtx ?? (trace.getSpan(active) ? active : sent?.parent ?? active);
1141
+ const attributes = baseAttributes(chainId, BLOCKCHAIN_OPERATION_NAME_VALUE_CONFIRM, parent);
1142
+ attributes[ATTR_BLOCKCHAIN_USER_OPERATION_HASH] = userOpHash;
1143
+ const span = getTracer().startSpan(`confirm ${chainId}`, {
1144
+ kind: SpanKind.CLIENT,
1145
+ attributes: redact(attributes),
1146
+ links: sent ? [{ context: sent.spanContext }] : [],
1147
+ ...input.startTime !== void 0 ? { startTime: input.startTime } : {}
1148
+ }, parent);
1149
+ const finish = finisher(span);
1150
+ const startMs = toEpochMs(input.startTime);
1151
+ const recordConfirmation = (endTime, outcome) => txMetrics.confirmationDuration(secondsSince(startMs, endTime), userOperationMetricAttributes(chainId, outcome));
1152
+ return {
1153
+ active: 0,
1154
+ ended: false,
1155
+ receipt: (receipt, endTime) => finish("record user operation receipt", () => {
1156
+ const attributes = userOperationReceiptAttributes(receipt ?? {});
1157
+ span.setAttributes(redact(attributes));
1158
+ const success = attributes[ATTR_BLOCKCHAIN_USER_OPERATION_SUCCESS];
1159
+ if (success === false) markError(span, BLOCKCHAIN_TX_STATUS_VALUE_REVERTED);
1160
+ const outcome = typeof success === "boolean" ? { [ATTR_BLOCKCHAIN_USER_OPERATION_SUCCESS]: success } : {};
1161
+ recordConfirmation(endTime, outcome);
1162
+ const cost = attributes[ATTR_BLOCKCHAIN_USER_OPERATION_GAS_COST];
1163
+ if (typeof cost === "string") txMetrics.fee(BigInt(cost), userOperationMetricAttributes(chainId, outcome));
1164
+ }, endTime),
1165
+ timeout: (endTime) => finish("record user operation confirmation timeout", () => recordConfirmation(endTime, { [ATTR_ERROR_TYPE]: markError(span, OBSERVER_TIMEOUT) }), endTime),
1166
+ fail: (error, read) => finish("record user operation confirmation failure", () => recordConfirmation(read.endTime, { [ATTR_ERROR_TYPE]: markError(span, reportedErrorType(error, read), error, errorType(error)) }), read.endTime)
1167
+ };
1168
+ };
1169
+ const startUserOperationConfirm = (input, parentCtx) => {
1170
+ const { chainId, userOpHash } = input;
1171
+ if (typeof userOpHash !== "string" || !TX_HASH.test(userOpHash)) {
1172
+ diag.debug("hashspan: not confirming a user operation without a valid hash");
1173
+ return NOOP_USER_OPERATION_CONFIRM;
1174
+ }
1175
+ const claim = joinConfirm(userOperationConfirmations, chainId, userOpHash, () => openUserOperationConfirm(input, parentCtx));
1176
+ if (!claim) return NOOP_USER_OPERATION_CONFIRM;
1177
+ const { shared } = claim;
1178
+ return {
1179
+ end: (receipt, second) => {
1180
+ const { endTime } = handleOptions(second);
1181
+ if (!claim.receive()) return;
1182
+ userOperationConfirmations.settle(chainId, userOpHash, shared);
1183
+ shared.receipt(receipt, endTime);
1184
+ },
1185
+ timeout: (second) => {
1186
+ const { endTime } = handleOptions(second);
1187
+ claim.withdraw(() => shared.timeout(endTime));
1188
+ },
1189
+ fail: (error, second) => {
1190
+ const read = handleOptions(second);
1191
+ claim.withdraw(() => shared.fail(error, read));
1192
+ }
1193
+ };
1194
+ };
864
1195
  return {
865
1196
  startSend: (input, parent) => safely("start send span", () => startSend(input, parent), noopSend(parent ?? context.active())),
866
1197
  startConfirm: (input, parent) => safely("start confirm span", () => startConfirm(input, parent), NOOP_CONFIRM),
867
- startPayment: (input, parent) => safely("start payment span", () => startPayment(input, parent), NOOP_PAYMENT)
1198
+ startPayment: (input, parent) => safely("start payment span", () => startPayment(input, parent), NOOP_PAYMENT),
1199
+ startUserOperationSend: (input, parent) => safely("start user operation send span", () => startUserOperationSend(input, parent), noopUserOperationSend(parent ?? context.active())),
1200
+ startUserOperationConfirm: (input, parent) => safely("start user operation confirm span", () => startUserOperationConfirm(input, parent), NOOP_USER_OPERATION_CONFIRM)
868
1201
  };
869
1202
  }
870
1203
  //#endregion
871
- export { ATTR_BLOCKCHAIN_BLOCK_NUMBER, ATTR_BLOCKCHAIN_CHAIN_ID, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR, ATTR_BLOCKCHAIN_OPERATION_NAME, ATTR_BLOCKCHAIN_PAYMENT_AMOUNT, ATTR_BLOCKCHAIN_PAYMENT_ASSET, ATTR_BLOCKCHAIN_PAYMENT_PAYER, ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL, ATTR_BLOCKCHAIN_PAYMENT_RECIPIENT, ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT, ATTR_BLOCKCHAIN_PAYMENT_STATUS, ATTR_BLOCKCHAIN_PAYMENT_VERIFIED, ATTR_BLOCKCHAIN_SYSTEM, ATTR_BLOCKCHAIN_TX_EFFECTIVE_GAS_PRICE, ATTR_BLOCKCHAIN_TX_FEE, ATTR_BLOCKCHAIN_TX_FROM, ATTR_BLOCKCHAIN_TX_GAS_USED, ATTR_BLOCKCHAIN_TX_HASH, ATTR_BLOCKCHAIN_TX_L1_FEE, ATTR_BLOCKCHAIN_TX_NONCE, ATTR_BLOCKCHAIN_TX_REPLACEMENT_HASH, ATTR_BLOCKCHAIN_TX_REPLACEMENT_REASON, ATTR_BLOCKCHAIN_TX_REVERT_REASON, ATTR_BLOCKCHAIN_TX_STATUS, ATTR_BLOCKCHAIN_TX_TO, ATTR_BLOCKCHAIN_TX_VALUE, ATTR_ERROR_TYPE, ATTR_GEN_AI_AGENT_ID, ATTR_GEN_AI_AGENT_NAME, ATTR_X402_RESOURCE, ATTR_X402_SCHEME, BLOCKCHAIN_OPERATION_NAME_VALUE_CONFIRM, BLOCKCHAIN_OPERATION_NAME_VALUE_PAYMENT, BLOCKCHAIN_OPERATION_NAME_VALUE_SEND, BLOCKCHAIN_PAYMENT_PROTOCOL_VALUE_X402, BLOCKCHAIN_PAYMENT_STATUS_VALUE_FAILED, BLOCKCHAIN_PAYMENT_STATUS_VALUE_PENDING, BLOCKCHAIN_PAYMENT_STATUS_VALUE_SETTLED, BLOCKCHAIN_SYSTEM_VALUE_EVM, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_CANCELLED, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_REPLACED, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_REPRICED, BLOCKCHAIN_TX_STATUS_VALUE_REPLACED, BLOCKCHAIN_TX_STATUS_VALUE_REVERTED, BLOCKCHAIN_TX_STATUS_VALUE_SUCCESS, BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT, ERROR_TYPE_VALUE_OTHER, VERSION, createTxTracker };
1204
+ export { ATTR_BLOCKCHAIN_BLOCK_NUMBER, ATTR_BLOCKCHAIN_CHAIN_ID, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR, ATTR_BLOCKCHAIN_OPERATION_NAME, ATTR_BLOCKCHAIN_OPERATION_SUBJECT, ATTR_BLOCKCHAIN_PAYMENT_AMOUNT, ATTR_BLOCKCHAIN_PAYMENT_ASSET, ATTR_BLOCKCHAIN_PAYMENT_PAYER, ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL, ATTR_BLOCKCHAIN_PAYMENT_RECIPIENT, ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT, ATTR_BLOCKCHAIN_PAYMENT_STATUS, ATTR_BLOCKCHAIN_PAYMENT_VERIFIED, ATTR_BLOCKCHAIN_SYSTEM, ATTR_BLOCKCHAIN_TX_EFFECTIVE_GAS_PRICE, ATTR_BLOCKCHAIN_TX_FEE, ATTR_BLOCKCHAIN_TX_FROM, ATTR_BLOCKCHAIN_TX_GAS_USED, ATTR_BLOCKCHAIN_TX_HASH, ATTR_BLOCKCHAIN_TX_L1_FEE, ATTR_BLOCKCHAIN_TX_NONCE, ATTR_BLOCKCHAIN_TX_REPLACEMENT_HASH, ATTR_BLOCKCHAIN_TX_REPLACEMENT_REASON, ATTR_BLOCKCHAIN_TX_REVERT_REASON, ATTR_BLOCKCHAIN_TX_STATUS, ATTR_BLOCKCHAIN_TX_TO, ATTR_BLOCKCHAIN_TX_VALUE, ATTR_BLOCKCHAIN_USER_OPERATION_CALL_COUNT, ATTR_BLOCKCHAIN_USER_OPERATION_ENTRY_POINT, ATTR_BLOCKCHAIN_USER_OPERATION_GAS_COST, ATTR_BLOCKCHAIN_USER_OPERATION_GAS_USED, ATTR_BLOCKCHAIN_USER_OPERATION_HASH, ATTR_BLOCKCHAIN_USER_OPERATION_NONCE, ATTR_BLOCKCHAIN_USER_OPERATION_PAYMASTER, ATTR_BLOCKCHAIN_USER_OPERATION_SENDER, ATTR_BLOCKCHAIN_USER_OPERATION_SUCCESS, ATTR_ERROR_TYPE, ATTR_GEN_AI_AGENT_ID, ATTR_GEN_AI_AGENT_NAME, ATTR_X402_RESOURCE, ATTR_X402_SCHEME, BLOCKCHAIN_OPERATION_NAME_VALUE_CONFIRM, BLOCKCHAIN_OPERATION_NAME_VALUE_PAYMENT, BLOCKCHAIN_OPERATION_NAME_VALUE_SEND, BLOCKCHAIN_OPERATION_SUBJECT_VALUE_USER_OPERATION, BLOCKCHAIN_PAYMENT_PROTOCOL_VALUE_X402, BLOCKCHAIN_PAYMENT_STATUS_VALUE_FAILED, BLOCKCHAIN_PAYMENT_STATUS_VALUE_PENDING, BLOCKCHAIN_PAYMENT_STATUS_VALUE_SETTLED, BLOCKCHAIN_SYSTEM_VALUE_EVM, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_CANCELLED, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_REPLACED, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_REPRICED, BLOCKCHAIN_TX_STATUS_VALUE_REPLACED, BLOCKCHAIN_TX_STATUS_VALUE_REVERTED, BLOCKCHAIN_TX_STATUS_VALUE_SUCCESS, BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT, ERROR_TYPE_VALUE_OTHER, METRIC_BLOCKCHAIN_CLIENT_CONFIRMATION_DURATION, METRIC_BLOCKCHAIN_CLIENT_FEE, METRIC_BLOCKCHAIN_CLIENT_SEND_DURATION, VERSION, createTxTracker };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hashspan/core",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Transaction lifecycle tracing for on-chain actions of AI agents, built on OpenTelemetry.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Selim Aytac",