@hashspan/core 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -12
- package/dist/index.cjs +297 -50
- package/dist/index.d.cts +175 -27
- package/dist/index.d.mts +175 -27
- package/dist/index.mjs +287 -51
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ 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.8.0/docs/adr/0011-agent-identity-precedence.md).
|
|
9
9
|
*/
|
|
10
10
|
function agentAttributes(ctx, identity, fromBaggage = true) {
|
|
11
11
|
const baggage = fromBaggage ? propagation.getBaggage(ctx) : void 0;
|
|
@@ -21,7 +21,7 @@ function agentAttributes(ctx, identity, fromBaggage = true) {
|
|
|
21
21
|
/**
|
|
22
22
|
* Attribute keys emitted by hashspan.
|
|
23
23
|
*
|
|
24
|
-
* Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
24
|
+
* Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/semconv.md for
|
|
25
25
|
* definitions and value types. These names are a public contract: changes follow the deprecation policy in AGENTS.md.
|
|
26
26
|
*/
|
|
27
27
|
const ATTR_BLOCKCHAIN_SYSTEM = "blockchain.system";
|
|
@@ -45,12 +45,12 @@ const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME = "blockchain.contract.function.nam
|
|
|
45
45
|
const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR = "blockchain.contract.function.selector";
|
|
46
46
|
/**
|
|
47
47
|
* Opt-in: decoded call arguments as a JSON array. See
|
|
48
|
-
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
48
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0004-privacy-defaults.md.
|
|
49
49
|
*/
|
|
50
50
|
const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS = "blockchain.contract.function.arguments";
|
|
51
51
|
/**
|
|
52
52
|
* Payments settled on chain by a party other than the agent, e.g. an x402 facilitator. See
|
|
53
|
-
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
53
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0013-x402-payments.md.
|
|
54
54
|
*/
|
|
55
55
|
const ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL = "blockchain.payment.protocol";
|
|
56
56
|
const ATTR_BLOCKCHAIN_PAYMENT_PAYER = "blockchain.payment.payer";
|
|
@@ -63,12 +63,33 @@ const ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT = "blockchain.payment.settled_amoun
|
|
|
63
63
|
/**
|
|
64
64
|
* Whether the settlement transaction's receipt carries the payment, as checked by the adapter; absent when no check
|
|
65
65
|
* was possible. See
|
|
66
|
-
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
66
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0017-x402-payment-verification.md.
|
|
67
67
|
*/
|
|
68
68
|
const ATTR_BLOCKCHAIN_PAYMENT_VERIFIED = "blockchain.payment.verified";
|
|
69
69
|
/** x402's own payment fields. */
|
|
70
70
|
const ATTR_X402_SCHEME = "x402.scheme";
|
|
71
71
|
const ATTR_X402_RESOURCE = "x402.resource";
|
|
72
|
+
/**
|
|
73
|
+
* User operations of ERC-4337 smart accounts. See
|
|
74
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0021-user-operations.md.
|
|
75
|
+
*/
|
|
76
|
+
const ATTR_BLOCKCHAIN_USER_OPERATION_HASH = "blockchain.user_operation.hash";
|
|
77
|
+
const ATTR_BLOCKCHAIN_USER_OPERATION_SENDER = "blockchain.user_operation.sender";
|
|
78
|
+
const ATTR_BLOCKCHAIN_USER_OPERATION_ENTRY_POINT = "blockchain.user_operation.entry_point";
|
|
79
|
+
const ATTR_BLOCKCHAIN_USER_OPERATION_CALL_COUNT = "blockchain.user_operation.call_count";
|
|
80
|
+
const ATTR_BLOCKCHAIN_USER_OPERATION_NONCE = "blockchain.user_operation.nonce";
|
|
81
|
+
const ATTR_BLOCKCHAIN_USER_OPERATION_SUCCESS = "blockchain.user_operation.success";
|
|
82
|
+
const ATTR_BLOCKCHAIN_USER_OPERATION_GAS_USED = "blockchain.user_operation.gas.used";
|
|
83
|
+
/** What the operation itself paid, in wei; the bundle transaction's fee covers every operation in the bundle. */
|
|
84
|
+
const ATTR_BLOCKCHAIN_USER_OPERATION_GAS_COST = "blockchain.user_operation.gas.cost";
|
|
85
|
+
const ATTR_BLOCKCHAIN_USER_OPERATION_PAYMASTER = "blockchain.user_operation.paymaster";
|
|
86
|
+
/**
|
|
87
|
+
* Metrics only: what a send, confirmation or fee sample is about. Recorded as `user_operation` on samples of user
|
|
88
|
+
* operations, and absent on those of transactions.
|
|
89
|
+
*/
|
|
90
|
+
const ATTR_BLOCKCHAIN_OPERATION_SUBJECT = "blockchain.operation.subject";
|
|
91
|
+
/** Values for {@link ATTR_BLOCKCHAIN_OPERATION_SUBJECT}. */
|
|
92
|
+
const BLOCKCHAIN_OPERATION_SUBJECT_VALUE_USER_OPERATION = "user_operation";
|
|
72
93
|
/** Values for {@link ATTR_BLOCKCHAIN_SYSTEM}. */
|
|
73
94
|
const BLOCKCHAIN_SYSTEM_VALUE_EVM = "evm";
|
|
74
95
|
/** Values for {@link ATTR_BLOCKCHAIN_OPERATION_NAME}. */
|
|
@@ -87,7 +108,7 @@ const BLOCKCHAIN_TX_STATUS_VALUE_REVERTED = "reverted";
|
|
|
87
108
|
/**
|
|
88
109
|
* @deprecated No longer recorded: a confirm span that gave up waiting records `error.type` `timeout` and no
|
|
89
110
|
* `blockchain.tx.status`. The constant is removed in 1.0. See
|
|
90
|
-
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
111
|
+
* https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
|
|
91
112
|
*/
|
|
92
113
|
const BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT = "timeout";
|
|
93
114
|
const BLOCKCHAIN_TX_STATUS_VALUE_REPLACED = "replaced";
|
|
@@ -101,11 +122,20 @@ const ATTR_ERROR_TYPE = "error.type";
|
|
|
101
122
|
const ERROR_TYPE_VALUE_OTHER = "_OTHER";
|
|
102
123
|
//#endregion
|
|
103
124
|
//#region src/metrics.ts
|
|
104
|
-
/**
|
|
125
|
+
/**
|
|
126
|
+
* Duration of a send of a transaction or user operation: from the start of the sending call until the hash is known or
|
|
127
|
+
* the call failed.
|
|
128
|
+
*/
|
|
105
129
|
const METRIC_BLOCKCHAIN_CLIENT_SEND_DURATION = "blockchain.client.send.duration";
|
|
106
|
-
/**
|
|
130
|
+
/**
|
|
131
|
+
* Duration of a confirmation of a transaction or user operation: from the start of the wait until the receipt, a
|
|
132
|
+
* timeout or a failure.
|
|
133
|
+
*/
|
|
107
134
|
const METRIC_BLOCKCHAIN_CLIENT_CONFIRMATION_DURATION = "blockchain.client.confirmation.duration";
|
|
108
|
-
/**
|
|
135
|
+
/**
|
|
136
|
+
* Total fee of a mined transaction (execution fee plus L1 data fee), or the cost of a user operation, in the chain's
|
|
137
|
+
* smallest unit (wei).
|
|
138
|
+
*/
|
|
109
139
|
const METRIC_BLOCKCHAIN_CLIENT_FEE = "blockchain.client.fee";
|
|
110
140
|
const DURATION_BUCKETS = [
|
|
111
141
|
.05,
|
|
@@ -123,6 +153,21 @@ const DURATION_BUCKETS = [
|
|
|
123
153
|
300
|
|
124
154
|
];
|
|
125
155
|
const FEE_BUCKETS = Array.from({ length: 11 }, (_, i) => 10 ** (i + 8));
|
|
156
|
+
const ERROR_CLASS = /^[A-Z][A-Za-z]{0,62}Error$|^Error$/;
|
|
157
|
+
const ERROR_CODE = /^[a-z]{1,32}(_[a-z]{1,32}){0,7}$/;
|
|
158
|
+
/**
|
|
159
|
+
* `attributes` with `error.type` kept only if it is an error class name or a lower-case code, else `_OTHER`. Error
|
|
160
|
+
* names are free text (any `Error.name`), and metrics do not pass through the `redact` hook; the span keeps its own
|
|
161
|
+
* `error.type`.
|
|
162
|
+
*/
|
|
163
|
+
function withMetricErrorType(attributes) {
|
|
164
|
+
const type = attributes[ATTR_ERROR_TYPE];
|
|
165
|
+
if (type === void 0) return attributes;
|
|
166
|
+
return typeof type === "string" && (type === "_OTHER" || ERROR_CLASS.test(type) || ERROR_CODE.test(type)) ? attributes : {
|
|
167
|
+
...attributes,
|
|
168
|
+
[ATTR_ERROR_TYPE]: ERROR_TYPE_VALUE_OTHER
|
|
169
|
+
};
|
|
170
|
+
}
|
|
126
171
|
/** Milliseconds since the epoch of a span time, as the OpenTelemetry API accepts it. */
|
|
127
172
|
function toEpochMs(time) {
|
|
128
173
|
if (time === void 0) return Date.now();
|
|
@@ -140,17 +185,17 @@ function createTxMetrics(meterProvider, name, version) {
|
|
|
140
185
|
return {
|
|
141
186
|
send: meter.createHistogram(METRIC_BLOCKCHAIN_CLIENT_SEND_DURATION, {
|
|
142
187
|
unit: "s",
|
|
143
|
-
description: "Duration of sending a transaction, until its hash is known",
|
|
188
|
+
description: "Duration of sending a transaction or user operation, until its hash is known",
|
|
144
189
|
advice: { explicitBucketBoundaries: DURATION_BUCKETS }
|
|
145
190
|
}),
|
|
146
191
|
confirmation: meter.createHistogram(METRIC_BLOCKCHAIN_CLIENT_CONFIRMATION_DURATION, {
|
|
147
192
|
unit: "s",
|
|
148
|
-
description: "Duration of waiting for a transaction receipt",
|
|
193
|
+
description: "Duration of waiting for a transaction or user operation receipt",
|
|
149
194
|
advice: { explicitBucketBoundaries: DURATION_BUCKETS }
|
|
150
195
|
}),
|
|
151
196
|
fee: meter.createHistogram(METRIC_BLOCKCHAIN_CLIENT_FEE, {
|
|
152
197
|
unit: "{wei}",
|
|
153
|
-
description: "Total fee of a mined transaction",
|
|
198
|
+
description: "Total fee of a mined transaction, or the cost of a user operation",
|
|
154
199
|
advice: { explicitBucketBoundaries: FEE_BUCKETS }
|
|
155
200
|
})
|
|
156
201
|
};
|
|
@@ -166,10 +211,10 @@ function createTxMetrics(meterProvider, name, version) {
|
|
|
166
211
|
};
|
|
167
212
|
return {
|
|
168
213
|
sendDuration: (seconds, attributes) => record("send duration", () => {
|
|
169
|
-
if (seconds >= 0) get().send.record(seconds, attributes);
|
|
214
|
+
if (seconds >= 0) get().send.record(seconds, withMetricErrorType(attributes));
|
|
170
215
|
}),
|
|
171
216
|
confirmationDuration: (seconds, attributes) => record("confirmation duration", () => {
|
|
172
|
-
if (seconds >= 0) get().confirmation.record(seconds, attributes);
|
|
217
|
+
if (seconds >= 0) get().confirmation.record(seconds, withMetricErrorType(attributes));
|
|
173
218
|
}),
|
|
174
219
|
fee: (wei, attributes) => record("fee", () => {
|
|
175
220
|
if (wei >= 0n) get().fee.record(Number(wei), attributes);
|
|
@@ -462,7 +507,7 @@ function sanitizeResource(resource) {
|
|
|
462
507
|
}
|
|
463
508
|
//#endregion
|
|
464
509
|
//#region src/version.ts
|
|
465
|
-
const VERSION = "0.
|
|
510
|
+
const VERSION = "0.8.0";
|
|
466
511
|
//#endregion
|
|
467
512
|
//#region src/tracker.ts
|
|
468
513
|
const INSTRUMENTATION_NAME = "@hashspan/core";
|
|
@@ -485,13 +530,20 @@ const NON_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
|
485
530
|
ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL,
|
|
486
531
|
ATTR_BLOCKCHAIN_PAYMENT_STATUS,
|
|
487
532
|
ATTR_BLOCKCHAIN_PAYMENT_VERIFIED,
|
|
533
|
+
ATTR_BLOCKCHAIN_USER_OPERATION_HASH,
|
|
534
|
+
ATTR_BLOCKCHAIN_USER_OPERATION_SUCCESS,
|
|
488
535
|
ATTR_ERROR_TYPE,
|
|
489
536
|
ATTR_EXCEPTION_TYPE
|
|
490
537
|
]);
|
|
491
538
|
const TX_HASH = /^0x[0-9a-fA-F]{64}$/;
|
|
492
539
|
const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
540
|
+
const ZERO_ADDRESS = /^0x0{40}$/;
|
|
493
541
|
/** A non-negative integer that fits in 256 bits. */
|
|
494
542
|
const AMOUNT = /^(0|[1-9][0-9]{0,77})$/;
|
|
543
|
+
/** A `0x` hex quantity of at most 256 bits, as JSON-RPC encodes integers. */
|
|
544
|
+
const HEX_QUANTITY = /^0x[0-9a-fA-F]{1,64}$/;
|
|
545
|
+
const MAX_UINT256 = 2n ** 256n - 1n;
|
|
546
|
+
const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
|
|
495
547
|
/** `error.type` of a wait that gave up: a confirmation or a payment whose outcome was never learned. */
|
|
496
548
|
const OBSERVER_TIMEOUT = "timeout";
|
|
497
549
|
const PAYMENT_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -510,6 +562,11 @@ const noopSend = (parent) => ({
|
|
|
510
562
|
end: () => {},
|
|
511
563
|
fail: () => {}
|
|
512
564
|
});
|
|
565
|
+
const noopUserOperationSend = (parent) => ({
|
|
566
|
+
context: parent,
|
|
567
|
+
end: () => {},
|
|
568
|
+
fail: () => {}
|
|
569
|
+
});
|
|
513
570
|
const NOOP_PAYMENT = {
|
|
514
571
|
end: () => {},
|
|
515
572
|
fail: () => {},
|
|
@@ -521,6 +578,11 @@ const NOOP_CONFIRM = {
|
|
|
521
578
|
timeout: () => {},
|
|
522
579
|
fail: () => {}
|
|
523
580
|
};
|
|
581
|
+
const NOOP_USER_OPERATION_CONFIRM = {
|
|
582
|
+
end: () => {},
|
|
583
|
+
timeout: () => {},
|
|
584
|
+
fail: () => {}
|
|
585
|
+
};
|
|
524
586
|
/** Runs `fn`, logging instead of throwing: instrumentation must never break the caller. */
|
|
525
587
|
function safely(what, fn, fallback) {
|
|
526
588
|
try {
|
|
@@ -543,6 +605,22 @@ const ERROR_TYPE_OVERRIDE = /^[A-Za-z0-9_.-]{1,64}$/;
|
|
|
543
605
|
function identifier(value) {
|
|
544
606
|
return typeof value === "string" && ERROR_TYPE_OVERRIDE.test(value) ? value : void 0;
|
|
545
607
|
}
|
|
608
|
+
/**
|
|
609
|
+
* A non-negative integer of at most 256 bits, from a bigint, a safe integer, or a decimal or `0x` hex string, as
|
|
610
|
+
* bundlers return them; undefined for anything else.
|
|
611
|
+
*/
|
|
612
|
+
function quantity(value) {
|
|
613
|
+
let parsed;
|
|
614
|
+
if (typeof value === "bigint") parsed = value;
|
|
615
|
+
else if (typeof value === "number" && Number.isSafeInteger(value)) parsed = BigInt(value);
|
|
616
|
+
else if (typeof value === "string" && (AMOUNT.test(value) || HEX_QUANTITY.test(value))) parsed = BigInt(value);
|
|
617
|
+
return parsed !== void 0 && parsed >= 0n && parsed <= MAX_UINT256 ? parsed : void 0;
|
|
618
|
+
}
|
|
619
|
+
/** A quantity as a number, or undefined when it is none or too large to be one exactly. */
|
|
620
|
+
function smallQuantity(value) {
|
|
621
|
+
const parsed = quantity(value);
|
|
622
|
+
return parsed !== void 0 && parsed <= MAX_SAFE_INTEGER ? Number(parsed) : void 0;
|
|
623
|
+
}
|
|
546
624
|
/** A decimal amount, or undefined when `value` is not a non-negative integer. */
|
|
547
625
|
function amount(value) {
|
|
548
626
|
const text = typeof value === "bigint" ? value.toString() : value;
|
|
@@ -588,8 +666,44 @@ function reportedErrorType(error, options) {
|
|
|
588
666
|
return errorType(error);
|
|
589
667
|
}
|
|
590
668
|
/**
|
|
669
|
+
* Joins the confirm span for `hash` in `registry`, opening it with `open` for the first handle; undefined when the
|
|
670
|
+
* key recently got a receipt. A receipt from any handle ends the span; a timeout or failure only ends it when it is the
|
|
671
|
+
* last handle still waiting.
|
|
672
|
+
*/
|
|
673
|
+
function joinConfirm(registry, chainId, hash, open) {
|
|
674
|
+
const current = registry.get(chainId, hash);
|
|
675
|
+
if (current === "settled") return void 0;
|
|
676
|
+
let confirm = current;
|
|
677
|
+
if (!confirm) {
|
|
678
|
+
confirm = open();
|
|
679
|
+
registry.start(chainId, hash, confirm);
|
|
680
|
+
}
|
|
681
|
+
const shared = confirm;
|
|
682
|
+
shared.active += 1;
|
|
683
|
+
let done = false;
|
|
684
|
+
return {
|
|
685
|
+
shared,
|
|
686
|
+
receive: () => {
|
|
687
|
+
if (done || shared.ended) return false;
|
|
688
|
+
done = true;
|
|
689
|
+
shared.active -= 1;
|
|
690
|
+
shared.ended = true;
|
|
691
|
+
return true;
|
|
692
|
+
},
|
|
693
|
+
withdraw: (end) => {
|
|
694
|
+
if (done || shared.ended) return;
|
|
695
|
+
done = true;
|
|
696
|
+
shared.active -= 1;
|
|
697
|
+
if (shared.active > 0) return;
|
|
698
|
+
shared.ended = true;
|
|
699
|
+
registry.release(chainId, hash, shared);
|
|
700
|
+
end();
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
591
705
|
* Creates a tracker that records transactions as `send` and `confirm` spans, and payments as `payment` spans, with
|
|
592
|
-
* `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
706
|
+
* `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/semconv.md). It makes
|
|
593
707
|
* no network calls; the caller passes hashes and receipts. Its methods and handles never throw: failures are logged
|
|
594
708
|
* via `diag`, and a method that fails returns a handle that records nothing.
|
|
595
709
|
*/
|
|
@@ -605,6 +719,14 @@ function createTxTracker(options = {}) {
|
|
|
605
719
|
const formatAddress = safely("configure address mode", () => resolveAddressFormatter(options.address), OFF_ADDRESS_FORMATTER);
|
|
606
720
|
const errorMessages = safely("configure error message mode", () => resolveErrorMessageMode(options.errorMessages), "off");
|
|
607
721
|
const paymentResource = safely("configure payment resource mode", () => resolvePaymentResourceMode(options.paymentResource), "off");
|
|
722
|
+
const userOperationLinks = new LinkStore({
|
|
723
|
+
ttlMs: options.linkTtlMs ?? DEFAULT_LINK_TTL_MS,
|
|
724
|
+
maxEntries: options.maxTrackedTransactions ?? DEFAULT_MAX_TRACKED
|
|
725
|
+
});
|
|
726
|
+
const userOperationConfirmations = new ConfirmRegistry({
|
|
727
|
+
ttlMs: options.linkTtlMs ?? DEFAULT_LINK_TTL_MS,
|
|
728
|
+
maxEntries: options.maxTrackedTransactions ?? DEFAULT_MAX_TRACKED
|
|
729
|
+
});
|
|
608
730
|
const txMetrics = createTxMetrics(options.meterProvider, INSTRUMENTATION_NAME, VERSION);
|
|
609
731
|
/** Attributes of a metric: low-cardinality only, never an address, hash or agent identity. */
|
|
610
732
|
const metricAttributes = (chainId, extra = {}) => ({
|
|
@@ -612,6 +734,11 @@ function createTxTracker(options = {}) {
|
|
|
612
734
|
[ATTR_BLOCKCHAIN_CHAIN_ID]: chainId,
|
|
613
735
|
...extra
|
|
614
736
|
});
|
|
737
|
+
/** Metric attributes of a user operation sample: as for transactions, plus what the sample is about. */
|
|
738
|
+
const userOperationMetricAttributes = (chainId, extra = {}) => metricAttributes(chainId, {
|
|
739
|
+
[ATTR_BLOCKCHAIN_OPERATION_SUBJECT]: BLOCKCHAIN_OPERATION_SUBJECT_VALUE_USER_OPERATION,
|
|
740
|
+
...extra
|
|
741
|
+
});
|
|
615
742
|
const secondsSince = (startMs, endTime) => (toEpochMs(endTime) - startMs) / 1e3;
|
|
616
743
|
let tracer;
|
|
617
744
|
const getTracer = () => {
|
|
@@ -637,7 +764,7 @@ function createTxTracker(options = {}) {
|
|
|
637
764
|
/**
|
|
638
765
|
* Exception event attributes for `error`, per the error message mode. The error object itself is never handed to
|
|
639
766
|
* the SDK: its message and stack can carry addresses and calldata
|
|
640
|
-
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
767
|
+
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0006-error-privacy.md).
|
|
641
768
|
*/
|
|
642
769
|
const exceptionAttributes = (type, error) => {
|
|
643
770
|
const attributes = { [ATTR_EXCEPTION_TYPE]: type };
|
|
@@ -692,8 +819,8 @@ function createTxTracker(options = {}) {
|
|
|
692
819
|
const formatted = formatAddress(address);
|
|
693
820
|
if (formatted !== void 0) attributes[key] = formatted;
|
|
694
821
|
};
|
|
695
|
-
/** Records `address` only if it is one: payment addresses come from remote parties. */
|
|
696
|
-
const
|
|
822
|
+
/** Records `address` only if it is one: payment and user operation addresses come from remote parties. */
|
|
823
|
+
const setRemoteAddress = (attributes, key, address) => {
|
|
697
824
|
if (typeof address === "string" && ADDRESS.test(address)) setAddress(attributes, key, address);
|
|
698
825
|
};
|
|
699
826
|
const baseAttributes = (chainId, operation, ctx) => ({
|
|
@@ -829,7 +956,7 @@ function createTxTracker(options = {}) {
|
|
|
829
956
|
};
|
|
830
957
|
/**
|
|
831
958
|
* Ends `shared` with `receipt`, attributing it to the transaction that was mined
|
|
832
|
-
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.
|
|
959
|
+
* (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.8.0/docs/adr/0008-replaced-transactions.md).
|
|
833
960
|
*/
|
|
834
961
|
const endWithReceipt = (chainId, hash, shared, receipt, endTime) => {
|
|
835
962
|
const mined = receipt.transactionHash;
|
|
@@ -859,41 +986,22 @@ function createTxTracker(options = {}) {
|
|
|
859
986
|
*/
|
|
860
987
|
const startConfirm = (input, parentCtx) => {
|
|
861
988
|
const { chainId, hash } = input;
|
|
862
|
-
const
|
|
863
|
-
if (
|
|
864
|
-
|
|
865
|
-
if (!confirm) {
|
|
866
|
-
confirm = openConfirm(input, parentCtx);
|
|
867
|
-
confirmations.start(chainId, hash, confirm);
|
|
868
|
-
}
|
|
869
|
-
const shared = confirm;
|
|
870
|
-
shared.active += 1;
|
|
871
|
-
let done = false;
|
|
872
|
-
const withdraw = (end) => {
|
|
873
|
-
if (done || shared.ended) return;
|
|
874
|
-
done = true;
|
|
875
|
-
shared.active -= 1;
|
|
876
|
-
if (shared.active > 0) return;
|
|
877
|
-
shared.ended = true;
|
|
878
|
-
confirmations.release(chainId, hash, shared);
|
|
879
|
-
end();
|
|
880
|
-
};
|
|
989
|
+
const claim = joinConfirm(confirmations, chainId, hash, () => openConfirm(input, parentCtx));
|
|
990
|
+
if (!claim) return NOOP_CONFIRM;
|
|
991
|
+
const { shared } = claim;
|
|
881
992
|
return {
|
|
882
993
|
end: (receipt, second) => {
|
|
883
994
|
const { endTime } = handleOptions(second);
|
|
884
|
-
if (
|
|
885
|
-
done = true;
|
|
886
|
-
shared.active -= 1;
|
|
887
|
-
shared.ended = true;
|
|
995
|
+
if (!claim.receive()) return;
|
|
888
996
|
safely("record receipt", () => endWithReceipt(chainId, hash, shared, receipt, endTime), void 0);
|
|
889
997
|
},
|
|
890
998
|
timeout: (second) => {
|
|
891
999
|
const { endTime } = handleOptions(second);
|
|
892
|
-
withdraw(() => shared.timeout(endTime));
|
|
1000
|
+
claim.withdraw(() => shared.timeout(endTime));
|
|
893
1001
|
},
|
|
894
1002
|
fail: (error, second) => {
|
|
895
1003
|
const { endTime } = handleOptions(second);
|
|
896
|
-
withdraw(() => shared.fail(error, endTime));
|
|
1004
|
+
claim.withdraw(() => shared.fail(error, endTime));
|
|
897
1005
|
}
|
|
898
1006
|
};
|
|
899
1007
|
};
|
|
@@ -902,10 +1010,10 @@ function createTxTracker(options = {}) {
|
|
|
902
1010
|
const attributes = baseAttributes(input.chainId, BLOCKCHAIN_OPERATION_NAME_VALUE_PAYMENT, parent);
|
|
903
1011
|
const protocol = identifier(input.protocol);
|
|
904
1012
|
if (protocol !== void 0) attributes[ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL] = protocol;
|
|
905
|
-
|
|
1013
|
+
setRemoteAddress(attributes, ATTR_BLOCKCHAIN_PAYMENT_PAYER, input.payer);
|
|
906
1014
|
const knownPayer = typeof input.payer === "string" && ADDRESS.test(input.payer);
|
|
907
|
-
|
|
908
|
-
|
|
1015
|
+
setRemoteAddress(attributes, ATTR_BLOCKCHAIN_PAYMENT_RECIPIENT, input.recipient);
|
|
1016
|
+
setRemoteAddress(attributes, ATTR_BLOCKCHAIN_PAYMENT_ASSET, input.asset);
|
|
909
1017
|
const paid = amount(input.amount);
|
|
910
1018
|
if (paid !== void 0) attributes[ATTR_BLOCKCHAIN_PAYMENT_AMOUNT] = paid;
|
|
911
1019
|
const scheme = identifier(input.x402?.scheme);
|
|
@@ -937,7 +1045,7 @@ function createTxTracker(options = {}) {
|
|
|
937
1045
|
const settled = { [ATTR_BLOCKCHAIN_PAYMENT_STATUS]: status };
|
|
938
1046
|
const hash = settlement.hash;
|
|
939
1047
|
if (linkHash(hash)) settled[ATTR_BLOCKCHAIN_TX_HASH] = hash;
|
|
940
|
-
if (!knownPayer)
|
|
1048
|
+
if (!knownPayer) setRemoteAddress(settled, ATTR_BLOCKCHAIN_PAYMENT_PAYER, settlement.payer);
|
|
941
1049
|
const settledAmount = amount(settlement.amount);
|
|
942
1050
|
if (settledAmount !== void 0) {
|
|
943
1051
|
settled[ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT] = settledAmount;
|
|
@@ -958,11 +1066,139 @@ function createTxTracker(options = {}) {
|
|
|
958
1066
|
link: (hash) => safely("link the payment span", () => void linkHash(hash), void 0)
|
|
959
1067
|
};
|
|
960
1068
|
};
|
|
1069
|
+
const startUserOperationSend = (input, parentCtx) => {
|
|
1070
|
+
const parent = parentCtx ?? context.active();
|
|
1071
|
+
const { chainId } = input;
|
|
1072
|
+
const attributes = baseAttributes(chainId, BLOCKCHAIN_OPERATION_NAME_VALUE_SEND, parent);
|
|
1073
|
+
setRemoteAddress(attributes, ATTR_BLOCKCHAIN_USER_OPERATION_SENDER, input.sender);
|
|
1074
|
+
setRemoteAddress(attributes, ATTR_BLOCKCHAIN_USER_OPERATION_ENTRY_POINT, input.entryPoint);
|
|
1075
|
+
const callCount = smallQuantity(input.callCount);
|
|
1076
|
+
if (callCount !== void 0) attributes[ATTR_BLOCKCHAIN_USER_OPERATION_CALL_COUNT] = callCount;
|
|
1077
|
+
const span = getTracer().startSpan(`send ${chainId}`, {
|
|
1078
|
+
kind: SpanKind.CLIENT,
|
|
1079
|
+
attributes: redact(attributes),
|
|
1080
|
+
...input.startTime !== void 0 ? { startTime: input.startTime } : {}
|
|
1081
|
+
}, parent);
|
|
1082
|
+
const finish = finisher(span);
|
|
1083
|
+
const startMs = toEpochMs(input.startTime);
|
|
1084
|
+
const recordSend = (endTime, errorType) => txMetrics.sendDuration(secondsSince(startMs, endTime), userOperationMetricAttributes(chainId, errorType === void 0 ? {} : { [ATTR_ERROR_TYPE]: errorType }));
|
|
1085
|
+
return {
|
|
1086
|
+
context: trace.setSpan(parent, span),
|
|
1087
|
+
end: (result, second) => {
|
|
1088
|
+
const { endTime } = handleOptions(second);
|
|
1089
|
+
finish("record user operation hash", () => {
|
|
1090
|
+
const hash = result?.userOpHash;
|
|
1091
|
+
if (typeof hash !== "string" || !TX_HASH.test(hash)) {
|
|
1092
|
+
diag.debug("hashspan: ending a send span without a valid user operation hash");
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
userOperationLinks.set(chainId, hash, {
|
|
1096
|
+
spanContext: span.spanContext(),
|
|
1097
|
+
parent
|
|
1098
|
+
});
|
|
1099
|
+
span.setAttributes(redact({ [ATTR_BLOCKCHAIN_USER_OPERATION_HASH]: hash }));
|
|
1100
|
+
recordSend(endTime);
|
|
1101
|
+
}, endTime);
|
|
1102
|
+
},
|
|
1103
|
+
fail: (error, second) => {
|
|
1104
|
+
const read = handleOptions(second);
|
|
1105
|
+
finish("record user operation send failure", () => recordSend(read.endTime, markError(span, reportedErrorType(error, read), error, errorType(error))), read.endTime);
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
1108
|
+
};
|
|
1109
|
+
/**
|
|
1110
|
+
* Attributes of a user operation receipt. Its values come from a bundler: what is malformed is left out. The bundle
|
|
1111
|
+
* transaction's status, gas and fee are not recorded: they cover every operation in the bundle (ADR 0021).
|
|
1112
|
+
*/
|
|
1113
|
+
const userOperationReceiptAttributes = (receipt) => {
|
|
1114
|
+
const attributes = {};
|
|
1115
|
+
const success = receipt.success;
|
|
1116
|
+
if (typeof success === "boolean") attributes[ATTR_BLOCKCHAIN_USER_OPERATION_SUCCESS] = success;
|
|
1117
|
+
const gasUsed = smallQuantity(receipt.actualGasUsed);
|
|
1118
|
+
if (gasUsed !== void 0) attributes[ATTR_BLOCKCHAIN_USER_OPERATION_GAS_USED] = gasUsed;
|
|
1119
|
+
const gasCost = quantity(receipt.actualGasCost);
|
|
1120
|
+
if (gasCost !== void 0) attributes[ATTR_BLOCKCHAIN_USER_OPERATION_GAS_COST] = gasCost.toString();
|
|
1121
|
+
setRemoteAddress(attributes, ATTR_BLOCKCHAIN_USER_OPERATION_SENDER, receipt.sender);
|
|
1122
|
+
const nonce = quantity(receipt.nonce);
|
|
1123
|
+
if (nonce !== void 0) attributes[ATTR_BLOCKCHAIN_USER_OPERATION_NONCE] = nonce.toString();
|
|
1124
|
+
const paymaster = receipt.paymaster;
|
|
1125
|
+
if (typeof paymaster === "string" && !ZERO_ADDRESS.test(paymaster)) setRemoteAddress(attributes, ATTR_BLOCKCHAIN_USER_OPERATION_PAYMASTER, paymaster);
|
|
1126
|
+
setRemoteAddress(attributes, ATTR_BLOCKCHAIN_USER_OPERATION_ENTRY_POINT, receipt.entryPoint);
|
|
1127
|
+
const reason = receipt.revertReason;
|
|
1128
|
+
if (typeof reason === "string") attributes[ATTR_BLOCKCHAIN_TX_REVERT_REASON] = formatAddressesIn(reason, formatAddress);
|
|
1129
|
+
const bundle = receipt.transactionHash;
|
|
1130
|
+
if (typeof bundle === "string" && TX_HASH.test(bundle)) attributes[ATTR_BLOCKCHAIN_TX_HASH] = bundle;
|
|
1131
|
+
const block = smallQuantity(receipt.blockNumber);
|
|
1132
|
+
if (block !== void 0) attributes[ATTR_BLOCKCHAIN_BLOCK_NUMBER] = block;
|
|
1133
|
+
return attributes;
|
|
1134
|
+
};
|
|
1135
|
+
/** Opens the confirm span of a user operation. */
|
|
1136
|
+
const openUserOperationConfirm = (input, parentCtx) => {
|
|
1137
|
+
const { chainId, userOpHash } = input;
|
|
1138
|
+
const sent = userOperationLinks.get(chainId, userOpHash);
|
|
1139
|
+
const active = context.active();
|
|
1140
|
+
const parent = parentCtx ?? (trace.getSpan(active) ? active : sent?.parent ?? active);
|
|
1141
|
+
const attributes = baseAttributes(chainId, BLOCKCHAIN_OPERATION_NAME_VALUE_CONFIRM, parent);
|
|
1142
|
+
attributes[ATTR_BLOCKCHAIN_USER_OPERATION_HASH] = userOpHash;
|
|
1143
|
+
const span = getTracer().startSpan(`confirm ${chainId}`, {
|
|
1144
|
+
kind: SpanKind.CLIENT,
|
|
1145
|
+
attributes: redact(attributes),
|
|
1146
|
+
links: sent ? [{ context: sent.spanContext }] : [],
|
|
1147
|
+
...input.startTime !== void 0 ? { startTime: input.startTime } : {}
|
|
1148
|
+
}, parent);
|
|
1149
|
+
const finish = finisher(span);
|
|
1150
|
+
const startMs = toEpochMs(input.startTime);
|
|
1151
|
+
const recordConfirmation = (endTime, outcome) => txMetrics.confirmationDuration(secondsSince(startMs, endTime), userOperationMetricAttributes(chainId, outcome));
|
|
1152
|
+
return {
|
|
1153
|
+
active: 0,
|
|
1154
|
+
ended: false,
|
|
1155
|
+
receipt: (receipt, endTime) => finish("record user operation receipt", () => {
|
|
1156
|
+
const attributes = userOperationReceiptAttributes(receipt ?? {});
|
|
1157
|
+
span.setAttributes(redact(attributes));
|
|
1158
|
+
const success = attributes[ATTR_BLOCKCHAIN_USER_OPERATION_SUCCESS];
|
|
1159
|
+
if (success === false) markError(span, BLOCKCHAIN_TX_STATUS_VALUE_REVERTED);
|
|
1160
|
+
const outcome = typeof success === "boolean" ? { [ATTR_BLOCKCHAIN_USER_OPERATION_SUCCESS]: success } : {};
|
|
1161
|
+
recordConfirmation(endTime, outcome);
|
|
1162
|
+
const cost = attributes[ATTR_BLOCKCHAIN_USER_OPERATION_GAS_COST];
|
|
1163
|
+
if (typeof cost === "string") txMetrics.fee(BigInt(cost), userOperationMetricAttributes(chainId, outcome));
|
|
1164
|
+
}, endTime),
|
|
1165
|
+
timeout: (endTime) => finish("record user operation confirmation timeout", () => recordConfirmation(endTime, { [ATTR_ERROR_TYPE]: markError(span, OBSERVER_TIMEOUT) }), endTime),
|
|
1166
|
+
fail: (error, read) => finish("record user operation confirmation failure", () => recordConfirmation(read.endTime, { [ATTR_ERROR_TYPE]: markError(span, reportedErrorType(error, read), error, errorType(error)) }), read.endTime)
|
|
1167
|
+
};
|
|
1168
|
+
};
|
|
1169
|
+
const startUserOperationConfirm = (input, parentCtx) => {
|
|
1170
|
+
const { chainId, userOpHash } = input;
|
|
1171
|
+
if (typeof userOpHash !== "string" || !TX_HASH.test(userOpHash)) {
|
|
1172
|
+
diag.debug("hashspan: not confirming a user operation without a valid hash");
|
|
1173
|
+
return NOOP_USER_OPERATION_CONFIRM;
|
|
1174
|
+
}
|
|
1175
|
+
const claim = joinConfirm(userOperationConfirmations, chainId, userOpHash, () => openUserOperationConfirm(input, parentCtx));
|
|
1176
|
+
if (!claim) return NOOP_USER_OPERATION_CONFIRM;
|
|
1177
|
+
const { shared } = claim;
|
|
1178
|
+
return {
|
|
1179
|
+
end: (receipt, second) => {
|
|
1180
|
+
const { endTime } = handleOptions(second);
|
|
1181
|
+
if (!claim.receive()) return;
|
|
1182
|
+
userOperationConfirmations.settle(chainId, userOpHash, shared);
|
|
1183
|
+
shared.receipt(receipt, endTime);
|
|
1184
|
+
},
|
|
1185
|
+
timeout: (second) => {
|
|
1186
|
+
const { endTime } = handleOptions(second);
|
|
1187
|
+
claim.withdraw(() => shared.timeout(endTime));
|
|
1188
|
+
},
|
|
1189
|
+
fail: (error, second) => {
|
|
1190
|
+
const read = handleOptions(second);
|
|
1191
|
+
claim.withdraw(() => shared.fail(error, read));
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
};
|
|
961
1195
|
return {
|
|
962
1196
|
startSend: (input, parent) => safely("start send span", () => startSend(input, parent), noopSend(parent ?? context.active())),
|
|
963
1197
|
startConfirm: (input, parent) => safely("start confirm span", () => startConfirm(input, parent), NOOP_CONFIRM),
|
|
964
|
-
startPayment: (input, parent) => safely("start payment span", () => startPayment(input, parent), NOOP_PAYMENT)
|
|
1198
|
+
startPayment: (input, parent) => safely("start payment span", () => startPayment(input, parent), NOOP_PAYMENT),
|
|
1199
|
+
startUserOperationSend: (input, parent) => safely("start user operation send span", () => startUserOperationSend(input, parent), noopUserOperationSend(parent ?? context.active())),
|
|
1200
|
+
startUserOperationConfirm: (input, parent) => safely("start user operation confirm span", () => startUserOperationConfirm(input, parent), NOOP_USER_OPERATION_CONFIRM)
|
|
965
1201
|
};
|
|
966
1202
|
}
|
|
967
1203
|
//#endregion
|
|
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 };
|
|
1204
|
+
export { ATTR_BLOCKCHAIN_BLOCK_NUMBER, ATTR_BLOCKCHAIN_CHAIN_ID, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR, ATTR_BLOCKCHAIN_OPERATION_NAME, ATTR_BLOCKCHAIN_OPERATION_SUBJECT, ATTR_BLOCKCHAIN_PAYMENT_AMOUNT, ATTR_BLOCKCHAIN_PAYMENT_ASSET, ATTR_BLOCKCHAIN_PAYMENT_PAYER, ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL, ATTR_BLOCKCHAIN_PAYMENT_RECIPIENT, ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT, ATTR_BLOCKCHAIN_PAYMENT_STATUS, ATTR_BLOCKCHAIN_PAYMENT_VERIFIED, ATTR_BLOCKCHAIN_SYSTEM, ATTR_BLOCKCHAIN_TX_EFFECTIVE_GAS_PRICE, ATTR_BLOCKCHAIN_TX_FEE, ATTR_BLOCKCHAIN_TX_FROM, ATTR_BLOCKCHAIN_TX_GAS_USED, ATTR_BLOCKCHAIN_TX_HASH, ATTR_BLOCKCHAIN_TX_L1_FEE, ATTR_BLOCKCHAIN_TX_NONCE, ATTR_BLOCKCHAIN_TX_REPLACEMENT_HASH, ATTR_BLOCKCHAIN_TX_REPLACEMENT_REASON, ATTR_BLOCKCHAIN_TX_REVERT_REASON, ATTR_BLOCKCHAIN_TX_STATUS, ATTR_BLOCKCHAIN_TX_TO, ATTR_BLOCKCHAIN_TX_VALUE, ATTR_BLOCKCHAIN_USER_OPERATION_CALL_COUNT, ATTR_BLOCKCHAIN_USER_OPERATION_ENTRY_POINT, ATTR_BLOCKCHAIN_USER_OPERATION_GAS_COST, ATTR_BLOCKCHAIN_USER_OPERATION_GAS_USED, ATTR_BLOCKCHAIN_USER_OPERATION_HASH, ATTR_BLOCKCHAIN_USER_OPERATION_NONCE, ATTR_BLOCKCHAIN_USER_OPERATION_PAYMASTER, ATTR_BLOCKCHAIN_USER_OPERATION_SENDER, ATTR_BLOCKCHAIN_USER_OPERATION_SUCCESS, ATTR_ERROR_TYPE, ATTR_GEN_AI_AGENT_ID, ATTR_GEN_AI_AGENT_NAME, ATTR_X402_RESOURCE, ATTR_X402_SCHEME, BLOCKCHAIN_OPERATION_NAME_VALUE_CONFIRM, BLOCKCHAIN_OPERATION_NAME_VALUE_PAYMENT, BLOCKCHAIN_OPERATION_NAME_VALUE_SEND, BLOCKCHAIN_OPERATION_SUBJECT_VALUE_USER_OPERATION, BLOCKCHAIN_PAYMENT_PROTOCOL_VALUE_X402, BLOCKCHAIN_PAYMENT_STATUS_VALUE_FAILED, BLOCKCHAIN_PAYMENT_STATUS_VALUE_PENDING, BLOCKCHAIN_PAYMENT_STATUS_VALUE_SETTLED, BLOCKCHAIN_SYSTEM_VALUE_EVM, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_CANCELLED, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_REPLACED, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_REPRICED, BLOCKCHAIN_TX_STATUS_VALUE_REPLACED, BLOCKCHAIN_TX_STATUS_VALUE_REVERTED, BLOCKCHAIN_TX_STATUS_VALUE_SUCCESS, BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT, ERROR_TYPE_VALUE_OTHER, METRIC_BLOCKCHAIN_CLIENT_CONFIRMATION_DURATION, METRIC_BLOCKCHAIN_CLIENT_FEE, METRIC_BLOCKCHAIN_CLIENT_SEND_DURATION, VERSION, createTxTracker };
|