@lunora/x402 1.0.0-alpha.11 → 1.0.0-alpha.13

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.
@@ -1,5 +1,5 @@
1
- import { F as FacilitatorConfig, c as X402ChargeConfig, f as X402Price } from "../packem_shared/config.d-B-NYmHgG.mjs";
2
- export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, type a as EvmAddress, type P as PaymentEventRow, type d as X402Network, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2, x as toPaymentEventRow, y as toReceipt } from "../packem_shared/config.d-B-NYmHgG.mjs";
1
+ import { F as FacilitatorConfig, c as X402ChargeConfig, f as X402Price } from "../packem_shared/config.d-Dki_2r9a.mjs";
2
+ export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, type a as EvmAddress, type P as PaymentEventRow, type d as X402Network, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2, y as toPaymentEventRow, z as toReceipt } from "../packem_shared/config.d-Dki_2r9a.mjs";
3
3
  import { HTTPFacilitatorClient } from '@x402/core/server';
4
4
  import { RouteConfig } from '@x402/core/http';
5
5
  import '@x402/evm';
@@ -1,5 +1,5 @@
1
- import { F as FacilitatorConfig, c as X402ChargeConfig, f as X402Price } from "../packem_shared/config.d-B-NYmHgG.js";
2
- export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, type a as EvmAddress, type P as PaymentEventRow, type d as X402Network, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2, x as toPaymentEventRow, y as toReceipt } from "../packem_shared/config.d-B-NYmHgG.js";
1
+ import { F as FacilitatorConfig, c as X402ChargeConfig, f as X402Price } from "../packem_shared/config.d-Dki_2r9a.js";
2
+ export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, type a as EvmAddress, type P as PaymentEventRow, type d as X402Network, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2, y as toPaymentEventRow, z as toReceipt } from "../packem_shared/config.d-Dki_2r9a.js";
3
3
  import { HTTPFacilitatorClient } from '@x402/core/server';
4
4
  import { RouteConfig } from '@x402/core/http';
5
5
  import '@x402/evm';
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, E as EVM_NETWORKS, type a as EvmAddress, type F as FacilitatorConfig, type b as FriendlyNetwork, N as NETWORK_TO_CAIP2, S as SVM_NETWORKS, type X as X402CdpSignerConfig, type c as X402ChargeConfig, type d as X402Network, type e as X402PayConfig, type f as X402Price, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, type j as X402SignerConfig, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2 } from "./packem_shared/config.d-B-NYmHgG.mjs";
1
+ export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, E as EVM_NETWORKS, type a as EvmAddress, type F as FacilitatorConfig, type b as FriendlyNetwork, N as NETWORK_TO_CAIP2, S as SVM_NETWORKS, type X as X402CdpSignerConfig, type c as X402ChargeConfig, type d as X402Network, type e as X402PayConfig, type f as X402Price, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, type j as X402SignerConfig, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2 } from "./packem_shared/config.d-Dki_2r9a.mjs";
2
2
  import '@x402/evm';
3
3
  import '@x402/svm';
4
4
  import '@x402/core/http';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, E as EVM_NETWORKS, type a as EvmAddress, type F as FacilitatorConfig, type b as FriendlyNetwork, N as NETWORK_TO_CAIP2, S as SVM_NETWORKS, type X as X402CdpSignerConfig, type c as X402ChargeConfig, type d as X402Network, type e as X402PayConfig, type f as X402Price, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, type j as X402SignerConfig, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2 } from "./packem_shared/config.d-B-NYmHgG.js";
1
+ export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, E as EVM_NETWORKS, type a as EvmAddress, type F as FacilitatorConfig, type b as FriendlyNetwork, N as NETWORK_TO_CAIP2, S as SVM_NETWORKS, type X as X402CdpSignerConfig, type c as X402ChargeConfig, type d as X402Network, type e as X402PayConfig, type f as X402Price, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, type j as X402SignerConfig, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2 } from "./packem_shared/config.d-Dki_2r9a.js";
2
2
  import '@x402/evm';
3
3
  import '@x402/svm';
4
4
  import '@x402/core/http';
