@hashspan/core 0.5.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 +21 -10
- package/dist/index.cjs +138 -23
- package/dist/index.d.cts +50 -20
- package/dist/index.d.mts +50 -20
- package/dist/index.mjs +136 -25
- package/package.json +1 -1
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
118
|
-
- The redaction hook (`redact`) runs last on every attribute set and on exception attributes; use it for
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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";
|
|
@@ -61,6 +61,12 @@ const ATTR_BLOCKCHAIN_PAYMENT_AMOUNT = "blockchain.payment.amount";
|
|
|
61
61
|
const ATTR_BLOCKCHAIN_PAYMENT_STATUS = "blockchain.payment.status";
|
|
62
62
|
/** The amount the settling party reports it settled, e.g. less than the authorized maximum with x402 `upto`. */
|
|
63
63
|
const ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT = "blockchain.payment.settled_amount";
|
|
64
|
+
/**
|
|
65
|
+
* Whether the settlement transaction's receipt carries the payment, as checked by the adapter; absent when no check
|
|
66
|
+
* was possible. See
|
|
67
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0017-x402-payment-verification.md.
|
|
68
|
+
*/
|
|
69
|
+
const ATTR_BLOCKCHAIN_PAYMENT_VERIFIED = "blockchain.payment.verified";
|
|
64
70
|
/** x402's own payment fields. */
|
|
65
71
|
const ATTR_X402_SCHEME = "x402.scheme";
|
|
66
72
|
const ATTR_X402_RESOURCE = "x402.resource";
|
|
@@ -82,7 +88,7 @@ const BLOCKCHAIN_TX_STATUS_VALUE_REVERTED = "reverted";
|
|
|
82
88
|
/**
|
|
83
89
|
* @deprecated No longer recorded: a confirm span that gave up waiting records `error.type` `timeout` and no
|
|
84
90
|
* `blockchain.tx.status`. The constant is removed in 1.0. See
|
|
85
|
-
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
91
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
|
|
86
92
|
*/
|
|
87
93
|
const BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT = "timeout";
|
|
88
94
|
const BLOCKCHAIN_TX_STATUS_VALUE_REPLACED = "replaced";
|
|
@@ -95,6 +101,83 @@ const ATTR_ERROR_TYPE = "error.type";
|
|
|
95
101
|
/** Fallback {@link ATTR_ERROR_TYPE} value when the error has no name. */
|
|
96
102
|
const ERROR_TYPE_VALUE_OTHER = "_OTHER";
|
|
97
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
|
|
98
181
|
//#region src/confirm-registry.ts
|
|
99
182
|
/**
|
|
100
183
|
* Bounded registry from (chainId, tx hash) to its in-flight confirm span, or to "settled" for a while after a
|
|
@@ -380,7 +463,7 @@ function sanitizeResource(resource) {
|
|
|
380
463
|
}
|
|
381
464
|
//#endregion
|
|
382
465
|
//#region src/version.ts
|
|
383
|
-
const VERSION = "0.
|
|
466
|
+
const VERSION = "0.7.0";
|
|
384
467
|
//#endregion
|
|
385
468
|
//#region src/tracker.ts
|
|
386
469
|
const INSTRUMENTATION_NAME = "@hashspan/core";
|
|
@@ -402,6 +485,7 @@ const NON_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
|
402
485
|
ATTR_BLOCKCHAIN_TX_REPLACEMENT_REASON,
|
|
403
486
|
ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL,
|
|
404
487
|
ATTR_BLOCKCHAIN_PAYMENT_STATUS,
|
|
488
|
+
ATTR_BLOCKCHAIN_PAYMENT_VERIFIED,
|
|
405
489
|
ATTR_ERROR_TYPE,
|
|
406
490
|
ATTR_EXCEPTION_TYPE
|
|
407
491
|
]);
|
|
@@ -430,7 +514,8 @@ const noopSend = (parent) => ({
|
|
|
430
514
|
const NOOP_PAYMENT = {
|
|
431
515
|
end: () => {},
|
|
432
516
|
fail: () => {},
|
|
433
|
-
timeout: () => {}
|
|
517
|
+
timeout: () => {},
|
|
518
|
+
link: () => {}
|
|
434
519
|
};
|
|
435
520
|
const NOOP_CONFIRM = {
|
|
436
521
|
end: () => {},
|
|
@@ -505,7 +590,7 @@ function reportedErrorType(error, options) {
|
|
|
505
590
|
}
|
|
506
591
|
/**
|
|
507
592
|
* Creates a tracker that records transactions as `send` and `confirm` spans, and payments as `payment` spans, with
|
|
508
|
-
* `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
593
|
+
* `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md). It makes
|
|
509
594
|
* no network calls; the caller passes hashes and receipts. Its methods and handles never throw: failures are logged
|
|
510
595
|
* via `diag`, and a method that fails returns a handle that records nothing.
|
|
511
596
|
*/
|
|
@@ -521,6 +606,14 @@ function createTxTracker(options = {}) {
|
|
|
521
606
|
const formatAddress = safely("configure address mode", () => resolveAddressFormatter(options.address), OFF_ADDRESS_FORMATTER);
|
|
522
607
|
const errorMessages = safely("configure error message mode", () => resolveErrorMessageMode(options.errorMessages), "off");
|
|
523
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;
|
|
524
617
|
let tracer;
|
|
525
618
|
const getTracer = () => {
|
|
526
619
|
tracer ??= (options.tracerProvider ?? _opentelemetry_api.trace.getTracerProvider()).getTracer(INSTRUMENTATION_NAME, VERSION);
|
|
@@ -545,7 +638,7 @@ function createTxTracker(options = {}) {
|
|
|
545
638
|
/**
|
|
546
639
|
* Exception event attributes for `error`, per the error message mode. The error object itself is never handed to
|
|
547
640
|
* the SDK: its message and stack can carry addresses and calldata
|
|
548
|
-
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
641
|
+
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0006-error-privacy.md).
|
|
549
642
|
*/
|
|
550
643
|
const exceptionAttributes = (type, error) => {
|
|
551
644
|
const attributes = { [ATTR_EXCEPTION_TYPE]: type };
|
|
@@ -578,6 +671,7 @@ function createTxTracker(options = {}) {
|
|
|
578
671
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
579
672
|
...message !== void 0 ? { message } : {}
|
|
580
673
|
});
|
|
674
|
+
return type;
|
|
581
675
|
};
|
|
582
676
|
/** Ends a span exactly once; the span is always ended even if recording attributes fails. */
|
|
583
677
|
const finisher = (span) => {
|
|
@@ -629,6 +723,8 @@ function createTxTracker(options = {}) {
|
|
|
629
723
|
...input.startTime !== void 0 ? { startTime: input.startTime } : {}
|
|
630
724
|
}, parent);
|
|
631
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 }));
|
|
632
728
|
return {
|
|
633
729
|
context: _opentelemetry_api.trace.setSpan(parent, span),
|
|
634
730
|
end: (result, second) => finish("record transaction hash", () => {
|
|
@@ -642,10 +738,11 @@ function createTxTracker(options = {}) {
|
|
|
642
738
|
parent
|
|
643
739
|
});
|
|
644
740
|
span.setAttributes(redact({ [ATTR_BLOCKCHAIN_TX_HASH]: hash }));
|
|
741
|
+
recordSend(handleOptions(second).endTime);
|
|
645
742
|
}, handleOptions(second).endTime),
|
|
646
743
|
fail: (error, second, third) => {
|
|
647
744
|
const options = handleOptions(second, third);
|
|
648
|
-
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);
|
|
649
746
|
}
|
|
650
747
|
};
|
|
651
748
|
};
|
|
@@ -681,6 +778,8 @@ function createTxTracker(options = {}) {
|
|
|
681
778
|
...explicitStart !== void 0 ? { startTime: explicitStart } : {}
|
|
682
779
|
}, parent);
|
|
683
780
|
const finish = finisher(span);
|
|
781
|
+
const startMs = toEpochMs(explicitStart);
|
|
782
|
+
const recordConfirmation = (endTime, outcome) => txMetrics.confirmationDuration(secondsSince(startMs, endTime), metricAttributes(input.chainId, outcome));
|
|
684
783
|
return {
|
|
685
784
|
active: 0,
|
|
686
785
|
ended: false,
|
|
@@ -690,11 +789,16 @@ function createTxTracker(options = {}) {
|
|
|
690
789
|
links: [{ context: span.spanContext() }, ...sent ? [{ context: sent.spanContext }] : []]
|
|
691
790
|
},
|
|
692
791
|
receipt: (receipt, endTime) => finish("record receipt", () => {
|
|
693
|
-
|
|
792
|
+
const attributes = receiptAttributes(receipt);
|
|
793
|
+
span.setAttributes(redact(attributes));
|
|
694
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));
|
|
695
799
|
}, endTime),
|
|
696
|
-
timeout: (endTime) => finish("record confirmation timeout", () => markError(span, OBSERVER_TIMEOUT), endTime),
|
|
697
|
-
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),
|
|
698
802
|
replaced: (hash, reason, endTime) => finish("record replacement", () => {
|
|
699
803
|
const attributes = {
|
|
700
804
|
[ATTR_BLOCKCHAIN_TX_STATUS]: BLOCKCHAIN_TX_STATUS_VALUE_REPLACED,
|
|
@@ -702,8 +806,9 @@ function createTxTracker(options = {}) {
|
|
|
702
806
|
};
|
|
703
807
|
if (reason !== void 0 && REPLACEMENT_REASONS.has(reason)) attributes[ATTR_BLOCKCHAIN_TX_REPLACEMENT_REASON] = reason;
|
|
704
808
|
span.setAttributes(redact(attributes));
|
|
809
|
+
recordConfirmation(endTime, { [ATTR_BLOCKCHAIN_TX_STATUS]: BLOCKCHAIN_TX_STATUS_VALUE_REPLACED });
|
|
705
810
|
}, endTime),
|
|
706
|
-
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)
|
|
707
812
|
};
|
|
708
813
|
};
|
|
709
814
|
/**
|
|
@@ -725,7 +830,7 @@ function createTxTracker(options = {}) {
|
|
|
725
830
|
};
|
|
726
831
|
/**
|
|
727
832
|
* Ends `shared` with `receipt`, attributing it to the transaction that was mined
|
|
728
|
-
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
833
|
+
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0008-replaced-transactions.md).
|
|
729
834
|
*/
|
|
730
835
|
const endWithReceipt = (chainId, hash, shared, receipt, endTime) => {
|
|
731
836
|
const mined = receipt.transactionHash;
|
|
@@ -815,6 +920,15 @@ function createTxTracker(options = {}) {
|
|
|
815
920
|
...input.startTime !== void 0 ? { startTime: input.startTime } : {}
|
|
816
921
|
}, parent);
|
|
817
922
|
const finish = finisher(span);
|
|
923
|
+
/** Links the confirm span of `hash` to this payment span, unless the tracker already links that hash. */
|
|
924
|
+
const linkHash = (hash) => {
|
|
925
|
+
if (typeof hash !== "string" || !TX_HASH.test(hash)) return false;
|
|
926
|
+
if (!links.get(input.chainId, hash)) links.set(input.chainId, hash, {
|
|
927
|
+
spanContext: span.spanContext(),
|
|
928
|
+
parent
|
|
929
|
+
});
|
|
930
|
+
return true;
|
|
931
|
+
};
|
|
818
932
|
const recordSettlement = (settlement) => {
|
|
819
933
|
const status = settlement.status;
|
|
820
934
|
if (!PAYMENT_STATUSES.has(status)) {
|
|
@@ -823,19 +937,15 @@ function createTxTracker(options = {}) {
|
|
|
823
937
|
}
|
|
824
938
|
const settled = { [ATTR_BLOCKCHAIN_PAYMENT_STATUS]: status };
|
|
825
939
|
const hash = settlement.hash;
|
|
826
|
-
if (
|
|
827
|
-
if (!links.get(input.chainId, hash)) links.set(input.chainId, hash, {
|
|
828
|
-
spanContext: span.spanContext(),
|
|
829
|
-
parent
|
|
830
|
-
});
|
|
831
|
-
settled[ATTR_BLOCKCHAIN_TX_HASH] = hash;
|
|
832
|
-
}
|
|
940
|
+
if (linkHash(hash)) settled[ATTR_BLOCKCHAIN_TX_HASH] = hash;
|
|
833
941
|
if (!knownPayer) setPaymentAddress(settled, ATTR_BLOCKCHAIN_PAYMENT_PAYER, settlement.payer);
|
|
834
942
|
const settledAmount = amount(settlement.amount);
|
|
835
943
|
if (settledAmount !== void 0) {
|
|
836
944
|
settled[ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT] = settledAmount;
|
|
837
945
|
if (paid === void 0) settled[ATTR_BLOCKCHAIN_PAYMENT_AMOUNT] = settledAmount;
|
|
838
946
|
}
|
|
947
|
+
const verified = settlement.verified;
|
|
948
|
+
if (typeof verified === "boolean") settled[ATTR_BLOCKCHAIN_PAYMENT_VERIFIED] = verified;
|
|
839
949
|
span.setAttributes(redact(settled));
|
|
840
950
|
if (status === "failed") markError(span, identifier(settlement.errorReason) ?? "_OTHER");
|
|
841
951
|
};
|
|
@@ -845,7 +955,8 @@ function createTxTracker(options = {}) {
|
|
|
845
955
|
const read = handleOptions(options);
|
|
846
956
|
finish("record payment failure", () => markError(span, reportedErrorType(error, read), error, errorType(error)), read.endTime);
|
|
847
957
|
},
|
|
848
|
-
timeout: (options) => finish("record payment timeout", () => markError(span, OBSERVER_TIMEOUT), handleOptions(options).endTime)
|
|
958
|
+
timeout: (options) => finish("record payment timeout", () => markError(span, OBSERVER_TIMEOUT), handleOptions(options).endTime),
|
|
959
|
+
link: (hash) => safely("link the payment span", () => void linkHash(hash), void 0)
|
|
849
960
|
};
|
|
850
961
|
};
|
|
851
962
|
return {
|
|
@@ -868,6 +979,7 @@ exports.ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL = ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL;
|
|
|
868
979
|
exports.ATTR_BLOCKCHAIN_PAYMENT_RECIPIENT = ATTR_BLOCKCHAIN_PAYMENT_RECIPIENT;
|
|
869
980
|
exports.ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT = ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT;
|
|
870
981
|
exports.ATTR_BLOCKCHAIN_PAYMENT_STATUS = ATTR_BLOCKCHAIN_PAYMENT_STATUS;
|
|
982
|
+
exports.ATTR_BLOCKCHAIN_PAYMENT_VERIFIED = ATTR_BLOCKCHAIN_PAYMENT_VERIFIED;
|
|
871
983
|
exports.ATTR_BLOCKCHAIN_SYSTEM = ATTR_BLOCKCHAIN_SYSTEM;
|
|
872
984
|
exports.ATTR_BLOCKCHAIN_TX_EFFECTIVE_GAS_PRICE = ATTR_BLOCKCHAIN_TX_EFFECTIVE_GAS_PRICE;
|
|
873
985
|
exports.ATTR_BLOCKCHAIN_TX_FEE = ATTR_BLOCKCHAIN_TX_FEE;
|
|
@@ -903,5 +1015,8 @@ exports.BLOCKCHAIN_TX_STATUS_VALUE_REVERTED = BLOCKCHAIN_TX_STATUS_VALUE_REVERTE
|
|
|
903
1015
|
exports.BLOCKCHAIN_TX_STATUS_VALUE_SUCCESS = BLOCKCHAIN_TX_STATUS_VALUE_SUCCESS;
|
|
904
1016
|
exports.BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT = BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT;
|
|
905
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;
|
|
906
1021
|
exports.VERSION = VERSION;
|
|
907
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.
|
|
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.
|
|
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.
|
|
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
|
/**
|
|
@@ -75,8 +80,8 @@ interface TxTrackerOptions {
|
|
|
75
80
|
* If it throws or returns something other than an attributes object, the tracker fails closed and records only
|
|
76
81
|
* `blockchain.system`, `blockchain.chain.id`, `blockchain.operation.name`, `blockchain.tx.hash`,
|
|
77
82
|
* `blockchain.tx.status`, `blockchain.tx.replacement.hash`, `blockchain.tx.replacement.reason`,
|
|
78
|
-
* `blockchain.payment.protocol`, `blockchain.payment.status`, `
|
|
79
|
-
* failure via `diag`.
|
|
83
|
+
* `blockchain.payment.protocol`, `blockchain.payment.status`, `blockchain.payment.verified`, `error.type` and
|
|
84
|
+
* `exception.type`, and logs the failure via `diag`.
|
|
80
85
|
*/
|
|
81
86
|
redact?: ((attributes: Attributes) => Attributes) | undefined;
|
|
82
87
|
/** How long a sent transaction can be linked from its confirmation. Default: 10 minutes. */
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 {
|
|
@@ -257,13 +262,18 @@ interface PaymentSettlement {
|
|
|
257
262
|
* `blockchain.payment.amount` when the input had none.
|
|
258
263
|
*/
|
|
259
264
|
amount?: bigint | string | undefined;
|
|
265
|
+
/**
|
|
266
|
+
* Whether the settlement transaction's receipt carries this payment, as the adapter checked it from the payer's own
|
|
267
|
+
* data; recorded as `blockchain.payment.verified`. Leave it unset when no check was possible.
|
|
268
|
+
*/
|
|
269
|
+
verified?: boolean | undefined;
|
|
260
270
|
/** Why a `failed` settlement failed, recorded as `error.type` if it is a short identifier, else `_OTHER`. */
|
|
261
271
|
errorReason?: string | undefined;
|
|
262
272
|
}
|
|
263
273
|
/**
|
|
264
274
|
* Ends a payment span. Only the first call counts; methods never throw.
|
|
265
275
|
* Produced by the tracker only; methods may be added in minor releases
|
|
266
|
-
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
276
|
+
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0014-core-api-boundary.md).
|
|
267
277
|
*/
|
|
268
278
|
interface PaymentHandle {
|
|
269
279
|
/** Ends the payment span with its settlement. */
|
|
@@ -279,6 +289,12 @@ interface PaymentHandle {
|
|
|
279
289
|
* learned, e.g. no response arrived before the authorization expired.
|
|
280
290
|
*/
|
|
281
291
|
timeout(options?: EndOptions): void;
|
|
292
|
+
/**
|
|
293
|
+
* Makes the confirm span of the settling transaction `hash` link to this payment span before it ends, for an
|
|
294
|
+
* adapter that ends it only after checking that transaction's receipt (ADR 0017). A hash this tracker already links,
|
|
295
|
+
* such as one of its own sends, keeps its link; `end` with a hash links it as well.
|
|
296
|
+
*/
|
|
297
|
+
link(hash: string): void;
|
|
282
298
|
}
|
|
283
299
|
//#endregion
|
|
284
300
|
//#region src/agent.d.ts
|
|
@@ -289,7 +305,7 @@ export declare const ATTR_GEN_AI_AGENT_NAME: "gen_ai.agent.name";
|
|
|
289
305
|
/**
|
|
290
306
|
* Attribute keys emitted by hashspan.
|
|
291
307
|
*
|
|
292
|
-
* Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
308
|
+
* Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md for
|
|
293
309
|
* definitions and value types. These names are a public contract: changes follow the deprecation policy in AGENTS.md.
|
|
294
310
|
*/
|
|
295
311
|
export declare const ATTR_BLOCKCHAIN_SYSTEM: "blockchain.system";
|
|
@@ -313,12 +329,12 @@ export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME: "blockchain.contrac
|
|
|
313
329
|
export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR: "blockchain.contract.function.selector";
|
|
314
330
|
/**
|
|
315
331
|
* Opt-in: decoded call arguments as a JSON array. See
|
|
316
|
-
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
332
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0004-privacy-defaults.md.
|
|
317
333
|
*/
|
|
318
334
|
export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS: "blockchain.contract.function.arguments";
|
|
319
335
|
/**
|
|
320
336
|
* Payments settled on chain by a party other than the agent, e.g. an x402 facilitator. See
|
|
321
|
-
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
337
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0013-x402-payments.md.
|
|
322
338
|
*/
|
|
323
339
|
export declare const ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL: "blockchain.payment.protocol";
|
|
324
340
|
export declare const ATTR_BLOCKCHAIN_PAYMENT_PAYER: "blockchain.payment.payer";
|
|
@@ -328,6 +344,12 @@ export declare const ATTR_BLOCKCHAIN_PAYMENT_AMOUNT: "blockchain.payment.amount"
|
|
|
328
344
|
export declare const ATTR_BLOCKCHAIN_PAYMENT_STATUS: "blockchain.payment.status";
|
|
329
345
|
/** The amount the settling party reports it settled, e.g. less than the authorized maximum with x402 `upto`. */
|
|
330
346
|
export declare const ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT: "blockchain.payment.settled_amount";
|
|
347
|
+
/**
|
|
348
|
+
* Whether the settlement transaction's receipt carries the payment, as checked by the adapter; absent when no check
|
|
349
|
+
* was possible. See
|
|
350
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0017-x402-payment-verification.md.
|
|
351
|
+
*/
|
|
352
|
+
export declare const ATTR_BLOCKCHAIN_PAYMENT_VERIFIED: "blockchain.payment.verified";
|
|
331
353
|
/** x402's own payment fields. */
|
|
332
354
|
export declare const ATTR_X402_SCHEME: "x402.scheme";
|
|
333
355
|
export declare const ATTR_X402_RESOURCE: "x402.resource";
|
|
@@ -349,7 +371,7 @@ export declare const BLOCKCHAIN_TX_STATUS_VALUE_REVERTED: "reverted";
|
|
|
349
371
|
/**
|
|
350
372
|
* @deprecated No longer recorded: a confirm span that gave up waiting records `error.type` `timeout` and no
|
|
351
373
|
* `blockchain.tx.status`. The constant is removed in 1.0. See
|
|
352
|
-
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
374
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
|
|
353
375
|
*/
|
|
354
376
|
export declare const BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT: "timeout";
|
|
355
377
|
export declare const BLOCKCHAIN_TX_STATUS_VALUE_REPLACED: "replaced";
|
|
@@ -362,11 +384,19 @@ export declare const ATTR_ERROR_TYPE: "error.type";
|
|
|
362
384
|
/** Fallback {@link ATTR_ERROR_TYPE} value when the error has no name. */
|
|
363
385
|
export declare const ERROR_TYPE_VALUE_OTHER: "_OTHER";
|
|
364
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
|
|
365
395
|
//#region src/tracker.d.ts
|
|
366
396
|
/**
|
|
367
397
|
* Records transactions and payments as spans. Obtain one from {@link createTxTracker}: it is not meant to be
|
|
368
398
|
* implemented, and members may be added to it and to its handles in minor releases
|
|
369
|
-
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
399
|
+
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0014-core-api-boundary.md).
|
|
370
400
|
*/
|
|
371
401
|
interface TxTracker {
|
|
372
402
|
/**
|
|
@@ -384,7 +414,7 @@ interface TxTracker {
|
|
|
384
414
|
/**
|
|
385
415
|
* Starts a `payment` span as a child of `parent` (default: the active context), for a payment that another party
|
|
386
416
|
* settles on chain
|
|
387
|
-
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
417
|
+
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0013-x402-payments.md). Call
|
|
388
418
|
* `end(settlement)` with the settlement, or `fail(error)`. A settlement with a hash links the transaction's confirm
|
|
389
419
|
* span to this span, as a send span would.
|
|
390
420
|
*/
|
|
@@ -392,7 +422,7 @@ interface TxTracker {
|
|
|
392
422
|
}
|
|
393
423
|
/**
|
|
394
424
|
* Creates a tracker that records transactions as `send` and `confirm` spans, and payments as `payment` spans, with
|
|
395
|
-
* `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
425
|
+
* `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md). It makes
|
|
396
426
|
* no network calls; the caller passes hashes and receipts. Its methods and handles never throw: failures are logged
|
|
397
427
|
* via `diag`, and a method that fails returns a handle that records nothing.
|
|
398
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.
|
|
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.
|
|
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.
|
|
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
|
/**
|
|
@@ -75,8 +80,8 @@ interface TxTrackerOptions {
|
|
|
75
80
|
* If it throws or returns something other than an attributes object, the tracker fails closed and records only
|
|
76
81
|
* `blockchain.system`, `blockchain.chain.id`, `blockchain.operation.name`, `blockchain.tx.hash`,
|
|
77
82
|
* `blockchain.tx.status`, `blockchain.tx.replacement.hash`, `blockchain.tx.replacement.reason`,
|
|
78
|
-
* `blockchain.payment.protocol`, `blockchain.payment.status`, `
|
|
79
|
-
* failure via `diag`.
|
|
83
|
+
* `blockchain.payment.protocol`, `blockchain.payment.status`, `blockchain.payment.verified`, `error.type` and
|
|
84
|
+
* `exception.type`, and logs the failure via `diag`.
|
|
80
85
|
*/
|
|
81
86
|
redact?: ((attributes: Attributes) => Attributes) | undefined;
|
|
82
87
|
/** How long a sent transaction can be linked from its confirmation. Default: 10 minutes. */
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 {
|
|
@@ -257,13 +262,18 @@ interface PaymentSettlement {
|
|
|
257
262
|
* `blockchain.payment.amount` when the input had none.
|
|
258
263
|
*/
|
|
259
264
|
amount?: bigint | string | undefined;
|
|
265
|
+
/**
|
|
266
|
+
* Whether the settlement transaction's receipt carries this payment, as the adapter checked it from the payer's own
|
|
267
|
+
* data; recorded as `blockchain.payment.verified`. Leave it unset when no check was possible.
|
|
268
|
+
*/
|
|
269
|
+
verified?: boolean | undefined;
|
|
260
270
|
/** Why a `failed` settlement failed, recorded as `error.type` if it is a short identifier, else `_OTHER`. */
|
|
261
271
|
errorReason?: string | undefined;
|
|
262
272
|
}
|
|
263
273
|
/**
|
|
264
274
|
* Ends a payment span. Only the first call counts; methods never throw.
|
|
265
275
|
* Produced by the tracker only; methods may be added in minor releases
|
|
266
|
-
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
276
|
+
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0014-core-api-boundary.md).
|
|
267
277
|
*/
|
|
268
278
|
interface PaymentHandle {
|
|
269
279
|
/** Ends the payment span with its settlement. */
|
|
@@ -279,6 +289,12 @@ interface PaymentHandle {
|
|
|
279
289
|
* learned, e.g. no response arrived before the authorization expired.
|
|
280
290
|
*/
|
|
281
291
|
timeout(options?: EndOptions): void;
|
|
292
|
+
/**
|
|
293
|
+
* Makes the confirm span of the settling transaction `hash` link to this payment span before it ends, for an
|
|
294
|
+
* adapter that ends it only after checking that transaction's receipt (ADR 0017). A hash this tracker already links,
|
|
295
|
+
* such as one of its own sends, keeps its link; `end` with a hash links it as well.
|
|
296
|
+
*/
|
|
297
|
+
link(hash: string): void;
|
|
282
298
|
}
|
|
283
299
|
//#endregion
|
|
284
300
|
//#region src/agent.d.ts
|
|
@@ -289,7 +305,7 @@ export declare const ATTR_GEN_AI_AGENT_NAME: "gen_ai.agent.name";
|
|
|
289
305
|
/**
|
|
290
306
|
* Attribute keys emitted by hashspan.
|
|
291
307
|
*
|
|
292
|
-
* Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
308
|
+
* Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md for
|
|
293
309
|
* definitions and value types. These names are a public contract: changes follow the deprecation policy in AGENTS.md.
|
|
294
310
|
*/
|
|
295
311
|
export declare const ATTR_BLOCKCHAIN_SYSTEM: "blockchain.system";
|
|
@@ -313,12 +329,12 @@ export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME: "blockchain.contrac
|
|
|
313
329
|
export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR: "blockchain.contract.function.selector";
|
|
314
330
|
/**
|
|
315
331
|
* Opt-in: decoded call arguments as a JSON array. See
|
|
316
|
-
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
332
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0004-privacy-defaults.md.
|
|
317
333
|
*/
|
|
318
334
|
export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS: "blockchain.contract.function.arguments";
|
|
319
335
|
/**
|
|
320
336
|
* Payments settled on chain by a party other than the agent, e.g. an x402 facilitator. See
|
|
321
|
-
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
337
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0013-x402-payments.md.
|
|
322
338
|
*/
|
|
323
339
|
export declare const ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL: "blockchain.payment.protocol";
|
|
324
340
|
export declare const ATTR_BLOCKCHAIN_PAYMENT_PAYER: "blockchain.payment.payer";
|
|
@@ -328,6 +344,12 @@ export declare const ATTR_BLOCKCHAIN_PAYMENT_AMOUNT: "blockchain.payment.amount"
|
|
|
328
344
|
export declare const ATTR_BLOCKCHAIN_PAYMENT_STATUS: "blockchain.payment.status";
|
|
329
345
|
/** The amount the settling party reports it settled, e.g. less than the authorized maximum with x402 `upto`. */
|
|
330
346
|
export declare const ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT: "blockchain.payment.settled_amount";
|
|
347
|
+
/**
|
|
348
|
+
* Whether the settlement transaction's receipt carries the payment, as checked by the adapter; absent when no check
|
|
349
|
+
* was possible. See
|
|
350
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0017-x402-payment-verification.md.
|
|
351
|
+
*/
|
|
352
|
+
export declare const ATTR_BLOCKCHAIN_PAYMENT_VERIFIED: "blockchain.payment.verified";
|
|
331
353
|
/** x402's own payment fields. */
|
|
332
354
|
export declare const ATTR_X402_SCHEME: "x402.scheme";
|
|
333
355
|
export declare const ATTR_X402_RESOURCE: "x402.resource";
|
|
@@ -349,7 +371,7 @@ export declare const BLOCKCHAIN_TX_STATUS_VALUE_REVERTED: "reverted";
|
|
|
349
371
|
/**
|
|
350
372
|
* @deprecated No longer recorded: a confirm span that gave up waiting records `error.type` `timeout` and no
|
|
351
373
|
* `blockchain.tx.status`. The constant is removed in 1.0. See
|
|
352
|
-
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
374
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
|
|
353
375
|
*/
|
|
354
376
|
export declare const BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT: "timeout";
|
|
355
377
|
export declare const BLOCKCHAIN_TX_STATUS_VALUE_REPLACED: "replaced";
|
|
@@ -362,11 +384,19 @@ export declare const ATTR_ERROR_TYPE: "error.type";
|
|
|
362
384
|
/** Fallback {@link ATTR_ERROR_TYPE} value when the error has no name. */
|
|
363
385
|
export declare const ERROR_TYPE_VALUE_OTHER: "_OTHER";
|
|
364
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
|
|
365
395
|
//#region src/tracker.d.ts
|
|
366
396
|
/**
|
|
367
397
|
* Records transactions and payments as spans. Obtain one from {@link createTxTracker}: it is not meant to be
|
|
368
398
|
* implemented, and members may be added to it and to its handles in minor releases
|
|
369
|
-
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
399
|
+
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0014-core-api-boundary.md).
|
|
370
400
|
*/
|
|
371
401
|
interface TxTracker {
|
|
372
402
|
/**
|
|
@@ -384,7 +414,7 @@ interface TxTracker {
|
|
|
384
414
|
/**
|
|
385
415
|
* Starts a `payment` span as a child of `parent` (default: the active context), for a payment that another party
|
|
386
416
|
* settles on chain
|
|
387
|
-
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
417
|
+
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0013-x402-payments.md). Call
|
|
388
418
|
* `end(settlement)` with the settlement, or `fail(error)`. A settlement with a hash links the transaction's confirm
|
|
389
419
|
* span to this span, as a send span would.
|
|
390
420
|
*/
|
|
@@ -392,7 +422,7 @@ interface TxTracker {
|
|
|
392
422
|
}
|
|
393
423
|
/**
|
|
394
424
|
* Creates a tracker that records transactions as `send` and `confirm` spans, and payments as `payment` spans, with
|
|
395
|
-
* `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
425
|
+
* `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md). It makes
|
|
396
426
|
* no network calls; the caller passes hashes and receipts. Its methods and handles never throw: failures are logged
|
|
397
427
|
* via `diag`, and a method that fails returns a handle that records nothing.
|
|
398
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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";
|
|
@@ -60,6 +60,12 @@ const ATTR_BLOCKCHAIN_PAYMENT_AMOUNT = "blockchain.payment.amount";
|
|
|
60
60
|
const ATTR_BLOCKCHAIN_PAYMENT_STATUS = "blockchain.payment.status";
|
|
61
61
|
/** The amount the settling party reports it settled, e.g. less than the authorized maximum with x402 `upto`. */
|
|
62
62
|
const ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT = "blockchain.payment.settled_amount";
|
|
63
|
+
/**
|
|
64
|
+
* Whether the settlement transaction's receipt carries the payment, as checked by the adapter; absent when no check
|
|
65
|
+
* was possible. See
|
|
66
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0017-x402-payment-verification.md.
|
|
67
|
+
*/
|
|
68
|
+
const ATTR_BLOCKCHAIN_PAYMENT_VERIFIED = "blockchain.payment.verified";
|
|
63
69
|
/** x402's own payment fields. */
|
|
64
70
|
const ATTR_X402_SCHEME = "x402.scheme";
|
|
65
71
|
const ATTR_X402_RESOURCE = "x402.resource";
|
|
@@ -81,7 +87,7 @@ const BLOCKCHAIN_TX_STATUS_VALUE_REVERTED = "reverted";
|
|
|
81
87
|
/**
|
|
82
88
|
* @deprecated No longer recorded: a confirm span that gave up waiting records `error.type` `timeout` and no
|
|
83
89
|
* `blockchain.tx.status`. The constant is removed in 1.0. See
|
|
84
|
-
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
90
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
|
|
85
91
|
*/
|
|
86
92
|
const BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT = "timeout";
|
|
87
93
|
const BLOCKCHAIN_TX_STATUS_VALUE_REPLACED = "replaced";
|
|
@@ -94,6 +100,83 @@ const ATTR_ERROR_TYPE = "error.type";
|
|
|
94
100
|
/** Fallback {@link ATTR_ERROR_TYPE} value when the error has no name. */
|
|
95
101
|
const ERROR_TYPE_VALUE_OTHER = "_OTHER";
|
|
96
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
|
|
97
180
|
//#region src/confirm-registry.ts
|
|
98
181
|
/**
|
|
99
182
|
* Bounded registry from (chainId, tx hash) to its in-flight confirm span, or to "settled" for a while after a
|
|
@@ -379,7 +462,7 @@ function sanitizeResource(resource) {
|
|
|
379
462
|
}
|
|
380
463
|
//#endregion
|
|
381
464
|
//#region src/version.ts
|
|
382
|
-
const VERSION = "0.
|
|
465
|
+
const VERSION = "0.7.0";
|
|
383
466
|
//#endregion
|
|
384
467
|
//#region src/tracker.ts
|
|
385
468
|
const INSTRUMENTATION_NAME = "@hashspan/core";
|
|
@@ -401,6 +484,7 @@ const NON_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
|
401
484
|
ATTR_BLOCKCHAIN_TX_REPLACEMENT_REASON,
|
|
402
485
|
ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL,
|
|
403
486
|
ATTR_BLOCKCHAIN_PAYMENT_STATUS,
|
|
487
|
+
ATTR_BLOCKCHAIN_PAYMENT_VERIFIED,
|
|
404
488
|
ATTR_ERROR_TYPE,
|
|
405
489
|
ATTR_EXCEPTION_TYPE
|
|
406
490
|
]);
|
|
@@ -429,7 +513,8 @@ const noopSend = (parent) => ({
|
|
|
429
513
|
const NOOP_PAYMENT = {
|
|
430
514
|
end: () => {},
|
|
431
515
|
fail: () => {},
|
|
432
|
-
timeout: () => {}
|
|
516
|
+
timeout: () => {},
|
|
517
|
+
link: () => {}
|
|
433
518
|
};
|
|
434
519
|
const NOOP_CONFIRM = {
|
|
435
520
|
end: () => {},
|
|
@@ -504,7 +589,7 @@ function reportedErrorType(error, options) {
|
|
|
504
589
|
}
|
|
505
590
|
/**
|
|
506
591
|
* Creates a tracker that records transactions as `send` and `confirm` spans, and payments as `payment` spans, with
|
|
507
|
-
* `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
592
|
+
* `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/semconv.md). It makes
|
|
508
593
|
* no network calls; the caller passes hashes and receipts. Its methods and handles never throw: failures are logged
|
|
509
594
|
* via `diag`, and a method that fails returns a handle that records nothing.
|
|
510
595
|
*/
|
|
@@ -520,6 +605,14 @@ function createTxTracker(options = {}) {
|
|
|
520
605
|
const formatAddress = safely("configure address mode", () => resolveAddressFormatter(options.address), OFF_ADDRESS_FORMATTER);
|
|
521
606
|
const errorMessages = safely("configure error message mode", () => resolveErrorMessageMode(options.errorMessages), "off");
|
|
522
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;
|
|
523
616
|
let tracer;
|
|
524
617
|
const getTracer = () => {
|
|
525
618
|
tracer ??= (options.tracerProvider ?? trace.getTracerProvider()).getTracer(INSTRUMENTATION_NAME, VERSION);
|
|
@@ -544,7 +637,7 @@ function createTxTracker(options = {}) {
|
|
|
544
637
|
/**
|
|
545
638
|
* Exception event attributes for `error`, per the error message mode. The error object itself is never handed to
|
|
546
639
|
* the SDK: its message and stack can carry addresses and calldata
|
|
547
|
-
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
640
|
+
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0006-error-privacy.md).
|
|
548
641
|
*/
|
|
549
642
|
const exceptionAttributes = (type, error) => {
|
|
550
643
|
const attributes = { [ATTR_EXCEPTION_TYPE]: type };
|
|
@@ -577,6 +670,7 @@ function createTxTracker(options = {}) {
|
|
|
577
670
|
code: SpanStatusCode.ERROR,
|
|
578
671
|
...message !== void 0 ? { message } : {}
|
|
579
672
|
});
|
|
673
|
+
return type;
|
|
580
674
|
};
|
|
581
675
|
/** Ends a span exactly once; the span is always ended even if recording attributes fails. */
|
|
582
676
|
const finisher = (span) => {
|
|
@@ -628,6 +722,8 @@ function createTxTracker(options = {}) {
|
|
|
628
722
|
...input.startTime !== void 0 ? { startTime: input.startTime } : {}
|
|
629
723
|
}, parent);
|
|
630
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 }));
|
|
631
727
|
return {
|
|
632
728
|
context: trace.setSpan(parent, span),
|
|
633
729
|
end: (result, second) => finish("record transaction hash", () => {
|
|
@@ -641,10 +737,11 @@ function createTxTracker(options = {}) {
|
|
|
641
737
|
parent
|
|
642
738
|
});
|
|
643
739
|
span.setAttributes(redact({ [ATTR_BLOCKCHAIN_TX_HASH]: hash }));
|
|
740
|
+
recordSend(handleOptions(second).endTime);
|
|
644
741
|
}, handleOptions(second).endTime),
|
|
645
742
|
fail: (error, second, third) => {
|
|
646
743
|
const options = handleOptions(second, third);
|
|
647
|
-
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);
|
|
648
745
|
}
|
|
649
746
|
};
|
|
650
747
|
};
|
|
@@ -680,6 +777,8 @@ function createTxTracker(options = {}) {
|
|
|
680
777
|
...explicitStart !== void 0 ? { startTime: explicitStart } : {}
|
|
681
778
|
}, parent);
|
|
682
779
|
const finish = finisher(span);
|
|
780
|
+
const startMs = toEpochMs(explicitStart);
|
|
781
|
+
const recordConfirmation = (endTime, outcome) => txMetrics.confirmationDuration(secondsSince(startMs, endTime), metricAttributes(input.chainId, outcome));
|
|
683
782
|
return {
|
|
684
783
|
active: 0,
|
|
685
784
|
ended: false,
|
|
@@ -689,11 +788,16 @@ function createTxTracker(options = {}) {
|
|
|
689
788
|
links: [{ context: span.spanContext() }, ...sent ? [{ context: sent.spanContext }] : []]
|
|
690
789
|
},
|
|
691
790
|
receipt: (receipt, endTime) => finish("record receipt", () => {
|
|
692
|
-
|
|
791
|
+
const attributes = receiptAttributes(receipt);
|
|
792
|
+
span.setAttributes(redact(attributes));
|
|
693
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));
|
|
694
798
|
}, endTime),
|
|
695
|
-
timeout: (endTime) => finish("record confirmation timeout", () => markError(span, OBSERVER_TIMEOUT), endTime),
|
|
696
|
-
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),
|
|
697
801
|
replaced: (hash, reason, endTime) => finish("record replacement", () => {
|
|
698
802
|
const attributes = {
|
|
699
803
|
[ATTR_BLOCKCHAIN_TX_STATUS]: BLOCKCHAIN_TX_STATUS_VALUE_REPLACED,
|
|
@@ -701,8 +805,9 @@ function createTxTracker(options = {}) {
|
|
|
701
805
|
};
|
|
702
806
|
if (reason !== void 0 && REPLACEMENT_REASONS.has(reason)) attributes[ATTR_BLOCKCHAIN_TX_REPLACEMENT_REASON] = reason;
|
|
703
807
|
span.setAttributes(redact(attributes));
|
|
808
|
+
recordConfirmation(endTime, { [ATTR_BLOCKCHAIN_TX_STATUS]: BLOCKCHAIN_TX_STATUS_VALUE_REPLACED });
|
|
704
809
|
}, endTime),
|
|
705
|
-
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)
|
|
706
811
|
};
|
|
707
812
|
};
|
|
708
813
|
/**
|
|
@@ -724,7 +829,7 @@ function createTxTracker(options = {}) {
|
|
|
724
829
|
};
|
|
725
830
|
/**
|
|
726
831
|
* Ends `shared` with `receipt`, attributing it to the transaction that was mined
|
|
727
|
-
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
832
|
+
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.7.0/docs/adr/0008-replaced-transactions.md).
|
|
728
833
|
*/
|
|
729
834
|
const endWithReceipt = (chainId, hash, shared, receipt, endTime) => {
|
|
730
835
|
const mined = receipt.transactionHash;
|
|
@@ -814,6 +919,15 @@ function createTxTracker(options = {}) {
|
|
|
814
919
|
...input.startTime !== void 0 ? { startTime: input.startTime } : {}
|
|
815
920
|
}, parent);
|
|
816
921
|
const finish = finisher(span);
|
|
922
|
+
/** Links the confirm span of `hash` to this payment span, unless the tracker already links that hash. */
|
|
923
|
+
const linkHash = (hash) => {
|
|
924
|
+
if (typeof hash !== "string" || !TX_HASH.test(hash)) return false;
|
|
925
|
+
if (!links.get(input.chainId, hash)) links.set(input.chainId, hash, {
|
|
926
|
+
spanContext: span.spanContext(),
|
|
927
|
+
parent
|
|
928
|
+
});
|
|
929
|
+
return true;
|
|
930
|
+
};
|
|
817
931
|
const recordSettlement = (settlement) => {
|
|
818
932
|
const status = settlement.status;
|
|
819
933
|
if (!PAYMENT_STATUSES.has(status)) {
|
|
@@ -822,19 +936,15 @@ function createTxTracker(options = {}) {
|
|
|
822
936
|
}
|
|
823
937
|
const settled = { [ATTR_BLOCKCHAIN_PAYMENT_STATUS]: status };
|
|
824
938
|
const hash = settlement.hash;
|
|
825
|
-
if (
|
|
826
|
-
if (!links.get(input.chainId, hash)) links.set(input.chainId, hash, {
|
|
827
|
-
spanContext: span.spanContext(),
|
|
828
|
-
parent
|
|
829
|
-
});
|
|
830
|
-
settled[ATTR_BLOCKCHAIN_TX_HASH] = hash;
|
|
831
|
-
}
|
|
939
|
+
if (linkHash(hash)) settled[ATTR_BLOCKCHAIN_TX_HASH] = hash;
|
|
832
940
|
if (!knownPayer) setPaymentAddress(settled, ATTR_BLOCKCHAIN_PAYMENT_PAYER, settlement.payer);
|
|
833
941
|
const settledAmount = amount(settlement.amount);
|
|
834
942
|
if (settledAmount !== void 0) {
|
|
835
943
|
settled[ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT] = settledAmount;
|
|
836
944
|
if (paid === void 0) settled[ATTR_BLOCKCHAIN_PAYMENT_AMOUNT] = settledAmount;
|
|
837
945
|
}
|
|
946
|
+
const verified = settlement.verified;
|
|
947
|
+
if (typeof verified === "boolean") settled[ATTR_BLOCKCHAIN_PAYMENT_VERIFIED] = verified;
|
|
838
948
|
span.setAttributes(redact(settled));
|
|
839
949
|
if (status === "failed") markError(span, identifier(settlement.errorReason) ?? "_OTHER");
|
|
840
950
|
};
|
|
@@ -844,7 +954,8 @@ function createTxTracker(options = {}) {
|
|
|
844
954
|
const read = handleOptions(options);
|
|
845
955
|
finish("record payment failure", () => markError(span, reportedErrorType(error, read), error, errorType(error)), read.endTime);
|
|
846
956
|
},
|
|
847
|
-
timeout: (options) => finish("record payment timeout", () => markError(span, OBSERVER_TIMEOUT), handleOptions(options).endTime)
|
|
957
|
+
timeout: (options) => finish("record payment timeout", () => markError(span, OBSERVER_TIMEOUT), handleOptions(options).endTime),
|
|
958
|
+
link: (hash) => safely("link the payment span", () => void linkHash(hash), void 0)
|
|
848
959
|
};
|
|
849
960
|
};
|
|
850
961
|
return {
|
|
@@ -854,4 +965,4 @@ function createTxTracker(options = {}) {
|
|
|
854
965
|
};
|
|
855
966
|
}
|
|
856
967
|
//#endregion
|
|
857
|
-
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_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 };
|