@hashspan/core 0.6.0 → 0.7.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/README.md CHANGED
@@ -12,7 +12,7 @@ Payments that another party settles on chain, such as x402 payments, become a **
12
12
  of a `send` span.
13
13
 
14
14
  The core is library-agnostic and read-only: it never signs, sends or fetches anything. Adapters such as
15
- [`@hashspan/viem`](https://github.com/selimaytac/hashspan/tree/@hashspan/core@0.6.0/packages/viem) call it for you. Use the core directly to instrument any other send path.
15
+ [`@hashspan/viem`](https://github.com/selimaytac/hashspan/tree/@hashspan/core@0.7.0/packages/viem) call it for you. Use the core directly to instrument any other send path.
16
16
 
17
17
  ## Install
18
18
 
@@ -69,27 +69,28 @@ stays the class name.
69
69
  `tracker.startPayment({ chainId, protocol, payer, recipient, asset, amount })` records a payment that another
70
70
  party settles on chain, such as an x402 facilitator, as a `payment {chainId}` span; end it with
71
71
  `end({ status, hash })` or `fail(error)`. A settlement with a hash links the transaction's confirm span to the payment
72
- span ([ADR 0013](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md)).
72
+ span ([ADR 0013](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0013-x402-payments.md)).
73
73
 
74
74
  All three calls accept an explicit parent `Context` as a second argument. An integration that learns about a call only
75
75
  after it started can record it after the fact: pass `startTime` in the input and `endTime` in the options of the
76
- handle method, e.g. `send.end({ hash }, { endTime })` ([ADR 0009](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0009-telemetry-off-the-call-path.md)). Every method is safe to call: failures inside
76
+ handle method, e.g. `send.end({ hash }, { endTime })` ([ADR 0009](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0009-telemetry-off-the-call-path.md)). Every method is safe to call: failures inside
77
77
  the instrumentation are reported through `diag` and never thrown into your code. The positional forms of earlier
78
78
  releases, `send.end(hash, endTime)` and `send.fail(error, endTime, { errorType })`, still work and are deprecated
79
- until 1.0 ([ADR 0014](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md)).
79
+ until 1.0 ([ADR 0014](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0014-core-api-boundary.md)).
80
80
 
81
81
  ## Options
82
82
 
83
83
  | Option | Default | Description |
84
84
  |---|---|---|
85
85
  | `tracerProvider` | global provider | Tracer provider to use |
86
+ | `meterProvider` | global provider | Meter provider for the [metrics](#metrics) |
86
87
  | `address` | `'raw'` | `'raw'`, `'hashed'`, `'off'`, or `{ mode: 'hashed', hash: (address) => string }` |
87
- | `errorMessages` | `'off'` | What failed spans record about the error: `'off'` (type only), `'sanitized'` (first line, addresses per `address` mode, calldata removed; in `hashed` and `off` mode any hex longer than an address) or `'raw'` (full message and stack trace). `'raw'` can record RPC URLs that include API keys, as some libraries put the request URL in the message; `'sanitized'` keeps only the first line (viem puts the URL on a later line), which is best effort. See [ADR 0006](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0006-error-privacy.md) |
88
+ | `errorMessages` | `'off'` | What failed spans record about the error: `'off'` (type only), `'sanitized'` (first line, addresses per `address` mode, calldata removed; in `hashed` and `off` mode any hex longer than an address) or `'raw'` (full message and stack trace). `'raw'` can record RPC URLs that include API keys, as some libraries put the request URL in the message; `'sanitized'` keeps only the first line (viem puts the URL on a later line), which is best effort. See [ADR 0006](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0006-error-privacy.md) |
88
89
  | `paymentResource` | `'origin'` | How much of a paid resource's URL `x402.resource` records: `'origin'` (scheme, host and port; nothing for a resource that is not a URL), `'path'` (also the path, never the query string, fragment or user info) or `'off'`. Paths often carry user or account identifiers. At most 512 characters are recorded |
89
90
  | `recordFunctionArguments` | `false` | Record `functionArguments` as a JSON array in `blockchain.contract.function.arguments`: bigints as decimal strings, addresses per `address` mode (longer hex values become `<hex>` in `hashed` and `off` mode), at most 4096 characters. Reads only own enumerable data properties: `toJSON()` and getters are never called, so a `Date` records as `{}`; a Proxy's traps still run |
90
91
  | `agent` | none | Agent `{ id, name }`; a field set here always wins, unset fields come from the Baggage entries `gen_ai.agent.id` / `gen_ai.agent.name` |
91
92
  | `agentFromBaggage` | `true` | Read agent identity fields that `agent` leaves unset from Baggage; set to `false` in services that accept requests from outside their trust boundary |
92
- | `redact` | none | `(attributes) => attributes`, runs last on every attribute set, including exception event attributes; if it throws, only non-sensitive identifiers are kept |
93
+ | `redact` | none | `(attributes) => attributes`, runs last on every span attribute set, including exception event attributes, but not on [metrics](#metrics); if it throws, only non-sensitive identifiers are kept |
93
94
  | `linkTtlMs` | `600000` | How long a sent transaction can be linked from its confirmation |
94
95
  | `maxTrackedTransactions` | `10000` | Upper bound on transactions kept for linking |
95
96
 
@@ -99,7 +100,17 @@ Chain id, transaction hash, sender/recipient (per `address` mode), value, nonce,
99
100
  on confirmation, status, block number, gas used, effective gas price, L1 fee, total fee and revert reason. Decoded
100
101
  call arguments are recorded only with `recordFunctionArguments`, and error messages only with `errorMessages`.
101
102
  Attribute definitions:
102
- [docs/semconv.md](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md).
103
+ [docs/semconv.md](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md).
104
+
105
+ ## Metrics
106
+
107
+ With an OpenTelemetry metrics SDK set up (or `meterProvider`), the tracker records three histograms:
108
+ `blockchain.client.send.duration` and `blockchain.client.confirmation.duration` in seconds, and
109
+ `blockchain.client.fee` in wei. Their attributes are the chain and the outcome only, never an address, hash or
110
+ agent identity. The `redact` hook does not run on metrics: a fee it removes from spans is still recorded by
111
+ `blockchain.client.fee`. To keep a histogram out of your backend, drop it with a View of your metrics SDK (drop
112
+ aggregation). Definitions:
113
+ [docs/semconv.md](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md#metrics).
103
114
 
104
115
  ## Privacy notes
105
116
 
@@ -114,9 +125,9 @@ Attribute definitions:
114
125
  propagated, or strip the entries before outbound calls.
115
126
  - **Inbound Baggage can claim an identity.** A caller can send Baggage entries with any agent id. A field set in the
116
127
  `agent` option cannot be overridden that way; to ignore identity from Baggage entirely, set `agentFromBaggage: false`
117
- ([ADR 0011](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0011-agent-identity-precedence.md)).
118
- - The redaction hook (`redact`) runs last on every attribute set and on exception attributes; use it for anything
119
- else your policy forbids.
128
+ ([ADR 0011](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0011-agent-identity-precedence.md)).
129
+ - The redaction hook (`redact`) runs last on every span attribute set and on exception attributes; use it for
130
+ anything else your policy forbids. It does not run on [metrics](#metrics), which carry no address or hash.
120
131
  - **Your callbacks' errors go to the diagnostic logger.** If a custom `hash` function or the `redact` hook throws,
121
132
  its error object is logged through the OpenTelemetry `diag` logger, outside the address mode and the redaction
122
133
  hook. Errors of the instrumented call never are. Do not put sensitive values, such as the address being hashed,
package/dist/index.cjs CHANGED
@@ -6,7 +6,7 @@ const ATTR_GEN_AI_AGENT_NAME = "gen_ai.agent.name";
6
6
  /**
7
7
  * Agent identity as GenAI attributes. A field set in the static identity always wins; Baggage, which a remote caller
8
8
  * can set, only fills fields it leaves unset, and is not read at all with `fromBaggage` false
9
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0011-agent-identity-precedence.md).
9
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0011-agent-identity-precedence.md).
10
10
  */
11
11
  function agentAttributes(ctx, identity, fromBaggage = true) {
12
12
  const baggage = fromBaggage ? _opentelemetry_api.propagation.getBaggage(ctx) : void 0;
@@ -22,7 +22,7 @@ function agentAttributes(ctx, identity, fromBaggage = true) {
22
22
  /**
23
23
  * Attribute keys emitted by hashspan.
24
24
  *
25
- * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md for
25
+ * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md for
26
26
  * definitions and value types. These names are a public contract: changes follow the deprecation policy in AGENTS.md.
27
27
  */
28
28
  const ATTR_BLOCKCHAIN_SYSTEM = "blockchain.system";
@@ -46,12 +46,12 @@ const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME = "blockchain.contract.function.nam
46
46
  const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR = "blockchain.contract.function.selector";
47
47
  /**
48
48
  * Opt-in: decoded call arguments as a JSON array. See
49
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0004-privacy-defaults.md.
49
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0004-privacy-defaults.md.
50
50
  */
51
51
  const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS = "blockchain.contract.function.arguments";
52
52
  /**
53
53
  * Payments settled on chain by a party other than the agent, e.g. an x402 facilitator. See
54
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md.
54
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0013-x402-payments.md.
55
55
  */
56
56
  const ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL = "blockchain.payment.protocol";
57
57
  const ATTR_BLOCKCHAIN_PAYMENT_PAYER = "blockchain.payment.payer";
@@ -64,7 +64,7 @@ const ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT = "blockchain.payment.settled_amoun
64
64
  /**
65
65
  * Whether the settlement transaction's receipt carries the payment, as checked by the adapter; absent when no check
66
66
  * was possible. See
67
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0017-x402-payment-verification.md.
67
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0017-x402-payment-verification.md.
68
68
  */
69
69
  const ATTR_BLOCKCHAIN_PAYMENT_VERIFIED = "blockchain.payment.verified";
70
70
  /** x402's own payment fields. */
@@ -88,7 +88,7 @@ const BLOCKCHAIN_TX_STATUS_VALUE_REVERTED = "reverted";
88
88
  /**
89
89
  * @deprecated No longer recorded: a confirm span that gave up waiting records `error.type` `timeout` and no
90
90
  * `blockchain.tx.status`. The constant is removed in 1.0. See
91
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
91
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
92
92
  */
93
93
  const BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT = "timeout";
94
94
  const BLOCKCHAIN_TX_STATUS_VALUE_REPLACED = "replaced";
@@ -101,6 +101,83 @@ const ATTR_ERROR_TYPE = "error.type";
101
101
  /** Fallback {@link ATTR_ERROR_TYPE} value when the error has no name. */
102
102
  const ERROR_TYPE_VALUE_OTHER = "_OTHER";
103
103
  //#endregion
104
+ //#region src/metrics.ts
105
+ /** Duration of a send: from the start of the sending call until the hash is known or the call failed. */
106
+ const METRIC_BLOCKCHAIN_CLIENT_SEND_DURATION = "blockchain.client.send.duration";
107
+ /** Duration of a confirmation: from the start of the wait until the receipt, a timeout or a failure. */
108
+ const METRIC_BLOCKCHAIN_CLIENT_CONFIRMATION_DURATION = "blockchain.client.confirmation.duration";
109
+ /** Total fee of a mined transaction (execution fee plus L1 data fee), in the chain's smallest unit (wei). */
110
+ const METRIC_BLOCKCHAIN_CLIENT_FEE = "blockchain.client.fee";
111
+ const DURATION_BUCKETS = [
112
+ .05,
113
+ .1,
114
+ .25,
115
+ .5,
116
+ 1,
117
+ 2,
118
+ 5,
119
+ 10,
120
+ 20,
121
+ 30,
122
+ 60,
123
+ 120,
124
+ 300
125
+ ];
126
+ const FEE_BUCKETS = Array.from({ length: 11 }, (_, i) => 10 ** (i + 8));
127
+ /** Milliseconds since the epoch of a span time, as the OpenTelemetry API accepts it. */
128
+ function toEpochMs(time) {
129
+ if (time === void 0) return Date.now();
130
+ if (time instanceof Date) return time.getTime();
131
+ if (Array.isArray(time)) return time[0] * 1e3 + time[1] / 1e6;
132
+ if (typeof time !== "number") return Date.now();
133
+ const origin = globalThis.performance?.timeOrigin;
134
+ return typeof origin === "number" && time < origin ? origin + time : time;
135
+ }
136
+ function createTxMetrics(meterProvider, name, version) {
137
+ let histograms;
138
+ const get = () => {
139
+ histograms ??= (() => {
140
+ const meter = (meterProvider ?? _opentelemetry_api.metrics.getMeterProvider()).getMeter(name, version);
141
+ return {
142
+ send: meter.createHistogram(METRIC_BLOCKCHAIN_CLIENT_SEND_DURATION, {
143
+ unit: "s",
144
+ description: "Duration of sending a transaction, until its hash is known",
145
+ advice: { explicitBucketBoundaries: DURATION_BUCKETS }
146
+ }),
147
+ confirmation: meter.createHistogram(METRIC_BLOCKCHAIN_CLIENT_CONFIRMATION_DURATION, {
148
+ unit: "s",
149
+ description: "Duration of waiting for a transaction receipt",
150
+ advice: { explicitBucketBoundaries: DURATION_BUCKETS }
151
+ }),
152
+ fee: meter.createHistogram(METRIC_BLOCKCHAIN_CLIENT_FEE, {
153
+ unit: "{wei}",
154
+ description: "Total fee of a mined transaction",
155
+ advice: { explicitBucketBoundaries: FEE_BUCKETS }
156
+ })
157
+ };
158
+ })();
159
+ return histograms;
160
+ };
161
+ const record = (what, run) => {
162
+ try {
163
+ run();
164
+ } catch (error) {
165
+ _opentelemetry_api.diag.error(`hashspan: failed to record the ${what} metric`, error);
166
+ }
167
+ };
168
+ return {
169
+ sendDuration: (seconds, attributes) => record("send duration", () => {
170
+ if (seconds >= 0) get().send.record(seconds, attributes);
171
+ }),
172
+ confirmationDuration: (seconds, attributes) => record("confirmation duration", () => {
173
+ if (seconds >= 0) get().confirmation.record(seconds, attributes);
174
+ }),
175
+ fee: (wei, attributes) => record("fee", () => {
176
+ if (wei >= 0n) get().fee.record(Number(wei), attributes);
177
+ })
178
+ };
179
+ }
180
+ //#endregion
104
181
  //#region src/confirm-registry.ts
105
182
  /**
106
183
  * Bounded registry from (chainId, tx hash) to its in-flight confirm span, or to "settled" for a while after a
@@ -386,7 +463,7 @@ function sanitizeResource(resource) {
386
463
  }
387
464
  //#endregion
388
465
  //#region src/version.ts
389
- const VERSION = "0.6.0";
466
+ const VERSION = "0.7.0";
390
467
  //#endregion
391
468
  //#region src/tracker.ts
392
469
  const INSTRUMENTATION_NAME = "@hashspan/core";
@@ -513,7 +590,7 @@ function reportedErrorType(error, options) {
513
590
  }
514
591
  /**
515
592
  * Creates a tracker that records transactions as `send` and `confirm` spans, and payments as `payment` spans, with
516
- * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md). It makes
593
+ * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md). It makes
517
594
  * no network calls; the caller passes hashes and receipts. Its methods and handles never throw: failures are logged
518
595
  * via `diag`, and a method that fails returns a handle that records nothing.
519
596
  */
@@ -529,6 +606,14 @@ function createTxTracker(options = {}) {
529
606
  const formatAddress = safely("configure address mode", () => resolveAddressFormatter(options.address), OFF_ADDRESS_FORMATTER);
530
607
  const errorMessages = safely("configure error message mode", () => resolveErrorMessageMode(options.errorMessages), "off");
531
608
  const paymentResource = safely("configure payment resource mode", () => resolvePaymentResourceMode(options.paymentResource), "off");
609
+ const txMetrics = createTxMetrics(options.meterProvider, INSTRUMENTATION_NAME, VERSION);
610
+ /** Attributes of a metric: low-cardinality only, never an address, hash or agent identity. */
611
+ const metricAttributes = (chainId, extra = {}) => ({
612
+ [ATTR_BLOCKCHAIN_SYSTEM]: "evm",
613
+ [ATTR_BLOCKCHAIN_CHAIN_ID]: chainId,
614
+ ...extra
615
+ });
616
+ const secondsSince = (startMs, endTime) => (toEpochMs(endTime) - startMs) / 1e3;
532
617
  let tracer;
533
618
  const getTracer = () => {
534
619
  tracer ??= (options.tracerProvider ?? _opentelemetry_api.trace.getTracerProvider()).getTracer(INSTRUMENTATION_NAME, VERSION);
@@ -553,7 +638,7 @@ function createTxTracker(options = {}) {
553
638
  /**
554
639
  * Exception event attributes for `error`, per the error message mode. The error object itself is never handed to
555
640
  * the SDK: its message and stack can carry addresses and calldata
556
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0006-error-privacy.md).
641
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0006-error-privacy.md).
557
642
  */
558
643
  const exceptionAttributes = (type, error) => {
559
644
  const attributes = { [ATTR_EXCEPTION_TYPE]: type };
@@ -586,6 +671,7 @@ function createTxTracker(options = {}) {
586
671
  code: _opentelemetry_api.SpanStatusCode.ERROR,
587
672
  ...message !== void 0 ? { message } : {}
588
673
  });
674
+ return type;
589
675
  };
590
676
  /** Ends a span exactly once; the span is always ended even if recording attributes fails. */
591
677
  const finisher = (span) => {
@@ -637,6 +723,8 @@ function createTxTracker(options = {}) {
637
723
  ...input.startTime !== void 0 ? { startTime: input.startTime } : {}
638
724
  }, parent);
639
725
  const finish = finisher(span);
726
+ const startMs = toEpochMs(input.startTime);
727
+ const recordSend = (endTime, errorType) => txMetrics.sendDuration(secondsSince(startMs, endTime), metricAttributes(input.chainId, errorType === void 0 ? {} : { [ATTR_ERROR_TYPE]: errorType }));
640
728
  return {
641
729
  context: _opentelemetry_api.trace.setSpan(parent, span),
642
730
  end: (result, second) => finish("record transaction hash", () => {
@@ -650,10 +738,11 @@ function createTxTracker(options = {}) {
650
738
  parent
651
739
  });
652
740
  span.setAttributes(redact({ [ATTR_BLOCKCHAIN_TX_HASH]: hash }));
741
+ recordSend(handleOptions(second).endTime);
653
742
  }, handleOptions(second).endTime),
654
743
  fail: (error, second, third) => {
655
744
  const options = handleOptions(second, third);
656
- finish("record send failure", () => markError(span, reportedErrorType(error, options), error, errorType(error)), options.endTime);
745
+ finish("record send failure", () => recordSend(options.endTime, markError(span, reportedErrorType(error, options), error, errorType(error))), options.endTime);
657
746
  }
658
747
  };
659
748
  };
@@ -689,6 +778,8 @@ function createTxTracker(options = {}) {
689
778
  ...explicitStart !== void 0 ? { startTime: explicitStart } : {}
690
779
  }, parent);
691
780
  const finish = finisher(span);
781
+ const startMs = toEpochMs(explicitStart);
782
+ const recordConfirmation = (endTime, outcome) => txMetrics.confirmationDuration(secondsSince(startMs, endTime), metricAttributes(input.chainId, outcome));
692
783
  return {
693
784
  active: 0,
694
785
  ended: false,
@@ -698,11 +789,16 @@ function createTxTracker(options = {}) {
698
789
  links: [{ context: span.spanContext() }, ...sent ? [{ context: sent.spanContext }] : []]
699
790
  },
700
791
  receipt: (receipt, endTime) => finish("record receipt", () => {
701
- span.setAttributes(redact(receiptAttributes(receipt)));
792
+ const attributes = receiptAttributes(receipt);
793
+ span.setAttributes(redact(attributes));
702
794
  if (receipt.status === "reverted") markError(span, BLOCKCHAIN_TX_STATUS_VALUE_REVERTED);
795
+ const status = { [ATTR_BLOCKCHAIN_TX_STATUS]: attributes[ATTR_BLOCKCHAIN_TX_STATUS] };
796
+ recordConfirmation(endTime, status);
797
+ const fee = attributes[ATTR_BLOCKCHAIN_TX_FEE];
798
+ if (typeof fee === "string") txMetrics.fee(BigInt(fee), metricAttributes(input.chainId, status));
703
799
  }, endTime),
704
- timeout: (endTime) => finish("record confirmation timeout", () => markError(span, OBSERVER_TIMEOUT), endTime),
705
- fail: (error, endTime) => finish("record confirmation failure", () => markError(span, errorType(error), error), endTime),
800
+ timeout: (endTime) => finish("record confirmation timeout", () => recordConfirmation(endTime, { [ATTR_ERROR_TYPE]: markError(span, OBSERVER_TIMEOUT) }), endTime),
801
+ fail: (error, endTime) => finish("record confirmation failure", () => recordConfirmation(endTime, { [ATTR_ERROR_TYPE]: markError(span, errorType(error), error) }), endTime),
706
802
  replaced: (hash, reason, endTime) => finish("record replacement", () => {
707
803
  const attributes = {
708
804
  [ATTR_BLOCKCHAIN_TX_STATUS]: BLOCKCHAIN_TX_STATUS_VALUE_REPLACED,
@@ -710,8 +806,9 @@ function createTxTracker(options = {}) {
710
806
  };
711
807
  if (reason !== void 0 && REPLACEMENT_REASONS.has(reason)) attributes[ATTR_BLOCKCHAIN_TX_REPLACEMENT_REASON] = reason;
712
808
  span.setAttributes(redact(attributes));
809
+ recordConfirmation(endTime, { [ATTR_BLOCKCHAIN_TX_STATUS]: BLOCKCHAIN_TX_STATUS_VALUE_REPLACED });
713
810
  }, endTime),
714
- unattributable: (endTime) => finish("record unattributable receipt", () => markError(span, ERROR_TYPE_VALUE_OTHER), endTime)
811
+ unattributable: (endTime) => finish("record unattributable receipt", () => recordConfirmation(endTime, { [ATTR_ERROR_TYPE]: markError(span, ERROR_TYPE_VALUE_OTHER) }), endTime)
715
812
  };
716
813
  };
717
814
  /**
@@ -733,7 +830,7 @@ function createTxTracker(options = {}) {
733
830
  };
734
831
  /**
735
832
  * Ends `shared` with `receipt`, attributing it to the transaction that was mined
736
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0008-replaced-transactions.md).
833
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0008-replaced-transactions.md).
737
834
  */
738
835
  const endWithReceipt = (chainId, hash, shared, receipt, endTime) => {
739
836
  const mined = receipt.transactionHash;
@@ -918,5 +1015,8 @@ exports.BLOCKCHAIN_TX_STATUS_VALUE_REVERTED = BLOCKCHAIN_TX_STATUS_VALUE_REVERTE
918
1015
  exports.BLOCKCHAIN_TX_STATUS_VALUE_SUCCESS = BLOCKCHAIN_TX_STATUS_VALUE_SUCCESS;
919
1016
  exports.BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT = BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT;
920
1017
  exports.ERROR_TYPE_VALUE_OTHER = ERROR_TYPE_VALUE_OTHER;
1018
+ exports.METRIC_BLOCKCHAIN_CLIENT_CONFIRMATION_DURATION = METRIC_BLOCKCHAIN_CLIENT_CONFIRMATION_DURATION;
1019
+ exports.METRIC_BLOCKCHAIN_CLIENT_FEE = METRIC_BLOCKCHAIN_CLIENT_FEE;
1020
+ exports.METRIC_BLOCKCHAIN_CLIENT_SEND_DURATION = METRIC_BLOCKCHAIN_CLIENT_SEND_DURATION;
921
1021
  exports.VERSION = VERSION;
922
1022
  exports.createTxTracker = createTxTracker;
package/dist/index.d.cts CHANGED
@@ -1,13 +1,13 @@
1
- import { Attributes, Context, TimeInput, TracerProvider } from "@opentelemetry/api";
1
+ import { Attributes, Context, MeterProvider, TimeInput, TracerProvider } from "@opentelemetry/api";
2
2
  //#region src/types.d.ts
3
3
  /**
4
4
  * How wallet addresses are recorded. See
5
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0004-privacy-defaults.md.
5
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0004-privacy-defaults.md.
6
6
  */
7
7
  type AddressMode = "raw" | "hashed" | "off";
8
8
  /**
9
9
  * How error messages are recorded on exception events and span status. See
10
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0006-error-privacy.md.
10
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0006-error-privacy.md.
11
11
  * - `off`: error type only
12
12
  * - `sanitized`: first line, addresses per address mode, other long hex data removed
13
13
  * - `raw`: full message and stack trace, as thrown
@@ -43,6 +43,11 @@ interface AgentIdentity {
43
43
  interface TxTrackerOptions {
44
44
  /** Defaults to the globally registered tracer provider. */
45
45
  tracerProvider?: TracerProvider | undefined;
46
+ /**
47
+ * Meter provider for the send, confirmation and fee histograms. Defaults to the globally registered one, which
48
+ * records nothing until an OpenTelemetry metrics SDK is set up.
49
+ */
50
+ meterProvider?: MeterProvider | undefined;
46
51
  /** Address recording mode. Default: `raw`. */
47
52
  address?: AddressMode | AddressOptions | undefined;
48
53
  /**
@@ -61,7 +66,7 @@ interface TxTrackerOptions {
61
66
  /**
62
67
  * Agent identity. A field set here always wins; fields left unset are taken from the Baggage entries
63
68
  * `gen_ai.agent.id` / `gen_ai.agent.name` unless `agentFromBaggage` is false
64
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0011-agent-identity-precedence.md).
69
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0011-agent-identity-precedence.md).
65
70
  */
66
71
  agent?: AgentIdentity | undefined;
67
72
  /**
@@ -103,7 +108,7 @@ interface SendInput {
103
108
  functionArguments?: readonly unknown[] | undefined;
104
109
  /**
105
110
  * When the send started, for adapters that record it after the fact
106
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0009-telemetry-off-the-call-path.md).
111
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0009-telemetry-off-the-call-path.md).
107
112
  * Omit it otherwise: with an explicit start time, the SDK measures the span by the wall clock, so pass the end time
108
113
  * to the handle too.
109
114
  */
@@ -112,14 +117,14 @@ interface SendInput {
112
117
  /**
113
118
  * Ends a send span. Only the first call counts; methods never throw.
114
119
  * Produced by the tracker only; methods may be added in minor releases
115
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
120
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0014-core-api-boundary.md).
116
121
  */
117
122
  interface SendHandle {
118
123
  /**
119
124
  * The parent context with the send span set. Run the call that sends the transaction in it, e.g.
120
125
  * `await context.with(send.context, () => sendSomehow())`, so that spans of wallet, RPC or HTTP instrumentation
121
126
  * nest under the send span
122
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0015-send-span-as-active-context.md).
127
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0015-send-span-as-active-context.md).
123
128
  * Run only that call in it: a confirm span started in it becomes a child of the send span.
124
129
  */
125
130
  readonly context: Context;
@@ -182,7 +187,7 @@ interface ReceiptLike {
182
187
  /**
183
188
  * Hash of the mined transaction. When it differs from the awaited hash, the awaited transaction was replaced: its
184
189
  * confirm span ends as `replaced` and the receipt is recorded for this hash
185
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0008-replaced-transactions.md).
190
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0008-replaced-transactions.md).
186
191
  */
187
192
  transactionHash?: string | undefined;
188
193
  /** Replacement reason reported by the library, when {@link transactionHash} differs from the awaited hash. */
@@ -192,7 +197,7 @@ interface ReceiptLike {
192
197
  * One wait for a transaction's receipt, joined to the transaction's shared confirm span. Only the first call counts;
193
198
  * methods never throw.
194
199
  * Produced by the tracker only; methods may be added in minor releases
195
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
200
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0014-core-api-boundary.md).
196
201
  */
197
202
  interface ConfirmHandle {
198
203
  /** Ends the shared confirm span with the receipt, for every handle of the transaction. */
@@ -216,7 +221,7 @@ interface ConfirmHandle {
216
221
  }
217
222
  /**
218
223
  * A payment the agent authorizes and another party settles on chain, e.g. an x402 facilitator
219
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md).
224
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0013-x402-payments.md).
220
225
  * Values often come from a remote server: addresses, amounts and identifiers that are malformed are not recorded.
221
226
  */
222
227
  interface PaymentInput {
@@ -268,7 +273,7 @@ interface PaymentSettlement {
268
273
  /**
269
274
  * Ends a payment span. Only the first call counts; methods never throw.
270
275
  * Produced by the tracker only; methods may be added in minor releases
271
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
276
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0014-core-api-boundary.md).
272
277
  */
273
278
  interface PaymentHandle {
274
279
  /** Ends the payment span with its settlement. */
@@ -300,7 +305,7 @@ export declare const ATTR_GEN_AI_AGENT_NAME: "gen_ai.agent.name";
300
305
  /**
301
306
  * Attribute keys emitted by hashspan.
302
307
  *
303
- * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md for
308
+ * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md for
304
309
  * definitions and value types. These names are a public contract: changes follow the deprecation policy in AGENTS.md.
305
310
  */
306
311
  export declare const ATTR_BLOCKCHAIN_SYSTEM: "blockchain.system";
@@ -324,12 +329,12 @@ export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME: "blockchain.contrac
324
329
  export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR: "blockchain.contract.function.selector";
325
330
  /**
326
331
  * Opt-in: decoded call arguments as a JSON array. See
327
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0004-privacy-defaults.md.
332
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0004-privacy-defaults.md.
328
333
  */
329
334
  export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS: "blockchain.contract.function.arguments";
330
335
  /**
331
336
  * Payments settled on chain by a party other than the agent, e.g. an x402 facilitator. See
332
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md.
337
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0013-x402-payments.md.
333
338
  */
334
339
  export declare const ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL: "blockchain.payment.protocol";
335
340
  export declare const ATTR_BLOCKCHAIN_PAYMENT_PAYER: "blockchain.payment.payer";
@@ -342,7 +347,7 @@ export declare const ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT: "blockchain.payment
342
347
  /**
343
348
  * Whether the settlement transaction's receipt carries the payment, as checked by the adapter; absent when no check
344
349
  * was possible. See
345
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0017-x402-payment-verification.md.
350
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0017-x402-payment-verification.md.
346
351
  */
347
352
  export declare const ATTR_BLOCKCHAIN_PAYMENT_VERIFIED: "blockchain.payment.verified";
348
353
  /** x402's own payment fields. */
@@ -366,7 +371,7 @@ export declare const BLOCKCHAIN_TX_STATUS_VALUE_REVERTED: "reverted";
366
371
  /**
367
372
  * @deprecated No longer recorded: a confirm span that gave up waiting records `error.type` `timeout` and no
368
373
  * `blockchain.tx.status`. The constant is removed in 1.0. See
369
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
374
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
370
375
  */
371
376
  export declare const BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT: "timeout";
372
377
  export declare const BLOCKCHAIN_TX_STATUS_VALUE_REPLACED: "replaced";
@@ -379,11 +384,19 @@ export declare const ATTR_ERROR_TYPE: "error.type";
379
384
  /** Fallback {@link ATTR_ERROR_TYPE} value when the error has no name. */
380
385
  export declare const ERROR_TYPE_VALUE_OTHER: "_OTHER";
381
386
  //#endregion
387
+ //#region src/metrics.d.ts
388
+ /** Duration of a send: from the start of the sending call until the hash is known or the call failed. */
389
+ export declare const METRIC_BLOCKCHAIN_CLIENT_SEND_DURATION: "blockchain.client.send.duration";
390
+ /** Duration of a confirmation: from the start of the wait until the receipt, a timeout or a failure. */
391
+ export declare const METRIC_BLOCKCHAIN_CLIENT_CONFIRMATION_DURATION: "blockchain.client.confirmation.duration";
392
+ /** Total fee of a mined transaction (execution fee plus L1 data fee), in the chain's smallest unit (wei). */
393
+ export declare const METRIC_BLOCKCHAIN_CLIENT_FEE: "blockchain.client.fee";
394
+ //#endregion
382
395
  //#region src/tracker.d.ts
383
396
  /**
384
397
  * Records transactions and payments as spans. Obtain one from {@link createTxTracker}: it is not meant to be
385
398
  * implemented, and members may be added to it and to its handles in minor releases
386
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
399
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0014-core-api-boundary.md).
387
400
  */
388
401
  interface TxTracker {
389
402
  /**
@@ -401,7 +414,7 @@ interface TxTracker {
401
414
  /**
402
415
  * Starts a `payment` span as a child of `parent` (default: the active context), for a payment that another party
403
416
  * settles on chain
404
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md). Call
417
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0013-x402-payments.md). Call
405
418
  * `end(settlement)` with the settlement, or `fail(error)`. A settlement with a hash links the transaction's confirm
406
419
  * span to this span, as a send span would.
407
420
  */
@@ -409,7 +422,7 @@ interface TxTracker {
409
422
  }
410
423
  /**
411
424
  * Creates a tracker that records transactions as `send` and `confirm` spans, and payments as `payment` spans, with
412
- * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md). It makes
425
+ * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md). It makes
413
426
  * no network calls; the caller passes hashes and receipts. Its methods and handles never throw: failures are logged
414
427
  * via `diag`, and a method that fails returns a handle that records nothing.
415
428
  */
package/dist/index.d.mts CHANGED
@@ -1,13 +1,13 @@
1
- import { Attributes, Context, TimeInput, TracerProvider } from "@opentelemetry/api";
1
+ import { Attributes, Context, MeterProvider, TimeInput, TracerProvider } from "@opentelemetry/api";
2
2
  //#region src/types.d.ts
3
3
  /**
4
4
  * How wallet addresses are recorded. See
5
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0004-privacy-defaults.md.
5
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0004-privacy-defaults.md.
6
6
  */
7
7
  type AddressMode = "raw" | "hashed" | "off";
8
8
  /**
9
9
  * How error messages are recorded on exception events and span status. See
10
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0006-error-privacy.md.
10
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0006-error-privacy.md.
11
11
  * - `off`: error type only
12
12
  * - `sanitized`: first line, addresses per address mode, other long hex data removed
13
13
  * - `raw`: full message and stack trace, as thrown
@@ -43,6 +43,11 @@ interface AgentIdentity {
43
43
  interface TxTrackerOptions {
44
44
  /** Defaults to the globally registered tracer provider. */
45
45
  tracerProvider?: TracerProvider | undefined;
46
+ /**
47
+ * Meter provider for the send, confirmation and fee histograms. Defaults to the globally registered one, which
48
+ * records nothing until an OpenTelemetry metrics SDK is set up.
49
+ */
50
+ meterProvider?: MeterProvider | undefined;
46
51
  /** Address recording mode. Default: `raw`. */
47
52
  address?: AddressMode | AddressOptions | undefined;
48
53
  /**
@@ -61,7 +66,7 @@ interface TxTrackerOptions {
61
66
  /**
62
67
  * Agent identity. A field set here always wins; fields left unset are taken from the Baggage entries
63
68
  * `gen_ai.agent.id` / `gen_ai.agent.name` unless `agentFromBaggage` is false
64
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0011-agent-identity-precedence.md).
69
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0011-agent-identity-precedence.md).
65
70
  */
66
71
  agent?: AgentIdentity | undefined;
67
72
  /**
@@ -103,7 +108,7 @@ interface SendInput {
103
108
  functionArguments?: readonly unknown[] | undefined;
104
109
  /**
105
110
  * When the send started, for adapters that record it after the fact
106
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0009-telemetry-off-the-call-path.md).
111
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0009-telemetry-off-the-call-path.md).
107
112
  * Omit it otherwise: with an explicit start time, the SDK measures the span by the wall clock, so pass the end time
108
113
  * to the handle too.
109
114
  */
@@ -112,14 +117,14 @@ interface SendInput {
112
117
  /**
113
118
  * Ends a send span. Only the first call counts; methods never throw.
114
119
  * Produced by the tracker only; methods may be added in minor releases
115
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
120
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0014-core-api-boundary.md).
116
121
  */
117
122
  interface SendHandle {
118
123
  /**
119
124
  * The parent context with the send span set. Run the call that sends the transaction in it, e.g.
120
125
  * `await context.with(send.context, () => sendSomehow())`, so that spans of wallet, RPC or HTTP instrumentation
121
126
  * nest under the send span
122
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0015-send-span-as-active-context.md).
127
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0015-send-span-as-active-context.md).
123
128
  * Run only that call in it: a confirm span started in it becomes a child of the send span.
124
129
  */
125
130
  readonly context: Context;
@@ -182,7 +187,7 @@ interface ReceiptLike {
182
187
  /**
183
188
  * Hash of the mined transaction. When it differs from the awaited hash, the awaited transaction was replaced: its
184
189
  * confirm span ends as `replaced` and the receipt is recorded for this hash
185
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0008-replaced-transactions.md).
190
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0008-replaced-transactions.md).
186
191
  */
187
192
  transactionHash?: string | undefined;
188
193
  /** Replacement reason reported by the library, when {@link transactionHash} differs from the awaited hash. */
@@ -192,7 +197,7 @@ interface ReceiptLike {
192
197
  * One wait for a transaction's receipt, joined to the transaction's shared confirm span. Only the first call counts;
193
198
  * methods never throw.
194
199
  * Produced by the tracker only; methods may be added in minor releases
195
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
200
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0014-core-api-boundary.md).
196
201
  */
197
202
  interface ConfirmHandle {
198
203
  /** Ends the shared confirm span with the receipt, for every handle of the transaction. */
@@ -216,7 +221,7 @@ interface ConfirmHandle {
216
221
  }
217
222
  /**
218
223
  * A payment the agent authorizes and another party settles on chain, e.g. an x402 facilitator
219
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md).
224
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0013-x402-payments.md).
220
225
  * Values often come from a remote server: addresses, amounts and identifiers that are malformed are not recorded.
221
226
  */
222
227
  interface PaymentInput {
@@ -268,7 +273,7 @@ interface PaymentSettlement {
268
273
  /**
269
274
  * Ends a payment span. Only the first call counts; methods never throw.
270
275
  * Produced by the tracker only; methods may be added in minor releases
271
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
276
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0014-core-api-boundary.md).
272
277
  */
273
278
  interface PaymentHandle {
274
279
  /** Ends the payment span with its settlement. */
@@ -300,7 +305,7 @@ export declare const ATTR_GEN_AI_AGENT_NAME: "gen_ai.agent.name";
300
305
  /**
301
306
  * Attribute keys emitted by hashspan.
302
307
  *
303
- * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md for
308
+ * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md for
304
309
  * definitions and value types. These names are a public contract: changes follow the deprecation policy in AGENTS.md.
305
310
  */
306
311
  export declare const ATTR_BLOCKCHAIN_SYSTEM: "blockchain.system";
@@ -324,12 +329,12 @@ export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME: "blockchain.contrac
324
329
  export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR: "blockchain.contract.function.selector";
325
330
  /**
326
331
  * Opt-in: decoded call arguments as a JSON array. See
327
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0004-privacy-defaults.md.
332
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0004-privacy-defaults.md.
328
333
  */
329
334
  export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS: "blockchain.contract.function.arguments";
330
335
  /**
331
336
  * Payments settled on chain by a party other than the agent, e.g. an x402 facilitator. See
332
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md.
337
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0013-x402-payments.md.
333
338
  */
334
339
  export declare const ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL: "blockchain.payment.protocol";
335
340
  export declare const ATTR_BLOCKCHAIN_PAYMENT_PAYER: "blockchain.payment.payer";
@@ -342,7 +347,7 @@ export declare const ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT: "blockchain.payment
342
347
  /**
343
348
  * Whether the settlement transaction's receipt carries the payment, as checked by the adapter; absent when no check
344
349
  * was possible. See
345
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0017-x402-payment-verification.md.
350
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0017-x402-payment-verification.md.
346
351
  */
347
352
  export declare const ATTR_BLOCKCHAIN_PAYMENT_VERIFIED: "blockchain.payment.verified";
348
353
  /** x402's own payment fields. */
@@ -366,7 +371,7 @@ export declare const BLOCKCHAIN_TX_STATUS_VALUE_REVERTED: "reverted";
366
371
  /**
367
372
  * @deprecated No longer recorded: a confirm span that gave up waiting records `error.type` `timeout` and no
368
373
  * `blockchain.tx.status`. The constant is removed in 1.0. See
369
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
374
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
370
375
  */
371
376
  export declare const BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT: "timeout";
372
377
  export declare const BLOCKCHAIN_TX_STATUS_VALUE_REPLACED: "replaced";
@@ -379,11 +384,19 @@ export declare const ATTR_ERROR_TYPE: "error.type";
379
384
  /** Fallback {@link ATTR_ERROR_TYPE} value when the error has no name. */
380
385
  export declare const ERROR_TYPE_VALUE_OTHER: "_OTHER";
381
386
  //#endregion
387
+ //#region src/metrics.d.ts
388
+ /** Duration of a send: from the start of the sending call until the hash is known or the call failed. */
389
+ export declare const METRIC_BLOCKCHAIN_CLIENT_SEND_DURATION: "blockchain.client.send.duration";
390
+ /** Duration of a confirmation: from the start of the wait until the receipt, a timeout or a failure. */
391
+ export declare const METRIC_BLOCKCHAIN_CLIENT_CONFIRMATION_DURATION: "blockchain.client.confirmation.duration";
392
+ /** Total fee of a mined transaction (execution fee plus L1 data fee), in the chain's smallest unit (wei). */
393
+ export declare const METRIC_BLOCKCHAIN_CLIENT_FEE: "blockchain.client.fee";
394
+ //#endregion
382
395
  //#region src/tracker.d.ts
383
396
  /**
384
397
  * Records transactions and payments as spans. Obtain one from {@link createTxTracker}: it is not meant to be
385
398
  * implemented, and members may be added to it and to its handles in minor releases
386
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
399
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0014-core-api-boundary.md).
387
400
  */
388
401
  interface TxTracker {
389
402
  /**
@@ -401,7 +414,7 @@ interface TxTracker {
401
414
  /**
402
415
  * Starts a `payment` span as a child of `parent` (default: the active context), for a payment that another party
403
416
  * settles on chain
404
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md). Call
417
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0013-x402-payments.md). Call
405
418
  * `end(settlement)` with the settlement, or `fail(error)`. A settlement with a hash links the transaction's confirm
406
419
  * span to this span, as a send span would.
407
420
  */
@@ -409,7 +422,7 @@ interface TxTracker {
409
422
  }
410
423
  /**
411
424
  * Creates a tracker that records transactions as `send` and `confirm` spans, and payments as `payment` spans, with
412
- * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md). It makes
425
+ * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md). It makes
413
426
  * no network calls; the caller passes hashes and receipts. Its methods and handles never throw: failures are logged
414
427
  * via `diag`, and a method that fails returns a handle that records nothing.
415
428
  */
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.7.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.7.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.7.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.7.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,7 +63,7 @@ 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.7.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. */
@@ -87,7 +87,7 @@ const BLOCKCHAIN_TX_STATUS_VALUE_REVERTED = "reverted";
87
87
  /**
88
88
  * @deprecated No longer recorded: a confirm span that gave up waiting records `error.type` `timeout` and no
89
89
  * `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.
90
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
91
91
  */
92
92
  const BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT = "timeout";
93
93
  const BLOCKCHAIN_TX_STATUS_VALUE_REPLACED = "replaced";
@@ -100,6 +100,83 @@ const ATTR_ERROR_TYPE = "error.type";
100
100
  /** Fallback {@link ATTR_ERROR_TYPE} value when the error has no name. */
101
101
  const ERROR_TYPE_VALUE_OTHER = "_OTHER";
102
102
  //#endregion
103
+ //#region src/metrics.ts
104
+ /** Duration of a send: from the start of the sending call until the hash is known or the call failed. */
105
+ const METRIC_BLOCKCHAIN_CLIENT_SEND_DURATION = "blockchain.client.send.duration";
106
+ /** Duration of a confirmation: from the start of the wait until the receipt, a timeout or a failure. */
107
+ const METRIC_BLOCKCHAIN_CLIENT_CONFIRMATION_DURATION = "blockchain.client.confirmation.duration";
108
+ /** Total fee of a mined transaction (execution fee plus L1 data fee), in the chain's smallest unit (wei). */
109
+ const METRIC_BLOCKCHAIN_CLIENT_FEE = "blockchain.client.fee";
110
+ const DURATION_BUCKETS = [
111
+ .05,
112
+ .1,
113
+ .25,
114
+ .5,
115
+ 1,
116
+ 2,
117
+ 5,
118
+ 10,
119
+ 20,
120
+ 30,
121
+ 60,
122
+ 120,
123
+ 300
124
+ ];
125
+ const FEE_BUCKETS = Array.from({ length: 11 }, (_, i) => 10 ** (i + 8));
126
+ /** Milliseconds since the epoch of a span time, as the OpenTelemetry API accepts it. */
127
+ function toEpochMs(time) {
128
+ if (time === void 0) return Date.now();
129
+ if (time instanceof Date) return time.getTime();
130
+ if (Array.isArray(time)) return time[0] * 1e3 + time[1] / 1e6;
131
+ if (typeof time !== "number") return Date.now();
132
+ const origin = globalThis.performance?.timeOrigin;
133
+ return typeof origin === "number" && time < origin ? origin + time : time;
134
+ }
135
+ function createTxMetrics(meterProvider, name, version) {
136
+ let histograms;
137
+ const get = () => {
138
+ histograms ??= (() => {
139
+ const meter = (meterProvider ?? metrics.getMeterProvider()).getMeter(name, version);
140
+ return {
141
+ send: meter.createHistogram(METRIC_BLOCKCHAIN_CLIENT_SEND_DURATION, {
142
+ unit: "s",
143
+ description: "Duration of sending a transaction, until its hash is known",
144
+ advice: { explicitBucketBoundaries: DURATION_BUCKETS }
145
+ }),
146
+ confirmation: meter.createHistogram(METRIC_BLOCKCHAIN_CLIENT_CONFIRMATION_DURATION, {
147
+ unit: "s",
148
+ description: "Duration of waiting for a transaction receipt",
149
+ advice: { explicitBucketBoundaries: DURATION_BUCKETS }
150
+ }),
151
+ fee: meter.createHistogram(METRIC_BLOCKCHAIN_CLIENT_FEE, {
152
+ unit: "{wei}",
153
+ description: "Total fee of a mined transaction",
154
+ advice: { explicitBucketBoundaries: FEE_BUCKETS }
155
+ })
156
+ };
157
+ })();
158
+ return histograms;
159
+ };
160
+ const record = (what, run) => {
161
+ try {
162
+ run();
163
+ } catch (error) {
164
+ diag.error(`hashspan: failed to record the ${what} metric`, error);
165
+ }
166
+ };
167
+ return {
168
+ sendDuration: (seconds, attributes) => record("send duration", () => {
169
+ if (seconds >= 0) get().send.record(seconds, attributes);
170
+ }),
171
+ confirmationDuration: (seconds, attributes) => record("confirmation duration", () => {
172
+ if (seconds >= 0) get().confirmation.record(seconds, attributes);
173
+ }),
174
+ fee: (wei, attributes) => record("fee", () => {
175
+ if (wei >= 0n) get().fee.record(Number(wei), attributes);
176
+ })
177
+ };
178
+ }
179
+ //#endregion
103
180
  //#region src/confirm-registry.ts
104
181
  /**
105
182
  * Bounded registry from (chainId, tx hash) to its in-flight confirm span, or to "settled" for a while after a
@@ -385,7 +462,7 @@ function sanitizeResource(resource) {
385
462
  }
386
463
  //#endregion
387
464
  //#region src/version.ts
388
- const VERSION = "0.6.0";
465
+ const VERSION = "0.7.0";
389
466
  //#endregion
390
467
  //#region src/tracker.ts
391
468
  const INSTRUMENTATION_NAME = "@hashspan/core";
@@ -512,7 +589,7 @@ function reportedErrorType(error, options) {
512
589
  }
513
590
  /**
514
591
  * 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
592
+ * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md). It makes
516
593
  * no network calls; the caller passes hashes and receipts. Its methods and handles never throw: failures are logged
517
594
  * via `diag`, and a method that fails returns a handle that records nothing.
518
595
  */
@@ -528,6 +605,14 @@ function createTxTracker(options = {}) {
528
605
  const formatAddress = safely("configure address mode", () => resolveAddressFormatter(options.address), OFF_ADDRESS_FORMATTER);
529
606
  const errorMessages = safely("configure error message mode", () => resolveErrorMessageMode(options.errorMessages), "off");
530
607
  const paymentResource = safely("configure payment resource mode", () => resolvePaymentResourceMode(options.paymentResource), "off");
608
+ const txMetrics = createTxMetrics(options.meterProvider, INSTRUMENTATION_NAME, VERSION);
609
+ /** Attributes of a metric: low-cardinality only, never an address, hash or agent identity. */
610
+ const metricAttributes = (chainId, extra = {}) => ({
611
+ [ATTR_BLOCKCHAIN_SYSTEM]: "evm",
612
+ [ATTR_BLOCKCHAIN_CHAIN_ID]: chainId,
613
+ ...extra
614
+ });
615
+ const secondsSince = (startMs, endTime) => (toEpochMs(endTime) - startMs) / 1e3;
531
616
  let tracer;
532
617
  const getTracer = () => {
533
618
  tracer ??= (options.tracerProvider ?? trace.getTracerProvider()).getTracer(INSTRUMENTATION_NAME, VERSION);
@@ -552,7 +637,7 @@ function createTxTracker(options = {}) {
552
637
  /**
553
638
  * Exception event attributes for `error`, per the error message mode. The error object itself is never handed to
554
639
  * 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).
640
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0006-error-privacy.md).
556
641
  */
557
642
  const exceptionAttributes = (type, error) => {
558
643
  const attributes = { [ATTR_EXCEPTION_TYPE]: type };
@@ -585,6 +670,7 @@ function createTxTracker(options = {}) {
585
670
  code: SpanStatusCode.ERROR,
586
671
  ...message !== void 0 ? { message } : {}
587
672
  });
673
+ return type;
588
674
  };
589
675
  /** Ends a span exactly once; the span is always ended even if recording attributes fails. */
590
676
  const finisher = (span) => {
@@ -636,6 +722,8 @@ function createTxTracker(options = {}) {
636
722
  ...input.startTime !== void 0 ? { startTime: input.startTime } : {}
637
723
  }, parent);
638
724
  const finish = finisher(span);
725
+ const startMs = toEpochMs(input.startTime);
726
+ const recordSend = (endTime, errorType) => txMetrics.sendDuration(secondsSince(startMs, endTime), metricAttributes(input.chainId, errorType === void 0 ? {} : { [ATTR_ERROR_TYPE]: errorType }));
639
727
  return {
640
728
  context: trace.setSpan(parent, span),
641
729
  end: (result, second) => finish("record transaction hash", () => {
@@ -649,10 +737,11 @@ function createTxTracker(options = {}) {
649
737
  parent
650
738
  });
651
739
  span.setAttributes(redact({ [ATTR_BLOCKCHAIN_TX_HASH]: hash }));
740
+ recordSend(handleOptions(second).endTime);
652
741
  }, handleOptions(second).endTime),
653
742
  fail: (error, second, third) => {
654
743
  const options = handleOptions(second, third);
655
- finish("record send failure", () => markError(span, reportedErrorType(error, options), error, errorType(error)), options.endTime);
744
+ finish("record send failure", () => recordSend(options.endTime, markError(span, reportedErrorType(error, options), error, errorType(error))), options.endTime);
656
745
  }
657
746
  };
658
747
  };
@@ -688,6 +777,8 @@ function createTxTracker(options = {}) {
688
777
  ...explicitStart !== void 0 ? { startTime: explicitStart } : {}
689
778
  }, parent);
690
779
  const finish = finisher(span);
780
+ const startMs = toEpochMs(explicitStart);
781
+ const recordConfirmation = (endTime, outcome) => txMetrics.confirmationDuration(secondsSince(startMs, endTime), metricAttributes(input.chainId, outcome));
691
782
  return {
692
783
  active: 0,
693
784
  ended: false,
@@ -697,11 +788,16 @@ function createTxTracker(options = {}) {
697
788
  links: [{ context: span.spanContext() }, ...sent ? [{ context: sent.spanContext }] : []]
698
789
  },
699
790
  receipt: (receipt, endTime) => finish("record receipt", () => {
700
- span.setAttributes(redact(receiptAttributes(receipt)));
791
+ const attributes = receiptAttributes(receipt);
792
+ span.setAttributes(redact(attributes));
701
793
  if (receipt.status === "reverted") markError(span, BLOCKCHAIN_TX_STATUS_VALUE_REVERTED);
794
+ const status = { [ATTR_BLOCKCHAIN_TX_STATUS]: attributes[ATTR_BLOCKCHAIN_TX_STATUS] };
795
+ recordConfirmation(endTime, status);
796
+ const fee = attributes[ATTR_BLOCKCHAIN_TX_FEE];
797
+ if (typeof fee === "string") txMetrics.fee(BigInt(fee), metricAttributes(input.chainId, status));
702
798
  }, 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),
799
+ timeout: (endTime) => finish("record confirmation timeout", () => recordConfirmation(endTime, { [ATTR_ERROR_TYPE]: markError(span, OBSERVER_TIMEOUT) }), endTime),
800
+ fail: (error, endTime) => finish("record confirmation failure", () => recordConfirmation(endTime, { [ATTR_ERROR_TYPE]: markError(span, errorType(error), error) }), endTime),
705
801
  replaced: (hash, reason, endTime) => finish("record replacement", () => {
706
802
  const attributes = {
707
803
  [ATTR_BLOCKCHAIN_TX_STATUS]: BLOCKCHAIN_TX_STATUS_VALUE_REPLACED,
@@ -709,8 +805,9 @@ function createTxTracker(options = {}) {
709
805
  };
710
806
  if (reason !== void 0 && REPLACEMENT_REASONS.has(reason)) attributes[ATTR_BLOCKCHAIN_TX_REPLACEMENT_REASON] = reason;
711
807
  span.setAttributes(redact(attributes));
808
+ recordConfirmation(endTime, { [ATTR_BLOCKCHAIN_TX_STATUS]: BLOCKCHAIN_TX_STATUS_VALUE_REPLACED });
712
809
  }, endTime),
713
- unattributable: (endTime) => finish("record unattributable receipt", () => markError(span, ERROR_TYPE_VALUE_OTHER), endTime)
810
+ unattributable: (endTime) => finish("record unattributable receipt", () => recordConfirmation(endTime, { [ATTR_ERROR_TYPE]: markError(span, ERROR_TYPE_VALUE_OTHER) }), endTime)
714
811
  };
715
812
  };
716
813
  /**
@@ -732,7 +829,7 @@ function createTxTracker(options = {}) {
732
829
  };
733
830
  /**
734
831
  * 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).
832
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0008-replaced-transactions.md).
736
833
  */
737
834
  const endWithReceipt = (chainId, hash, shared, receipt, endTime) => {
738
835
  const mined = receipt.transactionHash;
@@ -868,4 +965,4 @@ function createTxTracker(options = {}) {
868
965
  };
869
966
  }
870
967
  //#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 };
968
+ 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, 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.7.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",