@@ -0,0 +1 @@
1
+ import{LunoraError as d}from"@lunora/errors";import{toCaip2 as w}from"./EVM_NETWORKS-XOms9Rtk.mjs";const E=/^\d+(?:\.\d+)?$/,u=(e,t)=>t.startsWith("eip155:")?e.toLowerCase():e,p=(e,t)=>`${t}|${u(e,t)}`,S=/^\d+$/,f=e=>S.test(e)?BigInt(e):void 0,A=6,v=[{asset:"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",decimals:6,network:"base"},{asset:"0x036CbD53842c5426634e7929541eC2318f3dCF7e",decimals:6,network:"base-sepolia"},{asset:"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",decimals:6,network:"polygon"},{asset:"0xaf88d065e77c8cC2239327C5EDb3A432268e5831",decimals:6,network:"arbitrum"},{asset:"0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",decimals:6,network:"arbitrum-sepolia"},{asset:"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",decimals:6,network:"solana"},{asset:"4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",decimals:6,network:"solana-devnet"}],R=()=>{let e=0n;return{add:t=>{e+=t},release:t=>{e=e>t?e-t:0n},get spentAtomic(){return e}}},x=(e,t=A)=>{if(!Number.isInteger(t)||t<0)throw new d("BAD_REQUEST",`x402 policy: decimals must be a non-negative integer, got ${String(t)}.`);const n=(typeof e=="number"?e.toString():e).trim(),s=n.startsWith("$")?n.slice(1):n;if(!E.test(s))throw new d("BAD_REQUEST",`x402 policy: "${String(e)}" is not a valid USD amount (use a plain decimal like "0.01").`);const[o="0",r=""]=s.split("."),a=r.slice(0,t).padEnd(t,"0");return BigInt(o)*10n**BigInt(t)+BigInt(a===""?"0":a)},g=e=>{if(e.decimals!==void 0)throw new d("BAD_REQUEST","x402 policy: `decimals` is no longer supported — a single policy-wide decimal count can't describe the assets a server may name. List the asset in `allowedAssets` with its own `decimals` instead.");const t=e.allowedAssets??v;if(t.length===0)throw new d("BAD_REQUEST","x402 policy: `allowedAssets` is empty, so no payment could ever be made. Omit it to accept the default USDC assets, or list at least one asset.");const n=new Map;for(const s of t){if(!Number.isInteger(s.decimals)||s.decimals<0)throw new d("BAD_REQUEST",`x402 policy: allowedAssets[${s.asset}].decimals must be a non-negative integer, got ${String(s.decimals)}.`);const o=w(s.network),r=p(s.asset,o),a=n.get(r);if(a!==void 0&&a.decimals!==s.decimals)throw new d("BAD_REQUEST",`x402 policy: allowedAssets lists ${s.asset} on ${o} twice with different decimals (${String(a.decimals)} and ${String(s.decimals)}). One asset cannot have two precisions.`);n.set(r,{...s,network:o})}return n},y=(e,t)=>{const n=new Map;if(e===void 0)return n;for(const{decimals:s}of t.values())n.has(s)||n.set(s,x(e,s));return n},$=e=>{const t=g(e),n=y(e.maxPerCall,t),s=e.allowedNetworks?.map(r=>w(r)),{allowedRecipients:o}=e;return(r,a)=>a.filter(i=>{const l=t.get(p(i.asset,i.network));if(l===void 0)return!1;const c=f(i.amount);if(c===void 0)return!1;const m=n.get(l.decimals);if(m!==void 0&&c>m||s!==void 0&&!s.includes(i.network))return!1;if(o!==void 0){const b=u(i.payTo,i.network);return o.some(h=>u(h,i.network)===b)}return!0})},C=(e,t)=>{const n=g(e),s=y(e.maxPerRun,n);let o;return async r=>{const a=r.selectedRequirements,i=n.get(p(a.asset,a.network));if(i===void 0)return{abort:!0,reason:`x402 policy: asset ${a.asset} on ${a.network} is not in this wallet's allowed assets, so its amount cannot be priced against the caps.`};if(o===void 0)o=i.decimals;else if(o!==i.decimals)return{abort:!0,reason:`x402 policy: this payment is in a ${String(i.decimals)}-decimal asset but the run's spend is tracked in ${String(o)}-decimal units. One wallet cannot mix asset precisions under a single per-run cap.`};const l=f(a.amount);if(l===void 0)return{abort:!0,reason:`x402 policy: this payment's amount (${a.amount}) is not a canonical atomic quantity, so it cannot be checked against the caps.`};const c=s.get(i.decimals);if(c!==void 0&&t.spentAtomic+l>c)return{abort:!0,reason:`x402 policy: this payment (${a.amount}) would exceed the per-run cap (already spent ${t.spentAtomic.toString()}, cap ${c.toString()}, in atomic base units).`};t.add(l);try{return e.onPaymentRequired!==void 0&&!await e.onPaymentRequired(a)?(t.release(l),{abort:!0,reason:"x402 policy: payment was declined by onPaymentRequired."}):void 0}catch(m){throw t.release(l),m}}},P=e=>t=>(e.release(BigInt(t.selectedRequirements.amount)),Promise.resolve()),B=e=>{if(!(e.maxPerCall!==void 0||e.maxPerRun!==void 0||e.onPaymentRequired!==void 0))throw new d("FORBIDDEN","x402 pay: refusing to build a wallet with an unbounded spend policy. Set at least one of maxPerCall, maxPerRun, or onPaymentRequired (allowedNetworks/allowedRecipients/allowedAssets narrow but do not bound spend).")};export{v as DEFAULT_ALLOWED_ASSETS,A as DEFAULT_STABLECOIN_DECIMALS,B as assertBoundedPolicy,C as buildPaymentGuard,$ as buildSpendPolicy,R as createSpendState,P as releaseSpendOnFailure,x as usdToAtomic};
@@ -160,12 +160,48 @@ declare const isEvmNetwork: (network: X402Network) => boolean;
160
160
  */
161
161
  declare const isSvmNetwork: (network: X402Network) => boolean;
162
162
  /**
163
- * USDC and every asset in `@x402/evm` / `@x402/svm`'s `DEFAULT_STABLECOINS`
164
- * uses 6 decimals, so a USD price converts to atomic base units at `10 ** 6`.
165
- * Override per {@link SpendPolicy.decimals} only for a custom, non-6-decimal asset.
163
+ * USDC uses 6 decimals, so a USD price converts to atomic base units at `10 ** 6`.
164
+ * This is only the *default* for the standalone {@link usdToAtomic} helper
165
+ * a spend policy never assumes it, and scales each requirement by the decimals of
166
+ * the asset it actually names (see {@link SpendPolicy.allowedAssets}).
166
167
  * @experimental
167
168
  */
168
169
  declare const DEFAULT_STABLECOIN_DECIMALS = 6;
