@lunora/x402 1.0.0-alpha.4 → 1.0.0-alpha.41

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.
Files changed (37) hide show
  1. package/LICENSE.md +18 -0
  2. package/README.md +18 -0
  3. package/dist/charge/index.d.mts +155 -95
  4. package/dist/charge/index.d.ts +155 -95
  5. package/dist/charge/index.mjs +1 -7
  6. package/dist/index.d.mts +2 -3
  7. package/dist/index.d.ts +2 -3
  8. package/dist/index.mjs +1 -2
  9. package/dist/packem_shared/DEFAULT_ALLOWED_ASSETS-hq1xeZL6.mjs +1 -0
  10. package/dist/packem_shared/DEFAULT_FACILITATOR_URL-CPJNUMvi.mjs +1 -0
  11. package/dist/packem_shared/EVM_NETWORKS-C5yyzK27.mjs +1 -0
  12. package/dist/packem_shared/createChargeMiddleware-CURPSr23.mjs +1 -0
  13. package/dist/packem_shared/createFacilitatorClient-piLSnKs5.mjs +1 -0
  14. package/dist/packem_shared/createPayFetch-lyg7i3Js.mjs +1 -0
  15. package/dist/packem_shared/createProcedureChargeGate-Ki_K8dQE.mjs +1 -0
  16. package/dist/packem_shared/index.d-CzQaqaP6.d.mts +533 -0
  17. package/dist/packem_shared/index.d-CzQaqaP6.d.ts +533 -0
  18. package/dist/packem_shared/optional-peer-BBhVxozi.mjs +1 -0
  19. package/dist/packem_shared/registerWallet-DvCt0Q7M.mjs +1 -0
  20. package/dist/packem_shared/toPaymentEventRow-DTXuTDh0.mjs +1 -0
  21. package/dist/packem_shared/withX402-CJcBVHdI.mjs +1 -0
  22. package/dist/pay/index.d.mts +61 -61
  23. package/dist/pay/index.d.ts +61 -61
  24. package/dist/pay/index.mjs +1 -21
  25. package/package.json +21 -9
  26. package/dist/packem_shared/DEFAULT_FACILITATOR_URL-Cbz6kIqa.mjs +0 -4
  27. package/dist/packem_shared/DEFAULT_STABLECOIN_DECIMALS-CpW619nu.mjs +0 -90
  28. package/dist/packem_shared/EVM_NETWORKS-BhnYWUQ4.mjs +0 -26
  29. package/dist/packem_shared/config.d-5Nqi5iox.d.mts +0 -404
  30. package/dist/packem_shared/config.d-5Nqi5iox.d.ts +0 -404
  31. package/dist/packem_shared/createChargeMiddleware-D3yhOpFs.mjs +0 -162
  32. package/dist/packem_shared/createFacilitatorClient-rXHBnCZm.mjs +0 -16
  33. package/dist/packem_shared/createPayFetch-BeT05njL.mjs +0 -17
  34. package/dist/packem_shared/createProcedureChargeGate-eh9yv36U.mjs +0 -21
  35. package/dist/packem_shared/registerWallet-I4pVwq65.mjs +0 -109
  36. package/dist/packem_shared/toPaymentEventRow-DW4O9N7Y.mjs +0 -22
  37. package/dist/packem_shared/withX402-DILL2DvD.mjs +0 -15
