mppx 0.6.27 → 0.6.29

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 (295) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/Challenge.d.ts.map +1 -1
  3. package/dist/Challenge.js +16 -10
  4. package/dist/Challenge.js.map +1 -1
  5. package/dist/Method.d.ts +1 -1
  6. package/dist/Method.d.ts.map +1 -1
  7. package/dist/Store.d.ts +32 -9
  8. package/dist/Store.d.ts.map +1 -1
  9. package/dist/Store.js +42 -10
  10. package/dist/Store.js.map +1 -1
  11. package/dist/client/Methods.d.ts +1 -0
  12. package/dist/client/Methods.d.ts.map +1 -1
  13. package/dist/client/Methods.js +1 -0
  14. package/dist/client/Methods.js.map +1 -1
  15. package/dist/client/Mppx.d.ts +3 -3
  16. package/dist/client/Mppx.d.ts.map +1 -1
  17. package/dist/client/Mppx.js +1 -0
  18. package/dist/client/Mppx.js.map +1 -1
  19. package/dist/client/Transport.d.ts +10 -3
  20. package/dist/client/Transport.d.ts.map +1 -1
  21. package/dist/client/Transport.js +60 -7
  22. package/dist/client/Transport.js.map +1 -1
  23. package/dist/client/index.d.ts +1 -1
  24. package/dist/client/index.d.ts.map +1 -1
  25. package/dist/client/index.js +1 -1
  26. package/dist/client/index.js.map +1 -1
  27. package/dist/client/internal/Fetch.d.ts +3 -0
  28. package/dist/client/internal/Fetch.d.ts.map +1 -1
  29. package/dist/client/internal/Fetch.js +12 -20
  30. package/dist/client/internal/Fetch.js.map +1 -1
  31. package/dist/evm/Assets.d.ts +2 -0
  32. package/dist/evm/Assets.d.ts.map +1 -0
  33. package/dist/evm/Assets.js +2 -0
  34. package/dist/evm/Assets.js.map +1 -0
  35. package/dist/evm/Chains.d.ts +5 -0
  36. package/dist/evm/Chains.d.ts.map +1 -0
  37. package/dist/evm/Chains.js +5 -0
  38. package/dist/evm/Chains.js.map +1 -0
  39. package/dist/evm/Methods.d.ts +68 -0
  40. package/dist/evm/Methods.d.ts.map +1 -0
  41. package/dist/evm/Methods.js +28 -0
  42. package/dist/evm/Methods.js.map +1 -0
  43. package/dist/evm/Types.d.ts +143 -0
  44. package/dist/evm/Types.d.ts.map +1 -0
  45. package/dist/evm/Types.js +102 -0
  46. package/dist/evm/Types.js.map +1 -0
  47. package/dist/evm/client/Charge.d.ts +102 -0
  48. package/dist/evm/client/Charge.d.ts.map +1 -0
  49. package/dist/evm/client/Charge.js +141 -0
  50. package/dist/evm/client/Charge.js.map +1 -0
  51. package/dist/evm/client/Methods.d.ts +81 -0
  52. package/dist/evm/client/Methods.d.ts.map +1 -0
  53. package/dist/evm/client/Methods.js +16 -0
  54. package/dist/evm/client/Methods.js.map +1 -0
  55. package/dist/evm/client/index.d.ts +6 -0
  56. package/dist/evm/client/index.d.ts.map +1 -0
  57. package/dist/evm/client/index.js +6 -0
  58. package/dist/evm/client/index.js.map +1 -0
  59. package/dist/evm/index.d.ts +9 -0
  60. package/dist/evm/index.d.ts.map +1 -0
  61. package/dist/evm/index.js +8 -0
  62. package/dist/evm/index.js.map +1 -0
  63. package/dist/evm/server/Charge.d.ts +62 -0
  64. package/dist/evm/server/Charge.d.ts.map +1 -0
  65. package/dist/evm/server/Charge.js +172 -0
  66. package/dist/evm/server/Charge.js.map +1 -0
  67. package/dist/evm/server/Methods.d.ts +80 -0
  68. package/dist/evm/server/Methods.d.ts.map +1 -0
  69. package/dist/evm/server/Methods.js +16 -0
  70. package/dist/evm/server/Methods.js.map +1 -0
  71. package/dist/evm/server/index.d.ts +6 -0
  72. package/dist/evm/server/index.d.ts.map +1 -0
  73. package/dist/evm/server/index.js +6 -0
  74. package/dist/evm/server/index.js.map +1 -0
  75. package/dist/index.d.ts +2 -0
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +2 -0
  78. package/dist/index.js.map +1 -1
  79. package/dist/internal/HeaderCodec.d.ts +18 -0
  80. package/dist/internal/HeaderCodec.d.ts.map +1 -0
  81. package/dist/internal/HeaderCodec.js +31 -0
  82. package/dist/internal/HeaderCodec.js.map +1 -0
  83. package/dist/middlewares/elysia.d.ts.map +1 -1
  84. package/dist/middlewares/elysia.js +2 -3
  85. package/dist/middlewares/elysia.js.map +1 -1
  86. package/dist/middlewares/express.js +2 -1
  87. package/dist/middlewares/express.js.map +1 -1
  88. package/dist/proxy/internal/Headers.d.ts +13 -1
  89. package/dist/proxy/internal/Headers.d.ts.map +1 -1
  90. package/dist/proxy/internal/Headers.js +25 -2
  91. package/dist/proxy/internal/Headers.js.map +1 -1
  92. package/dist/proxy/services/openai.d.ts.map +1 -1
  93. package/dist/proxy/services/openai.js +2 -0
  94. package/dist/proxy/services/openai.js.map +1 -1
  95. package/dist/server/Methods.d.ts +1 -0
  96. package/dist/server/Methods.d.ts.map +1 -1
  97. package/dist/server/Methods.js +1 -0
  98. package/dist/server/Methods.js.map +1 -1
  99. package/dist/server/Mppx.d.ts.map +1 -1
  100. package/dist/server/Mppx.js +90 -12
  101. package/dist/server/Mppx.js.map +1 -1
  102. package/dist/server/Transport.d.ts +10 -0
  103. package/dist/server/Transport.d.ts.map +1 -1
  104. package/dist/server/Transport.js +9 -0
  105. package/dist/server/Transport.js.map +1 -1
  106. package/dist/server/index.d.ts +1 -1
  107. package/dist/server/index.d.ts.map +1 -1
  108. package/dist/server/index.js +1 -1
  109. package/dist/server/index.js.map +1 -1
  110. package/dist/stripe/server/Charge.d.ts +31 -1
  111. package/dist/stripe/server/Charge.d.ts.map +1 -1
  112. package/dist/stripe/server/Charge.js +88 -11
  113. package/dist/stripe/server/Charge.js.map +1 -1
  114. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  115. package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
  116. package/dist/stripe/server/internal/html.gen.js +1 -1
  117. package/dist/stripe/server/internal/html.gen.js.map +1 -1
  118. package/dist/tempo/client/ChannelOps.d.ts +3 -3
  119. package/dist/tempo/client/ChannelOps.d.ts.map +1 -1
  120. package/dist/tempo/client/ChannelOps.js +13 -6
  121. package/dist/tempo/client/ChannelOps.js.map +1 -1
  122. package/dist/tempo/client/Charge.d.ts.map +1 -1
  123. package/dist/tempo/client/Charge.js +8 -5
  124. package/dist/tempo/client/Charge.js.map +1 -1
  125. package/dist/tempo/client/Methods.d.ts +0 -1
  126. package/dist/tempo/client/Methods.d.ts.map +1 -1
  127. package/dist/tempo/client/Session.d.ts +2 -4
  128. package/dist/tempo/client/Session.d.ts.map +1 -1
  129. package/dist/tempo/client/Session.js +10 -12
  130. package/dist/tempo/client/Session.js.map +1 -1
  131. package/dist/tempo/client/SessionManager.d.ts +3 -3
  132. package/dist/tempo/client/SessionManager.d.ts.map +1 -1
  133. package/dist/tempo/client/SessionManager.js +1 -1
  134. package/dist/tempo/client/SessionManager.js.map +1 -1
  135. package/dist/tempo/internal/account.d.ts +5 -0
  136. package/dist/tempo/internal/account.d.ts.map +1 -1
  137. package/dist/tempo/internal/account.js +8 -0
  138. package/dist/tempo/internal/account.js.map +1 -1
  139. package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
  140. package/dist/tempo/internal/fee-payer.js +5 -2
  141. package/dist/tempo/internal/fee-payer.js.map +1 -1
  142. package/dist/tempo/server/Charge.d.ts +6 -0
  143. package/dist/tempo/server/Charge.d.ts.map +1 -1
  144. package/dist/tempo/server/Charge.js +30 -3
  145. package/dist/tempo/server/Charge.js.map +1 -1
  146. package/dist/tempo/server/Session.d.ts +6 -0
  147. package/dist/tempo/server/Session.d.ts.map +1 -1
  148. package/dist/tempo/server/Session.js +14 -13
  149. package/dist/tempo/server/Session.js.map +1 -1
  150. package/dist/tempo/server/Subscription.d.ts +6 -0
  151. package/dist/tempo/server/Subscription.d.ts.map +1 -1
  152. package/dist/tempo/server/Subscription.js +1 -1
  153. package/dist/tempo/server/Subscription.js.map +1 -1
  154. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  155. package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
  156. package/dist/tempo/server/internal/html.gen.js +1 -1
  157. package/dist/tempo/server/internal/html.gen.js.map +1 -1
  158. package/dist/tempo/session/Chain.d.ts +2 -0
  159. package/dist/tempo/session/Chain.d.ts.map +1 -1
  160. package/dist/tempo/session/Chain.js +8 -8
  161. package/dist/tempo/session/Chain.js.map +1 -1
  162. package/dist/tempo/session/Voucher.d.ts +4 -3
  163. package/dist/tempo/session/Voucher.d.ts.map +1 -1
  164. package/dist/tempo/session/Voucher.js +71 -44
  165. package/dist/tempo/session/Voucher.js.map +1 -1
  166. package/dist/tempo/session/Ws.d.ts.map +1 -1
  167. package/dist/tempo/session/Ws.js +15 -0
  168. package/dist/tempo/session/Ws.js.map +1 -1
  169. package/dist/tempo/subscription/KeyAuthorization.d.ts +2 -2
  170. package/dist/x402/Assets.d.ts +29 -0
  171. package/dist/x402/Assets.d.ts.map +1 -0
  172. package/dist/x402/Assets.js +46 -0
  173. package/dist/x402/Assets.js.map +1 -0
  174. package/dist/x402/Header.d.ts +14 -0
  175. package/dist/x402/Header.d.ts.map +1 -0
  176. package/dist/x402/Header.js +18 -0
  177. package/dist/x402/Header.js.map +1 -0
  178. package/dist/x402/Types.d.ts +289 -0
  179. package/dist/x402/Types.d.ts.map +1 -0
  180. package/dist/x402/Types.js +139 -0
  181. package/dist/x402/Types.js.map +1 -0
  182. package/dist/x402/client/Exact.d.ts +38 -0
  183. package/dist/x402/client/Exact.d.ts.map +1 -0
  184. package/dist/x402/client/Exact.js +141 -0
  185. package/dist/x402/client/Exact.js.map +1 -0
  186. package/dist/x402/index.d.ts +6 -0
  187. package/dist/x402/index.d.ts.map +1 -0
  188. package/dist/x402/index.js +6 -0
  189. package/dist/x402/index.js.map +1 -0
  190. package/dist/x402/internal/ChallengeBrand.d.ts +5 -0
  191. package/dist/x402/internal/ChallengeBrand.d.ts.map +1 -0
  192. package/dist/x402/internal/ChallengeBrand.js +13 -0
  193. package/dist/x402/internal/ChallengeBrand.js.map +1 -0
  194. package/dist/x402/internal/RouteBinding.d.ts +8 -0
  195. package/dist/x402/internal/RouteBinding.d.ts.map +1 -0
  196. package/dist/x402/internal/RouteBinding.js +12 -0
  197. package/dist/x402/internal/RouteBinding.js.map +1 -0
  198. package/dist/x402/server/EvmCharge.d.ts +50 -0
  199. package/dist/x402/server/EvmCharge.d.ts.map +1 -0
  200. package/dist/x402/server/EvmCharge.js +301 -0
  201. package/dist/x402/server/EvmCharge.js.map +1 -0
  202. package/dist/x402/server/Facilitator.d.ts +12 -0
  203. package/dist/x402/server/Facilitator.d.ts.map +1 -0
  204. package/dist/x402/server/Facilitator.js +42 -0
  205. package/dist/x402/server/Facilitator.js.map +1 -0
  206. package/package.json +41 -21
  207. package/src/Challenge.test.ts +28 -0
  208. package/src/Challenge.ts +17 -10
  209. package/src/Method.ts +1 -1
  210. package/src/Store.test-d.ts +58 -0
  211. package/src/Store.test.ts +77 -0
  212. package/src/Store.ts +155 -74
  213. package/src/client/Methods.ts +1 -0
  214. package/src/client/Mppx.ts +4 -3
  215. package/src/client/Transport.test.ts +165 -30
  216. package/src/client/Transport.ts +76 -8
  217. package/src/client/index.ts +1 -1
  218. package/src/client/internal/Fetch.test.ts +31 -2
  219. package/src/client/internal/Fetch.ts +26 -19
  220. package/src/evm/Assets.ts +1 -0
  221. package/src/evm/Chains.ts +5 -0
  222. package/src/evm/Methods.ts +44 -0
  223. package/src/evm/PublicInterface.test-d.ts +109 -0
  224. package/src/evm/Types.ts +140 -0
  225. package/src/evm/client/Charge.test.ts +99 -0
  226. package/src/evm/client/Charge.ts +198 -0
  227. package/src/evm/client/Methods.ts +19 -0
  228. package/src/evm/client/index.ts +5 -0
  229. package/src/evm/index.ts +13 -0
  230. package/src/evm/server/Charge.test.ts +199 -0
  231. package/src/evm/server/Charge.ts +283 -0
  232. package/src/evm/server/Methods.ts +22 -0
  233. package/src/evm/server/index.ts +5 -0
  234. package/src/index.ts +2 -0
  235. package/src/internal/HeaderCodec.ts +36 -0
  236. package/src/middlewares/elysia.test.ts +25 -0
  237. package/src/middlewares/elysia.ts +1 -2
  238. package/src/middlewares/express.test.ts +28 -0
  239. package/src/middlewares/express.ts +1 -1
  240. package/src/middlewares/hono.test.ts +138 -2
  241. package/src/middlewares/nextjs.test.ts +22 -0
  242. package/src/proxy/internal/Headers.test.ts +38 -0
  243. package/src/proxy/internal/Headers.ts +26 -2
  244. package/src/proxy/services/openai.test.ts +57 -1
  245. package/src/proxy/services/openai.ts +2 -0
  246. package/src/server/Methods.ts +1 -0
  247. package/src/server/Mppx.test.ts +244 -1
  248. package/src/server/Mppx.ts +124 -11
  249. package/src/server/NodeListener.test.ts +28 -1
  250. package/src/server/Transport.test.ts +19 -0
  251. package/src/server/Transport.ts +20 -0
  252. package/src/server/index.ts +1 -1
  253. package/src/stripe/server/Charge.test.ts +215 -1
  254. package/src/stripe/server/Charge.ts +150 -20
  255. package/src/stripe/server/internal/html.gen.ts +1 -1
  256. package/src/tempo/AccessKeyAuthorization.test.ts +231 -0
  257. package/src/tempo/client/ChannelOps.test.ts +61 -7
  258. package/src/tempo/client/ChannelOps.ts +18 -7
  259. package/src/tempo/client/Charge.test.ts +126 -0
  260. package/src/tempo/client/Charge.ts +10 -6
  261. package/src/tempo/client/Session.test.ts +130 -1
  262. package/src/tempo/client/Session.ts +12 -19
  263. package/src/tempo/client/SessionManager.test.ts +69 -2
  264. package/src/tempo/client/SessionManager.ts +4 -4
  265. package/src/tempo/internal/account.ts +13 -0
  266. package/src/tempo/internal/fee-payer.test.ts +32 -2
  267. package/src/tempo/internal/fee-payer.ts +6 -2
  268. package/src/tempo/server/Charge.test.ts +91 -1
  269. package/src/tempo/server/Charge.ts +48 -2
  270. package/src/tempo/server/Session.test.ts +30 -0
  271. package/src/tempo/server/Session.ts +24 -17
  272. package/src/tempo/server/Subscription.ts +7 -1
  273. package/src/tempo/server/internal/html.gen.ts +1 -1
  274. package/src/tempo/session/Chain.test.ts +4 -4
  275. package/src/tempo/session/Chain.ts +10 -6
  276. package/src/tempo/session/ChannelStore.test.ts +21 -0
  277. package/src/tempo/session/Voucher.test.ts +230 -1
  278. package/src/tempo/session/Voucher.ts +96 -48
  279. package/src/tempo/session/Ws.test.ts +71 -0
  280. package/src/tempo/session/Ws.ts +13 -0
  281. package/src/tempo/subscription/Store.test.ts +55 -0
  282. package/src/x402/Assets.ts +65 -0
  283. package/src/x402/Exact.e2e.test.ts +448 -0
  284. package/src/x402/Header.test.ts +73 -0
  285. package/src/x402/Header.ts +34 -0
  286. package/src/x402/PublicInterface.test-d.ts +39 -0
  287. package/src/x402/Types.ts +248 -0
  288. package/src/x402/client/Exact.test.ts +180 -0
  289. package/src/x402/client/Exact.ts +198 -0
  290. package/src/x402/index.ts +5 -0
  291. package/src/x402/internal/ChallengeBrand.ts +14 -0
  292. package/src/x402/internal/RouteBinding.ts +18 -0
  293. package/src/x402/server/EvmCharge.ts +394 -0
  294. package/src/x402/server/Facilitator.test.ts +111 -0
  295. package/src/x402/server/Facilitator.ts +56 -0