170
+ /**
171
+ * A stablecoin an agent wallet may pay in, and the decimals its atomic amounts are
172
+ * denominated in.
173
+ *
174
+ * `decimals` is required and explicit: a 402 server names the token contract to
175
+ * transfer, and the same atomic amount means a wildly different sum in a 6-decimal
176
+ * versus an 18-decimal token. Assuming a decimal count is how a tiny USD cap comes
177
+ * to authorise a large transfer, so the caller states it per asset. Note
178
+ * `@x402/evm`'s own `DEFAULT_STABLECOINS` registry is *not* uniformly 6-decimal
179
+ * (MegaUSD and Mezo USD are 18), which is exactly why it can't be trusted as a gate.
180
+ * @experimental
181
+ */
182
+ interface AllowedAsset {
183
+ /** Token contract (EVM) or mint (SVM) address. Matched case-insensitively on EVM, exactly on SVM. */
184
+ readonly asset: string;
185
+ /** Atomic base-unit decimals for this asset. Used to scale the USD caps — must match the token on chain. */
186
+ readonly decimals: number;
187
+ /** The network this asset lives on. Friendly names are resolved to CAIP-2. */
188
+ readonly network: X402Network;
189
+ }
190
+ /**
191
+ * The assets a policy allows when it doesn't name its own — the canonical
192
+ * 6-decimal, dollar-pegged USDC on each network Lunora has a friendly alias for.
193
+ *
194
+ * Deliberately a small hand-mirrored table rather than `@x402/evm`'s
195
+ * `DEFAULT_STABLECOINS`: that registry mixes 6- and 18-decimal assets, and importing
196
+ * it would pull viem into every bundle that touches a policy (the scheme modules are
197
+ * dynamically imported for exactly that reason — see `wallet.ts`). Addresses verified
198
+ * against `@x402/evm` `DEFAULT_STABLECOINS` and `@x402/svm` `USDC_*_ADDRESS` at 2.19.0.
199
+ *
200
+ * `ethereum` (`eip155:1`) is absent — the SDK ships no default stablecoin for it, so
201
+ * paying there needs an explicit {@link SpendPolicy.allowedAssets} entry.
202
+ * @experimental
203
+ */
204
+ declare const DEFAULT_ALLOWED_ASSETS: ReadonlyArray<AllowedAsset>;
169
205
  /**
170
206
  * Spend limits and approval gates for an agent wallet. At least one bound must be
171
207
  * set — see {@link assertBoundedPolicy} — or the pay rail refuses to build.
@@ -175,11 +211,30 @@ declare const DEFAULT_STABLECOIN_DECIMALS = 6;
175
211
  * @experimental
176
212
  */