@@ -0,0 +1,533 @@
1
+ import { ProcessSettleSuccessResponse } from '@x402/core/http';
2
+ import { BeforePaymentCreationHook, PaymentPolicy, OnPaymentCreationFailureHook } from '@x402/core/client';
3
+ import { PaymentRequirements } from '@x402/core/types';
4
+ import { TransactionSigner } from '@solana/accounts';
5
+ /**
6
+ * ClientEvmSigner - Used by x402 clients to sign payment authorizations.
7
+ *
8
+ * Typically a viem LocalAccount:
9
+ * ```typescript
10
+ * const account = privateKeyToAccount('0x...');
11
+ * ```
12
+ *
13
+ * Or composed via `toClientEvmSigner(account, publicClient)`.
14
+ */
15
+ type ClientEvmSigner = {
16
+ readonly address: `0x${string}`;
17
+ signTypedData(message: {
18
+ domain: Record<string, unknown>;
19
+ types: Record<string, unknown>;
20
+ primaryType: string;
21
+ message: Record<string, unknown>;
22
+ }): Promise<`0x${string}`>;
23
+ /**
24
+ * Optional on-chain reads.
25
+ * Required only for extension enrichment (EIP-2612 / ERC-20 approval).
26
+ */
27
+ readContract?(args: {
28
+ address: `0x${string}`;
29
+ abi: readonly unknown[];
30
+ functionName: string;
31
+ args?: readonly unknown[];
32
+ }): Promise<unknown>;
33
+ /**
34
+ * Optional: Signs a raw EIP-1559 transaction without broadcasting.
35
+ * Required for ERC-20 approval gas sponsoring when the token lacks EIP-2612.
36
+ */
37
+ signTransaction?(args: {
38
+ to: `0x${string}`;
39
+ data: `0x${string}`;
40
+ nonce: number;
41
+ gas: bigint;
42
+ maxFeePerGas: bigint;
43
+ maxPriorityFeePerGas: bigint;
44
+ chainId: number;
45
+ }): Promise<`0x${string}`>;
46
+ /**
47
+ * Optional: Gets the current transaction count (nonce) for an address.
48
+ * Required for ERC-20 approval gas sponsoring.
49
+ */
50
+ getTransactionCount?(args: {
51
+ address: `0x${string}`;
52
+ }): Promise<number>;
53
+ /**
54
+ * Optional: Estimates current gas fees per gas.
55
+ * Required for ERC-20 approval gas sponsoring.
56
+ */
57
+ estimateFeesPerGas?(): Promise<{
58
+ maxFeePerGas: bigint;
59
+ maxPriorityFeePerGas: bigint;
60
+ }>;
61
+ };
62
+ /**
63
+ * Client-side signer for creating and signing Solana transactions
64
+ * This is a wrapper around TransactionSigner from @solana/kit
65
+ */
66
+ type ClientSvmSigner = TransactionSigner;
67
+ /**
68
+ * A normalised record of one settled x402 payment. The settled `amount` is kept
69
+ * as its exact on-chain atomic-unit string (USDC has 6 decimals) — never coerced
70
+ * to a fractional-dollar number — so no precision is lost crossing the reporting
71
+ * seam.
72
+ * @experimental
73
+ */
74
+ interface X402Receipt {
75
+ /** Settled amount in the asset's atomic base units (USDC: 6 decimals), as an exact string. */
76
+ readonly amount: string;
77
+ /** The settled asset's contract / mint address (e.g. Base USDC). */
78
+ readonly asset: string;
79
+ /** The payer's wallet address, when the facilitator reports it. */
80
+ readonly from: string | undefined;
81
+ /** The settlement network as a CAIP-2 id (e.g. `eip155:8453`). */
82
+ readonly network: string;
83
+ /** The gated resource this payment bought (a URL, or a procedure's `file:function` id). */
84
+ readonly resource: string;
85
+ /** The payout wallet the funds settled to (the merchant recipient). */
86
+ readonly to: string;
87
+ /** When the receipt was produced (epoch milliseconds). */
88
+ readonly ts: number;
89
+ /** On-chain settlement transaction id / hash. */
90
+ readonly tx: string;
91
+ }
92
+ /**
93
+ * A one-way, opt-in sink for settled-payment receipts. Wire it via
94
+ * `config.onReceipt`. It is best-effort telemetry — the middleware fires it after
95
+ * settlement, does not block the paid response on it, and swallows any error it
96
+ * throws — so a sink must never rely on being awaited or on its failures
97
+ * surfacing.
98
+ * @experimental
99
+ */
100
+ type X402ReceiptSink = (receipt: X402Receipt) => Promise<void> | void;
101
+ /**
102
+ * Normalise a successful facilitator settlement into an {@link X402Receipt}.
103
+ * `resource` (the gated URL or procedure id) and `ts` are supplied by the caller —
104
+ * the settlement result carries neither. Prefers the actual settled `amount`
105
+ * (present for `upto`-scheme partial settlements) and falls back to the route's
106
+ * required amount for `exact`.
107
+ * @experimental
108
+ */
109
+ declare const toReceipt: (settlement: ProcessSettleSuccessResponse, context: {
110
+ readonly resource: string;
111
+ readonly ts: number;
112
+ }) => X402Receipt;
113
+ /**
114
+ * A row for `@lunora/payment`'s durable `events` table. Deliberately a plain
115
+ * structural type — building one imports nothing from `@lunora/payment`, so the
116
+ * rails stay decoupled.
117
+ * @experimental
118
+ */
119
+ interface PaymentEventRow {
120
+ /** Epoch milliseconds the settlement was recorded. */
121
+ readonly processedAt: number;
122
+ /** The rail that produced the event. */
123
+ readonly provider: "x402";
124
+ /** The settlement tx hash — the natural unique event id (the table is unique on `(provider, providerEventId)`). */
125
+ readonly providerEventId: string;
126
+ /** The event kind, namespaced to the x402 rail. */
127
+ readonly type: "x402.settled";
128
+ }
129
+ /**
130
+ * Shape a receipt as a row for `@lunora/payment`'s durable `events` table, so a
131
+ * settled x402 payment shows in Studio's Payments panel (its recent-events card)
132
+ * with ZERO coupling: this returns a plain object matching that table's
133
+ * documented column contract (`provider` / `providerEventId` / `type` /
134
+ * `processedAt`, unique on `(provider, providerEventId)`) and imports nothing
135
+ * from `@lunora/payment`. Insert it from a mutation ctx:
136
+ *
137
+ * ```ts
138
+ * onReceipt: (receipt) => ctx.db.insert("events", toPaymentEventRow(receipt)),
139
+ * ```
140
+ *
141
+ * The source of truth for the column contract is `@lunora/payment`'s `events`
142
+ * table (`packages/payment/src/schema.ts`). Amount / from / to / resource are
143
+ * intentionally not on this row — that card renders none of them; read them off
144
+ * the {@link X402Receipt} (e.g. into your own revenue table) if you need them.
145
+ * @experimental
146
+ */
147
+ declare const toPaymentEventRow: (receipt: X402Receipt) => PaymentEventRow;
148
+ /**
149
+ * Network identity for `@lunora/x402`.
150
+ *
151
+ * `@x402/core` v2 speaks **CAIP-2** chain ids (`eip155:8453` for Base,
152
+ * `solana:5eyk…` for Solana mainnet) — `type Network = ` `${string}:${string}` ``.
153
+ * Lunora keeps ergonomic **friendly** names (`"base"`, `"base-sepolia"`) as the
154
+ * public surface and maps them to CAIP-2 here, at the single seam where we hand a
155
+ * network to the SDK. A raw CAIP-2 string is also accepted as a power-user escape
156
+ * hatch (e.g. a chain we don't yet have a friendly alias for).
157
+ *
158
+ * The friendly set is intentionally scoped to chains `@x402/evm` / `@x402/svm`
159
+ * can settle the ergonomic `price:"$0.01"` path on out of the box (i.e. chains in
160
+ * their `DEFAULT_STABLECOINS` registry). Notably that excludes Optimism and
161
+ * Avalanche today — advertising them would 500 at settlement — so they are not
162
+ * friendly aliases; a caller who needs them can still pass a raw CAIP-2 id with an
163
+ * explicit asset.
164
+ */
165
+ /**
166
+ * A CAIP-2 chain identifier, e.g. `"eip155:8453"` (Base) or `"solana:5eyk…"`.
167
+ * @experimental
168
+ */
169
+ type Caip2 = `${string}:${string}`;
170
+ /**
171
+ * Friendly network names Lunora maps to CAIP-2 for `@x402/core`.
172
+ * @experimental
173
+ */
174
+ type FriendlyNetwork = "arbitrum" | "arbitrum-sepolia" | "base" | "base-sepolia" | "ethereum" | "polygon" | "solana" | "solana-devnet";
175
+ /**
176
+ * A network Lunora can settle on: a {@link FriendlyNetwork} alias (mapped to
177
+ * CAIP-2 internally) or a raw {@link Caip2} id for chains without a friendly name.
178
+ * @experimental
179
+ */
180
+ type X402Network = Caip2 | FriendlyNetwork;
181
+ /**
182
+ * Friendly name → CAIP-2 id. Values verified against `@x402/evm` and `@x402/svm`
183
+ * `DEFAULT_STABLECOINS` at 2.17.0. `base` / `base-sepolia` are the primary
184
+ * prod / test pair.
185
+ * @experimental
186
+ */
187
+ declare const NETWORK_TO_CAIP2: {
188
+ readonly arbitrum: "eip155:42161";
189
+ readonly "arbitrum-sepolia": "eip155:421614";
190
+ readonly base: "eip155:8453";
191
+ readonly "base-sepolia": "eip155:84532";
192
+ readonly ethereum: "eip155:1";
193
+ readonly polygon: "eip155:137";
194
+ readonly solana: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
195
+ readonly "solana-devnet": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1";
196
+ };
197
+ /**
198
+ * EVM friendly networks (signed via `@x402/evm` + viem).
199
+ * @experimental
200
+ */
201
+ declare const EVM_NETWORKS: readonly ["arbitrum", "arbitrum-sepolia", "base", "base-sepolia", "ethereum", "polygon"];
202
+ /**
203
+ * Solana friendly networks (signed via `@x402/svm`).
204
+ * @experimental
205
+ */
206
+ declare const SVM_NETWORKS: readonly ["solana", "solana-devnet"];
207
+ /**
208
+ * Resolve a network to its CAIP-2 id. Friendly aliases are looked up; a value
209
+ * that already looks like CAIP-2 (`namespace:reference`) passes through.
210
+ * @experimental
211
+ */
212
+ declare const toCaip2: (network: X402Network) => Caip2;
213
+ /**
214
+ * True when `network` settles on an EVM chain (viem signer path).
215
+ * @experimental
216
+ */
217
+ declare const isEvmNetwork: (network: X402Network) => boolean;
218
+ /**
219
+ * True when `network` settles on Solana (`@x402/svm` signer path).
220
+ * @experimental
221
+ */
222
+ declare const isSvmNetwork: (network: X402Network) => boolean;
223
+ /**
224
+ * USDC uses 6 decimals, so a USD price converts to atomic base units at `10 ** 6`.
225
+ * This is only the *default* for the standalone {@link usdToAtomic} helper —
226
+ * a spend policy never assumes it, and scales each requirement by the decimals of
227
+ * the asset it actually names (see {@link SpendPolicy.allowedAssets}).
228
+ * @experimental
229
+ */
230
+ declare const DEFAULT_STABLECOIN_DECIMALS = 6;
231
+ /**
232
+ * A stablecoin an agent wallet may pay in, and the decimals its atomic amounts are
233
+ * denominated in.
234
+ *
235
+ * `decimals` is required and explicit: a 402 server names the token contract to
236
+ * transfer, and the same atomic amount means a wildly different sum in a 6-decimal
237
+ * versus an 18-decimal token. Assuming a decimal count is how a tiny USD cap comes
238
+ * to authorise a large transfer, so the caller states it per asset. Note
239
+ * `@x402/evm`'s own `DEFAULT_STABLECOINS` registry is *not* uniformly 6-decimal
240
+ * (MegaUSD and Mezo USD are 18), which is exactly why it can't be trusted as a gate.
241
+ * @experimental
242
+ */
243
+ interface AllowedAsset {
244
+ /** Token contract (EVM) or mint (SVM) address. Matched case-insensitively on EVM, exactly on SVM. */
245
+ readonly asset: string;
246
+ /** Atomic base-unit decimals for this asset. Used to scale the USD caps — must match the token on chain. */
247
+ readonly decimals: number;
248
+ /** The network this asset lives on. Friendly names are resolved to CAIP-2. */
249
+ readonly network: X402Network;
250
+ }
251
+ /**
252
+ * The assets a policy allows when it doesn't name its own — the canonical
253
+ * 6-decimal, dollar-pegged USDC on each network Lunora has a friendly alias for.
254
+ *
255
+ * Deliberately a small hand-mirrored table rather than `@x402/evm`'s
256
+ * `DEFAULT_STABLECOINS`: that registry mixes 6- and 18-decimal assets, and importing
257
+ * it would pull viem into every bundle that touches a policy (the scheme modules are
258
+ * dynamically imported for exactly that reason — see `wallet.ts`). Addresses verified
259
+ * against `@x402/evm` `DEFAULT_STABLECOINS` and `@x402/svm` `USDC_*_ADDRESS` at 2.19.0.
260
+ *
261
+ * `ethereum` (`eip155:1`) is absent — the SDK ships no default stablecoin for it, so
262
+ * paying there needs an explicit {@link SpendPolicy.allowedAssets} entry.
263
+ * @experimental
264
+ */
265
+ declare const DEFAULT_ALLOWED_ASSETS: ReadonlyArray<AllowedAsset>;
266
+ /**
267
+ * Spend limits and approval gates for an agent wallet. At least one bound must be
268
+ * set — see {@link assertBoundedPolicy} — or the pay rail refuses to build.
269
+ *
270
+ * Caps are denominated in USD (the stablecoin's dollar value); addresses and
271
+ * networks are matched against the requirement the server offers.
272
+ * @experimental
273
+ */
274
+ interface SpendPolicy {
275
+ /**
276
+ * Asset allowlist, each entry carrying its own `decimals` (defaults to
277
+ * {@link DEFAULT_ALLOWED_ASSETS} — canonical USDC per friendly network).
278
+ *
279
+ * This is a **security gate, not a convenience**: the server picks which token
280
+ * contract gets transferred, so a policy that only caps a USD amount caps nothing
281
+ * until the asset behind that amount is pinned. A requirement naming an asset
282
+ * outside this list is refused, and the USD caps are scaled by the matched entry's
283
+ * `decimals` — never by an assumed decimal count.
284
+ *
285
+ * Every entry must be dollar-pegged: the caps are USD, and the per-run ledger sums
286
+ * atomic units across payments, so a non-$1 asset silently mis-prices both.
287
+ */
288
+ readonly allowedAssets?: ReadonlyArray<AllowedAsset>;
289
+ /** Network allowlist. When set, only these networks may be paid on. */
290
+ readonly allowedNetworks?: ReadonlyArray<X402Network>;
291
+ /** Recipient allowlist. When set, only these `payTo` addresses may be paid. */
292
+ readonly allowedRecipients?: ReadonlyArray<string>;
293
+ /**
294
+ * @deprecated One policy-wide decimal count can't describe the assets a server may
295
+ * name, and guessing it is what let a small USD cap authorise a large transfer.
296
+ * Setting this now throws — put the asset in {@link SpendPolicy.allowedAssets} with
297
+ * its own `decimals` instead.
298
+ */
299
+ readonly decimals?: number;
300
+ /** Hard ceiling on a single payment, in USD. */
301
+ readonly maxPerCall?: X402Price;
302
+ /** Hard ceiling on cumulative spend across this wallet's lifetime, in USD. */
303
+ readonly maxPerRun?: X402Price;
304
+ /**
305
+ * Approval gate. Called with the selected requirement before signing; return
306
+ * `false` (or reject) to refuse the payment. Use for human-in-the-loop or any
307
+ * dynamic rule the static caps can't express.
308
+ */
309
+ readonly onPaymentRequired?: (requirement: PaymentRequirements) => Promise<boolean> | boolean;
310
+ }
311
+ /**
312
+ * A running spend ledger the per-run cap is measured (and reserved) against.
313
+ * @experimental
314
+ */
315
+ interface SpendState {
316
+ /** Reserve a payment (atomic base units) against the running total, before it is signed. */
317
+ readonly add: (amount: bigint) => void;
318
+ /** Release a previously reserved amount (atomic base units) — e.g. a declined or failed payment. Clamps at 0. */
319
+ readonly release: (amount: bigint) => void;
320
+ /** Cumulative spend so far, in atomic base units. */
321
+ readonly spentAtomic: bigint;
322
+ }
323
+ /**
324
+ * A fresh spend ledger. One per wallet instance; the guard reserves into it and
325
+ * releases from it.
326
+ * @experimental
327
+ */
328
+ declare const createSpendState: () => SpendState;
329
+ /**
330
+ * Convert a USD amount (`0.01`, `"0.01"`, or the `"$0.01"` shorthand) to atomic
331
+ * stablecoin base units, exactly — parsed digit-by-digit so no binary-float drift
332
+ * can round a cap the wrong way. Throws on a malformed amount (including
333
+ * exponential notation like `"1e-7"`, which a decimal string never needs).
334
+ * @experimental
335
+ */
336
+ declare const usdToAtomic: (usd: X402Price, decimals?: number) => bigint;
337
+ /**
338
+ * A `PaymentPolicy` that narrows the server's offered requirements to those a
339
+ * bounded wallet may pay: in an allowed asset, within the per-call cap *for that
340
+ * asset's decimals*, to an allowed recipient, on an allowed network. An empty result
341
+ * means the client cannot pay — fail-closed.
342
+ *
343
+ * The asset check comes first and is what makes the amount check mean anything. The
344
+ * server chooses the token contract the scheme will sign a transfer against, and
345
+ * `amount` is in that token's atomic base units — so comparing it against a USD cap
346
+ * converted at an assumed decimal count mis-prices any asset that doesn't match the
347
+ * assumption. Pinning the asset (and taking its decimals from the policy, not the
348
+ * requirement) closes that.
349
+ * @experimental
350
+ */
351
+ declare const buildSpendPolicy: (policy: SpendPolicy) => PaymentPolicy;
352
+ /**
353
+ * A `BeforePaymentCreationHook` enforcing the stateful bounds the stateless
354
+ * {@link buildSpendPolicy} filter can't: the cumulative per-run cap and the async
355
+ * confirmation gate. Aborts (no signature) when either would be violated.
356
+ *
357
+ * The per-run cap is *reserved* into `state` as soon as the check passes — before
358
+ * the `await policy.onPaymentRequired` below, and before `@x402/core` ever attempts
359
+ * to sign — not recorded afterwards. This closes a check-then-act race: without an
360
+ * atomic reserve, N concurrent payments could each read the same `spentAtomic`,
361
+ * all pass the cap check, and all record, overspending the cap by up to
362
+ * (N−1)×maxPerCall. A declined confirmation releases the reservation before this
363
+ * hook returns; {@link releaseSpendOnFailure} releases it if the signature itself
364
+ * later fails. The reservation is intentionally *not* released on success — a
365
+ * committed payment stays counted.
366
+ *
367
+ * The asset is re-checked here rather than trusted from {@link buildSpendPolicy}:
368
+ * the two are registered on separate client seams, and a guard that assumed the
369
+ * filter ran would be one wiring change away from signing an unpinned asset.
370
+ * @experimental
371
+ */
372
+ declare const buildPaymentGuard: (policy: SpendPolicy, state: SpendState) => BeforePaymentCreationHook;
373
+ /**
374
+ * An `OnPaymentCreationFailureHook` that releases a reservation
375
+ * {@link buildPaymentGuard} made when the scheme's signature creation itself
376
+ * throws (network error, wallet error, …) after the guard already approved and
377
+ * reserved the amount. Without this, a failed signature would permanently
378
+ * over-count against the per-run cap for the rest of the run — fail-closed, but
379
+ * needlessly so when the client (`wrapFetchWithPayment`) may retry.
380
+ * @experimental
381
+ */
382
+ declare const releaseSpendOnFailure: (state: SpendState) => OnPaymentCreationFailureHook;
383
+ /**
384
+ * Guard at wallet-build time: refuse a policy with no bound whatsoever. Signing
385
+ * money on an agent's behalf with unlimited spend authority is never the intent,
386
+ * so this fails loudly rather than defaulting to unbounded.
387
+ *
388
+ * `allowedNetworks` / `allowedRecipients` / `allowedAssets` narrow *where* a payment
389
+ * can go and *in what*, but none caps *how much* — a policy with only allowlists still
390
+ * authorises unlimited spend to any recipient it permits. Only `maxPerCall`,
391
+ * `maxPerRun`, or a dynamic `onPaymentRequired` gate actually bound spend, so only
392
+ * those count here.
393
+ * @experimental
394
+ */
395
+ declare const assertBoundedPolicy: (policy: SpendPolicy) => void;
396
+ /**
397
+ * The public, Coinbase-operated facilitator (verify + settle). It needs no API
398
+ * key. Override with a self-hosted or CDP facilitator via {@link FacilitatorConfig}.
399
+ * @experimental
400
+ */
401
+ declare const DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator";
402
+ /**
403
+ * How to reach a facilitator's `/verify` + `/settle` endpoints.
404
+ * @experimental
405
+ */
406
+ interface FacilitatorConfig {
407
+ /** Extra headers for a private facilitator (e.g. a CDP bearer token). */
408
+ readonly headers?: Record<string, string>;
409
+ /** Base URL. Defaults to {@link DEFAULT_FACILITATOR_URL}. */
410
+ readonly url?: string;
411
+ }
412
+ /**
413
+ * A resource's price, as a USD-denominated decimal string (`"0.01"`, or the
414
+ * `"$0.01"` shorthand) or a number of dollars (`0.01`). The scheme resolves it
415
+ * to the network's stablecoin base units (USDC has 6 decimals) at challenge
416
+ * time. (Kept `number | string` rather than a `` `$${string}` `` template
417
+ * member — the template is subsumed by `string`, so it only adds noise.)
418
+ * @experimental
419
+ */
420
+ type X402Price = number | string;
421
+ /**
422
+ * An EVM recipient address (the merchant wallet that receives settlement).
423
+ * @experimental
424
+ */
425
+ type EvmAddress = `0x${string}`;
426
+ /**
427
+ * Recipient wallet the facilitator settles payments to, per network family.
428
+ * @experimental
429
+ */
430
+ interface X402Recipient {
431
+ /** EVM payout address (required for EVM networks). */
432
+ readonly evm?: EvmAddress;
433
+ /** Solana payout address, base58 (required for SVM networks). */
434
+ readonly svm?: string;
435
+ }
436
+ /**
437
+ * Server-side (charge rail) config. The server needs only a **recipient
438
+ * address** — no private key — because the facilitator performs settlement.
439
+ * @experimental
440
+ */
441
+ interface X402ChargeConfig {
442
+ readonly facilitator?: FacilitatorConfig;
443
+ /** Network this resource settles on. */
444
+ readonly network: X402Network;
445
+ /**
446
+ * Opt-in, one-way telemetry sink fired once per settled payment. Best-effort:
447
+ * it runs after settlement, never blocks the paid response, and its errors are
448
+ * swallowed. Use it to mirror x402 revenue into a durable table / `@lunora/payment`'s
449
+ * `events` table (see `toPaymentEventRow`) so it surfaces in Studio.
450
+ */
451
+ readonly onReceipt?: X402ReceiptSink;
452
+ /** Default price for a gated resource; per-resource overrides win. */
453
+ readonly price: X402Price;
454
+ /** Payout wallet(s). */
455
+ readonly recipient: X402Recipient;
456
+ }
457
+ /**
458
+ * Client-side (pay rail) config. The signer holds spending authority, so the
459
+ * pay rail is ActionCtx-only and MUST be paired with a spend `policy` — the pay
460
+ * rail refuses to build if the policy is unbounded.
461
+ * @experimental
462
+ */
463
+ interface X402PayConfig {
464
+ /** Network to transact on. Determines the signer family (EVM vs SVM). */
465
+ readonly network: X402Network;
466
+ /** Mandatory spend limits + approval gates. An unbounded policy is refused. */
467
+ readonly policy: SpendPolicy;
468
+ /** How the agent wallet is custodied (raw key, a user-supplied signer, or CDP-managed). */
469
+ readonly signer: X402SignerConfig;
470
+ }
471
+ /**
472
+ * CDP-managed wallet custody via `@coinbase/cdp-sdk` (an optional peer). The SDK
473
+ * gets-or-creates a named server account and signs the x402 EIP-712 payment
474
+ * authorization with it — no private key ever leaves Coinbase. Needs three CDP
475
+ * credentials, read from `ctx.secrets` under names that default to the SDK's own
476
+ * env-var names; override them if your secrets are named differently. (Note
477
+ * `@coinbase/x402` is a facilitator-auth helper, not a signer provider — CDP
478
+ * custody is `@coinbase/cdp-sdk`.) EVM only today; for CDP on Solana, build a
479
+ * `@solana/kit` signer around your CDP account and pass it via the `"signer"`
480
+ * escape hatch.
481
+ * @experimental
482
+ */
483
+ interface X402CdpSignerConfig {
484
+ /** CDP account name to get-or-create and sign with. */
485
+ readonly account: string;
486
+ /** `ctx.secrets` name for the CDP API key id. Default `"CDP_API_KEY_ID"`. */
487
+ readonly apiKeyIdSecretName?: string;
488
+ /** `ctx.secrets` name for the CDP API key secret. Default `"CDP_API_KEY_SECRET"`. */
489
+ readonly apiKeySecretName?: string;
490
+ readonly type: "cdp";
491
+ /** `ctx.secrets` name for the CDP wallet secret. Default `"CDP_WALLET_SECRET"`. */
492
+ readonly walletSecretName?: string;
493
+ }
494
+ /**
495
+ * Wallet custody for the pay rail — three shapes.
496
+ *
497
+ * `"raw-key"` resolves a private key from `ctx.secrets` (viem for EVM, a
498
+ * `@solana/kit` keypair for Solana) — simplest, self-custodied.
499
+ *
500
+ * `"signer"` is the escape hatch: hand in a signer you already built — any
501
+ * `@x402/evm` `ClientEvmSigner` (a viem account from Turnkey, Privy, an AWS/GCP
502
+ * KMS `toAccount`, CDP's viem adapter, …) on an EVM network, or an `@x402/svm`
503
+ * `ClientSvmSigner` (a `@solana/kit` `TransactionSigner`) on Solana. Adapt any
504
+ * custody provider to the structural signer and pass it here; `@lunora/x402`
505
+ * takes no dependency on the provider's SDK.
506
+ *
507
+ * `"cdp"` is a Coinbase-managed wallet via `@coinbase/cdp-sdk`
508
+ * ({@link X402CdpSignerConfig}).
509
+ *
510
+ * Wired today: raw-key (EVM + SVM), the user-supplied signer (both families),
511
+ * and CDP-managed EVM custody. CDP on Solana is not yet wired — use the escape
512
+ * hatch.
513
+ * @experimental
514
+ */
515
+ type X402SignerConfig = X402CdpSignerConfig | {
516
+ /** Name of the `ctx.secrets` entry holding the private key. */
517
+ readonly secretName: string;
518
+ readonly type: "raw-key";
519
+ } | {
520
+ /**
521
+ * A pre-built signer you own: an EVM `ClientEvmSigner` (viem account) on
522
+ * an EVM network, or an SVM `ClientSvmSigner` (`@solana/kit`
523
+ * `TransactionSigner`) on Solana. Must match the config `network`'s family.
524
+ */
525
+ readonly signer: ClientEvmSigner | ClientSvmSigner;
526
+ readonly type: "signer";
527
+ };
528
+ /**
529
+ * Resolve a facilitator's base URL, applying the public default.
530
+ * @experimental
531
+ */
532
+ declare const resolveFacilitatorUrl: (facilitator?: FacilitatorConfig) => string;
533
+ export { AllowedAsset as A, FriendlyNetwork as B, type ClientEvmSigner as C, DEFAULT_ALLOWED_ASSETS as D, EvmAddress as E, type FacilitatorConfig as F, SVM_NETWORKS as G, NETWORK_TO_CAIP2 as N, PaymentEventRow as P, type SpendPolicy as S, X402PayConfig as X, ClientSvmSigner as a, Caip2 as b, type DEFAULT_FACILITATOR_URL as c, type DEFAULT_STABLECOIN_DECIMALS as d, SpendState as e, X402CdpSignerConfig as f, X402Network as g, X402Price as h, X402SignerConfig as i, assertBoundedPolicy as j, buildPaymentGuard as k, buildSpendPolicy as l, createSpendState as m, isEvmNetwork as n, isSvmNetwork as o, resolveFacilitatorUrl as p, X402ChargeConfig as q, releaseSpendOnFailure as r, X402Receipt as s, toCaip2 as t, usdToAtomic as u, X402ReceiptSink as v, X402Recipient as w, toPaymentEventRow as x, toReceipt as y, EVM_NETWORKS as z };
@@ -0,0 +1 @@
1
+ import{LunoraError as u}from"@lunora/errors";const a=new Set(["ERR_MODULE_NOT_FOUND","MODULE_NOT_FOUND"]),i=/cannot find (?:module|package)|module not found|failed to resolve (?:module|import)/i,s=(t,o=3)=>{if(typeof t!="object"||t===null||o<0)return!1;const{cause:e,code:r,message:n}=t;return typeof r=="string"&&a.has(r)||typeof n=="string"&&i.test(n)?!0:s(e,o-1)},f=async(t,o)=>{try{return await t()}catch(e){throw s(e)?new u("ENV_INVALID",o,{cause:e}):e}};export{f as i};
@@ -0,0 +1 @@
1
+ import{LunoraError as n}from"@lunora/errors";import{toCaip2 as p,isEvmNetwork as w}from"./EVM_NETWORKS-C5yyzK27.mjs";import{i as l}from"./optional-peer-BBhVxozi.mjs";const d=/^0x[0-9a-fA-F]{64}$/,y=async(t,e)=>{const a=await t(e);if(a===void 0||a.length===0)throw new n("ENV_INVALID",`x402 pay: secret "${e}" is not set — the agent wallet has no key to sign with.`);return a},m=(t,e)=>{const a=t.address.startsWith("0x");if(e&&!a)throw new n("ENV_INVALID",`x402 pay: the supplied signer address "${t.address}" is not an EVM (0x…) address, but the network is EVM.`);if(!e&&a)throw new n("ENV_INVALID",`x402 pay: the supplied signer address "${t.address}" is an EVM (0x…) address, but the network is Solana.`)},h=async(t,e)=>{const{CdpClient:a}=await l(()=>import("@coinbase/cdp-sdk"),'x402 pay: CDP-managed custody needs the optional @coinbase/cdp-sdk peer — install it, or use "raw-key"/"signer" custody instead.'),[c,s,r]=await Promise.all([y(e,t.apiKeyIdSecretName??"CDP_API_KEY_ID"),y(e,t.apiKeySecretName??"CDP_API_KEY_SECRET"),y(e,t.walletSecretName??"CDP_WALLET_SECRET")]);return new a({apiKeyId:c,apiKeySecret:s,walletSecret:r}).evm.getOrCreateAccount({name:t.account})},u=async t=>{const e=t.startsWith("0x")?t:`0x${t}`;if(!d.test(e))throw new n("ENV_INVALID","x402 pay: the EVM wallet key must be a 32-byte hex private key (64 hex chars, optional 0x prefix).");const{privateKeyToAccount:a}=await l(()=>import("viem/accounts"),'x402 pay: raw-key EVM custody needs the optional viem peer — install it, or use "cdp"/"signer" custody instead.');return a(e)},E=async t=>{const e=t.trim(),{createKeyPairSignerFromBytes:a,createKeyPairSignerFromPrivateKeyBytes:c,getBase58Encoder:s}=await l(()=>import("@solana/kit"),'x402 pay: raw-key Solana custody needs the optional @solana/kit peer — install it, or pass a pre-built signer via "signer" custody instead.');let r;if(e.startsWith("[")){let o;try{o=JSON.parse(e)}catch{throw new n("ENV_INVALID","x402 pay: the Solana wallet key looks like a JSON byte array but is not valid JSON.")}if(!Array.isArray(o)||o.some(i=>typeof i!="number"))throw new n("ENV_INVALID","x402 pay: the Solana wallet key JSON must be an array of byte values.");r=Uint8Array.from(o)}else try{r=Uint8Array.from(s().encode(e))}catch{throw new n("ENV_INVALID","x402 pay: the Solana wallet key must be a base58 secret key or a JSON byte array.")}if(r.length===64)return a(r);if(r.length===32)return c(r);throw new n("ENV_INVALID",`x402 pay: the Solana wallet key must decode to 32 or 64 bytes (got ${String(r.length)}). Provide a base58 secret key or a JSON byte array.`)},x=async(t,e,a)=>{const c=p(e.network),{signer:s}=e,r=w(e.network);let o;if(s.type==="signer")m(s.signer,r),o=s.signer;else if(s.type==="cdp"){if(!r)throw new n("NOT_IMPLEMENTED",`x402 pay: CDP-managed Solana custody (account "${s.account}") is not wired — a CDP Solana account is not a @solana/kit signer. Build a @solana/kit signer around it and pass it via the { type: "signer" } escape hatch, or use "raw-key".`);o=await h(s,a.getSecret)}else{const i=await y(a.getSecret,s.secretName);o=r?await u(i):await E(i)}if(r){const{registerExactEvmScheme:i}=await l(()=>import("@x402/evm/exact/client"),"x402 pay: EVM networks need the optional @x402/evm + viem peers — install them, or configure an SVM network.");i(t,{networks:[c],signer:o})}else{const{registerExactSvmScheme:i}=await l(()=>import("@x402/svm/exact/client"),"x402 pay: SVM networks need the optional @x402/svm + @solana/kit peers — install them, or configure an EVM network.");i(t,{networks:[c],signer:o})}};export{x as registerWallet,u as resolveEvmAccount,E as resolveSvmSigner};
@@ -0,0 +1 @@
1
+ const e=(r,o)=>({amount:r.amount??r.requirements.amount,asset:r.requirements.asset,from:r.payer,network:r.network,resource:o.resource,to:r.requirements.payTo,ts:o.ts,tx:r.transaction}),s=r=>({processedAt:r.ts,provider:"x402",providerEventId:r.tx,type:"x402.settled"});export{s as toPaymentEventRow,e as toReceipt};
@@ -0,0 +1 @@
1
+ import{createChargeMiddleware as n}from"./createChargeMiddleware-CURPSr23.mjs";const w=(t,a)=>{let e;return async(d,r)=>(e??=n(t).catch(i=>{throw e=void 0,i}),(await e).handle(r,()=>a(d,r)))};export{w as withX402};