mppx 0.7.0 → 0.8.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 (278) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +20 -11
  3. package/dist/Challenge.d.ts.map +1 -1
  4. package/dist/Challenge.js +18 -6
  5. package/dist/Challenge.js.map +1 -1
  6. package/dist/Mcp.d.ts +3 -0
  7. package/dist/Mcp.d.ts.map +1 -1
  8. package/dist/Mcp.js +2 -0
  9. package/dist/Mcp.js.map +1 -1
  10. package/dist/PaymentRequest.d.ts +10 -10
  11. package/dist/PaymentRequest.js +8 -8
  12. package/dist/client/Mppx.js +2 -2
  13. package/dist/client/Mppx.js.map +1 -1
  14. package/dist/client/Transport.d.ts +11 -16
  15. package/dist/client/Transport.d.ts.map +1 -1
  16. package/dist/client/Transport.js +55 -75
  17. package/dist/client/Transport.js.map +1 -1
  18. package/dist/client/index.d.ts +3 -0
  19. package/dist/client/index.d.ts.map +1 -1
  20. package/dist/client/index.js +1 -0
  21. package/dist/client/index.js.map +1 -1
  22. package/dist/client/internal/Fetch.d.ts.map +1 -1
  23. package/dist/client/internal/Fetch.js +46 -7
  24. package/dist/client/internal/Fetch.js.map +1 -1
  25. package/dist/client/internal/protocols/Mcp.d.ts +7 -0
  26. package/dist/client/internal/protocols/Mcp.d.ts.map +1 -0
  27. package/dist/client/internal/protocols/Mcp.js +159 -0
  28. package/dist/client/internal/protocols/Mcp.js.map +1 -0
  29. package/dist/client/internal/protocols/Mpp.d.ts +4 -0
  30. package/dist/client/internal/protocols/Mpp.d.ts.map +1 -0
  31. package/dist/client/internal/protocols/Mpp.js +18 -0
  32. package/dist/client/internal/protocols/Mpp.js.map +1 -0
  33. package/dist/client/internal/protocols/Protocol.d.ts +10 -0
  34. package/dist/client/internal/protocols/Protocol.d.ts.map +1 -0
  35. package/dist/client/internal/protocols/Protocol.js +2 -0
  36. package/dist/client/internal/protocols/Protocol.js.map +1 -0
  37. package/dist/client/internal/protocols/Shared.d.ts +5 -0
  38. package/dist/client/internal/protocols/Shared.d.ts.map +1 -0
  39. package/dist/client/internal/protocols/Shared.js +20 -0
  40. package/dist/client/internal/protocols/Shared.js.map +1 -0
  41. package/dist/client/internal/protocols/X402.d.ts +8 -0
  42. package/dist/client/internal/protocols/X402.d.ts.map +1 -0
  43. package/dist/client/internal/protocols/X402.js +39 -0
  44. package/dist/client/internal/protocols/X402.js.map +1 -0
  45. package/dist/evm/client/index.d.ts +1 -0
  46. package/dist/evm/client/index.d.ts.map +1 -1
  47. package/dist/evm/client/index.js +1 -0
  48. package/dist/evm/client/index.js.map +1 -1
  49. package/dist/evm/index.d.ts +2 -0
  50. package/dist/evm/index.d.ts.map +1 -1
  51. package/dist/evm/index.js +2 -0
  52. package/dist/evm/index.js.map +1 -1
  53. package/dist/evm/server/index.d.ts +1 -0
  54. package/dist/evm/server/index.d.ts.map +1 -1
  55. package/dist/evm/server/index.js +1 -0
  56. package/dist/evm/server/index.js.map +1 -1
  57. package/dist/mcp/client/McpClient.d.ts +101 -0
  58. package/dist/mcp/client/McpClient.d.ts.map +1 -0
  59. package/dist/mcp/client/McpClient.js +162 -0
  60. package/dist/mcp/client/McpClient.js.map +1 -0
  61. package/dist/mcp/client/index.d.ts.map +1 -0
  62. package/dist/mcp/client/index.js.map +1 -0
  63. package/dist/mcp/server/Transport.d.ts.map +1 -0
  64. package/dist/mcp/server/Transport.js.map +1 -0
  65. package/dist/mcp/server/index.d.ts.map +1 -0
  66. package/dist/mcp/server/index.js.map +1 -0
  67. package/dist/server/Mppx.d.ts +1 -1
  68. package/dist/server/Mppx.d.ts.map +1 -1
  69. package/dist/server/Mppx.js +9 -0
  70. package/dist/server/Mppx.js.map +1 -1
  71. package/dist/server/Transport.d.ts +1 -1
  72. package/dist/server/Transport.d.ts.map +1 -1
  73. package/dist/server/Transport.js +1 -1
  74. package/dist/server/Transport.js.map +1 -1
  75. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  76. package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
  77. package/dist/stripe/server/internal/html.gen.js +1 -1
  78. package/dist/stripe/server/internal/html.gen.js.map +1 -1
  79. package/dist/tempo/Proof.d.ts +85 -1
  80. package/dist/tempo/Proof.d.ts.map +1 -1
  81. package/dist/tempo/Proof.js +35 -0
  82. package/dist/tempo/Proof.js.map +1 -1
  83. package/dist/tempo/client/Charge.d.ts +13 -1
  84. package/dist/tempo/client/Charge.d.ts.map +1 -1
  85. package/dist/tempo/client/Charge.js +38 -25
  86. package/dist/tempo/client/Charge.js.map +1 -1
  87. package/dist/tempo/client/Methods.d.ts +5 -3
  88. package/dist/tempo/client/Methods.d.ts.map +1 -1
  89. package/dist/tempo/client/Methods.js +4 -2
  90. package/dist/tempo/client/Methods.js.map +1 -1
  91. package/dist/tempo/client/ResolveAccount.d.ts +40 -0
  92. package/dist/tempo/client/ResolveAccount.d.ts.map +1 -0
  93. package/dist/tempo/client/ResolveAccount.js +2 -0
  94. package/dist/tempo/client/ResolveAccount.js.map +1 -0
  95. package/dist/tempo/internal/fee-payer.d.ts +9 -1
  96. package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
  97. package/dist/tempo/internal/fee-payer.js +35 -6
  98. package/dist/tempo/internal/fee-payer.js.map +1 -1
  99. package/dist/tempo/internal/proof.d.ts +71 -5
  100. package/dist/tempo/internal/proof.d.ts.map +1 -1
  101. package/dist/tempo/internal/proof.js +42 -6
  102. package/dist/tempo/internal/proof.js.map +1 -1
  103. package/dist/tempo/legacy/client/SessionManager.d.ts.map +1 -1
  104. package/dist/tempo/legacy/client/SessionManager.js +10 -3
  105. package/dist/tempo/legacy/client/SessionManager.js.map +1 -1
  106. package/dist/tempo/server/Charge.d.ts.map +1 -1
  107. package/dist/tempo/server/Charge.js +42 -18
  108. package/dist/tempo/server/Charge.js.map +1 -1
  109. package/dist/tempo/server/Methods.d.ts +4 -2
  110. package/dist/tempo/server/Methods.d.ts.map +1 -1
  111. package/dist/tempo/server/Methods.js +4 -2
  112. package/dist/tempo/server/Methods.js.map +1 -1
  113. package/dist/tempo/server/Subscription.d.ts +10 -0
  114. package/dist/tempo/server/Subscription.d.ts.map +1 -1
  115. package/dist/tempo/server/Subscription.js +135 -23
  116. package/dist/tempo/server/Subscription.js.map +1 -1
  117. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  118. package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
  119. package/dist/tempo/server/internal/html.gen.js +1 -1
  120. package/dist/tempo/server/internal/html.gen.js.map +1 -1
  121. package/dist/tempo/session/client/ChannelOps.d.ts +2 -3
  122. package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -1
  123. package/dist/tempo/session/client/ChannelOps.js +7 -10
  124. package/dist/tempo/session/client/ChannelOps.js.map +1 -1
  125. package/dist/tempo/session/client/ChannelStore.d.ts +51 -0
  126. package/dist/tempo/session/client/ChannelStore.d.ts.map +1 -0
  127. package/dist/tempo/session/client/ChannelStore.js +63 -0
  128. package/dist/tempo/session/client/ChannelStore.js.map +1 -0
  129. package/dist/tempo/session/client/CredentialState.d.ts +7 -24
  130. package/dist/tempo/session/client/CredentialState.d.ts.map +1 -1
  131. package/dist/tempo/session/client/CredentialState.js +51 -49
  132. package/dist/tempo/session/client/CredentialState.js.map +1 -1
  133. package/dist/tempo/session/client/Session.d.ts +8 -2
  134. package/dist/tempo/session/client/Session.d.ts.map +1 -1
  135. package/dist/tempo/session/client/Session.js +22 -8
  136. package/dist/tempo/session/client/Session.js.map +1 -1
  137. package/dist/tempo/session/client/SessionManager.d.ts +4 -40
  138. package/dist/tempo/session/client/SessionManager.d.ts.map +1 -1
  139. package/dist/tempo/session/client/SessionManager.js +124 -174
  140. package/dist/tempo/session/client/SessionManager.js.map +1 -1
  141. package/dist/tempo/session/client/index.d.ts +3 -4
  142. package/dist/tempo/session/client/index.d.ts.map +1 -1
  143. package/dist/tempo/session/client/index.js +1 -0
  144. package/dist/tempo/session/client/index.js.map +1 -1
  145. package/dist/tempo/session/precompile/Voucher.d.ts +3 -3
  146. package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -1
  147. package/dist/tempo/session/precompile/Voucher.js +24 -25
  148. package/dist/tempo/session/precompile/Voucher.js.map +1 -1
  149. package/dist/tempo/session/server/Settlement.d.ts.map +1 -1
  150. package/dist/tempo/session/server/Settlement.js +4 -2
  151. package/dist/tempo/session/server/Settlement.js.map +1 -1
  152. package/dist/tempo/session/server/Sse.d.ts.map +1 -1
  153. package/dist/tempo/session/server/Sse.js.map +1 -1
  154. package/dist/tempo/session/server/Ws.d.ts.map +1 -1
  155. package/dist/tempo/session/server/Ws.js.map +1 -1
  156. package/dist/tempo/subscription/KeyAuthorization.d.ts +712 -1
  157. package/dist/tempo/subscription/KeyAuthorization.d.ts.map +1 -1
  158. package/dist/tempo/subscription/Store.d.ts +2 -0
  159. package/dist/tempo/subscription/Store.d.ts.map +1 -1
  160. package/dist/tempo/subscription/Store.js +16 -1
  161. package/dist/tempo/subscription/Store.js.map +1 -1
  162. package/dist/x402/index.d.ts +1 -0
  163. package/dist/x402/index.d.ts.map +1 -1
  164. package/dist/x402/index.js +1 -0
  165. package/dist/x402/index.js.map +1 -1
  166. package/package.json +21 -10
  167. package/src/Challenge.test.ts +40 -0
  168. package/src/Challenge.ts +19 -6
  169. package/src/Mcp.ts +4 -0
  170. package/src/PaymentRequest.ts +10 -10
  171. package/src/cli/cli.test.ts +15 -15
  172. package/src/client/Mppx.test-d.ts +21 -1
  173. package/src/client/Mppx.test.ts +1 -1
  174. package/src/client/Mppx.ts +2 -2
  175. package/src/client/Transport.test.ts +225 -178
  176. package/src/client/Transport.ts +77 -83
  177. package/src/client/index.ts +14 -0
  178. package/src/client/internal/Fetch.test.ts +207 -2
  179. package/src/client/internal/Fetch.ts +52 -6
  180. package/src/client/internal/protocols/Mcp.test.ts +220 -0
  181. package/src/client/internal/protocols/Mcp.ts +162 -0
  182. package/src/client/internal/protocols/Mpp.ts +21 -0
  183. package/src/client/internal/protocols/Protocol.ts +10 -0
  184. package/src/client/internal/protocols/Shared.ts +25 -0
  185. package/src/client/internal/protocols/X402.ts +42 -0
  186. package/src/discovery/OpenApi.test.ts +1 -1
  187. package/src/evm/PublicInterface.test-d.ts +1 -1
  188. package/src/evm/client/index.ts +1 -0
  189. package/src/evm/index.ts +2 -0
  190. package/src/evm/server/Charge.test.ts +1 -1
  191. package/src/evm/server/index.ts +1 -0
  192. package/src/{mcp-sdk → mcp}/client/McpClient.integration.test.ts +10 -4
  193. package/src/{mcp-sdk → mcp}/client/McpClient.test-d.ts +45 -18
  194. package/src/{mcp-sdk → mcp}/client/McpClient.test.ts +211 -5
  195. package/src/mcp/client/McpClient.ts +307 -0
  196. package/src/{mcp-sdk → mcp}/client/McpClient.unit.test.ts +9 -5
  197. package/src/middlewares/elysia.test.ts +1 -1
  198. package/src/middlewares/express.test.ts +1 -1
  199. package/src/middlewares/hono.test.ts +1 -1
  200. package/src/middlewares/internal/mppx.test.ts +1 -1
  201. package/src/middlewares/nextjs.test.ts +1 -1
  202. package/src/proxy/Proxy.test.ts +1 -1
  203. package/src/proxy/services/anthropic.test.ts +1 -1
  204. package/src/proxy/services/openai.test.ts +1 -1
  205. package/src/proxy/services/stripe.test.ts +1 -1
  206. package/src/server/Mppx.authorize.test.ts +1 -1
  207. package/src/server/Mppx.test-d.ts +1 -1
  208. package/src/server/Mppx.test.ts +20 -2
  209. package/src/server/Mppx.ts +14 -1
  210. package/src/server/Transport.test.ts +6 -6
  211. package/src/server/Transport.ts +1 -1
  212. package/src/stripe/Charge.integration.test.ts +1 -1
  213. package/src/stripe/client/Charge.test.ts +1 -1
  214. package/src/stripe/server/Charge.test.ts +1 -1
  215. package/src/stripe/server/internal/html/package.json +1 -1
  216. package/src/stripe/server/internal/html.gen.ts +1 -1
  217. package/src/tempo/Proof.conformance.test.ts +146 -0
  218. package/src/tempo/Proof.test-d.ts +15 -0
  219. package/src/tempo/Proof.ts +52 -1
  220. package/src/tempo/Subscription.integration.test.ts +1 -1
  221. package/src/tempo/client/Charge.test.ts +173 -0
  222. package/src/tempo/client/Charge.ts +65 -36
  223. package/src/tempo/client/Methods.ts +4 -2
  224. package/src/tempo/client/ResolveAccount.ts +46 -0
  225. package/src/tempo/internal/fee-payer.test.ts +65 -10
  226. package/src/tempo/internal/fee-payer.ts +42 -6
  227. package/src/tempo/internal/proof.test.ts +12 -4
  228. package/src/tempo/internal/proof.ts +55 -6
  229. package/src/tempo/legacy/client/SessionManager.ts +11 -3
  230. package/src/tempo/legacy/server/Session.test.ts +91 -26
  231. package/src/tempo/server/Charge.test.ts +388 -17
  232. package/src/tempo/server/Charge.ts +46 -24
  233. package/src/tempo/server/Methods.ts +4 -2
  234. package/src/tempo/server/Subscription.test.ts +465 -3
  235. package/src/tempo/server/Subscription.ts +174 -19
  236. package/src/tempo/server/internal/html/package.json +2 -2
  237. package/src/tempo/server/internal/html.gen.ts +1 -1
  238. package/src/tempo/session/client/ChannelOps.ts +5 -19
  239. package/src/tempo/session/client/ChannelStore.ts +111 -0
  240. package/src/tempo/session/client/CredentialState.test.ts +206 -62
  241. package/src/tempo/session/client/CredentialState.ts +58 -73
  242. package/src/tempo/session/client/Session.test.ts +41 -1
  243. package/src/tempo/session/client/Session.ts +36 -10
  244. package/src/tempo/session/client/SessionManager.test.ts +154 -65
  245. package/src/tempo/session/client/SessionManager.ts +141 -235
  246. package/src/tempo/session/client/index.ts +8 -5
  247. package/src/tempo/session/precompile/Voucher.test.ts +45 -7
  248. package/src/tempo/session/precompile/Voucher.ts +27 -25
  249. package/src/tempo/session/server/Session.test.ts +4 -4
  250. package/src/tempo/session/server/Settlement.test.ts +88 -1
  251. package/src/tempo/session/server/Settlement.ts +2 -1
  252. package/src/tempo/session/server/Sse.ts +0 -2
  253. package/src/tempo/session/server/Ws.ts +0 -4
  254. package/src/tempo/subscription/Store.ts +27 -9
  255. package/src/x402/Exact.e2e.test.ts +1 -1
  256. package/src/x402/PublicInterface.test-d.ts +1 -1
  257. package/src/x402/index.ts +1 -0
  258. package/dist/mcp-sdk/client/McpClient.d.ts +0 -85
  259. package/dist/mcp-sdk/client/McpClient.d.ts.map +0 -1
  260. package/dist/mcp-sdk/client/McpClient.js +0 -118
  261. package/dist/mcp-sdk/client/McpClient.js.map +0 -1
  262. package/dist/mcp-sdk/client/index.d.ts.map +0 -1
  263. package/dist/mcp-sdk/client/index.js.map +0 -1
  264. package/dist/mcp-sdk/server/Transport.d.ts.map +0 -1
  265. package/dist/mcp-sdk/server/Transport.js.map +0 -1
  266. package/dist/mcp-sdk/server/index.d.ts.map +0 -1
  267. package/dist/mcp-sdk/server/index.js.map +0 -1
  268. package/src/mcp-sdk/client/McpClient.ts +0 -228
  269. /package/dist/{mcp-sdk → mcp}/client/index.d.ts +0 -0
  270. /package/dist/{mcp-sdk → mcp}/client/index.js +0 -0
  271. /package/dist/{mcp-sdk → mcp}/server/Transport.d.ts +0 -0
  272. /package/dist/{mcp-sdk → mcp}/server/Transport.js +0 -0
  273. /package/dist/{mcp-sdk → mcp}/server/index.d.ts +0 -0
  274. /package/dist/{mcp-sdk → mcp}/server/index.js +0 -0
  275. /package/src/{mcp-sdk → mcp}/client/index.ts +0 -0
  276. /package/src/{mcp-sdk → mcp}/server/Transport.test.ts +0 -0
  277. /package/src/{mcp-sdk → mcp}/server/Transport.ts +0 -0
  278. /package/src/{mcp-sdk → mcp}/server/index.ts +0 -0