177
213
  interface SpendPolicy {
214
+ /**
215
+ * Asset allowlist, each entry carrying its own `decimals` (defaults to
216
+ * {@link DEFAULT_ALLOWED_ASSETS} — canonical USDC per friendly network).
217
+ *
218
+ * This is a **security gate, not a convenience**: the server picks which token
219
+ * contract gets transferred, so a policy that only caps a USD amount caps nothing
220
+ * until the asset behind that amount is pinned. A requirement naming an asset
221
+ * outside this list is refused, and the USD caps are scaled by the matched entry's
222
+ * `decimals` — never by an assumed decimal count.
223
+ *
224
+ * Every entry must be dollar-pegged: the caps are USD, and the per-run ledger sums
225
+ * atomic units across payments, so a non-$1 asset silently mis-prices both.
226
+ */
227
+ readonly allowedAssets?: ReadonlyArray<AllowedAsset>;
178
228
  /** Network allowlist. When set, only these networks may be paid on. */
179
229
  readonly allowedNetworks?: ReadonlyArray<X402Network>;
180
230
  /** Recipient allowlist. When set, only these `payTo` addresses may be paid. */
181
231
  readonly allowedRecipients?: ReadonlyArray<string>;
182
- /** Stablecoin decimals for USD→atomic conversion (default {@link DEFAULT_STABLECOIN_DECIMALS}). */
232
+ /**
233
+ * @deprecated One policy-wide decimal count can't describe the assets a server may
234
+ * name, and guessing it is what let a small USD cap authorise a large transfer.
235
+ * Setting this now throws — put the asset in {@link SpendPolicy.allowedAssets} with
236
+ * its own `decimals` instead.
237
+ */
183
238
  readonly decimals?: number;
184
239
  /** Hard ceiling on a single payment, in USD. */
185
240
  readonly maxPerCall?: X402Price;
@@ -220,8 +275,16 @@ declare const createSpendState: () => SpendState;
220
275
  declare const usdToAtomic: (usd: X402Price, decimals?: number) => bigint;
221
276
  /**
222
277
  * A `PaymentPolicy` that narrows the server's offered requirements to those a
223
- * bounded wallet may pay: within the per-call cap, to an allowed recipient, on an
224
- * allowed network. An empty result means the client cannot pay fail-closed.
278
+ * bounded wallet may pay: in an allowed asset, within the per-call cap *for that
279
+ * asset's decimals*, to an allowed recipient, on an allowed network. An empty result
280
+ * means the client cannot pay — fail-closed.
281
+ *
282
+ * The asset check comes first and is what makes the amount check mean anything. The
283
+ * server chooses the token contract the scheme will sign a transfer against, and
284
+ * `amount` is in that token's atomic base units — so comparing it against a USD cap
285
+ * converted at an assumed decimal count mis-prices any asset that doesn't match the
286
+ * assumption. Pinning the asset (and taking its decimals from the policy, not the
287
+ * requirement) closes that.
225
288
  * @experimental
226
289
  */
227
290
  declare const buildSpendPolicy: (policy: SpendPolicy) => PaymentPolicy;
@@ -239,6 +302,10 @@ declare const buildSpendPolicy: (policy: SpendPolicy) => PaymentPolicy;
239
302
  * hook returns; {@link releaseSpendOnFailure} releases it if the signature itself
240
303
  * later fails. The reservation is intentionally *not* released on success — a
241
304
  * committed payment stays counted.
305
+ *
306
+ * The asset is re-checked here rather than trusted from {@link buildSpendPolicy}:
307
+ * the two are registered on separate client seams, and a guard that assumed the
308
+ * filter ran would be one wiring change away from signing an unpinned asset.
242
309
  * @experimental
243
310
  */
244
311
  declare const buildPaymentGuard: (policy: SpendPolicy, state: SpendState) => BeforePaymentCreationHook;
@@ -257,10 +324,11 @@ declare const releaseSpendOnFailure: (state: SpendState) => OnPaymentCreationFai
257
324
  * money on an agent's behalf with unlimited spend authority is never the intent,
258
325
  * so this fails loudly rather than defaulting to unbounded.
259
326
  *
260
- * `allowedNetworks` / `allowedRecipients` narrow *where* a payment can go, but
261
- * neither caps *how much* — a policy with only an allowlist still authorises
262
- * unlimited spend to any recipient it permits. Only `maxPerCall`, `maxPerRun`, or
263
- * a dynamic `onPaymentRequired` gate actually bound spend, so only those count here.
327
+ * `allowedNetworks` / `allowedRecipients` / `allowedAssets` narrow *where* a payment
328
+ * can go and *in what*, but none caps *how much* — a policy with only allowlists still
329
+ * authorises unlimited spend to any recipient it permits. Only `maxPerCall`,
330
+ * `maxPerRun`, or a dynamic `onPaymentRequired` gate actually bound spend, so only
331
+ * those count here.
264
332
  * @experimental
265
333
  */
266
334
  declare const assertBoundedPolicy: (policy: SpendPolicy) => void;
@@ -401,4 +469,4 @@ type X402SignerConfig = X402CdpSignerConfig | {
401
469
  * @experimental
402
470
  */
403
471
  declare const resolveFacilitatorUrl: (facilitator?: FacilitatorConfig) => string;
404
- export { Caip2 as C, DEFAULT_FACILITATOR_URL as D, EVM_NETWORKS as E, FacilitatorConfig as F, NETWORK_TO_CAIP2 as N, PaymentEventRow as P, SVM_NETWORKS as S, X402CdpSignerConfig as X, EvmAddress as a, FriendlyNetwork as b, X402ChargeConfig as c, X402Network as d, X402PayConfig as e, X402Price as f, X402Receipt as g, X402ReceiptSink as h, X402Recipient as i, X402SignerConfig as j, isEvmNetwork as k, isSvmNetwork as l, DEFAULT_STABLECOIN_DECIMALS as m, SpendPolicy as n, SpendState as o, assertBoundedPolicy as p, buildPaymentGuard as q, resolveFacilitatorUrl as r, buildSpendPolicy as s, toCaip2 as t, createSpendState as u, releaseSpendOnFailure as v, usdToAtomic as w, toPaymentEventRow as x, toReceipt as y };
472
+ export { AllowedAsset as A, Caip2 as C, DEFAULT_FACILITATOR_URL as D, EVM_NETWORKS as E, FacilitatorConfig as F, NETWORK_TO_CAIP2 as N, PaymentEventRow as P, SVM_NETWORKS as S, X402CdpSignerConfig as X, EvmAddress as a, FriendlyNetwork as b, X402ChargeConfig as c, X402Network as d, X402PayConfig as e, X402Price as f, X402Receipt as g, X402ReceiptSink as h, X402Recipient as i, X402SignerConfig as j, isEvmNetwork as k, isSvmNetwork as l, DEFAULT_ALLOWED_ASSETS as m, DEFAULT_STABLECOIN_DECIMALS as n, SpendPolicy as o, SpendState as p, assertBoundedPolicy as q, resolveFacilitatorUrl as r, buildPaymentGuard as s, toCaip2 as t, buildSpendPolicy as u, createSpendState as v, releaseSpendOnFailure as w, usdToAtomic as x, toPaymentEventRow as y, toReceipt as z };
@@ -160,12 +160,48 @@ declare const isEvmNetwork: (network: X402Network) => boolean;
160
160
  */
161
161
  declare const isSvmNetwork: (network: X402Network) => boolean;
162
162
  /**
163
- * USDC and every asset in `@x402/evm` / `@x402/svm`'s `DEFAULT_STABLECOINS`
164
- * uses 6 decimals, so a USD price converts to atomic base units at `10 ** 6`.
165
- * Override per {@link SpendPolicy.decimals} only for a custom, non-6-decimal asset.
163
+ * USDC uses 6 decimals, so a USD price converts to atomic base units at `10 ** 6`.
164
+ * This is only the *default* for the standalone {@link usdToAtomic} helper
165
+ * a spend policy never assumes it, and scales each requirement by the decimals of
166
+ * the asset it actually names (see {@link SpendPolicy.allowedAssets}).
166
167
  * @experimental
167
168
  */
168
169
  declare const DEFAULT_STABLECOIN_DECIMALS = 6;
170
+ /**
171
+ * A stablecoin an agent wallet may pay in, and the decimals its atomic amounts are
172
+ * denominated in.
173
+ *
174
+ * `decimals` is required and explicit: a 402 server names the token contract to
175
+ * transfer, and the same atomic amount means a wildly different sum in a 6-decimal
176
+ * versus an 18-decimal token. Assuming a decimal count is how a tiny USD cap comes
177
+ * to authorise a large transfer, so the caller states it per asset. Note
178
+ * `@x402/evm`'s own `DEFAULT_STABLECOINS` registry is *not* uniformly 6-decimal
179
+ * (MegaUSD and Mezo USD are 18), which is exactly why it can't be trusted as a gate.
180
+ * @experimental
181
+ */
182
+ interface AllowedAsset {
183
+ /** Token contract (EVM) or mint (SVM) address. Matched case-insensitively on EVM, exactly on SVM. */
184
+ readonly asset: string;
185
+ /** Atomic base-unit decimals for this asset. Used to scale the USD caps — must match the token on chain. */
186
+ readonly decimals: number;
187
+ /** The network this asset lives on. Friendly names are resolved to CAIP-2. */
188
+ readonly network: X402Network;
189
+ }
190
+ /**
191
+ * The assets a policy allows when it doesn't name its own — the canonical
192
+ * 6-decimal, dollar-pegged USDC on each network Lunora has a friendly alias for.
193
+ *
194
+ * Deliberately a small hand-mirrored table rather than `@x402/evm`'s
195
+ * `DEFAULT_STABLECOINS`: that registry mixes 6- and 18-decimal assets, and importing
196
+ * it would pull viem into every bundle that touches a policy (the scheme modules are
197
+ * dynamically imported for exactly that reason — see `wallet.ts`). Addresses verified
198
+ * against `@x402/evm` `DEFAULT_STABLECOINS` and `@x402/svm` `USDC_*_ADDRESS` at 2.19.0.
199
+ *
200
+ * `ethereum` (`eip155:1`) is absent — the SDK ships no default stablecoin for it, so
201
+ * paying there needs an explicit {@link SpendPolicy.allowedAssets} entry.
202
+ * @experimental
203
+ */
204
+ declare const DEFAULT_ALLOWED_ASSETS: ReadonlyArray<AllowedAsset>;
169
205
  /**
170
206
  * Spend limits and approval gates for an agent wallet. At least one bound must be
171
207
  * set — see {@link assertBoundedPolicy} — or the pay rail refuses to build.
@@ -175,11 +211,30 @@ declare const DEFAULT_STABLECOIN_DECIMALS = 6;
175
211
  * @experimental
176
212
  */
177
213
  interface SpendPolicy {
214
+ /**
215
+ * Asset allowlist, each entry carrying its own `decimals` (defaults to
216
+ * {@link DEFAULT_ALLOWED_ASSETS} — canonical USDC per friendly network).
217
+ *
218
+ * This is a **security gate, not a convenience**: the server picks which token
219
+ * contract gets transferred, so a policy that only caps a USD amount caps nothing
220
+ * until the asset behind that amount is pinned. A requirement naming an asset
221
+ * outside this list is refused, and the USD caps are scaled by the matched entry's
222
+ * `decimals` — never by an assumed decimal count.
223
+ *
224
+ * Every entry must be dollar-pegged: the caps are USD, and the per-run ledger sums
225
+ * atomic units across payments, so a non-$1 asset silently mis-prices both.
226
+ */
227
+ readonly allowedAssets?: ReadonlyArray<AllowedAsset>;
178
228
  /** Network allowlist. When set, only these networks may be paid on. */
179
229
  readonly allowedNetworks?: ReadonlyArray<X402Network>;
180
230
  /** Recipient allowlist. When set, only these `payTo` addresses may be paid. */
181
231
  readonly allowedRecipients?: ReadonlyArray<string>;
182
- /** Stablecoin decimals for USD→atomic conversion (default {@link DEFAULT_STABLECOIN_DECIMALS}). */
232
+ /**
233
+ * @deprecated One policy-wide decimal count can't describe the assets a server may
234
+ * name, and guessing it is what let a small USD cap authorise a large transfer.
235
+ * Setting this now throws — put the asset in {@link SpendPolicy.allowedAssets} with
236
+ * its own `decimals` instead.
237
+ */
183
238
  readonly decimals?: number;
184
239
  /** Hard ceiling on a single payment, in USD. */
185
240
  readonly maxPerCall?: X402Price;
@@ -220,8 +275,16 @@ declare const createSpendState: () => SpendState;
220
275
  declare const usdToAtomic: (usd: X402Price, decimals?: number) => bigint;
221
276
  /**
222
277
  * A `PaymentPolicy` that narrows the server's offered requirements to those a
223
- * bounded wallet may pay: within the per-call cap, to an allowed recipient, on an
224
- * allowed network. An empty result means the client cannot pay fail-closed.
278
+ * bounded wallet may pay: in an allowed asset, within the per-call cap *for that
279
+ * asset's decimals*, to an allowed recipient, on an allowed network. An empty result
280
+ * means the client cannot pay — fail-closed.
281
+ *
282
+ * The asset check comes first and is what makes the amount check mean anything. The
283
+ * server chooses the token contract the scheme will sign a transfer against, and
284
+ * `amount` is in that token's atomic base units — so comparing it against a USD cap
285
+ * converted at an assumed decimal count mis-prices any asset that doesn't match the
286
+ * assumption. Pinning the asset (and taking its decimals from the policy, not the
287
+ * requirement) closes that.
225
288
  * @experimental
226
289
  */
227
290
  declare const buildSpendPolicy: (policy: SpendPolicy) => PaymentPolicy;
@@ -239,6 +302,10 @@ declare const buildSpendPolicy: (policy: SpendPolicy) => PaymentPolicy;
239
302
  * hook returns; {@link releaseSpendOnFailure} releases it if the signature itself
240
303
  * later fails. The reservation is intentionally *not* released on success — a
241
304
  * committed payment stays counted.
305
+ *
306
+ * The asset is re-checked here rather than trusted from {@link buildSpendPolicy}:
307
+ * the two are registered on separate client seams, and a guard that assumed the
308
+ * filter ran would be one wiring change away from signing an unpinned asset.
242
309
  * @experimental
243
310
  */
244
311
  declare const buildPaymentGuard: (policy: SpendPolicy, state: SpendState) => BeforePaymentCreationHook;
@@ -257,10 +324,11 @@ declare const releaseSpendOnFailure: (state: SpendState) => OnPaymentCreationFai
257
324
  * money on an agent's behalf with unlimited spend authority is never the intent,
258
325
  * so this fails loudly rather than defaulting to unbounded.
259
326
  *
260
- * `allowedNetworks` / `allowedRecipients` narrow *where* a payment can go, but
261
- * neither caps *how much* — a policy with only an allowlist still authorises
262
- * unlimited spend to any recipient it permits. Only `maxPerCall`, `maxPerRun`, or
263
- * a dynamic `onPaymentRequired` gate actually bound spend, so only those count here.
327
+ * `allowedNetworks` / `allowedRecipients` / `allowedAssets` narrow *where* a payment
328
+ * can go and *in what*, but none caps *how much* — a policy with only allowlists still
329
+ * authorises unlimited spend to any recipient it permits. Only `maxPerCall`,
330
+ * `maxPerRun`, or a dynamic `onPaymentRequired` gate actually bound spend, so only
331
+ * those count here.
264
332
  * @experimental
265
333
  */
266
334
  declare const assertBoundedPolicy: (policy: SpendPolicy) => void;
@@ -401,4 +469,4 @@ type X402SignerConfig = X402CdpSignerConfig | {
401
469
  * @experimental
402
470
  */
403
471
  declare const resolveFacilitatorUrl: (facilitator?: FacilitatorConfig) => string;
404
- export { Caip2 as C, DEFAULT_FACILITATOR_URL as D, EVM_NETWORKS as E, FacilitatorConfig as F, NETWORK_TO_CAIP2 as N, PaymentEventRow as P, SVM_NETWORKS as S, X402CdpSignerConfig as X, EvmAddress as a, FriendlyNetwork as b, X402ChargeConfig as c, X402Network as d, X402PayConfig as e, X402Price as f, X402Receipt as g, X402ReceiptSink as h, X402Recipient as i, X402SignerConfig as j, isEvmNetwork as k, isSvmNetwork as l, DEFAULT_STABLECOIN_DECIMALS as m, SpendPolicy as n, SpendState as o, assertBoundedPolicy as p, buildPaymentGuard as q, resolveFacilitatorUrl as r, buildSpendPolicy as s, toCaip2 as t, createSpendState as u, releaseSpendOnFailure as v, usdToAtomic as w, toPaymentEventRow as x, toReceipt as y };
472
+ export { AllowedAsset as A, Caip2 as C, DEFAULT_FACILITATOR_URL as D, EVM_NETWORKS as E, FacilitatorConfig as F, NETWORK_TO_CAIP2 as N, PaymentEventRow as P, SVM_NETWORKS as S, X402CdpSignerConfig as X, EvmAddress as a, FriendlyNetwork as b, X402ChargeConfig as c, X402Network as d, X402PayConfig as e, X402Price as f, X402Receipt as g, X402ReceiptSink as h, X402Recipient as i, X402SignerConfig as j, isEvmNetwork as k, isSvmNetwork as l, DEFAULT_ALLOWED_ASSETS as m, DEFAULT_STABLECOIN_DECIMALS as n, SpendPolicy as o, SpendState as p, assertBoundedPolicy as q, resolveFacilitatorUrl as r, buildPaymentGuard as s, toCaip2 as t, buildSpendPolicy as u, createSpendState as v, releaseSpendOnFailure as w, usdToAtomic as x, toPaymentEventRow as y, toReceipt as z };
@@ -0,0 +1 @@
1
+ import{x402Client as l}from"@x402/core/client";import{wrapFetchWithPayment as c}from"@x402/fetch";import{assertBoundedPolicy as m,buildSpendPolicy as p,buildPaymentGuard as y,releaseSpendOnFailure as s,createSpendState as d}from"./DEFAULT_ALLOWED_ASSETS-BIIP_mdG.mjs";import{registerWallet as P}from"./registerWallet-Cu4qF5tj.mjs";const S=async(r,t)=>{m(r.policy);const i=d(),o=p(r.policy),a=y(r.policy,i),n=s(i),e=new l;return await P(e,r,t),e.registerPolicy(o),e.onBeforePaymentCreation(a),e.onPaymentCreationFailure(n),c(t.fetch??globalThis.fetch,e)};export{S as createPayFetch};
@@ -1,5 +1,5 @@
1
- import { e as X402PayConfig } from "../packem_shared/config.d-B-NYmHgG.mjs";
2
- export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, m as DEFAULT_STABLECOIN_DECIMALS, type n as SpendPolicy, type o as SpendState, type X as X402CdpSignerConfig, type d as X402Network, type f as X402Price, type j as X402SignerConfig, p as assertBoundedPolicy, q as buildPaymentGuard, s as buildSpendPolicy, u as createSpendState, k as isEvmNetwork, l as isSvmNetwork, v as releaseSpendOnFailure, r as resolveFacilitatorUrl, t as toCaip2, w as usdToAtomic } from "../packem_shared/config.d-B-NYmHgG.mjs";
1
+ import { e as X402PayConfig } from "../packem_shared/config.d-Dki_2r9a.mjs";
2
+ export { type A as AllowedAsset, type C as Caip2, m as DEFAULT_ALLOWED_ASSETS, D as DEFAULT_FACILITATOR_URL, n as DEFAULT_STABLECOIN_DECIMALS, type o as SpendPolicy, type p as SpendState, type X as X402CdpSignerConfig, type d as X402Network, type f as X402Price, type j as X402SignerConfig, q as assertBoundedPolicy, s as buildPaymentGuard, u as buildSpendPolicy, v as createSpendState, k as isEvmNetwork, l as isSvmNetwork, w as releaseSpendOnFailure, r as resolveFacilitatorUrl, t as toCaip2, x as usdToAtomic } from "../packem_shared/config.d-Dki_2r9a.mjs";
3
3
  import { x402Client } from '@x402/core/client';
4
4
  import { ClientSvmSigner } from '@x402/svm';
5
5
  import { PrivateKeyAccount } from 'viem/accounts';
@@ -1,5 +1,5 @@
1
- import { e as X402PayConfig } from "../packem_shared/config.d-B-NYmHgG.js";
2
- export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, m as DEFAULT_STABLECOIN_DECIMALS, type n as SpendPolicy, type o as SpendState, type X as X402CdpSignerConfig, type d as X402Network, type f as X402Price, type j as X402SignerConfig, p as assertBoundedPolicy, q as buildPaymentGuard, s as buildSpendPolicy, u as createSpendState, k as isEvmNetwork, l as isSvmNetwork, v as releaseSpendOnFailure, r as resolveFacilitatorUrl, t as toCaip2, w as usdToAtomic } from "../packem_shared/config.d-B-NYmHgG.js";
1
+ import { e as X402PayConfig } from "../packem_shared/config.d-Dki_2r9a.js";
2
+ export { type A as AllowedAsset, type C as Caip2, m as DEFAULT_ALLOWED_ASSETS, D as DEFAULT_FACILITATOR_URL, n as DEFAULT_STABLECOIN_DECIMALS, type o as SpendPolicy, type p as SpendState, type X as X402CdpSignerConfig, type d as X402Network, type f as X402Price, type j as X402SignerConfig, q as assertBoundedPolicy, s as buildPaymentGuard, u as buildSpendPolicy, v as createSpendState, k as isEvmNetwork, l as isSvmNetwork, w as releaseSpendOnFailure, r as resolveFacilitatorUrl, t as toCaip2, x as usdToAtomic } from "../packem_shared/config.d-Dki_2r9a.js";
3
3
  import { x402Client } from '@x402/core/client';
4
4
  import { ClientSvmSigner } from '@x402/svm';
5
5
  import { PrivateKeyAccount } from 'viem/accounts';
@@ -1 +1 @@
1
- import{createPayFetch as c}from"../packem_shared/createPayFetch-CDZJaS13.mjs";import{DEFAULT_FACILITATOR_URL as p,resolveFacilitatorUrl as d}from"../packem_shared/DEFAULT_FACILITATOR_URL-lUtljfJI.mjs";import{isEvmNetwork as S,isSvmNetwork as f,toCaip2 as u}from"../packem_shared/EVM_NETWORKS-XOms9Rtk.mjs";import{DEFAULT_STABLECOIN_DECIMALS as v,assertBoundedPolicy as E,buildPaymentGuard as F,buildSpendPolicy as L,createSpendState as P,releaseSpendOnFailure as T,usdToAtomic as x}from"../packem_shared/DEFAULT_STABLECOIN_DECIMALS-gY9zXmGa.mjs";import{registerWallet as w,resolveEvmAccount as C,resolveSvmSigner as I}from"../packem_shared/registerWallet-Cu4qF5tj.mjs";const i=async(e,r)=>({fetch:await c(e,r)}),s=(e,r)=>{let t;return{fetch:async(o,a)=>(t??=i(e,r),(await t).fetch(o,a))}};export{p as DEFAULT_FACILITATOR_URL,v as DEFAULT_STABLECOIN_DECIMALS,E as assertBoundedPolicy,F as buildPaymentGuard,L as buildSpendPolicy,c as createPayFetch,P as createSpendState,i as createX402Pay,S as isEvmNetwork,f as isSvmNetwork,s as lazyX402Pay,w as registerWallet,T as releaseSpendOnFailure,C as resolveEvmAccount,d as resolveFacilitatorUrl,I as resolveSvmSigner,u as toCaip2,x as usdToAtomic};
1
+ import{createPayFetch as c}from"../packem_shared/createPayFetch-B6A51xPI.mjs";import{DEFAULT_FACILITATOR_URL as S,resolveFacilitatorUrl as A}from"../packem_shared/DEFAULT_FACILITATOR_URL-lUtljfJI.mjs";import{isEvmNetwork as d,isSvmNetwork as y,toCaip2 as E}from"../packem_shared/EVM_NETWORKS-XOms9Rtk.mjs";import{DEFAULT_ALLOWED_ASSETS as f,DEFAULT_STABLECOIN_DECIMALS as u,assertBoundedPolicy as T,buildPaymentGuard as v,buildSpendPolicy as F,createSpendState as P,releaseSpendOnFailure as _,usdToAtomic as x}from"../packem_shared/DEFAULT_ALLOWED_ASSETS-BIIP_mdG.mjs";import{registerWallet as U,resolveEvmAccount as h,resolveSvmSigner as w}from"../packem_shared/registerWallet-Cu4qF5tj.mjs";const i=async(e,r)=>({fetch:await c(e,r)}),s=(e,r)=>{let t;return{fetch:async(o,a)=>(t??=i(e,r),(await t).fetch(o,a))}};export{f as DEFAULT_ALLOWED_ASSETS,S as DEFAULT_FACILITATOR_URL,u as DEFAULT_STABLECOIN_DECIMALS,T as assertBoundedPolicy,v as buildPaymentGuard,F as buildSpendPolicy,c as createPayFetch,P as createSpendState,i as createX402Pay,d as isEvmNetwork,y as isSvmNetwork,s as lazyX402Pay,U as registerWallet,_ as releaseSpendOnFailure,h as resolveEvmAccount,A as resolveFacilitatorUrl,w as resolveSvmSigner,E as toCaip2,x as usdToAtomic};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/x402",
3
- "version": "1.0.0-alpha.11",
3
+ "version": "1.0.0-alpha.13",
4
4
  "description": "Agentic payments (x402) for Lunora: charge agents per request (charge rail) and let your agents pay x402-gated resources (pay rail)",
5
5
  "keywords": [
6
6
  "agents",
@@ -1 +0,0 @@
1
- import{LunoraError as s}from"@lunora/errors";import{toCaip2 as w}from"./EVM_NETWORKS-XOms9Rtk.mjs";const y=/^\d+(?:\.\d+)?$/,u=(e,t)=>t.startsWith("eip155:")?e.toLowerCase():e,l=6,v=()=>{let e=0n;return{add:t=>{e+=t},release:t=>{e=e>t?e-t:0n},get spentAtomic(){return e}}},m=(e,t=l)=>{if(!Number.isInteger(t)||t<0)throw new s("BAD_REQUEST",`x402 policy: decimals must be a non-negative integer, got ${String(t)}.`);const n=(typeof e=="number"?e.toString():e).trim(),o=n.startsWith("$")?n.slice(1):n;if(!y.test(o))throw new s("BAD_REQUEST",`x402 policy: "${String(e)}" is not a valid USD amount (use a plain decimal like "0.01").`);const[d="0",i=""]=o.split("."),r=i.slice(0,t).padEnd(t,"0");return BigInt(d)*10n**BigInt(t)+BigInt(r===""?"0":r)},R=e=>{const t=e.decimals??l,n=e.maxPerCall===void 0?void 0:m(e.maxPerCall,t),o=e.allowedNetworks?.map(i=>w(i)),{allowedRecipients:d}=e;return(i,r)=>r.filter(a=>{if(n!==void 0&&BigInt(a.amount)>n||o!==void 0&&!o.includes(a.network))return!1;if(d!==void 0){const c=u(a.payTo,a.network);return d.some(p=>u(p,a.network)===c)}return!0})},x=(e,t)=>{const n=e.decimals??l,o=e.maxPerRun===void 0?void 0:m(e.maxPerRun,n);return async d=>{const i=d.selectedRequirements,r=BigInt(i.amount);if(o!==void 0&&t.spentAtomic+r>o)return{abort:!0,reason:`x402 policy: this payment (${i.amount}) would exceed the per-run cap (already spent ${t.spentAtomic.toString()}, cap ${o.toString()}, in atomic base units).`};t.add(r);try{return e.onPaymentRequired!==void 0&&!await e.onPaymentRequired(i)?(t.release(r),{abort:!0,reason:"x402 policy: payment was declined by onPaymentRequired."}):void 0}catch(a){throw t.release(r),a}}},S=e=>t=>(e.release(BigInt(t.selectedRequirements.amount)),Promise.resolve()),b=e=>{if(!(e.maxPerCall!==void 0||e.maxPerRun!==void 0||e.onPaymentRequired!==void 0))throw new s("FORBIDDEN","x402 pay: refusing to build a wallet with an unbounded spend policy. Set at least one of maxPerCall, maxPerRun, or onPaymentRequired (allowedNetworks/allowedRecipients narrow but do not bound spend).")};export{l as DEFAULT_STABLECOIN_DECIMALS,b as assertBoundedPolicy,x as buildPaymentGuard,R as buildSpendPolicy,v as createSpendState,S as releaseSpendOnFailure,m as usdToAtomic};
@@ -1 +0,0 @@
1
- import{x402Client as i}from"@x402/core/client";import{wrapFetchWithPayment as a}from"@x402/fetch";import{assertBoundedPolicy as n,buildSpendPolicy as c,buildPaymentGuard as l,releaseSpendOnFailure as m,createSpendState as p}from"./DEFAULT_STABLECOIN_DECIMALS-gY9zXmGa.mjs";import{registerWallet as y}from"./registerWallet-Cu4qF5tj.mjs";const u=async(r,t)=>{n(r.policy);const e=new i;await y(e,r,t);const o=p();return e.registerPolicy(c(r.policy)),e.onBeforePaymentCreation(l(r.policy,o)),e.onPaymentCreationFailure(m(o)),a(t.fetch??globalThis.fetch,e)};export{u as createPayFetch};