@@ -1,7 +1,8 @@
1
1
  import { Hex } from 'ox'
2
- import { type Address, createClient } from 'viem'
2
+ import { SignatureEnvelope } from 'ox/tempo'
3
+ import { type Address, createClient, decodeFunctionData } from 'viem'
3
4
  import { privateKeyToAccount } from 'viem/accounts'
4
- import { Addresses } from 'viem/tempo'
5
+ import { Account as TempoAccount, Addresses, Transaction } from 'viem/tempo'
5
6
  import { beforeAll, describe, expect, test } from 'vp/test'
6
7
  import { nodeEnv } from '~test/config.js'
7
8
  import { deployEscrow, openChannel } from '~test/tempo/session.js'
@@ -15,6 +16,7 @@ import {
15
16
  chainId as chainIdDefaults,
16
17
  escrowContract as escrowContractDefaults,
17
18
  } from '../internal/defaults.js'
19
+ import { escrowAbi } from '../session/Chain.js'
18
20
  import { verifyVoucher } from '../session/Voucher.js'
19
21
  import {
20
22
  createClosePayload,
@@ -140,6 +142,40 @@ describe('createVoucherPayload', () => {
140
142
  )
141
143
  expect(valid).toBe(true)
142
144
  })
145
+
146
+ test('uses raw access-key signatures when no voucherSigner is provided', async () => {
147
+ const accessKey = TempoAccount.fromSecp256k1(
148
+ '0x59c6995e998f97a5a0044966f09453863d462d2b3f1446a99f0a3d7b5d0f5a0d',
149
+ { access: localAccount },
150
+ )
151
+ const accessKeyClient = createClient({
152
+ account: accessKey,
153
+ transport: http('http://127.0.0.1'),
154
+ })
155
+
156
+ const result = await createVoucherPayload(
157
+ accessKeyClient,
158
+ accessKey,
159
+ channelId,
160
+ 5_000_000n,
161
+ escrowContract,
162
+ chainId,
163
+ )
164
+
165
+ expect(result.action).toBe('voucher')
166
+ if (result.action !== 'voucher') throw new Error('unexpected action')
167
+
168
+ const envelope = SignatureEnvelope.from(result.signature as SignatureEnvelope.Serialized)
169
+ expect(envelope.type).toBe('secp256k1')
170
+
171
+ const valid = await verifyVoucher(
172
+ escrowContract,
173
+ chainId,
174
+ { channelId, cumulativeAmount: 5_000_000n, signature: result.signature },
175
+ accessKey.accessKeyAddress,
176
+ )
177
+ expect(valid).toBe(true)
178
+ })
143
179
  })
