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