uvd-x402-sdk 2.85.0 → 2.87.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.
@@ -0,0 +1,3865 @@
1
+ import { S as SigningWalletAdapter } from './wallet-w7BnImDG.mjs';
2
+ import { X as X402Version, i as X402Header, j as X402PayloadData } from './index-DOBhTF-j.mjs';
3
+
4
+ /**
5
+ * uvd-x402-sdk - Signed lifecycle orders for `release` / `refundInEscrow`.
6
+ *
7
+ * Both actions move money that is ALREADY escrowed, so neither carries an
8
+ * ERC-3009 signature: there is no transfer left to authorize. That left the
9
+ * other half of the question open — who is entitled to ask for the move —
10
+ * and the de-facto answer was "whoever calls". On 2026-08-30 a third party
11
+ * probed exactly that: five calls with a fabricated `paymentInfo`, two of
12
+ * them mined, gas spent.
13
+ *
14
+ * This module is the client half of the answer: an EIP-712 order signed by
15
+ * the party the action belongs to, hung off `payload.lifecycleAuth`.
16
+ *
17
+ * >>> THE SIGNER IS INJECTED <<<
18
+ * The SDK never reaches for a key on its own. Any adapter that can sign
19
+ * typed data works — `EnvKeyAdapter` (server / CLI), `OWSWalletAdapter`, a
20
+ * wagmi/viem wallet client in the browser ({@link wagmiLifecycleSigner}), a
21
+ * KMS. In H2A/H2H the PAYER signs in their own browser with the same wallet
22
+ * they paid with, and the marketplace merely transports the block.
23
+ *
24
+ * >>> TWO SHAPES, ONE ORDER <<<
25
+ * `buildLifecycleAuth` does the whole ceremony in one call: it mints the
26
+ * nonce and the deadline, signs through the injected adapter, and returns the
27
+ * block. That is the server and the `wagmi` paths.
28
+ *
29
+ * A publisher's browser cannot use it. There the backend assembles the
30
+ * document — nonce and deadline included — the browser signs whatever it was
31
+ * handed, and gives back a signature and nothing else. That is
32
+ * {@link buildLifecycleTypedData} on one side and
33
+ * {@link lifecycleAuthFromSignature} on the other, and it produces the SAME
34
+ * bytes as the one-call path for the same nonce and deadline, pinned in
35
+ * `src/lifecycle-auth.test.ts`.
36
+ *
37
+ * Source of truth for the format is the facilitator, `x402-rs`
38
+ * `src/payment_operator/lifecycle_auth.rs` (PR #21). The Python twin is
39
+ * `uvd_x402_sdk.escrow_signing.build_lifecycle_auth` (0.78.0); the two must
40
+ * produce byte-identical signatures, pinned here by the shared vector in
41
+ * `src/lifecycle-auth.test.ts`.
42
+ *
43
+ * Facilitator rollout is by `ESCROW_LIFECYCLE_AUTH`: `off` (default, the
44
+ * order is not looked at), `log` (verify when present, log the verdict,
45
+ * never reject) and `enforce`. That is why `lifecycleSigner` is OPTIONAL
46
+ * everywhere: without one, the request goes out byte-for-byte as before.
47
+ *
48
+ * @example Server-side (EnvKeyAdapter)
49
+ * ```typescript
50
+ * import { buildLifecycleAuth, EnvKeyAdapter } from 'uvd-x402-sdk';
51
+ *
52
+ * const auth = await buildLifecycleAuth({
53
+ * action: 'release',
54
+ * paymentInfo: pi, // the SAME camelCase object that is sent
55
+ * payer: payerAddress, // payload.payer — NOT inside paymentInfo
56
+ * amount: '1000000', // the SAME as payload.amount
57
+ * chainId: 8453,
58
+ * wallet: new EnvKeyAdapter(),
59
+ * });
60
+ * // -> payload.lifecycleAuth = auth
61
+ * ```
62
+ */
63
+ /**
64
+ * EIP-712 domain. `chainId` is the payment network's; there is NO
65
+ * `verifyingContract` because `paymentInfo.operator` already rides inside the
66
+ * signed struct. Copied from `lifecycle_auth.rs:69-70` (DOMAIN_NAME /
67
+ * DOMAIN_VERSION).
68
+ */
69
+ declare const LIFECYCLE_DOMAIN_NAME = "x402 escrow lifecycle";
70
+ declare const LIFECYCLE_DOMAIN_VERSION = "1";
71
+ /**
72
+ * The facilitator's `deadline` ceiling: 900 s (`lifecycle_auth.rs:64`,
73
+ * `DEFAULT_MAX_DEADLINE_SECS`). A leaked order is not a standing permission.
74
+ */
75
+ declare const LIFECYCLE_MAX_DEADLINE_SECS = 900;
76
+ /**
77
+ * What we sign by default, leaving 300 s of headroom under the ceiling.
78
+ * Signing the full 900 s is an order that a facilitator clock five seconds
79
+ * behind already reads as `deadline_too_far`: the margin is not cosmetic, it
80
+ * is what separates a valid order from a rejection the caller cannot explain.
81
+ */
82
+ declare const LIFECYCLE_DEFAULT_DEADLINE_SECS = 600;
83
+ /** The two wire values, which are also what enters the signature. */
84
+ declare const LIFECYCLE_ACTIONS: readonly ["release", "refundInEscrow"];
85
+ type LifecycleAction = (typeof LIFECYCLE_ACTIONS)[number];
86
+ /**
87
+ * The EIP-712 types. `PaymentInfo` is the AuthCaptureEscrow type string
88
+ * VERBATIM — the same one this SDK already types for ERC-3009. The field
89
+ * ORDER is part of the type hash: reordering it invalidates every order ever
90
+ * issued. Exact mirror of `lifecycle_auth.rs:73-99`.
91
+ */
92
+ declare const LIFECYCLE_ORDER_TYPES: Record<string, Array<{
93
+ name: string;
94
+ type: string;
95
+ }>>;
96
+ /** The wire `paymentInfo` of a lifecycle request (camelCase, `salt` in hex). */
97
+ interface LifecyclePaymentInfo {
98
+ operator: string;
99
+ receiver: string;
100
+ token: string;
101
+ /** Atomic units (uint120 on-chain). */
102
+ maxAmount: string | number | bigint;
103
+ preApprovalExpiry: number | string;
104
+ authorizationExpiry: number | string;
105
+ refundExpiry: number | string;
106
+ minFeeBps: number | string;
107
+ maxFeeBps: number | string;
108
+ feeReceiver: string;
109
+ /** 32-byte hex on the wire; uint256 in the signature. */
110
+ salt: string | number | bigint;
111
+ }
112
+ /**
113
+ * Minimal signer for {@link buildLifecycleAuth}: an address plus EIP-712
114
+ * typed-data signing over the full typed-data JSON string. This is the same
115
+ * contract as `SigningWalletAdapter`, so `EnvKeyAdapter` and
116
+ * `OWSWalletAdapter` satisfy it structurally, and a browser wallet only has
117
+ * to wrap its own `signTypedData` in this shape — the key never leaves it.
118
+ */
119
+ interface LifecycleSigner {
120
+ /** The EVM address that will be claimed as `lifecycleAuth.signer`. */
121
+ getAddress(): string;
122
+ /** Sign the full EIP-712 JSON (`domain`, `types`, `primaryType`, `message`). */
123
+ signTypedData(typedData: string): Promise<{
124
+ signature: string;
125
+ }>;
126
+ }
127
+ /** The block that goes in `payload.lifecycleAuth`. */
128
+ interface LifecycleAuth {
129
+ /** Who claims to have signed. Checked against the recovered address. */
130
+ signer: string;
131
+ /** Unix seconds after which the order is dead. */
132
+ deadline: number;
133
+ /** 32-byte hex replay guard, consumed by the facilitator on success. */
134
+ nonce: string;
135
+ /** 65-byte EIP-712 signature over the `LifecycleOrder`. */
136
+ signature: string;
137
+ }
138
+ interface LifecycleTypedDataParams {
139
+ /** `'release'` or `'refundInEscrow'`. */
140
+ action: LifecycleAction;
141
+ /**
142
+ * The paymentInfo EXACTLY as it is serialized on the wire. This same object
143
+ * is signed, which is what keeps the two ends from drifting.
144
+ */
145
+ paymentInfo: LifecyclePaymentInfo;
146
+ /** The escrow's payer — travels as `payload.payer`, not inside paymentInfo. */
147
+ payer: string;
148
+ /** Atomic amount, the SAME one as `payload.amount`. */
149
+ amount: string | number | bigint;
150
+ /** EVM chain id of the payment network. */
151
+ chainId: number;
152
+ /**
153
+ * Unix seconds after which the order is dead. Default: `now + 600`, which
154
+ * leaves 300 s of headroom under the facilitator's 900 s ceiling.
155
+ */
156
+ deadline?: number;
157
+ /**
158
+ * 32 bytes, hex. Default: a fresh random one. Distinct PER ORDER — the
159
+ * facilitator consumes it on acceptance.
160
+ */
161
+ nonce?: string;
162
+ /** Unix seconds, for the `deadline` default and for tests. Default: the clock. */
163
+ now?: number;
164
+ }
165
+ /**
166
+ * The EIP-712 document itself: what {@link buildLifecycleTypedData} returns and
167
+ * what {@link lifecycleAuthFromSignature} reads back.
168
+ *
169
+ * Named because it is a WIRE type in the split flow: a backend builds it,
170
+ * ships it to a browser as JSON, and the browser hands it to `signTypedData`.
171
+ * `types` carries no `EIP712Domain` entry — ethers and viem both derive that
172
+ * one, and including it makes ethers throw `ambiguous primary types`.
173
+ */
174
+ interface LifecycleTypedData {
175
+ domain: {
176
+ name: string;
177
+ version: string;
178
+ chainId: number;
179
+ };
180
+ types: Record<string, Array<{
181
+ name: string;
182
+ type: string;
183
+ }>>;
184
+ primaryType: 'LifecycleOrder';
185
+ message: Record<string, unknown>;
186
+ }
187
+ interface BuildLifecycleAuthParams extends LifecycleTypedDataParams {
188
+ /**
189
+ * The signer. Must be the payer, the receiver or the operator owner
190
+ * depending on the action — see the table in the module docblock.
191
+ */
192
+ wallet: LifecycleSigner;
193
+ }
194
+ /**
195
+ * The exact EIP-712 document a lifecycle order signs.
196
+ *
197
+ * Kept separate from {@link buildLifecycleAuth} because it is the useful
198
+ * seam: it is what the Python twin mirrors, what a test can poke field by
199
+ * field to confirm the signature stops verifying, and — the reason it is
200
+ * PUBLIC — what a backend hands to a browser that signs.
201
+ *
202
+ * {@link buildLifecycleAuth} cannot serve that browser: it takes an injected
203
+ * `WalletAdapter` and mints its own nonce and deadline, while a publisher
204
+ * signs a document the backend already assembled and hands back only a
205
+ * signature. Build it here, ship it, and reassemble the wire block with
206
+ * {@link lifecycleAuthFromSignature} — which reads `deadline` and `nonce`
207
+ * back out of this same document, so the two cannot disagree.
208
+ *
209
+ * @example The split flow, backend half
210
+ * ```typescript
211
+ * const typedData = buildLifecycleTypedData({
212
+ * action: 'release',
213
+ * paymentInfo: pi, // the SAME object that will be sent
214
+ * payer, // payload.payer
215
+ * amount, // the SAME as payload.amount
216
+ * chainId: 8453,
217
+ * }); // deadline -> now + 600, nonce -> 32 fresh bytes
218
+ * // -> res.json({ typedData }) ... the browser signs it and posts back a signature
219
+ * const lifecycleAuth = lifecycleAuthFromSignature(typedData, signature, payer);
220
+ * ```
221
+ *
222
+ * @throws {X402Error} `INVALID_CONFIG` on an unknown action or a paymentInfo
223
+ * missing a field; `INVALID_AMOUNT` on a negative amount. Nothing IN THE
224
+ * SIGNED STRUCT is defaulted: an invented field is a signature over a
225
+ * different struct than the one that is sent, i.e. a rejection the caller
226
+ * cannot diagnose. `deadline` and `nonce` are the exception because they
227
+ * exist nowhere else — they are born here and read back from the document.
228
+ */
229
+ declare function buildLifecycleTypedData(params: LifecycleTypedDataParams): LifecycleTypedData;
230
+ /**
231
+ * Sign the order and return the complete `payload.lifecycleAuth` block.
232
+ *
233
+ * This is the first link of enforce: the facilitator already verifies these
234
+ * orders in `log` mode and measured ZERO signatures across 17 days of traffic
235
+ * (2,953 release/refund, 22 payers, 9 networks) — the field did not exist and
236
+ * nobody sent it. Under `enforce` today, 100% would be rejected.
237
+ *
238
+ * The signer is INJECTED; the SDK does not read keys from the environment.
239
+ *
240
+ * @throws {X402Error} `INVALID_CONFIG` if the deadline has already passed or
241
+ * sits past the facilitator's 900 s ceiling. It fails HERE and not there:
242
+ * an out-of-window order is an `expired` / `deadline_too_far` that under
243
+ * `enforce` is stuck money, and the caller has no log to see it in.
244
+ */
245
+ declare function buildLifecycleAuth(params: BuildLifecycleAuthParams): Promise<LifecycleAuth>;
246
+ /**
247
+ * Reassemble `payload.lifecycleAuth` from a document that was signed
248
+ * ELSEWHERE — the other half of the split flow.
249
+ *
250
+ * `buildLifecycleAuth` owns the whole ceremony: it mints the nonce and the
251
+ * deadline, signs, and returns the block. A publisher's browser owns none of
252
+ * that. It receives a document the backend already assembled and gives back
253
+ * one string. This is the seam where that string becomes a wire block.
254
+ *
255
+ * `deadline` and `nonce` are NOT parameters: they are read out of
256
+ * `typedData.message`, which is the document that was actually hashed. Taking
257
+ * them from the caller would let the block claim a nonce the signature never
258
+ * committed to — a `bad_signature` the caller cannot see, because both halves
259
+ * look right on their own.
260
+ *
261
+ * The signature is NOT recovered here. This SDK's payers include ERC-7702
262
+ * delegated accounts and contract wallets that validate through ERC-1271
263
+ * (`src/erc7702.ts:8`), whose signatures do not ecrecover to their address;
264
+ * `ethers.verifyTypedData` would reject the good ones. Recovery — and the
265
+ * role check that goes with it — is the facilitator's, against the chain.
266
+ *
267
+ * @param typedData - what {@link buildLifecycleTypedData} returned, unmodified.
268
+ * @param signature - the 0x-hex the wallet gave back (`signTypedData`).
269
+ * @param signer - the address that signed, claimed in the block and checked
270
+ * against the recovered one by the facilitator.
271
+ *
272
+ * @throws {X402Error} `INVALID_CONFIG` if the document is not a
273
+ * `LifecycleOrder` for this domain, if the signature is not hex, or if the
274
+ * signer is not an address.
275
+ *
276
+ * @example The browser half
277
+ * ```typescript
278
+ * const signature = await walletClient.signTypedData({
279
+ * domain: typedData.domain,
280
+ * types: typedData.types,
281
+ * primaryType: typedData.primaryType,
282
+ * message: typedData.message,
283
+ * });
284
+ * const lifecycleAuth = lifecycleAuthFromSignature(typedData, signature, address);
285
+ * ```
286
+ */
287
+ declare function lifecycleAuthFromSignature(typedData: LifecycleTypedData, signature: string, signer: string): LifecycleAuth;
288
+ /**
289
+ * Minimal wagmi/viem wallet client: what {@link wagmiLifecycleSigner} needs.
290
+ *
291
+ * Deliberately narrower than this SDK's payment `WalletClient`: a lifecycle
292
+ * domain has NO `verifyingContract`, so a client typed to require one cannot
293
+ * sign these orders.
294
+ */
295
+ interface WagmiLifecycleWalletClient {
296
+ account?: {
297
+ address: string;
298
+ } | null;
299
+ signTypedData(args: {
300
+ domain: Record<string, unknown>;
301
+ types: Record<string, Array<{
302
+ name: string;
303
+ type: string;
304
+ }>>;
305
+ primaryType: string;
306
+ message: Record<string, unknown>;
307
+ }): Promise<string>;
308
+ }
309
+ /**
310
+ * Wrap a wagmi/viem wallet client as a {@link LifecycleSigner}, so the PAYER
311
+ * signs the order in their own browser with the same wallet they paid with,
312
+ * and the marketplace only transports the block.
313
+ *
314
+ * @param walletClient - from `useWalletClient()` (wagmi) or `createWalletClient` (viem).
315
+ * @param address - the signing account; defaults to `walletClient.account.address`.
316
+ *
317
+ * @example
318
+ * ```typescript
319
+ * const { data: walletClient } = useWalletClient();
320
+ * const auth = await buildLifecycleAuth({
321
+ * action: 'release',
322
+ * paymentInfo: pi,
323
+ * payer: address,
324
+ * amount: task.bountyAtomic,
325
+ * chainId: 8453,
326
+ * wallet: wagmiLifecycleSigner(walletClient!),
327
+ * });
328
+ * await fetch('/api/tasks/x/approve', {
329
+ * method: 'POST',
330
+ * body: JSON.stringify({ lifecycleAuth: auth }),
331
+ * });
332
+ * ```
333
+ */
334
+ declare function wagmiLifecycleSigner(walletClient: WagmiLifecycleWalletClient, address?: string): LifecycleSigner;
335
+
336
+ /**
337
+ * Reading a facilitator refusal as DATA instead of prose.
338
+ *
339
+ * # Why this file exists
340
+ *
341
+ * `402` and `503` say opposite things and the SDK used to collapse both into
342
+ * `success: false` plus an English sentence.
343
+ *
344
+ * - **402** means the payment was REJECTED. The credential is spent; sign a new
345
+ * authorization.
346
+ * - **503** means NO VERDICT WAS REACHED. Nothing was rejected and nothing was
347
+ * executed; retry the SAME credential.
348
+ *
349
+ * Turning a 503 into a 402 makes the buyer sign and send a second payment for a
350
+ * money movement that was never refused — they pay twice. That is the most
351
+ * expensive mistake this SDK can make, so every facilitator edge now reports the
352
+ * status, the facilitator's own `reason`, and whether the request may be
353
+ * replayed.
354
+ *
355
+ * # The writer lease
356
+ *
357
+ * The facilitator serialises every EVM write through the one process that holds
358
+ * the EVM writer lease, because they all spend gas from the same EOA and the
359
+ * nonce for it is allocated in memory. A task that does not hold the lease
360
+ * forwards the request to the one that does; when it cannot, it answers
361
+ * `503` + `Retry-After: 5` + `{"error": "...", "reason": "<why>"}`.
362
+ *
363
+ * The five reasons do NOT share retry semantics, which is the whole point of
364
+ * surfacing them:
365
+ *
366
+ * | reason | did the write run? | replay? |
367
+ * |----------------------------|--------------------|---------|
368
+ * | `holder_unknown` | no | yes |
369
+ * | `forwarding_disabled` | no | yes |
370
+ * | `forwarded_but_not_writer` | no | yes |
371
+ * | `body_unreadable` | no | yes |
372
+ * | `forward_failed` | **maybe** | **no** |
373
+ *
374
+ * `forward_failed` is emitted after the forward was attempted: the holder may
375
+ * have executed the write and the response been lost on the way back. It is a
376
+ * timeout wearing a status code. Replaying a `/register` on it is exactly the
377
+ * sequence that once minted five duplicate agents — reconcile with
378
+ * `GET /identity/{network}/owner/{recipient}` or `getRegisterStatus` instead.
379
+ *
380
+ * # The two `502`s of `/settle`, which mean opposite things
381
+ *
382
+ * | body `error` | `Retry-After` | did the money move? | retry? |
383
+ * |----------------------------|---------------|---------------------|-----------|
384
+ * | `upstream_rpc_unavailable` | `30` | no, never broadcast | **yes** |
385
+ * | `settlement_unconfirmed` | **absent** | **maybe — mined?** | **NEVER** |
386
+ *
387
+ * `settlement_unconfirmed` is answered after the transaction was broadcast and
388
+ * no receipt ever arrived, so it MAY be mined. Retrying re-signs a new
389
+ * authorization with a fresh nonce, which the chain accepts as a second,
390
+ * perfectly valid payment for the same purchase — the buyer pays twice, in
391
+ * exactly the case the facilitator emits it to prevent. The `transaction` hash
392
+ * and `paymentId` travel in the body so the caller can LOOK AT THE CHAIN; they
393
+ * are not an invitation to send it again.
394
+ *
395
+ * The status cannot tell the two apart, which is why this file used to get it
396
+ * wrong: every `502` was retryable, and until `settlement_unconfirmed` existed
397
+ * that was correct. **Branch on the body.**
398
+ *
399
+ * The general form of that rule — **a 5xx whose body carries a transaction hash
400
+ * was broadcast, whatever the error is called** — is adopted from the Python
401
+ * SDK, which has carried it as its anti-double-settle guard
402
+ * (`uvd_x402_sdk/client.py`, `_is_retryable_settle_error`) while this one had
403
+ * only the status to go on.
404
+ *
405
+ * Source of the shape: x402-rs `src/handlers.rs` `writer_lease_unavailable()`
406
+ * and `require_writer_lease()`; `SettlementUnconfirmedResponse` in
407
+ * `src/types.rs`, built in the `IntoResponse` of `FacilitatorLocalError`.
408
+ */
409
+ /**
410
+ * A `reason` the facilitator attaches to a writer-lease 503.
411
+ *
412
+ * Typed as a union for discrimination but never used to VALIDATE: the fields
413
+ * that carry it are plain `string`, so a reason added on the server does not
414
+ * break compilation here.
415
+ */
416
+ type WriterLeaseReason = 'holder_unknown' | 'forwarding_disabled' | 'forwarded_but_not_writer' | 'body_unreadable' | 'forward_failed';
417
+ /** Every writer-lease reason the facilitator emits today. */
418
+ declare const WRITER_LEASE_REASONS: readonly WriterLeaseReason[];
419
+ /**
420
+ * The reasons returned BEFORE the write is handed to anyone.
421
+ *
422
+ * The facilitator refuses these in its router, so the request provably did not
423
+ * execute and re-sending the identical body is safe — including a mint.
424
+ */
425
+ declare const REPLAYABLE_LEASE_REASONS: readonly WriterLeaseReason[];
426
+ /**
427
+ * The reason that is ambiguous: the write may already have happened.
428
+ *
429
+ * Still `retryable` — the credential was not rejected — but never replayed
430
+ * automatically. Resolve it by reading state, not by re-POSTing.
431
+ */
432
+ declare const AMBIGUOUS_LEASE_REASONS: readonly WriterLeaseReason[];
433
+ /**
434
+ * The facilitator's `error` code for a settle it broadcast and could not confirm.
435
+ *
436
+ * The transaction may be mined. This is the one upstream failure that must
437
+ * never be retried; reconcile with `transaction` / `paymentId` instead.
438
+ */
439
+ declare const SETTLEMENT_UNCONFIRMED = "settlement_unconfirmed";
440
+ /**
441
+ * Ceiling, in seconds, on how long an automatic retry will wait.
442
+ *
443
+ * `Retry-After` is a hint from a server that may be misconfigured. Honouring a
444
+ * literal `Retry-After: 3600` would hang the caller's request for an hour
445
+ * inside a function documented as returning promptly, so the header is honoured
446
+ * only up to this bound.
447
+ */
448
+ declare const MAX_RETRY_AFTER_SECONDS = 15;
449
+ /** Wait used when a 503 carries no usable `Retry-After`. */
450
+ declare const DEFAULT_RETRY_AFTER_SECONDS = 5;
451
+ /** How many EXTRA attempts a retryable facilitator refusal gets by default. */
452
+ declare const DEFAULT_FACILITATOR_RETRIES = 2;
453
+ /**
454
+ * A non-2xx answer from the facilitator, kept structured.
455
+ *
456
+ * `error` is byte-identical to the string this SDK has always produced, so
457
+ * callers matching on it keep working; everything else is new and optional.
458
+ */
459
+ interface FacilitatorErrorInfo {
460
+ /** Legacy flattened message: `Facilitator error: <status> - <body>`. */
461
+ error: string;
462
+ /** HTTP status the facilitator answered with. */
463
+ status: number;
464
+ /** The facilitator's own `reason`, when the body carried one. */
465
+ reason?: string;
466
+ /**
467
+ * The facilitator's machine-readable `error` code, verbatim.
468
+ *
469
+ * Some codes carry a `(ref: <uuid>)` suffix, so compare with care —
470
+ * {@link SETTLEMENT_UNCONFIRMED} is emitted bare. Branch on this rather than
471
+ * on the status: `/settle` has two `502`s that mean opposite things.
472
+ */
473
+ errorCode?: string;
474
+ /**
475
+ * The hash of a transaction that WAS broadcast, in that chain's own encoding.
476
+ *
477
+ * Present on {@link SETTLEMENT_UNCONFIRMED}. Not always `0x`-prefixed:
478
+ * Algorand prints base32 and Solana base58, and reformatting it makes it
479
+ * unpasteable in an explorer — which is the entire remedy on offer.
480
+ */
481
+ transaction?: string;
482
+ /** `keccak256(caip2 ‖ txHash)`, the same id a successful settle would print. */
483
+ paymentId?: string;
484
+ /**
485
+ * Seconds to wait before retrying, already clamped to
486
+ * {@link MAX_RETRY_AFTER_SECONDS}. Absent when the answer is not retryable.
487
+ */
488
+ retryAfterSeconds?: number;
489
+ /**
490
+ * The request reached no verdict; the credential is untouched and the SAME
491
+ * request may be sent again. Never surface this as a payment rejection.
492
+ */
493
+ retryable: boolean;
494
+ /**
495
+ * The facilitator NAMED a reason that proves it executed nothing, so an
496
+ * automatic replay cannot double-write.
497
+ *
498
+ * False for `forward_failed`, whose write may already have landed, and false
499
+ * for an unattributed 5xx from a proxy — "something in front answered" is not
500
+ * evidence that nothing ran.
501
+ */
502
+ safeToReplay: boolean;
503
+ /** Raw response body, for logs. */
504
+ body: string;
505
+ }
506
+ /** Fields every facilitator response type gained so a 503 stops looking terminal. */
507
+ interface FacilitatorFailureFields {
508
+ /** HTTP status, when the failure was transport-level rather than a verdict. */
509
+ status?: number;
510
+ /** The facilitator's `reason` for the refusal (see {@link WriterLeaseReason}). */
511
+ reason?: string;
512
+ /** The facilitator's machine-readable `error` code (see {@link FacilitatorErrorInfo.errorCode}). */
513
+ errorCode?: string;
514
+ /**
515
+ * A transaction that WAS broadcast and could not be confirmed. Look it up on
516
+ * chain; do NOT send the payment again. See {@link SETTLEMENT_UNCONFIRMED}.
517
+ */
518
+ transaction?: string;
519
+ /** The payment id for that transaction, identical to the one a settle prints. */
520
+ paymentId?: string;
521
+ /** True when the same request may be sent again without re-signing anything. */
522
+ retryable?: boolean;
523
+ /** Seconds to wait before retrying, clamped to {@link MAX_RETRY_AFTER_SECONDS}. */
524
+ retryAfterSeconds?: number;
525
+ /** True when the facilitator provably executed nothing. */
526
+ safeToReplay?: boolean;
527
+ }
528
+ /** A 503/429 that carries a reason known to be pre-execution. */
529
+ declare function isReplayableLeaseReason(reason?: string): boolean;
530
+ /** A 503 whose write may already have run. Reconcile, do not re-POST. */
531
+ declare function isAmbiguousLeaseReason(reason?: string): boolean;
532
+ /**
533
+ * Read `Retry-After` and clamp it.
534
+ *
535
+ * Tolerates a `Response`-shaped object with no `headers` at all: test doubles
536
+ * and non-standard fetch polyfills routinely omit it, and throwing there would
537
+ * turn a readable refusal into an unreadable crash.
538
+ */
539
+ declare function parseRetryAfterSeconds(response: {
540
+ headers?: {
541
+ get?: (name: string) => string | null;
542
+ };
543
+ }): number | undefined;
544
+ /** Everything this SDK reads out of a facilitator error body. */
545
+ interface ParsedFacilitatorErrorBody {
546
+ /** The `error` code, verbatim. */
547
+ errorCode?: string;
548
+ /** The writer-lease `reason`. */
549
+ reason?: string;
550
+ /** A broadcast transaction hash, in its own chain's encoding. */
551
+ transaction?: string;
552
+ /** The payment id for that hash. */
553
+ paymentId?: string;
554
+ /**
555
+ * The facilitator's OWN retry verdict, when it stated one.
556
+ *
557
+ * `undefined` on every body that does not carry the field — which is most of
558
+ * them, so absence means "the facilitator did not say", never "no".
559
+ */
560
+ retryable?: boolean;
561
+ }
562
+ /**
563
+ * Read a JSON error body, tolerating anything that is not one.
564
+ *
565
+ * Exported so {@link FacilitatorErrorInfo} and `Erc8004LookupError` read the
566
+ * SAME fields the same way. Two subtly different parses of the same body is how
567
+ * one code path stops honouring a `retryable: false` the other one honours.
568
+ */
569
+ declare function parseFacilitatorErrorBody(body: string): ParsedFacilitatorErrorBody;
570
+ /**
571
+ * This refusal reports a transaction that may already be mined.
572
+ *
573
+ * The caller's move is to look up `transaction` / `paymentId` on chain. Sending
574
+ * the payment again is the double-charge.
575
+ */
576
+ declare function isSettlementUnconfirmed(failure: {
577
+ errorCode?: string;
578
+ }): boolean;
579
+ /**
580
+ * Turn a non-2xx facilitator response into {@link FacilitatorErrorInfo}.
581
+ *
582
+ * Reads the body exactly once. The `error` string keeps the historical format
583
+ * verbatim; reformatting it would break callers that match on it.
584
+ */
585
+ declare function readFacilitatorError(response: {
586
+ status: number;
587
+ text: () => Promise<string>;
588
+ headers?: {
589
+ get?: (name: string) => string | null;
590
+ };
591
+ }): Promise<FacilitatorErrorInfo>;
592
+ /**
593
+ * Copy the failure fields off one response onto another.
594
+ *
595
+ * Used where a wrapper returns its own shape: without this the wrapper flattens
596
+ * a `503` back into a bare failure and reintroduces, one level up, exactly the
597
+ * ambiguity the fields exist to remove.
598
+ */
599
+ declare function carryFailureFields(source: FacilitatorFailureFields): FacilitatorFailureFields;
600
+ /** Options for {@link facilitatorFetch}. */
601
+ interface FacilitatorFetchOptions {
602
+ /** Abort the request after this many milliseconds. */
603
+ timeoutMs: number;
604
+ /** Extra attempts after the first. Default {@link DEFAULT_FACILITATOR_RETRIES}. */
605
+ retries?: number;
606
+ /**
607
+ * Whether this particular refusal may be replayed.
608
+ *
609
+ * Default: only when the facilitator proved it executed nothing
610
+ * (`info.safeToReplay`). Pass a stricter predicate on paths where even a
611
+ * proven-safe replay is unwanted.
612
+ */
613
+ canReplay?: (info: FacilitatorErrorInfo) => boolean;
614
+ /** Injected for tests. */
615
+ fetchImpl?: typeof fetch;
616
+ /** Injected for tests, so a retry does not really sleep. */
617
+ sleepImpl?: (ms: number) => Promise<void>;
618
+ }
619
+ /**
620
+ * POST/GET the facilitator, replaying a refusal that provably executed nothing.
621
+ *
622
+ * Returns the response plus, when it was not ok, the structured refusal — the
623
+ * body has already been consumed to build it, so callers must not read it
624
+ * again.
625
+ *
626
+ * Network errors and timeouts still THROW, exactly as before: callers already
627
+ * have `catch` blocks that turn them into `{ success: false }`, and an
628
+ * `AbortError` on the escrow paths triggers an on-chain reconciliation that
629
+ * must keep firing.
630
+ */
631
+ declare function facilitatorFetch(url: string, init: RequestInit, options: FacilitatorFetchOptions): Promise<{
632
+ response: Response;
633
+ error?: FacilitatorErrorInfo;
634
+ }>;
635
+
636
+ /**
637
+ * Payment requirements sent to the facilitator
638
+ */
639
+ interface PaymentRequirements {
640
+ /** Payment scheme */
641
+ scheme: 'exact' | 'escrow' | 'commerce';
642
+ /** Network name (v1) or CAIP-2 identifier (v2) */
643
+ network: string;
644
+ /** Maximum amount required in atomic units (e.g., "1000000" for 1 USDC) */
645
+ maxAmountRequired: string;
646
+ /** Resource URL being paid for */
647
+ resource: string;
648
+ /** Description of what's being paid for */
649
+ description: string;
650
+ /** MIME type of the resource */
651
+ mimeType: string;
652
+ /** Recipient address for payment */
653
+ payTo: string;
654
+ /** Maximum timeout in seconds */
655
+ maxTimeoutSeconds: number;
656
+ /** Token contract address */
657
+ asset: string;
658
+ /** Optional output schema for the resource */
659
+ outputSchema?: unknown;
660
+ /** Optional extra data */
661
+ extra?: unknown;
662
+ }
663
+ /**
664
+ * Verify request body for the facilitator /verify endpoint -- the **v1**
665
+ * envelope. {@link VerifyRequestV2} is the other one.
666
+ */
667
+ interface VerifyRequest {
668
+ /**
669
+ * Always `1`: this marker names the ENVELOPE, and this envelope is v1.
670
+ *
671
+ * Narrowed from `X402Version` on 2026-09-04. A `VerifyRequest` carrying `2`
672
+ * was always an uninhabitable value -- a body declaring v2 while shaped as
673
+ * v1 -- and typing it as `1 | 2` is what let the payer's marker be copied in
674
+ * here. The payer's version lives in `paymentPayload.x402Version`, which is
675
+ * still the full union.
676
+ */
677
+ x402Version: 1;
678
+ paymentPayload: X402Header;
679
+ paymentRequirements: PaymentRequirements;
680
+ }
681
+ /**
682
+ * Settle request body for the facilitator /settle endpoint -- the **v1**
683
+ * envelope. {@link SettleRequestV2} is the other one.
684
+ */
685
+ interface SettleRequest {
686
+ /** Always `1` -- see {@link VerifyRequest.x402Version}. */
687
+ x402Version: 1;
688
+ paymentPayload: X402Header;
689
+ paymentRequirements: PaymentRequirements;
690
+ }
691
+ /**
692
+ * Verify response from the facilitator
693
+ */
694
+ interface VerifyResponse extends FacilitatorFailureFields {
695
+ isValid: boolean;
696
+ /**
697
+ * Why the payment is not valid.
698
+ *
699
+ * Read `retryable` before showing this to anyone. When `retryable` is true the
700
+ * facilitator reached NO VERDICT -- it did not reject the payment, so this
701
+ * string is a transport diagnosis, not a rejection, and re-signing on it makes
702
+ * the buyer pay twice.
703
+ */
704
+ invalidReason?: string;
705
+ payer?: string;
706
+ network?: string;
707
+ }
708
+ /**
709
+ * Settle response from the facilitator
710
+ */
711
+ interface SettleResponse extends FacilitatorFailureFields {
712
+ success: boolean;
713
+ transactionHash?: string;
714
+ network?: string;
715
+ /**
716
+ * Transport-level failure (unreachable facilitator, non-2xx, timeout).
717
+ *
718
+ * `success: false` with `retryable: true` is NOT a rejected payment. The
719
+ * authorization is untouched and the same one must be resent; treating it as
720
+ * a refusal and asking for a new signature charges the buyer twice.
721
+ */
722
+ error?: string;
723
+ /**
724
+ * The facilitator's own reason when it settled nothing — e.g. a transfer that
725
+ * mined and reverted. Distinct from `error` above, which is this client
726
+ * failing to ask; this one is the facilitator answering "no".
727
+ */
728
+ errorReason?: string;
729
+ /** The address the facilitator confirmed as the payer. */
730
+ payer?: string;
731
+ /**
732
+ * Settlement proof, present when the ERC-8004 extension asked for it.
733
+ *
734
+ * Typed here rather than only on {@link SettleResponseWithProof} because
735
+ * `settle()` returns it whenever the facilitator sends it, and it is what
736
+ * DX402's `anchorEvidence` needs to reach `verified: true`.
737
+ */
738
+ proofOfPayment?: ProofOfPayment;
739
+ }
740
+ /**
741
+ * Options for building payment requirements
742
+ */
743
+ interface PaymentRequirementsOptions {
744
+ /** Amount in human-readable format (e.g., "1.00") */
745
+ amount: string;
746
+ /** Recipient address */
747
+ recipient: string;
748
+ /** Resource URL being protected */
749
+ resource: string;
750
+ /** Chain name (e.g., "base") */
751
+ chainName?: string;
752
+ /** Description of the resource */
753
+ description?: string;
754
+ /** MIME type of the resource */
755
+ mimeType?: string;
756
+ /** Timeout in seconds (default: 300) */
757
+ timeoutSeconds?: number;
758
+ /** x402 version to use */
759
+ x402Version?: X402Version;
760
+ }
761
+ /**
762
+ * x402 payment option advertised in a 402 response.
763
+ *
764
+ * The SDK keeps the response shape richer than the minimal protocol fields so
765
+ * servers can preserve settlement-critical metadata such as payTo and extra.
766
+ */
767
+ interface PaymentAcceptance {
768
+ network: string;
769
+ asset: string;
770
+ amount: string;
771
+ /**
772
+ * Payment scheme. REQUIRED by the facilitator's v2 `PaymentRequirementsV2`.
773
+ *
774
+ * Optional here only so existing callers keep compiling — when it is absent,
775
+ * `buildRequirementFromAcceptance` defaults it to `'exact'`. Do not treat its
776
+ * optionality as "the facilitator does not need it": an accepts[] entry that
777
+ * reaches a v2 client without a scheme is unpayable.
778
+ */
779
+ scheme?: string;
780
+ payTo?: string;
781
+ facilitator?: string;
782
+ resource?: string;
783
+ description?: string;
784
+ mimeType?: string;
785
+ maxTimeoutSeconds?: number;
786
+ outputSchema?: unknown;
787
+ extra?: unknown;
788
+ }
789
+ /**
790
+ * Verified payment context attached by server middleware.
791
+ */
792
+ interface VerifiedPaymentState {
793
+ payment: X402Header;
794
+ requirements: PaymentRequirements;
795
+ verifyResult: VerifyResponse;
796
+ settle: () => Promise<SettleResponse>;
797
+ }
798
+ /**
799
+ * Shared server middleware options.
800
+ */
801
+ interface PaymentMiddlewareOptions extends FacilitatorClientOptions {
802
+ /** Alias for baseUrl to keep middleware options ergonomic */
803
+ facilitatorUrl?: string;
804
+ /**
805
+ * Settlement behavior after verification.
806
+ * - manual: verify only; caller settles explicitly
807
+ * - before-handler: settle immediately before calling next()
808
+ */
809
+ settlementStrategy?: 'manual' | 'before-handler';
810
+ }
811
+ /**
812
+ * Custom resolver for selecting the correct payment requirement when multiple
813
+ * accepts are advertised.
814
+ */
815
+ type PaymentRequirementResolver = (payment: X402Header, requirements: PaymentRequirements[]) => PaymentRequirements | null | Promise<PaymentRequirements | null>;
816
+ /**
817
+ * Parse X-PAYMENT or PAYMENT-SIGNATURE header value
818
+ *
819
+ * @param headerValue - Base64-encoded header value (or undefined/null)
820
+ * @returns Parsed x402 header object, or null if invalid
821
+ *
822
+ * @example
823
+ * ```ts
824
+ * // Express.js
825
+ * const payment = parsePaymentHeader(req.headers['x-payment']);
826
+ * if (!payment) {
827
+ * return res.status(400).json({ error: 'Invalid payment header' });
828
+ * }
829
+ * ```
830
+ */
831
+ declare function parsePaymentHeader(headerValue: string | undefined | null): X402Header | null;
832
+ /**
833
+ * Extract payment header from request headers object
834
+ *
835
+ * Checks both X-PAYMENT and PAYMENT-SIGNATURE headers.
836
+ *
837
+ * @param headers - Request headers object (case-insensitive)
838
+ * @returns Parsed x402 header object, or null if not found/invalid
839
+ *
840
+ * @example
841
+ * ```ts
842
+ * const payment = extractPaymentFromHeaders(req.headers);
843
+ * ```
844
+ */
845
+ declare function extractPaymentFromHeaders(headers: Record<string, string | string[] | undefined>): X402Header | null;
846
+ /**
847
+ * Build payment requirements for the facilitator
848
+ *
849
+ * @param options - Payment requirements options
850
+ * @returns PaymentRequirements object ready for verify/settle
851
+ *
852
+ * @example
853
+ * ```ts
854
+ * const requirements = buildPaymentRequirements({
855
+ * amount: '1.00',
856
+ * recipient: '0x1234...',
857
+ * resource: 'https://api.example.com/premium-data',
858
+ * chainName: 'base',
859
+ * });
860
+ * ```
861
+ */
862
+ declare function buildPaymentRequirements(options: PaymentRequirementsOptions): PaymentRequirements;
863
+ /**
864
+ * Build a verify request for the facilitator /verify endpoint
865
+ *
866
+ * @param paymentHeader - Parsed x402 payment header
867
+ * @param requirements - Payment requirements
868
+ * @returns VerifyRequest body ready for fetch/axios
869
+ *
870
+ * @example
871
+ * ```ts
872
+ * const payment = parsePaymentHeader(req.headers['x-payment']);
873
+ * const verifyBody = buildVerifyRequest(payment, requirements);
874
+ *
875
+ * const response = await fetch('https://facilitator.uvd.xyz/verify', {
876
+ * method: 'POST',
877
+ * headers: { 'Content-Type': 'application/json' },
878
+ * body: JSON.stringify(verifyBody),
879
+ * });
880
+ * ```
881
+ */
882
+ declare function buildVerifyRequest(paymentHeader: X402Header, requirements: PaymentRequirements): VerifyRequest;
883
+ /**
884
+ * Build a settle request for the facilitator /settle endpoint
885
+ *
886
+ * @param paymentHeader - Parsed x402 payment header
887
+ * @param requirements - Payment requirements
888
+ * @returns SettleRequest body ready for fetch/axios
889
+ */
890
+ /**
891
+ * Describes the protected resource, as x402 v2 expects it.
892
+ *
893
+ * Note this is an OBJECT in v2. Sending a bare URL string here is the single
894
+ * most common v2 mistake and it fails as an unhelpful "no variant matched"
895
+ * deserialization error at the facilitator, naming no field.
896
+ */
897
+ interface ResourceInfoV2 {
898
+ url: string;
899
+ description: string;
900
+ mimeType: string;
901
+ }
902
+ /**
903
+ * Payment requirements in x402 v2 form.
904
+ *
905
+ * Differences from v1 that actually bite:
906
+ * - `network` is CAIP-2 (`eip155:8453`), NOT a plain name (`base`). Mixing a v1
907
+ * name into a v2 request fails deserialization, and vice versa.
908
+ * - `maxAmountRequired` is renamed to `amount`.
909
+ * - `resource` / `description` / `mimeType` moved out to {@link ResourceInfoV2}.
910
+ */
911
+ interface PaymentRequirementsV2 {
912
+ scheme: string;
913
+ /** CAIP-2 chain id, e.g. `eip155:8453`. */
914
+ network: string;
915
+ asset: string;
916
+ amount: string;
917
+ payTo: string;
918
+ maxTimeoutSeconds: number;
919
+ extra?: unknown;
920
+ }
921
+ /** The v2 payment payload — note it carries no top-level scheme/network. */
922
+ interface PaymentPayloadV2 {
923
+ x402Version: 2;
924
+ resource: ResourceInfoV2;
925
+ accepted: PaymentRequirementsV2;
926
+ payload: X402PayloadData;
927
+ extensions?: Record<string, unknown>;
928
+ }
929
+ /**
930
+ * Verify request body in x402 v2 form.
931
+ *
932
+ * There is deliberately NO `paymentRequirements` key: that is the v1 envelope.
933
+ * v2 carries `resource` and `accepted` at the top level instead.
934
+ */
935
+ interface VerifyRequestV2 {
936
+ x402Version: 2;
937
+ paymentPayload: PaymentPayloadV2;
938
+ resource: ResourceInfoV2;
939
+ accepted: PaymentRequirementsV2;
940
+ }
941
+ /** Settle request body in x402 v2 form. Same shape as {@link VerifyRequestV2}. */
942
+ type SettleRequestV2 = VerifyRequestV2;
943
+ /**
944
+ * Build a verify request for the facilitator `/verify` endpoint, in **v2** form.
945
+ *
946
+ * Use this whenever your 402 advertises CAIP-2 networks. {@link buildVerifyRequest}
947
+ * emits the v1 envelope `{x402Version, paymentPayload, paymentRequirements}` and
948
+ * cannot express v2 — putting a v2 payload inside it matches no variant at the
949
+ * facilitator and fails with an error that names no field.
950
+ *
951
+ * @example
952
+ * ```ts
953
+ * const body = buildVerifyRequestV2(
954
+ * payment.payload,
955
+ * { url: 'https://api.example.com/thing', description: 'Thing', mimeType: 'application/json' },
956
+ * { scheme: 'exact', network: 'eip155:8453', asset: '0x8335...', amount: '100000',
957
+ * payTo: '0xabc...', maxTimeoutSeconds: 300 }
958
+ * );
959
+ * ```
960
+ */
961
+ declare function buildVerifyRequestV2(payload: X402PayloadData, resource: ResourceInfoV2, accepted: PaymentRequirementsV2): VerifyRequestV2;
962
+ /**
963
+ * Build a settle request for the facilitator `/settle` endpoint, in **v2** form.
964
+ *
965
+ * See {@link buildVerifyRequestV2} — the envelope is identical.
966
+ */
967
+ declare function buildSettleRequestV2(payload: X402PayloadData, resource: ResourceInfoV2, accepted: PaymentRequirementsV2): SettleRequestV2;
968
+ declare function buildSettleRequest(paymentHeader: X402Header, requirements: PaymentRequirements): SettleRequest;
969
+ /**
970
+ * Derive the v2 `resource` object from v1-shaped requirements.
971
+ *
972
+ * v2 moved `resource` / `description` / `mimeType` out of the requirements and
973
+ * into an object of their own, and the facilitator requires ALL THREE keys:
974
+ * measured 2026-09-03, a `resource` carrying only `url` is a 400.
975
+ *
976
+ * The `??` defaults are not decoration. `PaymentRequirements` types these as
977
+ * required, but a JavaScript caller can still hand over an object without them,
978
+ * and a missing key does not fail with "description is missing" -- it fails with
979
+ * `data did not match any variant of untagged enum VerifyRequestEnvelope`, which
980
+ * names no field. That error is what cost two teams a day.
981
+ */
982
+ declare function toResourceInfoV2(requirements: PaymentRequirements): ResourceInfoV2;
983
+ /**
984
+ * Derive v2 `accepted` requirements from v1-shaped requirements.
985
+ *
986
+ * Two renames do the damage, and neither is reported by name when it is wrong:
987
+ * - `maxAmountRequired` is spelled `amount` in v2.
988
+ * - `network` must be CAIP-2; a plain name inside a v2 body is a 400.
989
+ *
990
+ * `extra` is carried through when present -- it is where the EIP-712 domain
991
+ * `name`/`version` live for tokens the facilitator does not know by address, so
992
+ * dropping it breaks EURC and the bridged USDCs.
993
+ *
994
+ * @throws If `requirements.network` has NO CAIP-2 form. `chainToCAIP2` answers
995
+ * with the name unchanged when it does not know a chain, and XRPL maps to
996
+ * itself on purpose -- its v1 string IS its network id. Passing that through
997
+ * would put a plain name inside a v2 body, which is a measured 400 (the same
998
+ * `no variant matched` that names no field). Only reachable by PINNING version
999
+ * 2 on such a network; `auto` leaves them on v1, where they work. Failing here
1000
+ * names the network and the fix, which a 400 from the facilitator does not.
1001
+ */
1002
+ declare function toPaymentRequirementsV2(requirements: PaymentRequirements): PaymentRequirementsV2;
1003
+ /**
1004
+ * Decide which envelope this (payment, requirements) pair has to travel in.
1005
+ *
1006
+ * `requested` wins when it names a version; `'auto'` (the default) reads the
1007
+ * wire.
1008
+ *
1009
+ * **Auto keys off CAIP-2, NOT off `paymentHeader.x402Version`,** and that is a
1010
+ * measured decision rather than a stylistic one. The facilitator's envelope enum
1011
+ * is untagged: it matches on SHAPE and ignores the version marker.
1012
+ *
1013
+ * Re-measured against `https://facilitator.ultravioletadao.xyz/verify` on
1014
+ * **2026-09-04** with a fabricated signature. The signature never verifies, so
1015
+ * every row is an HTTP 400 and the STATUS discriminates nothing -- what does is
1016
+ * the error code. `invalid_request_body` means the facilitator could not
1017
+ * deserialize the body; `contract_call_failed` means it read the body, resolved
1018
+ * the chain and got as far as the on-chain call, i.e. the envelope was fine.
1019
+ *
1020
+ * | payload network | requirements network | v1 envelope today |
1021
+ * |-----------------|----------------------|-------------------|
1022
+ * | `base` | `base` | understood |
1023
+ * | `base` (header says `x402Version: 2`) | `base` | understood |
1024
+ * | `eip155:8453` | `base` | understood |
1025
+ * | `base` | `eip155:8453` | understood |
1026
+ * | `eip155:8453` | `eip155:8453` | understood |
1027
+ *
1028
+ * **The last three rows used to be a hard 400** (`unknown variant
1029
+ * \`eip155:8453\``) when this function was written on 2026-09-03. The
1030
+ * facilitator has since taught the v1 envelope to read CAIP-2, so the original
1031
+ * argument for this rule -- "every CAIP-2 combination is already a 400, so
1032
+ * upgrading them cannot regress anyone" -- **is no longer true**. The rule is
1033
+ * unchanged; three other reasons hold it up:
1034
+ *
1035
+ * 1. A CAIP-2 network on the wire means the 402 that produced it advertised v2.
1036
+ * Answering in v2 is speaking the protocol the seller announced.
1037
+ * 2. v2-with-CAIP-2 is the only shape BOTH generations of the facilitator
1038
+ * accept. v1-with-CAIP-2 is a hard 400 on any build older than 2026-09-04,
1039
+ * so choosing v1 there is what breaks against a self-hosted or pinned one.
1040
+ * 3. The Python SDK resolves the identical rule, so the same wire produces the
1041
+ * same body in both SDKs -- pinned by phase 6 of `npm run test:xlang`.
1042
+ *
1043
+ * And the marker still decides nothing: row 2 above is served correctly today,
1044
+ * so upgrading on the strength of it would change a call that works.
1045
+ *
1046
+ * The negative half of that measurement, without which "understood" proves
1047
+ * nothing -- the same run, bodies broken on purpose, all three
1048
+ * `invalid_request_body`: a v2 body carrying a plain network name, one with
1049
+ * `resource` as a bare string, and one with `accepted` removed. A well-formed
1050
+ * v2 body with CAIP-2 reached `contract_call_failed` like the rows above.
1051
+ */
1052
+ declare function resolveEnvelopeVersion(paymentHeader: X402Header | PaymentPayloadV2, requirements: PaymentRequirements, requested?: X402Version | 'auto'): X402Version;
1053
+ /**
1054
+ * Build a `/verify` body in whichever envelope `version` names.
1055
+ *
1056
+ * The v1 return is byte-for-byte what {@link buildVerifyRequest} produces, so
1057
+ * pinning `1` is exactly today's behaviour.
1058
+ *
1059
+ * @example
1060
+ * ```ts
1061
+ * const version = resolveEnvelopeVersion(payment, requirements);
1062
+ * const body = buildVerifyRequestForVersion(payment, requirements, version);
1063
+ * ```
1064
+ */
1065
+ declare function buildVerifyRequestForVersion(paymentHeader: X402Header, requirements: PaymentRequirements, version: X402Version): VerifyRequest | VerifyRequestV2;
1066
+ /**
1067
+ * Build a `/settle` body in whichever envelope `version` names.
1068
+ *
1069
+ * See {@link buildVerifyRequestForVersion} -- `/settle` takes the same body as
1070
+ * `/verify` in both versions.
1071
+ */
1072
+ declare function buildSettleRequestForVersion(paymentHeader: X402Header, requirements: PaymentRequirements, version: X402Version): SettleRequest | SettleRequestV2;
1073
+ /**
1074
+ * Recommended CORS headers for x402 payment APIs
1075
+ *
1076
+ * These headers allow browsers to send payment headers in cross-origin requests.
1077
+ */
1078
+ declare const X402_CORS_HEADERS: {
1079
+ readonly 'Access-Control-Allow-Headers': "Content-Type, X-PAYMENT, PAYMENT-SIGNATURE, Authorization";
1080
+ readonly 'Access-Control-Expose-Headers': "X-PAYMENT-RESPONSE, PAYMENT-RESPONSE, PAYMENT-REQUIRED";
1081
+ readonly 'Access-Control-Allow-Methods': "GET, POST, OPTIONS";
1082
+ };
1083
+ /**
1084
+ * All x402 custom header names that should be allowed in CORS
1085
+ */
1086
+ declare const X402_HEADER_NAMES: readonly ["X-PAYMENT", "PAYMENT-SIGNATURE", "X-PAYMENT-RESPONSE", "PAYMENT-RESPONSE", "PAYMENT-REQUIRED"];
1087
+ /**
1088
+ * Get CORS headers with custom origin
1089
+ *
1090
+ * @param origin - Allowed origin (use '*' for any, or specific domain)
1091
+ * @returns Complete CORS headers object
1092
+ *
1093
+ * @example
1094
+ * ```ts
1095
+ * // Express.js middleware
1096
+ * app.use((req, res, next) => {
1097
+ * const corsHeaders = getCorsHeaders('https://myapp.com');
1098
+ * Object.entries(corsHeaders).forEach(([key, value]) => {
1099
+ * res.setHeader(key, value);
1100
+ * });
1101
+ * if (req.method === 'OPTIONS') {
1102
+ * return res.status(204).end();
1103
+ * }
1104
+ * next();
1105
+ * });
1106
+ * ```
1107
+ */
1108
+ declare function getCorsHeaders(origin?: string): Record<string, string>;
1109
+ /**
1110
+ * Options for the FacilitatorClient
1111
+ */
1112
+ interface FacilitatorClientOptions {
1113
+ /** Base URL of the facilitator (default: https://facilitator.ultravioletadao.xyz) */
1114
+ baseUrl?: string;
1115
+ /**
1116
+ * Request timeout in milliseconds (default: auto per network).
1117
+ * When not set, the client uses per-network defaults from ESCROW_TIMEOUT_MS
1118
+ * (960s for Ethereum L1, 90s for L2s, 30s for others).
1119
+ * Set explicitly to override per-network auto-detection.
1120
+ */
1121
+ timeout?: number;
1122
+ /**
1123
+ * Extra attempts after the first when the facilitator answers a refusal it
1124
+ * proved it did not execute (`safeToReplay`). Default 2; `0` disables.
1125
+ *
1126
+ * Only ever spent on `429` and on a `503` naming a pre-execution writer-lease
1127
+ * reason. An ambiguous `forward_failed` -- whose write may already have landed
1128
+ * -- is never replayed here, at any setting.
1129
+ */
1130
+ retries?: number;
1131
+ /**
1132
+ * Which envelope to send to `/verify` and `/settle`. Default `'auto'`.
1133
+ *
1134
+ * `'auto'` reads the wire: CAIP-2 networks get the v2 envelope, plain names
1135
+ * get v1. See {@link resolveEnvelopeVersion} for the measurements behind that
1136
+ * rule. Pin `1` or `2` to take the decision yourself -- a pin is honoured
1137
+ * even when it contradicts the wire, because choosing the version is the
1138
+ * point of the option.
1139
+ */
1140
+ x402Version?: X402Version | 'auto';
1141
+ }
1142
+ /**
1143
+ * Client for interacting with the x402 facilitator API
1144
+ *
1145
+ * @example
1146
+ * ```ts
1147
+ * const client = new FacilitatorClient();
1148
+ *
1149
+ * // Verify a payment
1150
+ * const verifyResult = await client.verify(paymentHeader, requirements);
1151
+ * if (!verifyResult.isValid) {
1152
+ * return res.status(402).json({ error: verifyResult.invalidReason });
1153
+ * }
1154
+ *
1155
+ * // Provide the service, then settle
1156
+ * const settleResult = await client.settle(paymentHeader, requirements);
1157
+ * if (!settleResult.success) {
1158
+ * // Handle settlement failure (maybe refund or retry)
1159
+ * }
1160
+ * ```
1161
+ */
1162
+ declare class FacilitatorClient {
1163
+ private readonly baseUrl;
1164
+ private readonly timeout;
1165
+ private readonly explicitTimeout;
1166
+ private readonly retries;
1167
+ private readonly x402Version;
1168
+ constructor(options?: FacilitatorClientOptions);
1169
+ /**
1170
+ * Get timeout for a specific network, using per-chain defaults when no explicit timeout was set.
1171
+ */
1172
+ private getTimeout;
1173
+ /**
1174
+ * Verify a payment with the facilitator
1175
+ *
1176
+ * Call this before providing the paid resource to validate the payment.
1177
+ *
1178
+ * @param paymentHeader - Parsed x402 payment header
1179
+ * @param requirements - Payment requirements
1180
+ * @returns Verification result
1181
+ */
1182
+ verify(paymentHeader: X402Header, requirements: PaymentRequirements): Promise<VerifyResponse>;
1183
+ /**
1184
+ * Settle a payment with the facilitator
1185
+ *
1186
+ * Call this after providing the paid resource to execute the on-chain transfer.
1187
+ *
1188
+ * @param paymentHeader - Parsed x402 payment header
1189
+ * @param requirements - Payment requirements
1190
+ * @returns Settlement result with transaction hash
1191
+ */
1192
+ settle(paymentHeader: X402Header, requirements: PaymentRequirements): Promise<SettleResponse>;
1193
+ /**
1194
+ * Verify and settle atomically
1195
+ *
1196
+ * Convenience method that verifies first, then settles if valid.
1197
+ * Use this for simple payment flows where you don't need custom logic between verify and settle.
1198
+ *
1199
+ * @param paymentHeader - Parsed x402 payment header
1200
+ * @param requirements - Payment requirements
1201
+ * @returns Combined result with verify and settle status
1202
+ */
1203
+ verifyAndSettle(paymentHeader: X402Header, requirements: PaymentRequirements): Promise<{
1204
+ verified: boolean;
1205
+ settled: boolean;
1206
+ transactionHash?: string;
1207
+ error?: string;
1208
+ } & FacilitatorFailureFields>;
1209
+ /**
1210
+ * Check if the facilitator is healthy
1211
+ *
1212
+ * @returns True if the facilitator is responding
1213
+ */
1214
+ healthCheck(): Promise<boolean>;
1215
+ /**
1216
+ * Get the facilitator version info
1217
+ *
1218
+ * @returns Version info (e.g., { version: "1.37.0" })
1219
+ */
1220
+ getVersion(): Promise<{
1221
+ version: string;
1222
+ [key: string]: unknown;
1223
+ }>;
1224
+ /**
1225
+ * Get the facilitator's supported networks and payment schemes
1226
+ *
1227
+ * @returns Supported networks/schemes with 'kinds' array
1228
+ *
1229
+ * @example
1230
+ * ```ts
1231
+ * const supported = await client.getSupported();
1232
+ * for (const kind of supported.kinds) {
1233
+ * console.log(`${kind.network} - ${kind.scheme}`);
1234
+ * }
1235
+ * ```
1236
+ */
1237
+ getSupported(): Promise<{
1238
+ kinds: Array<{
1239
+ network: string;
1240
+ scheme: string;
1241
+ [key: string]: unknown;
1242
+ }>;
1243
+ [key: string]: unknown;
1244
+ }>;
1245
+ /**
1246
+ * Aggregated totals per network and asset (`GET /api/stats`).
1247
+ *
1248
+ * **An index, not a ledger.** Records are written best-effort AFTER
1249
+ * settlement, so an outage loses rows while payments proceed — verify
1250
+ * anything that matters against the transaction hash. Counting starts when
1251
+ * the operator enabled the store, so earlier operations are UNKNOWN, not
1252
+ * zero. And unless `X402_EVENTS_PUBLISH_FAILURES=true`, operations that ERROR
1253
+ * are not recorded at all: a 100% success rate means "no failures were
1254
+ * recorded".
1255
+ *
1256
+ * `volumeAtomic` is a STRING (u256-shaped; a JS number loses precision above
1257
+ * 2^53) and each row carries its own `decimals`. **Use that, never a
1258
+ * constant** — USDC is 6 decimals nearly everywhere and 18 on BSC, so scaling
1259
+ * by 6 there overstates volume by 10^12. `decimals` is null when the asset is
1260
+ * unrecognised; render the atomic value rather than guessing a scale.
1261
+ */
1262
+ getStats(): Promise<{
1263
+ totals: {
1264
+ settlesOk: number;
1265
+ settlesFailed: number;
1266
+ verifies: number;
1267
+ networks: number;
1268
+ };
1269
+ byNetworkAndAsset: Array<{
1270
+ network: string;
1271
+ asset: string;
1272
+ settlesOk: number;
1273
+ settlesFailed: number;
1274
+ verifies: number;
1275
+ volumeAtomic: string;
1276
+ decimals: number | null;
1277
+ lastTs: number;
1278
+ }>;
1279
+ [key: string]: unknown;
1280
+ }>;
1281
+ /**
1282
+ * Recent recorded operations, newest first (`GET /transactions`).
1283
+ *
1284
+ * There is **no pagination and no cursor**: this returns the newest N,
1285
+ * walking back at most 30 days. With 10,000 rows you get the newest 200, not
1286
+ * page one of fifty. `limit` is clamped to 200 by the facilitator.
1287
+ *
1288
+ * `network` matches the canonical slug `/supported` uses, which is not always
1289
+ * the alias you may send — `skale` is accepted inbound but records say
1290
+ * `skale-base`.
1291
+ */
1292
+ getTransactions(options?: {
1293
+ limit?: number;
1294
+ network?: string;
1295
+ }): Promise<{
1296
+ transactions: Array<Record<string, unknown>>;
1297
+ count: number;
1298
+ [key: string]: unknown;
1299
+ }>;
1300
+ /**
1301
+ * Get the facilitator's blocked/sanctioned addresses
1302
+ *
1303
+ * @returns Blacklist info (totalBlocked, loadedAtStartup, addresses)
1304
+ *
1305
+ * @example
1306
+ * ```ts
1307
+ * const bl = await client.getBlacklist();
1308
+ * console.log(`Blocked: ${bl.totalBlocked} addresses`);
1309
+ * ```
1310
+ */
1311
+ getBlacklist(): Promise<{
1312
+ totalBlocked: number;
1313
+ loadedAtStartup: boolean;
1314
+ [key: string]: unknown;
1315
+ }>;
1316
+ /**
1317
+ * Negotiate payment requirements with the facilitator via POST /accepts.
1318
+ *
1319
+ * Sends merchant payment requirements to the facilitator, which matches
1320
+ * them against its supported capabilities and returns enriched requirements
1321
+ * with facilitator data (feePayer, tokens, escrow configuration).
1322
+ *
1323
+ * This is used by Faremeter middleware and clients that need to discover
1324
+ * what the facilitator can settle before constructing payment authorizations.
1325
+ *
1326
+ * @param paymentRequirements - List of payment requirement objects
1327
+ * @param x402Version - x402 protocol version (default: 2)
1328
+ * @returns List of enriched payment requirements with facilitator extras
1329
+ *
1330
+ * @example
1331
+ * ```ts
1332
+ * const enriched = await client.accepts([
1333
+ * {
1334
+ * scheme: 'exact',
1335
+ * network: 'base-mainnet',
1336
+ * maxAmountRequired: '1000000',
1337
+ * resource: 'https://api.example.com/data',
1338
+ * payTo: '0xMerchant...',
1339
+ * },
1340
+ * ]);
1341
+ * // enriched[0].extra.feePayer is now set
1342
+ * ```
1343
+ */
1344
+ accepts(paymentRequirements: PaymentRequirements[], x402Version?: number): Promise<PaymentRequirements[]>;
1345
+ }
1346
+ /**
1347
+ * Create a 402 Payment Required response
1348
+ *
1349
+ * @param requirements - Payment requirements
1350
+ * @param options - Additional response options
1351
+ * @returns Object with status code, headers, and body for the 402 response
1352
+ *
1353
+ * @example
1354
+ * ```ts
1355
+ * // Express.js
1356
+ * app.get('/premium-data', (req, res) => {
1357
+ * const payment = extractPaymentFromHeaders(req.headers);
1358
+ *
1359
+ * if (!payment) {
1360
+ * const { status, headers, body } = create402Response({
1361
+ * amount: '1.00',
1362
+ * recipient: '0x...',
1363
+ * resource: 'https://api.example.com/premium-data',
1364
+ * });
1365
+ * return res.status(status).set(headers).json(body);
1366
+ * }
1367
+ *
1368
+ * // Verify and serve...
1369
+ * });
1370
+ * ```
1371
+ */
1372
+ declare function create402Response(requirements: PaymentRequirementsOptions, options?: {
1373
+ accepts?: PaymentAcceptance[];
1374
+ }): {
1375
+ status: 402;
1376
+ headers: Record<string, string>;
1377
+ body: Record<string, unknown>;
1378
+ };
1379
+ /** The body of a no-verdict refusal. See {@link buildUnavailableResponse}. */
1380
+ interface UnavailableBody {
1381
+ /** Human-readable summary. Never says the payment was rejected. */
1382
+ error: string;
1383
+ /** The facilitator's own reason, under whichever field it populated. */
1384
+ reason?: string;
1385
+ /** Always `true` — this response exists to say "no verdict", not "refused". */
1386
+ retryable: true;
1387
+ /** Whole seconds to wait, mirroring the `Retry-After` header. */
1388
+ retryAfterSeconds: number;
1389
+ /**
1390
+ * `false` for `forward_failed` and for a bare timeout: the write may already
1391
+ * have landed, so the caller must reconcile before resending.
1392
+ */
1393
+ safeToReplay: boolean;
1394
+ }
1395
+ /** A framework-agnostic HTTP response. See {@link buildUnavailableResponse}. */
1396
+ interface UnavailableResponse {
1397
+ /** Always 503. */
1398
+ status: 503;
1399
+ /** `Retry-After`, ready to spread onto any framework's header setter. */
1400
+ headers: Record<string, string>;
1401
+ body: UnavailableBody;
1402
+ /** Same value as the header, already clamped to a whole second >= 1. */
1403
+ retryAfterSeconds: number;
1404
+ }
1405
+ /**
1406
+ * Build the answer to a facilitator refusal that reached NO VERDICT.
1407
+ *
1408
+ * `verify` returns invalid for two different things: a payment that was
1409
+ * REJECTED, and a facilitator that never reached a verdict (`retryable`).
1410
+ * Answering `402` in the second case tells the buyer to sign a NEW
1411
+ * authorization while the first one is still live and still spendable by the
1412
+ * facilitator — so the buyer pays twice. The correct answer is `503` plus
1413
+ * `Retry-After`, which asks for the SAME credential again.
1414
+ *
1415
+ * This returns plain data — status, headers, body — so a handler written
1416
+ * without Express (Lambda, Hono, a Next route, Fastify, a bare `Response`) can
1417
+ * answer correctly without re-deriving the rule. The Express and Hono
1418
+ * middlewares in this SDK both build their reply from it.
1419
+ *
1420
+ * @param message - Summary for the `error` field, e.g. `'Payment verification unavailable'`
1421
+ * @param failure - The facilitator result. Only its failure fields are read.
1422
+ *
1423
+ * @example Lambda / any framework
1424
+ * ```ts
1425
+ * const verifyResult = await client.verify(payment, requirements);
1426
+ * if (!verifyResult.isValid && verifyResult.retryable) {
1427
+ * const r = buildUnavailableResponse('Payment verification unavailable', verifyResult);
1428
+ * return { statusCode: r.status, headers: r.headers, body: JSON.stringify(r.body) };
1429
+ * }
1430
+ * ```
1431
+ *
1432
+ * @example Web `Response`
1433
+ * ```ts
1434
+ * const r = buildUnavailableResponse('Payment settlement unavailable', settleResult);
1435
+ * return Response.json(r.body, { status: r.status, headers: r.headers });
1436
+ * ```
1437
+ */
1438
+ declare function buildUnavailableResponse(message: string, failure: FacilitatorFailureFields & {
1439
+ error?: string;
1440
+ invalidReason?: string;
1441
+ }): UnavailableResponse;
1442
+ /**
1443
+ * Create an Express-compatible middleware for x402 payments
1444
+ *
1445
+ * @param getRequirements - Function to get payment requirements for a request
1446
+ * @param options - Middleware options
1447
+ * @returns Express middleware function
1448
+ *
1449
+ * @example
1450
+ * ```ts
1451
+ * const paymentMiddleware = createPaymentMiddleware(
1452
+ * (req) => ({
1453
+ * amount: '1.00',
1454
+ * recipient: process.env.PAYMENT_RECIPIENT,
1455
+ * resource: `${req.protocol}://${req.get('host')}${req.originalUrl}`,
1456
+ * }),
1457
+ * { facilitatorUrl: 'https://facilitator.uvd.xyz' }
1458
+ * );
1459
+ *
1460
+ * app.get('/premium/*', paymentMiddleware, async (req, res) => {
1461
+ * const settleResult = await req.x402?.settle();
1462
+ * if (!settleResult?.success) {
1463
+ * return res.status(500).json({ error: settleResult?.error });
1464
+ * }
1465
+ *
1466
+ * res.json({ premium: 'data' });
1467
+ * });
1468
+ * ```
1469
+ */
1470
+ declare function createPaymentMiddleware(getRequirements: (req: {
1471
+ headers: Record<string, string | string[] | undefined>;
1472
+ }) => PaymentRequirementsOptions, options?: PaymentMiddlewareOptions): (req: {
1473
+ headers: Record<string, string | string[] | undefined>;
1474
+ x402?: VerifiedPaymentState;
1475
+ }, res: {
1476
+ status: (code: number) => {
1477
+ json: (body: unknown) => void;
1478
+ set: (headers: Record<string, string>) => {
1479
+ json: (body: unknown) => void;
1480
+ };
1481
+ };
1482
+ }, next: () => void) => Promise<void>;
1483
+ /**
1484
+ * Options for creating a Hono x402 payment middleware
1485
+ */
1486
+ interface HonoMiddlewareOptions extends PaymentMiddlewareOptions {
1487
+ /** Payment requirements to advertise */
1488
+ accepts: PaymentAcceptance[];
1489
+ /** Response version to advertise (defaults to auto) */
1490
+ x402Version?: X402Version | 'auto';
1491
+ /** Custom requirement resolver for ambiguous multi-accept flows */
1492
+ resolveRequirement?: PaymentRequirementResolver;
1493
+ }
1494
+ declare function createHonoMiddleware(options: HonoMiddlewareOptions): (c: {
1495
+ req: {
1496
+ header: (name: string) => string | undefined;
1497
+ url: string;
1498
+ };
1499
+ json: (body: unknown, status?: number) => unknown;
1500
+ set?: (key: string, value: unknown) => void;
1501
+ /** Hono's response-header setter. Optional so older context doubles still fit. */
1502
+ header?: (name: string, value: string) => void;
1503
+ }, next: () => Promise<void>) => Promise<unknown>;
1504
+ /**
1505
+ * Maximum length of the free-text `q` filter.
1506
+ *
1507
+ * Mirrors the facilitator's `MAX_SEARCH_LEN`; a longer needle is rejected
1508
+ * server-side with a 400.
1509
+ */
1510
+ declare const MAX_SEARCH_LEN = 128;
1511
+ /**
1512
+ * Liveness of a registered resource, as measured by the facilitator's prober.
1513
+ *
1514
+ * Resources that stop answering are quarantined rather than deleted, so filter
1515
+ * on this before paying anyone.
1516
+ */
1517
+ type DiscoveryHealthStatus = 'alive' | 'degraded' | 'auth_gated' | 'quarantined' | 'unknown' | 'unprobeable';
1518
+ /** Values accepted by the `health` filter, including the `any` escape hatch. */
1519
+ declare const HEALTH_FILTERS: readonly ["alive", "degraded", "auth_gated", "quarantined", "unknown", "unprobeable", "any"];
1520
+ /** Curated tier, in descending order of trust. */
1521
+ type DiscoveryTier = 'first_party' | 'vip' | 'verified' | 'listed';
1522
+ /** Values accepted by the `tier` filter. */
1523
+ declare const TIER_FILTERS: readonly ["first_party", "vip", "verified", "listed"];
1524
+ /** How a resource got into the registry. */
1525
+ type DiscoverySource = 'self_registered' | 'settlement' | 'crawled' | 'aggregated';
1526
+ /** Health of a single resource, as reported by the registry's prober. */
1527
+ interface DiscoveryHealth {
1528
+ /** Last observed liveness */
1529
+ status?: DiscoveryHealthStatus;
1530
+ /** Unix epoch seconds of the last probe */
1531
+ lastChecked?: number;
1532
+ /** HTTP status the probe got back (402 is the healthy answer for x402) */
1533
+ httpStatus?: number;
1534
+ /** Round-trip time of the last probe, in milliseconds */
1535
+ latencyMs?: number;
1536
+ }
1537
+ /** Curation metadata attached to a resource. */
1538
+ interface DiscoveryCuration {
1539
+ /** Curated tier */
1540
+ tier?: DiscoveryTier;
1541
+ /** Human-readable name of the curated set */
1542
+ label?: string;
1543
+ }
1544
+ /** One payment method a resource declares. */
1545
+ interface DiscoveryAccepts {
1546
+ /** Payment scheme ("exact", "escrow", "commerce") */
1547
+ scheme: string;
1548
+ /** CAIP-2 network id, e.g. "eip155:8453" */
1549
+ network: string;
1550
+ /** Token contract address */
1551
+ asset?: string;
1552
+ /** Price in atomic units of `asset` */
1553
+ amount?: string;
1554
+ /** Recipient address */
1555
+ payTo?: string;
1556
+ /** Settlement deadline in seconds */
1557
+ maxTimeoutSeconds?: number;
1558
+ /** Scheme-specific extras (EIP-712 domain, etc.) */
1559
+ extra?: Record<string, unknown>;
1560
+ /** Anything the registry adds later */
1561
+ [key: string]: unknown;
1562
+ }
1563
+ /**
1564
+ * A discoverable paid resource, exactly as `GET /discovery/resources` serves it.
1565
+ *
1566
+ * Timestamps are Unix epoch **seconds**, not ISO strings and not milliseconds.
1567
+ */
1568
+ interface DiscoveryResource {
1569
+ /** Resource URL. This is the registry's primary key -- there is no `id` */
1570
+ url: string;
1571
+ /** Resource type ("http", "mcp", "a2a") */
1572
+ type: string;
1573
+ /** x402 protocol version the resource speaks */
1574
+ x402Version: number;
1575
+ /** Human-readable description */
1576
+ description?: string;
1577
+ /** Payment methods the resource accepts */
1578
+ accepts: DiscoveryAccepts[];
1579
+ /** Free-form metadata (category, provider, tags) */
1580
+ metadata?: Record<string, unknown>;
1581
+ /** How this resource entered the registry */
1582
+ source?: DiscoverySource;
1583
+ /** Facilitator this resource was aggregated from */
1584
+ sourceFacilitator?: string;
1585
+ /** Unix epoch seconds when the registry first saw this resource */
1586
+ firstSeen?: number;
1587
+ /** Unix epoch seconds when the registry last saw this resource */
1588
+ lastSeen?: number;
1589
+ /** Unix epoch seconds of the last change to this record */
1590
+ lastUpdated?: number;
1591
+ /** Liveness, when the resource has been probed */
1592
+ health?: DiscoveryHealth;
1593
+ /** Curation tier, when the resource has been curated */
1594
+ curation?: DiscoveryCuration;
1595
+ /** Anything the registry adds later */
1596
+ [key: string]: unknown;
1597
+ }
1598
+ /** Pagination envelope of `GET /discovery/resources`. */
1599
+ interface DiscoveryPagination {
1600
+ /** Page size that was applied */
1601
+ limit: number;
1602
+ /** Offset that was applied */
1603
+ offset: number;
1604
+ /** Total number of resources matching the filters, across all pages */
1605
+ total: number;
1606
+ }
1607
+ /** Paginated response from `GET /discovery/resources`. */
1608
+ interface DiscoveryResponse {
1609
+ /** x402 protocol version of the response envelope */
1610
+ x402Version: number;
1611
+ /** Resources on this page */
1612
+ items: DiscoveryResource[];
1613
+ /** Pagination state */
1614
+ pagination: DiscoveryPagination;
1615
+ }
1616
+ /**
1617
+ * Filters for `listResources()`.
1618
+ *
1619
+ * Every one of these is applied server-side over the whole catalog, so
1620
+ * `pagination.total` reflects the filtered set. Filtering a page after the
1621
+ * fact is not the same thing and will under-report.
1622
+ */
1623
+ interface DiscoveryListOptions {
1624
+ /** Page size (default: 10, max: 100) */
1625
+ limit?: number;
1626
+ /** Number of resources to skip */
1627
+ offset?: number;
1628
+ /** Filter by category */
1629
+ category?: string;
1630
+ /** Filter by network, CAIP-2 or v1 name */
1631
+ network?: string;
1632
+ /** Filter by provider name */
1633
+ provider?: string;
1634
+ /** Filter by tag */
1635
+ tag?: string;
1636
+ /** Filter by how the resource was discovered */
1637
+ source?: DiscoverySource;
1638
+ /** Filter by originating facilitator */
1639
+ sourceFacilitator?: string;
1640
+ /** Filter by liveness, or 'any' to opt out of the default visibility rules */
1641
+ health?: DiscoveryHealthStatus | 'any';
1642
+ /** Filter by curated tier */
1643
+ tier?: DiscoveryTier;
1644
+ /** Free-text search over url / description / provider / category / tags */
1645
+ q?: string;
1646
+ }
1647
+ /** Options for `registerResource()`. */
1648
+ interface DiscoveryRegisterOptions {
1649
+ /** URL of the paid resource. Doubles as its identity in the registry */
1650
+ url: string;
1651
+ /** Resource type (default: "http") */
1652
+ type?: string;
1653
+ /** Human-readable description */
1654
+ description?: string;
1655
+ /** Payment methods the resource accepts */
1656
+ accepts?: DiscoveryAccepts[];
1657
+ /** Free-form metadata (category, provider, tags) */
1658
+ metadata?: Record<string, unknown>;
1659
+ }
1660
+ /** Aggregate catalog metrics from `GET /discovery/stats`. */
1661
+ interface DiscoveryStats {
1662
+ /** Every record the registry holds, including quarantined ones */
1663
+ total: number;
1664
+ /** Records served by default listings */
1665
+ visible: number;
1666
+ /** Counts by discovery source */
1667
+ bySource: Record<string, number>;
1668
+ /** Counts by originating facilitator */
1669
+ bySourceFacilitator: Record<string, number>;
1670
+ /** Counts by CAIP-2 network */
1671
+ byNetwork: Record<string, number>;
1672
+ /** Counts by curated tier */
1673
+ byTier: Record<string, number>;
1674
+ /** Counts by liveness */
1675
+ byHealth: Record<string, number>;
1676
+ /** Unix epoch seconds this snapshot was computed (60s cache) */
1677
+ generatedAt?: number;
1678
+ }
1679
+ /** Options for the {@link BazaarClient}. */
1680
+ interface BazaarClientOptions {
1681
+ /** Facilitator base URL (default: https://facilitator.ultravioletadao.xyz) */
1682
+ baseUrl?: string;
1683
+ /** Request timeout in milliseconds (default: 30000) */
1684
+ timeout?: number;
1685
+ }
1686
+ /** Render an epoch-seconds field as a `Date`. */
1687
+ declare function epochToDate(seconds?: number): Date | undefined;
1688
+ /** True when the last probe reached this resource. */
1689
+ declare function isAlive(resource: DiscoveryResource): boolean;
1690
+ /**
1691
+ * Client for the x402 Bazaar Discovery API.
1692
+ *
1693
+ * The Bazaar is the facilitator's own registry of x402-enabled resources.
1694
+ * Providers register their endpoints and consumers discover them, with a
1695
+ * liveness probe and a curation tier attached to every record.
1696
+ *
1697
+ * @example
1698
+ * ```ts
1699
+ * const bazaar = new BazaarClient();
1700
+ *
1701
+ * // Only endpoints a probe actually reached, best-curated first
1702
+ * const page = await bazaar.listResources({ limit: 20, health: 'alive', tier: 'vip' });
1703
+ * for (const r of page.items) {
1704
+ * console.log(r.url, r.health?.status, r.health?.latencyMs, r.curation?.label);
1705
+ * }
1706
+ *
1707
+ * // Free-text search runs server-side over the whole catalog
1708
+ * const hits = await bazaar.listResources({ q: 'logs' });
1709
+ * console.log(hits.pagination.total);
1710
+ *
1711
+ * // Register your own
1712
+ * await bazaar.registerResource({
1713
+ * url: 'https://api.example.com/v1/generate',
1714
+ * description: 'Generate images with AI',
1715
+ * accepts: [{
1716
+ * scheme: 'exact',
1717
+ * network: 'eip155:8453',
1718
+ * asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
1719
+ * amount: '10000',
1720
+ * payTo: '0xYourWallet...',
1721
+ * maxTimeoutSeconds: 60,
1722
+ * }],
1723
+ * metadata: { category: 'ai', tags: ['image'] },
1724
+ * });
1725
+ * ```
1726
+ */
1727
+ declare class BazaarClient {
1728
+ private readonly baseUrl;
1729
+ private readonly timeout;
1730
+ constructor(options?: BazaarClientOptions);
1731
+ /**
1732
+ * Issue a request against the facilitator with the configured timeout.
1733
+ */
1734
+ private request;
1735
+ /**
1736
+ * List resources from the discovery registry.
1737
+ *
1738
+ * @param options - Server-side filters and pagination
1739
+ * @returns One page of resources plus the total across all pages
1740
+ *
1741
+ * @example
1742
+ * ```ts
1743
+ * const page = await bazaar.listResources({ network: 'eip155:8453', health: 'alive' });
1744
+ * ```
1745
+ */
1746
+ listResources(options?: DiscoveryListOptions): Promise<DiscoveryResponse>;
1747
+ /**
1748
+ * Walk the whole filtered catalog, one page at a time.
1749
+ *
1750
+ * Pages are fetched in sequence rather than in parallel: the read routes are
1751
+ * rate limited, and a burst of parallel pages is how a legitimate catalog
1752
+ * walk turns into a wall of 429s.
1753
+ *
1754
+ * @param options - Same filters as {@link listResources}; `limit` is the page size
1755
+ *
1756
+ * @example
1757
+ * ```ts
1758
+ * for await (const r of bazaar.iterateResources({ health: 'alive' })) {
1759
+ * console.log(r.url);
1760
+ * }
1761
+ * ```
1762
+ */
1763
+ iterateResources(options?: DiscoveryListOptions): AsyncGenerator<DiscoveryResource, void, undefined>;
1764
+ /**
1765
+ * Look up a single resource by its URL.
1766
+ *
1767
+ * The registry keys on URL and has no by-id lookup, so this searches and
1768
+ * then matches exactly.
1769
+ *
1770
+ * @param resourceUrl - Exact URL of the resource
1771
+ * @returns The resource, or null when it is not registered
1772
+ */
1773
+ getResourceByUrl(resourceUrl: string): Promise<DiscoveryResource | null>;
1774
+ /**
1775
+ * Register a paid resource in the discovery registry.
1776
+ *
1777
+ * Registration is open and rate limited; re-registering a known URL updates
1778
+ * the existing record rather than creating a duplicate.
1779
+ *
1780
+ * @param options - Resource details
1781
+ * @returns The registry's acknowledgement
1782
+ */
1783
+ registerResource(options: DiscoveryRegisterOptions): Promise<Record<string, unknown>>;
1784
+ /**
1785
+ * Aggregate catalog metrics (60s cached server-side).
1786
+ *
1787
+ * @returns Counts by source, facilitator, network, tier and liveness
1788
+ */
1789
+ getStats(): Promise<DiscoveryStats>;
1790
+ /**
1791
+ * Check that the facilitator serving the registry is up.
1792
+ *
1793
+ * @returns True when the facilitator answers its health check
1794
+ */
1795
+ healthCheck(): Promise<boolean>;
1796
+ /**
1797
+ * @deprecated Renamed to {@link listResources}, which returns the registry's
1798
+ * real `{ items, pagination }` envelope. The old `discover()` returned a
1799
+ * `{ resources, page, totalPages }` shape that no endpoint ever served.
1800
+ */
1801
+ discover(options?: DiscoveryListOptions): Promise<DiscoveryResponse>;
1802
+ }
1803
+ /**
1804
+ * @deprecated Use {@link DiscoveryResource}. The old shape (`id`, `name`,
1805
+ * `pricePerRequest`, `isActive`, ISO `createdAt`) described an API that was
1806
+ * never deployed.
1807
+ */
1808
+ type BazaarResource = DiscoveryResource;
1809
+ /** @deprecated Use {@link DiscoveryResponse}. */
1810
+ type BazaarDiscoverResponse = DiscoveryResponse;
1811
+ /** @deprecated Use {@link DiscoveryListOptions}. */
1812
+ type BazaarDiscoverOptions = DiscoveryListOptions;
1813
+ /** @deprecated Use {@link DiscoveryRegisterOptions}. */
1814
+ type BazaarRegisterOptions = DiscoveryRegisterOptions;
1815
+ /**
1816
+ * Escrow payment status
1817
+ */
1818
+ type EscrowStatus = 'pending' | 'held' | 'released' | 'refunded' | 'disputed' | 'expired';
1819
+ /**
1820
+ * Refund request status
1821
+ */
1822
+ type RefundStatus = 'pending' | 'approved' | 'rejected' | 'processed' | 'disputed';
1823
+ /**
1824
+ * Dispute resolution outcome
1825
+ */
1826
+ type DisputeOutcome = 'pending' | 'payer_wins' | 'recipient_wins' | 'split';
1827
+ /**
1828
+ * Escrow payment record
1829
+ */
1830
+ interface EscrowPayment {
1831
+ /** Unique escrow ID */
1832
+ id: string;
1833
+ /** Original payment header (base64 encoded) */
1834
+ paymentHeader: string;
1835
+ /** Current status */
1836
+ status: EscrowStatus;
1837
+ /** Network where payment was made */
1838
+ network: string;
1839
+ /** Payer address */
1840
+ payer: string;
1841
+ /** Recipient address */
1842
+ recipient: string;
1843
+ /** Amount in atomic units */
1844
+ amount: string;
1845
+ /** Token/asset contract */
1846
+ asset: string;
1847
+ /** Resource URL being paid for */
1848
+ resource: string;
1849
+ /** Escrow expiration timestamp (ISO) */
1850
+ expiresAt: string;
1851
+ /** Release conditions (optional) */
1852
+ releaseConditions?: {
1853
+ /** Minimum time before release (seconds) */
1854
+ minHoldTime?: number;
1855
+ /** Required confirmations */
1856
+ confirmations?: number;
1857
+ /** Custom condition metadata */
1858
+ custom?: unknown;
1859
+ };
1860
+ /** Transaction hash if released/refunded */
1861
+ transactionHash?: string;
1862
+ /** Creation timestamp (ISO) */
1863
+ createdAt: string;
1864
+ /** Last update timestamp (ISO) */
1865
+ updatedAt: string;
1866
+ }
1867
+ /**
1868
+ * Refund request record
1869
+ */
1870
+ interface RefundRequest {
1871
+ /** Unique refund request ID */
1872
+ id: string;
1873
+ /** Related escrow ID */
1874
+ escrowId: string;
1875
+ /** Current status */
1876
+ status: RefundStatus;
1877
+ /** Reason for refund request */
1878
+ reason: string;
1879
+ /** Additional evidence/details */
1880
+ evidence?: string;
1881
+ /** Amount requested (may be partial) */
1882
+ amountRequested: string;
1883
+ /** Amount approved (if any) */
1884
+ amountApproved?: string;
1885
+ /** Requester (payer) address */
1886
+ requester: string;
1887
+ /** Transaction hash if processed */
1888
+ transactionHash?: string;
1889
+ /** Response from recipient/facilitator */
1890
+ response?: {
1891
+ status: 'approved' | 'rejected';
1892
+ reason?: string;
1893
+ respondedAt: string;
1894
+ };
1895
+ /** Creation timestamp (ISO) */
1896
+ createdAt: string;
1897
+ /** Last update timestamp (ISO) */
1898
+ updatedAt: string;
1899
+ }
1900
+ /**
1901
+ * Dispute record
1902
+ */
1903
+ interface Dispute {
1904
+ /** Unique dispute ID */
1905
+ id: string;
1906
+ /** Related escrow ID */
1907
+ escrowId: string;
1908
+ /** Related refund request ID (if any) */
1909
+ refundRequestId?: string;
1910
+ /** Dispute outcome */
1911
+ outcome: DisputeOutcome;
1912
+ /** Initiator (payer or recipient) */
1913
+ initiator: 'payer' | 'recipient';
1914
+ /** Reason for dispute */
1915
+ reason: string;
1916
+ /** Evidence from payer */
1917
+ payerEvidence?: string;
1918
+ /** Evidence from recipient */
1919
+ recipientEvidence?: string;
1920
+ /** Arbitration notes */
1921
+ arbitrationNotes?: string;
1922
+ /** Amount resolved to payer */
1923
+ payerAmount?: string;
1924
+ /** Amount resolved to recipient */
1925
+ recipientAmount?: string;
1926
+ /** Transaction hash(es) for resolution */
1927
+ transactionHashes?: string[];
1928
+ /** Creation timestamp (ISO) */
1929
+ createdAt: string;
1930
+ /** Resolution timestamp (ISO) */
1931
+ resolvedAt?: string;
1932
+ }
1933
+ /**
1934
+ * Options for creating an escrow payment
1935
+ */
1936
+ interface CreateEscrowOptions {
1937
+ /** Payment header (from client SDK) */
1938
+ paymentHeader: string;
1939
+ /** Payment requirements */
1940
+ requirements: PaymentRequirements;
1941
+ /** Escrow duration in seconds (default: 86400 = 24h) */
1942
+ escrowDuration?: number;
1943
+ /** Release conditions */
1944
+ releaseConditions?: {
1945
+ minHoldTime?: number;
1946
+ confirmations?: number;
1947
+ custom?: unknown;
1948
+ };
1949
+ }
1950
+ /**
1951
+ * Options for requesting a refund
1952
+ */
1953
+ interface RequestRefundOptions {
1954
+ /** Escrow ID to refund */
1955
+ escrowId: string;
1956
+ /** Reason for refund */
1957
+ reason: string;
1958
+ /** Amount to refund (full amount if not specified) */
1959
+ amount?: string;
1960
+ /** Supporting evidence */
1961
+ evidence?: string;
1962
+ }
1963
+ /**
1964
+ * Options for the EscrowClient
1965
+ */
1966
+ interface EscrowClientOptions {
1967
+ /** Base URL of the Escrow API (default: https://escrow.ultravioletadao.xyz) */
1968
+ baseUrl?: string;
1969
+ /** API key for authenticated operations */
1970
+ apiKey?: string;
1971
+ /** Request timeout in milliseconds (default: 30000) */
1972
+ timeout?: number;
1973
+ }
1974
+ /**
1975
+ * Client for x402 Escrow & Refund operations
1976
+ *
1977
+ * The Escrow system holds payments until service is verified,
1978
+ * enabling refunds and dispute resolution.
1979
+ *
1980
+ * @example
1981
+ * ```ts
1982
+ * // Create escrow payment (backend)
1983
+ * const escrow = new EscrowClient();
1984
+ * const escrowPayment = await escrow.createEscrow({
1985
+ * paymentHeader: req.headers['x-payment'],
1986
+ * requirements: paymentRequirements,
1987
+ * escrowDuration: 86400, // 24 hours
1988
+ * });
1989
+ *
1990
+ * // After service is provided, release the escrow
1991
+ * await escrow.release(escrowPayment.id);
1992
+ *
1993
+ * // If service not provided, payer can request refund
1994
+ * await escrow.requestRefund({
1995
+ * escrowId: escrowPayment.id,
1996
+ * reason: 'Service not delivered within expected timeframe',
1997
+ * });
1998
+ * ```
1999
+ */
2000
+ declare class EscrowClient {
2001
+ private readonly baseUrl;
2002
+ private readonly apiKey?;
2003
+ private readonly timeout;
2004
+ constructor(options?: EscrowClientOptions);
2005
+ private getHeaders;
2006
+ /**
2007
+ * Create an escrow payment
2008
+ *
2009
+ * Holds the payment in escrow until released or refunded.
2010
+ *
2011
+ * @param options - Escrow creation options
2012
+ * @returns Created escrow payment
2013
+ */
2014
+ createEscrow(options: CreateEscrowOptions): Promise<EscrowPayment>;
2015
+ /**
2016
+ * Get escrow payment by ID
2017
+ *
2018
+ * @param escrowId - Escrow payment ID
2019
+ * @returns Escrow payment details
2020
+ */
2021
+ getEscrow(escrowId: string): Promise<EscrowPayment>;
2022
+ /**
2023
+ * Release escrow funds to recipient
2024
+ *
2025
+ * Call this after service has been successfully provided.
2026
+ *
2027
+ * @param escrowId - Escrow payment ID
2028
+ * @returns Updated escrow payment with transaction hash
2029
+ */
2030
+ release(escrowId: string): Promise<EscrowPayment>;
2031
+ /**
2032
+ * Request a refund for an escrow payment
2033
+ *
2034
+ * Initiates a refund request that must be approved.
2035
+ *
2036
+ * @param options - Refund request options
2037
+ * @returns Created refund request
2038
+ */
2039
+ requestRefund(options: RequestRefundOptions): Promise<RefundRequest>;
2040
+ /**
2041
+ * Approve a refund request (for recipients)
2042
+ *
2043
+ * @param refundId - Refund request ID
2044
+ * @param amount - Amount to approve (may be less than requested)
2045
+ * @returns Updated refund request
2046
+ */
2047
+ approveRefund(refundId: string, amount?: string): Promise<RefundRequest>;
2048
+ /**
2049
+ * Reject a refund request (for recipients)
2050
+ *
2051
+ * @param refundId - Refund request ID
2052
+ * @param reason - Reason for rejection
2053
+ * @returns Updated refund request
2054
+ */
2055
+ rejectRefund(refundId: string, reason: string): Promise<RefundRequest>;
2056
+ /**
2057
+ * Get refund request by ID
2058
+ *
2059
+ * @param refundId - Refund request ID
2060
+ * @returns Refund request details
2061
+ */
2062
+ getRefund(refundId: string): Promise<RefundRequest>;
2063
+ /**
2064
+ * Open a dispute for an escrow payment
2065
+ *
2066
+ * Initiates arbitration when payer and recipient disagree.
2067
+ *
2068
+ * @param escrowId - Escrow payment ID
2069
+ * @param reason - Reason for dispute
2070
+ * @param evidence - Supporting evidence
2071
+ * @returns Created dispute
2072
+ */
2073
+ openDispute(escrowId: string, reason: string, evidence?: string): Promise<Dispute>;
2074
+ /**
2075
+ * Submit evidence to a dispute
2076
+ *
2077
+ * @param disputeId - Dispute ID
2078
+ * @param evidence - Evidence to submit
2079
+ * @returns Updated dispute
2080
+ */
2081
+ submitEvidence(disputeId: string, evidence: string): Promise<Dispute>;
2082
+ /**
2083
+ * Get dispute by ID
2084
+ *
2085
+ * @param disputeId - Dispute ID
2086
+ * @returns Dispute details
2087
+ */
2088
+ getDispute(disputeId: string): Promise<Dispute>;
2089
+ /**
2090
+ * List escrow payments (with filters)
2091
+ *
2092
+ * @param options - Filter and pagination options
2093
+ * @returns Paginated list of escrow payments
2094
+ */
2095
+ listEscrows(options?: {
2096
+ status?: EscrowStatus;
2097
+ payer?: string;
2098
+ recipient?: string;
2099
+ page?: number;
2100
+ limit?: number;
2101
+ }): Promise<{
2102
+ escrows: EscrowPayment[];
2103
+ total: number;
2104
+ page: number;
2105
+ limit: number;
2106
+ hasMore: boolean;
2107
+ }>;
2108
+ /**
2109
+ * Query on-chain escrow state from the facilitator
2110
+ *
2111
+ * Calls POST /escrow/state to read current escrow state without settlement.
2112
+ *
2113
+ * @param options - Escrow state query parameters
2114
+ * @returns On-chain escrow state (status, balance, timestamps)
2115
+ *
2116
+ * @example
2117
+ * ```ts
2118
+ * const state = await escrow.getEscrowState({
2119
+ * network: 'base-mainnet',
2120
+ * payer: '0xPayer...',
2121
+ * recipient: '0xRecipient...',
2122
+ * nonce: '0x1234...',
2123
+ * });
2124
+ * console.log(`Status: ${state.status}`);
2125
+ * ```
2126
+ */
2127
+ getEscrowState(options: {
2128
+ network: string;
2129
+ payer: string;
2130
+ recipient: string;
2131
+ nonce: string;
2132
+ }): Promise<Record<string, unknown>>;
2133
+ /**
2134
+ * Check Escrow API health
2135
+ *
2136
+ * @returns True if healthy
2137
+ */
2138
+ healthCheck(): Promise<boolean>;
2139
+ }
2140
+ /**
2141
+ * Check if an escrow can be released
2142
+ *
2143
+ * @param escrow - Escrow payment to check
2144
+ * @returns True if the escrow can be released
2145
+ */
2146
+ declare function canReleaseEscrow(escrow: EscrowPayment): boolean;
2147
+ /**
2148
+ * Check if an escrow can be refunded
2149
+ *
2150
+ * @param escrow - Escrow payment to check
2151
+ * @returns True if the escrow can be refunded
2152
+ */
2153
+ declare function canRefundEscrow(escrow: EscrowPayment): boolean;
2154
+ /**
2155
+ * Check if an escrow is expired
2156
+ *
2157
+ * @param escrow - Escrow payment to check
2158
+ * @returns True if the escrow is expired
2159
+ */
2160
+ declare function isEscrowExpired(escrow: EscrowPayment): boolean;
2161
+ /**
2162
+ * Calculate time remaining until escrow expires
2163
+ *
2164
+ * @param escrow - Escrow payment to check
2165
+ * @returns Milliseconds until expiration (negative if expired)
2166
+ */
2167
+ declare function escrowTimeRemaining(escrow: EscrowPayment): number;
2168
+ /**
2169
+ * ERC-8004 extension identifier
2170
+ */
2171
+ declare const ERC8004_EXTENSION_ID = "8004-reputation";
2172
+ /**
2173
+ * Agent ID type: EVM uses sequential uint256 (number), Solana uses base58 pubkey (string)
2174
+ */
2175
+ type AgentId = number | string;
2176
+ /**
2177
+ * ERC-8004 contract addresses per network (21 networks: 19 EVM + 2 Solana)
2178
+ */
2179
+ declare const ERC8004_CONTRACTS: Record<string, {
2180
+ identityRegistry?: string;
2181
+ reputationRegistry?: string;
2182
+ validationRegistry?: string;
2183
+ agentRegistryProgram?: string;
2184
+ atomEngineProgram?: string;
2185
+ }>;
2186
+ /**
2187
+ * Return the network name the facilitator actually accepts.
2188
+ *
2189
+ * `base-mainnet` reads like the canonical spelling and is not: the facilitator
2190
+ * answers `400 {"error": "Invalid network: base-mainnet"}`. Every name is passed
2191
+ * through here before it reaches a URL or a request body, so callers holding the
2192
+ * old spelling keep working instead of being rejected at the edge.
2193
+ */
2194
+ declare function wireNetwork(network: string): string;
2195
+ /**
2196
+ * Network type for ERC-8004 operations (21 networks: 19 EVM + 2 Solana)
2197
+ *
2198
+ * These are the names the FACILITATOR accepts, verified against
2199
+ * GET /feedback -> supportedNetworks. 'base-mainnet' is kept only as a
2200
+ * deprecated alias: the facilitator rejects it outright (400 "Invalid network"),
2201
+ * so anything passed through this module is normalised to 'base' before it
2202
+ * reaches the wire. Use 'base'.
2203
+ */
2204
+ type Erc8004Network = 'ethereum' | 'base' | 'polygon' | 'arbitrum' | 'optimism' | 'celo' | 'bsc' | 'monad' | 'avalanche' | 'scroll' | 'skale-base' | 'base-mainnet' | 'ethereum-sepolia' | 'base-sepolia' | 'polygon-amoy' | 'arbitrum-sepolia' | 'optimism-sepolia' | 'celo-sepolia' | 'avalanche-fuji' | 'skale-base-sepolia' | 'solana' | 'solana-devnet';
2205
+ /**
2206
+ * Networks where the facilitator serves the RELAYED feedback rail, i.e. where
2207
+ * Execution Market has deployed a `FeedbackDelegate` and the facilitator
2208
+ * verified it on-chain (code present, and its `REPUTATION_REGISTRY()` reads
2209
+ * back that network's registry).
2210
+ *
2211
+ * Anywhere else `POST /feedback/evm/prepare` answers 400 — and it should. An
2212
+ * invented delegate address would send a type-4 transaction to an account with
2213
+ * no code behind it, and in the EVM a `.call()` to an address with no code
2214
+ * RETURNS SUCCESS. The failure would look exactly like a rating that rated
2215
+ * nobody.
2216
+ *
2217
+ * `avalanche` is absent and is not waiting to join: the C-Chain rejects the
2218
+ * transaction type itself (`-32000 transaction type not supported`), so there
2219
+ * is nothing to deploy against. Anchor the rating on a chain that supports
2220
+ * EIP-7702; the payment stays where it was made.
2221
+ */
2222
+ declare const RELAYED_FEEDBACK_NETWORKS: readonly Erc8004Network[];
2223
+ /**
2224
+ * Whether `network` serves the rater-authored feedback rail.
2225
+ *
2226
+ * Lets a caller route without paying a round trip for a 400. The facilitator
2227
+ * re-checks the delegate on-chain on every request regardless — this list is a
2228
+ * routing hint, never the authority.
2229
+ */
2230
+ declare function supportsRelayedFeedback(network: string): boolean;
2231
+ /**
2232
+ * An EIP-7702 authorization, as a wallet produces it.
2233
+ *
2234
+ * Needed only the first time a rater rates: it points their EOA at the
2235
+ * `FeedbackDelegate`. Once delegated, `prepare` answers `delegated: true` and
2236
+ * the submission carries no authorization at all.
2237
+ */
2238
+ interface RelayAuthorizationParams {
2239
+ /**
2240
+ * Chain the authorization is for.
2241
+ *
2242
+ * `0` is EIP-7702's wildcard and is valid on EVERY chain — a far broader
2243
+ * grant than pinning this one. Send the chain id `prepare` returned.
2244
+ */
2245
+ chainId: number;
2246
+ /**
2247
+ * The delegate the account is pointed at. Must be the address `prepare`
2248
+ * offered; the facilitator refuses anything else before it pays for a
2249
+ * transaction.
2250
+ */
2251
+ address: string;
2252
+ /** The rater account's nonce at the moment the authorization executes */
2253
+ nonce: number;
2254
+ yParity: number;
2255
+ r: string;
2256
+ s: string;
2257
+ }
2258
+ /**
2259
+ * Request body for `POST /feedback/evm/prepare`.
2260
+ *
2261
+ * `rater` is the address that will appear on-chain as the author, which is the
2262
+ * whole point of this rail.
2263
+ */
2264
+ interface PrepareRelayFeedbackRequest {
2265
+ x402Version: 1 | 2;
2266
+ network: Erc8004Network;
2267
+ feedback: FeedbackParams & {
2268
+ rater: string;
2269
+ };
2270
+ }
2271
+ /**
2272
+ * Response from `POST /feedback/evm/prepare`.
2273
+ *
2274
+ * Everything the rater has to sign so the CHAIN records them as the author
2275
+ * while the facilitator pays the gas.
2276
+ */
2277
+ interface PrepareRelayFeedbackResponse {
2278
+ success: boolean;
2279
+ /** The `FeedbackDelegate` the rater's EOA must be delegated to */
2280
+ delegate?: string;
2281
+ /** Registry calldata the rater is authorising, hex-encoded */
2282
+ data?: string;
2283
+ /**
2284
+ * The value the rater's signature must recover against.
2285
+ *
2286
+ * **The EIP-191 envelope is already applied here.** A holder of a raw key
2287
+ * signs this directly as a prehash (viem's `sign({ hash })`, ethers'
2288
+ * `signingKey.sign`). A WALLET must not be handed this value: `personal_sign`
2289
+ * applies the envelope itself, so it gets wrapped twice and recovers an
2290
+ * address that is not the rater. Wallets sign {@link signingPayload}.
2291
+ */
2292
+ digest?: string;
2293
+ /**
2294
+ * The same hash with the envelope still OFF — what a wallet signs.
2295
+ *
2296
+ * `keccak256('\x19Ethereum Signed Message:\n32' || signingPayload)` is
2297
+ * exactly {@link digest}, so a client can check the two against each other
2298
+ * rather than rebuilding the preimage from `data`.
2299
+ *
2300
+ * Requires facilitator v1.95.0+. Older facilitators omit it; a client that
2301
+ * needs it should fail loudly rather than fall back to signing `digest`
2302
+ * through a wallet, which produces a well-formed signature that authorises
2303
+ * nobody.
2304
+ */
2305
+ signingPayload?: string;
2306
+ /**
2307
+ * The full `eth_signTypedData_v4` payload. **v4 delegates only.**
2308
+ *
2309
+ * Present exactly when the delegate deployed on that chain is v4, which the
2310
+ * facilitator reads from the chain per request rather than assuming from a
2311
+ * release. **When it is present, sign IT** — the wallet renders the agent, the
2312
+ * score, the tags and the deadline as named fields, so the rater sees what
2313
+ * they authorise instead of a hex blob.
2314
+ *
2315
+ * v4 carries no {@link signingPayload} and needs none: `signTypedData` has no
2316
+ * envelope to apply twice, which is the entire class of bug that kept the v3
2317
+ * rail at zero signatures for days.
2318
+ *
2319
+ * Requires facilitator v1.96.0+.
2320
+ */
2321
+ typedData?: Record<string, unknown>;
2322
+ /**
2323
+ * Unix seconds after which the authorisation is void. Short on purpose:
2324
+ * relaying is permissionless, so a signed authorisation is live in the wild
2325
+ * until it expires.
2326
+ */
2327
+ deadline?: number;
2328
+ /** Single-use value binding this authorisation. Echo it back on submit */
2329
+ nonce?: string;
2330
+ /**
2331
+ * Whether the account is already delegated. When `false` the submission MUST
2332
+ * carry an `authorization`.
2333
+ */
2334
+ delegated: boolean;
2335
+ /** The account nonce to put in the EIP-7702 authorization, when needed */
2336
+ accountNonce?: number;
2337
+ chainId: number;
2338
+ error?: string;
2339
+ network: Erc8004Network;
2340
+ }
2341
+ /**
2342
+ * Request body for `POST /feedback/evm/submit`.
2343
+ *
2344
+ * The feedback parameters are not redundant with `prepare`: the facilitator
2345
+ * rebuilds the registry calldata from them and requires the rater's signature
2346
+ * to cover exactly that. It does not relay calldata it was handed.
2347
+ */
2348
+ interface SubmitRelayFeedbackRequest {
2349
+ x402Version: 1 | 2;
2350
+ network: Erc8004Network;
2351
+ feedback: FeedbackParams & {
2352
+ rater: string;
2353
+ };
2354
+ /** The deadline `prepare` returned */
2355
+ deadline: number;
2356
+ /** The single-use nonce `prepare` returned */
2357
+ nonce: string;
2358
+ /**
2359
+ * The rater's signature. It must recover to `rater` over `digest` — so
2360
+ * either a raw-key prehash signature over `digest`, or a wallet
2361
+ * `personal_sign` over `signingPayload`. Not `personal_sign` over `digest`.
2362
+ */
2363
+ signature: string;
2364
+ /** Required only when `prepare` answered `delegated: false` */
2365
+ authorization?: RelayAuthorizationParams;
2366
+ }
2367
+ /**
2368
+ * Request body for `POST /feedback/response/evm/prepare`.
2369
+ *
2370
+ * `responder` is the address the chain will record as the author.
2371
+ */
2372
+ interface PrepareRelayResponseRequest {
2373
+ x402Version: 1 | 2;
2374
+ network: Erc8004Network;
2375
+ responder: string;
2376
+ agentId: number | string;
2377
+ /** WHOSE feedback is being answered — inside the signed struct. */
2378
+ clientAddress: string;
2379
+ /** Which feedback (1-indexed) — also inside the struct. */
2380
+ feedbackIndex: number;
2381
+ responseUri: string;
2382
+ responseHash?: string;
2383
+ }
2384
+ /** Request body for `POST /feedback/response/evm/submit`. */
2385
+ interface SubmitRelayResponseRequest {
2386
+ x402Version: 1 | 2;
2387
+ network: Erc8004Network;
2388
+ responder: string;
2389
+ agentId: number | string;
2390
+ clientAddress: string;
2391
+ feedbackIndex: number;
2392
+ responseUri: string;
2393
+ responseHash?: string;
2394
+ deadline: number;
2395
+ nonce: string;
2396
+ /** The responder's signature over the typed data. */
2397
+ signature: string;
2398
+ authorization?: RelayAuthorizationParams;
2399
+ }
2400
+ /**
2401
+ * Proof of payment returned when settling with ERC-8004 extension
2402
+ */
2403
+ interface ProofOfPayment {
2404
+ /** Transaction hash of the settled payment */
2405
+ transactionHash: string;
2406
+ /** Block number where the transaction was included */
2407
+ blockNumber: number;
2408
+ /** Network where the payment was settled */
2409
+ network: string;
2410
+ /** The payer (consumer/client) address */
2411
+ payer: string;
2412
+ /** The payee (agent/resource owner) address */
2413
+ payee: string;
2414
+ /** Amount paid in token base units */
2415
+ amount: string;
2416
+ /** Token contract address */
2417
+ token: string;
2418
+ /** Unix timestamp of the block */
2419
+ timestamp: number;
2420
+ /** Keccak256 hash of the payment data for verification */
2421
+ paymentHash: string;
2422
+ }
2423
+ /**
2424
+ * Extended settle response with ERC-8004 proof of payment
2425
+ */
2426
+ interface SettleResponseWithProof extends SettleResponse {
2427
+ /** Proof of payment for ERC-8004 reputation submission */
2428
+ proofOfPayment?: ProofOfPayment;
2429
+ }
2430
+ /**
2431
+ * Agent identity from the Identity Registry
2432
+ */
2433
+ interface AgentIdentity {
2434
+ /** The agent's ID (EVM: sequential uint256, Solana: base58 pubkey string) */
2435
+ agentId: AgentId;
2436
+ /** Owner address of the agent NFT */
2437
+ owner: string;
2438
+ /** URI pointing to agent registration file */
2439
+ agentUri: string;
2440
+ /** Payment wallet address (if set) */
2441
+ agentWallet?: string;
2442
+ /** Network where the agent is registered */
2443
+ network: Erc8004Network;
2444
+ }
2445
+ /**
2446
+ * Agent registration file structure (resolved from agentURI)
2447
+ */
2448
+ interface AgentRegistrationFile {
2449
+ /** Type identifier */
2450
+ type: string;
2451
+ /** Agent name */
2452
+ name: string;
2453
+ /** Agent description */
2454
+ description: string;
2455
+ /** Image URL */
2456
+ image?: string;
2457
+ /** List of services the agent provides */
2458
+ services: AgentService[];
2459
+ /** Whether x402 payments are supported */
2460
+ x402Support: boolean;
2461
+ /** Whether the agent is active */
2462
+ active: boolean;
2463
+ /** List of registrations across chains */
2464
+ registrations: AgentRegistration[];
2465
+ /** Supported trust models */
2466
+ supportedTrust: string[];
2467
+ }
2468
+ /**
2469
+ * Agent service entry
2470
+ */
2471
+ interface AgentService {
2472
+ name: string;
2473
+ endpoint: string;
2474
+ version?: string;
2475
+ }
2476
+ /**
2477
+ * Agent registration reference
2478
+ */
2479
+ interface AgentRegistration {
2480
+ agentId: AgentId;
2481
+ agentRegistry: string;
2482
+ }
2483
+ /**
2484
+ * Reputation summary for an agent
2485
+ */
2486
+ interface ReputationSummary {
2487
+ /** Agent ID (EVM: number, Solana: string) */
2488
+ agentId: AgentId;
2489
+ /** Number of feedback entries */
2490
+ count: number;
2491
+ /** Aggregated value */
2492
+ summaryValue: number;
2493
+ /** Decimal places for summaryValue */
2494
+ summaryValueDecimals: number;
2495
+ /** Network */
2496
+ network: Erc8004Network;
2497
+ }
2498
+ /**
2499
+ * Individual feedback entry
2500
+ */
2501
+ interface FeedbackEntry {
2502
+ /** Client who submitted the feedback */
2503
+ client: string;
2504
+ /** Feedback index (1-indexed) */
2505
+ feedbackIndex: number;
2506
+ /** Feedback value */
2507
+ value: number;
2508
+ /** Value decimals */
2509
+ valueDecimals: number;
2510
+ /** Primary tag */
2511
+ tag1: string;
2512
+ /** Secondary tag */
2513
+ tag2: string;
2514
+ /** Whether this feedback was revoked */
2515
+ isRevoked: boolean;
2516
+ }
2517
+ /**
2518
+ * Parameters for submitting reputation feedback
2519
+ */
2520
+ interface FeedbackParams {
2521
+ /** The agent's ID (EVM: tokenId number, Solana: base58 pubkey string) */
2522
+ agentId: AgentId;
2523
+ /** Feedback value (e.g., 87 for 87/100) */
2524
+ value: number;
2525
+ /** Decimal places for value interpretation (0-18) */
2526
+ valueDecimals?: number;
2527
+ /** Primary categorization tag (e.g., "starred", "uptime") */
2528
+ tag1?: string;
2529
+ /** Secondary categorization tag */
2530
+ tag2?: string;
2531
+ /** Service endpoint that was used */
2532
+ endpoint?: string;
2533
+ /** URI to off-chain feedback file (IPFS, HTTPS) */
2534
+ feedbackUri?: string;
2535
+ /** Keccak256 hash of feedback content (for integrity) */
2536
+ feedbackHash?: string;
2537
+ /**
2538
+ * Quality score 0-100.
2539
+ *
2540
+ * Solana only, and effectively required there: the ATOM Engine ignores an
2541
+ * unscored feedback. It is written to the agent but contributes nothing to
2542
+ * reputation, and the program reports `had_impact=false`. This is not
2543
+ * retroactive — reputation stays at zero however much unscored feedback
2544
+ * accumulates.
2545
+ */
2546
+ score?: number;
2547
+ /** Proof of payment (required for authorized feedback) */
2548
+ proof?: ProofOfPayment;
2549
+ }
2550
+ /**
2551
+ * Feedback request body for POST /feedback
2552
+ */
2553
+ interface FeedbackRequest {
2554
+ /** x402 protocol version */
2555
+ x402Version: 1 | 2;
2556
+ /** Network where feedback will be submitted */
2557
+ network: Erc8004Network;
2558
+ /** Feedback parameters */
2559
+ feedback: FeedbackParams;
2560
+ }
2561
+ /**
2562
+ * Feedback response from POST /feedback
2563
+ */
2564
+ interface FeedbackResponse extends FacilitatorFailureFields {
2565
+ /** Whether the feedback was successfully submitted */
2566
+ success: boolean;
2567
+ /** Transaction hash of the feedback submission */
2568
+ transaction?: string;
2569
+ /** Feedback index assigned (1-indexed) */
2570
+ feedbackIndex?: number;
2571
+ /** Error message (if failed) */
2572
+ error?: string;
2573
+ /** Network where feedback was submitted */
2574
+ network: Erc8004Network;
2575
+ }
2576
+ /**
2577
+ * Reputation query response
2578
+ */
2579
+ /**
2580
+ * Error from an ERC-8004 lookup, carrying the HTTP status as a field.
2581
+ *
2582
+ * `notFound` and `retryable` are mutually exclusive and must stay that way in
2583
+ * calling code: the facilitator answers 404 for "this address owns no agent"
2584
+ * and 503 for "I could not find out", usually an RPC failure behind it.
2585
+ * Treating a 503 as absence is how a transient failure becomes a permanent
2586
+ * wrong answer — on a registration path it mints a second agent for an owner
2587
+ * who already has one, burning gas and leaving an orphan.
2588
+ */
2589
+ declare class Erc8004LookupError extends Error {
2590
+ /** HTTP status returned by the facilitator */
2591
+ readonly status: number;
2592
+ /** Raw response body, for debugging */
2593
+ readonly body: string;
2594
+ /**
2595
+ * `Retry-After`, already clamped, when the facilitator sent one.
2596
+ *
2597
+ * Optional so every existing three-argument construction keeps compiling; it
2598
+ * falls back to the default wait rather than to zero, because a caller that
2599
+ * retries instantly on a 503 is the load that caused it.
2600
+ */
2601
+ private readonly retryAfterHint;
2602
+ constructor(message: string, status: number, body: string, retryAfterSeconds?: number);
2603
+ /** The address genuinely owns no agent on this network. */
2604
+ get notFound(): boolean;
2605
+ /**
2606
+ * The lookup reached no verdict. Retry; never read as "owns nothing".
2607
+ *
2608
+ * `502` and `504` join `503` and `429` here: a gateway that answered on the
2609
+ * facilitator's behalf is exactly as silent about the agent's existence, and
2610
+ * reading either as absence has the same consequence -- a duplicate mint.
2611
+ *
2612
+ * **Except when the body says otherwise.** `POST /register` goes through the
2613
+ * same EVM `send_transaction_from` as a settle, so it can answer
2614
+ * `settlement_unconfirmed`: the mint was broadcast and may be mined. That is
2615
+ * a `502` where retrying is precisely the thing that mints the duplicate this
2616
+ * class exists to prevent, so an explicit `retryable: false` wins over the
2617
+ * status. See {@link SETTLEMENT_UNCONFIRMED}.
2618
+ */
2619
+ get retryable(): boolean;
2620
+ /**
2621
+ * The facilitator's own `reason`, when the body carried one.
2622
+ *
2623
+ * On a WRITE route this is the writer-lease reason and it decides whether the
2624
+ * request may be re-sent; see {@link isReplayableLeaseReason}.
2625
+ */
2626
+ get reason(): string | undefined;
2627
+ /** The facilitator's machine-readable `error` code, when the body carried one. */
2628
+ get errorCode(): string | undefined;
2629
+ /**
2630
+ * A transaction that WAS broadcast and could not be confirmed.
2631
+ *
2632
+ * Present on `settlement_unconfirmed`. This is what to do INSTEAD of
2633
+ * retrying: look it up on chain. An error that carries "do not retry" and no
2634
+ * hash leaves the caller with nothing to act on.
2635
+ */
2636
+ get transaction(): string | undefined;
2637
+ /** The payment id for {@link transaction}, identical to a successful settle's. */
2638
+ get paymentId(): string | undefined;
2639
+ /**
2640
+ * The facilitator NAMED a reason proving it executed nothing.
2641
+ *
2642
+ * False for `forward_failed` and for every unattributed 5xx: "something
2643
+ * answered" is not evidence that nothing ran. On `/register`, replaying when
2644
+ * this is false is the sequence that minted five duplicate agents.
2645
+ */
2646
+ get safeToReplay(): boolean;
2647
+ /** Seconds to wait before retrying, clamped. Absent when not retryable. */
2648
+ get retryAfterSeconds(): number | undefined;
2649
+ }
2650
+ /**
2651
+ * ATOM Engine reputation analytics (Solana only).
2652
+ *
2653
+ * Present only when the agent's `atom_stats` account has been initialized. The
2654
+ * facilitator does that during `registerAgent`; agents registered elsewhere may
2655
+ * never have it, in which case their feedback is never scored.
2656
+ *
2657
+ * The engine measures quality through EMA scores, so there are no
2658
+ * positive/negative tallies.
2659
+ */
2660
+ interface AtomStats {
2661
+ /** Trust tier 0-4 */
2662
+ trustTier: number;
2663
+ /** Human-readable trust tier */
2664
+ trustTierName: string;
2665
+ /** Cached quality score */
2666
+ qualityScore: number;
2667
+ /** Cached loyalty score */
2668
+ loyaltyScore: number;
2669
+ /** Statistical confidence */
2670
+ confidence: number;
2671
+ /** Risk assessment (lower is better) */
2672
+ riskScore: number;
2673
+ /** Client diversity from HyperLogLog */
2674
+ diversityRatio: number;
2675
+ /** Lowest score ever recorded */
2676
+ minScore: number;
2677
+ /** Highest score ever recorded */
2678
+ maxScore: number;
2679
+ /** Most recent score recorded */
2680
+ lastScore: number;
2681
+ /** Total feedback counted by the engine */
2682
+ feedbackCount: number;
2683
+ /** Slot of the most recent feedback */
2684
+ lastFeedbackSlot: number;
2685
+ }
2686
+ interface ReputationResponse {
2687
+ agentId: AgentId;
2688
+ summary: ReputationSummary;
2689
+ feedback?: FeedbackEntry[];
2690
+ /** ATOM Engine analytics; absent when the agent has no initialized stats */
2691
+ atomStats?: AtomStats | null;
2692
+ network: Erc8004Network;
2693
+ }
2694
+ /**
2695
+ * Key-value metadata entry for agent registration
2696
+ */
2697
+ interface MetadataEntryParam {
2698
+ /** Metadata key */
2699
+ key: string;
2700
+ /** Metadata value (hex-encoded bytes or UTF-8 string) */
2701
+ value: string;
2702
+ }
2703
+ /**
2704
+ * Request body for POST /register
2705
+ */
2706
+ interface RegisterAgentRequest {
2707
+ /** x402 protocol version */
2708
+ x402Version: 1 | 2;
2709
+ /** Network where agent will be registered */
2710
+ network: Erc8004Network;
2711
+ /** URI pointing to agent registration file (IPFS, HTTPS) */
2712
+ agentUri: string;
2713
+ /** Optional metadata key-value pairs */
2714
+ metadata?: MetadataEntryParam[];
2715
+ /** Optional recipient address - NFT is transferred to this address after minting */
2716
+ recipient?: string;
2717
+ }
2718
+ /**
2719
+ * Response from POST /register
2720
+ */
2721
+ interface RegisterAgentResponse extends FacilitatorFailureFields {
2722
+ /** Whether registration succeeded */
2723
+ success: boolean;
2724
+ /** The newly assigned agent ID (EVM: tokenId number, Solana: base58 pubkey string) */
2725
+ agentId?: AgentId;
2726
+ /** Registration transaction hash */
2727
+ transaction?: string;
2728
+ /** Transfer transaction hash (if recipient was specified) */
2729
+ transferTransaction?: string;
2730
+ /** Owner address of the agent NFT */
2731
+ owner?: string;
2732
+ /** Error message if failed */
2733
+ error?: string;
2734
+ /** Network where agent was registered */
2735
+ network: string;
2736
+ }
2737
+ /**
2738
+ * Response from GET /identity/{network}/owner/{address}
2739
+ */
2740
+ interface IdentityByOwnerResponse {
2741
+ /** First (lowest) token ID owned by this address */
2742
+ agentId: AgentId;
2743
+ /** The queried address (checksummed) */
2744
+ owner: string;
2745
+ /** Agent's registration URI (may be empty) */
2746
+ agentUri: string;
2747
+ /** Network name */
2748
+ network: string;
2749
+ /** Total number of agent NFTs owned (as string) */
2750
+ balance: string;
2751
+ }
2752
+ /**
2753
+ * Response from GET /identity/{network}/{agent_id}/metadata/{key}
2754
+ */
2755
+ /**
2756
+ * Lifecycle of an async registration. `mint_confirmed` and `done` carry an
2757
+ * `agentId`; `failed` carries an `error`.
2758
+ */
2759
+ type RegisterJobStatus = 'pending' | 'mint_confirmed' | 'done' | 'failed';
2760
+ /**
2761
+ * Status of an asynchronous registration.
2762
+ *
2763
+ * Returned by `POST /register` with `Prefer: respond-async` (HTTP 202) and by
2764
+ * `GET /register/status/{jobId}`.
2765
+ *
2766
+ * Terminal jobs are retained for one hour and then age out, after which the
2767
+ * status endpoint 404s. Read the agent id before then, or it is only
2768
+ * recoverable from the chain.
2769
+ */
2770
+ interface RegisterJobResponse {
2771
+ jobId: string;
2772
+ status: RegisterJobStatus;
2773
+ network?: string;
2774
+ agentId?: AgentId;
2775
+ transaction?: string;
2776
+ transferTransaction?: string;
2777
+ owner?: string;
2778
+ error?: string;
2779
+ }
2780
+ /** Whether polling can stop: the job either finished or failed. */
2781
+ declare function isRegisterJobTerminal(job: RegisterJobResponse): boolean;
2782
+ /**
2783
+ * Thrown when a registration is still running after the wait elapsed.
2784
+ *
2785
+ * This is emphatically **not** a failure. The mint may still land. `jobId` is a
2786
+ * field rather than only part of the message, because the correct recovery is to
2787
+ * keep polling `getRegisterStatus(jobId)` — and a caller who cannot reach the id
2788
+ * without parsing a string will re-register instead, minting a duplicate agent.
2789
+ * That is the exact sequence that once produced five duplicate mints.
2790
+ *
2791
+ * Never map this to "registration failed".
2792
+ */
2793
+ declare class RegistrationPendingError extends Error {
2794
+ readonly jobId: string;
2795
+ readonly lastStatus: RegisterJobStatus;
2796
+ readonly timeoutMs: number;
2797
+ readonly retryable = true;
2798
+ constructor(jobId: string, lastStatus: RegisterJobStatus, timeoutMs: number);
2799
+ }
2800
+ interface IdentityMetadataResponse {
2801
+ /** Agent ID (EVM: number, Solana: string) */
2802
+ agentId: AgentId;
2803
+ /** Metadata key */
2804
+ key: string;
2805
+ /**
2806
+ * Raw hex-encoded value. The facilitator sends this as `value`; this field
2807
+ * used to be declared as `valueHex`, which no response ever carried, so it
2808
+ * was always undefined at runtime.
2809
+ */
2810
+ value: string;
2811
+ /** UTF-8 decoded value (if decodable) */
2812
+ valueUtf8?: string;
2813
+ /** Whether the entry can still be changed */
2814
+ immutable?: boolean;
2815
+ /** Network */
2816
+ network: string;
2817
+ }
2818
+ /**
2819
+ * Response from GET /identity/{network}/total-supply
2820
+ *
2821
+ * On Solana the counts come from the Metaplex Core collection, not the registry,
2822
+ * which keeps no counter of its own.
2823
+ */
2824
+ interface IdentityTotalSupplyResponse {
2825
+ /** Registered agents, net of burns */
2826
+ totalSupply: number;
2827
+ /** All-time mint count (Solana) */
2828
+ numMinted?: number;
2829
+ /** Metaplex Core collection backing the count (Solana) */
2830
+ collection?: string;
2831
+ /** Network */
2832
+ network: string;
2833
+ }
2834
+ /**
2835
+ * Options for the ERC8004Client
2836
+ */
2837
+ interface Erc8004ClientOptions {
2838
+ /** Base URL of the facilitator (default: https://facilitator.ultravioletadao.xyz) */
2839
+ baseUrl?: string;
2840
+ /** Request timeout in milliseconds (default: 30000) */
2841
+ timeout?: number;
2842
+ /**
2843
+ * Extra attempts after the first, spent only on a refusal the facilitator
2844
+ * proved it did not execute. Default 2; `0` disables. An ambiguous
2845
+ * `forward_failed` is never replayed at any setting.
2846
+ */
2847
+ retries?: number;
2848
+ }
2849
+ /**
2850
+ * Client for ERC-8004 Trustless Agents API
2851
+ *
2852
+ * Provides methods for:
2853
+ * - Registering new agents (gasless, facilitator pays gas)
2854
+ * - Registering agents on behalf of users (gasless delegation)
2855
+ * - Querying agent identity, metadata, and total supply
2856
+ * - Querying agent reputation
2857
+ * - Submitting reputation feedback
2858
+ * - Revoking feedback
2859
+ *
2860
+ * @example
2861
+ * ```ts
2862
+ * const client = new Erc8004Client();
2863
+ *
2864
+ * // Get agent identity
2865
+ * const identity = await client.getIdentity('ethereum', 42);
2866
+ * console.log(identity.agentUri);
2867
+ *
2868
+ * // Get agent reputation
2869
+ * const reputation = await client.getReputation('ethereum', 42);
2870
+ * console.log(`Score: ${reputation.summary.summaryValue}`);
2871
+ *
2872
+ * // Submit feedback after payment
2873
+ * const result = await client.submitFeedback({
2874
+ * x402Version: 1,
2875
+ * network: 'ethereum',
2876
+ * feedback: {
2877
+ * agentId: 42,
2878
+ * value: 95,
2879
+ * valueDecimals: 0,
2880
+ * tag1: 'quality',
2881
+ * proof: settleResponse.proofOfPayment,
2882
+ * },
2883
+ * });
2884
+ * ```
2885
+ */
2886
+ declare class Erc8004Client {
2887
+ private readonly baseUrl;
2888
+ private readonly timeout;
2889
+ private readonly retries;
2890
+ constructor(options?: Erc8004ClientOptions);
2891
+ /**
2892
+ * POST a write route, keeping a refusal readable.
2893
+ *
2894
+ * Every ERC-8004 write goes through the facilitator's EVM writer lease, so
2895
+ * every one of them can answer `503` + `reason`. Flattened to a string, those
2896
+ * are indistinguishable from "the registry rejected your feedback" — and on
2897
+ * `/register` the wrong reading re-POSTs a mint that may already have landed,
2898
+ * which is precisely how five duplicate agents were once created.
2899
+ *
2900
+ * A refusal the facilitator proved it did not execute is replayed
2901
+ * automatically (`safeToReplay`); `forward_failed` never is.
2902
+ */
2903
+ private writeJson;
2904
+ /**
2905
+ * Get agent identity from the Identity Registry
2906
+ *
2907
+ * @param network - Network where agent is registered
2908
+ * @param agentId - Agent's tokenId
2909
+ * @returns Agent identity information
2910
+ */
2911
+ getIdentity(network: Erc8004Network, agentId: AgentId): Promise<AgentIdentity>;
2912
+ /**
2913
+ * Get agent identity by owner address
2914
+ *
2915
+ * Resolves the first ERC-8004 agent ID owned by a wallet address on a given network.
2916
+ *
2917
+ * @param network - Network to query
2918
+ * @param address - Owner wallet address
2919
+ * @returns Agent identity information including balance
2920
+ *
2921
+ * @example
2922
+ * ```ts
2923
+ * const identity = await client.getIdentityByOwner('base-mainnet', '0x52E0...');
2924
+ * console.log(`Agent #${identity.agentId}, balance: ${identity.balance}`);
2925
+ * ```
2926
+ */
2927
+ getIdentityByOwner(network: Erc8004Network, address: string): Promise<IdentityByOwnerResponse>;
2928
+ /**
2929
+ * Resolve agent registration file from agentURI
2930
+ *
2931
+ * @param agentUri - URI pointing to agent registration file
2932
+ * @returns Resolved agent registration file
2933
+ */
2934
+ resolveAgentUri(agentUri: string): Promise<AgentRegistrationFile>;
2935
+ /**
2936
+ * Get agent reputation from the Reputation Registry
2937
+ *
2938
+ * @param network - Network where agent is registered
2939
+ * @param agentId - Agent's tokenId
2940
+ * @param options - Query options (tag filters, include individual feedback, client addresses)
2941
+ * @param options.clientAddresses - Comma-separated client addresses to filter by.
2942
+ * If omitted, the facilitator auto-discovers all clients via getClients().
2943
+ * @returns Reputation summary and optionally individual feedback entries
2944
+ */
2945
+ getReputation(network: Erc8004Network, agentId: AgentId, options?: {
2946
+ tag1?: string;
2947
+ tag2?: string;
2948
+ includeFeedback?: boolean;
2949
+ clientAddresses?: string;
2950
+ }): Promise<ReputationResponse>;
2951
+ /**
2952
+ * Submit reputation feedback for an agent
2953
+ *
2954
+ * Requires proof of payment for authorized feedback submission.
2955
+ *
2956
+ * @deprecated On this route the facilitator is the AUTHOR: the registry
2957
+ * records `msg.sender`, and that is the facilitator's wallet — which can also
2958
+ * revoke what it wrote. On the networks in {@link RELAYED_FEEDBACK_NETWORKS}
2959
+ * use {@link Erc8004Client.prepareRelayedFeedback} +
2960
+ * {@link Erc8004Client.submitRelayedFeedback} instead, which record the RATER
2961
+ * as author. This route still works and is not going away without notice: it
2962
+ * is the only one available where no `FeedbackDelegate` is deployed.
2963
+ *
2964
+ * @param request - Feedback request with agent ID, value, and proof
2965
+ * @returns Feedback response with transaction hash
2966
+ *
2967
+ * @example
2968
+ * ```ts
2969
+ * // After settling a payment with ERC-8004 extension
2970
+ * const settleResult = await facilitator.settle(payment, {
2971
+ * ...requirements,
2972
+ * extra: { '8004-reputation': { includeProof: true } },
2973
+ * });
2974
+ *
2975
+ * // Submit feedback with proof of payment
2976
+ * const feedback = await erc8004.submitFeedback({
2977
+ * x402Version: 1,
2978
+ * network: 'ethereum',
2979
+ * feedback: {
2980
+ * agentId: 42,
2981
+ * value: 95, // 95/100
2982
+ * valueDecimals: 0,
2983
+ * tag1: 'quality',
2984
+ * tag2: 'response-time',
2985
+ * proof: settleResult.proofOfPayment,
2986
+ * },
2987
+ * });
2988
+ * ```
2989
+ */
2990
+ submitFeedback(request: FeedbackRequest): Promise<FeedbackResponse>;
2991
+ /**
2992
+ * Ask the facilitator what the rater must sign to author a rating.
2993
+ *
2994
+ * Step 1 of the rater-authored rail. Writes nothing on-chain and costs
2995
+ * nothing: it reads the delegate, the rater's delegation state and their
2996
+ * account nonce, then hands back a digest, a deadline and a single-use nonce.
2997
+ *
2998
+ * Why this exists: the ERC-8004 Reputation Registry records `msg.sender` as
2999
+ * the author, and the deployed implementation has no delegation path — no
3000
+ * `giveFeedbackWithSignature`, no ERC-2771 forwarder. So a rating the
3001
+ * facilitator relays the ordinary way is a rating attributed to the
3002
+ * FACILITATOR. EIP-7702 fixes it without touching the registry: the rater
3003
+ * delegates their own EOA to the `FeedbackDelegate` and the transaction is
3004
+ * sent TO THE RATER'S ADDRESS, so the registry sees the rater while the
3005
+ * facilitator pays.
3006
+ *
3007
+ * What to do with the answer:
3008
+ * 1. Produce the rater's signature. **Which value you sign depends on how you
3009
+ * sign it**, and getting it wrong yields a well-formed signature that
3010
+ * authorises nobody:
3011
+ * - raw key: sign `digest` as a **prehash**. It already carries the
3012
+ * EIP-191 envelope.
3013
+ * - wallet: `personal_sign` over `signingPayload`. `personal_sign` adds the
3014
+ * envelope itself, so signing `digest` with it wraps the value TWICE and
3015
+ * recovers a stranger — the only symptom is `relay_bad_signature`.
3016
+ * - **unless `typedData` came back** — that chain runs a v4 delegate. Then
3017
+ * sign THAT with `eth_signTypedData_v4` and ignore the other two: it is
3018
+ * the only form the rater can read, and it has no envelope ambiguity.
3019
+ * 2. If `delegated` is `false`, also produce an EIP-7702 authorization over
3020
+ * `(chainId, delegate, accountNonce)`.
3021
+ * 3. Hand both to {@link submitRelayedFeedback} with the SAME feedback
3022
+ * parameters, `deadline` and `nonce`.
3023
+ *
3024
+ * @param request - Network, rater address and feedback parameters
3025
+ * @returns Everything needed to sign, including whether an EIP-7702
3026
+ * authorization is still required
3027
+ *
3028
+ * @example
3029
+ * ```ts
3030
+ * const prep = await erc8004.prepareRelayedFeedback({
3031
+ * x402Version: 1,
3032
+ * network: 'base',
3033
+ * feedback: { agentId: 18896, value: 95, tag1: 'quality', rater: raterAddress },
3034
+ * });
3035
+ * // prep.delegated === false -> an EIP-7702 authorization is required
3036
+ * ```
3037
+ */
3038
+ prepareRelayedFeedback(request: PrepareRelayFeedbackRequest): Promise<PrepareRelayFeedbackResponse>;
3039
+ /**
3040
+ * Relay a rater-authored rating; the facilitator pays the gas.
3041
+ *
3042
+ * Step 2 of the rater-authored rail. The on-chain record that comes out of it
3043
+ * has the RATER as `msg.sender`, so `getClients(agentId)` shows the rater
3044
+ * rather than the facilitator.
3045
+ *
3046
+ * Pass back the same feedback parameters, `deadline` and `nonce` that
3047
+ * {@link prepareRelayedFeedback} returned. They are not redundant: the
3048
+ * facilitator rebuilds the registry calldata from them and requires the
3049
+ * rater's signature to cover exactly that.
3050
+ *
3051
+ * `authorization` is required only when `prepare` answered
3052
+ * `delegated: false`. One that names a different delegate than the one
3053
+ * `prepare` offered is refused before any gas is spent.
3054
+ *
3055
+ * @param request - Feedback parameters plus the rater's signature
3056
+ * @returns Feedback response with the transaction hash
3057
+ */
3058
+ submitRelayedFeedback(request: SubmitRelayFeedbackRequest): Promise<FeedbackResponse>;
3059
+ /**
3060
+ * Revoke previously submitted feedback
3061
+ *
3062
+ * Only the original submitter can revoke their feedback.
3063
+ *
3064
+ * @param network - Network where feedback was submitted
3065
+ * @param agentId - Agent ID
3066
+ * @param feedbackIndex - Index of feedback to revoke
3067
+ * @returns Revocation result
3068
+ */
3069
+ revokeFeedback(network: Erc8004Network, agentId: AgentId, feedbackIndex: number, options?: {
3070
+ sealHash?: string;
3071
+ originalFeedback?: Omit<FeedbackParams, 'agentId' | 'proof'>;
3072
+ }): Promise<FeedbackResponse>;
3073
+ /**
3074
+ * Get ERC-8004 contract addresses for a network
3075
+ *
3076
+ * @param network - Network to get contracts for
3077
+ * @returns Contract addresses or undefined if not deployed
3078
+ */
3079
+ getContracts(network: Erc8004Network): typeof ERC8004_CONTRACTS[Erc8004Network] | undefined;
3080
+ /**
3081
+ * Check if ERC-8004 is available on a network
3082
+ *
3083
+ * @param network - Network to check
3084
+ * @returns True if ERC-8004 contracts are deployed
3085
+ */
3086
+ isAvailable(network: string): network is Erc8004Network;
3087
+ /**
3088
+ * Get feedback endpoint metadata
3089
+ *
3090
+ * @returns Endpoint information for /feedback
3091
+ */
3092
+ getFeedbackMetadata(): Promise<{
3093
+ endpoint: string;
3094
+ supportedNetworks: Erc8004Network[];
3095
+ version: string;
3096
+ }>;
3097
+ /**
3098
+ * Ask what the RESPONDER must sign to author a response on-chain.
3099
+ *
3100
+ * The mirror of {@link prepareRelayedFeedback}, for the other write the
3101
+ * registry accepts from anybody. `appendResponse` is not agent-only — the
3102
+ * registry takes it from any address — so on the plain {@link appendResponse}
3103
+ * route the `responder` recorded on-chain is the FACILITATOR. That does not
3104
+ * destroy anyone's reputation the way a revoke would; it ties the
3105
+ * facilitator's on-chain identity to a third party's content, which is its own
3106
+ * kind of wrong.
3107
+ *
3108
+ * **v4 delegates only.** The v3 delegate accepts exactly two selectors and
3109
+ * `appendResponse` is not one of them, so a v3 network answers 400
3110
+ * `relay_response_needs_v4` rather than silently falling back to the route
3111
+ * this replaces.
3112
+ *
3113
+ * `clientAddress` and `feedbackIndex` are inside the signed struct: without
3114
+ * them one signature would answer any client's rating, or any rating at that
3115
+ * index.
3116
+ */
3117
+ prepareRelayedResponse(request: PrepareRelayResponseRequest): Promise<PrepareRelayFeedbackResponse>;
3118
+ /**
3119
+ * Relay a responder-authored response; the facilitator pays the gas.
3120
+ *
3121
+ * Pass back the same parameters, `deadline` and `nonce` that
3122
+ * {@link prepareRelayedResponse} returned: the facilitator rebuilds the struct
3123
+ * from them and refuses to relay anything the signature does not cover.
3124
+ */
3125
+ submitRelayedResponse(request: SubmitRelayResponseRequest): Promise<FeedbackResponse>;
3126
+ /** Shared POST for the relay routes: a refusal is data, never a throw. */
3127
+ private postRelay;
3128
+ /**
3129
+ * Append a response to existing feedback
3130
+ *
3131
+ * @deprecated On this route the facilitator is the AUTHOR: the registry
3132
+ * records `msg.sender` as the `responder`, and that is the facilitator's
3133
+ * wallet. Where the delegate is **v4**, use {@link prepareRelayedResponse} +
3134
+ * {@link submitRelayedResponse} instead. This route still works and is the
3135
+ * only one available where the delegate is still v3.
3136
+ *
3137
+ * **This is NOT agent-only**, despite what this comment claimed until
3138
+ * 2026-08-25. Verified on-chain on 2026-08-18: the registry accepts
3139
+ * `appendResponse` from ANY address. There is no identity-owner check, here or
3140
+ * in the contract.
3141
+ *
3142
+ * @param network - Network where feedback was submitted
3143
+ * @param agentId - Agent ID
3144
+ * @param feedbackIndex - Index of feedback to respond to
3145
+ * @param response - Response content
3146
+ * @param responseUri - Optional URI to off-chain response file
3147
+ * @returns Response result
3148
+ *
3149
+ * @example
3150
+ * ```ts
3151
+ * // Agent responds to feedback
3152
+ * const result = await erc8004.appendResponse(
3153
+ * 'ethereum',
3154
+ * 42,
3155
+ * 1,
3156
+ * 'Thank you for your feedback! We have addressed the issue.',
3157
+ * );
3158
+ * ```
3159
+ */
3160
+ appendResponse(network: Erc8004Network, agentId: AgentId, feedbackIndex: number, response: string, options?: {
3161
+ responseUri?: string;
3162
+ sealHash?: string;
3163
+ }): Promise<FeedbackResponse>;
3164
+ /**
3165
+ * Register an agent on the Identity Registry (idempotent)
3166
+ *
3167
+ * If the recipient already owns an agent on the target network, returns the
3168
+ * existing one instead of minting a duplicate. The facilitator pays gas fees.
3169
+ * Optionally transfer the NFT to a recipient address (gasless delegation).
3170
+ *
3171
+ * @param request - Registration request
3172
+ * @returns Registration response with agent ID and transaction hash
3173
+ *
3174
+ * @example
3175
+ * ```ts
3176
+ * // Register agent owned by facilitator
3177
+ * const result = await client.registerAgent({
3178
+ * x402Version: 1,
3179
+ * network: 'ethereum',
3180
+ * agentUri: 'ipfs://QmYourAgentFile',
3181
+ * });
3182
+ * console.log(`Agent #${result.agentId} registered`);
3183
+ *
3184
+ * // Register agent and transfer to user
3185
+ * const result = await client.registerAgent({
3186
+ * x402Version: 1,
3187
+ * network: 'ethereum',
3188
+ * agentUri: 'ipfs://QmYourAgentFile',
3189
+ * recipient: '0xUserAddress...',
3190
+ * });
3191
+ * console.log(`Agent #${result.agentId} transferred to user`);
3192
+ * ```
3193
+ */
3194
+ registerAgent(request: RegisterAgentRequest, options?: {
3195
+ asyncTransport?: boolean;
3196
+ pollIntervalMs?: number;
3197
+ timeoutMs?: number;
3198
+ }): Promise<RegisterAgentResponse>;
3199
+ /**
3200
+ * Start a registration without waiting for the chain to confirm it.
3201
+ *
3202
+ * Registration waits on a mint receipt, which on a congested chain outlives
3203
+ * client and proxy timeouts. A timed-out synchronous call is genuinely
3204
+ * ambiguous — the mint may well have landed — and retrying it is how five
3205
+ * duplicate agents once got minted. This returns immediately with a job id
3206
+ * instead; poll {@link getRegisterStatus} or use {@link waitForRegistration}.
3207
+ *
3208
+ * On Solana, `recipient` is a base58 address: the facilitator mints,
3209
+ * initializes the ATOM stats and transfers, paying every fee.
3210
+ */
3211
+ registerAgentAsync(request: RegisterAgentRequest): Promise<RegisterJobResponse>;
3212
+ /**
3213
+ * Read the current state of an asynchronous registration.
3214
+ *
3215
+ * Throws {@link Erc8004LookupError} with `notFound` when the job is unknown or
3216
+ * has aged out — terminal jobs are kept for one hour.
3217
+ */
3218
+ getRegisterStatus(jobId: string): Promise<RegisterJobResponse>;
3219
+ /**
3220
+ * Poll an asynchronous registration until it finishes.
3221
+ *
3222
+ * Rejects on timeout rather than resolving with the last non-terminal status,
3223
+ * so "still pending" is never mistaken for "did not happen": the mint may
3224
+ * still land afterwards, and treating a timeout as failure is what leads to
3225
+ * registering the same agent twice. Keep the job id and poll again rather
3226
+ * than re-registering.
3227
+ */
3228
+ waitForRegistration(jobId: string, options?: {
3229
+ pollIntervalMs?: number;
3230
+ timeoutMs?: number;
3231
+ }): Promise<RegisterJobResponse>;
3232
+ /**
3233
+ * Get registration endpoint metadata
3234
+ *
3235
+ * @returns Endpoint information for POST /register
3236
+ */
3237
+ getRegisterInfo(): Promise<Record<string, unknown>>;
3238
+ /**
3239
+ * Get a specific metadata entry for an agent
3240
+ *
3241
+ * @param network - Network where agent is registered
3242
+ * @param agentId - Agent's tokenId
3243
+ * @param key - Metadata key to retrieve
3244
+ * @returns Metadata value (hex-encoded and UTF-8 decoded if possible)
3245
+ */
3246
+ getIdentityMetadata(network: Erc8004Network, agentId: AgentId, key: string): Promise<IdentityMetadataResponse>;
3247
+ /**
3248
+ * Get total number of registered agents on a network
3249
+ *
3250
+ * @param network - Network to query
3251
+ * @returns Total supply count
3252
+ */
3253
+ getIdentityTotalSupply(network: Erc8004Network): Promise<IdentityTotalSupplyResponse>;
3254
+ }
3255
+ /**
3256
+ * Build payment requirements with ERC-8004 extension
3257
+ *
3258
+ * Adds the 8004-reputation extension to include proof of payment
3259
+ * in settlement responses for reputation submission.
3260
+ *
3261
+ * @param options - Base payment requirements options
3262
+ * @returns Payment requirements with ERC-8004 extension
3263
+ *
3264
+ * @example
3265
+ * ```ts
3266
+ * const requirements = buildErc8004PaymentRequirements({
3267
+ * amount: '1.00',
3268
+ * recipient: '0x...',
3269
+ * resource: 'https://api.example.com/service',
3270
+ * chainName: 'ethereum',
3271
+ * });
3272
+ *
3273
+ * // Settlement will include proofOfPayment
3274
+ * const result = await facilitator.settle(payment, requirements);
3275
+ * console.log(result.proofOfPayment);
3276
+ * ```
3277
+ */
3278
+ declare function buildErc8004PaymentRequirements(options: PaymentRequirementsOptions): PaymentRequirements & {
3279
+ extra: {
3280
+ '8004-reputation': {
3281
+ includeProof: boolean;
3282
+ };
3283
+ };
3284
+ };
3285
+ /**
3286
+ * PAYMENT_INFO_TYPEHASH used for nonce computation.
3287
+ * Must match the on-chain AuthCaptureEscrow contract.
3288
+ */
3289
+ declare const PAYMENT_INFO_TYPEHASH = "0xae68ac7ce30c86ece8196b61a7c486d8f0061f575037fbd34e7fe4e2820c6591";
3290
+ declare const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
3291
+ /**
3292
+ * Contract deposit limit (enforced by PaymentOperator condition).
3293
+ * As of 2026-02-03, commerce-payments contracts enforce $100 max per deposit.
3294
+ */
3295
+ declare const DEPOSIT_LIMIT_USDC = "100000000";
3296
+ /**
3297
+ * Default facilitator request timeout per chain in milliseconds.
3298
+ * Ethereum L1 (~12s blocks) needs much longer than L2s (~2s blocks).
3299
+ * Timeout chain: Client > SDK > Facilitator. The facilitator uses 900s for Ethereum L1.
3300
+ *
3301
+ * These are correct, and they are also a PRODUCT constraint worth reading before
3302
+ * you offer a network in a checkout: Ethereum L1 is 960s against 90s for every
3303
+ * L2 here. A buyer waiting in a browser will not wait sixteen minutes, so a
3304
+ * human-facing flow should offer the L2s and leave L1 to agent-to-agent or
3305
+ * batch callers that can tolerate the wait. Do not shorten this to make a
3306
+ * checkout feel faster — the value tracks L1 block time and the facilitator's
3307
+ * own 900s TxWatcher, and cutting it just times out a payment that was going
3308
+ * to land.
3309
+ */
3310
+ declare const ESCROW_TIMEOUT_MS: Record<number, number>;
3311
+ /**
3312
+ * USDC EIP-712 domain name per chain.
3313
+ * Most chains use "USD Coin", but some (Celo, Monad, HyperEVM) use "USDC".
3314
+ * This must match the on-chain token's name() for EIP-712 signing to work.
3315
+ */
3316
+ declare const USDC_DOMAIN_NAME: Record<number, string>;
3317
+ /**
3318
+ * Multi-chain escrow contract addresses for the Advanced Escrow system.
3319
+ * Keyed by EVM chain ID. Source: x402r-sdk A1igator/multichain-config deployment.
3320
+ */
3321
+ /**
3322
+ * Per-chain escrow contract addresses.
3323
+ *
3324
+ * KNOWN GAP, measured 2026-09-05: the `operator` entries are
3325
+ * PaymentOperatorFactory addresses, not deployed PaymentOperator instances.
3326
+ * Verified on Base mainnet, Base Sepolia and Arbitrum — they answer
3327
+ * `ESCROW()` and `operators(bytes32)` and revert on `FEE_CALCULATOR()`,
3328
+ * `FEE_RECIPIENT()` and `release(...)` — and x402-rs
3329
+ * `docs/X402R_MULTICHAIN_DEPLOYMENT.md` labels the same addresses
3330
+ * "PaymentOperatorFactory". A factory has no `release`/`charge`, so the direct
3331
+ * on-chain paths below cannot execute against these addresses as written;
3332
+ * resolve the real operator from the marketplace's own escrow config
3333
+ * ({@link EscrowNetworkConfig}) and pass it via `options.contracts`.
3334
+ * See {@link OPERATOR_FEE_BPS} for the commands.
3335
+ */
3336
+ declare const ESCROW_CONTRACTS: Record<number, AdvancedEscrowContracts>;
3337
+ /**
3338
+ * Base Mainnet contract addresses for the Advanced Escrow system.
3339
+ * @deprecated Use ESCROW_CONTRACTS[8453] or getEscrowContractsByChainId(8453) instead.
3340
+ */
3341
+ declare const BASE_MAINNET_CONTRACTS: AdvancedEscrowContracts;
3342
+ /**
3343
+ * Get escrow contract addresses for a given chain ID.
3344
+ *
3345
+ * @param chainId - EVM chain ID (e.g., 8453 for Base, 1 for Ethereum)
3346
+ * @returns Contract addresses or undefined if chain is not supported
3347
+ */
3348
+ declare function getEscrowContractsByChainId(chainId: number): AdvancedEscrowContracts | undefined;
3349
+ /**
3350
+ * Get all chain IDs that have escrow contracts deployed.
3351
+ *
3352
+ * @returns Array of chain IDs with escrow support
3353
+ */
3354
+ declare function getEscrowSupportedChainIds(): number[];
3355
+ /**
3356
+ * Check if escrow contracts are deployed on a given chain.
3357
+ *
3358
+ * @param chainId - EVM chain ID
3359
+ * @returns True if escrow is supported on this chain
3360
+ */
3361
+ declare function isEscrowSupportedOnChain(chainId: number): boolean;
3362
+ /**
3363
+ * Task tiers determine timing parameters for escrow operations.
3364
+ */
3365
+ type AdvancedEscrowTaskTier = 'micro' | 'standard' | 'premium' | 'enterprise';
3366
+ /**
3367
+ * Timing configuration per task tier (in seconds).
3368
+ */
3369
+ declare const TIER_TIMINGS: Record<AdvancedEscrowTaskTier, {
3370
+ pre: number;
3371
+ auth: number;
3372
+ refund: number;
3373
+ }>;
3374
+ /**
3375
+ * PaymentInfo struct matching the on-chain PaymentOperator contract.
3376
+ */
3377
+ interface AdvancedPaymentInfo {
3378
+ operator: string;
3379
+ receiver: string;
3380
+ token: string;
3381
+ maxAmount: string;
3382
+ preApprovalExpiry: number;
3383
+ authorizationExpiry: number;
3384
+ refundExpiry: number;
3385
+ minFeeBps: number;
3386
+ maxFeeBps: number;
3387
+ feeReceiver: string;
3388
+ salt: string;
3389
+ }
3390
+ /**
3391
+ * Optional signed lifecycle order for `releaseViaFacilitator` /
3392
+ * `refundViaFacilitator`.
3393
+ *
3394
+ * Both actions move money that is ALREADY escrowed, so neither carries an
3395
+ * ERC-3009 signature and the entitlement question — who may ask for the move —
3396
+ * has no other answer. Supplying a `lifecycleSigner` answers it; omitting one
3397
+ * leaves the request byte-for-byte as it was, which is why every field here is
3398
+ * optional and the default is to send nothing.
3399
+ *
3400
+ * The accepted signers are the facilitator's, not this SDK's: `release` takes
3401
+ * the payer or the operator owner (`FEE_RECIPIENT()`); `refundInEscrow` takes
3402
+ * the receiver, the operator owner, or the payer once `authorizationExpiry`
3403
+ * has passed. Anything else is logged as `unauthorized_role`.
3404
+ */
3405
+ interface LifecycleAuthOptions {
3406
+ /**
3407
+ * Who signs the order. Any adapter with `getAddress()` + `signTypedData()`:
3408
+ * `EnvKeyAdapter` server-side, `OWSWalletAdapter`, or
3409
+ * `wagmiLifecycleSigner(walletClient)` when the PAYER signs in their own
3410
+ * browser and this backend only transports the block.
3411
+ */
3412
+ lifecycleSigner?: LifecycleSigner;
3413
+ /**
3414
+ * Unix seconds. Default: `now + 600`, which leaves 300 s of headroom under
3415
+ * the facilitator's 900 s ceiling. Only read alongside `lifecycleSigner` —
3416
+ * a `lifecycleAuth` already carries the deadline it was signed with.
3417
+ */
3418
+ lifecycleDeadline?: number;
3419
+ /**
3420
+ * An order that was ALREADY SIGNED somewhere else — the publisher's browser
3421
+ * — and that this backend only transports.
3422
+ *
3423
+ * The signing side builds the document with `buildLifecycleTypedData`, the
3424
+ * browser signs it, and `lifecycleAuthFromSignature` turns the signature
3425
+ * back into this block. Nothing is re-derived here: the `paymentInfo`, the
3426
+ * `payer` and the `amount` this call sends must be the ones the order
3427
+ * committed to, or the facilitator reads `bad_signature`.
3428
+ *
3429
+ * MUTUALLY EXCLUSIVE with `lifecycleSigner`. Passing both is not a
3430
+ * preference to resolve — it means the caller believes two different orders
3431
+ * are about to travel, and only one can. It throws.
3432
+ */
3433
+ lifecycleAuth?: LifecycleAuth;
3434
+ }
3435
+ /**
3436
+ * Result of an AUTHORIZE operation.
3437
+ */
3438
+ interface AdvancedAuthorizationResult {
3439
+ success: boolean;
3440
+ transactionHash?: string;
3441
+ paymentInfo?: AdvancedPaymentInfo;
3442
+ salt?: string;
3443
+ error?: string;
3444
+ }
3445
+ /**
3446
+ * Result of an on-chain transaction (release, refund, charge).
3447
+ */
3448
+ interface AdvancedTransactionResult extends FacilitatorFailureFields {
3449
+ success: boolean;
3450
+ transactionHash?: string;
3451
+ gasUsed?: number;
3452
+ /**
3453
+ * Why it failed.
3454
+ *
3455
+ * On the gasless (`*ViaFacilitator`) paths, check `retryable` first: a
3456
+ * facilitator that could not hand the write to its lease holder rejected
3457
+ * nothing on-chain, and the escrow is exactly as it was. Reporting that as a
3458
+ * failed refund is how funds get written off while they are still sitting in
3459
+ * escrow, recoverable.
3460
+ */
3461
+ error?: string;
3462
+ }
3463
+ /**
3464
+ * Response from the facilitator's /escrow/state endpoint.
3465
+ * Represents the on-chain state of an escrow for a given paymentInfo + payer.
3466
+ */
3467
+ interface EscrowStateResponse {
3468
+ /** Whether the payment has already been collected (released) */
3469
+ hasCollectedPayment: boolean;
3470
+ /** Amount that can still be captured/released (in atomic units) */
3471
+ capturableAmount: string;
3472
+ /** Amount that can still be refunded to the payer (in atomic units) */
3473
+ refundableAmount: string;
3474
+ /** Keccak256 hash of the paymentInfo struct */
3475
+ paymentInfoHash: string;
3476
+ /** Network in CAIP-2 format (e.g., "eip155:8453") */
3477
+ network: string;
3478
+ }
3479
+ /**
3480
+ * Contract addresses configuration for AdvancedEscrowClient.
3481
+ *
3482
+ * Maps to the on-chain x402r escrow contracts:
3483
+ * - operator: PaymentOperatorFactory
3484
+ * - escrow: AuthCaptureEscrow
3485
+ * - tokenCollector: TokenCollector
3486
+ * - protocolFeeConfig: ProtocolFeeConfig
3487
+ * - refundRequest: RefundRequest
3488
+ * - usdc: USDC token contract
3489
+ */
3490
+ interface AdvancedEscrowContracts {
3491
+ /** PaymentOperatorFactory contract address */
3492
+ operator: string;
3493
+ /** AuthCaptureEscrow contract address */
3494
+ escrow: string;
3495
+ /** TokenCollector contract address */
3496
+ tokenCollector: string;
3497
+ /** ProtocolFeeConfig contract address */
3498
+ protocolFeeConfig: string;
3499
+ /** RefundRequest contract address */
3500
+ refundRequest: string;
3501
+ /** USDC token contract address */
3502
+ usdc: string;
3503
+ }
3504
+ /**
3505
+ * Configuration options for AdvancedEscrowClient.
3506
+ */
3507
+ interface AdvancedEscrowClientOptions {
3508
+ /** Facilitator URL for AUTHORIZE operations */
3509
+ facilitatorUrl?: string;
3510
+ /** JSON-RPC URL for on-chain operations (required when using SigningWalletAdapter) */
3511
+ rpcUrl?: string;
3512
+ /**
3513
+ * Chain ID (default: 8453 for Base Mainnet).
3514
+ * Supported chains: 8453 (Base), 84532 (Base Sepolia), 1 (Ethereum),
3515
+ * 11155111 (Ethereum Sepolia), 137 (Polygon), 42161 (Arbitrum),
3516
+ * 10 (Optimism), 42220 (Celo), 143 (Monad), 43114 (Avalanche).
3517
+ */
3518
+ chainId?: number;
3519
+ /** Contract addresses (auto-resolved from chainId if not provided) */
3520
+ contracts?: AdvancedEscrowContracts;
3521
+ /** Gas limit for transactions (default: 300000) */
3522
+ gasLimit?: number;
3523
+ /**
3524
+ * Request timeout in milliseconds for facilitator HTTP calls (authorize, gasless release/refund).
3525
+ * Default is per-network: 960s for Ethereum L1, 90s for L2s.
3526
+ * Ethereum L1 confirmations can take several minutes under congestion.
3527
+ */
3528
+ timeout?: number;
3529
+ /**
3530
+ * SigningWalletAdapter for OWS wallet signing (v2.36.0+).
3531
+ *
3532
+ * When provided, the client uses the adapter for all signing operations
3533
+ * instead of requiring a raw ethers.Signer. The first constructor argument
3534
+ * is ignored when `wallet` is set.
3535
+ *
3536
+ * Requires `rpcUrl` to be set for on-chain transaction building.
3537
+ *
3538
+ * @example
3539
+ * ```typescript
3540
+ * import { OWSWalletAdapter, AdvancedEscrowClient } from 'uvd-x402-sdk/backend';
3541
+ *
3542
+ * const wallet = new OWSWalletAdapter(owsWallet);
3543
+ * const client = new AdvancedEscrowClient(null, {
3544
+ * wallet,
3545
+ * rpcUrl: 'https://mainnet.base.org',
3546
+ * chainId: 8453,
3547
+ * });
3548
+ * ```
3549
+ */
3550
+ wallet?: SigningWalletAdapter;
3551
+ /**
3552
+ * Extra attempts on the gasless facilitator paths when the facilitator proved
3553
+ * it executed nothing. Default 2; `0` disables.
3554
+ */
3555
+ retries?: number;
3556
+ }
3557
+ /**
3558
+ * Minimal PaymentOperator ABI for the 4 on-chain functions.
3559
+ * (AUTHORIZE goes through the facilitator, not directly on-chain)
3560
+ */
3561
+ declare const OPERATOR_ABI: string[];
3562
+ /**
3563
+ * CREATE3-deployed operators (SKALE, future chains) use updated ABI with extra `bytes data` param
3564
+ * on release() and refundInEscrow(). Pass empty bytes (0x) for the data parameter.
3565
+ */
3566
+ declare const OPERATOR_ABI_CREATE3: string[];
3567
+ /**
3568
+ * AdvancedEscrowClient provides the 5 Advanced Escrow flows via the
3569
+ * PaymentOperator contract on 9 supported EVM networks.
3570
+ *
3571
+ * Supported chains: Base (8453), Base Sepolia (84532), Ethereum (1),
3572
+ * Ethereum Sepolia (11155111), Polygon (137), Arbitrum (42161),
3573
+ * Optimism (10), Celo (42220), Monad (143), Avalanche (43114).
3574
+ *
3575
+ * Contract addresses are auto-resolved from the chain ID.
3576
+ * Pass custom contracts to override.
3577
+ *
3578
+ * Two signer modes (v2.36.0+):
3579
+ * - **Legacy**: Pass an ethers.Signer as the first argument.
3580
+ * - **OWS Wallet**: Pass a SigningWalletAdapter via `options.wallet`.
3581
+ * The adapter signs transactions offline (no raw private key needed).
3582
+ * Requires `rpcUrl` for transaction building and broadcast.
3583
+ *
3584
+ * @example Legacy mode (ethers.Signer)
3585
+ * ```typescript
3586
+ * import { ethers } from 'ethers';
3587
+ * import { AdvancedEscrowClient } from 'uvd-x402-sdk/backend';
3588
+ *
3589
+ * const provider = new ethers.JsonRpcProvider('https://mainnet.base.org');
3590
+ * const signer = new ethers.Wallet(process.env.KEY!, provider);
3591
+ * const client = new AdvancedEscrowClient(signer, { chainId: 8453 });
3592
+ * ```
3593
+ *
3594
+ * @example OWS Wallet mode (SigningWalletAdapter)
3595
+ * ```typescript
3596
+ * import { OWSWalletAdapter } from 'uvd-x402-sdk';
3597
+ * import { AdvancedEscrowClient } from 'uvd-x402-sdk/backend';
3598
+ *
3599
+ * const wallet = new OWSWalletAdapter(owsWallet);
3600
+ * const client = new AdvancedEscrowClient(null, {
3601
+ * wallet,
3602
+ * rpcUrl: 'https://mainnet.base.org',
3603
+ * chainId: 8453,
3604
+ * });
3605
+ * await client.init();
3606
+ *
3607
+ * const pi = client.buildPaymentInfo('0xWorker...', '5000000', 'standard');
3608
+ * const auth = await client.authorize(pi);
3609
+ * const release = await client.release(pi);
3610
+ * ```
3611
+ */
3612
+ declare class AdvancedEscrowClient {
3613
+ private facilitatorUrl;
3614
+ private chainId;
3615
+ private gasLimit;
3616
+ private readonly timeout;
3617
+ private readonly retries;
3618
+ private contracts;
3619
+ private signer;
3620
+ private walletAdapter;
3621
+ private rpcUrl;
3622
+ private payerAddress;
3623
+ /**
3624
+ * Create an AdvancedEscrowClient.
3625
+ *
3626
+ * Two modes of operation:
3627
+ *
3628
+ * 1. **Legacy (ethers.Signer)**: Pass an ethers Signer as the first argument.
3629
+ * ```ts
3630
+ * const client = new AdvancedEscrowClient(signer, { rpcUrl, chainId });
3631
+ * ```
3632
+ *
3633
+ * 2. **OWS Wallet (SigningWalletAdapter)**: Pass `wallet` in options. The
3634
+ * first argument is ignored (pass `null`). Requires `rpcUrl` for on-chain
3635
+ * transaction building and broadcast.
3636
+ * ```ts
3637
+ * const wallet = new OWSWalletAdapter(owsWallet);
3638
+ * const client = new AdvancedEscrowClient(null, { wallet, rpcUrl, chainId });
3639
+ * ```
3640
+ *
3641
+ * @param signer - ethers.Signer instance (ignored when options.wallet is set)
3642
+ * @param options - Configuration options
3643
+ */
3644
+ constructor(signer: any, options?: AdvancedEscrowClientOptions);
3645
+ /**
3646
+ * Initialize the client (resolves payer address).
3647
+ * Call this before using any methods.
3648
+ */
3649
+ init(): Promise<void>;
3650
+ /**
3651
+ * Build a PaymentInfo struct with appropriate timing for the task tier.
3652
+ *
3653
+ * @param receiver - Worker's wallet address
3654
+ * @param amount - Amount in token atomic units (e.g., '5000000' for $5 USDC)
3655
+ * @param tier - Task tier determines timing parameters
3656
+ * @param salt - Random salt (auto-generated if not provided)
3657
+ */
3658
+ buildPaymentInfo(receiver: string, amount: string, tier?: AdvancedEscrowTaskTier, salt?: string, opts?: {
3659
+ deadline?: number;
3660
+ reviewWindowSec?: number;
3661
+ /**
3662
+ * Fee bounds to sign, in basis points. Default
3663
+ * {@link DEFAULT_MIN_FEE_BPS} / {@link DEFAULT_MAX_FEE_BPS}. Override
3664
+ * only against an operator whose fee you have actually read on-chain —
3665
+ * `maxFeeBps` is a CEILING, so a value under the operator's own fee does
3666
+ * not shave the fee, it reverts the deposit.
3667
+ */
3668
+ minFeeBps?: number;
3669
+ maxFeeBps?: number;
3670
+ }): AdvancedPaymentInfo;
3671
+ /**
3672
+ * Compute the correct nonce (with PAYMENT_INFO_TYPEHASH).
3673
+ * Matches the on-chain AuthCaptureEscrow nonce derivation.
3674
+ */
3675
+ private computeNonce;
3676
+ /**
3677
+ * Sign ReceiveWithAuthorization for ERC-3009.
3678
+ *
3679
+ * Uses SigningWalletAdapter.signTypedData() when in OWS mode,
3680
+ * or ethers Signer.signTypedData() in legacy mode.
3681
+ */
3682
+ private signErc3009;
3683
+ /**
3684
+ * Build the on-chain PaymentInfo tuple for contract calls.
3685
+ */
3686
+ private buildTuple;
3687
+ /**
3688
+ * AUTHORIZE: Lock funds in escrow via the facilitator.
3689
+ *
3690
+ * Sends an ERC-3009 ReceiveWithAuthorization to the facilitator,
3691
+ * which calls PaymentOperator.authorize() on-chain.
3692
+ */
3693
+ authorize(paymentInfo: AdvancedPaymentInfo): Promise<AdvancedAuthorizationResult>;
3694
+ /**
3695
+ * RELEASE: Capture escrowed funds to receiver (worker gets paid).
3696
+ *
3697
+ * Calls PaymentOperator.release() -> escrow.capture()
3698
+ *
3699
+ * @param paymentInfo - PaymentInfo from the authorize step
3700
+ * @param amount - Amount to release (defaults to maxAmount)
3701
+ */
3702
+ release(paymentInfo: AdvancedPaymentInfo, amount?: string): Promise<AdvancedTransactionResult>;
3703
+ /**
3704
+ * REFUND IN ESCROW: Return escrowed funds to payer (cancel task).
3705
+ *
3706
+ * Calls PaymentOperator.refundInEscrow() -> escrow.partialVoid()
3707
+ *
3708
+ * @param paymentInfo - PaymentInfo from the authorize step
3709
+ * @param amount - Amount to refund (defaults to maxAmount)
3710
+ */
3711
+ refundInEscrow(paymentInfo: AdvancedPaymentInfo, amount?: string): Promise<AdvancedTransactionResult>;
3712
+ /**
3713
+ * GASLESS RELEASE: Release escrowed funds via the facilitator.
3714
+ *
3715
+ * Instead of calling the PaymentOperator contract directly (which requires
3716
+ * gas), this sends a release request to the facilitator, which submits
3717
+ * the transaction on your behalf.
3718
+ *
3719
+ * @param paymentInfo - PaymentInfo from the authorize step
3720
+ * @param amount - Amount to release in atomic units (defaults to maxAmount)
3721
+ * @returns Transaction result from the facilitator
3722
+ *
3723
+ * @example
3724
+ * ```typescript
3725
+ * const pi = client.buildPaymentInfo('0xWorker...', '5000000', 'standard');
3726
+ * await client.authorize(pi);
3727
+ * // Worker completes task...
3728
+ * const result = await client.releaseViaFacilitator(pi);
3729
+ * console.log(result.transactionHash);
3730
+ * ```
3731
+ *
3732
+ * @example Signed release (the payer proves the release is theirs to ask for)
3733
+ * ```typescript
3734
+ * await client.releaseViaFacilitator(pi, undefined, {
3735
+ * lifecycleSigner: payerWallet, // EnvKeyAdapter, OWS, wagmiLifecycleSigner(...)
3736
+ * });
3737
+ * ```
3738
+ *
3739
+ * @example Transporting an order the publisher signed in their browser
3740
+ * ```typescript
3741
+ * // The browser sent back a signature over a document THIS backend built.
3742
+ * const lifecycleAuth = lifecycleAuthFromSignature(typedData, signature, payer);
3743
+ * await client.releaseViaFacilitator(pi, amount, { lifecycleAuth });
3744
+ * ```
3745
+ */
3746
+ releaseViaFacilitator(paymentInfo: AdvancedPaymentInfo, amount?: string, options?: LifecycleAuthOptions): Promise<AdvancedTransactionResult>;
3747
+ /**
3748
+ * GASLESS REFUND: return escrowed funds to the payer via the facilitator.
3749
+ *
3750
+ * Sends `action: "refundInEscrow"` to `POST /settle`; the facilitator's
3751
+ * PaymentOperator calls `AuthCaptureEscrow.partialVoid`.
3752
+ *
3753
+ * # This is how an EXPIRED escrow is recovered
3754
+ *
3755
+ * A widely repeated claim — including in this SDK's own comments until now —
3756
+ * says that once `authorizationExpiry` passes, only the payer's `reclaim()`
3757
+ * can move the funds. **That is false, and believing it has left real money
3758
+ * stranded.**
3759
+ *
3760
+ * Read the contract (`AuthCaptureEscrow.sol`):
3761
+ *
3762
+ * - `partialVoid` is `onlySender(paymentInfo.operator)` — the operator is the
3763
+ * FACILITATOR, not the payer — it sends the tokens **to the payer**, and it
3764
+ * **does not check `authorizationExpiry` at all**. It works before expiry
3765
+ * and after it, and the payer never has to appear.
3766
+ * - `reclaim` is `onlySender(paymentInfo.payer)` and only after expiry. It is
3767
+ * a payer's self-service escape hatch, which is why this facilitator does
3768
+ * not expose it — **not** the only way out.
3769
+ *
3770
+ * So a release that reverted with `AfterAuthorizationExpiry` is recoverable
3771
+ * from here, with no gas and no cooperation from the payer. Get the amount
3772
+ * from {@link queryEscrowState}'s `capturableAmount`.
3773
+ *
3774
+ * # A refusal that is not a refusal
3775
+ *
3776
+ * Check `retryable` before writing an escrow off. A `503` means the
3777
+ * facilitator never reached the chain — the escrow is untouched and the same
3778
+ * request should be sent again.
3779
+ *
3780
+ * @param paymentInfo - PaymentInfo from the authorize step
3781
+ * @param amount - Amount to refund in atomic units (defaults to maxAmount).
3782
+ * For a stuck escrow pass `capturableAmount` from {@link queryEscrowState}.
3783
+ * @returns Transaction result from the facilitator
3784
+ *
3785
+ * @example Recovering an escrow whose release window already closed
3786
+ * ```typescript
3787
+ * const state = await client.queryEscrowState(pi);
3788
+ * if (state.capturableAmount !== '0') {
3789
+ * // No payer needed, no gas, and expiry is irrelevant to partialVoid.
3790
+ * const result = await client.refundViaFacilitator(pi, state.capturableAmount);
3791
+ * if (!result.success && result.retryable) {
3792
+ * // No verdict was reached. The funds are still there; send it again.
3793
+ * }
3794
+ * }
3795
+ * ```
3796
+ */
3797
+ refundViaFacilitator(paymentInfo: AdvancedPaymentInfo, amount?: string, options?: LifecycleAuthOptions): Promise<AdvancedTransactionResult>;
3798
+ /**
3799
+ * QUERY ESCROW STATE: Read on-chain escrow state via the facilitator.
3800
+ *
3801
+ * This is a read-only operation that queries the facilitator for the
3802
+ * current escrow state without requiring gas or a signer.
3803
+ *
3804
+ * @param paymentInfo - PaymentInfo to query state for
3805
+ * @returns Escrow state including capturable/refundable amounts
3806
+ *
3807
+ * @example
3808
+ * ```typescript
3809
+ * const pi = client.buildPaymentInfo('0xWorker...', '5000000', 'standard');
3810
+ * await client.authorize(pi);
3811
+ *
3812
+ * const state = await client.queryEscrowState(pi);
3813
+ * console.log(`Capturable: ${state.capturableAmount}`);
3814
+ * console.log(`Refundable: ${state.refundableAmount}`);
3815
+ * console.log(`Already collected: ${state.hasCollectedPayment}`);
3816
+ * ```
3817
+ */
3818
+ queryEscrowState(paymentInfo: AdvancedPaymentInfo): Promise<EscrowStateResponse>;
3819
+ /**
3820
+ * CHARGE: Direct instant payment (no escrow hold).
3821
+ *
3822
+ * Calls PaymentOperator.charge() -> escrow.charge()
3823
+ * Funds go directly from payer to receiver.
3824
+ *
3825
+ * @param paymentInfo - PaymentInfo with receiver and amount
3826
+ * @param amount - Amount to charge (defaults to maxAmount)
3827
+ */
3828
+ charge(paymentInfo: AdvancedPaymentInfo, amount?: string): Promise<AdvancedTransactionResult>;
3829
+ /**
3830
+ * REFUND POST ESCROW: Dispute refund after funds were released.
3831
+ *
3832
+ * Calls PaymentOperator.refundPostEscrow() -> escrow.refund()
3833
+ *
3834
+ * WARNING: NOT FUNCTIONAL IN PRODUCTION (as of 2026-02-03).
3835
+ * The protocol team has not implemented the required tokenCollector
3836
+ * contract. This call will fail on-chain.
3837
+ *
3838
+ * For dispute resolution, use refundInEscrow() instead: keep funds
3839
+ * in escrow and refund before releasing. This guarantees funds are
3840
+ * available and under arbiter control.
3841
+ *
3842
+ * Kept for future use when tokenCollector is implemented.
3843
+ *
3844
+ * @param paymentInfo - PaymentInfo from the original authorization
3845
+ * @param amount - Amount to refund (defaults to maxAmount)
3846
+ * @param tokenCollector - Address of token collector for refund sourcing
3847
+ * @param collectorData - Data for the token collector
3848
+ */
3849
+ refundPostEscrow(paymentInfo: AdvancedPaymentInfo, amount?: string, tokenCollector?: string, collectorData?: string): Promise<AdvancedTransactionResult>;
3850
+ /**
3851
+ * Build an unsigned transaction, sign via SigningWalletAdapter, and broadcast.
3852
+ *
3853
+ * Used by release(), refundInEscrow(), charge(), refundPostEscrow() when
3854
+ * operating in OWS wallet adapter mode. The adapter signs the serialized
3855
+ * transaction offline; the RPC provider broadcasts the signed raw TX.
3856
+ *
3857
+ * @param ethersModule - ethers namespace (from `const { ethers } = await import('ethers')`)
3858
+ * @param abi - Contract ABI (OPERATOR_ABI or OPERATOR_ABI_CREATE3)
3859
+ * @param encodeCalldata - Function that encodes the calldata using the interface
3860
+ * @returns Transaction result
3861
+ */
3862
+ private sendViaAdapter;
3863
+ }
3864
+
3865
+ export { getCorsHeaders as $, AMBIGUOUS_LEASE_REASONS as A, type BuildLifecycleAuthParams as B, buildLifecycleAuth as C, DEFAULT_FACILITATOR_RETRIES as D, buildLifecycleTypedData as E, FacilitatorClient as F, buildPaymentRequirements as G, type HonoMiddlewareOptions as H, buildSettleRequest as I, buildSettleRequestForVersion as J, buildSettleRequestV2 as K, LIFECYCLE_ACTIONS as L, MAX_RETRY_AFTER_SECONDS as M, buildVerifyRequest as N, buildVerifyRequestForVersion as O, type ParsedFacilitatorErrorBody as P, buildVerifyRequestV2 as Q, REPLAYABLE_LEASE_REASONS as R, SETTLEMENT_UNCONFIRMED as S, create402Response as T, createHonoMiddleware as U, type VerifiedPaymentState as V, WRITER_LEASE_REASONS as W, X402_CORS_HEADERS as X, createPaymentMiddleware as Y, extractPaymentFromHeaders as Z, facilitatorFetch as _, DEFAULT_RETRY_AFTER_SECONDS as a, type FeedbackResponse as a$, isAmbiguousLeaseReason as a0, isReplayableLeaseReason as a1, isSettlementUnconfirmed as a2, lifecycleAuthFromSignature as a3, parseFacilitatorErrorBody as a4, parseRetryAfterSeconds as a5, readFacilitatorError as a6, resolveEnvelopeVersion as a7, toPaymentRequirementsV2 as a8, toResourceInfoV2 as a9, type DiscoveryHealthStatus as aA, type DiscoveryListOptions as aB, type DiscoveryPagination as aC, type DiscoveryRegisterOptions as aD, type DiscoveryResource as aE, type DiscoveryResponse as aF, type DiscoverySource as aG, type DiscoveryStats as aH, type DiscoveryTier as aI, type Dispute as aJ, type DisputeOutcome as aK, ERC8004_CONTRACTS as aL, ERC8004_EXTENSION_ID as aM, ESCROW_CONTRACTS as aN, ESCROW_TIMEOUT_MS as aO, Erc8004Client as aP, type Erc8004ClientOptions as aQ, Erc8004LookupError as aR, type Erc8004Network as aS, EscrowClient as aT, type EscrowClientOptions as aU, type EscrowPayment as aV, type EscrowStateResponse as aW, type EscrowStatus as aX, type FeedbackEntry as aY, type FeedbackParams as aZ, type FeedbackRequest as a_, wagmiLifecycleSigner as aa, type AdvancedAuthorizationResult as ab, AdvancedEscrowClient as ac, type AdvancedEscrowClientOptions as ad, type AdvancedEscrowContracts as ae, type AdvancedEscrowTaskTier as af, type AdvancedPaymentInfo as ag, type AdvancedTransactionResult as ah, type AgentId as ai, type AgentIdentity as aj, type AgentRegistration as ak, type AgentRegistrationFile as al, type AgentService as am, type AtomStats as an, BASE_MAINNET_CONTRACTS as ao, BazaarClient as ap, type BazaarClientOptions as aq, type BazaarDiscoverOptions as ar, type BazaarDiscoverResponse as as, type BazaarRegisterOptions as at, type BazaarResource as au, type CreateEscrowOptions as av, DEPOSIT_LIMIT_USDC as aw, type DiscoveryAccepts as ax, type DiscoveryCuration as ay, type DiscoveryHealth as az, type FacilitatorClientOptions as b, HEALTH_FILTERS as b0, type IdentityByOwnerResponse as b1, type IdentityMetadataResponse as b2, type IdentityTotalSupplyResponse as b3, type LifecycleAuthOptions as b4, MAX_SEARCH_LEN as b5, type MetadataEntryParam as b6, OPERATOR_ABI as b7, OPERATOR_ABI_CREATE3 as b8, PAYMENT_INFO_TYPEHASH as b9, USDC_DOMAIN_NAME as bA, type UnavailableBody as bB, type UnavailableResponse as bC, type VerifyRequest as bD, type VerifyResponse as bE, ZERO_ADDRESS as bF, buildErc8004PaymentRequirements as bG, buildUnavailableResponse as bH, canRefundEscrow as bI, canReleaseEscrow as bJ, carryFailureFields as bK, epochToDate as bL, escrowTimeRemaining as bM, getEscrowContractsByChainId as bN, getEscrowSupportedChainIds as bO, isAlive as bP, isEscrowExpired as bQ, isEscrowSupportedOnChain as bR, isRegisterJobTerminal as bS, parsePaymentHeader as bT, supportsRelayedFeedback as bU, wireNetwork as bV, type PaymentRequirementResolver as ba, type PaymentRequirements as bb, type PaymentRequirementsOptions as bc, type PrepareRelayFeedbackRequest as bd, type PrepareRelayFeedbackResponse as be, type PrepareRelayResponseRequest as bf, type ProofOfPayment as bg, RELAYED_FEEDBACK_NETWORKS as bh, type RefundRequest as bi, type RefundStatus as bj, type RegisterAgentRequest as bk, type RegisterAgentResponse as bl, type RegisterJobResponse as bm, type RegisterJobStatus as bn, RegistrationPendingError as bo, type RelayAuthorizationParams as bp, type ReputationResponse as bq, type ReputationSummary as br, type RequestRefundOptions as bs, type SettleRequest as bt, type SettleResponse as bu, type SettleResponseWithProof as bv, type SubmitRelayFeedbackRequest as bw, type SubmitRelayResponseRequest as bx, TIER_FILTERS as by, TIER_TIMINGS as bz, type FacilitatorErrorInfo as c, type FacilitatorFailureFields as d, type FacilitatorFetchOptions as e, LIFECYCLE_DEFAULT_DEADLINE_SECS as f, LIFECYCLE_DOMAIN_NAME as g, LIFECYCLE_DOMAIN_VERSION as h, LIFECYCLE_MAX_DEADLINE_SECS as i, LIFECYCLE_ORDER_TYPES as j, type LifecycleAction as k, type LifecycleAuth as l, type LifecyclePaymentInfo as m, type LifecycleSigner as n, type LifecycleTypedData as o, type LifecycleTypedDataParams as p, type PaymentAcceptance as q, type PaymentMiddlewareOptions as r, type PaymentPayloadV2 as s, type PaymentRequirementsV2 as t, type ResourceInfoV2 as u, type SettleRequestV2 as v, type VerifyRequestV2 as w, type WagmiLifecycleWalletClient as x, type WriterLeaseReason as y, X402_HEADER_NAMES as z };