144
180
 
145
181
  describe('createClosePayload', () => {
@@ -228,11 +264,13 @@ describe.runIf(isLocalnet)('createOpenPayload', () => {
228
264
  chainId: chain.id,
229
265
  })
230
266
 
231
- expect((result.payload as any).authorizedSigner).toBe(payer.address)
267
+ expect(result.payload.action).toBe('open')
268
+ if (result.payload.action !== 'open') throw new Error('unexpected action')
269
+ expect(result.payload.authorizedSigner).toBe(payer.address)
232
270
  })
233
271
 
234
- test('uses custom authorizedSigner when provided', async () => {
235
- const customSigner = accounts[5].address
272
+ test('derives authorizedSigner from voucherSigner when provided', async () => {
273
+ const voucherSigner = accounts[5]
236
274
  const payerClient = createClient({
237
275
  account: payer,
238
276
  chain,
@@ -240,7 +278,7 @@ describe.runIf(isLocalnet)('createOpenPayload', () => {
240
278
  })
241
279
 
242
280
  const result = await createOpenPayload(payerClient, payer, {
243
- authorizedSigner: customSigner,
281
+ voucherSigner,
244
282
  escrowContract: escrow,
245
283
  payee,
246
284
  currency,
@@ -249,7 +287,23 @@ describe.runIf(isLocalnet)('createOpenPayload', () => {
249
287
  chainId: chain.id,
250
288
  })
251
289
 
252
- expect((result.payload as any).authorizedSigner).toBe(customSigner)
290
+ expect(result.payload.action).toBe('open')
291
+ if (result.payload.action !== 'open') throw new Error('unexpected action')
292
+ expect(result.payload.authorizedSigner).toBe(voucherSigner.address)
293
+
294
+ const transaction = Transaction.deserialize(result.payload.transaction)
295
+ if (!('calls' in transaction)) throw new Error('unexpected transaction type')
296
+ const calls = transaction.calls as readonly [
297
+ { to?: Address; data?: Hex.Hex },
298
+ { to?: Address; data?: Hex.Hex },
299
+ ]
300
+ const openCall = calls[1]
301
+ const open = decodeFunctionData({
302
+ abi: escrowAbi,
303
+ data: openCall?.data ?? '0x',
304
+ })
305
+ const openArgs = open.args as readonly [Address, Address, bigint, string, Address]
306
+ expect(openArgs[4].toLowerCase()).toBe(voucherSigner.address.toLowerCase())
253
307
  })
254
308
  })
255
309
 
@@ -18,6 +18,7 @@ import { Abis } from 'viem/tempo'
18
18
 
19
19
  import type { Challenge } from '../../Challenge.js'
20
20
  import * as Credential from '../../Credential.js'
21
+ import { getAccountSignerAddress } from '../internal/account.js'
21
22
  import * as defaults from '../internal/defaults.js'
22
23
  import { escrowAbi, getOnChainChannel } from '../session/Chain.js'
23
24
  import * as Channel from '../session/Channel.js'
@@ -33,6 +34,13 @@ export type ChannelEntry = {
33
34
  opened: boolean
34
35
  }
35
36
 
37
+ function resolveVoucherSigner(
38
+ account: viem_Account,
39
+ voucherSigner?: viem_Account | undefined,
40
+ ): viem_Account {
41
+ return voucherSigner ?? account
42
+ }
43
+
36
44
  export function resolveChainId(challenge: Challenge): number {
37
45
  const md = challenge.request.methodDetails as { chainId?: number } | undefined
38
46
  return md?.chainId ?? 0
@@ -76,15 +84,16 @@ export async function createVoucherPayload(
76
84
  cumulativeAmount: bigint,
77
85
  escrowContract: Address,
78
86
  chainId: number,
79
- authorizedSigner?: Address | undefined,
87
+ voucherSigner?: viem_Account | undefined,
80
88
  ): Promise<SessionCredentialPayload> {
89
+ const signer = resolveVoucherSigner(account, voucherSigner)
81
90
  const signature = await signVoucher(
82
91
  client,
83
92
  account,
84
93
  { channelId, cumulativeAmount },
85
94
  escrowContract,
86
95
  chainId,
87
- authorizedSigner,
96
+ signer,
88
97
  )
89
98
  return {
90
99
  action: 'voucher',
@@ -101,15 +110,16 @@ export async function createClosePayload(
101
110
  cumulativeAmount: bigint,
102
111
  escrowContract: Address,
103
112
  chainId: number,
104
- authorizedSigner?: Address | undefined,
113
+ voucherSigner?: viem_Account | undefined,
105
114
  ): Promise<SessionCredentialPayload> {
115
+ const signer = resolveVoucherSigner(account, voucherSigner)
106
116
  const signature = await signVoucher(
107
117
  client,
108
118
  account,
109
119
  { channelId, cumulativeAmount },
110
120
  escrowContract,
111
121
  chainId,
112
- authorizedSigner,
122
+ signer,
113
123
  )
114
124
  return {
115
125
  action: 'close',
@@ -123,7 +133,7 @@ export async function createOpenPayload(
123
133
  client: viem_Client,
124
134
  account: viem_Account,
125
135
  options: {
126
- authorizedSigner?: Address | undefined
136
+ voucherSigner?: viem_Account | undefined
127
137
  escrowContract: Address
128
138
  payee: Address
129
139
  currency: Address
@@ -134,7 +144,8 @@ export async function createOpenPayload(
134
144
  },
135
145
  ): Promise<{ entry: ChannelEntry; payload: SessionCredentialPayload }> {
136
146
  const { escrowContract, payee, currency, deposit, initialAmount, chainId, feePayer } = options
137
- const authorizedSigner = options.authorizedSigner ?? account.address
147
+ const voucherSigner = resolveVoucherSigner(account, options.voucherSigner)
148
+ const authorizedSigner = getAccountSignerAddress(voucherSigner)
138
149
 
139
150
  const salt = Hex.random(32)
140
151
  const channelId = Channel.computeId({
@@ -180,7 +191,7 @@ export async function createOpenPayload(
180
191
  { channelId, cumulativeAmount: initialAmount },
181
192
  escrowContract,
182
193
  chainId,
183
- options.authorizedSigner,
194
+ voucherSigner,
184
195
  )
185
196
 
186
197
  return {
@@ -0,0 +1,126 @@
1
+ import { Challenge, Credential } from 'mppx'
2
+ import { createClient, http } from 'viem'
3
+ import { privateKeyToAccount } from 'viem/accounts'
4
+ import { tempoLocalnet } from 'viem/chains'
5
+ import { describe, expect, test, vi } from 'vp/test'
6
+
7
+ import * as Methods from '../Methods.js'
8
+ import { charge } from './Charge.js'
9
+
10
+ const account = privateKeyToAccount(
11
+ '0x0000000000000000000000000000000000000000000000000000000000000001',
12
+ )
13
+ const currency = '0x3333333333333333333333333333333333333333'
14
+ const recipient = '0x2222222222222222222222222222222222222222'
15
+
16
+ type ChargeRequest = ReturnType<typeof Methods.charge.schema.request.parse>
17
+
18
+ function createChallenge(
19
+ overrides: Partial<Parameters<typeof Methods.charge.schema.request.parse>[0]> = {},
20
+ ): Challenge.Challenge<ChargeRequest, 'charge', 'tempo'> {
21
+ const request = Methods.charge.schema.request.parse({
22
+ amount: '0',
23
+ currency,
24
+ decimals: 6,
25
+ recipient,
26
+ ...overrides,
27
+ })
28
+ return Challenge.from({
29
+ id: 'test-challenge-id',
30
+ intent: 'charge',
31
+ method: 'tempo',
32
+ realm: 'api.example.com',
33
+ request,
34
+ }) as Challenge.Challenge<ChargeRequest, 'charge', 'tempo'>
35
+ }
36
+
37
+ describe('tempo.charge client', () => {
38
+ test('uses client chain ID when the challenge omits chainId', async () => {
39
+ const client = createClient({
40
+ account,
41
+ chain: tempoLocalnet,
42
+ transport: http('http://127.0.0.1'),
43
+ })
44
+ const method = charge({
45
+ account,
46
+ getClient: () => client,
47
+ })
48
+
49
+ const credential = Credential.deserialize(
50
+ await method.createCredential({
51
+ challenge: createChallenge(),
52
+ context: {},
53
+ }),
54
+ )
55
+
56
+ expect(credential.source).toBe(`did:pkh:eip155:${tempoLocalnet.id}:${account.address}`)
57
+ })
58
+
59
+ test('uses challenge chainId for client resolution and proof source', async () => {
60
+ let requestedChainId: number | undefined
61
+ const chainId = 42431
62
+ const client = createClient({
63
+ account,
64
+ chain: tempoLocalnet,
65
+ transport: http('http://127.0.0.1'),
66
+ })
67
+ const method = charge({
68
+ account,
69
+ getClient: (parameters) => {
70
+ requestedChainId = parameters.chainId
71
+ return client
72
+ },
73
+ })
74
+
75
+ const credential = Credential.deserialize(
76
+ await method.createCredential({
77
+ challenge: createChallenge({ chainId }),
78
+ context: {},
79
+ }),
80
+ )
81
+
82
+ expect(requestedChainId).toBe(chainId)
83
+ expect(credential.source).toBe(`did:pkh:eip155:${chainId}:${account.address}`)
84
+ })
85
+
86
+ test('uses challenge chainId for non-zero transaction source', async () => {
87
+ vi.resetModules()
88
+ const prepareTransactionRequest = vi.fn(async () => ({}))
89
+ const signTransaction = vi.fn(async () => '0xdeadbeef')
90
+ vi.doMock('viem/actions', () => ({
91
+ prepareTransactionRequest,
92
+ sendCallsSync: vi.fn(),
93
+ signTransaction,
94
+ signTypedData: vi.fn(),
95
+ }))
96
+
97
+ try {
98
+ const { charge: chargeWithMockedActions } = await import('./Charge.js')
99
+ const chainId = 42431
100
+ const client = createClient({
101
+ account,
102
+ chain: tempoLocalnet,
103
+ transport: http('http://127.0.0.1'),
104
+ })
105
+ const method = chargeWithMockedActions({
106
+ account,
107
+ getClient: () => client,
108
+ })
109
+
110
+ const credential = Credential.deserialize(
111
+ await method.createCredential({
112
+ challenge: createChallenge({ amount: '1', chainId, supportedModes: ['pull'] }),
113
+ context: {},
114
+ }),
115
+ )
116
+
117
+ expect(prepareTransactionRequest).toHaveBeenCalledOnce()
118
+ expect(signTransaction).toHaveBeenCalledOnce()
119
+ expect(credential.payload).toEqual({ signature: '0xdeadbeef', type: 'transaction' })
120
+ expect(credential.source).toBe(`did:pkh:eip155:${chainId}:${account.address}`)
121
+ } finally {
122
+ vi.doUnmock('viem/actions')
123
+ vi.resetModules()
124
+ }
125
+ })
126
+ })
@@ -51,8 +51,12 @@ export function charge(parameters: charge.Parameters = {}) {
51
51
  }),
52
52
 
53
53
  async createCredential({ challenge, context }) {
54
- const chainId = challenge.request.methodDetails?.chainId
55
- const client = await getClient({ chainId })
54
+ const challengeChainId = challenge.request.methodDetails?.chainId
55
+ const client = await getClient({ chainId: challengeChainId })
56
+ const chainId = challengeChainId ?? client.chain?.id
57
+ if (chainId === undefined)
58
+ throw new Error('No `chainId` provided. Pass a chain ID in the challenge or client.')
59
+
56
60
  const account = getAccount(client, context)
57
61
 
58
62
  const { request } = challenge
@@ -62,7 +66,7 @@ export function charge(parameters: charge.Parameters = {}) {
62
66
  if (BigInt(amount) === 0n) {
63
67
  const signature = await signTypedData(client, {
64
68
  account,
65
- domain: Proof.domain(chainId!),
69
+ domain: Proof.domain(chainId),
66
70
  types: Proof.types,
67
71
  primaryType: 'Proof',
68
72
  message: Proof.message(challenge.id, challenge.realm),
@@ -70,7 +74,7 @@ export function charge(parameters: charge.Parameters = {}) {
70
74
  return Credential.serialize({
71
75
  challenge,
72
76
  payload: { signature, type: 'proof' },
73
- source: Proof.proofSource({ address: account.address, chainId: chainId! }),
77
+ source: Proof.proofSource({ address: account.address, chainId }),
74
78
  })
75
79
  }
76
80
 
@@ -156,7 +160,7 @@ export function charge(parameters: charge.Parameters = {}) {
156
160
  return Credential.serialize({
157
161
  challenge,
158
162
  payload: { hash, type: 'hash' },
159
- source: `did:pkh:eip155:${chainId}:${account.address}`,
163
+ source: Proof.proofSource({ address: account.address, chainId }),
160
164
  })
161
165
  }
162
166
 
@@ -175,7 +179,7 @@ export function charge(parameters: charge.Parameters = {}) {
175
179
  return Credential.serialize({
176
180
  challenge,
177
181
  payload: { signature, type: 'transaction' },
178
- source: `did:pkh:eip155:${chainId}:${account.address}`,
182
+ source: Proof.proofSource({ address: account.address, chainId }),
179
183
  })
180
184
  },
181
185
  })
@@ -1,6 +1,7 @@
1
+ import { SignatureEnvelope } from 'ox/tempo'
1
2
  import { type Address, createClient, decodeFunctionData, erc20Abi, type Hex, http } from 'viem'
2
3
  import { privateKeyToAccount } from 'viem/accounts'
3
- import { Addresses, Transaction } from 'viem/tempo'
4
+ import { Account as TempoAccount, Addresses, Transaction, WebCryptoP256 } from 'viem/tempo'
4
5
  import { beforeAll, describe, expect, test } from 'vp/test'
5
6
  import { nodeEnv } from '~test/config.js'
6
7
  import { deployEscrow, openChannel } from '~test/tempo/session.js'
@@ -13,6 +14,7 @@ import * as Credential from '../../Credential.js'
13
14
  import { chainId, escrowContract as escrowContractDefaults } from '../internal/defaults.js'
14
15
  import { escrowAbi } from '../session/Chain.js'
15
16
  import type { SessionCredentialPayload } from '../session/Types.js'
17
+ import { verifyVoucher } from '../session/Voucher.js'
16
18
  import { session } from './Session.js'
17
19
 
18
20
  function deserializePayload(result: string) {
@@ -184,6 +186,133 @@ describe('session (pure)', () => {
184
186
  expect(cred.source).toBe(`did:pkh:eip155:42431:${pureAccount.address}`)
185
187
  })
186
188
 
189
+ test('manual open rejects P256 voucher signer while TIP-1020 verification is disabled', async () => {
190
+ const keyPair = await WebCryptoP256.createKeyPair()
191
+ const voucherSigner = TempoAccount.fromWebCryptoP256(keyPair)
192
+ const method = session({
193
+ getClient: () => pureClient,
194
+ account: pureAccount,
195
+ voucherSigner,
196
+ })
197
+
198
+ await expect(
199
+ method.createCredential({
200
+ challenge: makeChallenge(),
201
+ context: {
202
+ action: 'open',
203
+ channelId,
204
+ cumulativeAmount: '5',
205
+ transaction: '0xdeadbeef',
206
+ },
207
+ }),
208
+ ).rejects.toThrow('Session vouchers only support secp256k1 signatures')
209
+ })
210
+
211
+ test('manual open signs access-key vouchers with raw signatures', async () => {
212
+ const accessKey = TempoAccount.fromSecp256k1(
213
+ '0x59c6995e998f97a5a0044966f09453863d462d2b3f1446a99f0a3d7b5d0f5a0d',
214
+ { access: pureAccount },
215
+ )
216
+ const accessKeyClient = createClient({
217
+ account: accessKey,
218
+ transport: http('http://127.0.0.1'),
219
+ })
220
+ const method = session({
221
+ getClient: () => accessKeyClient,
222
+ account: accessKey,
223
+ })
224
+
225
+ const result = await method.createCredential({
226
+ challenge: makeChallenge(),
227
+ context: {
228
+ action: 'open',
229
+ channelId,
230
+ cumulativeAmount: '5',
231
+ transaction: '0xdeadbeef',
232
+ },
233
+ })
234
+
235
+ const cred = deserializePayload(result)
236
+ expect(cred.payload.action).toBe('open')
237
+ if (cred.payload.action !== 'open') throw new Error('unexpected action')
238
+ expect(cred.payload.authorizedSigner).toBeDefined()
239
+ if (!cred.payload.authorizedSigner) throw new Error('missing authorizedSigner')
240
+ expect(cred.payload.authorizedSigner.toLowerCase()).toBe(
241
+ accessKey.accessKeyAddress.toLowerCase(),
242
+ )
243
+
244
+ const envelope = SignatureEnvelope.from(
245
+ cred.payload.signature as SignatureEnvelope.Serialized,
246
+ )
247
+ expect(envelope.type).toBe('secp256k1')
248
+
249
+ const isValid = await verifyVoucher(
250
+ escrowAddress,
251
+ 42431,
252
+ {
253
+ channelId,
254
+ cumulativeAmount: 5_000_000n,
255
+ signature: cred.payload.signature,
256
+ },
257
+ accessKey.accessKeyAddress,
258
+ )
259
+ expect(isValid).toBe(true)
260
+ expect(cred.source).toBe(`did:pkh:eip155:42431:${pureAccount.address}`)
261
+ })
262
+
263
+ test('manual open signs access-key vouchers with direct voucher signer', async () => {
264
+ const privateKey = '0x59c6995e998f97a5a0044966f09453863d462d2b3f1446a99f0a3d7b5d0f5a0d'
265
+ const accessKey = TempoAccount.fromSecp256k1(privateKey, { access: pureAccount })
266
+ const voucherSigner = TempoAccount.fromSecp256k1(privateKey)
267
+ const accessKeyClient = createClient({
268
+ account: accessKey,
269
+ transport: http('http://127.0.0.1'),
270
+ })
271
+ const method = session({
272
+ getClient: () => accessKeyClient,
273
+ account: accessKey,
274
+ voucherSigner,
275
+ })
276
+
277
+ const result = await method.createCredential({
278
+ challenge: makeChallenge(),
279
+ context: {
280
+ action: 'open',
281
+ channelId,
282
+ cumulativeAmount: '5',
283
+ transaction: '0xdeadbeef',
284
+ },
285
+ })
286
+
287
+ const cred = deserializePayload(result)
288
+ expect(cred.payload.action).toBe('open')
289
+ if (cred.payload.action !== 'open') throw new Error('unexpected action')
290
+ expect(cred.payload.authorizedSigner).toBeDefined()
291
+ if (!cred.payload.authorizedSigner) throw new Error('missing authorizedSigner')
292
+ expect(cred.payload.authorizedSigner.toLowerCase()).toBe(
293
+ accessKey.accessKeyAddress.toLowerCase(),
294
+ )
295
+
296
+ const envelope = SignatureEnvelope.from(
297
+ cred.payload.signature as SignatureEnvelope.Serialized,
298
+ )
299
+ expect(envelope.type).toBe('secp256k1')
300
+ expect(cred.payload.signature.length).toBe(132)
301
+
302
+ const isValid = await verifyVoucher(
303
+ escrowAddress,
304
+ 42431,
305
+ {
306
+ channelId,
307
+ cumulativeAmount: 5_000_000n,
308
+ signature: cred.payload.signature,
309
+ },
310
+ accessKey.accessKeyAddress,
311
+ )
312
+ expect(isValid).toBe(true)
313
+ expect(cred.source).toBe(`did:pkh:eip155:42431:${pureAccount.address}`)
314
+ })
315
+
187
316
  test('manual close produces valid credential', async () => {
188
317
  const method = session({ getClient: () => pureClient, account: pureAccount })
189
318
 
@@ -7,6 +7,7 @@ import * as Method from '../../Method.js'
7
7
  import * as Account from '../../viem/Account.js'
8
8
  import * as Client from '../../viem/Client.js'
9
9
  import * as z from '../../zod.js'
10
+ import { getAccountSignerAddress } from '../internal/account.js'
10
11
  import * as defaults from '../internal/defaults.js'
11
12
  import * as Methods from '../Methods.js'
12
13
  import type { SessionCredentialPayload } from '../session/Types.js'
@@ -27,7 +28,6 @@ export const sessionContextSchema = z.object({
27
28
  cumulativeAmount: z.optional(z.amount()),
28
29
  cumulativeAmountRaw: z.optional(z.string()),
29
30
  transaction: z.optional(z.string()),
30
- authorizedSigner: z.optional(z.string()),
31
31
  additionalDeposit: z.optional(z.amount()),
32
32
  additionalDepositRaw: z.optional(z.string()),
33
33
  depositRaw: z.optional(z.string()),
@@ -79,9 +79,6 @@ export function session(parameters: session.Parameters = {}) {
79
79
  rpcUrl: defaults.rpcUrl,
80
80
  })
81
81
  const getAccount = Account.getResolver({ account: parameters.account })
82
- const getAuthorizedSigner = (account: viem_Account) =>
83
- parameters.authorizedSigner ??
84
- (account as unknown as { accessKeyAddress?: Address }).accessKeyAddress
85
82
 
86
83
  const maxDeposit =
87
84
  parameters.maxDeposit !== undefined ? parseUnits(parameters.maxDeposit, decimals) : undefined
@@ -141,7 +138,7 @@ export function session(parameters: session.Parameters = {}) {
141
138
  )
142
139
  })()
143
140
 
144
- const authorizedSigner = getAuthorizedSigner(account)
141
+ const voucherSigner = parameters.voucherSigner ?? account
145
142
 
146
143
  const key = channelKey(payee, currency, escrowContract)
147
144
  let entry = channels.get(key)
@@ -186,12 +183,12 @@ export function session(parameters: session.Parameters = {}) {
186
183
  entry.cumulativeAmount,
187
184
  escrowContract,
188
185
  chainId,
189
- authorizedSigner,
186
+ voucherSigner,
190
187
  )
191
188
  notifyUpdate(entry)
192
189
  } else {
193
190
  const result = await createOpenPayload(client, account, {
194
- authorizedSigner,
191
+ voucherSigner,
195
192
  escrowContract,
196
193
  payee,
197
194
  currency,
@@ -222,12 +219,8 @@ export function session(parameters: session.Parameters = {}) {
222
219
  const client = await getClient({ chainId })
223
220
 
224
221
  const action = context.action!
225
- const {
226
- channelId: channelIdRaw,
227
- transaction,
228
- authorizedSigner: contextAuthorizedSigner,
229
- } = context
230
- const authorizedSigner = (contextAuthorizedSigner as Address) ?? getAuthorizedSigner(account)
222
+ const { channelId: channelIdRaw, transaction } = context
223
+ const voucherSigner = parameters.voucherSigner ?? account
231
224
  const channelId = channelIdRaw as Hex.Hex
232
225
  const cumulativeAmount = context.cumulativeAmountRaw
233
226
  ? BigInt(context.cumulativeAmountRaw)
@@ -256,14 +249,14 @@ export function session(parameters: session.Parameters = {}) {
256
249
  { channelId, cumulativeAmount },
257
250
  escrowContract,
258
251
  chainId,
259
- authorizedSigner,
252
+ voucherSigner,
260
253
  )
261
254
  payload = {
262
255
  action: 'open',
263
256
  type: 'transaction',
264
257
  channelId,
265
258
  transaction: transaction as Hex.Hex,
266
- authorizedSigner: authorizedSigner ?? account.address,
259
+ authorizedSigner: getAccountSignerAddress(voucherSigner),
267
260
  cumulativeAmount: cumulativeAmount.toString(),
268
261
  signature,
269
262
  }
@@ -293,7 +286,7 @@ export function session(parameters: session.Parameters = {}) {
293
286
  cumulativeAmount,
294
287
  escrowContract,
295
288
  chainId,
296
- authorizedSigner,
289
+ voucherSigner,
297
290
  )
298
291
  const key = channelIdToKey.get(channelId)
299
292
  if (key) {
@@ -316,7 +309,7 @@ export function session(parameters: session.Parameters = {}) {
316
309
  { channelId, cumulativeAmount },
317
310
  escrowContract,
318
311
  chainId,
319
- authorizedSigner,
312
+ voucherSigner,
320
313
  )
321
314
  payload = {
322
315
  action: 'close',
@@ -364,8 +357,8 @@ export function session(parameters: session.Parameters = {}) {
364
357
  export declare namespace session {
365
358
  type Parameters = Account.getResolver.Parameters &
366
359
  Client.getResolver.Parameters & {
367
- /** Address authorized to sign vouchers. Defaults to the account address. Use when a separate access key (e.g. secp256k1) signs vouchers while the root account funds the channel. */
368
- authorizedSigner?: Address | undefined
360
+ /** Account that signs voucher digests. Defaults to `account`; access-key accounts sign raw vouchers as their access-key address. */
361
+ voucherSigner?: viem_Account | undefined
369
362
  /** Token decimals for parsing human-readable amounts (default: 6). */
370
363
  decimals?: number | undefined
371
364
  /** Initial deposit amount in human-readable units (e.g. "10" for 10 tokens). When set, the method handles the full channel lifecycle (open, voucher, cumulative tracking) automatically. */