@nevermined-io/core-kit 1.30.0 → 1.32.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.
|
@@ -319,11 +319,24 @@ export type NeverminedTypedDataMessageV3 = NeverminedTypedDataMessage & {
|
|
|
319
319
|
nonce: string;
|
|
320
320
|
};
|
|
321
321
|
/**
|
|
322
|
-
* The EIP-712 struct version
|
|
322
|
+
* The EIP-712 struct version an **x402** token is signed over — x402's ladder, and only
|
|
323
|
+
* x402's (#3266).
|
|
323
324
|
*
|
|
324
325
|
* `1` is not mintable — it only exists as a verification branch for tokens minted
|
|
325
326
|
* before #1678. New tokens are v2 (the default, reusable, pre-#2646 semantics) or
|
|
326
327
|
* v3 (seller/resource-bound and single-use).
|
|
328
|
+
*
|
|
329
|
+
* MPP has NO version ladder and no ordinal of its own: it signs ONE struct —
|
|
330
|
+
* {@link NeverminedTypedDataTypes}, reused rather than copied — and names no version for it.
|
|
331
|
+
* That is not a temporary state. MPP's own draft (`draft-ryan-httpauth-payment`, §9.2) carries
|
|
332
|
+
* no version on the wire either: the scheme name is the stable identifier, evolution is
|
|
333
|
+
* additive, and what versioning the protocol needs lives on the payment METHOD
|
|
334
|
+
* (`methodDetails.version`). So do not add an MPP rung here, and do not let an x402 rung reach
|
|
335
|
+
* the MPP minter — the two ladders were shared once, and #3258 is what that cost.
|
|
336
|
+
*
|
|
337
|
+
* ⚠️ That draft's "ignore what you don't recognise" rule belongs to the HTTP envelope and MUST
|
|
338
|
+
* NOT be carried into this struct: EIP-712 hashes the type set, so an unrecognised signed member
|
|
339
|
+
* is not ignorable — it changes the digest.
|
|
327
340
|
*/
|
|
328
341
|
export type NeverminedTokenVersion = 2 | 3;
|
|
329
342
|
/**
|
|
@@ -350,38 +363,55 @@ export type GenerateX402TokenOptions = {
|
|
|
350
363
|
* one-time nonce, which the resource server consumes at settle. A v3 token is
|
|
351
364
|
* therefore SINGLE-USE: a caller that opts in must mint one per paid request.
|
|
352
365
|
*
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
* challenge. So MPP crosses to v3 on the same schedule as x402 — when the SDKs mint
|
|
359
|
-
* one token per paid request.
|
|
366
|
+
* x402 ONLY (#3266). It used to apply to both protocols, which is exactly how the x402 nonce
|
|
367
|
+
* reached MPP — where the access token is the buyer's REUSABLE credential and the single-use
|
|
368
|
+
* unit is the CHALLENGE, so a per-token nonce kills every MPP buyer's second challenge
|
|
369
|
+
* (#3258). {@link GenerateMppTokenOptions} omits this field, and the MPP minter has no
|
|
370
|
+
* parameter that could carry it.
|
|
360
371
|
*/
|
|
361
372
|
tokenVersion?: NeverminedTokenVersion;
|
|
362
373
|
};
|
|
374
|
+
/**
|
|
375
|
+
* Options for minting an MPP access token — {@link GenerateX402TokenOptions} minus the x402
|
|
376
|
+
* version knob (#3266).
|
|
377
|
+
*
|
|
378
|
+
* The omission IS the divergence: MPP signs one struct and the server chooses it, so there is
|
|
379
|
+
* nothing here for a caller to name. Passing `tokenVersion` to
|
|
380
|
+
* {@link generateAgentMppAccessToken} is a compile error rather than a silently honoured
|
|
381
|
+
* request — which also means the guard is invisible to jest, since the suites run through SWC
|
|
382
|
+
* and it strips types without checking them. `nx typecheck` is what enforces it.
|
|
383
|
+
*/
|
|
384
|
+
export type GenerateMppTokenOptions = Omit<GenerateX402TokenOptions, 'tokenVersion'>;
|
|
363
385
|
/**
|
|
364
386
|
* Generate an x402 access token aligned with x402 v2 spec.
|
|
365
387
|
*
|
|
388
|
+
* Walks x402's OWN version ladder (#3266): v3 when the caller asks for the #2646 binding and
|
|
389
|
+
* one-time nonce, the base struct otherwise. Nothing chosen here can reach the MPP minter.
|
|
390
|
+
*
|
|
366
391
|
* @param options - Token generation options
|
|
367
392
|
* @returns x402 access token with EIP-712 signature
|
|
368
393
|
*/
|
|
369
394
|
export declare const generateAgentX402AccessToken: (options: GenerateX402TokenOptions) => Promise<AgentX402AccessToken>;
|
|
370
395
|
/**
|
|
371
|
-
* Generate a NATIVE MPP access token (#2640) — the same
|
|
396
|
+
* Generate a NATIVE MPP access token (#2640) — the same struct signed under the
|
|
372
397
|
* `Nevermined-MPP` EIP-712 domain.
|
|
373
398
|
*
|
|
374
399
|
* Native, not a wrapper: the revised Tier A design (#2024) deliberately does NOT embed an x402
|
|
375
400
|
* token inside an MPP credential, because unwrapping it would hand the holder a token that still
|
|
376
401
|
* settles on the public x402 path.
|
|
377
402
|
*
|
|
378
|
-
*
|
|
403
|
+
* ONE struct, NO version (#3266). {@link NeverminedTypedDataTypes} is REUSED rather than copied
|
|
404
|
+
* under an MPP name — the members are identical, and a second table would only drift. What
|
|
405
|
+
* diverges is the CHOICE: this minter has no ladder to walk and no parameter that could make it
|
|
406
|
+
* walk one, so a future x402 struct change cannot reach MPP by default the way #2646's nonce did.
|
|
407
|
+
*
|
|
408
|
+
* @param options - Token generation options, minus x402's `tokenVersion`
|
|
379
409
|
* @returns MPP access token with EIP-712 signature over the MPP domain. The envelope is IDENTICAL
|
|
380
410
|
* to an x402 token's, `accepted.scheme: 'nvm:erc4337'` included — only the signature differs, so
|
|
381
411
|
* the protocol is fixed by the endpoint, not readable off the wire. See
|
|
382
412
|
* {@link NEVERMINED_MPP_EIP712_DOMAIN_NAME} on routing vs. the security boundary.
|
|
383
413
|
*/
|
|
384
|
-
export declare const generateAgentMppAccessToken: (options:
|
|
414
|
+
export declare const generateAgentMppAccessToken: (options: GenerateMppTokenOptions) => Promise<AgentX402AccessToken>;
|
|
385
415
|
/**
|
|
386
416
|
* True when `authorization` carries a GENUINE signed planId (#1678) — a present,
|
|
387
417
|
* non-empty string. This is the single v1/v2 discriminator: verification keys off
|
|
@@ -450,8 +480,8 @@ export type SignedBindingEnvelope = {
|
|
|
450
480
|
*/
|
|
451
481
|
export declare const isSignedBindingConsistent: (authorization: X402Erc4337Payload["authorization"], envelope: SignedBindingEnvelope) => boolean;
|
|
452
482
|
/**
|
|
453
|
-
* Verify an x402 access token signature (see {@link
|
|
454
|
-
* detection and its downgrade-safety).
|
|
483
|
+
* Verify an x402 access token signature (see {@link selectX402AuthorizationTypes} for the
|
|
484
|
+
* v1/v2/v3 type-list detection and its downgrade-safety).
|
|
455
485
|
*
|
|
456
486
|
* Rejects an MPP token: it was signed under a different domain separator, so the digest differs.
|
|
457
487
|
*
|
|
@@ -469,8 +499,9 @@ export declare const verifyAgentX402AccessToken: (accessToken: AgentX402AccessTo
|
|
|
469
499
|
* AND downgrade — WHILE the signing key stays server-side. A subscriber holding it can mint under
|
|
470
500
|
* whichever domain they like, so the split constrains the transport, not that holder.
|
|
471
501
|
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
502
|
+
* One struct, no ladder: {@link selectMppAuthorizationTypes} accepts the base struct and nothing
|
|
503
|
+
* else, so a legacy (unsigned-planId) authorization is refused outright and an x402 struct version
|
|
504
|
+
* is not verifiable here just because x402 added it (#3266).
|
|
474
505
|
*
|
|
475
506
|
* @param accessToken - The MPP access token to verify
|
|
476
507
|
* @param publicClient - Viem public client
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentX402AccessToken.d.ts","sourceRoot":"","sources":["../../src/models/AgentX402AccessToken.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"AgentX402AccessToken.d.ts","sourceRoot":"","sources":["../../src/models/AgentX402AccessToken.ts"],"names":[],"mappings":"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"}
|
|
@@ -171,17 +171,14 @@ export const NeverminedTypedDataTypesV3 = {
|
|
|
171
171
|
return `0x${Array.from(bytes, (b)=>b.toString(16).padStart(2, '0')).join('')}`;
|
|
172
172
|
};
|
|
173
173
|
/**
|
|
174
|
-
*
|
|
175
|
-
* `domainName` — same signed struct, same envelope, same session keys. Keeping one implementation
|
|
176
|
-
* is deliberate: two copies would drift, and a drifted MPP minter that happened to reproduce the
|
|
177
|
-
* x402 digest would silently reopen the cross-protocol replay this domain split exists to close.
|
|
174
|
+
* Builds the Authorization message BOTH protocols sign.
|
|
178
175
|
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const
|
|
176
|
+
* This is the part that deliberately does NOT diverge (#3266): `from`, `sessionKeysProvider`,
|
|
177
|
+
* `sessionKeys` and `planId` mean the same thing on x402 and MPP, so a defect here would be
|
|
178
|
+
* universal — with two copies it would have to be found and fixed twice. Keeping it
|
|
179
|
+
* single-sourced is what bounds the cost of the divergence above it.
|
|
180
|
+
*/ const buildAuthorizationMessage = (options)=>{
|
|
181
|
+
const { subscriber, orderSessionKey, redeemSessionKey, planId } = options;
|
|
185
182
|
// A v2 token MUST carry a non-empty signed planId (#1678) — an empty/blank
|
|
186
183
|
// planId would otherwise be signed into `authorization.planId` and could be
|
|
187
184
|
// misread as a "present but empty" signed claim, blurring the v1/v2 boundary
|
|
@@ -190,55 +187,50 @@ export const NeverminedTypedDataTypesV3 = {
|
|
|
190
187
|
if (typeof planId !== 'string' || planId.trim() === '') {
|
|
191
188
|
throw new Error('Nevermined access token: planId is required and must be a non-empty string');
|
|
192
189
|
}
|
|
193
|
-
const chainId = requireChainId(publicClient, 'Nevermined access token mint');
|
|
194
|
-
// hash the session keys
|
|
195
|
-
const redeemSessionKeyHash = keccak256(toBytes(redeemSessionKey));
|
|
196
|
-
const orderSessionKeyHash = keccak256(toBytes(orderSessionKey));
|
|
197
|
-
// generate the message with `from` address and the SIGNED planId (#1678).
|
|
198
190
|
// Including `planId` here puts it inside the EIP-712 digest, so it cannot be
|
|
199
191
|
// altered without invalidating the signature.
|
|
200
|
-
|
|
192
|
+
return {
|
|
201
193
|
from: subscriber.address,
|
|
202
194
|
sessionKeysProvider: 'zerodev',
|
|
203
195
|
sessionKeys: [
|
|
204
196
|
{
|
|
205
197
|
id: 'order',
|
|
206
|
-
data:
|
|
198
|
+
data: keccak256(toBytes(orderSessionKey))
|
|
207
199
|
},
|
|
208
200
|
{
|
|
209
201
|
id: 'redeem',
|
|
210
|
-
data:
|
|
202
|
+
data: keccak256(toBytes(redeemSessionKey))
|
|
211
203
|
}
|
|
212
204
|
],
|
|
213
205
|
planId
|
|
214
206
|
};
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Signs an already-chosen struct under an already-chosen domain and wraps it in the envelope.
|
|
210
|
+
*
|
|
211
|
+
* `types` and `message` arrive TOGETHER and already agreed, because EIP-712 hashes the type set
|
|
212
|
+
* — a v3 message signed over the v2 list produces a digest no verifier reproduces. Choosing the
|
|
213
|
+
* pair is each protocol's job; this function never infers one from the other, and has no notion
|
|
214
|
+
* of a version to infer it from.
|
|
215
|
+
*
|
|
216
|
+
* Not exported — callers go through {@link generateAgentX402AccessToken} or
|
|
217
|
+
* {@link generateAgentMppAccessToken}, each with its domain pinned, so a protocol can never be
|
|
218
|
+
* selected by a caller-supplied value.
|
|
219
|
+
*/ const signAccessToken = async (options, domainName, types, message)=>{
|
|
220
|
+
const { subscriber, publicClient, planId, schemeVersion = '1', agentId, resource, httpVerb, validUntil } = options;
|
|
221
|
+
const chainId = requireChainId(publicClient, 'Nevermined access token mint');
|
|
226
222
|
const domain = {
|
|
227
223
|
name: domainName,
|
|
228
224
|
version: '1',
|
|
229
225
|
chainId,
|
|
230
226
|
verifyingContract: subscriber.address
|
|
231
227
|
};
|
|
232
|
-
|
|
233
|
-
// message shape must be chosen together — a v3 message signed over the v2 list
|
|
234
|
-
// would produce a digest no verifier reproduces.
|
|
235
|
-
const eip712Data = {
|
|
228
|
+
const signature = await subscriber.signTypedData({
|
|
236
229
|
domain,
|
|
237
|
-
types
|
|
230
|
+
types,
|
|
238
231
|
primaryType: 'Authorization',
|
|
239
232
|
message
|
|
240
|
-
};
|
|
241
|
-
const signature = await subscriber.signTypedData(eip712Data);
|
|
233
|
+
});
|
|
242
234
|
// Network in CAIP-2 format (eip155:chainId) — always resolvable, `requireChainId` already ran.
|
|
243
235
|
const network = `eip155:${chainId}`;
|
|
244
236
|
return {
|
|
@@ -273,23 +265,47 @@ export const NeverminedTypedDataTypesV3 = {
|
|
|
273
265
|
/**
|
|
274
266
|
* Generate an x402 access token aligned with x402 v2 spec.
|
|
275
267
|
*
|
|
268
|
+
* Walks x402's OWN version ladder (#3266): v3 when the caller asks for the #2646 binding and
|
|
269
|
+
* one-time nonce, the base struct otherwise. Nothing chosen here can reach the MPP minter.
|
|
270
|
+
*
|
|
276
271
|
* @param options - Token generation options
|
|
277
272
|
* @returns x402 access token with EIP-712 signature
|
|
278
|
-
*/ export const generateAgentX402AccessToken = (options)=>
|
|
273
|
+
*/ export const generateAgentX402AccessToken = async (options)=>{
|
|
274
|
+
const message = buildAuthorizationMessage(options);
|
|
275
|
+
// v3 (#2646) appends the seller/resource binding and the one-time nonce. A member the caller
|
|
276
|
+
// left undefined is signed as '' — EIP-712 has no optional members, so "not bound" must itself
|
|
277
|
+
// be a signed value; `isSignedBindingConsistent` then holds the unsigned envelope copy to the
|
|
278
|
+
// same absence.
|
|
279
|
+
if ((options.tokenVersion ?? 2) === 3) {
|
|
280
|
+
return signAccessToken(options, NEVERMINED_EIP712_DOMAIN_NAME, NeverminedTypedDataTypesV3, {
|
|
281
|
+
...message,
|
|
282
|
+
agentId: options.agentId ?? '',
|
|
283
|
+
resourceUrl: options.resource?.url ?? '',
|
|
284
|
+
httpVerb: options.httpVerb ?? '',
|
|
285
|
+
nonce: generateTokenNonce()
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
return signAccessToken(options, NEVERMINED_EIP712_DOMAIN_NAME, NeverminedTypedDataTypes, message);
|
|
289
|
+
};
|
|
279
290
|
/**
|
|
280
|
-
* Generate a NATIVE MPP access token (#2640) — the same
|
|
291
|
+
* Generate a NATIVE MPP access token (#2640) — the same struct signed under the
|
|
281
292
|
* `Nevermined-MPP` EIP-712 domain.
|
|
282
293
|
*
|
|
283
294
|
* Native, not a wrapper: the revised Tier A design (#2024) deliberately does NOT embed an x402
|
|
284
295
|
* token inside an MPP credential, because unwrapping it would hand the holder a token that still
|
|
285
296
|
* settles on the public x402 path.
|
|
286
297
|
*
|
|
287
|
-
*
|
|
298
|
+
* ONE struct, NO version (#3266). {@link NeverminedTypedDataTypes} is REUSED rather than copied
|
|
299
|
+
* under an MPP name — the members are identical, and a second table would only drift. What
|
|
300
|
+
* diverges is the CHOICE: this minter has no ladder to walk and no parameter that could make it
|
|
301
|
+
* walk one, so a future x402 struct change cannot reach MPP by default the way #2646's nonce did.
|
|
302
|
+
*
|
|
303
|
+
* @param options - Token generation options, minus x402's `tokenVersion`
|
|
288
304
|
* @returns MPP access token with EIP-712 signature over the MPP domain. The envelope is IDENTICAL
|
|
289
305
|
* to an x402 token's, `accepted.scheme: 'nvm:erc4337'` included — only the signature differs, so
|
|
290
306
|
* the protocol is fixed by the endpoint, not readable off the wire. See
|
|
291
307
|
* {@link NEVERMINED_MPP_EIP712_DOMAIN_NAME} on routing vs. the security boundary.
|
|
292
|
-
*/ export const generateAgentMppAccessToken = (options)=>
|
|
308
|
+
*/ export const generateAgentMppAccessToken = async (options)=>signAccessToken(options, NEVERMINED_MPP_EIP712_DOMAIN_NAME, NeverminedTypedDataTypes, buildAuthorizationMessage(options));
|
|
293
309
|
/**
|
|
294
310
|
* True when `authorization` carries a GENUINE signed planId (#1678) — a present,
|
|
295
311
|
* non-empty string. This is the single v1/v2 discriminator: verification keys off
|
|
@@ -358,27 +374,55 @@ export const NeverminedTypedDataTypesV3 = {
|
|
|
358
374
|
return matches(authorization.agentId, envelope.agentId) && matches(authorization.resourceUrl, envelope.resourceUrl) && matches(authorization.httpVerb, envelope.httpVerb);
|
|
359
375
|
};
|
|
360
376
|
/**
|
|
361
|
-
*
|
|
377
|
+
* x402's ladder: a genuine signed nonce (#2646) → v3, else a genuine signed planId (#1678) → v2,
|
|
378
|
+
* else the original v1 list. Selected by GENUINE presence and never by a caller-supplied hint.
|
|
379
|
+
* The branches are mutually exclusive and none falls back to another — trying a second type list
|
|
380
|
+
* on failure would erase exactly the downgrade protection the versioning provides.
|
|
381
|
+
*
|
|
382
|
+
* A v1 (unsigned-planId) authorization leaves `isSignedPlanIdConsistent` inert, so the tamperable
|
|
383
|
+
* `accepted.planId` becomes authoritative. That is a deliberate #1678 back-compat concession, and
|
|
384
|
+
* it is why this selector admits a shape {@link selectMppAuthorizationTypes} refuses.
|
|
385
|
+
*/ const selectX402AuthorizationTypes = (authorization)=>hasSignedNonce(authorization) ? NeverminedTypedDataTypesV3 : hasSignedPlanId(authorization) ? NeverminedTypedDataTypes : NeverminedTypedDataTypesV1;
|
|
386
|
+
/**
|
|
387
|
+
* MPP's table: one entry, no ladder (#3266) — {@link NeverminedTypedDataTypes} reused, not copied.
|
|
388
|
+
*
|
|
389
|
+
* Two consequences, both of them the point:
|
|
390
|
+
* - an authorization carrying a v3 nonce has nowhere to climb to. It is checked against the base
|
|
391
|
+
* list, the digest does not match, and it fails. An x402 struct version is not automatically
|
|
392
|
+
* an MPP one, which is the coupling #3258 came from.
|
|
393
|
+
*
|
|
394
|
+
* ⚠️ This is the one RUNTIME verdict this change flips, and it is retroactive: before #3266 the
|
|
395
|
+
* protocol-blind ladder read a v3 MPP authorization's `planId`, selected the v3 list, and the
|
|
396
|
+
* token VERIFIED. An MPP token already minted at `tokenVersion: 3` therefore stops verifying —
|
|
397
|
+
* it is not merely unmintable from here on. `validUntil` is written at mint but never compared
|
|
398
|
+
* against `now` on this path, so nothing expires such a token out of existence; the holder has
|
|
399
|
+
* to re-mint. No supported client can hold one (`@nevermined-io/payments` has never exposed
|
|
400
|
+
* `tokenVersion`, per #3257) and it was already single-settle in practice, since
|
|
401
|
+
* `consumeTokenNonce` is not MPP-aware and spends the nonce on an MPP settle too.
|
|
402
|
+
* - a legacy (unsigned-planId) authorization is REFUSED rather than checked against the v1 list.
|
|
403
|
+
* MPP is born after #1678 and has no pre-planId tokens to stay compatible with; accepting one
|
|
404
|
+
* would hand authority to the tamperable `accepted.planId`.
|
|
405
|
+
*/ const selectMppAuthorizationTypes = (authorization)=>hasSignedPlanId(authorization) ? NeverminedTypedDataTypes : null;
|
|
406
|
+
/**
|
|
407
|
+
* Shared verifier for both protocols, differing only in the EIP-712 domain name and the type
|
|
408
|
+
* list its protocol accepts.
|
|
362
409
|
*
|
|
363
|
-
* EIP-712 hashes the type set, so a token signed over one struct cannot verify against
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
* verify against the matching list. This keeps genuinely-old v1 tokens valid while
|
|
367
|
-
* newer tokens are checked over everything they signed.
|
|
410
|
+
* EIP-712 hashes the type set, so a token signed over one struct cannot verify against another's
|
|
411
|
+
* type list. `selectTypes` is therefore the protocol's whole verification policy: which shapes it
|
|
412
|
+
* recognises, and which it refuses without asking the chain.
|
|
368
413
|
*
|
|
369
|
-
* Downgrade-safe
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
* demote a signed token to shed a claim.
|
|
414
|
+
* Downgrade-safe on every ladder: a v2 token was signed over the with-planId digest, so stripping
|
|
415
|
+
* `authorization.planId` to force the v1 branch changes the digest and the signature fails; a v3
|
|
416
|
+
* token was signed over the with-nonce digest, so stripping the nonce to escape single-use, or
|
|
417
|
+
* stripping `agentId` / `resourceUrl` to escape the seller binding, fails the same way. An
|
|
418
|
+
* attacker cannot demote a signed token to shed a claim.
|
|
375
419
|
*
|
|
376
|
-
* EIP-712 hashes the DOMAIN separator too, which is what makes `domainName` the protocol
|
|
377
|
-
*
|
|
420
|
+
* EIP-712 hashes the DOMAIN separator too, which is what makes `domainName` the protocol boundary
|
|
421
|
+
* (#2640) rather than a label. Not exported: callers go through
|
|
378
422
|
* {@link verifyAgentX402AccessToken} or {@link verifyAgentMppAccessToken}, each with its domain
|
|
379
|
-
* and its
|
|
380
|
-
*
|
|
381
|
-
*/ const verifyAgentAccessToken = async (accessToken, publicClient, domainName,
|
|
423
|
+
* and its selector pinned, so no caller-supplied value can pick which protocol a token is checked
|
|
424
|
+
* against.
|
|
425
|
+
*/ const verifyAgentAccessToken = async (accessToken, publicClient, domainName, selectTypes)=>{
|
|
382
426
|
// `payload` is a union (erc4337 | card-delegation) and this is a public entry point with no
|
|
383
427
|
// guaranteed pre-validation — `validateX402TokenStructure` runs in `apps/api`, not here. A
|
|
384
428
|
// structural precondition, evaluated BEFORE the await: deliberately NOT a try/catch around the
|
|
@@ -388,10 +432,9 @@ export const NeverminedTypedDataTypesV3 = {
|
|
|
388
432
|
if (!erc4337Payload?.authorization?.from || !erc4337Payload.signature) return false;
|
|
389
433
|
const { signature, authorization } = erc4337Payload;
|
|
390
434
|
const subscriberAddress = authorization.from;
|
|
391
|
-
// A
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
if (!allowLegacyV1 && !hasSignedPlanId(authorization)) return false;
|
|
435
|
+
// A shape this protocol does not recognise is refused here, before any chain call.
|
|
436
|
+
const types = selectTypes(authorization);
|
|
437
|
+
if (!types) return false;
|
|
395
438
|
const domain = {
|
|
396
439
|
name: domainName,
|
|
397
440
|
version: '1',
|
|
@@ -401,12 +444,7 @@ export const NeverminedTypedDataTypesV3 = {
|
|
|
401
444
|
const isValid = await publicClient.verifyTypedData({
|
|
402
445
|
address: subscriberAddress,
|
|
403
446
|
signature,
|
|
404
|
-
|
|
405
|
-
// caller-supplied hint: a nonce (#2646) → v3, else a planId (#1678) → v2, else the
|
|
406
|
-
// original v1 list. The branches are mutually exclusive and no branch falls back to
|
|
407
|
-
// another — trying a second type list on failure would erase exactly the downgrade
|
|
408
|
-
// protection the versioning provides.
|
|
409
|
-
types: hasSignedNonce(authorization) ? NeverminedTypedDataTypesV3 : hasSignedPlanId(authorization) ? NeverminedTypedDataTypes : NeverminedTypedDataTypesV1,
|
|
447
|
+
types,
|
|
410
448
|
domain,
|
|
411
449
|
message: authorization,
|
|
412
450
|
primaryType: 'Authorization'
|
|
@@ -414,15 +452,15 @@ export const NeverminedTypedDataTypesV3 = {
|
|
|
414
452
|
return isValid;
|
|
415
453
|
};
|
|
416
454
|
/**
|
|
417
|
-
* Verify an x402 access token signature (see {@link
|
|
418
|
-
* detection and its downgrade-safety).
|
|
455
|
+
* Verify an x402 access token signature (see {@link selectX402AuthorizationTypes} for the
|
|
456
|
+
* v1/v2/v3 type-list detection and its downgrade-safety).
|
|
419
457
|
*
|
|
420
458
|
* Rejects an MPP token: it was signed under a different domain separator, so the digest differs.
|
|
421
459
|
*
|
|
422
460
|
* @param accessToken - The x402 access token to verify
|
|
423
461
|
* @param publicClient - Viem public client
|
|
424
462
|
* @returns true if signature is valid
|
|
425
|
-
*/ export const verifyAgentX402AccessToken = (accessToken, publicClient)=>verifyAgentAccessToken(accessToken, publicClient, NEVERMINED_EIP712_DOMAIN_NAME,
|
|
463
|
+
*/ export const verifyAgentX402AccessToken = (accessToken, publicClient)=>verifyAgentAccessToken(accessToken, publicClient, NEVERMINED_EIP712_DOMAIN_NAME, selectX402AuthorizationTypes);
|
|
426
464
|
/**
|
|
427
465
|
* Verify an MPP access token signature (#2640).
|
|
428
466
|
*
|
|
@@ -432,13 +470,14 @@ export const NeverminedTypedDataTypesV3 = {
|
|
|
432
470
|
* AND downgrade — WHILE the signing key stays server-side. A subscriber holding it can mint under
|
|
433
471
|
* whichever domain they like, so the split constrains the transport, not that holder.
|
|
434
472
|
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
473
|
+
* One struct, no ladder: {@link selectMppAuthorizationTypes} accepts the base struct and nothing
|
|
474
|
+
* else, so a legacy (unsigned-planId) authorization is refused outright and an x402 struct version
|
|
475
|
+
* is not verifiable here just because x402 added it (#3266).
|
|
437
476
|
*
|
|
438
477
|
* @param accessToken - The MPP access token to verify
|
|
439
478
|
* @param publicClient - Viem public client
|
|
440
479
|
* @returns true if signature is valid
|
|
441
|
-
*/ export const verifyAgentMppAccessToken = (accessToken, publicClient)=>verifyAgentAccessToken(accessToken, publicClient, NEVERMINED_MPP_EIP712_DOMAIN_NAME,
|
|
480
|
+
*/ export const verifyAgentMppAccessToken = (accessToken, publicClient)=>verifyAgentAccessToken(accessToken, publicClient, NEVERMINED_MPP_EIP712_DOMAIN_NAME, selectMppAuthorizationTypes);
|
|
442
481
|
/**
|
|
443
482
|
* Validate the structure of an x402 access token
|
|
444
483
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/models/AgentX402AccessToken.ts"],"sourcesContent":["import { keccak256, PublicClient, toBytes, TypedDataDefinition } from 'viem'\nimport { SmartAccount } from 'viem/account-abstraction'\n\n/**\n * x402 Resource information\n */\nexport type X402Resource = {\n url: string\n description?: string\n mimeType?: string\n}\n\n/**\n * Supported x402 payment schemes\n */\nexport type X402Scheme = 'nvm:erc4337' | 'nvm:card-delegation'\n\n/**\n * x402 Accepted scheme fields (common to all schemes)\n */\nexport type X402Accepted = {\n scheme: X402Scheme\n network: string\n planId?: string\n extra: {\n version: string\n agentId?: string\n httpVerb?: string\n }\n}\n\n/**\n * Payload specific to the nvm:erc4337 scheme\n */\nexport type X402Erc4337Payload = {\n signature: `0x${string}`\n authorization: {\n from: `0x${string}`\n sessionKeysProvider: string\n sessionKeys: { id: string; data: string }[]\n /**\n * Signed planId (#1678). Present on v2 tokens (minted after #1678) and\n * covered by the EIP-712 signature, so it is tamper-evident on its own.\n * OPTIONAL on the wire type only for backwards-compat: genuinely-old v1\n * tokens carry no `planId` here and verify against the legacy type list.\n */\n planId?: string\n /**\n * Signed seller/resource binding + one-time nonce (#2646). Present on v3\n * tokens and covered by the EIP-712 signature.\n *\n * `agentId` / `resourceUrl` / `httpVerb` are signed as the EMPTY STRING when\n * the mint had no value for them — EIP-712 has no optional members, so\n * \"absent\" has to be a signed value rather than a missing field. The guard\n * (`isSignedBindingConsistent`) then requires the unsigned envelope copy to\n * be absent too, which is what stops anyone ADDING a binding to a token that\n * was signed without one.\n *\n * `nonce` is always a non-empty random value on a v3 token, and is the\n * single v2/v3 discriminator (see `hasSignedNonce`).\n *\n * All four are OPTIONAL on the wire type only for backwards-compat: v1/v2\n * tokens carry none of them and verify against their own type lists.\n */\n agentId?: string\n resourceUrl?: string\n httpVerb?: string\n nonce?: string\n }\n}\n\n/**\n * Payload specific to the nvm:card-delegation scheme\n */\nexport type X402CardDelegationPayload = {\n token: string\n}\n\n/**\n * x402 Access Token aligned with x402 v2 spec\n *\n * Key changes from previous version:\n * - `subscriberAddress` moved to `payload.authorization.from`\n * - `planId` is now in `accepted` (optional — required for erc4337, optional for card-delegation)\n * - `agentId` moved to `accepted.extra.agentId` (optional)\n * - `scheme` and `network` moved to `accepted`\n * - Added `resource` and `extensions` for x402 v2 alignment\n */\nexport type AgentX402AccessToken = {\n x402Version: 2\n resource?: X402Resource\n accepted: X402Accepted\n payload: X402Erc4337Payload | X402CardDelegationPayload\n validUntil?: number\n extensions: Record<string, unknown>\n}\n\n/**\n * EIP-712 domain name for the x402 protocol — the original, unchanged value.\n */\nexport const NEVERMINED_EIP712_DOMAIN_NAME = 'Nevermined' as const\n\n/**\n * EIP-712 domain name for the MPP protocol (#2640).\n *\n * This constant IS the isolation between the two protocols. EIP-712 hashes the domain separator\n * into the signed digest, so a token minted under `Nevermined-MPP` cannot verify against the x402\n * verifier and vice versa — cross-protocol replay and downgrade are closed by construction.\n *\n * A plaintext `scheme`/`protocol` field would NOT do this for the SECURITY boundary:\n * `accepted.scheme` and `accepted.network` sit OUTSIDE the signature (see {@link X402Accepted}), so\n * they are forgeable. Only the domain is covered by the signature without touching the signed\n * struct.\n *\n * That objection does NOT extend to protocol ROUTING. The two envelopes are byte-identical apart\n * from the signature — `accepted.scheme` is `nvm:erc4337` for both — so a dispatcher holding a\n * decoded token has no signal for which verifier to call. If #2641 needs one, add an UNSIGNED hint\n * (`accepted.extra.protocol`) and document it as **routing only — NEVER the security boundary**:\n * forging it can only select the wrong verifier, and the wrong verifier returns `false`.\n * What is forbidden is trying both verifiers in turn — that collapses this isolation to zero.\n *\n * The signed `Authorization` struct is deliberately IDENTICAL for both protocols — do not add a\n * protocol field to it. Changing the struct would fork the type list and break the v1/v2\n * back-compat that `verifyAgentX402AccessToken` depends on, for no security gain.\n */\nexport const NEVERMINED_MPP_EIP712_DOMAIN_NAME = 'Nevermined-MPP' as const\n\n/** The EIP-712 domain name of a Nevermined access token — one per protocol. */\nexport type NeverminedEip712DomainName =\n | typeof NEVERMINED_EIP712_DOMAIN_NAME\n | typeof NEVERMINED_MPP_EIP712_DOMAIN_NAME\n\n/**\n * Defaults to the x402 domain name so the bare `NeverminedTypedDataDomain` keeps resolving to\n * `name: 'Nevermined'` for existing SDK consumers — widening it to the union would be a\n * source-breaking change to a published package (a `const n: 'Nevermined' = domain.name` stops\n * compiling), and `projectsRelationship: \"fixed\"` makes a core-kit major a workspace-wide major.\n */\nexport type NeverminedTypedDataDomain<\n TName extends NeverminedEip712DomainName = typeof NEVERMINED_EIP712_DOMAIN_NAME,\n> = {\n name: TName\n version: '1'\n chainId: number\n verifyingContract: `0x${string}`\n}\n\n/**\n * EIP-712 `Authorization` struct WITH a signed `planId` (#1678).\n *\n * `planId` is appended LAST so the change is purely additive to the EIP-712\n * encoding of the legacy (v1) struct. Signing the planId makes the token's plan\n * tamper-evident on its own — defense-in-depth atop the resource server's\n * `validateAcceptedMatchesRequirements` demand-match, which remains the primary\n * gate. See `NeverminedTypedDataTypesV1` for the legacy struct that pre-#1678\n * tokens were signed over; both shapes verify against the same signer.\n */\nexport type NeverminedTypedData = {\n SessionKey: [{ name: 'id'; type: 'string' }, { name: 'data'; type: 'string' }]\n SessionKeys: [{ name: 'sessionKeys'; type: 'SessionKey[]' }]\n Authorization: [\n { name: 'from'; type: 'address' },\n { name: 'sessionKeysProvider'; type: 'string' },\n { name: 'sessionKeys'; type: 'SessionKey[]' },\n { name: 'planId'; type: 'string' },\n ]\n}\n\nexport const NeverminedTypedDataTypes: NeverminedTypedData = {\n SessionKey: [\n { name: 'id', type: 'string' },\n { name: 'data', type: 'string' },\n ],\n SessionKeys: [{ name: 'sessionKeys', type: 'SessionKey[]' }],\n Authorization: [\n { name: 'from', type: 'address' },\n { name: 'sessionKeysProvider', type: 'string' },\n { name: 'sessionKeys', type: 'SessionKey[]' },\n { name: 'planId', type: 'string' },\n ],\n}\n\n/**\n * Legacy (v1) EIP-712 `Authorization` struct — WITHOUT a signed `planId`.\n *\n * Retained so `verifyAgentX402AccessToken` can still validate genuinely-old\n * tokens minted before #1678 (their `authorization` carries no `planId`, and\n * EIP-712 hashes the type set, so they only verify against THIS type list).\n * Mint always produces the v2 (with-planId) shape going forward.\n */\nexport type NeverminedTypedDataV1 = {\n SessionKey: [{ name: 'id'; type: 'string' }, { name: 'data'; type: 'string' }]\n SessionKeys: [{ name: 'sessionKeys'; type: 'SessionKey[]' }]\n Authorization: [\n { name: 'from'; type: 'address' },\n { name: 'sessionKeysProvider'; type: 'string' },\n { name: 'sessionKeys'; type: 'SessionKey[]' },\n ]\n}\n\nexport const NeverminedTypedDataTypesV1: NeverminedTypedDataV1 = {\n SessionKey: [\n { name: 'id', type: 'string' },\n { name: 'data', type: 'string' },\n ],\n SessionKeys: [{ name: 'sessionKeys', type: 'SessionKey[]' }],\n Authorization: [\n { name: 'from', type: 'address' },\n { name: 'sessionKeysProvider', type: 'string' },\n { name: 'sessionKeys', type: 'SessionKey[]' },\n ],\n}\n\n/**\n * EIP-712 `Authorization` struct v3 (#2646) — WITH the seller/resource binding and\n * a one-time `nonce`, appended after `planId`.\n *\n * Append-only, exactly as #1678 appended `planId` to the v1 struct: the four new\n * members come LAST, so the change is purely additive to the v2 encoding and the\n * older type lists keep verifying the tokens that were signed over them.\n *\n * What each member buys:\n * - `agentId` + `resourceUrl` + `httpVerb` remove the token's CROSS-SELLER\n * property. Before v3 these lived only in `accepted.extra` / `resource`, which\n * sit outside the signature and are demand-matched against a `paymentRequired`\n * the SELLER supplies — so seller A could present a token minted for seller B\n * on the same plan and nothing in the signature contradicted it.\n * - `nonce` removes the BEARER property: a v3 token is single-use at the\n * signature layer, enforced by the replay store in `apps/api` at settle time,\n * independently of any burn-level dedupe.\n *\n * A member the mint had no value for is signed as `''` — EIP-712 has no optional\n * struct members, so absence must itself be a signed value. `isSignedBindingConsistent`\n * pairs that with \"the unsigned copy must be absent too\", which is what prevents\n * ADDING a binding to a token signed without one.\n */\nexport type NeverminedTypedDataV3 = {\n SessionKey: [{ name: 'id'; type: 'string' }, { name: 'data'; type: 'string' }]\n SessionKeys: [{ name: 'sessionKeys'; type: 'SessionKey[]' }]\n Authorization: [\n { name: 'from'; type: 'address' },\n { name: 'sessionKeysProvider'; type: 'string' },\n { name: 'sessionKeys'; type: 'SessionKey[]' },\n { name: 'planId'; type: 'string' },\n { name: 'agentId'; type: 'string' },\n { name: 'resourceUrl'; type: 'string' },\n { name: 'httpVerb'; type: 'string' },\n { name: 'nonce'; type: 'string' },\n ]\n}\n\nexport const NeverminedTypedDataTypesV3: NeverminedTypedDataV3 = {\n SessionKey: [\n { name: 'id', type: 'string' },\n { name: 'data', type: 'string' },\n ],\n SessionKeys: [{ name: 'sessionKeys', type: 'SessionKey[]' }],\n Authorization: [\n { name: 'from', type: 'address' },\n { name: 'sessionKeysProvider', type: 'string' },\n { name: 'sessionKeys', type: 'SessionKey[]' },\n { name: 'planId', type: 'string' },\n { name: 'agentId', type: 'string' },\n { name: 'resourceUrl', type: 'string' },\n { name: 'httpVerb', type: 'string' },\n { name: 'nonce', type: 'string' },\n ],\n}\n\n/**\n * Legacy (v1) signed message shape — no `planId`. Mirrors `NeverminedTypedDataTypesV1`.\n * Only genuinely-old tokens carry this shape; mint never produces it.\n */\nexport type NeverminedTypedDataMessageV1 = {\n from: `0x${string}`\n sessionKeysProvider: string\n sessionKeys: { id: string; data: string }[]\n}\n\n/**\n * The v2 signed message shape — `planId` is REQUIRED. Mint always produces this\n * shape (#1678), so `planId` is non-optional here; the v1 variant above models\n * the legacy no-planId message. Mirrors `NeverminedTypedDataTypes`.\n */\nexport type NeverminedTypedDataMessage = NeverminedTypedDataMessageV1 & {\n /** Signed planId (#1678) — always present on v2 (minted) tokens. */\n planId: string\n}\n\n/**\n * The v3 signed message shape (#2646) — every binding member is REQUIRED, with\n * `''` standing for \"the mint had no value for this\". Mirrors\n * {@link NeverminedTypedDataTypesV3}.\n */\nexport type NeverminedTypedDataMessageV3 = NeverminedTypedDataMessage & {\n /** Signed agentId, or `''` when the token is not bound to an agent. */\n agentId: string\n /** Signed `resource.url`, or `''` when the token is not bound to a resource. */\n resourceUrl: string\n /** Signed HTTP verb, or `''` when the token is not bound to a verb. */\n httpVerb: string\n /** One-time nonce — always a non-empty random value on a v3 token. */\n nonce: string\n}\n\n/**\n * The EIP-712 struct version a token is signed over.\n *\n * `1` is not mintable — it only exists as a verification branch for tokens minted\n * before #1678. New tokens are v2 (the default, reusable, pre-#2646 semantics) or\n * v3 (seller/resource-bound and single-use).\n */\nexport type NeverminedTokenVersion = 2 | 3\n\n/**\n * Options for generating an x402 access token\n */\nexport type GenerateX402TokenOptions = {\n subscriber: SmartAccount\n orderSessionKey: string\n redeemSessionKey: string\n publicClient: PublicClient\n planId: string\n /** Scheme version (e.g., '1') - stored in accepted.extra.version */\n schemeVersion?: string\n agentId?: string\n resource?: X402Resource\n httpVerb?: string\n /** Token expiration timestamp in seconds (Unix epoch) */\n validUntil?: number\n /**\n * EIP-712 struct version to sign (#2646). Defaults to `2` — the reusable,\n * pre-#2646 shape — so existing callers are untouched and there is no flag day.\n *\n * `3` binds the token to `agentId` / `resource.url` / `httpVerb` and adds a\n * one-time nonce, which the resource server consumes at settle. A v3 token is\n * therefore SINGLE-USE: a caller that opts in must mint one per paid request.\n *\n * Applies identically to BOTH protocols, and MPP does not pin v3 even though it has no\n * pre-#2646 tokens in the wild to stay compatible with. The reason is GRANULARITY, not\n * retry safety (that is #2667's ledger, a separate layer): one MPP access token is\n * reused across MANY challenges, which are genuinely distinct operations rather than\n * retries of one. A per-token nonce would therefore kill every MPP buyer's second\n * challenge. So MPP crosses to v3 on the same schedule as x402 — when the SDKs mint\n * one token per paid request.\n */\n tokenVersion?: NeverminedTokenVersion\n}\n\n/**\n * The domain's `chainId` is a load-bearing part of the separator, and viem drops an `undefined`\n * one from the `EIP712Domain` type list — hashing IDENTICALLY to omitting the field, with no error.\n * A chainless `publicClient` (`createPublicClient({ transport })` with no `chain`, a shape that\n * already exists in this repo) would therefore mint a digest valid on every chain at once. Fail\n * loudly instead: this is a client misconfiguration, never a token defect.\n */\nconst requireChainId = (publicClient: PublicClient, caller: string): number => {\n const chainId = publicClient.chain?.id\n if (typeof chainId !== 'number') {\n throw new Error(`${caller}: publicClient has no chain — the EIP-712 domain needs a chainId`)\n }\n return chainId\n}\n\n/**\n * 32 bytes of CSPRNG output, hex-encoded — the one-time nonce of a v3 token (#2646).\n *\n * `globalThis.crypto` (WebCrypto) rather than node's `crypto` module so core-kit keeps\n * working in the browser, where this package is also consumed. 256 bits makes a\n * collision in the resource server's replay store (a UNIQUE column) unreachable, so a\n * legitimate mint can never be refused as someone else's replay.\n */\nconst generateTokenNonce = (): string => {\n const bytes = new Uint8Array(32)\n globalThis.crypto.getRandomValues(bytes)\n return `0x${Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')}`\n}\n\n/**\n * Shared minter for both protocols. The ONLY difference between an x402 token and an MPP token is\n * `domainName` — same signed struct, same envelope, same session keys. Keeping one implementation\n * is deliberate: two copies would drift, and a drifted MPP minter that happened to reproduce the\n * x402 digest would silently reopen the cross-protocol replay this domain split exists to close.\n *\n * Not exported — callers go through {@link generateAgentX402AccessToken} or\n * {@link generateAgentMppAccessToken}, each with its domain pinned, so a protocol can never be\n * selected by a caller-supplied value.\n */\nconst generateAgentAccessToken = async (\n options: GenerateX402TokenOptions,\n domainName: NeverminedEip712DomainName,\n): Promise<AgentX402AccessToken> => {\n const {\n subscriber,\n orderSessionKey,\n redeemSessionKey,\n publicClient,\n planId,\n schemeVersion = '1',\n agentId,\n resource,\n httpVerb,\n validUntil,\n } = options\n const tokenVersion: NeverminedTokenVersion = options.tokenVersion ?? 2\n\n // A v2 token MUST carry a non-empty signed planId (#1678) — an empty/blank\n // planId would otherwise be signed into `authorization.planId` and could be\n // misread as a \"present but empty\" signed claim, blurring the v1/v2 boundary\n // that verification keys off genuine presence. erc4337 already requires a\n // planId upstream (`validateX402TokenStructure`), so this is a hard invariant.\n if (typeof planId !== 'string' || planId.trim() === '') {\n throw new Error('Nevermined access token: planId is required and must be a non-empty string')\n }\n\n const chainId = requireChainId(publicClient, 'Nevermined access token mint')\n\n // hash the session keys\n const redeemSessionKeyHash = keccak256(toBytes(redeemSessionKey))\n const orderSessionKeyHash = keccak256(toBytes(orderSessionKey))\n\n // generate the message with `from` address and the SIGNED planId (#1678).\n // Including `planId` here puts it inside the EIP-712 digest, so it cannot be\n // altered without invalidating the signature.\n const messageV2: NeverminedTypedDataMessage = {\n from: subscriber.address,\n sessionKeysProvider: 'zerodev',\n sessionKeys: [\n {\n id: 'order',\n data: orderSessionKeyHash,\n },\n {\n id: 'redeem',\n data: redeemSessionKeyHash,\n },\n ],\n planId,\n }\n\n // v3 (#2646) appends the seller/resource binding and the one-time nonce. A member\n // the caller left undefined is signed as '' — EIP-712 has no optional members, so\n // \"not bound\" must itself be a signed value; `isSignedBindingConsistent` then holds\n // the unsigned envelope copy to the same absence.\n const message: NeverminedTypedDataMessage | NeverminedTypedDataMessageV3 =\n tokenVersion === 3\n ? {\n ...messageV2,\n agentId: agentId ?? '',\n resourceUrl: resource?.url ?? '',\n httpVerb: httpVerb ?? '',\n nonce: generateTokenNonce(),\n }\n : messageV2\n\n const domain: NeverminedTypedDataDomain<NeverminedEip712DomainName> = {\n name: domainName,\n version: '1',\n chainId,\n verifyingContract: subscriber.address,\n }\n\n // generate the eip712 data. EIP-712 hashes the TYPE SET, so the type list and the\n // message shape must be chosen together — a v3 message signed over the v2 list\n // would produce a digest no verifier reproduces.\n const eip712Data = {\n domain,\n\n types: tokenVersion === 3 ? NeverminedTypedDataTypesV3 : NeverminedTypedDataTypes,\n primaryType: 'Authorization',\n message,\n } as TypedDataDefinition<NeverminedTypedData | NeverminedTypedDataV3, 'Authorization'>\n\n const signature = await subscriber.signTypedData(eip712Data)\n\n // Network in CAIP-2 format (eip155:chainId) — always resolvable, `requireChainId` already ran.\n const network = `eip155:${chainId}`\n\n return {\n x402Version: 2,\n ...(resource && { resource }),\n accepted: {\n scheme: 'nvm:erc4337',\n network,\n planId,\n extra: {\n version: schemeVersion,\n ...(agentId && { agentId }),\n ...(httpVerb && { httpVerb }),\n },\n },\n payload: {\n signature,\n authorization: message,\n },\n ...(validUntil !== undefined && { validUntil }),\n extensions: {},\n }\n}\n\n/**\n * Generate an x402 access token aligned with x402 v2 spec.\n *\n * @param options - Token generation options\n * @returns x402 access token with EIP-712 signature\n */\nexport const generateAgentX402AccessToken = (\n options: GenerateX402TokenOptions,\n): Promise<AgentX402AccessToken> => generateAgentAccessToken(options, NEVERMINED_EIP712_DOMAIN_NAME)\n\n/**\n * Generate a NATIVE MPP access token (#2640) — the same token shape signed under the\n * `Nevermined-MPP` EIP-712 domain.\n *\n * Native, not a wrapper: the revised Tier A design (#2024) deliberately does NOT embed an x402\n * token inside an MPP credential, because unwrapping it would hand the holder a token that still\n * settles on the public x402 path.\n *\n * @param options - Token generation options (identical to the x402 minter)\n * @returns MPP access token with EIP-712 signature over the MPP domain. The envelope is IDENTICAL\n * to an x402 token's, `accepted.scheme: 'nvm:erc4337'` included — only the signature differs, so\n * the protocol is fixed by the endpoint, not readable off the wire. See\n * {@link NEVERMINED_MPP_EIP712_DOMAIN_NAME} on routing vs. the security boundary.\n */\nexport const generateAgentMppAccessToken = (\n options: GenerateX402TokenOptions,\n): Promise<AgentX402AccessToken> =>\n generateAgentAccessToken(options, NEVERMINED_MPP_EIP712_DOMAIN_NAME)\n\n/**\n * True when `authorization` carries a GENUINE signed planId (#1678) — a present,\n * non-empty string. This is the single v1/v2 discriminator: verification keys off\n * it to choose the matching EIP-712 type list, the handler keys off it to decide\n * whether the signed-vs-unsigned consistency guard fires. Keying off genuine\n * presence (not just `'planId' in authorization`) ensures a `planId: ''` / nullish\n * value can never be mistaken for a real signed claim.\n */\nexport const hasSignedPlanId = (\n authorization: X402Erc4337Payload['authorization'],\n): authorization is X402Erc4337Payload['authorization'] & { planId: string } => {\n return typeof authorization?.planId === 'string' && authorization.planId.trim() !== ''\n}\n\n/**\n * The signed-vs-unsigned planId consistency invariant (#1678), co-located with\n * `hasSignedPlanId` so signer, verifier, and the API handler share ONE\n * definition (no drift between where the planId is signed and where the guard\n * enforces it).\n *\n * For a v2 token (signed planId present) the tamperable unsigned `accepted.planId`\n * MUST exactly equal the signed `authorization.planId` — anything else is a\n * post-signing edit of the outer JSON. Returns `true` for v1 tokens (no signed\n * planId): there is no signed value to be consistent with, so the guard does not\n * apply (the caller handles the legacy fallback separately). The API handler maps\n * a `false` result to its forgery-gated rejection.\n */\nexport const isSignedPlanIdConsistent = (\n authorization: X402Erc4337Payload['authorization'],\n acceptedPlanId: string | null | undefined,\n): boolean => {\n if (!hasSignedPlanId(authorization)) return true\n return acceptedPlanId === authorization.planId\n}\n\n/**\n * True when `authorization` carries a GENUINE one-time nonce (#2646) — a present,\n * non-empty string. This is the single v2/v3 discriminator, the exact counterpart of\n * {@link hasSignedPlanId} for the v1/v2 boundary: verification keys off it to choose\n * the matching EIP-712 type list, and the resource server keys off it to decide\n * whether the binding guard and the single-use replay store apply.\n *\n * `nonce` is the discriminator rather than one of the binding members because it is\n * the only v3 member that is always non-empty — `agentId` / `resourceUrl` / `httpVerb`\n * are legitimately signed as `''` when the mint had no value for them.\n */\nexport const hasSignedNonce = (\n authorization: X402Erc4337Payload['authorization'],\n): authorization is X402Erc4337Payload['authorization'] & { nonce: string } => {\n return typeof authorization?.nonce === 'string' && authorization.nonce.trim() !== ''\n}\n\n/** The unsigned envelope values a v3 binding is checked against. */\nexport type SignedBindingEnvelope = {\n /** `accepted.extra.agentId` */\n agentId: string | null | undefined\n /** `resource.url` */\n resourceUrl: string | null | undefined\n /** `accepted.extra.httpVerb` */\n httpVerb: string | null | undefined\n}\n\n/**\n * The signed-vs-unsigned BINDING invariant (#2646), co-located with\n * {@link hasSignedNonce} so signer, verifier and the API handler share ONE definition —\n * the same reason {@link isSignedPlanIdConsistent} lives here.\n *\n * For a v3 token every binding member must agree with its unsigned envelope copy:\n * - a signed non-empty value must equal the envelope value EXACTLY. Both are our own\n * mint's output, so exact equality is right; the fuzzier, query-insensitive URL\n * comparison belongs to the demand-match against the SELLER-supplied\n * `paymentRequired`, which is a different question.\n * - a signed `''` means \"not bound\", so the envelope copy must be absent (or empty)\n * too. This is the half that stops someone ADDING an `agentId` to a token that was\n * signed without one — without it, `''` would be a hole rather than a claim.\n *\n * Returns `true` for v1/v2 tokens: there is no signed binding to be consistent with,\n * and the caller handles those on the legacy path. The API handler maps a `false`\n * result to its forgery-gated rejection, exactly as it does for the planId guard.\n */\nexport const isSignedBindingConsistent = (\n authorization: X402Erc4337Payload['authorization'],\n envelope: SignedBindingEnvelope,\n): boolean => {\n if (!hasSignedNonce(authorization)) return true\n\n const matches = (signed: string | undefined, unsigned: string | null | undefined): boolean => {\n const signedValue = signed ?? ''\n // Absence and '' are the same claim on both sides: the mint omits an empty member\n // from the envelope entirely, so an omitted field is what a signed '' looks like.\n if (signedValue === '') return unsigned === undefined || unsigned === null || unsigned === ''\n return unsigned === signedValue\n }\n\n return (\n matches(authorization.agentId, envelope.agentId) &&\n matches(authorization.resourceUrl, envelope.resourceUrl) &&\n matches(authorization.httpVerb, envelope.httpVerb)\n )\n}\n\n/**\n * Shared verifier for both protocols, differing only in the EIP-712 domain name.\n *\n * EIP-712 hashes the type set, so a token signed over one struct cannot verify against\n * another's type list. We therefore detect which shape the token carries — genuine\n * signed nonce → v3 (#2646), else genuine signed planId → v2 (#1678), else v1 — and\n * verify against the matching list. This keeps genuinely-old v1 tokens valid while\n * newer tokens are checked over everything they signed.\n *\n * Downgrade-safe, and the property extends to v3 unchanged: a v2 token was signed over\n * the with-planId digest, so stripping `authorization.planId` to force the v1 branch\n * changes the digest and the signature fails; a v3 token was signed over the with-nonce\n * digest, so stripping the nonce to escape single-use, or stripping `agentId` /\n * `resourceUrl` to escape the seller binding, fails the same way. An attacker cannot\n * demote a signed token to shed a claim.\n *\n * EIP-712 hashes the DOMAIN separator too, which is what makes `domainName` the protocol\n * boundary (#2640) rather than a label. Not exported: callers go through\n * {@link verifyAgentX402AccessToken} or {@link verifyAgentMppAccessToken}, each with its domain\n * and its `allowLegacyV1` stance pinned, so no caller-supplied value can pick which protocol a\n * token is checked against.\n */\nconst verifyAgentAccessToken = async (\n accessToken: AgentX402AccessToken,\n publicClient: PublicClient,\n domainName: NeverminedEip712DomainName,\n allowLegacyV1: boolean,\n): Promise<boolean> => {\n // `payload` is a union (erc4337 | card-delegation) and this is a public entry point with no\n // guaranteed pre-validation — `validateX402TokenStructure` runs in `apps/api`, not here. A\n // structural precondition, evaluated BEFORE the await: deliberately NOT a try/catch around the\n // verification itself, which must keep letting an RPC fault propagate rather than laundering it\n // into a `false` the caller reads as a forgery.\n const erc4337Payload = accessToken?.payload as X402Erc4337Payload | undefined\n if (!erc4337Payload?.authorization?.from || !erc4337Payload.signature) return false\n\n const { signature, authorization } = erc4337Payload\n const subscriberAddress = authorization.from\n\n // A v1 (unsigned-planId) token leaves `isSignedPlanIdConsistent` inert, so the tamperable\n // `accepted.planId` becomes authoritative. That is a deliberate #1678 back-compat concession on\n // x402 — a protocol with no legacy tokens must not inherit it.\n if (!allowLegacyV1 && !hasSignedPlanId(authorization)) return false\n\n const domain: NeverminedTypedDataDomain<NeverminedEip712DomainName> = {\n name: domainName,\n version: '1',\n chainId: requireChainId(publicClient, 'Nevermined access token verify'),\n verifyingContract: subscriberAddress,\n }\n const isValid = await publicClient.verifyTypedData({\n address: subscriberAddress,\n signature,\n // One branch per struct version, selected by GENUINE presence and never by a\n // caller-supplied hint: a nonce (#2646) → v3, else a planId (#1678) → v2, else the\n // original v1 list. The branches are mutually exclusive and no branch falls back to\n // another — trying a second type list on failure would erase exactly the downgrade\n // protection the versioning provides.\n types: hasSignedNonce(authorization)\n ? NeverminedTypedDataTypesV3\n : hasSignedPlanId(authorization)\n ? NeverminedTypedDataTypes\n : NeverminedTypedDataTypesV1,\n domain,\n message: authorization,\n primaryType: 'Authorization',\n })\n return isValid\n}\n\n/**\n * Verify an x402 access token signature (see {@link verifyAgentAccessToken} for the v1/v2 type-list\n * detection and its downgrade-safety).\n *\n * Rejects an MPP token: it was signed under a different domain separator, so the digest differs.\n *\n * @param accessToken - The x402 access token to verify\n * @param publicClient - Viem public client\n * @returns true if signature is valid\n */\nexport const verifyAgentX402AccessToken = (\n accessToken: AgentX402AccessToken,\n publicClient: PublicClient,\n): Promise<boolean> =>\n verifyAgentAccessToken(accessToken, publicClient, NEVERMINED_EIP712_DOMAIN_NAME, true)\n\n/**\n * Verify an MPP access token signature (#2640).\n *\n * Rejects an x402 token for the same reason the x402 verifier rejects an MPP one — the domain\n * separator is part of the signed digest. This is the property the whole Tier A isolation rests on:\n * a credential harvested from one protocol is inert on the other, closing cross-protocol replay\n * AND downgrade — WHILE the signing key stays server-side. A subscriber holding it can mint under\n * whichever domain they like, so the split constrains the transport, not that holder.\n *\n * v2-only: unlike the x402 verifier, this rejects a legacy (unsigned-planId) authorization\n * outright. MPP is born with #2640 and has no pre-#1678 tokens to be compatible with.\n *\n * @param accessToken - The MPP access token to verify\n * @param publicClient - Viem public client\n * @returns true if signature is valid\n */\nexport const verifyAgentMppAccessToken = (\n accessToken: AgentX402AccessToken,\n publicClient: PublicClient,\n): Promise<boolean> =>\n verifyAgentAccessToken(accessToken, publicClient, NEVERMINED_MPP_EIP712_DOMAIN_NAME, false)\n\n/**\n * Validation result for x402 token structure\n */\nexport type X402TokenValidationResult = {\n valid: boolean\n errors: string[]\n}\n\n/**\n * Validate the structure of an x402 access token\n *\n * @param token - The token to validate (unknown type for safety)\n * @returns Validation result with errors if any\n */\nconst SUPPORTED_SCHEMES: X402Scheme[] = ['nvm:erc4337', 'nvm:card-delegation']\n\nexport const validateX402TokenStructure = (token: unknown): X402TokenValidationResult => {\n const errors: string[] = []\n\n if (!token || typeof token !== 'object') {\n return { valid: false, errors: ['Token must be an object'] }\n }\n\n const t = token as Record<string, unknown>\n\n // Validate x402Version\n if (t.x402Version !== 2) {\n errors.push('x402Version must be 2')\n }\n\n // Validate accepted object\n if (!t.accepted || typeof t.accepted !== 'object') {\n errors.push('accepted is required')\n } else {\n const accepted = t.accepted as Record<string, unknown>\n const scheme = accepted.scheme as string\n\n if (!SUPPORTED_SCHEMES.includes(scheme as X402Scheme)) {\n errors.push(`scheme must be one of: ${SUPPORTED_SCHEMES.join(', ')}`)\n }\n\n if (!accepted.network || typeof accepted.network !== 'string') {\n errors.push('network is required in accepted')\n }\n\n // Validate extra\n if (!accepted.extra || typeof accepted.extra !== 'object') {\n errors.push('extra is required in accepted')\n } else {\n const extra = accepted.extra as Record<string, unknown>\n if (!extra.version || typeof extra.version !== 'string') {\n errors.push('version is required in accepted.extra')\n }\n }\n\n // Scheme-specific accepted validation\n if (scheme === 'nvm:erc4337') {\n if (!accepted.planId || typeof accepted.planId !== 'string') {\n errors.push('planId is required in accepted for nvm:erc4337')\n }\n }\n }\n\n // Validate payload\n if (!t.payload || typeof t.payload !== 'object') {\n errors.push('payload is required')\n } else {\n const accepted = t.accepted as Record<string, unknown> | undefined\n const scheme = accepted?.scheme as string | undefined\n const payload = t.payload as Record<string, unknown>\n\n if (scheme === 'nvm:erc4337') {\n if (!payload.signature || typeof payload.signature !== 'string') {\n errors.push('signature is required in payload')\n }\n\n if (!payload.authorization || typeof payload.authorization !== 'object') {\n errors.push('authorization is required in payload')\n } else {\n const auth = payload.authorization as Record<string, unknown>\n\n if (!auth.from || typeof auth.from !== 'string') {\n errors.push('from address is required in payload.authorization')\n } else if (!auth.from.startsWith('0x')) {\n errors.push('from must be a valid hex address')\n }\n\n if (!auth.sessionKeysProvider || typeof auth.sessionKeysProvider !== 'string') {\n errors.push('sessionKeysProvider is required in payload.authorization')\n }\n\n if (!Array.isArray(auth.sessionKeys)) {\n errors.push('sessionKeys array is required in payload.authorization')\n }\n }\n } else if (scheme === 'nvm:card-delegation') {\n if (!payload.token || typeof payload.token !== 'string') {\n errors.push('token is required in payload for nvm:card-delegation')\n }\n }\n }\n\n // Validate extensions (must be present, even if empty)\n if (t.extensions === undefined) {\n errors.push('extensions is required (can be empty object)')\n }\n\n return { valid: errors.length === 0, errors }\n}\n\nexport const b64EncodeX402AccessToken = (accessToken: AgentX402AccessToken): string => {\n return Buffer.from(JSON.stringify(accessToken)).toString('base64')\n}\n\nexport const b64DecodeX402AccessToken = (encoded: string): AgentX402AccessToken => {\n return JSON.parse(Buffer.from(encoded, 'base64').toString('utf-8'))\n}\n"],"names":["keccak256","toBytes","NEVERMINED_EIP712_DOMAIN_NAME","NEVERMINED_MPP_EIP712_DOMAIN_NAME","NeverminedTypedDataTypes","SessionKey","name","type","SessionKeys","Authorization","NeverminedTypedDataTypesV1","NeverminedTypedDataTypesV3","requireChainId","publicClient","caller","chainId","chain","id","Error","generateTokenNonce","bytes","Uint8Array","globalThis","crypto","getRandomValues","Array","from","b","toString","padStart","join","generateAgentAccessToken","options","domainName","subscriber","orderSessionKey","redeemSessionKey","planId","schemeVersion","agentId","resource","httpVerb","validUntil","tokenVersion","trim","redeemSessionKeyHash","orderSessionKeyHash","messageV2","address","sessionKeysProvider","sessionKeys","data","message","resourceUrl","url","nonce","domain","version","verifyingContract","eip712Data","types","primaryType","signature","signTypedData","network","x402Version","accepted","scheme","extra","payload","authorization","undefined","extensions","generateAgentX402AccessToken","generateAgentMppAccessToken","hasSignedPlanId","isSignedPlanIdConsistent","acceptedPlanId","hasSignedNonce","isSignedBindingConsistent","envelope","matches","signed","unsigned","signedValue","verifyAgentAccessToken","accessToken","allowLegacyV1","erc4337Payload","subscriberAddress","isValid","verifyTypedData","verifyAgentX402AccessToken","verifyAgentMppAccessToken","SUPPORTED_SCHEMES","validateX402TokenStructure","token","errors","valid","t","push","includes","auth","startsWith","isArray","length","b64EncodeX402AccessToken","Buffer","JSON","stringify","b64DecodeX402AccessToken","encoded","parse"],"mappings":"AAAA,SAASA,SAAS,EAAgBC,OAAO,QAA6B,OAAM;AAiG5E;;CAEC,GACD,OAAO,MAAMC,gCAAgC,aAAqB;AAElE;;;;;;;;;;;;;;;;;;;;;;CAsBC,GACD,OAAO,MAAMC,oCAAoC,iBAAyB;AA2C1E,OAAO,MAAMC,2BAAgD;IAC3DC,YAAY;QACV;YAAEC,MAAM;YAAMC,MAAM;QAAS;QAC7B;YAAED,MAAM;YAAQC,MAAM;QAAS;KAChC;IACDC,aAAa;QAAC;YAAEF,MAAM;YAAeC,MAAM;QAAe;KAAE;IAC5DE,eAAe;QACb;YAAEH,MAAM;YAAQC,MAAM;QAAU;QAChC;YAAED,MAAM;YAAuBC,MAAM;QAAS;QAC9C;YAAED,MAAM;YAAeC,MAAM;QAAe;QAC5C;YAAED,MAAM;YAAUC,MAAM;QAAS;KAClC;AACH,EAAC;AAoBD,OAAO,MAAMG,6BAAoD;IAC/DL,YAAY;QACV;YAAEC,MAAM;YAAMC,MAAM;QAAS;QAC7B;YAAED,MAAM;YAAQC,MAAM;QAAS;KAChC;IACDC,aAAa;QAAC;YAAEF,MAAM;YAAeC,MAAM;QAAe;KAAE;IAC5DE,eAAe;QACb;YAAEH,MAAM;YAAQC,MAAM;QAAU;QAChC;YAAED,MAAM;YAAuBC,MAAM;QAAS;QAC9C;YAAED,MAAM;YAAeC,MAAM;QAAe;KAC7C;AACH,EAAC;AAwCD,OAAO,MAAMI,6BAAoD;IAC/DN,YAAY;QACV;YAAEC,MAAM;YAAMC,MAAM;QAAS;QAC7B;YAAED,MAAM;YAAQC,MAAM;QAAS;KAChC;IACDC,aAAa;QAAC;YAAEF,MAAM;YAAeC,MAAM;QAAe;KAAE;IAC5DE,eAAe;QACb;YAAEH,MAAM;YAAQC,MAAM;QAAU;QAChC;YAAED,MAAM;YAAuBC,MAAM;QAAS;QAC9C;YAAED,MAAM;YAAeC,MAAM;QAAe;QAC5C;YAAED,MAAM;YAAUC,MAAM;QAAS;QACjC;YAAED,MAAM;YAAWC,MAAM;QAAS;QAClC;YAAED,MAAM;YAAeC,MAAM;QAAS;QACtC;YAAED,MAAM;YAAYC,MAAM;QAAS;QACnC;YAAED,MAAM;YAASC,MAAM;QAAS;KACjC;AACH,EAAC;AAkFD;;;;;;CAMC,GACD,MAAMK,iBAAiB,CAACC,cAA4BC;IAClD,MAAMC,UAAUF,aAAaG,KAAK,EAAEC;IACpC,IAAI,OAAOF,YAAY,UAAU;QAC/B,MAAM,IAAIG,MAAM,GAAGJ,OAAO,gEAAgE,CAAC;IAC7F;IACA,OAAOC;AACT;AAEA;;;;;;;CAOC,GACD,MAAMI,qBAAqB;IACzB,MAAMC,QAAQ,IAAIC,WAAW;IAC7BC,WAAWC,MAAM,CAACC,eAAe,CAACJ;IAClC,OAAO,CAAC,EAAE,EAAEK,MAAMC,IAAI,CAACN,OAAO,CAACO,IAAMA,EAAEC,QAAQ,CAAC,IAAIC,QAAQ,CAAC,GAAG,MAAMC,IAAI,CAAC,KAAK;AAClF;AAEA;;;;;;;;;CASC,GACD,MAAMC,2BAA2B,OAC/BC,SACAC;IAEA,MAAM,EACJC,UAAU,EACVC,eAAe,EACfC,gBAAgB,EAChBvB,YAAY,EACZwB,MAAM,EACNC,gBAAgB,GAAG,EACnBC,OAAO,EACPC,QAAQ,EACRC,QAAQ,EACRC,UAAU,EACX,GAAGV;IACJ,MAAMW,eAAuCX,QAAQW,YAAY,IAAI;IAErE,2EAA2E;IAC3E,4EAA4E;IAC5E,6EAA6E;IAC7E,0EAA0E;IAC1E,+EAA+E;IAC/E,IAAI,OAAON,WAAW,YAAYA,OAAOO,IAAI,OAAO,IAAI;QACtD,MAAM,IAAI1B,MAAM;IAClB;IAEA,MAAMH,UAAUH,eAAeC,cAAc;IAE7C,wBAAwB;IACxB,MAAMgC,uBAAuB7C,UAAUC,QAAQmC;IAC/C,MAAMU,sBAAsB9C,UAAUC,QAAQkC;IAE9C,0EAA0E;IAC1E,6EAA6E;IAC7E,8CAA8C;IAC9C,MAAMY,YAAwC;QAC5CrB,MAAMQ,WAAWc,OAAO;QACxBC,qBAAqB;QACrBC,aAAa;YACX;gBACEjC,IAAI;gBACJkC,MAAML;YACR;YACA;gBACE7B,IAAI;gBACJkC,MAAMN;YACR;SACD;QACDR;IACF;IAEA,kFAAkF;IAClF,kFAAkF;IAClF,oFAAoF;IACpF,kDAAkD;IAClD,MAAMe,UACJT,iBAAiB,IACb;QACE,GAAGI,SAAS;QACZR,SAASA,WAAW;QACpBc,aAAab,UAAUc,OAAO;QAC9Bb,UAAUA,YAAY;QACtBc,OAAOpC;IACT,IACA4B;IAEN,MAAMS,SAAgE;QACpElD,MAAM2B;QACNwB,SAAS;QACT1C;QACA2C,mBAAmBxB,WAAWc,OAAO;IACvC;IAEA,kFAAkF;IAClF,+EAA+E;IAC/E,iDAAiD;IACjD,MAAMW,aAAa;QACjBH;QAEAI,OAAOjB,iBAAiB,IAAIhC,6BAA6BP;QACzDyD,aAAa;QACbT;IACF;IAEA,MAAMU,YAAY,MAAM5B,WAAW6B,aAAa,CAACJ;IAEjD,+FAA+F;IAC/F,MAAMK,UAAU,CAAC,OAAO,EAAEjD,SAAS;IAEnC,OAAO;QACLkD,aAAa;QACb,GAAIzB,YAAY;YAAEA;QAAS,CAAC;QAC5B0B,UAAU;YACRC,QAAQ;YACRH;YACA3B;YACA+B,OAAO;gBACLX,SAASnB;gBACT,GAAIC,WAAW;oBAAEA;gBAAQ,CAAC;gBAC1B,GAAIE,YAAY;oBAAEA;gBAAS,CAAC;YAC9B;QACF;QACA4B,SAAS;YACPP;YACAQ,eAAelB;QACjB;QACA,GAAIV,eAAe6B,aAAa;YAAE7B;QAAW,CAAC;QAC9C8B,YAAY,CAAC;IACf;AACF;AAEA;;;;;CAKC,GACD,OAAO,MAAMC,+BAA+B,CAC1CzC,UACkCD,yBAAyBC,SAAS9B,+BAA8B;AAEpG;;;;;;;;;;;;;CAaC,GACD,OAAO,MAAMwE,8BAA8B,CACzC1C,UAEAD,yBAAyBC,SAAS7B,mCAAkC;AAEtE;;;;;;;CAOC,GACD,OAAO,MAAMwE,kBAAkB,CAC7BL;IAEA,OAAO,OAAOA,eAAejC,WAAW,YAAYiC,cAAcjC,MAAM,CAACO,IAAI,OAAO;AACtF,EAAC;AAED;;;;;;;;;;;;CAYC,GACD,OAAO,MAAMgC,2BAA2B,CACtCN,eACAO;IAEA,IAAI,CAACF,gBAAgBL,gBAAgB,OAAO;IAC5C,OAAOO,mBAAmBP,cAAcjC,MAAM;AAChD,EAAC;AAED;;;;;;;;;;CAUC,GACD,OAAO,MAAMyC,iBAAiB,CAC5BR;IAEA,OAAO,OAAOA,eAAef,UAAU,YAAYe,cAAcf,KAAK,CAACX,IAAI,OAAO;AACpF,EAAC;AAYD;;;;;;;;;;;;;;;;;CAiBC,GACD,OAAO,MAAMmC,4BAA4B,CACvCT,eACAU;IAEA,IAAI,CAACF,eAAeR,gBAAgB,OAAO;IAE3C,MAAMW,UAAU,CAACC,QAA4BC;QAC3C,MAAMC,cAAcF,UAAU;QAC9B,kFAAkF;QAClF,kFAAkF;QAClF,IAAIE,gBAAgB,IAAI,OAAOD,aAAaZ,aAAaY,aAAa,QAAQA,aAAa;QAC3F,OAAOA,aAAaC;IACtB;IAEA,OACEH,QAAQX,cAAc/B,OAAO,EAAEyC,SAASzC,OAAO,KAC/C0C,QAAQX,cAAcjB,WAAW,EAAE2B,SAAS3B,WAAW,KACvD4B,QAAQX,cAAc7B,QAAQ,EAAEuC,SAASvC,QAAQ;AAErD,EAAC;AAED;;;;;;;;;;;;;;;;;;;;;CAqBC,GACD,MAAM4C,yBAAyB,OAC7BC,aACAzE,cACAoB,YACAsD;IAEA,4FAA4F;IAC5F,2FAA2F;IAC3F,+FAA+F;IAC/F,gGAAgG;IAChG,gDAAgD;IAChD,MAAMC,iBAAiBF,aAAajB;IACpC,IAAI,CAACmB,gBAAgBlB,eAAe5C,QAAQ,CAAC8D,eAAe1B,SAAS,EAAE,OAAO;IAE9E,MAAM,EAAEA,SAAS,EAAEQ,aAAa,EAAE,GAAGkB;IACrC,MAAMC,oBAAoBnB,cAAc5C,IAAI;IAE5C,0FAA0F;IAC1F,gGAAgG;IAChG,+DAA+D;IAC/D,IAAI,CAAC6D,iBAAiB,CAACZ,gBAAgBL,gBAAgB,OAAO;IAE9D,MAAMd,SAAgE;QACpElD,MAAM2B;QACNwB,SAAS;QACT1C,SAASH,eAAeC,cAAc;QACtC6C,mBAAmB+B;IACrB;IACA,MAAMC,UAAU,MAAM7E,aAAa8E,eAAe,CAAC;QACjD3C,SAASyC;QACT3B;QACA,6EAA6E;QAC7E,mFAAmF;QACnF,oFAAoF;QACpF,mFAAmF;QACnF,sCAAsC;QACtCF,OAAOkB,eAAeR,iBAClB3D,6BACAgE,gBAAgBL,iBACdlE,2BACAM;QACN8C;QACAJ,SAASkB;QACTT,aAAa;IACf;IACA,OAAO6B;AACT;AAEA;;;;;;;;;CASC,GACD,OAAO,MAAME,6BAA6B,CACxCN,aACAzE,eAEAwE,uBAAuBC,aAAazE,cAAcX,+BAA+B,MAAK;AAExF;;;;;;;;;;;;;;;CAeC,GACD,OAAO,MAAM2F,4BAA4B,CACvCP,aACAzE,eAEAwE,uBAAuBC,aAAazE,cAAcV,mCAAmC,OAAM;AAU7F;;;;;CAKC,GACD,MAAM2F,oBAAkC;IAAC;IAAe;CAAsB;AAE9E,OAAO,MAAMC,6BAA6B,CAACC;IACzC,MAAMC,SAAmB,EAAE;IAE3B,IAAI,CAACD,SAAS,OAAOA,UAAU,UAAU;QACvC,OAAO;YAAEE,OAAO;YAAOD,QAAQ;gBAAC;aAA0B;QAAC;IAC7D;IAEA,MAAME,IAAIH;IAEV,uBAAuB;IACvB,IAAIG,EAAElC,WAAW,KAAK,GAAG;QACvBgC,OAAOG,IAAI,CAAC;IACd;IAEA,2BAA2B;IAC3B,IAAI,CAACD,EAAEjC,QAAQ,IAAI,OAAOiC,EAAEjC,QAAQ,KAAK,UAAU;QACjD+B,OAAOG,IAAI,CAAC;IACd,OAAO;QACL,MAAMlC,WAAWiC,EAAEjC,QAAQ;QAC3B,MAAMC,SAASD,SAASC,MAAM;QAE9B,IAAI,CAAC2B,kBAAkBO,QAAQ,CAAClC,SAAuB;YACrD8B,OAAOG,IAAI,CAAC,CAAC,uBAAuB,EAAEN,kBAAkBhE,IAAI,CAAC,OAAO;QACtE;QAEA,IAAI,CAACoC,SAASF,OAAO,IAAI,OAAOE,SAASF,OAAO,KAAK,UAAU;YAC7DiC,OAAOG,IAAI,CAAC;QACd;QAEA,iBAAiB;QACjB,IAAI,CAAClC,SAASE,KAAK,IAAI,OAAOF,SAASE,KAAK,KAAK,UAAU;YACzD6B,OAAOG,IAAI,CAAC;QACd,OAAO;YACL,MAAMhC,QAAQF,SAASE,KAAK;YAC5B,IAAI,CAACA,MAAMX,OAAO,IAAI,OAAOW,MAAMX,OAAO,KAAK,UAAU;gBACvDwC,OAAOG,IAAI,CAAC;YACd;QACF;QAEA,sCAAsC;QACtC,IAAIjC,WAAW,eAAe;YAC5B,IAAI,CAACD,SAAS7B,MAAM,IAAI,OAAO6B,SAAS7B,MAAM,KAAK,UAAU;gBAC3D4D,OAAOG,IAAI,CAAC;YACd;QACF;IACF;IAEA,mBAAmB;IACnB,IAAI,CAACD,EAAE9B,OAAO,IAAI,OAAO8B,EAAE9B,OAAO,KAAK,UAAU;QAC/C4B,OAAOG,IAAI,CAAC;IACd,OAAO;QACL,MAAMlC,WAAWiC,EAAEjC,QAAQ;QAC3B,MAAMC,SAASD,UAAUC;QACzB,MAAME,UAAU8B,EAAE9B,OAAO;QAEzB,IAAIF,WAAW,eAAe;YAC5B,IAAI,CAACE,QAAQP,SAAS,IAAI,OAAOO,QAAQP,SAAS,KAAK,UAAU;gBAC/DmC,OAAOG,IAAI,CAAC;YACd;YAEA,IAAI,CAAC/B,QAAQC,aAAa,IAAI,OAAOD,QAAQC,aAAa,KAAK,UAAU;gBACvE2B,OAAOG,IAAI,CAAC;YACd,OAAO;gBACL,MAAME,OAAOjC,QAAQC,aAAa;gBAElC,IAAI,CAACgC,KAAK5E,IAAI,IAAI,OAAO4E,KAAK5E,IAAI,KAAK,UAAU;oBAC/CuE,OAAOG,IAAI,CAAC;gBACd,OAAO,IAAI,CAACE,KAAK5E,IAAI,CAAC6E,UAAU,CAAC,OAAO;oBACtCN,OAAOG,IAAI,CAAC;gBACd;gBAEA,IAAI,CAACE,KAAKrD,mBAAmB,IAAI,OAAOqD,KAAKrD,mBAAmB,KAAK,UAAU;oBAC7EgD,OAAOG,IAAI,CAAC;gBACd;gBAEA,IAAI,CAAC3E,MAAM+E,OAAO,CAACF,KAAKpD,WAAW,GAAG;oBACpC+C,OAAOG,IAAI,CAAC;gBACd;YACF;QACF,OAAO,IAAIjC,WAAW,uBAAuB;YAC3C,IAAI,CAACE,QAAQ2B,KAAK,IAAI,OAAO3B,QAAQ2B,KAAK,KAAK,UAAU;gBACvDC,OAAOG,IAAI,CAAC;YACd;QACF;IACF;IAEA,uDAAuD;IACvD,IAAID,EAAE3B,UAAU,KAAKD,WAAW;QAC9B0B,OAAOG,IAAI,CAAC;IACd;IAEA,OAAO;QAAEF,OAAOD,OAAOQ,MAAM,KAAK;QAAGR;IAAO;AAC9C,EAAC;AAED,OAAO,MAAMS,2BAA2B,CAACpB;IACvC,OAAOqB,OAAOjF,IAAI,CAACkF,KAAKC,SAAS,CAACvB,cAAc1D,QAAQ,CAAC;AAC3D,EAAC;AAED,OAAO,MAAMkF,2BAA2B,CAACC;IACvC,OAAOH,KAAKI,KAAK,CAACL,OAAOjF,IAAI,CAACqF,SAAS,UAAUnF,QAAQ,CAAC;AAC5D,EAAC"}
|
|
1
|
+
{"version":3,"sources":["../../src/models/AgentX402AccessToken.ts"],"sourcesContent":["import { keccak256, PublicClient, toBytes, TypedDataDefinition } from 'viem'\nimport { SmartAccount } from 'viem/account-abstraction'\n\n/**\n * x402 Resource information\n */\nexport type X402Resource = {\n url: string\n description?: string\n mimeType?: string\n}\n\n/**\n * Supported x402 payment schemes\n */\nexport type X402Scheme = 'nvm:erc4337' | 'nvm:card-delegation'\n\n/**\n * x402 Accepted scheme fields (common to all schemes)\n */\nexport type X402Accepted = {\n scheme: X402Scheme\n network: string\n planId?: string\n extra: {\n version: string\n agentId?: string\n httpVerb?: string\n }\n}\n\n/**\n * Payload specific to the nvm:erc4337 scheme\n */\nexport type X402Erc4337Payload = {\n signature: `0x${string}`\n authorization: {\n from: `0x${string}`\n sessionKeysProvider: string\n sessionKeys: { id: string; data: string }[]\n /**\n * Signed planId (#1678). Present on v2 tokens (minted after #1678) and\n * covered by the EIP-712 signature, so it is tamper-evident on its own.\n * OPTIONAL on the wire type only for backwards-compat: genuinely-old v1\n * tokens carry no `planId` here and verify against the legacy type list.\n */\n planId?: string\n /**\n * Signed seller/resource binding + one-time nonce (#2646). Present on v3\n * tokens and covered by the EIP-712 signature.\n *\n * `agentId` / `resourceUrl` / `httpVerb` are signed as the EMPTY STRING when\n * the mint had no value for them — EIP-712 has no optional members, so\n * \"absent\" has to be a signed value rather than a missing field. The guard\n * (`isSignedBindingConsistent`) then requires the unsigned envelope copy to\n * be absent too, which is what stops anyone ADDING a binding to a token that\n * was signed without one.\n *\n * `nonce` is always a non-empty random value on a v3 token, and is the\n * single v2/v3 discriminator (see `hasSignedNonce`).\n *\n * All four are OPTIONAL on the wire type only for backwards-compat: v1/v2\n * tokens carry none of them and verify against their own type lists.\n */\n agentId?: string\n resourceUrl?: string\n httpVerb?: string\n nonce?: string\n }\n}\n\n/**\n * Payload specific to the nvm:card-delegation scheme\n */\nexport type X402CardDelegationPayload = {\n token: string\n}\n\n/**\n * x402 Access Token aligned with x402 v2 spec\n *\n * Key changes from previous version:\n * - `subscriberAddress` moved to `payload.authorization.from`\n * - `planId` is now in `accepted` (optional — required for erc4337, optional for card-delegation)\n * - `agentId` moved to `accepted.extra.agentId` (optional)\n * - `scheme` and `network` moved to `accepted`\n * - Added `resource` and `extensions` for x402 v2 alignment\n */\nexport type AgentX402AccessToken = {\n x402Version: 2\n resource?: X402Resource\n accepted: X402Accepted\n payload: X402Erc4337Payload | X402CardDelegationPayload\n validUntil?: number\n extensions: Record<string, unknown>\n}\n\n/**\n * EIP-712 domain name for the x402 protocol — the original, unchanged value.\n */\nexport const NEVERMINED_EIP712_DOMAIN_NAME = 'Nevermined' as const\n\n/**\n * EIP-712 domain name for the MPP protocol (#2640).\n *\n * This constant IS the isolation between the two protocols. EIP-712 hashes the domain separator\n * into the signed digest, so a token minted under `Nevermined-MPP` cannot verify against the x402\n * verifier and vice versa — cross-protocol replay and downgrade are closed by construction.\n *\n * A plaintext `scheme`/`protocol` field would NOT do this for the SECURITY boundary:\n * `accepted.scheme` and `accepted.network` sit OUTSIDE the signature (see {@link X402Accepted}), so\n * they are forgeable. Only the domain is covered by the signature without touching the signed\n * struct.\n *\n * That objection does NOT extend to protocol ROUTING. The two envelopes are byte-identical apart\n * from the signature — `accepted.scheme` is `nvm:erc4337` for both — so a dispatcher holding a\n * decoded token has no signal for which verifier to call. If #2641 needs one, add an UNSIGNED hint\n * (`accepted.extra.protocol`) and document it as **routing only — NEVER the security boundary**:\n * forging it can only select the wrong verifier, and the wrong verifier returns `false`.\n * What is forbidden is trying both verifiers in turn — that collapses this isolation to zero.\n *\n * The signed `Authorization` struct is deliberately IDENTICAL for both protocols — do not add a\n * protocol field to it. Changing the struct would fork the type list and break the v1/v2\n * back-compat that `verifyAgentX402AccessToken` depends on, for no security gain.\n */\nexport const NEVERMINED_MPP_EIP712_DOMAIN_NAME = 'Nevermined-MPP' as const\n\n/** The EIP-712 domain name of a Nevermined access token — one per protocol. */\nexport type NeverminedEip712DomainName =\n | typeof NEVERMINED_EIP712_DOMAIN_NAME\n | typeof NEVERMINED_MPP_EIP712_DOMAIN_NAME\n\n/**\n * Defaults to the x402 domain name so the bare `NeverminedTypedDataDomain` keeps resolving to\n * `name: 'Nevermined'` for existing SDK consumers — widening it to the union would be a\n * source-breaking change to a published package (a `const n: 'Nevermined' = domain.name` stops\n * compiling), and `projectsRelationship: \"fixed\"` makes a core-kit major a workspace-wide major.\n */\nexport type NeverminedTypedDataDomain<\n TName extends NeverminedEip712DomainName = typeof NEVERMINED_EIP712_DOMAIN_NAME,\n> = {\n name: TName\n version: '1'\n chainId: number\n verifyingContract: `0x${string}`\n}\n\n/**\n * EIP-712 `Authorization` struct WITH a signed `planId` (#1678).\n *\n * `planId` is appended LAST so the change is purely additive to the EIP-712\n * encoding of the legacy (v1) struct. Signing the planId makes the token's plan\n * tamper-evident on its own — defense-in-depth atop the resource server's\n * `validateAcceptedMatchesRequirements` demand-match, which remains the primary\n * gate. See `NeverminedTypedDataTypesV1` for the legacy struct that pre-#1678\n * tokens were signed over; both shapes verify against the same signer.\n */\nexport type NeverminedTypedData = {\n SessionKey: [{ name: 'id'; type: 'string' }, { name: 'data'; type: 'string' }]\n SessionKeys: [{ name: 'sessionKeys'; type: 'SessionKey[]' }]\n Authorization: [\n { name: 'from'; type: 'address' },\n { name: 'sessionKeysProvider'; type: 'string' },\n { name: 'sessionKeys'; type: 'SessionKey[]' },\n { name: 'planId'; type: 'string' },\n ]\n}\n\nexport const NeverminedTypedDataTypes: NeverminedTypedData = {\n SessionKey: [\n { name: 'id', type: 'string' },\n { name: 'data', type: 'string' },\n ],\n SessionKeys: [{ name: 'sessionKeys', type: 'SessionKey[]' }],\n Authorization: [\n { name: 'from', type: 'address' },\n { name: 'sessionKeysProvider', type: 'string' },\n { name: 'sessionKeys', type: 'SessionKey[]' },\n { name: 'planId', type: 'string' },\n ],\n}\n\n/**\n * Legacy (v1) EIP-712 `Authorization` struct — WITHOUT a signed `planId`.\n *\n * Retained so `verifyAgentX402AccessToken` can still validate genuinely-old\n * tokens minted before #1678 (their `authorization` carries no `planId`, and\n * EIP-712 hashes the type set, so they only verify against THIS type list).\n * Mint always produces the v2 (with-planId) shape going forward.\n */\nexport type NeverminedTypedDataV1 = {\n SessionKey: [{ name: 'id'; type: 'string' }, { name: 'data'; type: 'string' }]\n SessionKeys: [{ name: 'sessionKeys'; type: 'SessionKey[]' }]\n Authorization: [\n { name: 'from'; type: 'address' },\n { name: 'sessionKeysProvider'; type: 'string' },\n { name: 'sessionKeys'; type: 'SessionKey[]' },\n ]\n}\n\nexport const NeverminedTypedDataTypesV1: NeverminedTypedDataV1 = {\n SessionKey: [\n { name: 'id', type: 'string' },\n { name: 'data', type: 'string' },\n ],\n SessionKeys: [{ name: 'sessionKeys', type: 'SessionKey[]' }],\n Authorization: [\n { name: 'from', type: 'address' },\n { name: 'sessionKeysProvider', type: 'string' },\n { name: 'sessionKeys', type: 'SessionKey[]' },\n ],\n}\n\n/**\n * EIP-712 `Authorization` struct v3 (#2646) — WITH the seller/resource binding and\n * a one-time `nonce`, appended after `planId`.\n *\n * Append-only, exactly as #1678 appended `planId` to the v1 struct: the four new\n * members come LAST, so the change is purely additive to the v2 encoding and the\n * older type lists keep verifying the tokens that were signed over them.\n *\n * What each member buys:\n * - `agentId` + `resourceUrl` + `httpVerb` remove the token's CROSS-SELLER\n * property. Before v3 these lived only in `accepted.extra` / `resource`, which\n * sit outside the signature and are demand-matched against a `paymentRequired`\n * the SELLER supplies — so seller A could present a token minted for seller B\n * on the same plan and nothing in the signature contradicted it.\n * - `nonce` removes the BEARER property: a v3 token is single-use at the\n * signature layer, enforced by the replay store in `apps/api` at settle time,\n * independently of any burn-level dedupe.\n *\n * A member the mint had no value for is signed as `''` — EIP-712 has no optional\n * struct members, so absence must itself be a signed value. `isSignedBindingConsistent`\n * pairs that with \"the unsigned copy must be absent too\", which is what prevents\n * ADDING a binding to a token signed without one.\n */\nexport type NeverminedTypedDataV3 = {\n SessionKey: [{ name: 'id'; type: 'string' }, { name: 'data'; type: 'string' }]\n SessionKeys: [{ name: 'sessionKeys'; type: 'SessionKey[]' }]\n Authorization: [\n { name: 'from'; type: 'address' },\n { name: 'sessionKeysProvider'; type: 'string' },\n { name: 'sessionKeys'; type: 'SessionKey[]' },\n { name: 'planId'; type: 'string' },\n { name: 'agentId'; type: 'string' },\n { name: 'resourceUrl'; type: 'string' },\n { name: 'httpVerb'; type: 'string' },\n { name: 'nonce'; type: 'string' },\n ]\n}\n\nexport const NeverminedTypedDataTypesV3: NeverminedTypedDataV3 = {\n SessionKey: [\n { name: 'id', type: 'string' },\n { name: 'data', type: 'string' },\n ],\n SessionKeys: [{ name: 'sessionKeys', type: 'SessionKey[]' }],\n Authorization: [\n { name: 'from', type: 'address' },\n { name: 'sessionKeysProvider', type: 'string' },\n { name: 'sessionKeys', type: 'SessionKey[]' },\n { name: 'planId', type: 'string' },\n { name: 'agentId', type: 'string' },\n { name: 'resourceUrl', type: 'string' },\n { name: 'httpVerb', type: 'string' },\n { name: 'nonce', type: 'string' },\n ],\n}\n\n/**\n * Legacy (v1) signed message shape — no `planId`. Mirrors `NeverminedTypedDataTypesV1`.\n * Only genuinely-old tokens carry this shape; mint never produces it.\n */\nexport type NeverminedTypedDataMessageV1 = {\n from: `0x${string}`\n sessionKeysProvider: string\n sessionKeys: { id: string; data: string }[]\n}\n\n/**\n * The v2 signed message shape — `planId` is REQUIRED. Mint always produces this\n * shape (#1678), so `planId` is non-optional here; the v1 variant above models\n * the legacy no-planId message. Mirrors `NeverminedTypedDataTypes`.\n */\nexport type NeverminedTypedDataMessage = NeverminedTypedDataMessageV1 & {\n /** Signed planId (#1678) — always present on v2 (minted) tokens. */\n planId: string\n}\n\n/**\n * The v3 signed message shape (#2646) — every binding member is REQUIRED, with\n * `''` standing for \"the mint had no value for this\". Mirrors\n * {@link NeverminedTypedDataTypesV3}.\n */\nexport type NeverminedTypedDataMessageV3 = NeverminedTypedDataMessage & {\n /** Signed agentId, or `''` when the token is not bound to an agent. */\n agentId: string\n /** Signed `resource.url`, or `''` when the token is not bound to a resource. */\n resourceUrl: string\n /** Signed HTTP verb, or `''` when the token is not bound to a verb. */\n httpVerb: string\n /** One-time nonce — always a non-empty random value on a v3 token. */\n nonce: string\n}\n\n/**\n * The EIP-712 struct version an **x402** token is signed over — x402's ladder, and only\n * x402's (#3266).\n *\n * `1` is not mintable — it only exists as a verification branch for tokens minted\n * before #1678. New tokens are v2 (the default, reusable, pre-#2646 semantics) or\n * v3 (seller/resource-bound and single-use).\n *\n * MPP has NO version ladder and no ordinal of its own: it signs ONE struct —\n * {@link NeverminedTypedDataTypes}, reused rather than copied — and names no version for it.\n * That is not a temporary state. MPP's own draft (`draft-ryan-httpauth-payment`, §9.2) carries\n * no version on the wire either: the scheme name is the stable identifier, evolution is\n * additive, and what versioning the protocol needs lives on the payment METHOD\n * (`methodDetails.version`). So do not add an MPP rung here, and do not let an x402 rung reach\n * the MPP minter — the two ladders were shared once, and #3258 is what that cost.\n *\n * ⚠️ That draft's \"ignore what you don't recognise\" rule belongs to the HTTP envelope and MUST\n * NOT be carried into this struct: EIP-712 hashes the type set, so an unrecognised signed member\n * is not ignorable — it changes the digest.\n */\nexport type NeverminedTokenVersion = 2 | 3\n\n/**\n * Options for generating an x402 access token\n */\nexport type GenerateX402TokenOptions = {\n subscriber: SmartAccount\n orderSessionKey: string\n redeemSessionKey: string\n publicClient: PublicClient\n planId: string\n /** Scheme version (e.g., '1') - stored in accepted.extra.version */\n schemeVersion?: string\n agentId?: string\n resource?: X402Resource\n httpVerb?: string\n /** Token expiration timestamp in seconds (Unix epoch) */\n validUntil?: number\n /**\n * EIP-712 struct version to sign (#2646). Defaults to `2` — the reusable,\n * pre-#2646 shape — so existing callers are untouched and there is no flag day.\n *\n * `3` binds the token to `agentId` / `resource.url` / `httpVerb` and adds a\n * one-time nonce, which the resource server consumes at settle. A v3 token is\n * therefore SINGLE-USE: a caller that opts in must mint one per paid request.\n *\n * x402 ONLY (#3266). It used to apply to both protocols, which is exactly how the x402 nonce\n * reached MPP — where the access token is the buyer's REUSABLE credential and the single-use\n * unit is the CHALLENGE, so a per-token nonce kills every MPP buyer's second challenge\n * (#3258). {@link GenerateMppTokenOptions} omits this field, and the MPP minter has no\n * parameter that could carry it.\n */\n tokenVersion?: NeverminedTokenVersion\n}\n\n/**\n * Options for minting an MPP access token — {@link GenerateX402TokenOptions} minus the x402\n * version knob (#3266).\n *\n * The omission IS the divergence: MPP signs one struct and the server chooses it, so there is\n * nothing here for a caller to name. Passing `tokenVersion` to\n * {@link generateAgentMppAccessToken} is a compile error rather than a silently honoured\n * request — which also means the guard is invisible to jest, since the suites run through SWC\n * and it strips types without checking them. `nx typecheck` is what enforces it.\n */\nexport type GenerateMppTokenOptions = Omit<GenerateX402TokenOptions, 'tokenVersion'>\n\n/**\n * The domain's `chainId` is a load-bearing part of the separator, and viem drops an `undefined`\n * one from the `EIP712Domain` type list — hashing IDENTICALLY to omitting the field, with no error.\n * A chainless `publicClient` (`createPublicClient({ transport })` with no `chain`, a shape that\n * already exists in this repo) would therefore mint a digest valid on every chain at once. Fail\n * loudly instead: this is a client misconfiguration, never a token defect.\n */\nconst requireChainId = (publicClient: PublicClient, caller: string): number => {\n const chainId = publicClient.chain?.id\n if (typeof chainId !== 'number') {\n throw new Error(`${caller}: publicClient has no chain — the EIP-712 domain needs a chainId`)\n }\n return chainId\n}\n\n/**\n * 32 bytes of CSPRNG output, hex-encoded — the one-time nonce of a v3 token (#2646).\n *\n * `globalThis.crypto` (WebCrypto) rather than node's `crypto` module so core-kit keeps\n * working in the browser, where this package is also consumed. 256 bits makes a\n * collision in the resource server's replay store (a UNIQUE column) unreachable, so a\n * legitimate mint can never be refused as someone else's replay.\n */\nconst generateTokenNonce = (): string => {\n const bytes = new Uint8Array(32)\n globalThis.crypto.getRandomValues(bytes)\n return `0x${Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')}`\n}\n\n/**\n * Builds the Authorization message BOTH protocols sign.\n *\n * This is the part that deliberately does NOT diverge (#3266): `from`, `sessionKeysProvider`,\n * `sessionKeys` and `planId` mean the same thing on x402 and MPP, so a defect here would be\n * universal — with two copies it would have to be found and fixed twice. Keeping it\n * single-sourced is what bounds the cost of the divergence above it.\n */\nconst buildAuthorizationMessage = (\n options: GenerateMppTokenOptions,\n): NeverminedTypedDataMessage => {\n const { subscriber, orderSessionKey, redeemSessionKey, planId } = options\n\n // A v2 token MUST carry a non-empty signed planId (#1678) — an empty/blank\n // planId would otherwise be signed into `authorization.planId` and could be\n // misread as a \"present but empty\" signed claim, blurring the v1/v2 boundary\n // that verification keys off genuine presence. erc4337 already requires a\n // planId upstream (`validateX402TokenStructure`), so this is a hard invariant.\n if (typeof planId !== 'string' || planId.trim() === '') {\n throw new Error('Nevermined access token: planId is required and must be a non-empty string')\n }\n\n // Including `planId` here puts it inside the EIP-712 digest, so it cannot be\n // altered without invalidating the signature.\n return {\n from: subscriber.address,\n sessionKeysProvider: 'zerodev',\n sessionKeys: [\n { id: 'order', data: keccak256(toBytes(orderSessionKey)) },\n { id: 'redeem', data: keccak256(toBytes(redeemSessionKey)) },\n ],\n planId,\n }\n}\n\n/**\n * Signs an already-chosen struct under an already-chosen domain and wraps it in the envelope.\n *\n * `types` and `message` arrive TOGETHER and already agreed, because EIP-712 hashes the type set\n * — a v3 message signed over the v2 list produces a digest no verifier reproduces. Choosing the\n * pair is each protocol's job; this function never infers one from the other, and has no notion\n * of a version to infer it from.\n *\n * Not exported — callers go through {@link generateAgentX402AccessToken} or\n * {@link generateAgentMppAccessToken}, each with its domain pinned, so a protocol can never be\n * selected by a caller-supplied value.\n */\nconst signAccessToken = async (\n options: GenerateMppTokenOptions,\n domainName: NeverminedEip712DomainName,\n types: NeverminedTypedData | NeverminedTypedDataV3,\n message: NeverminedTypedDataMessage | NeverminedTypedDataMessageV3,\n): Promise<AgentX402AccessToken> => {\n const {\n subscriber,\n publicClient,\n planId,\n schemeVersion = '1',\n agentId,\n resource,\n httpVerb,\n validUntil,\n } = options\n\n const chainId = requireChainId(publicClient, 'Nevermined access token mint')\n\n const domain: NeverminedTypedDataDomain<NeverminedEip712DomainName> = {\n name: domainName,\n version: '1',\n chainId,\n verifyingContract: subscriber.address,\n }\n\n const signature = await subscriber.signTypedData({\n domain,\n types,\n primaryType: 'Authorization',\n message,\n } as TypedDataDefinition<NeverminedTypedData | NeverminedTypedDataV3, 'Authorization'>)\n\n // Network in CAIP-2 format (eip155:chainId) — always resolvable, `requireChainId` already ran.\n const network = `eip155:${chainId}`\n\n return {\n x402Version: 2,\n ...(resource && { resource }),\n accepted: {\n scheme: 'nvm:erc4337',\n network,\n planId,\n extra: {\n version: schemeVersion,\n ...(agentId && { agentId }),\n ...(httpVerb && { httpVerb }),\n },\n },\n payload: {\n signature,\n authorization: message,\n },\n ...(validUntil !== undefined && { validUntil }),\n extensions: {},\n }\n}\n\n/**\n * Generate an x402 access token aligned with x402 v2 spec.\n *\n * Walks x402's OWN version ladder (#3266): v3 when the caller asks for the #2646 binding and\n * one-time nonce, the base struct otherwise. Nothing chosen here can reach the MPP minter.\n *\n * @param options - Token generation options\n * @returns x402 access token with EIP-712 signature\n */\nexport const generateAgentX402AccessToken = async (\n options: GenerateX402TokenOptions,\n): Promise<AgentX402AccessToken> => {\n const message = buildAuthorizationMessage(options)\n\n // v3 (#2646) appends the seller/resource binding and the one-time nonce. A member the caller\n // left undefined is signed as '' — EIP-712 has no optional members, so \"not bound\" must itself\n // be a signed value; `isSignedBindingConsistent` then holds the unsigned envelope copy to the\n // same absence.\n if ((options.tokenVersion ?? 2) === 3) {\n return signAccessToken(options, NEVERMINED_EIP712_DOMAIN_NAME, NeverminedTypedDataTypesV3, {\n ...message,\n agentId: options.agentId ?? '',\n resourceUrl: options.resource?.url ?? '',\n httpVerb: options.httpVerb ?? '',\n nonce: generateTokenNonce(),\n })\n }\n\n return signAccessToken(options, NEVERMINED_EIP712_DOMAIN_NAME, NeverminedTypedDataTypes, message)\n}\n\n/**\n * Generate a NATIVE MPP access token (#2640) — the same struct signed under the\n * `Nevermined-MPP` EIP-712 domain.\n *\n * Native, not a wrapper: the revised Tier A design (#2024) deliberately does NOT embed an x402\n * token inside an MPP credential, because unwrapping it would hand the holder a token that still\n * settles on the public x402 path.\n *\n * ONE struct, NO version (#3266). {@link NeverminedTypedDataTypes} is REUSED rather than copied\n * under an MPP name — the members are identical, and a second table would only drift. What\n * diverges is the CHOICE: this minter has no ladder to walk and no parameter that could make it\n * walk one, so a future x402 struct change cannot reach MPP by default the way #2646's nonce did.\n *\n * @param options - Token generation options, minus x402's `tokenVersion`\n * @returns MPP access token with EIP-712 signature over the MPP domain. The envelope is IDENTICAL\n * to an x402 token's, `accepted.scheme: 'nvm:erc4337'` included — only the signature differs, so\n * the protocol is fixed by the endpoint, not readable off the wire. See\n * {@link NEVERMINED_MPP_EIP712_DOMAIN_NAME} on routing vs. the security boundary.\n */\nexport const generateAgentMppAccessToken = async (\n options: GenerateMppTokenOptions,\n): Promise<AgentX402AccessToken> =>\n signAccessToken(\n options,\n NEVERMINED_MPP_EIP712_DOMAIN_NAME,\n NeverminedTypedDataTypes,\n buildAuthorizationMessage(options),\n )\n\n/**\n * True when `authorization` carries a GENUINE signed planId (#1678) — a present,\n * non-empty string. This is the single v1/v2 discriminator: verification keys off\n * it to choose the matching EIP-712 type list, the handler keys off it to decide\n * whether the signed-vs-unsigned consistency guard fires. Keying off genuine\n * presence (not just `'planId' in authorization`) ensures a `planId: ''` / nullish\n * value can never be mistaken for a real signed claim.\n */\nexport const hasSignedPlanId = (\n authorization: X402Erc4337Payload['authorization'],\n): authorization is X402Erc4337Payload['authorization'] & { planId: string } => {\n return typeof authorization?.planId === 'string' && authorization.planId.trim() !== ''\n}\n\n/**\n * The signed-vs-unsigned planId consistency invariant (#1678), co-located with\n * `hasSignedPlanId` so signer, verifier, and the API handler share ONE\n * definition (no drift between where the planId is signed and where the guard\n * enforces it).\n *\n * For a v2 token (signed planId present) the tamperable unsigned `accepted.planId`\n * MUST exactly equal the signed `authorization.planId` — anything else is a\n * post-signing edit of the outer JSON. Returns `true` for v1 tokens (no signed\n * planId): there is no signed value to be consistent with, so the guard does not\n * apply (the caller handles the legacy fallback separately). The API handler maps\n * a `false` result to its forgery-gated rejection.\n */\nexport const isSignedPlanIdConsistent = (\n authorization: X402Erc4337Payload['authorization'],\n acceptedPlanId: string | null | undefined,\n): boolean => {\n if (!hasSignedPlanId(authorization)) return true\n return acceptedPlanId === authorization.planId\n}\n\n/**\n * True when `authorization` carries a GENUINE one-time nonce (#2646) — a present,\n * non-empty string. This is the single v2/v3 discriminator, the exact counterpart of\n * {@link hasSignedPlanId} for the v1/v2 boundary: verification keys off it to choose\n * the matching EIP-712 type list, and the resource server keys off it to decide\n * whether the binding guard and the single-use replay store apply.\n *\n * `nonce` is the discriminator rather than one of the binding members because it is\n * the only v3 member that is always non-empty — `agentId` / `resourceUrl` / `httpVerb`\n * are legitimately signed as `''` when the mint had no value for them.\n */\nexport const hasSignedNonce = (\n authorization: X402Erc4337Payload['authorization'],\n): authorization is X402Erc4337Payload['authorization'] & { nonce: string } => {\n return typeof authorization?.nonce === 'string' && authorization.nonce.trim() !== ''\n}\n\n/** The unsigned envelope values a v3 binding is checked against. */\nexport type SignedBindingEnvelope = {\n /** `accepted.extra.agentId` */\n agentId: string | null | undefined\n /** `resource.url` */\n resourceUrl: string | null | undefined\n /** `accepted.extra.httpVerb` */\n httpVerb: string | null | undefined\n}\n\n/**\n * The signed-vs-unsigned BINDING invariant (#2646), co-located with\n * {@link hasSignedNonce} so signer, verifier and the API handler share ONE definition —\n * the same reason {@link isSignedPlanIdConsistent} lives here.\n *\n * For a v3 token every binding member must agree with its unsigned envelope copy:\n * - a signed non-empty value must equal the envelope value EXACTLY. Both are our own\n * mint's output, so exact equality is right; the fuzzier, query-insensitive URL\n * comparison belongs to the demand-match against the SELLER-supplied\n * `paymentRequired`, which is a different question.\n * - a signed `''` means \"not bound\", so the envelope copy must be absent (or empty)\n * too. This is the half that stops someone ADDING an `agentId` to a token that was\n * signed without one — without it, `''` would be a hole rather than a claim.\n *\n * Returns `true` for v1/v2 tokens: there is no signed binding to be consistent with,\n * and the caller handles those on the legacy path. The API handler maps a `false`\n * result to its forgery-gated rejection, exactly as it does for the planId guard.\n */\nexport const isSignedBindingConsistent = (\n authorization: X402Erc4337Payload['authorization'],\n envelope: SignedBindingEnvelope,\n): boolean => {\n if (!hasSignedNonce(authorization)) return true\n\n const matches = (signed: string | undefined, unsigned: string | null | undefined): boolean => {\n const signedValue = signed ?? ''\n // Absence and '' are the same claim on both sides: the mint omits an empty member\n // from the envelope entirely, so an omitted field is what a signed '' looks like.\n if (signedValue === '') return unsigned === undefined || unsigned === null || unsigned === ''\n return unsigned === signedValue\n }\n\n return (\n matches(authorization.agentId, envelope.agentId) &&\n matches(authorization.resourceUrl, envelope.resourceUrl) &&\n matches(authorization.httpVerb, envelope.httpVerb)\n )\n}\n\n/**\n * Picks the EIP-712 type list a protocol verifies an authorization against, or `null` to refuse\n * it outright.\n *\n * One selector per protocol IS the divergence (#3266). This used to be a single, protocol-blind\n * field-presence ladder, which made every x402 struct version automatically a verifiable MPP\n * one. Presence detection also cannot distinguish two protocols that want the same field set\n * with different meaning — the next #3258 waiting to happen.\n */\ntype AuthorizationTypesSelector = (\n authorization: X402Erc4337Payload['authorization'],\n) => NeverminedTypedData | NeverminedTypedDataV1 | NeverminedTypedDataV3 | null\n\n/**\n * x402's ladder: a genuine signed nonce (#2646) → v3, else a genuine signed planId (#1678) → v2,\n * else the original v1 list. Selected by GENUINE presence and never by a caller-supplied hint.\n * The branches are mutually exclusive and none falls back to another — trying a second type list\n * on failure would erase exactly the downgrade protection the versioning provides.\n *\n * A v1 (unsigned-planId) authorization leaves `isSignedPlanIdConsistent` inert, so the tamperable\n * `accepted.planId` becomes authoritative. That is a deliberate #1678 back-compat concession, and\n * it is why this selector admits a shape {@link selectMppAuthorizationTypes} refuses.\n */\nconst selectX402AuthorizationTypes: AuthorizationTypesSelector = (authorization) =>\n hasSignedNonce(authorization)\n ? NeverminedTypedDataTypesV3\n : hasSignedPlanId(authorization)\n ? NeverminedTypedDataTypes\n : NeverminedTypedDataTypesV1\n\n/**\n * MPP's table: one entry, no ladder (#3266) — {@link NeverminedTypedDataTypes} reused, not copied.\n *\n * Two consequences, both of them the point:\n * - an authorization carrying a v3 nonce has nowhere to climb to. It is checked against the base\n * list, the digest does not match, and it fails. An x402 struct version is not automatically\n * an MPP one, which is the coupling #3258 came from.\n *\n * ⚠️ This is the one RUNTIME verdict this change flips, and it is retroactive: before #3266 the\n * protocol-blind ladder read a v3 MPP authorization's `planId`, selected the v3 list, and the\n * token VERIFIED. An MPP token already minted at `tokenVersion: 3` therefore stops verifying —\n * it is not merely unmintable from here on. `validUntil` is written at mint but never compared\n * against `now` on this path, so nothing expires such a token out of existence; the holder has\n * to re-mint. No supported client can hold one (`@nevermined-io/payments` has never exposed\n * `tokenVersion`, per #3257) and it was already single-settle in practice, since\n * `consumeTokenNonce` is not MPP-aware and spends the nonce on an MPP settle too.\n * - a legacy (unsigned-planId) authorization is REFUSED rather than checked against the v1 list.\n * MPP is born after #1678 and has no pre-planId tokens to stay compatible with; accepting one\n * would hand authority to the tamperable `accepted.planId`.\n */\nconst selectMppAuthorizationTypes: AuthorizationTypesSelector = (authorization) =>\n hasSignedPlanId(authorization) ? NeverminedTypedDataTypes : null\n\n/**\n * Shared verifier for both protocols, differing only in the EIP-712 domain name and the type\n * list its protocol accepts.\n *\n * EIP-712 hashes the type set, so a token signed over one struct cannot verify against another's\n * type list. `selectTypes` is therefore the protocol's whole verification policy: which shapes it\n * recognises, and which it refuses without asking the chain.\n *\n * Downgrade-safe on every ladder: a v2 token was signed over the with-planId digest, so stripping\n * `authorization.planId` to force the v1 branch changes the digest and the signature fails; a v3\n * token was signed over the with-nonce digest, so stripping the nonce to escape single-use, or\n * stripping `agentId` / `resourceUrl` to escape the seller binding, fails the same way. An\n * attacker cannot demote a signed token to shed a claim.\n *\n * EIP-712 hashes the DOMAIN separator too, which is what makes `domainName` the protocol boundary\n * (#2640) rather than a label. Not exported: callers go through\n * {@link verifyAgentX402AccessToken} or {@link verifyAgentMppAccessToken}, each with its domain\n * and its selector pinned, so no caller-supplied value can pick which protocol a token is checked\n * against.\n */\nconst verifyAgentAccessToken = async (\n accessToken: AgentX402AccessToken,\n publicClient: PublicClient,\n domainName: NeverminedEip712DomainName,\n selectTypes: AuthorizationTypesSelector,\n): Promise<boolean> => {\n // `payload` is a union (erc4337 | card-delegation) and this is a public entry point with no\n // guaranteed pre-validation — `validateX402TokenStructure` runs in `apps/api`, not here. A\n // structural precondition, evaluated BEFORE the await: deliberately NOT a try/catch around the\n // verification itself, which must keep letting an RPC fault propagate rather than laundering it\n // into a `false` the caller reads as a forgery.\n const erc4337Payload = accessToken?.payload as X402Erc4337Payload | undefined\n if (!erc4337Payload?.authorization?.from || !erc4337Payload.signature) return false\n\n const { signature, authorization } = erc4337Payload\n const subscriberAddress = authorization.from\n\n // A shape this protocol does not recognise is refused here, before any chain call.\n const types = selectTypes(authorization)\n if (!types) return false\n\n const domain: NeverminedTypedDataDomain<NeverminedEip712DomainName> = {\n name: domainName,\n version: '1',\n chainId: requireChainId(publicClient, 'Nevermined access token verify'),\n verifyingContract: subscriberAddress,\n }\n const isValid = await publicClient.verifyTypedData({\n address: subscriberAddress,\n signature,\n types,\n domain,\n message: authorization,\n primaryType: 'Authorization',\n })\n return isValid\n}\n\n/**\n * Verify an x402 access token signature (see {@link selectX402AuthorizationTypes} for the\n * v1/v2/v3 type-list detection and its downgrade-safety).\n *\n * Rejects an MPP token: it was signed under a different domain separator, so the digest differs.\n *\n * @param accessToken - The x402 access token to verify\n * @param publicClient - Viem public client\n * @returns true if signature is valid\n */\nexport const verifyAgentX402AccessToken = (\n accessToken: AgentX402AccessToken,\n publicClient: PublicClient,\n): Promise<boolean> =>\n verifyAgentAccessToken(\n accessToken,\n publicClient,\n NEVERMINED_EIP712_DOMAIN_NAME,\n selectX402AuthorizationTypes,\n )\n\n/**\n * Verify an MPP access token signature (#2640).\n *\n * Rejects an x402 token for the same reason the x402 verifier rejects an MPP one — the domain\n * separator is part of the signed digest. This is the property the whole Tier A isolation rests on:\n * a credential harvested from one protocol is inert on the other, closing cross-protocol replay\n * AND downgrade — WHILE the signing key stays server-side. A subscriber holding it can mint under\n * whichever domain they like, so the split constrains the transport, not that holder.\n *\n * One struct, no ladder: {@link selectMppAuthorizationTypes} accepts the base struct and nothing\n * else, so a legacy (unsigned-planId) authorization is refused outright and an x402 struct version\n * is not verifiable here just because x402 added it (#3266).\n *\n * @param accessToken - The MPP access token to verify\n * @param publicClient - Viem public client\n * @returns true if signature is valid\n */\nexport const verifyAgentMppAccessToken = (\n accessToken: AgentX402AccessToken,\n publicClient: PublicClient,\n): Promise<boolean> =>\n verifyAgentAccessToken(\n accessToken,\n publicClient,\n NEVERMINED_MPP_EIP712_DOMAIN_NAME,\n selectMppAuthorizationTypes,\n )\n\n/**\n * Validation result for x402 token structure\n */\nexport type X402TokenValidationResult = {\n valid: boolean\n errors: string[]\n}\n\n/**\n * Validate the structure of an x402 access token\n *\n * @param token - The token to validate (unknown type for safety)\n * @returns Validation result with errors if any\n */\nconst SUPPORTED_SCHEMES: X402Scheme[] = ['nvm:erc4337', 'nvm:card-delegation']\n\nexport const validateX402TokenStructure = (token: unknown): X402TokenValidationResult => {\n const errors: string[] = []\n\n if (!token || typeof token !== 'object') {\n return { valid: false, errors: ['Token must be an object'] }\n }\n\n const t = token as Record<string, unknown>\n\n // Validate x402Version\n if (t.x402Version !== 2) {\n errors.push('x402Version must be 2')\n }\n\n // Validate accepted object\n if (!t.accepted || typeof t.accepted !== 'object') {\n errors.push('accepted is required')\n } else {\n const accepted = t.accepted as Record<string, unknown>\n const scheme = accepted.scheme as string\n\n if (!SUPPORTED_SCHEMES.includes(scheme as X402Scheme)) {\n errors.push(`scheme must be one of: ${SUPPORTED_SCHEMES.join(', ')}`)\n }\n\n if (!accepted.network || typeof accepted.network !== 'string') {\n errors.push('network is required in accepted')\n }\n\n // Validate extra\n if (!accepted.extra || typeof accepted.extra !== 'object') {\n errors.push('extra is required in accepted')\n } else {\n const extra = accepted.extra as Record<string, unknown>\n if (!extra.version || typeof extra.version !== 'string') {\n errors.push('version is required in accepted.extra')\n }\n }\n\n // Scheme-specific accepted validation\n if (scheme === 'nvm:erc4337') {\n if (!accepted.planId || typeof accepted.planId !== 'string') {\n errors.push('planId is required in accepted for nvm:erc4337')\n }\n }\n }\n\n // Validate payload\n if (!t.payload || typeof t.payload !== 'object') {\n errors.push('payload is required')\n } else {\n const accepted = t.accepted as Record<string, unknown> | undefined\n const scheme = accepted?.scheme as string | undefined\n const payload = t.payload as Record<string, unknown>\n\n if (scheme === 'nvm:erc4337') {\n if (!payload.signature || typeof payload.signature !== 'string') {\n errors.push('signature is required in payload')\n }\n\n if (!payload.authorization || typeof payload.authorization !== 'object') {\n errors.push('authorization is required in payload')\n } else {\n const auth = payload.authorization as Record<string, unknown>\n\n if (!auth.from || typeof auth.from !== 'string') {\n errors.push('from address is required in payload.authorization')\n } else if (!auth.from.startsWith('0x')) {\n errors.push('from must be a valid hex address')\n }\n\n if (!auth.sessionKeysProvider || typeof auth.sessionKeysProvider !== 'string') {\n errors.push('sessionKeysProvider is required in payload.authorization')\n }\n\n if (!Array.isArray(auth.sessionKeys)) {\n errors.push('sessionKeys array is required in payload.authorization')\n }\n }\n } else if (scheme === 'nvm:card-delegation') {\n if (!payload.token || typeof payload.token !== 'string') {\n errors.push('token is required in payload for nvm:card-delegation')\n }\n }\n }\n\n // Validate extensions (must be present, even if empty)\n if (t.extensions === undefined) {\n errors.push('extensions is required (can be empty object)')\n }\n\n return { valid: errors.length === 0, errors }\n}\n\nexport const b64EncodeX402AccessToken = (accessToken: AgentX402AccessToken): string => {\n return Buffer.from(JSON.stringify(accessToken)).toString('base64')\n}\n\nexport const b64DecodeX402AccessToken = (encoded: string): AgentX402AccessToken => {\n return JSON.parse(Buffer.from(encoded, 'base64').toString('utf-8'))\n}\n"],"names":["keccak256","toBytes","NEVERMINED_EIP712_DOMAIN_NAME","NEVERMINED_MPP_EIP712_DOMAIN_NAME","NeverminedTypedDataTypes","SessionKey","name","type","SessionKeys","Authorization","NeverminedTypedDataTypesV1","NeverminedTypedDataTypesV3","requireChainId","publicClient","caller","chainId","chain","id","Error","generateTokenNonce","bytes","Uint8Array","globalThis","crypto","getRandomValues","Array","from","b","toString","padStart","join","buildAuthorizationMessage","options","subscriber","orderSessionKey","redeemSessionKey","planId","trim","address","sessionKeysProvider","sessionKeys","data","signAccessToken","domainName","types","message","schemeVersion","agentId","resource","httpVerb","validUntil","domain","version","verifyingContract","signature","signTypedData","primaryType","network","x402Version","accepted","scheme","extra","payload","authorization","undefined","extensions","generateAgentX402AccessToken","tokenVersion","resourceUrl","url","nonce","generateAgentMppAccessToken","hasSignedPlanId","isSignedPlanIdConsistent","acceptedPlanId","hasSignedNonce","isSignedBindingConsistent","envelope","matches","signed","unsigned","signedValue","selectX402AuthorizationTypes","selectMppAuthorizationTypes","verifyAgentAccessToken","accessToken","selectTypes","erc4337Payload","subscriberAddress","isValid","verifyTypedData","verifyAgentX402AccessToken","verifyAgentMppAccessToken","SUPPORTED_SCHEMES","validateX402TokenStructure","token","errors","valid","t","push","includes","auth","startsWith","isArray","length","b64EncodeX402AccessToken","Buffer","JSON","stringify","b64DecodeX402AccessToken","encoded","parse"],"mappings":"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"}
|