mppx 0.8.19 → 0.9.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.
Files changed (112) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +0 -6
  3. package/dist/Challenge.d.ts +7 -0
  4. package/dist/Challenge.js +32 -6
  5. package/dist/Constants.d.ts +1 -0
  6. package/dist/Constants.js +1 -0
  7. package/dist/Credential.d.ts +11 -5
  8. package/dist/Credential.js +8 -8
  9. package/dist/Html.js +1 -1
  10. package/dist/cli/THIRD-PARTY-LICENSES.txt +1 -1
  11. package/dist/cli/cli.d.ts +2 -2
  12. package/dist/cli/cli.js +134 -134
  13. package/dist/cli/plugins/index.js +13 -13
  14. package/dist/cli/sessions/Manager.js +4 -6
  15. package/dist/cli/sessions/commands.d.ts +1 -1
  16. package/dist/cli/sessions/store.js +13 -19
  17. package/dist/cli/validate/index.d.ts +2 -2
  18. package/dist/client/Mppx.d.ts +39 -2
  19. package/dist/client/Mppx.js +102 -46
  20. package/dist/client/Transport.js +1 -1
  21. package/dist/client/internal/protocols/Mpp.d.ts +1 -1
  22. package/dist/client/internal/protocols/Mpp.js +5 -3
  23. package/dist/client/internal/protocols/Protocol.d.ts +3 -1
  24. package/dist/client/internal/protocols/Shared.js +5 -1
  25. package/dist/integrations/x402/Evm.d.ts +29 -0
  26. package/dist/integrations/x402/Evm.js +150 -0
  27. package/dist/integrations/x402/Negotiator.d.ts +36 -0
  28. package/dist/integrations/x402/Negotiator.js +165 -0
  29. package/dist/mcp/internal/Credential.d.ts +9 -0
  30. package/dist/mcp/internal/Credential.js +16 -0
  31. package/dist/mcp/server/Transport.js +2 -4
  32. package/dist/middlewares/express.js +5 -12
  33. package/dist/middlewares/internal/express.d.ts +8 -0
  34. package/dist/middlewares/internal/express.js +24 -0
  35. package/dist/middlewares/internal/node.d.ts +10 -0
  36. package/dist/middlewares/internal/node.js +34 -0
  37. package/dist/proxy/Proxy.js +15 -9
  38. package/dist/server/Mppx.d.ts +2 -0
  39. package/dist/server/Mppx.js +17 -14
  40. package/dist/server/Transport.d.ts +8 -2
  41. package/dist/server/Transport.js +6 -3
  42. package/dist/server/internal/SecretKey.d.ts +3 -0
  43. package/dist/server/internal/SecretKey.js +9 -0
  44. package/dist/server/internal/html/serviceWorker.client.d.ts +1 -1
  45. package/dist/server/internal/html/serviceWorker.client.js +2 -2
  46. package/dist/server/internal/html/serviceWorker.gen.d.ts +1 -1
  47. package/dist/server/internal/html/serviceWorker.gen.js +1 -1
  48. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  49. package/dist/stripe/server/internal/html.gen.js +1 -1
  50. package/dist/tempo/Methods.d.ts +0 -10
  51. package/dist/tempo/Methods.js +3 -12
  52. package/dist/tempo/client/Charge.d.ts +0 -3
  53. package/dist/tempo/client/Charge.js +3 -13
  54. package/dist/tempo/client/Methods.d.ts +0 -10
  55. package/dist/tempo/internal/account.d.ts +15 -6
  56. package/dist/tempo/internal/account.js +17 -26
  57. package/dist/tempo/internal/defaults.d.ts +0 -11
  58. package/dist/tempo/internal/defaults.js +0 -11
  59. package/dist/tempo/internal/fee-payer.d.ts +21 -3
  60. package/dist/tempo/internal/fee-payer.js +10 -19
  61. package/dist/tempo/internal/remote-fee-payer.d.ts +12 -0
  62. package/dist/tempo/internal/remote-fee-payer.js +17 -0
  63. package/dist/tempo/legacy/client/Session.d.ts +0 -7
  64. package/dist/tempo/server/Charge.d.ts +0 -3
  65. package/dist/tempo/server/Charge.js +26 -68
  66. package/dist/tempo/server/Methods.d.ts +4 -18
  67. package/dist/tempo/server/Methods.js +4 -8
  68. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  69. package/dist/tempo/server/internal/html.gen.js +1 -1
  70. package/dist/tempo/session/client/ChannelOps.d.ts +0 -14
  71. package/dist/tempo/session/client/ChannelOps.js +0 -22
  72. package/dist/tempo/session/client/ChannelStore.d.ts +5 -0
  73. package/dist/tempo/session/client/ChannelStore.js +2 -2
  74. package/dist/tempo/session/client/CredentialState.d.ts +2 -12
  75. package/dist/tempo/session/client/CredentialState.js +75 -73
  76. package/dist/tempo/session/client/Session.d.ts +2 -9
  77. package/dist/tempo/session/client/Session.js +71 -176
  78. package/dist/tempo/session/client/SessionManager.js +9 -28
  79. package/dist/tempo/session/precompile/Chain.d.ts +0 -4
  80. package/dist/tempo/session/precompile/Chain.js +1 -29
  81. package/dist/tempo/session/precompile/Protocol.d.ts +0 -13
  82. package/dist/tempo/session/precompile/Voucher.d.ts +1 -3
  83. package/dist/tempo/session/precompile/Voucher.js +12 -6
  84. package/dist/tempo/session/server/ChannelStore.d.ts +0 -6
  85. package/dist/tempo/session/server/ChannelStore.js +4 -11
  86. package/dist/tempo/session/server/CredentialVerification.d.ts +7 -9
  87. package/dist/tempo/session/server/CredentialVerification.js +118 -184
  88. package/dist/tempo/session/server/RequestState.d.ts +0 -17
  89. package/dist/tempo/session/server/RequestState.js +15 -59
  90. package/dist/tempo/session/server/Session.d.ts +0 -9
  91. package/dist/tempo/session/server/Session.js +8 -37
  92. package/dist/tempo/session/server/Settlement.d.ts +0 -7
  93. package/dist/tempo/session/server/Settlement.js +16 -50
  94. package/dist/viem/Client.d.ts +7 -2
  95. package/dist/viem/Client.js +17 -5
  96. package/dist/x402/Types.d.ts +2 -0
  97. package/dist/x402/Types.js +2 -0
  98. package/dist/x402/mcp.d.ts +20 -0
  99. package/dist/x402/mcp.js +153 -0
  100. package/dist/x402/middlewares/express.d.ts +23 -0
  101. package/dist/x402/middlewares/express.js +49 -0
  102. package/dist/x402/middlewares/hono.d.ts +13 -0
  103. package/dist/x402/middlewares/hono.js +36 -0
  104. package/dist/x402/middlewares/next.d.ts +17 -0
  105. package/dist/x402/middlewares/next.js +58 -0
  106. package/package.json +41 -1
  107. package/dist/tempo/internal/machine-token-charge.d.ts +0 -51
  108. package/dist/tempo/internal/machine-token-charge.js +0 -113
  109. package/dist/tempo/internal/machine-token-session.d.ts +0 -88
  110. package/dist/tempo/internal/machine-token-session.js +0 -173
  111. package/dist/tempo/internal/signature-envelope.d.ts +0 -14
  112. package/dist/tempo/internal/signature-envelope.js +0 -28
