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
@@ -1,4 +1,4 @@
1
- import { isAddressEqual, parseUnits } from 'viem';
1
+ import { parseUnits } from 'viem';
2
2
  import { tempo as tempo_chain } from 'viem/chains';
3
3
  import * as MethodChallenge from '../../../client/internal/MethodChallenge.js';
4
4
  import * as MethodResponse from '../../../client/internal/MethodResponse.js';
@@ -9,15 +9,13 @@ import * as Account from '../../../viem/Account.js';
9
9
  import * as Client from '../../../viem/Client.js';
10
10
  import * as AutoSwap from '../../internal/auto-swap.js';
11
11
  import * as defaults from '../../internal/defaults.js';
12
- import * as MachineTokenSession from '../../internal/machine-token-session.js';
13
12
  import * as Methods from '../../Methods.js';
14
- import * as Chain from '../precompile/Chain.js';
15
13
  import * as Channel from '../precompile/Channel.js';
16
- import { deserializeSessionReceipt, isEventStream, readSessionChallengeAmount, requireSessionCredentialContext, tip20ChannelEscrow, } from '../precompile/Protocol.js';
17
- import { createVoucherPayload, serializeCredential } from './ChannelOps.js';
14
+ import { deserializeSessionReceipt, isEventStream, readSessionChallengeAmount, requireSessionCredentialContext, } from '../precompile/Protocol.js';
15
+ import { serializeCredential } from './ChannelOps.js';
18
16
  import { createChannelStore, entryKey } from './ChannelStore.js';
19
- import { canSignDescriptor, executeCredentialPlan, hasSessionAction, planCredential, requireContextAmount, resolveChallengeContext, resolveRecoverContext, sessionContextSchema, } from './CredentialState.js';
20
- import { assertWithinMaxDeposit, resolveAutomaticTopUp, resolveOpeningDeposit } from './Runtime.js';
17
+ import { canSignDescriptor, executeCredentialPlan, hasSessionAction, planCredential, resolveChallengeContext, resolveRecoverContext, sessionContextSchema, } from './CredentialState.js';
18
+ import { assertWithinMaxDeposit, resolveAutomaticTopUp } from './Runtime.js';
21
19
  import { getSessionSnapshot, handleSseNeedVoucher, isTip1034SessionChallenge, postTopUp, wrapSseResponse, } from './Transports.js';
22
20
  export { sessionContextSchema } from './CredentialState.js';
23
21
  /** Tail promise for each payment scope, used to serialize automatic opens per store. */
@@ -71,42 +69,6 @@ function acknowledgesOpen(outcome, challenge, opened) {
71
69
  return matchesOpen(opened, getSessionSnapshot(candidate));
72
70
  }) ?? false);
73
71
  }