@@ -1,3 +1,4 @@
1
+ import { Secp256k1 } from 'ox'
1
2
  import type { TempoAddress } from 'ox/tempo'
2
3
  import { TxEnvelopeTempo } from 'ox/tempo'
3
4
  import type { Hex } from 'viem'
@@ -173,6 +174,10 @@ function hostedFeePayerRequest(transaction: SponsoredTransaction) {
173
174
  * Co-signs a sender-signed partial sponsorship envelope using a hosted
174
175
  * fee-payer endpoint without letting the endpoint mutate sender-committed
175
176
  * transaction fields.
177
+ *
178
+ * @returns The serialized co-signed transaction plus the sponsor's recovered
179
+ * `feePayer` address and chosen `feeToken`, so callers can pre-broadcast
180
+ * simulate the exact transaction the sponsor broadcasts.
176
181
  */
177
182
  export async function fillHostedFeePayerTransaction(parameters: {
178
183
  allowedFeeTokens: readonly TempoAddress.Address[]
@@ -207,12 +212,43 @@ export async function fillHostedFeePayerTransaction(parameters: {
207
212
 
208
213
  assertAllowedFeeToken({ feeToken: filled.feeToken }, allowedFeeTokens)
209
214
 
210
- return Transaction.serialize({
211
- ...transaction,
212
- feePayer: true,
213
- feePayerSignature: filled.feePayerSignature,
214
- feeToken: filled.feeToken,
215
- } as never)
215
+ const feePayerSignature = filled.feePayerSignature
216
+ const feeToken = filled.feeToken
217
+
218
+ // Recover the concrete sponsor address so the simulation can use a concrete
219
+ // `feePayer` (the node rejects `eth_call` with `feePayer: true`).
220
+ const feePayer = (() => {
221
+ try {
222
+ return Secp256k1.recoverAddress({
223
+ payload: TxEnvelopeTempo.getFeePayerSignPayload(
224
+ TxEnvelopeTempo.from({
225
+ ...transaction,
226
+ feePayerSignature: undefined,
227
+ feeToken,
228
+ signature: undefined,
229
+ } as never),
230
+ { sender: transaction.from as never },
231
+ ),
232
+ signature: feePayerSignature as never,
233
+ })
234
+ } catch {
235
+ throw new FeePayerValidationError(
236
+ 'hosted fee payer returned an invalid feePayerSignature',
237
+ {},
238
+ )
239
+ }
240
+ })()
241
+
242
+ return {
243
+ feePayer,
244
+ feeToken,
245
+ serializedTransaction: await Transaction.serialize({
246
+ ...transaction,
247
+ feePayer: true,
248
+ feePayerSignature,
249
+ feeToken,
250
+ } as never),
251
+ }
216
252
  }
217
253
 
218
254
  /** Returns a transaction shape suitable for pre-broadcast simulation. */
@@ -44,9 +44,10 @@ const parsePkhSourceCases = [
44
44
  ] as const
45
45
 
46
46
  describe('Proof', () => {
47
- test('types has Proof with challengeId and realm fields', () => {
47
+ test('types has Proof with account, challengeId and realm fields', () => {
48
48
  expect(Proof.types).toEqual({
49
49
  Proof: [
50
+ { name: 'account', type: 'address' },
50
51
  { name: 'challengeId', type: 'string' },
51
52
  { name: 'realm', type: 'string' },
52
53
  ],
@@ -55,7 +56,7 @@ describe('Proof', () => {
55
56
 
56
57
  test('domain returns EIP-712 domain with name, version, chainId', () => {
57
58
  const d = Proof.domain(42431)
58
- expect(d).toEqual({ name: 'MPP', version: '2', chainId: 42431 })
59
+ expect(d).toEqual({ name: 'MPP', version: '3', chainId: 42431 })
59
60
  })
60
61
 
61
62
  test('domain uses provided chainId', () => {
@@ -63,8 +64,15 @@ describe('Proof', () => {
63
64
  expect(Proof.domain(99999).chainId).toBe(99999)
64
65
  })
65
66
 
66
- test('message wraps challengeId and realm', () => {
67
- expect(Proof.message('abc123', 'api.example.com')).toEqual({
67
+ test('message wraps account, challengeId and realm', () => {
68
+ expect(
69
+ Proof.message({
70
+ account: '0xAbCdEf1234567890AbCdEf1234567890AbCdEf12',
71
+ challengeId: 'abc123',
72
+ realm: 'api.example.com',
73
+ }),
74
+ ).toEqual({
75
+ account: '0xAbCdEf1234567890AbCdEf1234567890AbCdEf12',
68
76
  challengeId: 'abc123',
69
77
  realm: 'api.example.com',
70
78
  })
@@ -1,8 +1,18 @@
1
- import { isAddress, type Address } from 'viem'
1
+ import { hashTypedData, isAddress, type Address, type Hex } from 'viem'
2
2
 
3
- /** EIP-712 typed data types for proof credentials. */
3
+ /** EIP-712 primary type for proof credentials. */
4
+ export const primaryType = 'Proof' as const
5
+
6
+ /**
7
+ * EIP-712 typed-data field definitions for Tempo zero-amount proof credentials.
8
+ *
9
+ * The `account` field cryptographically binds the signature to the payer
10
+ * wallet, so a proof signed for one account cannot be replayed against another
11
+ * — including across an access key that is authorized for multiple accounts.
12
+ */
4
13
  export const types = {
5
14
  Proof: [
15
+ { name: 'account', type: 'address' },
6
16
  { name: 'challengeId', type: 'string' },
7
17
  { name: 'realm', type: 'string' },
8
18
  ],
@@ -10,12 +20,51 @@ export const types = {
10
20
 
11
21
  /** Constructs the EIP-712 domain for a proof credential. */
12
22
  export function domain(chainId: number) {
13
- return { name: 'MPP', version: '2', chainId } as const
23
+ return { name: 'MPP', version: '3', chainId } as const
24
+ }
25
+
26
+ /**
27
+ * Constructs the EIP-712 message for a proof credential.
28
+ *
29
+ * @param parameters - Proof message parameters.
30
+ * @param parameters.account - Payer wallet address the proof is bound to.
31
+ * @param parameters.challengeId - Challenge `id` being proven.
32
+ * @param parameters.realm - Challenge `realm` being proven.
33
+ */
34
+ export function message(parameters: { account: Address; challengeId: string; realm: string }) {
35
+ const { account, challengeId, realm } = parameters
36
+ return { account, challengeId, realm } as const
37
+ }
38
+
39
+ /**
40
+ * Constructs the complete EIP-712 typed-data payload for a proof credential.
41
+ *
42
+ * This is the canonical, wallet-bound proof contract: signing this payload
43
+ * commits the signer to a specific `account`, `challengeId`, and `realm`.
44
+ */
45
+ export function typedData(parameters: {
46
+ account: Address
47
+ chainId: number
48
+ challengeId: string
49
+ realm: string
50
+ }) {
51
+ const { account, chainId, challengeId, realm } = parameters
52
+ return {
53
+ domain: domain(chainId),
54
+ types,
55
+ primaryType,
56
+ message: message({ account, challengeId, realm }),
57
+ } as const
14
58
  }
15
59
 
16
- /** Constructs the EIP-712 message for a proof credential. */
17
- export function message(challengeId: string, realm: string) {
18
- return { challengeId, realm } as const
60
+ /** Computes the EIP-712 digest (signing payload) for a proof credential. */
61
+ export function hash(parameters: {
62
+ account: Address
63
+ chainId: number
64
+ challengeId: string
65
+ realm: string
66
+ }): Hex {
67
+ return hashTypedData(typedData(parameters))
19
68
  }
20
69
 
21
70
  /** Constructs the expected `did:pkh` source DID for a proof credential. */
@@ -127,6 +127,7 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
127
127
  let channel: ChannelEntry | null = null
128
128
  let lastChallenge: Challenge.Challenge | null = null
129
129
  let lastUrl: RequestInfo | URL | null = null
130
+ let acceptedCumulative = 0n
130
131
  let spent = 0n
131
132
  let activeSocketChallenge: Challenge.Challenge | null = null
132
133
  let activeSocketChannelId: Hex.Hex | null = null
@@ -146,7 +147,10 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
146
147
  decimals: parameters.decimals,
147
148
  maxDeposit: parameters.maxDeposit,
148
149
  onChannelUpdate(entry) {
149
- if (entry.channelId !== channel?.channelId) spent = 0n
150
+ if (entry.channelId !== channel?.channelId) {
151
+ acceptedCumulative = 0n
152
+ spent = 0n
153
+ }
150
154
  channel = entry
151
155
  },
152
156
  })
@@ -164,7 +168,10 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
164
168
  function updateSpentFromReceipt(receipt: SessionReceipt | null | undefined) {
165
169
  if (!receipt || receipt.channelId !== channel?.channelId) return
166
170
  assertReceiptWithinLocalState(receipt)
171
+ const nextAcceptedCumulative = BigInt(receipt.acceptedCumulative)
167
172
  const next = BigInt(receipt.spent)
173
+ acceptedCumulative =
174
+ acceptedCumulative > nextAcceptedCumulative ? acceptedCumulative : nextAcceptedCumulative
168
175
  spent = spent > next ? spent : next
169
176
  }
170
177
 
@@ -253,8 +260,9 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
253
260
  return (bestSpent > cumulative ? cumulative : bestSpent).toString()
254
261
  }
255
262
 
256
- // SSE/HTTP: spent is kept in sync by inline receipts, use it directly.
257
- return spent.toString()
263
+ // Some legacy servers report receipt.spent as a per-request delta while
264
+ // still requiring close at the highest accepted cumulative voucher.
265
+ return (spent > acceptedCumulative ? spent : acceptedCumulative).toString()
258
266
  }
259
267
 
260
268
  function assertVoucherWithinLocalLimit(cumulativeAmount: bigint) {
@@ -123,7 +123,7 @@ describe.runIf(isLocalnet)('session', () => {
123
123
  } as session.Parameters),
124
124
  ],
125
125
  realm: 'api.example.com',
126
- secretKey: 'secret',
126
+ secretKey: 'test-secret-key-test-secret-key-32',
127
127
  })
128
128
  }
129
129
 
@@ -3473,6 +3473,71 @@ describe.runIf(isLocalnet)('session', () => {
3473
3473
  expect(persisted?.finalized).toBe(true)
3474
3474
  })
3475
3475
 
3476
+ test('sessionManager closes at accepted cumulative when HTTP receipt spent is per-request delta', async () => {
3477
+ const handler = createHandler()
3478
+ const route = handler.session({
3479
+ amount: '1',
3480
+ decimals: 6,
3481
+ unitType: 'token',
3482
+ })
3483
+
3484
+ let lastAcceptedCumulative = 0n
3485
+ const fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
3486
+ const request = new Request(input, init)
3487
+ const result = await route(request)
3488
+ if (result.status === 402) return result.challenge
3489
+
3490
+ const response = result.withReceipt(new Response('ok'))
3491
+ const receiptHeader = response.headers.get('Payment-Receipt')
3492
+ if (!receiptHeader) return response
3493
+
3494
+ const receipt = deserializeSessionReceipt(receiptHeader)
3495
+ if (
3496
+ Credential.fromRequest<LegacySessionCredentialPayload>(request).payload?.action ===
3497
+ 'close'
3498
+ ) {
3499
+ return response
3500
+ }
3501
+
3502
+ const acceptedCumulative = BigInt(receipt.acceptedCumulative)
3503
+ const delta = acceptedCumulative - lastAcceptedCumulative
3504
+ lastAcceptedCumulative = acceptedCumulative
3505
+ return new Response(response.body, {
3506
+ status: response.status,
3507
+ statusText: response.statusText,
3508
+ headers: {
3509
+ 'Payment-Receipt': serializeSessionReceipt({
3510
+ ...receipt,
3511
+ spent: delta.toString(),
3512
+ }),
3513
+ },
3514
+ })
3515
+ }
3516
+
3517
+ const manager = sessionManager({
3518
+ account: payer,
3519
+ client,
3520
+ escrowContract,
3521
+ fetch,
3522
+ maxDeposit: '3',
3523
+ })
3524
+
3525
+ const first = await manager.fetch('https://api.example.com/resource')
3526
+ expect(first.status).toBe(200)
3527
+ expect(first.receipt?.acceptedCumulative).toBe('1000000')
3528
+ expect(first.receipt?.spent).toBe('1000000')
3529
+
3530
+ const second = await manager.fetch('https://api.example.com/resource')
3531
+ expect(second.status).toBe(200)
3532
+ expect(second.receipt?.acceptedCumulative).toBe('2000000')
3533
+ expect(second.receipt?.spent).toBe('1000000')
3534
+
3535
+ const closeReceipt = await manager.close()
3536
+ expect(closeReceipt?.status).toBe('success')
3537
+ expect(closeReceipt?.acceptedCumulative).toBe('2000000')
3538
+ expect(closeReceipt?.spent).toBe('2000000')
3539
+ })
3540
+
3476
3541
  test('sessionManager rejects receipts that exceed the locally signed voucher', async () => {
3477
3542
  const handler = createHandler()
3478
3543
  const route = handler.session({
@@ -4211,7 +4276,7 @@ describe.runIf(isLocalnet)('session', () => {
4211
4276
  }),
4212
4277
  ],
4213
4278
  realm: 'api.example.com',
4214
- secretKey: 'secret',
4279
+ secretKey: 'test-secret-key-test-secret-key-32',
4215
4280
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
4216
4281
 
4217
4282
  const fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
@@ -4253,7 +4318,7 @@ describe.runIf(isLocalnet)('session', () => {
4253
4318
  }),
4254
4319
  ],
4255
4320
  realm: 'api.example.com',
4256
- secretKey: 'secret',
4321
+ secretKey: 'test-secret-key-test-secret-key-32',
4257
4322
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
4258
4323
 
4259
4324
  const fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
@@ -4296,7 +4361,7 @@ describe.runIf(isLocalnet)('session', () => {
4296
4361
  }),
4297
4362
  ],
4298
4363
  realm: 'api.example.com',
4299
- secretKey: 'secret',
4364
+ secretKey: 'test-secret-key-test-secret-key-32',
4300
4365
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
4301
4366
 
4302
4367
  const fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
@@ -4347,7 +4412,7 @@ describe.runIf(isLocalnet)('session', () => {
4347
4412
  }),
4348
4413
  ],
4349
4414
  realm: 'api.example.com',
4350
- secretKey: 'secret',
4415
+ secretKey: 'test-secret-key-test-secret-key-32',
4351
4416
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
4352
4417
 
4353
4418
  const fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
@@ -4390,7 +4455,7 @@ describe.runIf(isLocalnet)('session', () => {
4390
4455
  }),
4391
4456
  ],
4392
4457
  realm: 'api.example.com',
4393
- secretKey: 'secret',
4458
+ secretKey: 'test-secret-key-test-secret-key-32',
4394
4459
  })
4395
4460
 
4396
4461
  const result = await handler.session({
@@ -4434,7 +4499,7 @@ describe.runIf(isLocalnet)('session', () => {
4434
4499
  }),
4435
4500
  ],
4436
4501
  realm: 'api.example.com',
4437
- secretKey: 'secret',
4502
+ secretKey: 'test-secret-key-test-secret-key-32',
4438
4503
  })
4439
4504
 
4440
4505
  const result = await handler.session({
@@ -4464,7 +4529,7 @@ describe.runIf(isLocalnet)('session', () => {
4464
4529
  }),
4465
4530
  ],
4466
4531
  realm: 'api.example.com',
4467
- secretKey: 'secret',
4532
+ secretKey: 'test-secret-key-test-secret-key-32',
4468
4533
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
4469
4534
 
4470
4535
  let voucherPosts = 0
@@ -4542,7 +4607,7 @@ describe.runIf(isLocalnet)('session', () => {
4542
4607
  }),
4543
4608
  ],
4544
4609
  realm: 'api.example.com',
4545
- secretKey: 'secret',
4610
+ secretKey: 'test-secret-key-test-secret-key-32',
4546
4611
  }).session({ amount: '1', decimals: 6, unitType: 'request' })
4547
4612
 
4548
4613
  let voucherPosts = 0
@@ -4627,7 +4692,7 @@ describe.runIf(isLocalnet)('session', () => {
4627
4692
  }),
4628
4693
  ],
4629
4694
  realm: 'api.example.com',
4630
- secretKey: 'secret',
4695
+ secretKey: 'test-secret-key-test-secret-key-32',
4631
4696
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
4632
4697
 
4633
4698
  const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
@@ -4710,7 +4775,7 @@ describe.runIf(isLocalnet)('session', () => {
4710
4775
  }),
4711
4776
  ],
4712
4777
  realm: 'api.example.com',
4713
- secretKey: 'secret',
4778
+ secretKey: 'test-secret-key-test-secret-key-32',
4714
4779
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
4715
4780
 
4716
4781
  let voucherPosts = 0
@@ -4784,7 +4849,7 @@ describe.runIf(isLocalnet)('session', () => {
4784
4849
  }),
4785
4850
  ],
4786
4851
  realm: 'api.example.com',
4787
- secretKey: 'secret',
4852
+ secretKey: 'test-secret-key-test-secret-key-32',
4788
4853
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
4789
4854
 
4790
4855
  const fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
@@ -4825,7 +4890,7 @@ describe.runIf(isLocalnet)('session', () => {
4825
4890
  const handler = Mppx_server.create({
4826
4891
  methods: [tempo_server.charge({ account: accounts[0], currency: asset })],
4827
4892
  realm: 'api.example.com',
4828
- secretKey: 'secret',
4893
+ secretKey: 'test-secret-key-test-secret-key-32',
4829
4894
  })
4830
4895
 
4831
4896
  const result = await handler.charge({
@@ -4855,7 +4920,7 @@ describe.runIf(isLocalnet)('session', () => {
4855
4920
  }),
4856
4921
  ],
4857
4922
  realm: 'api.example.com',
4858
- secretKey: 'secret',
4923
+ secretKey: 'test-secret-key-test-secret-key-32',
4859
4924
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
4860
4925
 
4861
4926
  let voucherUpdates = 0
@@ -4968,7 +5033,7 @@ describe.runIf(isLocalnet)('session', () => {
4968
5033
  }),
4969
5034
  ],
4970
5035
  realm: 'api.example.com',
4971
- secretKey: 'secret',
5036
+ secretKey: 'test-secret-key-test-secret-key-32',
4972
5037
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
4973
5038
 
4974
5039
  let voucherUpdates = 0
@@ -5081,7 +5146,7 @@ describe.runIf(isLocalnet)('session', () => {
5081
5146
  }),
5082
5147
  ],
5083
5148
  realm: 'api.example.com',
5084
- secretKey: 'secret',
5149
+ secretKey: 'test-secret-key-test-secret-key-32',
5085
5150
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
5086
5151
 
5087
5152
  const route = (request: Request) => routeHandler(request)
@@ -5191,7 +5256,7 @@ describe.runIf(isLocalnet)('session', () => {
5191
5256
  }),
5192
5257
  ],
5193
5258
  realm: 'api.example.com',
5194
- secretKey: 'secret',
5259
+ secretKey: 'test-secret-key-test-secret-key-32',
5195
5260
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
5196
5261
 
5197
5262
  const httpHandler = NodeRequest.toNodeListener(async (request) => {
@@ -5278,7 +5343,7 @@ describe.runIf(isLocalnet)('session', () => {
5278
5343
  }),
5279
5344
  ],
5280
5345
  realm: 'api.example.com',
5281
- secretKey: 'secret',
5346
+ secretKey: 'test-secret-key-test-secret-key-32',
5282
5347
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
5283
5348
 
5284
5349
  const route = (request: Request) => routeHandler(request)
@@ -5371,7 +5436,7 @@ describe.runIf(isLocalnet)('session', () => {
5371
5436
  }),
5372
5437
  ],
5373
5438
  realm: 'api.example.com',
5374
- secretKey: 'secret',
5439
+ secretKey: 'test-secret-key-test-secret-key-32',
5375
5440
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
5376
5441
 
5377
5442
  const httpHandler = NodeRequest.toNodeListener(async (request) => {
@@ -5480,7 +5545,7 @@ describe.runIf(isLocalnet)('session', () => {
5480
5545
  }),
5481
5546
  ],
5482
5547
  realm: 'api.example.com',
5483
- secretKey: 'secret',
5548
+ secretKey: 'test-secret-key-test-secret-key-32',
5484
5549
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
5485
5550
 
5486
5551
  const route = (request: Request) => routeHandler(request)
@@ -5582,7 +5647,7 @@ describe.runIf(isLocalnet)('session', () => {
5582
5647
  }),
5583
5648
  ],
5584
5649
  realm: 'api.example.com',
5585
- secretKey: 'secret',
5650
+ secretKey: 'test-secret-key-test-secret-key-32',
5586
5651
  }).session({ amount: '1', decimals: 6, unitType: 'token' })
5587
5652
 
5588
5653
  const httpHandler = NodeRequest.toNodeListener(async (request) => {
@@ -6041,7 +6106,7 @@ describe('session default currency resolution', () => {
6041
6106
  }),
6042
6107
  ],
6043
6108
  realm: 'api.example.com',
6044
- secretKey: 'secret',
6109
+ secretKey: 'test-secret-key-test-secret-key-32',
6045
6110
  })
6046
6111
 
6047
6112
  const result = await (handler.session as Function)({
@@ -6067,7 +6132,7 @@ describe('session default currency resolution', () => {
6067
6132
  }),
6068
6133
  ],
6069
6134
  realm: 'api.example.com',
6070
- secretKey: 'secret',
6135
+ secretKey: 'test-secret-key-test-secret-key-32',
6071
6136
  })
6072
6137
 
6073
6138
  const result = await (handler.session as Function)({
@@ -6094,7 +6159,7 @@ describe('session default currency resolution', () => {
6094
6159
  }),
6095
6160
  ],
6096
6161
  realm: 'api.example.com',
6097
- secretKey: 'secret',
6162
+ secretKey: 'test-secret-key-test-secret-key-32',
6098
6163
  })
6099
6164
 
6100
6165
  const result = await (handler.session as Function)({
@@ -6122,7 +6187,7 @@ describe('session default currency resolution', () => {
6122
6187
  }),
6123
6188
  ],
6124
6189
  realm: 'api.example.com',
6125
- secretKey: 'secret',
6190
+ secretKey: 'test-secret-key-test-secret-key-32',
6126
6191
  })
6127
6192
 
6128
6193
  const result = await handler.session({
@@ -6149,7 +6214,7 @@ describe('session default currency resolution', () => {
6149
6214
  }),
6150
6215
  ],
6151
6216
  realm: 'api.example.com',
6152
- secretKey: 'secret',
6217
+ secretKey: 'test-secret-key-test-secret-key-32',
6153
6218
  })
6154
6219
 
6155
6220
  expect(() =>