@@ -25,7 +25,7 @@ export function mergeActiveOnChainState(current, state) {
25
25
  }
26
26
  /** Keeps the existing higher voucher, otherwise stores the supplied candidate voucher. */
27
27
  export function resolveHighestVoucher(parameters) {
28
- const { authorizationSignature, channelId, current, cumulativeAmount, signature } = parameters;
28
+ const { channelId, current, cumulativeAmount, signature } = parameters;
29
29
  if (current?.highestVoucherAmount && current.highestVoucherAmount > cumulativeAmount) {
30
30
  return {
31
31
  highestVoucherAmount: current.highestVoucherAmount,
@@ -34,12 +34,7 @@ export function resolveHighestVoucher(parameters) {
34
34
  }
35
35
  return {
36
36
  highestVoucherAmount: cumulativeAmount,
37
- highestVoucher: {
38
- channelId,
39
- cumulativeAmount,
40
- signature,
41
- ...(authorizationSignature ? { authorizationSignature } : {}),
42
- },
37
+ highestVoucher: { channelId, cumulativeAmount, signature },
43
38
  };
44
39
  }
45
40
  /** Derives persisted identity fields from a verified precompile channel descriptor. */
@@ -64,9 +59,8 @@ export function resolvePrecompileChannelIdentity(parameters) {
64
59
  export function openChannelState(parameters) {
65
60
  const { authorizedSigner, chainId, channelId, current, descriptor, escrow, expiringNonceHash } = parameters;
66
61
  const { state } = parameters;
67
- const { authorizationSignature, cumulativeAmount, signature } = parameters;
62
+ const { cumulativeAmount, signature } = parameters;
68
63
  const highestVoucher = resolveHighestVoucher({
69
- authorizationSignature,
70
64
  channelId,
71
65
  current,
72
66
  cumulativeAmount,
@@ -170,11 +164,10 @@ export function markPendingClose(parameters) {
170
164
  }
171
165
  /** Finalizes local channel state after a successful close transaction. */
172
166
  export function finalizeClosedChannelState(parameters) {
173
- const { authorizationSignature, captureAmount, channelId, cumulativeAmount, current, signature } = parameters;
167
+ const { captureAmount, channelId, cumulativeAmount, current, signature } = parameters;
174
168
  if (!current)
175
169
  return current;
176
170
  const highestVoucher = resolveHighestVoucher({
177
- authorizationSignature,
178
171
  channelId,
179
172
  current,
180
173
  cumulativeAmount,
@@ -5,22 +5,21 @@ import * as Chain from '../precompile/Chain.js';
5
5
  import * as Channel from '../precompile/Channel.js';
6
6
  import { type SessionCredentialPayload, type SessionReceipt } from '../precompile/Protocol.js';
7
7
  import * as ChannelStore from './ChannelStore.js';
8
- import { type ChallengePaymentFields } from './RequestState.js';
9
8
  import { type OnSessionSettlement } from './Settlement.js';
10
9
  /** Returns the effective voucher signer for a TIP-1034 descriptor. */
11
10
  export declare function authorizedSigner(descriptor: Channel.ChannelDescriptor): Address;
11
+ /** Asserts that a credential payload includes a TIP-1034 descriptor. */
12
+ export declare function assertDescriptor(payload: {
13
+ descriptor?: Channel.ChannelDescriptor | undefined;
14
+ }): asserts payload is {
15
+ descriptor: Channel.ChannelDescriptor;
16
+ };
12
17
  /** Asserts that two TIP-1034 descriptors identify the same channel. */
13
18
  export declare function assertSameDescriptor(a: Channel.ChannelDescriptor, b: Channel.ChannelDescriptor): void;
14
19
  /**
15
20
  * Validates a TIP-1034 descriptor against channel ID, server destination, and token.
16
21
  */
17
22
  export declare function validateChannelDescriptor(descriptor: Channel.ChannelDescriptor, channelId: Address | `0x${string}`, chainId: number, escrow: Address, recipient: Address, currency: Address, expectedOperator?: Address | undefined): void;
18
- type ResolvedCredentialRoute = {
19
- allowedFeeTokens: readonly Address[];
20
- expectedOperator: Address;
21
- machineRouter?: Address | undefined;
22
- payment: ChallengePaymentFields;
23
- };
24
23
  /** Validates on-chain channel state before accepting or charging a credential. */
25
24
  export declare function validateChannelState(state: Chain.ChannelState, amount?: bigint): void;
26
25
  /** Asserts that an opening channel covers the route's requested payment. */
@@ -86,7 +85,7 @@ export type BroadcastCredentialPayloadParameters = {
86
85
  /** @deprecated Use {@link BroadcastCredentialPayloadParameters}. */
87
86
  export type VerifyCredentialPayloadParameters = BroadcastCredentialPayloadParameters;
88
87
  /** Narrows shared credential broadcast inputs to one payload action. */
89
- export type BroadcastCredentialActionParameters<action extends SessionCredentialPayload['action']> = Omit<BroadcastCredentialPayloadParameters, 'payload'> & ResolvedCredentialRoute & {
88
+ export type BroadcastCredentialActionParameters<action extends SessionCredentialPayload['action']> = Omit<BroadcastCredentialPayloadParameters, 'payload'> & {
90
89
  /** Credential payload for the selected action. */
91
90
  payload: Extract<SessionCredentialPayload, {
92
91
  action: action;
@@ -117,5 +116,4 @@ export declare function validateCredentialPayload(parameters: ValidateCredential
117
116
  export declare function broadcastCredentialPayload(context: BroadcastCredentialPayloadParameters): Promise<SessionReceipt>;
118
117
  /** @deprecated Use {@link broadcastCredentialPayload}. */
119
118
  export declare function verifyCredentialPayload(context: VerifyCredentialPayloadParameters): Promise<SessionReceipt>;
120
- export {};
121
119
  //# sourceMappingURL=CredentialVerification.d.ts.map
@@ -1,6 +1,5 @@
1
1
  import { isAddress, isAddressEqual, zeroAddress, } from 'viem';
2
2
  import { AmountExceedsDepositError, ChannelClosedError, ChannelNotFoundError, InsufficientBalanceError, InvalidSignatureError, VerificationFailedError, } from '../../../Errors.js';
3
- import * as MachineTokenSession from '../../internal/machine-token-session.js';
4
3
  import * as Chain from '../precompile/Chain.js';
5
4
  import { readChannelClosedReceiptFields } from '../precompile/Chain.js';
6
5
  import * as Channel from '../precompile/Channel.js';
@@ -8,13 +7,20 @@ import { createSessionReceipt, uint96, } from '../precompile/Protocol.js';
8
7
  import * as Voucher from '../precompile/Voucher.js';
9
8
  import * as ChannelStore from './ChannelStore.js';
10
9
  import { getChallengePaymentFields } from './RequestState.js';
11
- import { assertSettlementSender, getClientAccount, resolveChannelTransactionOptions, } from './Settlement.js';
10
+ import { assertSettlementSender, getClientAccount } from './Settlement.js';
12
11
  /** Returns the effective voucher signer for a TIP-1034 descriptor. */
13
12
  export function authorizedSigner(descriptor) {
14
13
  return isAddressEqual(descriptor.authorizedSigner, zeroAddress)
15
14
  ? descriptor.payer
16
15
  : descriptor.authorizedSigner;
17
16
  }
17
+ /** Asserts that a credential payload includes a TIP-1034 descriptor. */
18
+ export function assertDescriptor(payload) {
19
+ if (!payload.descriptor)
20
+ throw new VerificationFailedError({
21
+ reason: 'descriptor required for TIP-1034 session action',
22
+ });
23
+ }
18
24
  /** Asserts that two TIP-1034 descriptors identify the same channel. */
19
25
  export function assertSameDescriptor(a, b) {
20
26
  if (!isAddressEqual(a.payer, b.payer) ||
@@ -52,52 +58,11 @@ export function validateChannelDescriptor(descriptor, channelId, chainId, escrow
52
58
  });
53
59
  }
54
60
  }
55
- async function resolveCredentialRoute(parameters) {
56
- const { challenge, chainId, client, escrow, payload } = parameters;
57
- const { descriptor } = payload;
58
- const channelId = ChannelStore.normalizeChannelId(payload.channelId);
59
- if (Channel.computeId({ ...descriptor, chainId, escrow }).toLowerCase() !== channelId.toLowerCase())
60
- throw new VerificationFailedError({ reason: 'channel descriptor does not match channelId' });
61
- const request = getChallengePaymentFields(challenge);
62
- const expectedOperator = parameters.expectedOperator ?? zeroAddress;
63
- const direct = isAddressEqual(descriptor.payee, request.recipient) &&
64
- isAddressEqual(descriptor.token, request.currency) &&
65
- isAddressEqual(descriptor.operator, expectedOperator);
66
- if (direct)
67
- return {
68
- allowedFeeTokens: MachineTokenSession.allowedSponsoredFeeTokens({
69
- chainId,
70
- override: parameters.feeToken,
71
- paymentToken: request.currency,
72
- }),
73
- expectedOperator,
74
- payment: request,
75
- };
76
- if (payload.action !== 'close' && !MachineTokenSession.isEnabledChallenge(challenge))
77
- throw new VerificationFailedError({ reason: 'credential descriptor does not match challenge' });
78
- const route = await MachineTokenSession.matchRoute(client, {
79
- active: payload.action !== 'close',
80
- chainId,
81
- descriptor,
82
- merchant: request.recipient,
83
- targetToken: request.currency,
84
- });
85
- if (!route)
86
- throw new VerificationFailedError({
87
- reason: 'machine-token channel is not bound to the challenged merchant and currency',
88
- });
89
- return {
90
- allowedFeeTokens: [
91
- MachineTokenSession.resolveFeeToken({
92
- chainId,
93
- override: parameters.feeToken,
94
- paymentToken: route.token,
95
- }),
96
- ],
97
- expectedOperator: route.operator,
98
- machineRouter: route.operator,
99
- payment: { ...request, currency: route.token, recipient: route.payee },
100
- };
61
+ /** Accepts the advertised fee token while preserving pre-advertisement direct clients. */
62
+ function allowedSponsoredFeeTokens(currency, feeToken) {
63
+ if (!feeToken || isAddressEqual(feeToken, currency))
64
+ return [currency];
65
+ return [feeToken, currency];
101
66
  }
102
67
  /** Validates on-chain channel state before accepting or charging a credential. */
103
68
  export function validateChannelState(state, amount) {
@@ -174,9 +139,6 @@ function readHex(value, field) {
174
139
  return value;
175
140
  throw new VerificationFailedError({ reason: `invalid session credential ${field}` });
176
141
  }
177
- function readOptionalHex(value, field) {
178
- return value === undefined ? undefined : readHex(value, field);
179
- }
180
142
  function readRawAmount(value, field) {
181
143
  if (typeof value === 'string' && /^[0-9]+$/.test(value))
182
144
  return value;
@@ -239,7 +201,6 @@ export function requireSessionCredentialPayload(payload) {
239
201
  channelId: header.channelId,
240
202
  transaction: readHex(candidate.transaction, 'transaction'),
241
203
  signature: readHex(candidate.signature, 'signature'),
242
- authorizationSignature: readOptionalHex(candidate.authorizationSignature, 'authorizationSignature'),
243
204
  descriptor: readDescriptor(candidate.descriptor),
244
205
  cumulativeAmount: readRawAmount(candidate.cumulativeAmount, 'cumulativeAmount'),
245
206
  ...(candidate.authorizedSigner === undefined
@@ -264,7 +225,6 @@ export function requireSessionCredentialPayload(payload) {
264
225
  descriptor: readDescriptor(candidate.descriptor),
265
226
  cumulativeAmount: readRawAmount(candidate.cumulativeAmount, 'cumulativeAmount'),
266
227
  signature: readHex(candidate.signature, 'signature'),
267
- authorizationSignature: readOptionalHex(candidate.authorizationSignature, 'authorizationSignature'),
268
228
  };
269
229
  case 'close':
270
230
  return {
@@ -273,8 +233,6 @@ export function requireSessionCredentialPayload(payload) {
273
233
  descriptor: readDescriptor(candidate.descriptor),
274
234
  cumulativeAmount: readRawAmount(candidate.cumulativeAmount, 'cumulativeAmount'),
275
235
  signature: readHex(candidate.signature, 'signature'),
276
- authorizationSignature: readOptionalHex(candidate.authorizationSignature, 'authorizationSignature'),
277
- refundSignature: readOptionalHex(candidate.refundSignature, 'refundSignature'),
278
236
  };
279
237
  }
280
238
  }
@@ -292,26 +250,28 @@ const refreshOnChainVerificationCache = {
292
250
  /** Validates all action-specific session credential invariants without changing payment state. */
293
251
  export async function validateCredentialPayload(parameters) {
294
252
  const { payload } = parameters;
295
- const context = await resolveCredentialContext(parameters);
296
253
  switch (payload.action) {
297
254
  case 'open':
298
- await validateOpenCredential(context, payload);
255
+ await validateOpenCredential(parameters, payload);
299
256
  break;
300
257
  case 'topUp':
301
- await validateTopUpCredential(context, payload);
258
+ await validateTopUpCredential(parameters, payload);
302
259
  break;
303
260
  case 'voucher':
304
- await validateVoucherCredential(context, payload);
261
+ await validateVoucherCredential(parameters, payload);
305
262
  break;
306
263
  case 'close':
307
- await validateCloseCredential(context, payload);
264
+ await validateCloseCredential(parameters, payload);
308
265
  break;
309
266
  }
310
267
  return { action: payload.action, channelId: ChannelStore.normalizeChannelId(payload.channelId) };
311
268
  }
312
269
  async function validateOpenCredential(parameters, payload) {
313
- const { challenge, chainId, client, escrow, expectedOperator, payment: request } = parameters;
270
+ const { challenge, chainId, client, escrow } = parameters;
271
+ const request = getChallengePaymentFields(challenge);
272
+ const expectedOperator = parameters.expectedOperator ?? zeroAddress;
314
273
  const cumulativeAmount = uint96(BigInt(payload.cumulativeAmount));
274
+ assertDescriptor(payload);
315
275
  if (payload.authorizedSigner !== undefined &&
316
276
  !isAddressEqual(payload.authorizedSigner, payload.descriptor.authorizedSigner))
317
277
  throw new VerificationFailedError({
@@ -320,7 +280,7 @@ async function validateOpenCredential(parameters, payload) {
320
280
  const channelId = ChannelStore.normalizeChannelId(payload.channelId);
321
281
  validateChannelDescriptor(payload.descriptor, channelId, chainId, escrow, request.recipient, request.currency, expectedOperator);
322
282
  const transaction = Chain.validateOpenCredentialTransaction({
323
- allowedFeeTokens: parameters.allowedFeeTokens,
283
+ allowedFeeTokens: allowedSponsoredFeeTokens(request.currency, parameters.feeToken),
324
284
  challengeExpires: challenge.expires,
325
285
  chainId,
326
286
  escrowContract: escrow,
@@ -353,8 +313,11 @@ async function validateOpenCredential(parameters, payload) {
353
313
  });
354
314
  }
355
315
  async function validateTopUpCredential(parameters, payload) {
356
- const { challenge, chainId, client, escrow, expectedOperator, payment: request, store, } = parameters;
316
+ const { challenge, chainId, client, escrow, store } = parameters;
317
+ const request = getChallengePaymentFields(challenge);
318
+ const expectedOperator = parameters.expectedOperator ?? zeroAddress;
357
319
  const additionalDeposit = uint96(BigInt(payload.additionalDeposit));
320
+ assertDescriptor(payload);
358
321
  const channelId = ChannelStore.normalizeChannelId(payload.channelId);
359
322
  validateChannelDescriptor(payload.descriptor, channelId, chainId, escrow, request.recipient, request.currency, expectedOperator);
360
323
  const channel = await ChannelStore.loadPrecompileChannel({
@@ -367,7 +330,7 @@ async function validateTopUpCredential(parameters, payload) {
367
330
  });
368
331
  const transaction = Chain.validateTopUpCredentialTransaction({
369
332
  additionalDeposit,
370
- allowedFeeTokens: parameters.allowedFeeTokens,
333
+ allowedFeeTokens: allowedSponsoredFeeTokens(request.currency, parameters.feeToken),
371
334
  challengeExpires: challenge.expires,
372
335
  chainId,
373
336
  descriptor: channel.descriptor,
@@ -386,10 +349,12 @@ async function validateTopUpCredential(parameters, payload) {
386
349
  });
387
350
  }
388
351
  async function validateVoucherCredential(parameters, payload) {
389
- const { chainId, client, credentialSource, escrow, minVoucherDelta, store, channelStateTtl, lastOnChainVerified, payment: request, } = parameters;
390
- const { expectedOperator } = parameters;
352
+ const { challenge, chainId, client, credentialSource, escrow, minVoucherDelta, store, channelStateTtl, lastOnChainVerified, } = parameters;
353
+ const request = getChallengePaymentFields(challenge);
354
+ const expectedOperator = parameters.expectedOperator ?? zeroAddress;
391
355
  const channelId = ChannelStore.normalizeChannelId(payload.channelId);
392
- const voucher = Voucher.parseVoucherFromPayload(channelId, payload.cumulativeAmount, payload.signature, payload.authorizationSignature);
356
+ const voucher = Voucher.parseVoucherFromPayload(channelId, payload.cumulativeAmount, payload.signature);
357
+ assertDescriptor(payload);
393
358
  validateChannelDescriptor(payload.descriptor, channelId, chainId, escrow, request.recipient, request.currency, expectedOperator);
394
359
  const channel = await ChannelStore.loadPrecompileChannel({
395
360
  descriptor: payload.descriptor,
@@ -430,56 +395,14 @@ async function resolveVoucherChannelState(parameters) {
430
395
  closeRequestedAt: state?.closeRequestedAt ?? Number(channel.closeRequestedAt),
431
396
  };
432
397
  }
433
- function verifyMachineSessionAuthorization(parameters) {
434
- const { chainId, payload, router } = parameters;
435
- if (payload.action === 'topUp')
436
- return undefined;
437
- if (!router)
438
- return undefined;
439
- // The escrow resolves a zero authorizedSigner to the payer, but the router
440
- // domain is bound to an explicit signer; reject the delegation up front
441
- // instead of failing signature verification against the zero address.
442
- if (isAddressEqual(payload.descriptor.authorizedSigner, zeroAddress))
443
- throw new VerificationFailedError({
444
- reason: 'machine-token sessions require an explicit authorizedSigner',
445
- });
446
- if (!payload.authorizationSignature)
447
- throw new VerificationFailedError({
448
- reason: 'machine-token voucher requires a router authorization',
449
- });
450
- const valid = MachineTokenSession.verifyAuthorization({
451
- authorization: {
452
- channelId: ChannelStore.normalizeChannelId(payload.channelId),
453
- cumulativeAmount: uint96(BigInt(payload.cumulativeAmount)),
454
- },
455
- chainId,
456
- expectedSigner: payload.descriptor.authorizedSigner,
457
- router,
458
- signature: payload.authorizationSignature,
459
- });
460
- if (!valid)
461
- throw new InvalidSignatureError({ reason: 'invalid machine-token router authorization' });
462
- }
463
- async function resolveCredentialContext(parameters) {
464
- const route = await resolveCredentialRoute(parameters);
465
- verifyMachineSessionAuthorization({
466
- chainId: parameters.chainId,
467
- payload: parameters.payload,
468
- router: route.machineRouter,
469
- });
470
- return { ...parameters, ...route };
471
- }
472
- function assertMachineCloseAmount(machineRouter, cumulativeAmount, captureAmount) {
473
- if (machineRouter && cumulativeAmount !== captureAmount)
474
- throw new VerificationFailedError({
475
- reason: `machine-token close voucher amount must equal ${captureAmount} (capture amount)`,
476
- });
477
- }
478
- async function inspectCloseCredential(parameters, payload) {
479
- const { account: accountOverride, chainId, client, escrow, expectedOperator, machineRouter, payment, store, } = parameters;
398
+ async function validateCloseCredential(parameters, payload) {
399
+ const { challenge, chainId, client, escrow, store } = parameters;
400
+ const request = getChallengePaymentFields(challenge);
401
+ const expectedOperator = parameters.expectedOperator ?? zeroAddress;
480
402
  const cumulativeAmount = uint96(BigInt(payload.cumulativeAmount));
481
403
  const channelId = ChannelStore.normalizeChannelId(payload.channelId);
482
- validateChannelDescriptor(payload.descriptor, channelId, chainId, escrow, payment.recipient, payment.currency, expectedOperator);
404
+ assertDescriptor(payload);
405
+ validateChannelDescriptor(payload.descriptor, channelId, chainId, escrow, request.recipient, request.currency, expectedOperator);
483
406
  const channel = await ChannelStore.loadPrecompileChannel({
484
407
  descriptor: payload.descriptor,
485
408
  channelId,
@@ -502,47 +425,24 @@ async function inspectCloseCredential(parameters, payload) {
502
425
  throw new VerificationFailedError({
503
426
  reason: `close voucher amount must be >= ${state.settled} (on-chain settled)`,
504
427
  });
505
- if (!(await Voucher.verifyVoucher(escrow, chainId, { channelId, cumulativeAmount, signature: payload.signature }, channel.authorizedSigner)))
428
+ const valid = await Voucher.verifyVoucher(escrow, chainId, { channelId, cumulativeAmount, signature: payload.signature }, channel.authorizedSigner);
429
+ if (!valid)
506
430
  throw new InvalidSignatureError({ reason: 'invalid voucher signature' });
507
- const refundSignature = machineRouter ? payload.refundSignature : undefined;
508
- if (machineRouter) {
509
- if (!refundSignature)
510
- throw new VerificationFailedError({
511
- reason: 'machine-token close requires a refund authorization',
512
- });
513
- if (!(await Voucher.verifyVoucher(escrow, chainId, { channelId, cumulativeAmount: uint96(state.deposit), signature: refundSignature }, payload.descriptor.authorizedSigner)))
514
- throw new InvalidSignatureError({ reason: 'invalid machine-token refund authorization' });
515
- }
516
431
  const captureAmount = uint96(channel.spent > state.settled ? channel.spent : state.settled);
517
432
  if (captureAmount > state.deposit)
518
433
  throw new AmountExceedsDepositError({ reason: 'close capture amount exceeds on-chain deposit' });
519
- assertMachineCloseAmount(machineRouter, cumulativeAmount, captureAmount);
520
- const account = accountOverride ?? getClientAccount(client);
521
- if (!machineRouter || !account)
522
- assertSettlementSender({
523
- operation: 'close',
524
- channelId,
525
- operator: channel.operator,
526
- payee: channel.payee,
527
- sender: account?.address,
528
- });
529
- return {
530
- account,
531
- authorizationSignature: machineRouter ? payload.authorizationSignature : undefined,
532
- captureAmount,
533
- channel,
434
+ const account = parameters.account ?? getClientAccount(client);
435
+ assertSettlementSender({
436
+ operation: 'close',
534
437
  channelId,
535
- cumulativeAmount,
536
- refundSignature,
537
- state,
538
- };
539
- }
540
- async function validateCloseCredential(parameters, payload) {
541
- await inspectCloseCredential(parameters, payload);
438
+ operator: channel.operator,
439
+ payee: channel.payee,
440
+ sender: account?.address,
441
+ });
542
442
  }
543
443
  /** Broadcasts a validated session credential payload and applies its state transition. */
544
444
  export async function broadcastCredentialPayload(context) {
545
- const receipt = await broadcastCredentialAction(await resolveCredentialContext(context));
445
+ const receipt = await broadcastCredentialAction(context);
546
446
  if (refreshOnChainVerificationCache[context.payload.action])
547
447
  context.lastOnChainVerified.set(receipt.channelId, Date.now());
548
448
  return receipt;
@@ -568,8 +468,11 @@ function actionContext(context, payload) {
568
468
  return { ...context, payload };
569
469
  }
570
470
  async function handleOpenCredential(parameters) {
571
- const { store, client, challenge, payload, chainId, escrow, expectedOperator, payment: request, } = parameters;
471
+ const { store, client, challenge, payload, chainId, escrow } = parameters;
472
+ const request = getChallengePaymentFields(challenge);
473
+ const expectedOperator = parameters.expectedOperator ?? zeroAddress;
572
474
  const cumulativeAmount = uint96(BigInt(payload.cumulativeAmount));
475
+ assertDescriptor(payload);
573
476
  if (payload.authorizedSigner !== undefined &&
574
477
  !isAddressEqual(payload.authorizedSigner, payload.descriptor.authorizedSigner))
575
478
  throw new VerificationFailedError({
@@ -578,7 +481,7 @@ async function handleOpenCredential(parameters) {
578
481
  const channelId = ChannelStore.normalizeChannelId(payload.channelId);
579
482
  validateChannelDescriptor(payload.descriptor, channelId, chainId, escrow, request.recipient, request.currency, expectedOperator);
580
483
  const result = await Chain.broadcastOpenTransaction({
581
- allowedFeeTokens: parameters.allowedFeeTokens,
484
+ allowedFeeTokens: allowedSponsoredFeeTokens(request.currency, parameters.feeToken),
582
485
  challengeExpires: challenge.expires,
583
486
  chainId,
584
487
  client,
@@ -620,7 +523,6 @@ async function handleOpenCredential(parameters) {
620
523
  expiringNonceHash: result.expiringNonceHash,
621
524
  cumulativeAmount,
622
525
  signature: payload.signature,
623
- authorizationSignature: payload.authorizationSignature,
624
526
  state,
625
527
  }));
626
528
  if (!updated)
@@ -635,8 +537,11 @@ async function handleOpenCredential(parameters) {
635
537
  });
636
538
  }
637
539
  async function handleTopUpCredential(parameters) {
638
- const { store, client, challenge, payload, chainId, escrow, expectedOperator, payment: request, } = parameters;
540
+ const { store, client, challenge, payload, chainId, escrow } = parameters;
541
+ const request = getChallengePaymentFields(challenge);
542
+ const expectedOperator = parameters.expectedOperator ?? zeroAddress;
639
543
  const additionalDeposit = uint96(BigInt(payload.additionalDeposit));
544
+ assertDescriptor(payload);
640
545
  const channelId = ChannelStore.normalizeChannelId(payload.channelId);
641
546
  validateChannelDescriptor(payload.descriptor, channelId, chainId, escrow, request.recipient, request.currency, expectedOperator);
642
547
  const channel = await ChannelStore.loadPrecompileChannel({
@@ -649,7 +554,7 @@ async function handleTopUpCredential(parameters) {
649
554
  });
650
555
  const result = await Chain.broadcastTopUpTransaction({
651
556
  additionalDeposit,
652
- allowedFeeTokens: parameters.allowedFeeTokens,
557
+ allowedFeeTokens: allowedSponsoredFeeTokens(request.currency, parameters.feeToken),
653
558
  challengeExpires: challenge.expires,
654
559
  chainId,
655
560
  client,
@@ -674,10 +579,12 @@ async function handleTopUpCredential(parameters) {
674
579
  });
675
580
  }
676
581
  async function handleVoucherCredential(parameters) {
677
- const { store, client, challenge, credentialSource, payload, chainId, escrow, minVoucherDelta, channelStateTtl, lastOnChainVerified, payment: request, } = parameters;
678
- const { expectedOperator } = parameters;
582
+ const { store, client, challenge, credentialSource, payload, chainId, escrow, minVoucherDelta, channelStateTtl, lastOnChainVerified, } = parameters;
583
+ const request = getChallengePaymentFields(challenge);
584
+ const expectedOperator = parameters.expectedOperator ?? zeroAddress;
679
585
  const channelId = ChannelStore.normalizeChannelId(payload.channelId);
680
- const voucher = Voucher.parseVoucherFromPayload(channelId, payload.cumulativeAmount, payload.signature, payload.authorizationSignature);
586
+ const voucher = Voucher.parseVoucherFromPayload(channelId, payload.cumulativeAmount, payload.signature);
587
+ assertDescriptor(payload);
681
588
  validateChannelDescriptor(payload.descriptor, channelId, chainId, escrow, request.recipient, request.currency, expectedOperator);
682
589
  const channel = await ChannelStore.loadPrecompileChannel({
683
590
  descriptor: payload.descriptor,
@@ -719,10 +626,41 @@ async function handleVoucherCredential(parameters) {
719
626
  });
720
627
  }
721
628
  async function handleCloseCredential(parameters) {
722
- const { store, client, challenge, payload, escrow, machineRouter } = parameters;
723
- const inspected = await inspectCloseCredential(parameters, payload);
724
- const { account, authorizationSignature, channel, channelId, cumulativeAmount, refundSignature, state, } = inspected;
725
- let { captureAmount } = inspected;
629
+ const { store, client, challenge, payload, chainId, escrow } = parameters;
630
+ const request = getChallengePaymentFields(challenge);
631
+ const expectedOperator = parameters.expectedOperator ?? zeroAddress;
632
+ const cumulativeAmount = uint96(BigInt(payload.cumulativeAmount));
633
+ const channelId = ChannelStore.normalizeChannelId(payload.channelId);
634
+ assertDescriptor(payload);
635
+ validateChannelDescriptor(payload.descriptor, channelId, chainId, escrow, request.recipient, request.currency, expectedOperator);
636
+ const channel = await ChannelStore.loadPrecompileChannel({
637
+ descriptor: payload.descriptor,
638
+ channelId,
639
+ chainId,
640
+ escrow,
641
+ store,
642
+ });
643
+ if (channel.finalized)
644
+ throw new ChannelClosedError({ reason: 'channel is already finalized' });
645
+ const state = await Chain.getChannelState(client, channelId, escrow);
646
+ if (state.closeRequestedAt !== 0)
647
+ throw new ChannelClosedError({ reason: 'channel has a pending close request' });
648
+ if (state.deposit === 0n && (cumulativeAmount !== 0n || channel.spent !== 0n))
649
+ throw new ChannelClosedError({ reason: 'channel deposit is zero (settled)' });
650
+ if (cumulativeAmount < channel.spent)
651
+ throw new VerificationFailedError({
652
+ reason: `close voucher amount must be >= ${channel.spent} (spent)`,
653
+ });
654
+ if (cumulativeAmount < state.settled)
655
+ throw new VerificationFailedError({
656
+ reason: `close voucher amount must be >= ${state.settled} (on-chain settled)`,
657
+ });
658
+ const valid = await Voucher.verifyVoucher(escrow, chainId, { channelId, cumulativeAmount: cumulativeAmount, signature: payload.signature }, channel.authorizedSigner);
659
+ if (!valid)
660
+ throw new InvalidSignatureError({ reason: 'invalid voucher signature' });
661
+ let captureAmount = uint96(channel.spent > state.settled ? channel.spent : state.settled);
662
+ if (captureAmount > state.deposit)
663
+ throw new AmountExceedsDepositError({ reason: 'close capture amount exceeds on-chain deposit' });
726
664
  const pendingCloseStartedAt = BigInt(Math.floor(Date.now() / 1000) || 1);
727
665
  const previousCloseRequestedAt = channel.closeRequestedAt;
728
666
  let pendingCloseMarked = false;
@@ -735,26 +673,31 @@ async function handleCloseCredential(parameters) {
735
673
  onChainSettled: state.settled,
736
674
  });
737
675
  if (next.state) {
738
- assertMachineCloseAmount(machineRouter, cumulativeAmount, next.captureAmount);
739
676
  captureAmount = next.captureAmount;
740
677
  pendingCloseMarked = true;
741
678
  }
742
679
  return next.state;
743
680
  });
681
+ const account = parameters.account ?? getClientAccount(client);
744
682
  let txHash;
745
683
  let receipt;
746
684
  try {
747
- const transactionOptions = resolveChannelTransactionOptions(channel, parameters, account, machineRouter);
748
- if (machineRouter) {
749
- if (!authorizationSignature || !refundSignature)
750
- throw new VerificationFailedError({
751
- reason: 'machine-token close requires router and refund authorizations',
752
- });
753
- txHash = await Chain.closeMachineSessionOnChain(client, channel.descriptor, cumulativeAmount, payload.signature, authorizationSignature, refundSignature, machineRouter, transactionOptions);
754
- }
755
- else {
756
- txHash = await Chain.closeOnChain(client, channel.descriptor, cumulativeAmount, captureAmount, payload.signature, escrow, transactionOptions);
757
- }
685
+ assertSettlementSender({
686
+ operation: 'close',
687
+ channelId,
688
+ operator: channel.operator,
689
+ payee: channel.payee,
690
+ sender: account?.address,
691
+ });
692
+ txHash = await Chain.closeOnChain(client, channel.descriptor, cumulativeAmount, captureAmount, payload.signature, escrow, account
693
+ ? {
694
+ account,
695
+ ...(parameters.feePayer ? { feePayer: parameters.feePayer } : {}),
696
+ ...(parameters.feePayerPolicy ? { feePayerPolicy: parameters.feePayerPolicy } : {}),
697
+ ...(parameters.feeToken ? { feeToken: parameters.feeToken } : {}),
698
+ candidateFeeTokens: [channel.token],
699
+ }
700
+ : undefined);
758
701
  receipt = await Chain.waitForSuccessfulReceipt(client, txHash);
759
702
  }
760
703
  catch (error) {
@@ -767,18 +710,9 @@ async function handleCloseCredential(parameters) {
767
710
  }
768
711
  const closed = readChannelClosedReceiptFields(Chain.getChannelEvent(receipt, 'ChannelClosed', channelId));
769
712
  const { refundedToPayer, settledToPayee } = closed;
770
- if (machineRouter) {
771
- if (settledToPayee !== state.deposit || refundedToPayer !== 0n)
772
- throw new VerificationFailedError({
773
- reason: 'machine-token ChannelClosed amounts are invalid',
774
- });
775
- }
776
- else if (settledToPayee > captureAmount || settledToPayee + refundedToPayer > state.deposit) {
713
+ if (settledToPayee > captureAmount || settledToPayee + refundedToPayer > state.deposit)
777
714
  throw new VerificationFailedError({ reason: 'ChannelClosed amounts do not match state' });
778
- }
779
- const logicalSettled = machineRouter ? captureAmount : settledToPayee;
780
715
  const updated = await store.updateChannel(channelId, (current) => ChannelStore.finalizeClosedChannelState({
781
- authorizationSignature,
782
716
  captureAmount,
783
717
  channelId,
784
718
  cumulativeAmount,
@@ -791,8 +725,8 @@ async function handleCloseCredential(parameters) {
791
725
  txHash,
792
726
  channelId,
793
727
  trigger: 'close',
794
- amount: logicalSettled,
795
- delta: logicalSettled - state.settled,
728
+ amount: settledToPayee,
729
+ delta: settledToPayee - state.settled,
796
730
  }));
797
731
  }
798
732
  catch {
@@ -2,7 +2,6 @@ import { type Account as viem_Account, type Address, type Hex } from 'viem';
2
2
  import * as Constants from '../../../Constants.js';
3
3
  import type * as Credential from '../../../Credential.js';
4
4
  import type { Challenge } from '../../../index.js';
5
- import type { MaybePromise } from '../../../internal/types.js';
6
5
  import type * as z from '../../../zod.js';
7
6
  import * as Methods from '../../Methods.js';
8
7
  import { isSessionContentRequest, type RequestBodyProbe } from '../../server/internal/request-body.js';
@@ -15,14 +14,10 @@ import { type ParameterFeePayer, type ResolvedFeePayer } from './Settlement.js';
15
14
  export type ResolveSessionSnapshotParameters = {
16
15
  /** Raw request amount that must be covered by the next voucher. */
17
16
  amount: bigint;
18
- /** Already-loaded channel for `channelId`, skipping the store read. */
19
- channel?: ChannelStore.State | null | undefined;
20
17
  /** Channel ID from credential or challenge request, when available. */
21
18
  channelId: Hex | undefined;
22
19
  /** Payment fields the reusable channel must match before it is advertised. */
23
20
  expected?: SessionSnapshotPaymentFields | undefined;
24
- /** Optional alternate-rail matcher when the stored descriptor differs from logical payment fields. */
25
- matchPaymentFields?: MatchSessionSnapshotPaymentFields | undefined;
26
21
  /** Server channel store. */
27
22
  store: ChannelStore.ChannelStore;
28
23
  };
@@ -37,15 +32,6 @@ export type SessionSnapshotPaymentFields = {
37
32
  /** Payee address expected by the challenge. */
38
33
  recipient: Address;
39
34
  };
40
- /**
41
- * Validates an alternate channel descriptor against the logical session payment
42
- * fields. `options.action` names the credential the snapshot serves: close
43
- * credentials accept retired routes, so their recovery snapshots match
44
- * inactive routes too.
45
- */
46
- export type MatchSessionSnapshotPaymentFields = (channel: ChannelStore.StoredPrecompileChannel, expected: SessionSnapshotPaymentFields, options?: {
47
- action?: SessionCredentialPayload['action'] | undefined;
48
- } | undefined) => MaybePromise<boolean>;
49
35
  /** Request metadata available to `resolveChannelId` without exposing a mutable `Request`. */
50
36
  export type SessionChannelIdRequest = {
51
37
  /** Request headers, useful for cookies or auth/session headers. */
@@ -141,8 +127,6 @@ export type SessionMethodDetails = {
141
127
  feePayer?: boolean | undefined;
142
128
  /** Fee token clients must use for open/top-up transactions. */
143
129
  feeToken?: Address | undefined;
144
- /** Whether clients may fund this logical session through the first-party machine-token rail. */
145
- machineTokenEnabled?: boolean | undefined;
146
130
  /** Minimum raw-unit increase required for voucher credentials. */
147
131
  minVoucherDelta?: string | undefined;
148
132
  /** Channel operator address the client should encode in new open transactions. */
@@ -165,7 +149,6 @@ export type ResolveSessionPaymentRequestParameters = {
165
149
  decimals: number;
166
150
  defaultFeePayer?: viem_Account | undefined;
167
151
  getClient: ResolveRequestChainIdParameters['getClient'];
168
- matchSnapshotPaymentFields?: MatchSessionSnapshotPaymentFields | undefined;
169
152
  parameterChainId?: number | undefined;
170
153
  parameterEscrowContract?: Address | undefined;
171
154
  parameterFeePayer?: ParameterFeePayer;