74
- function applyMachineTokenRoute(resolved, route) {
75
- const snapshotDescriptor = resolved.snapshot?.descriptor;
76
- const snapshotMatches = snapshotDescriptor &&
77
- isAddressEqual(snapshotDescriptor.payee, route.payee) &&
78
- isAddressEqual(snapshotDescriptor.operator, route.operator) &&
79
- isAddressEqual(snapshotDescriptor.token, route.token);
80
- return {
81
- ...resolved,
82
- operator: route.operator,
83
- payee: route.payee,
84
- paymentScope: { payee: resolved.payee, token: resolved.token },
85
- snapshot: snapshotMatches ? resolved.snapshot : undefined,
86
- token: route.token,
87
- };
88
- }
89
- async function resolveMachineContext(resolved, context) {
90
- const descriptor = context?.descriptor ?? resolved.snapshot?.descriptor;
91
- if (!descriptor)
92
- return undefined;
93
- if (!MachineTokenSession.matchDeployment({ chainId: resolved.chainId, descriptor }))
94
- return undefined;
95
- const closing = context?.action === 'close';
96
- if ((!closing && !MachineTokenSession.isEnabledChallenge(resolved.challenge)) ||
97
- !isAddressEqual(resolved.escrow, tip20ChannelEscrow))
98
- return undefined;
99
- const route = await MachineTokenSession.matchRoute(resolved.client, {
100
- active: !closing,
101
- chainId: resolved.chainId,
102
- descriptor,
103
- merchant: resolved.payee,
104
- targetToken: resolved.token,
105
- });
106
- if (!route)
107
- throw new Error('Machine-token channel is not bound to this merchant session challenge.');
108
- return applyMachineTokenRoute(resolved, route);
109
- }
110
72
  /**
111
73
  * Creates the low-level TIP-1034 session payment method for use with `Mppx.create()`.
112
74
  *
@@ -125,15 +87,7 @@ export function session(parameters = {}) {
125
87
  const maxDeposit = maxDepositParameter !== undefined ? parseUnits(maxDepositParameter, decimals) : undefined;
126
88
  const topUpAmount = topUpAmountParameter !== undefined ? parseUnits(topUpAmountParameter, decimals) : undefined;
127
89
  const store = channelStore ?? createChannelStore();
128
- const commitEntry = async (entry) => {
129
- if (!entry)
130
- return;
131
- if (entry.opened)
132
- await store.set(entry);
133
- else
134
- await store.delete(entryKey(entry));
135
- onChannelUpdate?.(entry);
136
- };
90
+ const sink = { store, notifyUpdate: (entry) => onChannelUpdate?.(entry) };
137
91
  const resolveCredentialAccount = async (resolved, context, entry) => {
138
92
  const defaultAccount = getAccount(resolved.client, context);
139
93
  const descriptor = context?.action
@@ -149,132 +103,67 @@ export function session(parameters = {}) {
149
103
  },
150
104
  })) ?? defaultAccount);
151
105
  };
152
- const resolveContext = (challenge) => resolveChallengeContext({
153
- allowCustomEscrow,
154
- challenge,
155
- escrowOverride,
156
- getClient,
106
+ const resolveCredentialPlan = async (resolved, context, entry) => planCredential({
107
+ account: await resolveCredentialAccount(resolved, context, entry),
108
+ entry,
109
+ context,
110
+ decimals,
111
+ maxDeposit,
112
+ resolved,
157
113
  });
158
- /** Selects one rail for this channel lifecycle before any credential is signed. */
159
- const resolveRail = async (direct, context) => {
160
- let account;
161
- const descriptor = context?.descriptor ?? direct.snapshot?.descriptor;
162
- if (descriptor || hasSessionAction(context)) {
163
- const machine = descriptor ? await resolveMachineContext(direct, context) : undefined;
164
- const resolved = machine ?? direct;
165
- return {
166
- account,
167
- entry: await store.get(resolved.key),
168
- machine: machine !== undefined,
169
- resolved,
170
- };
171
- }
172
- const directEntry = await store.get(direct.key);
173
- if (directEntry?.opened) {
174
- if (!MachineTokenSession.matchDeployment({
175
- chainId: direct.chainId,
176
- descriptor: directEntry.descriptor,
177
- }))
178
- return { account, entry: directEntry, machine: false, resolved: direct };
179
- const machine = await resolveMachineContext(direct, { descriptor: directEntry.descriptor });
180
- if (!machine)
181
- throw new Error('Machine-token channel is not bound to this merchant session challenge.');
182
- return { account, entry: directEntry, machine: true, resolved: machine };
183
- }
184
- if (!MachineTokenSession.isEnabledChallenge(direct.challenge) ||
185
- !isAddressEqual(direct.escrow, tip20ChannelEscrow))
186
- return { account, entry: undefined, machine: false, resolved: direct };
187
- const route = await MachineTokenSession.resolveRoute(direct.client, {
188
- chainId: direct.chainId,
189
- merchant: direct.payee,
190
- targetToken: direct.token,
191
- }).catch(() => undefined);
192
- if (!route)
193
- return { account, entry: undefined, machine: false, resolved: direct };
194
- const machine = applyMachineTokenRoute(direct, route);
195
- account = await resolveCredentialAccount(machine, context, undefined);
196
- const openingDeposit = resolveOpeningDeposit({
197
- contextDepositRaw: context?.depositRaw,
198
- maxDeposit,
199
- requestAmount: direct.amount,
200
- suggestedDepositRaw: direct.suggestedDepositRaw,
201
- });
202
- const funded = await MachineTokenSession.hasSufficientBalance(machine.client, {
203
- account: account.address,
204
- amount: openingDeposit,
205
- token: machine.token,
206
- });
207
- return funded
208
- ? { account, entry: undefined, machine: true, resolved: machine }
209
- : { account, entry: undefined, machine: false, resolved: direct };
210
- };
211
114
  const method = Method.toClient(Methods.session, {
212
115
  canHandleChallenge: ({ challenge }) => isTip1034SessionChallenge(challenge),
213
116
  context: sessionContextSchema,
214
117
  async createCredential(parameters) {
215
118
  const { challenge, context } = parameters;
119
+ const resolved = await resolveChallengeContext({
120
+ allowCustomEscrow,
121
+ challenge,
122
+ escrowOverride,
123
+ getClient,
124
+ });
216
125
  const attempt = MethodResponse.getAttempt(parameters);
217
126
  let release = () => { };
218
127
  try {
219
- const direct = await resolveContext(challenge);
220
- if (!hasSessionAction(context) && context?.descriptor === undefined) {
221
- release = await lockChannel(store, direct.key);
222
- if (attempt && (await store.get(direct.key))?.opened)
128
+ const automatic = !hasSessionAction(context) && context?.descriptor === undefined;
129
+ if (automatic) {
130
+ release = await lockChannel(store, resolved.key);
131
+ if (attempt && (await store.get(resolved.key))?.opened)
223
132
  await attempt.prepare();
224
133
  }
225
- const { account, entry, machine, resolved } = await resolveRail(direct, context);
226
- const credentialAccount = account ?? (await resolveCredentialAccount(resolved, context, entry));
227
- const plan = planCredential({
228
- account: credentialAccount,
229
- entry,
230
- context,
231
- decimals,
232
- maxDeposit,
233
- resolved,
234
- });
235
- if (machine && plan.type === 'manual' && plan.context.action === 'topUp') {
236
- const additionalDeposit = requireContextAmount(plan.context, plan.decimals, 'additionalDeposit', 'topUp');
237
- if (!(await MachineTokenSession.hasSufficientBalance(plan.resolved.client, {
238
- account: plan.account.address,
239
- amount: additionalDeposit,
240
- token: plan.resolved.token,
241
- })))
242
- throw new Error('Insufficient machine-token balance for session top-up.');
243
- }
244
- const feeToken = MachineTokenSession.resolveFeeToken({
245
- chainId: resolved.chainId,
246
- override: resolved.feeToken,
247
- paymentToken: resolved.token,
248
- });
249
- const result = await executeCredentialPlan(plan, AutoSwap.resolve(context?.autoSwap ?? autoSwapParameter, AutoSwap.defaultCurrencies), feeToken);
250
- let { payload } = result;
251
- if (machine && payload.action !== 'topUp') {
252
- if (payload.action === 'close') {
253
- const state = await Chain.getChannelState(plan.resolved.client, payload.channelId, plan.resolved.escrow);
254
- if (state.deposit === 0n)
255
- throw new Error('Cannot authorize a machine-token refund for an empty channel.');
256
- // The refund authorization covers the full on-chain deposit, while
257
- // the close voucher remains the exact amount the merchant captures.
258
- const depositSignature = BigInt(payload.cumulativeAmount) === state.deposit
259
- ? payload.signature
260
- : (await createVoucherPayload(plan.resolved.client, plan.account, payload.descriptor, state.deposit, plan.resolved.chainId, plan.resolved.escrow)).signature;
261
- payload = { ...payload, refundSignature: depositSignature };
134
+ let entry = await store.get(resolved.key);
135
+ let plan = await resolveCredentialPlan(resolved, context, entry);
136
+ if (!automatic && attempt && plan.type === 'open') {
137
+ release = await lockChannel(store, resolved.key);
138
+ const nextEntry = await store.get(resolved.key);
139
+ if (nextEntry?.channelId !== entry?.channelId) {
140
+ entry = nextEntry;
141
+ if (entry?.opened) {
142
+ await attempt.prepare();
143
+ entry = await store.get(resolved.key);
144
+ }
145
+ plan = await resolveCredentialPlan(resolved, context, entry);
262
146
  }
263
- const authorizationSignature = await MachineTokenSession.signAuthorization(plan.resolved.client, plan.account, {
264
- authorization: {
265
- channelId: payload.channelId,
266
- cumulativeAmount: BigInt(payload.cumulativeAmount),
267
- },
268
- chainId: plan.resolved.chainId,
269
- router: plan.resolved.operator,
270
- });
271
- payload = { ...payload, authorizationSignature };
272
147
  }
273
- if (!(attempt && plan.type === 'open'))
274
- await commitEntry(result.entry);
148
+ let pendingOpen;
149
+ // Defer opens only when low-level Fetch owns the response lifecycle.
150
+ // SessionManager unregisters this hook and keeps its existing transaction boundary.
151
+ const credentialSink = attempt && plan.type === 'open'
152
+ ? {
153
+ store: {
154
+ get: (key) => store.get(key),
155
+ async set(next) {
156
+ pendingOpen = next;
157
+ },
158
+ delete: (key) => store.delete(key),
159
+ },
160
+ notifyUpdate() { },
161
+ }
162
+ : sink;
163
+ const payload = await executeCredentialPlan(plan, credentialSink, AutoSwap.resolve(context?.autoSwap ?? autoSwapParameter, AutoSwap.defaultCurrencies));
275
164
  const credential = await serializeCredential(challenge, payload, resolved.chainId, plan.account);
276
- if (attempt && plan.type === 'open' && result.entry) {
277
- const opened = result.entry;
165
+ if (attempt && pendingOpen) {
166
+ const opened = pendingOpen;
278
167
  attempt.settle = async (outcome) => {
279
168
  const accepted = outcome.status === 'accepted' || acknowledgesOpen(outcome, challenge, opened);
280
169
  if (!accepted && outcome.status === 'pending')
@@ -284,7 +173,8 @@ export function session(parameters = {}) {
284
173
  const current = await store.get(resolved.key);
285
174
  if (!current?.opened ||
286
175
  current.channelId.toLowerCase() !== opened.channelId.toLowerCase()) {
287
- await commitEntry(opened);
176
+ await store.set(opened);
177
+ sink.notifyUpdate(opened);
288
178
  }
289
179
  }
290
180
  return true;
@@ -335,7 +225,7 @@ export function session(parameters = {}) {
335
225
  deposit: latest.deposit > nextDeposit ? latest.deposit : nextDeposit,
336
226
  };
337
227
  await store.set(updated);
338
- onChannelUpdate?.(updated);
228
+ sink.notifyUpdate(updated);
339
229
  };
340
230
  MethodChallenge.register(method, async ({ challenge, context, fetch, input }) => {
341
231
  if (!isTip1034SessionChallenge(challenge))
@@ -343,11 +233,13 @@ export function session(parameters = {}) {
343
233
  const sessionContext = context === undefined ? undefined : sessionContextSchema.parse(context);
344
234
  if (hasSessionAction(sessionContext))
345
235
  return;
346
- const direct = await resolveContext(challenge);
347
- const { entry: channel, resolved } = await resolveRail(direct, sessionContext).catch(() => ({
348
- entry: undefined,
349
- resolved: direct,
350
- }));
236
+ const resolved = await resolveChallengeContext({
237
+ allowCustomEscrow,
238
+ challenge,
239
+ escrowOverride,
240
+ getClient,
241
+ });
242
+ const channel = await store.get(resolved.key);
351
243
  if (!channel?.opened)
352
244
  return;
353
245
  const snapshot = resolved.snapshot?.channelId.toLowerCase() === channel.channelId.toLowerCase()
@@ -375,17 +267,20 @@ export function session(parameters = {}) {
375
267
  return MethodResponse.register(method, async ({ challenge, credential, fetch, headers, input, refetch, response, signal }) => {
376
268
  if (!isTip1034SessionChallenge(challenge))
377
269
  return response;
378
- const credentialContext = requireSessionCredentialContext(Credential.deserialize(credential).payload);
379
270
  if (!isEventStream(response)) {
271
+ const credentialContext = requireSessionCredentialContext(Credential.deserialize(credential).payload);
380
272
  if (credentialContext.action === 'open' &&
381
273
  headers.get('accept')?.toLowerCase().includes('text/event-stream'))
382
274
  return (await refetch?.()) ?? response;
383
275
  return response;
384
276
  }
385
- const resolvedContext = await resolveContext(challenge)
386
- .then(async (direct) => (await resolveMachineContext(direct, credentialContext)) ?? direct)
387
- .catch(() => undefined);
388
- const channel = resolvedContext ? await store.get(resolvedContext.key) : undefined;
277
+ const channelKey = (await resolveChallengeContext({
278
+ allowCustomEscrow,
279
+ challenge,
280
+ escrowOverride,
281
+ getClient,
282
+ })).key;
283
+ let channel = await store.get(channelKey);
389
284
  const driver = {
390
285
  assertVoucherWithinLocalLimit: (cumulativeAmount) => assertWithinMaxDeposit(cumulativeAmount, maxDeposit),
391
286
  createSessionCredential: (challenge, context) => method.createCredential({ challenge, context }),
@@ -9,7 +9,6 @@ import * as Account from '../../../viem/Account.js';
9
9
  import * as Client from '../../../viem/Client.js';
10
10
  import { charge as chargePlugin } from '../../client/Charge.js';
11
11
  import * as defaults from '../../internal/defaults.js';
12
- import { hasRewrittenScope, isMachineChannel } from '../client/ChannelOps.js';
13
12
  import { createChannelStore, entryKey } from '../client/ChannelStore.js';
14
13
  import { hydrateSessionSnapshot } from '../client/CredentialState.js';
15
14
  import { session as sessionPlugin } from '../client/Session.js';
@@ -274,7 +273,7 @@ export function sessionManager(parameters) {
274
273
  });
275
274
  }
276
275
  /** Persists a server snapshot into the channel store and returns the entry. */
277
- async function storeSnapshotHeader(response, paymentScope) {
276
+ async function storeSnapshotHeader(response) {
278
277
  const header = response.headers.get(Constants.Headers.paymentSessionSnapshot);
279
278
  if (!header)
280
279
  return undefined;
@@ -289,14 +288,11 @@ export function sessionManager(parameters) {
289
288
  kind: 'authorizePaymentChannel',
290
289
  },
291
290
  })) ?? defaultAccount;
292
- const hydrated = await hydrateSessionSnapshot({ account, client, snapshot });
293
- const entry = paymentScope && hasRewrittenScope({ descriptor: hydrated.entry.descriptor, paymentScope })
294
- ? { ...hydrated.entry, paymentScope }
295
- : hydrated.entry;
291
+ const { entry, spent } = await hydrateSessionSnapshot({ account, client, snapshot });
296
292
  assertVoucherWithinLocalLimit(entry.cumulativeAmount);
297
293
  await Promise.resolve(store.set(entry)).catch(() => undefined);
298
294
  runtime.channel = entry;
299
- runtime.spent = hydrated.spent;
295
+ runtime.spent = spent;
300
296
  return entry;
301
297
  }
302
298
  async function bootstrapSession(input, init) {
@@ -337,10 +333,7 @@ export function sessionManager(parameters) {
337
333
  },
338
334
  });
339
335
  if (response.ok)
340
- return await storeSnapshotHeader(response, {
341
- payee: challenge.request.recipient,
342
- token: challenge.request.currency,
343
- });
336
+ return await storeSnapshotHeader(response);
344
337
  return undefined;
345
338
  }
346
339
  catch {
@@ -381,9 +374,8 @@ export function sessionManager(parameters) {
381
374
  throw new Error('close snapshot payment scope does not match local session');
382
375
  }
383
376
  const acceptedCumulative = BigInt(snapshot.acceptedCumulative);
384
- const settled = BigInt(snapshot.settled);
385
377
  const snapshotSpent = BigInt(snapshot.spent);
386
- if (acceptedCumulative < 0n || settled < 0n || snapshotSpent < 0n) {
378
+ if (acceptedCumulative < 0n || snapshotSpent < 0n) {
387
379
  throw new Error('close snapshot amounts must not be negative');
388
380
  }
389
381
  if (snapshotSpent > acceptedCumulative) {
@@ -392,7 +384,7 @@ export function sessionManager(parameters) {
392
384
  if (acceptedCumulative > channel.cumulativeAmount) {
393
385
  throw new Error('close snapshot accepted cumulative exceeds local voucher state');
394
386
  }
395
- return { acceptedCumulative, settled, spent: snapshotSpent };
387
+ return { acceptedCumulative, spent: snapshotSpent };
396
388
  }
397
389
  function applyCloseSnapshot(target, challenge) {
398
390
  const snapshot = validateCloseSnapshot(target.channel, challenge);
@@ -410,14 +402,7 @@ export function sessionManager(parameters) {
410
402
  const snapshot = applySnapshot
411
403
  ? applyCloseSnapshot(target, challenge)
412
404
  : validateCloseSnapshot(target.channel, challenge);
413
- const machineSession = isMachineChannel(target.channel);
414
- const closeAmount = snapshot
415
- ? machineSession
416
- ? snapshot.spent > snapshot.settled
417
- ? snapshot.spent
418
- : snapshot.settled
419
- : snapshot.acceptedCumulative
420
- : getFallbackCloseAmount(challenge, target.channelId);
405
+ const closeAmount = snapshot?.acceptedCumulative ?? getFallbackCloseAmount(challenge, target.channelId);
421
406
  if (closeAmount > target.channel.cumulativeAmount) {
422
407
  throw new Error('fallback close amount exceeds local voucher state');
423
408
  }
@@ -545,10 +530,11 @@ export function sessionManager(parameters) {
545
530
  trackCreates: false,
546
531
  };
547
532
  channelUse = use;
533
+ // Cold starts resume from `channelStore` after the 402 reveals the scope.
534
+ const liveHint = runtime.channel?.opened ? runtime.channel.channelId : undefined;
548
535
  try {
549
536
  await bootstrapSession(input, init);
550
537
  use.trackCreates = true;
551
- const liveHint = runtime.channel?.opened ? runtime.channel.channelId : undefined;
552
538
  let effectiveInit = requestInitWithSessionHint(input, init, liveHint);
553
539
  // Stored channels may be stale, so retry once after evicting the resumed entry.
554
540
  let canRetryResumed = !previous.channel?.opened;
@@ -556,11 +542,6 @@ export function sessionManager(parameters) {
556
542
  const resumed = use.resumed;
557
543
  if (!canRetryResumed || !resumed)
558
544
  return false;
559
- // A machine-token entry holds the only local copy of its channel
560
- // descriptor; evicting it would strand the deposit, so fail loudly
561
- // instead of retrying without it.
562
- if (resumed.opened && (isMachineChannel(resumed) || hasRewrittenScope(resumed)))
563
- return false;
564
545
  canRetryResumed = false;
565
546
  await ignoreChannel(resumed);
566
547
  effectiveInit = requestInitWithSessionHint(input, init, undefined);
@@ -197,8 +197,6 @@ export declare function simulateCredentialTransaction(parameters: {
197
197
  * Submit a settle transaction on-chain.
198
198
  */
199
199
  export declare function settleOnChain(client: Client, descriptor: ChannelDescriptor, cumulativeAmount: bigint, signature: Hex, escrow?: Address, options?: ChannelTransactionOptions): Promise<Hex>;
200
- /** Submit a settle transaction through the first-party machine-token session router. */
201
- export declare function settleMachineSessionOnChain(client: Client, descriptor: ChannelDescriptor, cumulativeAmount: bigint, signature: Hex, authorizationSignature: Hex, router: Address, options?: ChannelTransactionOptions): Promise<Hex>;
202
200
  /**
203
201
  * Submit a top-up transaction on-chain.
204
202
  */
@@ -215,8 +213,6 @@ export declare function withdrawOnChain(client: Client, descriptor: ChannelDescr
215
213
  * Submit a close transaction on-chain.
216
214
  */
217
215
  export declare function closeOnChain(client: Client, descriptor: ChannelDescriptor, cumulativeAmount: bigint, captureAmount: bigint, signature: Hex, escrow?: Address, options?: ChannelTransactionOptions): Promise<Hex>;
218
- /** Submit a cooperative close through the first-party machine-token session router. */
219
- export declare function closeMachineSessionOnChain(client: Client, descriptor: ChannelDescriptor, cumulativeAmount: bigint, signature: Hex, authorizationSignature: Hex, refundSignature: Hex, router: Address, options?: ChannelTransactionOptions): Promise<Hex>;
220
216
  /** Receipt event shape emitted by TIP20EscrowChannel precompile management calls. */
221
217
  export type ChannelReceiptEvent = {
222
218
  args: {
@@ -1,4 +1,4 @@
1
- import { decodeFunctionData, encodeFunctionData, isAddressEqual, parseAbi, parseEventLogs, } from 'viem';
1
+ import { decodeFunctionData, encodeFunctionData, isAddressEqual, parseEventLogs } from 'viem';
2
2
  import { call, prepareTransactionRequest, readContract, sendRawTransaction, sendRawTransactionSync, sendTransaction as sendViemTransaction, signTransaction, waitForTransactionReceipt, } from 'viem/actions';
3
3
  import { Abis, Addresses, Transaction } from 'viem/tempo';
4
4
  import { BadRequestError, VerificationFailedError } from '../../../Errors.js';
@@ -8,10 +8,6 @@ import * as ChannelOps from '../server/ChannelOps.js';
8
8
  import * as ChannelUtils from './Channel.js';
9
9
  import { escrowAbi } from './escrow.abi.js';
10
10
  import { tip20ChannelEscrow } from './Protocol.js';
11
- const machineSessionAbi = parseAbi([
12
- 'function settleSession((address payer,address payee,address operator,address token,bytes32 salt,address authorizedSigner,bytes32 expiringNonceHash) descriptor,uint96 cumulativeAmount,bytes signature,bytes authorizationSignature)',
13
- 'function closeSession((address payer,address payee,address operator,address token,bytes32 salt,address authorizedSigner,bytes32 expiringNonceHash) descriptor,uint96 cumulativeAmount,bytes signature,bytes authorizationSignature,bytes refundSignature)',
14
- ]);
15
11
  const uint96Max = 2n ** 96n - 1n;
16
12
  function readBytes32(value, label) {
17
13
  if (typeof value === 'string' && /^0x[0-9a-fA-F]{64}$/.test(value))
@@ -345,26 +341,6 @@ export async function settleOnChain(client, descriptor, cumulativeAmount, signat
345
341
  const args = [descriptorTuple(descriptor), cumulativeAmount, signature];
346
342
  return sendPrecompileTransaction(client, escrow, encodeFunctionData({ abi: escrowAbi, functionName: 'settle', args }), 'settle', options);
347
343
  }
348
- function encodeMachineSessionCall(descriptor, cumulativeAmount, signature, authorizationSignature, refundSignature) {
349
- assertUint96(cumulativeAmount);
350
- const args = [
351
- descriptorTuple(descriptor),
352
- cumulativeAmount,
353
- signature,
354
- authorizationSignature,
355
- ];
356
- return refundSignature === undefined
357
- ? encodeFunctionData({ abi: machineSessionAbi, functionName: 'settleSession', args })
358
- : encodeFunctionData({
359
- abi: machineSessionAbi,
360
- functionName: 'closeSession',
361
- args: [...args, refundSignature],
362
- });
363
- }
364
- /** Submit a settle transaction through the first-party machine-token session router. */
365
- export async function settleMachineSessionOnChain(client, descriptor, cumulativeAmount, signature, authorizationSignature, router, options) {
366
- return sendPrecompileTransaction(client, router, encodeMachineSessionCall(descriptor, cumulativeAmount, signature, authorizationSignature), 'settle machine-token session', options);
367
- }
368
344
  /**
369
345
  * Submit a top-up transaction on-chain.
370
346
  */
@@ -396,10 +372,6 @@ export async function closeOnChain(client, descriptor, cumulativeAmount, capture
396
372
  const args = [descriptorTuple(descriptor), cumulativeAmount, captureAmount, signature];
397
373
  return sendPrecompileTransaction(client, escrow, encodeFunctionData({ abi: escrowAbi, functionName: 'close', args }), 'close', options);
398
374
  }
399
- /** Submit a cooperative close through the first-party machine-token session router. */
400
- export async function closeMachineSessionOnChain(client, descriptor, cumulativeAmount, signature, authorizationSignature, refundSignature, router, options) {
401
- return sendPrecompileTransaction(client, router, encodeMachineSessionCall(descriptor, cumulativeAmount, signature, authorizationSignature, refundSignature), 'close machine-token session', options);
402
- }
403
375
  /**
404
376
  * Asserts that a deserialized transaction has an existing sender signature.
405
377
  */
@@ -57,8 +57,6 @@ export type OpenCredentialPayload = {
57
57
  transaction: Hex;
58
58
  /** Voucher signature for `cumulativeAmount`. */
59
59
  signature: Hex;
60
- /** Machine-token-only router-domain guard for `cumulativeAmount`. */
61
- authorizationSignature?: Hex | undefined;
62
60
  /** Descriptor needed to recover and verify the channel. */
63
61
  descriptor: ChannelDescriptor;
64
62
  /** Initial cumulative spend authorized by the opening voucher, as raw units. */
@@ -94,8 +92,6 @@ export type VoucherCredentialPayload = {
94
92
  cumulativeAmount: RawAmountString;
95
93
  /** Voucher signature for `cumulativeAmount`. */
96
94
  signature: Hex;
97
- /** Machine-token-only router-domain guard for `cumulativeAmount`. */
98
- authorizationSignature?: Hex | undefined;
99
95
  };
100
96
  /**
101
97
  * Credential payload that cooperatively closes a channel at final cumulative spend.
@@ -110,13 +106,6 @@ export type CloseCredentialPayload = {
110
106
  cumulativeAmount: RawAmountString;
111
107
  /** Voucher signature for `cumulativeAmount`. */
112
108
  signature: Hex;
113
- /** Machine-token-only router-domain guard for `cumulativeAmount`. */
114
- authorizationSignature?: Hex | undefined;
115
- /**
116
- * Machine-token-only signature for the full on-chain deposit. It lets the trusted router
117
- * return policy-restricted principal without increasing the merchant's authorized capture.
118
- */
119
- refundSignature?: Hex | undefined;
120
109
  };
121
110
  /**
122
111
  * TIP20EscrowChannel precompile session credential payload (discriminated union).
@@ -135,8 +124,6 @@ export interface SessionVoucher {
135
124
  */
136
125
  export interface SessionSignedVoucher extends SessionVoucher {
137
126
  signature: Hex;
138
- /** Router-domain guard retained for scheduled machine-token settlement. */
139
- authorizationSignature?: Hex | undefined;
140
127
  }
141
128
  /**
142
129
  * Management action names shared by session credential payloads.
@@ -36,7 +36,5 @@ export declare function verifyVoucher(escrowContract: Address, chainId: number,
36
36
  /**
37
37
  * Parse a voucher from credential payload.
38
38
  */
39
- export declare function parseVoucherFromPayload(channelId: Hex, cumulativeAmount: string, signature: Hex, authorizationSignature?: Hex | undefined): SignedVoucher & {
40
- authorizationSignature?: Hex | undefined;
41
- };
39
+ export declare function parseVoucherFromPayload(channelId: Hex, cumulativeAmount: string, signature: Hex): SignedVoucher;
42
40
  //# sourceMappingURL=Voucher.d.ts.map
@@ -3,7 +3,6 @@ import { hashTypedData } from 'viem';
3
3
  import { signTypedData } from 'viem/actions';
4
4
  import { Account as TempoAccount } from 'viem/tempo';
5
5
  import * as TempoAddress from '../../internal/address.js';
6
- import { parseCanonicalEnvelope, serializeCanonicalEnvelope, } from '../../internal/signature-envelope.js';
7
6
  import { uint96 } from './Protocol.js';
8
7
  /** Must match the on-chain TIP-20 channel reserve DOMAIN_SEPARATOR name. */
9
8
  const DOMAIN_NAME = 'TIP20 Channel Reserve';
@@ -50,6 +49,9 @@ function getVoucherPayload(verifyingContract, chainId, voucher) {
50
49
  },
51
50
  });
52
51
  }
52
+ function isPrimitiveEnvelope(type) {
53
+ return type === 'secp256k1' || type === 'p256' || type === 'webAuthn';
54
+ }
53
55
  function signCanonicalTempoVoucher(account, parameters) {
54
56
  // viem/tempo's canonical TIP-1034 voucher signer accepts Tempo account
55
57
  // extensions that are wider than viem's base Account type. Keep that
@@ -83,7 +85,10 @@ export async function signVoucher(client, account, voucher, verifyingContract, c
83
85
  message: voucher,
84
86
  });
85
87
  })();
86
- return serializeCanonicalEnvelope(signature, 'TIP-1034 vouchers');
88
+ const envelope = SignatureEnvelope.from(signature);
89
+ if (!isPrimitiveEnvelope(envelope.type))
90
+ throw new Error(`TIP-1034 vouchers require a TIP-1020 primitive signature; received "${envelope.type}".`);
91
+ return SignatureEnvelope.serialize(envelope);
87
92
  }
88
93
  /**
89
94
  * Verify a voucher signature matches the expected signer.
@@ -93,8 +98,10 @@ export async function signVoucher(client, account, voucher, verifyingContract, c
93
98
  */
94
99
  export function verifyVoucher(escrowContract, chainId, voucher, expectedSigner) {
95
100
  try {
96
- const envelope = parseCanonicalEnvelope(voucher.signature);
97
- if (!envelope)
101
+ const envelope = SignatureEnvelope.from(voucher.signature);
102
+ if (!isPrimitiveEnvelope(envelope.type))
103
+ return false;
104
+ if (SignatureEnvelope.serialize(envelope).toLowerCase() !== voucher.signature.toLowerCase())
98
105
  return false;
99
106
  const payload = getVoucherPayload(escrowContract, chainId, voucher);
100
107
  const signer = SignatureEnvelope.extractAddress({ payload, signature: envelope });
@@ -108,12 +115,11 @@ export function verifyVoucher(escrowContract, chainId, voucher, expectedSigner)
108
115
  /**
109
116
  * Parse a voucher from credential payload.
110
117
  */
111
- export function parseVoucherFromPayload(channelId, cumulativeAmount, signature, authorizationSignature) {
118
+ export function parseVoucherFromPayload(channelId, cumulativeAmount, signature) {
112
119
  return {
113
120
  channelId,
114
121
  cumulativeAmount: uint96(BigInt(cumulativeAmount)),
115
122
  signature,
116
- ...(authorizationSignature ? { authorizationSignature } : {}),
117
123
  };
118
124
  }
119
125
  //# sourceMappingURL=Voucher.js.map
@@ -54,8 +54,6 @@ export type HighestVoucherParameters = {
54
54
  cumulativeAmount: bigint;
55
55
  /** Candidate voucher signature. */
56
56
  signature: Hex;
57
- /** Optional router-domain authorization retained for machine-token settlement. */
58
- authorizationSignature?: Hex | undefined;
59
57
  };
60
58
  /** Highest accepted voucher amount plus signed voucher payload. */
61
59
  export type HighestVoucher = {
@@ -95,8 +93,6 @@ export type OpenChannelStateParameters = {
95
93
  cumulativeAmount: bigint;
96
94
  /** Voucher signature for `cumulativeAmount`. */
97
95
  signature: Hex;
98
- /** Optional router-domain authorization for the initial machine-token voucher. */
99
- authorizationSignature?: Hex | undefined;
100
96
  /** Latest on-chain channel state read after open. */
101
97
  state: Chain.ChannelState;
102
98
  };
@@ -138,8 +134,6 @@ export type PendingCloseUpdate = {
138
134
  };
139
135
  /** Inputs for finalizing local state after a successful close transaction. */
140
136
  export type FinalizeClosedChannelParameters = {
141
- /** Router authorization paired with the close voucher on machine channels. */
142
- authorizationSignature?: Hex | undefined;
143
137
  /** Amount captured to payee during close. */
144
138
  captureAmount: bigint;
145
139
  /** Normalized channel ID. */