mppx 0.0.1 → 0.1.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 (446) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -0
  3. package/dist/BodyDigest.d.ts +42 -0
  4. package/dist/BodyDigest.d.ts.map +1 -0
  5. package/dist/BodyDigest.js +40 -0
  6. package/dist/BodyDigest.js.map +1 -0
  7. package/dist/Challenge.d.ts +271 -0
  8. package/dist/Challenge.d.ts.map +1 -0
  9. package/dist/Challenge.js +291 -0
  10. package/dist/Challenge.js.map +1 -0
  11. package/dist/Credential.d.ts +91 -0
  12. package/dist/Credential.d.ts.map +1 -0
  13. package/dist/Credential.js +122 -0
  14. package/dist/Credential.js.map +1 -0
  15. package/dist/Errors.d.ts +243 -0
  16. package/dist/Errors.d.ts.map +1 -0
  17. package/dist/Errors.js +201 -0
  18. package/dist/Errors.js.map +1 -0
  19. package/dist/Expires.d.ts +15 -0
  20. package/dist/Expires.d.ts.map +1 -0
  21. package/dist/Expires.js +29 -0
  22. package/dist/Expires.js.map +1 -0
  23. package/dist/Intent.d.ts +101 -0
  24. package/dist/Intent.d.ts.map +1 -0
  25. package/dist/Intent.js +83 -0
  26. package/dist/Intent.js.map +1 -0
  27. package/dist/Mcp.d.ts +74 -0
  28. package/dist/Mcp.d.ts.map +1 -0
  29. package/dist/Mcp.js +9 -0
  30. package/dist/Mcp.js.map +1 -0
  31. package/dist/MethodIntent.d.ts +225 -0
  32. package/dist/MethodIntent.d.ts.map +1 -0
  33. package/dist/MethodIntent.js +156 -0
  34. package/dist/MethodIntent.js.map +1 -0
  35. package/dist/PaymentRequest.d.ts +88 -0
  36. package/dist/PaymentRequest.d.ts.map +1 -0
  37. package/dist/PaymentRequest.js +81 -0
  38. package/dist/PaymentRequest.js.map +1 -0
  39. package/dist/Receipt.d.ts +110 -0
  40. package/dist/Receipt.d.ts.map +1 -0
  41. package/dist/Receipt.js +105 -0
  42. package/dist/Receipt.js.map +1 -0
  43. package/dist/Store.d.ts +28 -0
  44. package/dist/Store.d.ts.map +1 -0
  45. package/dist/Store.js +61 -0
  46. package/dist/Store.js.map +1 -0
  47. package/dist/cli.d.ts +3 -0
  48. package/dist/cli.d.ts.map +1 -0
  49. package/dist/cli.js +1219 -0
  50. package/dist/cli.js.map +1 -0
  51. package/dist/client/Methods.d.ts +4 -0
  52. package/dist/client/Methods.d.ts.map +1 -0
  53. package/dist/client/Methods.js +4 -0
  54. package/dist/client/Methods.js.map +1 -0
  55. package/dist/client/Mppx.d.ts +84 -0
  56. package/dist/client/Mppx.d.ts.map +1 -0
  57. package/dist/client/Mppx.js +64 -0
  58. package/dist/client/Mppx.js.map +1 -0
  59. package/dist/client/Transport.d.ts +56 -0
  60. package/dist/client/Transport.d.ts.map +1 -0
  61. package/dist/client/Transport.js +81 -0
  62. package/dist/client/Transport.js.map +1 -0
  63. package/dist/client/index.d.ts +5 -0
  64. package/dist/client/index.d.ts.map +1 -0
  65. package/dist/client/index.js +5 -0
  66. package/dist/client/index.js.map +1 -0
  67. package/dist/client/internal/Fetch.d.ts +85 -0
  68. package/dist/client/internal/Fetch.d.ts.map +1 -0
  69. package/dist/client/internal/Fetch.js +95 -0
  70. package/dist/client/internal/Fetch.js.map +1 -0
  71. package/dist/index.d.ts +13 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +13 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/internal/types.d.ts +302 -0
  76. package/dist/internal/types.d.ts.map +1 -0
  77. package/dist/internal/types.js +2 -0
  78. package/dist/internal/types.js.map +1 -0
  79. package/dist/mcp-sdk/client/McpClient.d.ts +78 -0
  80. package/dist/mcp-sdk/client/McpClient.d.ts.map +1 -0
  81. package/dist/mcp-sdk/client/McpClient.js +98 -0
  82. package/dist/mcp-sdk/client/McpClient.js.map +1 -0
  83. package/dist/mcp-sdk/client/index.d.ts +3 -0
  84. package/dist/mcp-sdk/client/index.d.ts.map +1 -0
  85. package/dist/mcp-sdk/client/index.js +3 -0
  86. package/dist/mcp-sdk/client/index.js.map +1 -0
  87. package/dist/mcp-sdk/server/Transport.d.ts +43 -0
  88. package/dist/mcp-sdk/server/Transport.d.ts.map +1 -0
  89. package/dist/mcp-sdk/server/Transport.js +71 -0
  90. package/dist/mcp-sdk/server/Transport.js.map +1 -0
  91. package/dist/mcp-sdk/server/index.d.ts +2 -0
  92. package/dist/mcp-sdk/server/index.d.ts.map +1 -0
  93. package/dist/mcp-sdk/server/index.js +2 -0
  94. package/dist/mcp-sdk/server/index.js.map +1 -0
  95. package/dist/middlewares/elysia.d.ts +51 -0
  96. package/dist/middlewares/elysia.d.ts.map +1 -0
  97. package/dist/middlewares/elysia.js +59 -0
  98. package/dist/middlewares/elysia.js.map +1 -0
  99. package/dist/middlewares/express.d.ts +46 -0
  100. package/dist/middlewares/express.d.ts.map +1 -0
  101. package/dist/middlewares/express.js +69 -0
  102. package/dist/middlewares/express.js.map +1 -0
  103. package/dist/middlewares/hono.d.ts +46 -0
  104. package/dist/middlewares/hono.d.ts.map +1 -0
  105. package/dist/middlewares/hono.js +57 -0
  106. package/dist/middlewares/hono.js.map +1 -0
  107. package/dist/middlewares/internal/mppx.d.ts +16 -0
  108. package/dist/middlewares/internal/mppx.d.ts.map +1 -0
  109. package/dist/middlewares/internal/mppx.js +16 -0
  110. package/dist/middlewares/internal/mppx.js.map +1 -0
  111. package/dist/middlewares/nextjs.d.ts +45 -0
  112. package/dist/middlewares/nextjs.d.ts.map +1 -0
  113. package/dist/middlewares/nextjs.js +57 -0
  114. package/dist/middlewares/nextjs.js.map +1 -0
  115. package/dist/proxy/Proxy.d.ts +47 -0
  116. package/dist/proxy/Proxy.d.ts.map +1 -0
  117. package/dist/proxy/Proxy.js +126 -0
  118. package/dist/proxy/Proxy.js.map +1 -0
  119. package/dist/proxy/Service.d.ts +100 -0
  120. package/dist/proxy/Service.d.ts.map +1 -0
  121. package/dist/proxy/Service.js +147 -0
  122. package/dist/proxy/Service.js.map +1 -0
  123. package/dist/proxy/index.d.ts +7 -0
  124. package/dist/proxy/index.d.ts.map +1 -0
  125. package/dist/proxy/index.js +7 -0
  126. package/dist/proxy/index.js.map +1 -0
  127. package/dist/proxy/internal/Headers.d.ts +3 -0
  128. package/dist/proxy/internal/Headers.d.ts.map +1 -0
  129. package/dist/proxy/internal/Headers.js +41 -0
  130. package/dist/proxy/internal/Headers.js.map +1 -0
  131. package/dist/proxy/internal/Route.d.ts +14 -0
  132. package/dist/proxy/internal/Route.d.ts.map +1 -0
  133. package/dist/proxy/internal/Route.js +47 -0
  134. package/dist/proxy/internal/Route.js.map +1 -0
  135. package/dist/proxy/services/anthropic.d.ts +29 -0
  136. package/dist/proxy/services/anthropic.d.ts.map +1 -0
  137. package/dist/proxy/services/anthropic.js +30 -0
  138. package/dist/proxy/services/anthropic.js.map +1 -0
  139. package/dist/proxy/services/openai.d.ts +29 -0
  140. package/dist/proxy/services/openai.d.ts.map +1 -0
  141. package/dist/proxy/services/openai.js +30 -0
  142. package/dist/proxy/services/openai.js.map +1 -0
  143. package/dist/proxy/services/stripe.d.ts +29 -0
  144. package/dist/proxy/services/stripe.d.ts.map +1 -0
  145. package/dist/proxy/services/stripe.js +30 -0
  146. package/dist/proxy/services/stripe.js.map +1 -0
  147. package/dist/server/Methods.d.ts +3 -0
  148. package/dist/server/Methods.d.ts.map +1 -0
  149. package/dist/server/Methods.js +3 -0
  150. package/dist/server/Methods.js.map +1 -0
  151. package/dist/server/Mppx.d.ts +116 -0
  152. package/dist/server/Mppx.d.ts.map +1 -0
  153. package/dist/server/Mppx.js +207 -0
  154. package/dist/server/Mppx.js.map +1 -0
  155. package/dist/server/NodeListener.d.ts +3 -0
  156. package/dist/server/NodeListener.d.ts.map +1 -0
  157. package/dist/server/NodeListener.js +3 -0
  158. package/dist/server/NodeListener.js.map +1 -0
  159. package/dist/server/Request.d.ts +24 -0
  160. package/dist/server/Request.d.ts.map +1 -0
  161. package/dist/server/Request.js +26 -0
  162. package/dist/server/Request.js.map +1 -0
  163. package/dist/server/Response.d.ts +10 -0
  164. package/dist/server/Response.d.ts.map +1 -0
  165. package/dist/server/Response.js +15 -0
  166. package/dist/server/Response.js.map +1 -0
  167. package/dist/server/Transport.d.ts +93 -0
  168. package/dist/server/Transport.d.ts.map +1 -0
  169. package/dist/server/Transport.js +132 -0
  170. package/dist/server/Transport.js.map +1 -0
  171. package/dist/server/index.d.ts +9 -0
  172. package/dist/server/index.d.ts.map +1 -0
  173. package/dist/server/index.js +9 -0
  174. package/dist/server/index.js.map +1 -0
  175. package/dist/stripe/Intents.d.ts +54 -0
  176. package/dist/stripe/Intents.d.ts.map +1 -0
  177. package/dist/stripe/Intents.js +27 -0
  178. package/dist/stripe/Intents.js.map +1 -0
  179. package/dist/stripe/client/Charge.d.ts +114 -0
  180. package/dist/stripe/client/Charge.d.ts.map +1 -0
  181. package/dist/stripe/client/Charge.js +77 -0
  182. package/dist/stripe/client/Charge.js.map +1 -0
  183. package/dist/stripe/client/MethodIntents.d.ts +80 -0
  184. package/dist/stripe/client/MethodIntents.d.ts.map +1 -0
  185. package/dist/stripe/client/MethodIntents.js +34 -0
  186. package/dist/stripe/client/MethodIntents.js.map +1 -0
  187. package/dist/stripe/client/index.d.ts +3 -0
  188. package/dist/stripe/client/index.d.ts.map +1 -0
  189. package/dist/stripe/client/index.js +3 -0
  190. package/dist/stripe/client/index.js.map +1 -0
  191. package/dist/stripe/index.d.ts +2 -0
  192. package/dist/stripe/index.d.ts.map +1 -0
  193. package/dist/stripe/index.js +2 -0
  194. package/dist/stripe/index.js.map +1 -0
  195. package/dist/stripe/server/Charge.d.ts +74 -0
  196. package/dist/stripe/server/Charge.d.ts.map +1 -0
  197. package/dist/stripe/server/Charge.js +79 -0
  198. package/dist/stripe/server/Charge.js.map +1 -0
  199. package/dist/stripe/server/MethodIntents.d.ts +65 -0
  200. package/dist/stripe/server/MethodIntents.d.ts.map +1 -0
  201. package/dist/stripe/server/MethodIntents.js +21 -0
  202. package/dist/stripe/server/MethodIntents.js.map +1 -0
  203. package/dist/stripe/server/index.d.ts +3 -0
  204. package/dist/stripe/server/index.d.ts.map +1 -0
  205. package/dist/stripe/server/index.js +3 -0
  206. package/dist/stripe/server/index.js.map +1 -0
  207. package/dist/tempo/Attribution.d.ts +101 -0
  208. package/dist/tempo/Attribution.d.ts.map +1 -0
  209. package/dist/tempo/Attribution.js +124 -0
  210. package/dist/tempo/Attribution.js.map +1 -0
  211. package/dist/tempo/Intents.d.ts +132 -0
  212. package/dist/tempo/Intents.d.ts.map +1 -0
  213. package/dist/tempo/Intents.js +81 -0
  214. package/dist/tempo/Intents.js.map +1 -0
  215. package/dist/tempo/client/ChannelOps.d.ts +54 -0
  216. package/dist/tempo/client/ChannelOps.d.ts.map +1 -0
  217. package/dist/tempo/client/ChannelOps.js +138 -0
  218. package/dist/tempo/client/ChannelOps.js.map +1 -0
  219. package/dist/tempo/client/Charge.d.ts +76 -0
  220. package/dist/tempo/client/Charge.d.ts.map +1 -0
  221. package/dist/tempo/client/Charge.js +69 -0
  222. package/dist/tempo/client/Charge.js.map +1 -0
  223. package/dist/tempo/client/MethodIntents.d.ts +157 -0
  224. package/dist/tempo/client/MethodIntents.d.ts.map +1 -0
  225. package/dist/tempo/client/MethodIntents.js +25 -0
  226. package/dist/tempo/client/MethodIntents.js.map +1 -0
  227. package/dist/tempo/client/Session.d.ts +159 -0
  228. package/dist/tempo/client/Session.d.ts.map +1 -0
  229. package/dist/tempo/client/Session.js +263 -0
  230. package/dist/tempo/client/Session.js.map +1 -0
  231. package/dist/tempo/client/SessionManager.d.ts +62 -0
  232. package/dist/tempo/client/SessionManager.d.ts.map +1 -0
  233. package/dist/tempo/client/SessionManager.js +196 -0
  234. package/dist/tempo/client/SessionManager.js.map +1 -0
  235. package/dist/tempo/client/index.d.ts +6 -0
  236. package/dist/tempo/client/index.d.ts.map +1 -0
  237. package/dist/tempo/client/index.js +5 -0
  238. package/dist/tempo/client/index.js.map +1 -0
  239. package/dist/tempo/index.d.ts +3 -0
  240. package/dist/tempo/index.d.ts.map +1 -0
  241. package/dist/tempo/index.js +3 -0
  242. package/dist/tempo/index.js.map +1 -0
  243. package/dist/tempo/internal/account.d.ts +32 -0
  244. package/dist/tempo/internal/account.d.ts.map +1 -0
  245. package/dist/tempo/internal/account.js +33 -0
  246. package/dist/tempo/internal/account.js.map +1 -0
  247. package/dist/tempo/internal/defaults.d.ts +18 -0
  248. package/dist/tempo/internal/defaults.d.ts.map +1 -0
  249. package/dist/tempo/internal/defaults.js +18 -0
  250. package/dist/tempo/internal/defaults.js.map +1 -0
  251. package/dist/tempo/internal/types.d.ts +11 -0
  252. package/dist/tempo/internal/types.d.ts.map +1 -0
  253. package/dist/tempo/internal/types.js +2 -0
  254. package/dist/tempo/internal/types.js.map +1 -0
  255. package/dist/tempo/server/Charge.d.ts +77 -0
  256. package/dist/tempo/server/Charge.d.ts.map +1 -0
  257. package/dist/tempo/server/Charge.js +228 -0
  258. package/dist/tempo/server/Charge.js.map +1 -0
  259. package/dist/tempo/server/MethodIntents.d.ts +140 -0
  260. package/dist/tempo/server/MethodIntents.d.ts.map +1 -0
  261. package/dist/tempo/server/MethodIntents.js +26 -0
  262. package/dist/tempo/server/MethodIntents.js.map +1 -0
  263. package/dist/tempo/server/Session.d.ts +148 -0
  264. package/dist/tempo/server/Session.d.ts.map +1 -0
  265. package/dist/tempo/server/Session.js +529 -0
  266. package/dist/tempo/server/Session.js.map +1 -0
  267. package/dist/tempo/server/internal/transport.d.ts +47 -0
  268. package/dist/tempo/server/internal/transport.d.ts.map +1 -0
  269. package/dist/tempo/server/internal/transport.js +118 -0
  270. package/dist/tempo/server/internal/transport.js.map +1 -0
  271. package/dist/tempo/stream/Chain.d.ts +52 -0
  272. package/dist/tempo/stream/Chain.d.ts.map +1 -0
  273. package/dist/tempo/stream/Chain.js +215 -0
  274. package/dist/tempo/stream/Chain.js.map +1 -0
  275. package/dist/tempo/stream/Channel.d.ts +26 -0
  276. package/dist/tempo/stream/Channel.d.ts.map +1 -0
  277. package/dist/tempo/stream/Channel.js +27 -0
  278. package/dist/tempo/stream/Channel.js.map +1 -0
  279. package/dist/tempo/stream/ChannelStore.d.ts +103 -0
  280. package/dist/tempo/stream/ChannelStore.d.ts.map +1 -0
  281. package/dist/tempo/stream/ChannelStore.js +100 -0
  282. package/dist/tempo/stream/ChannelStore.js.map +1 -0
  283. package/dist/tempo/stream/Receipt.d.ts +22 -0
  284. package/dist/tempo/stream/Receipt.d.ts.map +1 -0
  285. package/dist/tempo/stream/Receipt.js +34 -0
  286. package/dist/tempo/stream/Receipt.js.map +1 -0
  287. package/dist/tempo/stream/Sse.d.ts +134 -0
  288. package/dist/tempo/stream/Sse.d.ts.map +1 -0
  289. package/dist/tempo/stream/Sse.js +288 -0
  290. package/dist/tempo/stream/Sse.js.map +1 -0
  291. package/dist/tempo/stream/Types.d.ts +78 -0
  292. package/dist/tempo/stream/Types.d.ts.map +1 -0
  293. package/dist/tempo/stream/Types.js +2 -0
  294. package/dist/tempo/stream/Types.js.map +1 -0
  295. package/dist/tempo/stream/Voucher.d.ts +20 -0
  296. package/dist/tempo/stream/Voucher.d.ts.map +1 -0
  297. package/dist/tempo/stream/Voucher.js +98 -0
  298. package/dist/tempo/stream/Voucher.js.map +1 -0
  299. package/dist/tempo/stream/escrow.abi.d.ts +598 -0
  300. package/dist/tempo/stream/escrow.abi.d.ts.map +1 -0
  301. package/dist/tempo/stream/escrow.abi.js +760 -0
  302. package/dist/tempo/stream/escrow.abi.js.map +1 -0
  303. package/dist/tempo/stream/index.d.ts +8 -0
  304. package/dist/tempo/stream/index.d.ts.map +1 -0
  305. package/dist/tempo/stream/index.js +8 -0
  306. package/dist/tempo/stream/index.js.map +1 -0
  307. package/dist/viem/Account.d.ts +12 -0
  308. package/dist/viem/Account.d.ts.map +1 -0
  309. package/dist/viem/Account.js +14 -0
  310. package/dist/viem/Account.js.map +1 -0
  311. package/dist/viem/Client.d.ts +21 -0
  312. package/dist/viem/Client.d.ts.map +1 -0
  313. package/dist/viem/Client.js +19 -0
  314. package/dist/viem/Client.js.map +1 -0
  315. package/dist/zod.d.ts +17 -0
  316. package/dist/zod.d.ts.map +1 -0
  317. package/dist/zod.js +35 -0
  318. package/dist/zod.js.map +1 -0
  319. package/package.json +117 -4
  320. package/src/BodyDigest.test.ts +43 -0
  321. package/src/BodyDigest.ts +53 -0
  322. package/src/Challenge.test-d.ts +81 -0
  323. package/src/Challenge.test.ts +414 -0
  324. package/src/Challenge.ts +429 -0
  325. package/src/Credential.test.ts +227 -0
  326. package/src/Credential.ts +154 -0
  327. package/src/Errors.test.ts +402 -0
  328. package/src/Errors.ts +348 -0
  329. package/src/Expires.ts +34 -0
  330. package/src/Intent.test.ts +180 -0
  331. package/src/Intent.ts +109 -0
  332. package/src/Mcp.ts +81 -0
  333. package/src/MethodIntent.test.ts +303 -0
  334. package/src/MethodIntent.ts +388 -0
  335. package/src/PaymentRequest.test.ts +152 -0
  336. package/src/PaymentRequest.ts +107 -0
  337. package/src/Receipt.test.ts +98 -0
  338. package/src/Receipt.ts +129 -0
  339. package/src/Store.ts +84 -0
  340. package/src/cli.test.ts +542 -0
  341. package/src/cli.ts +1319 -0
  342. package/src/client/Methods.ts +3 -0
  343. package/src/client/Mppx.test-d.ts +90 -0
  344. package/src/client/Mppx.test.ts +468 -0
  345. package/src/client/Mppx.ts +149 -0
  346. package/src/client/Transport.test.ts +283 -0
  347. package/src/client/Transport.ts +115 -0
  348. package/src/client/index.ts +4 -0
  349. package/src/client/internal/Fetch.test-d.ts +57 -0
  350. package/src/client/internal/Fetch.test.ts +281 -0
  351. package/src/client/internal/Fetch.ts +157 -0
  352. package/src/env.d.ts +11 -0
  353. package/src/index.ts +12 -0
  354. package/src/internal/types.ts +403 -0
  355. package/src/mcp-sdk/client/McpClient.test-d.ts +109 -0
  356. package/src/mcp-sdk/client/McpClient.test.ts +219 -0
  357. package/src/mcp-sdk/client/McpClient.ts +187 -0
  358. package/src/mcp-sdk/client/index.ts +2 -0
  359. package/src/mcp-sdk/server/Transport.ts +94 -0
  360. package/src/mcp-sdk/server/index.ts +1 -0
  361. package/src/middlewares/elysia.ts +66 -0
  362. package/src/middlewares/express.test.ts +155 -0
  363. package/src/middlewares/express.ts +82 -0
  364. package/src/middlewares/hono.test.ts +148 -0
  365. package/src/middlewares/hono.ts +62 -0
  366. package/src/middlewares/internal/mppx.ts +30 -0
  367. package/src/middlewares/nextjs.test.ts +164 -0
  368. package/src/middlewares/nextjs.ts +66 -0
  369. package/src/proxy/Proxy.test.ts +472 -0
  370. package/src/proxy/Proxy.ts +175 -0
  371. package/src/proxy/Service.test.ts +125 -0
  372. package/src/proxy/Service.ts +227 -0
  373. package/src/proxy/index.ts +6 -0
  374. package/src/proxy/internal/Headers.test.ts +100 -0
  375. package/src/proxy/internal/Headers.ts +40 -0
  376. package/src/proxy/internal/Route.test.ts +143 -0
  377. package/src/proxy/internal/Route.ts +54 -0
  378. package/src/proxy/services/anthropic.ts +45 -0
  379. package/src/proxy/services/openai.test.ts +97 -0
  380. package/src/proxy/services/openai.ts +48 -0
  381. package/src/proxy/services/stripe.ts +49 -0
  382. package/src/server/Methods.ts +2 -0
  383. package/src/server/Mppx.test-d.ts +343 -0
  384. package/src/server/Mppx.test.ts +342 -0
  385. package/src/server/Mppx.ts +378 -0
  386. package/src/server/NodeListener.test.ts +188 -0
  387. package/src/server/NodeListener.ts +3 -0
  388. package/src/server/Request.test.ts +102 -0
  389. package/src/server/Request.ts +33 -0
  390. package/src/server/Response.test.ts +31 -0
  391. package/src/server/Response.ts +27 -0
  392. package/src/server/Transport.test.ts +294 -0
  393. package/src/server/Transport.ts +222 -0
  394. package/src/server/index.ts +8 -0
  395. package/src/stripe/Charge.integration.test.ts +326 -0
  396. package/src/stripe/Intents.test.ts +52 -0
  397. package/src/stripe/Intents.ts +27 -0
  398. package/src/stripe/client/Charge.ts +119 -0
  399. package/src/stripe/client/MethodIntents.ts +37 -0
  400. package/src/stripe/client/index.ts +2 -0
  401. package/src/stripe/index.ts +1 -0
  402. package/src/stripe/server/Charge.ts +121 -0
  403. package/src/stripe/server/MethodIntents.ts +24 -0
  404. package/src/stripe/server/index.ts +2 -0
  405. package/src/tempo/Attribution.test.ts +187 -0
  406. package/src/tempo/Attribution.ts +156 -0
  407. package/src/tempo/Intents.test.ts +84 -0
  408. package/src/tempo/Intents.ts +93 -0
  409. package/src/tempo/client/ChannelOps.ts +233 -0
  410. package/src/tempo/client/Charge.ts +84 -0
  411. package/src/tempo/client/MethodIntents.ts +28 -0
  412. package/src/tempo/client/Session.ts +369 -0
  413. package/src/tempo/client/SessionManager.test.ts +223 -0
  414. package/src/tempo/client/SessionManager.ts +270 -0
  415. package/src/tempo/client/index.ts +5 -0
  416. package/src/tempo/index.ts +2 -0
  417. package/src/tempo/internal/account.ts +47 -0
  418. package/src/tempo/internal/defaults.ts +20 -0
  419. package/src/tempo/internal/types.ts +8 -0
  420. package/src/tempo/server/Charge.test.ts +847 -0
  421. package/src/tempo/server/Charge.ts +309 -0
  422. package/src/tempo/server/MethodIntents.ts +29 -0
  423. package/src/tempo/server/Session.test.ts +1349 -0
  424. package/src/tempo/server/Session.ts +773 -0
  425. package/src/tempo/server/Sse.test.ts +289 -0
  426. package/src/tempo/server/index.ts +5 -0
  427. package/src/tempo/server/internal/transport.ts +153 -0
  428. package/src/tempo/stream/Chain.ts +333 -0
  429. package/src/tempo/stream/Channel.ts +50 -0
  430. package/src/tempo/stream/ChannelStore.test.ts +473 -0
  431. package/src/tempo/stream/ChannelStore.ts +202 -0
  432. package/src/tempo/stream/Receipt.test.ts +84 -0
  433. package/src/tempo/stream/Receipt.ts +45 -0
  434. package/src/tempo/stream/Sse.test.ts +401 -0
  435. package/src/tempo/stream/Sse.ts +375 -0
  436. package/src/tempo/stream/Types.ts +86 -0
  437. package/src/tempo/stream/Voucher.test.ts +134 -0
  438. package/src/tempo/stream/Voucher.ts +123 -0
  439. package/src/tempo/stream/escrow.abi.ts +759 -0
  440. package/src/tempo/stream/index.ts +7 -0
  441. package/src/tsconfig.json +10 -0
  442. package/src/viem/Account.test.ts +71 -0
  443. package/src/viem/Account.ts +30 -0
  444. package/src/viem/Client.test.ts +58 -0
  445. package/src/viem/Client.ts +33 -0
  446. package/src/zod.ts +47 -0
@@ -0,0 +1,1349 @@
1
+ import { Mppx as Mppx_server, tempo as tempo_server } from 'mppx/server'
2
+ import { type Address, createClient, type Hex } from 'viem'
3
+ import { Addresses } from 'viem/tempo'
4
+ import { beforeAll, beforeEach, describe, expect, test } from 'vitest'
5
+ import {
6
+ deployEscrow,
7
+ signOpenChannel,
8
+ signTopUpChannel,
9
+ topUpChannel,
10
+ } from '~test/tempo/stream.js'
11
+ import { accounts, asset, chain, client, fundAccount, http } from '~test/tempo/viem.js'
12
+ import {
13
+ ChannelClosedError,
14
+ ChannelNotFoundError,
15
+ InsufficientBalanceError,
16
+ InvalidSignatureError,
17
+ } from '../../Errors.js'
18
+ import * as Store from '../../Store.js'
19
+ import * as ChannelStore from '../stream/ChannelStore.js'
20
+ import type { StreamReceipt } from '../stream/Types.js'
21
+ import { signVoucher } from '../stream/Voucher.js'
22
+ import { charge, session, settle } from './Session.js'
23
+
24
+ const payer = accounts[2]
25
+ const recipient = accounts[0].address
26
+ const currency = asset
27
+
28
+ let escrowContract: Address
29
+ let saltCounter = 0
30
+
31
+ beforeAll(async () => {
32
+ escrowContract = await deployEscrow()
33
+ await fundAccount({ address: payer.address, token: Addresses.pathUsd })
34
+ await fundAccount({ address: payer.address, token: currency })
35
+ })
36
+
37
+ describe('session', () => {
38
+ let rawStore: Store.Store
39
+ let store: ChannelStore.ChannelStore
40
+
41
+ beforeEach(() => {
42
+ rawStore = Store.memory()
43
+ store = ChannelStore.fromStore(rawStore)
44
+ })
45
+
46
+ function createServer(overrides: Partial<session.Parameters> = {}) {
47
+ return session({
48
+ store: rawStore,
49
+ getClient: () => client,
50
+ account: recipient,
51
+ currency,
52
+ escrowContract,
53
+ chainId: chain.id,
54
+ ...overrides,
55
+ } as session.Parameters)
56
+ }
57
+
58
+ describe('open', () => {
59
+ test('accepts a valid open with voucher', async () => {
60
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
61
+ const server = createServer()
62
+
63
+ const receipt = await server.verify({
64
+ credential: {
65
+ challenge: makeChallenge({ channelId }),
66
+ payload: {
67
+ action: 'open' as const,
68
+ type: 'transaction' as const,
69
+ channelId,
70
+ transaction: serializedTransaction,
71
+ cumulativeAmount: '1000000',
72
+ signature: await signTestVoucher(channelId, 1000000n),
73
+ },
74
+ },
75
+ request: makeRequest(),
76
+ })
77
+
78
+ expect(receipt.method).toBe('tempo')
79
+ expect(receipt.status).toBe('success')
80
+ expect(receipt.reference).toBe(channelId)
81
+
82
+ const ch = await store.getChannel(channelId)
83
+ expect(ch).not.toBeNull()
84
+ expect(ch!.highestVoucherAmount).toBe(1000000n)
85
+ })
86
+
87
+ test('rejects open when payee mismatch', async () => {
88
+ const wrongPayee = accounts[3].address
89
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n, {
90
+ payee: wrongPayee,
91
+ })
92
+ const server = createServer()
93
+
94
+ await expect(
95
+ server.verify({
96
+ credential: {
97
+ challenge: makeChallenge({ channelId }),
98
+ payload: {
99
+ action: 'open' as const,
100
+ type: 'transaction' as const,
101
+ channelId,
102
+ transaction: serializedTransaction,
103
+ cumulativeAmount: '1000000',
104
+ signature: await signTestVoucher(channelId, 1000000n),
105
+ },
106
+ },
107
+ request: makeRequest(),
108
+ }),
109
+ ).rejects.toThrow('open transaction payee does not match server recipient')
110
+ })
111
+
112
+ test('rejects open when voucher exceeds deposit', async () => {
113
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(500000n)
114
+ const server = createServer()
115
+
116
+ await expect(
117
+ server.verify({
118
+ credential: {
119
+ challenge: makeChallenge({ channelId }),
120
+ payload: {
121
+ action: 'open' as const,
122
+ type: 'transaction' as const,
123
+ channelId,
124
+ transaction: serializedTransaction,
125
+ cumulativeAmount: '1000000',
126
+ signature: await signTestVoucher(channelId, 1000000n),
127
+ },
128
+ },
129
+ request: makeRequest(),
130
+ }),
131
+ ).rejects.toThrow('channel available balance insufficient for requested amount')
132
+ })
133
+
134
+ test('rejects open with invalid voucher signature', async () => {
135
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
136
+ const server = createServer()
137
+
138
+ await expect(
139
+ server.verify({
140
+ credential: {
141
+ challenge: makeChallenge({ channelId }),
142
+ payload: {
143
+ action: 'open' as const,
144
+ type: 'transaction' as const,
145
+ channelId,
146
+ transaction: serializedTransaction,
147
+ cumulativeAmount: '1000000',
148
+ signature: `0x${'ab'.repeat(65)}` as Hex,
149
+ },
150
+ },
151
+ request: makeRequest(),
152
+ }),
153
+ ).rejects.toThrow('invalid voucher signature')
154
+ })
155
+
156
+ test('reopen existing channel with higher voucher updates state', async () => {
157
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
158
+ const server = createServer()
159
+
160
+ await server.verify({
161
+ credential: {
162
+ challenge: makeChallenge({ id: 'open-1', channelId }),
163
+ payload: {
164
+ action: 'open' as const,
165
+ type: 'transaction' as const,
166
+ channelId,
167
+ transaction: serializedTransaction,
168
+ cumulativeAmount: '1000000',
169
+ signature: await signTestVoucher(channelId, 1000000n),
170
+ },
171
+ },
172
+ request: makeRequest(),
173
+ })
174
+
175
+ const ch1 = await store.getChannel(channelId)
176
+ expect(ch1!.highestVoucherAmount).toBe(1000000n)
177
+
178
+ const receipt = await server.verify({
179
+ credential: {
180
+ challenge: makeChallenge({ id: 'open-2', channelId }),
181
+ payload: {
182
+ action: 'open' as const,
183
+ type: 'transaction' as const,
184
+ channelId,
185
+ transaction: serializedTransaction,
186
+ cumulativeAmount: '5000000',
187
+ signature: await signTestVoucher(channelId, 5000000n),
188
+ },
189
+ },
190
+ request: makeRequest(),
191
+ })
192
+
193
+ expect(receipt.status).toBe('success')
194
+ const ch2 = await store.getChannel(channelId)
195
+ expect(ch2!.highestVoucherAmount).toBe(5000000n)
196
+ })
197
+
198
+ test('reopen existing channel with same voucher keeps existing state', async () => {
199
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
200
+ const server = createServer()
201
+
202
+ await server.verify({
203
+ credential: {
204
+ challenge: makeChallenge({ id: 'open-1', channelId }),
205
+ payload: {
206
+ action: 'open' as const,
207
+ type: 'transaction' as const,
208
+ channelId,
209
+ transaction: serializedTransaction,
210
+ cumulativeAmount: '1000000',
211
+ signature: await signTestVoucher(channelId, 1000000n),
212
+ },
213
+ },
214
+ request: makeRequest(),
215
+ })
216
+
217
+ const receipt = await server.verify({
218
+ credential: {
219
+ challenge: makeChallenge({ id: 'open-2', channelId }),
220
+ payload: {
221
+ action: 'open' as const,
222
+ type: 'transaction' as const,
223
+ channelId,
224
+ transaction: serializedTransaction,
225
+ cumulativeAmount: '1000000',
226
+ signature: await signTestVoucher(channelId, 1000000n),
227
+ },
228
+ },
229
+ request: makeRequest(),
230
+ })
231
+
232
+ expect(receipt.status).toBe('success')
233
+ const ch = await store.getChannel(channelId)
234
+ expect(ch!.highestVoucherAmount).toBe(1000000n)
235
+ })
236
+
237
+ test('rejects voucher below settledOnChain', async () => {
238
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
239
+ const server = createServer()
240
+
241
+ await server.verify({
242
+ credential: {
243
+ challenge: makeChallenge({ id: 'open-1', channelId }),
244
+ payload: {
245
+ action: 'open' as const,
246
+ type: 'transaction' as const,
247
+ channelId,
248
+ transaction: serializedTransaction,
249
+ cumulativeAmount: '5000000',
250
+ signature: await signTestVoucher(channelId, 5000000n),
251
+ },
252
+ },
253
+ request: makeRequest(),
254
+ })
255
+
256
+ await store.updateChannel(channelId, (ch) =>
257
+ ch ? { ...ch, settledOnChain: 5000000n } : null,
258
+ )
259
+
260
+ await expect(
261
+ server.verify({
262
+ credential: {
263
+ challenge: makeChallenge({ id: 'open-2', channelId }),
264
+ payload: {
265
+ action: 'open' as const,
266
+ type: 'transaction' as const,
267
+ channelId,
268
+ transaction: serializedTransaction,
269
+ cumulativeAmount: '3000000',
270
+ signature: await signTestVoucher(channelId, 3000000n),
271
+ },
272
+ },
273
+ request: makeRequest(),
274
+ }),
275
+ ).rejects.toThrow('voucher amount is below settled on-chain amount')
276
+ })
277
+
278
+ test('zero signer fallback uses payer', async () => {
279
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n, {
280
+ authorizedSigner: '0x0000000000000000000000000000000000000000' as Address,
281
+ })
282
+ const server = createServer()
283
+
284
+ const receipt = await server.verify({
285
+ credential: {
286
+ challenge: makeChallenge({ channelId }),
287
+ payload: {
288
+ action: 'open' as const,
289
+ type: 'transaction' as const,
290
+ channelId,
291
+ transaction: serializedTransaction,
292
+ cumulativeAmount: '1000000',
293
+ signature: await signTestVoucher(channelId, 1000000n),
294
+ },
295
+ },
296
+ request: makeRequest(),
297
+ })
298
+
299
+ expect(receipt.status).toBe('success')
300
+ })
301
+ })
302
+
303
+ describe('voucher', () => {
304
+ async function openServerChannel(
305
+ server: ReturnType<typeof createServer>,
306
+ channelId: Hex,
307
+ serializedTransaction: Hex,
308
+ ) {
309
+ await server.verify({
310
+ credential: {
311
+ challenge: makeChallenge({ id: 'open-challenge', channelId }),
312
+ payload: {
313
+ action: 'open' as const,
314
+ type: 'transaction' as const,
315
+ channelId,
316
+ transaction: serializedTransaction,
317
+ cumulativeAmount: '1000000',
318
+ signature: await signTestVoucher(channelId, 1000000n),
319
+ },
320
+ },
321
+ request: makeRequest(),
322
+ })
323
+ }
324
+
325
+ test('accepts increasing voucher', async () => {
326
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
327
+ const server = createServer()
328
+ await openServerChannel(server, channelId, serializedTransaction)
329
+
330
+ const receipt = await server.verify({
331
+ credential: {
332
+ challenge: makeChallenge({ id: 'challenge-2', channelId }),
333
+ payload: {
334
+ action: 'voucher' as const,
335
+ channelId,
336
+ cumulativeAmount: '2000000',
337
+ signature: await signTestVoucher(channelId, 2000000n),
338
+ },
339
+ },
340
+ request: makeRequest(),
341
+ })
342
+
343
+ expect(receipt.status).toBe('success')
344
+
345
+ const ch = await store.getChannel(channelId)
346
+ expect(ch!.highestVoucherAmount).toBe(2000000n)
347
+ })
348
+
349
+ test('returns success for non-increasing voucher (idempotency)', async () => {
350
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
351
+ const server = createServer()
352
+ await openServerChannel(server, channelId, serializedTransaction)
353
+
354
+ const receipt = await server.verify({
355
+ credential: {
356
+ challenge: makeChallenge({ id: 'challenge-2', channelId }),
357
+ payload: {
358
+ action: 'voucher' as const,
359
+ channelId,
360
+ cumulativeAmount: '500000',
361
+ signature: await signTestVoucher(channelId, 500000n),
362
+ },
363
+ },
364
+ request: makeRequest(),
365
+ })
366
+
367
+ expect(receipt.status).toBe('success')
368
+ expect((receipt as StreamReceipt).acceptedCumulative).toBe('1000000')
369
+ })
370
+
371
+ test('rejects voucher exceeding deposit', async () => {
372
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
373
+ const server = createServer()
374
+ await openServerChannel(server, channelId, serializedTransaction)
375
+
376
+ await expect(
377
+ server.verify({
378
+ credential: {
379
+ challenge: makeChallenge({ id: 'challenge-2', channelId }),
380
+ payload: {
381
+ action: 'voucher' as const,
382
+ channelId,
383
+ cumulativeAmount: '99999999',
384
+ signature: await signTestVoucher(channelId, 99999999n),
385
+ },
386
+ },
387
+ request: makeRequest(),
388
+ }),
389
+ ).rejects.toThrow('voucher amount exceeds on-chain deposit')
390
+ })
391
+
392
+ test('rejects voucher below minVoucherDelta', async () => {
393
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
394
+ const server = createServer({ minVoucherDelta: '2' })
395
+ await openServerChannel(server, channelId, serializedTransaction)
396
+
397
+ await expect(
398
+ server.verify({
399
+ credential: {
400
+ challenge: makeChallenge({ id: 'challenge-2', channelId }),
401
+ payload: {
402
+ action: 'voucher' as const,
403
+ channelId,
404
+ cumulativeAmount: '1500000',
405
+ signature: await signTestVoucher(channelId, 1500000n),
406
+ },
407
+ },
408
+ request: makeRequest(),
409
+ }),
410
+ ).rejects.toThrow('voucher delta 500000 below minimum 2000000')
411
+ })
412
+
413
+ test('rejects voucher on unknown channel', async () => {
414
+ const { channelId } = await createSignedOpenTransaction(10000000n)
415
+ const server = createServer()
416
+
417
+ await expect(
418
+ server.verify({
419
+ credential: {
420
+ challenge: makeChallenge({ channelId }),
421
+ payload: {
422
+ action: 'voucher' as const,
423
+ channelId,
424
+ cumulativeAmount: '1000000',
425
+ signature: await signTestVoucher(channelId, 1000000n),
426
+ },
427
+ },
428
+ request: makeRequest(),
429
+ }),
430
+ ).rejects.toThrow(ChannelNotFoundError)
431
+ })
432
+ })
433
+
434
+ describe('topUp', () => {
435
+ async function openServerChannel(
436
+ server: ReturnType<typeof createServer>,
437
+ channelId: Hex,
438
+ serializedTransaction: Hex,
439
+ ) {
440
+ await server.verify({
441
+ credential: {
442
+ challenge: makeChallenge({ id: 'open-challenge', channelId }),
443
+ payload: {
444
+ action: 'open' as const,
445
+ type: 'transaction' as const,
446
+ channelId,
447
+ transaction: serializedTransaction,
448
+ cumulativeAmount: '1000000',
449
+ signature: await signTestVoucher(channelId, 1000000n),
450
+ },
451
+ },
452
+ request: makeRequest(),
453
+ })
454
+ }
455
+
456
+ test('accepts topUp with increased deposit', async () => {
457
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
458
+ const server = createServer()
459
+ await openServerChannel(server, channelId, serializedTransaction)
460
+
461
+ const { serializedTransaction: topUpTx } = await signTopUpChannel({
462
+ escrow: escrowContract,
463
+ payer,
464
+ channelId,
465
+ token: currency,
466
+ amount: 10000000n,
467
+ })
468
+
469
+ const receipt = await server.verify({
470
+ credential: {
471
+ challenge: makeChallenge({ id: 'challenge-2', channelId }),
472
+ payload: {
473
+ action: 'topUp' as const,
474
+ type: 'transaction' as const,
475
+ channelId,
476
+ transaction: topUpTx,
477
+ additionalDeposit: '10000000',
478
+ },
479
+ },
480
+ request: makeRequest(),
481
+ })
482
+
483
+ expect(receipt.status).toBe('success')
484
+
485
+ const ch = await store.getChannel(channelId)
486
+ expect(ch!.deposit).toBe(20000000n)
487
+ })
488
+
489
+ test('topUp receipt preserves spent and units from prior charges', async () => {
490
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
491
+ const server = createServer()
492
+ await openServerChannel(server, channelId, serializedTransaction)
493
+
494
+ await charge(store, channelId, 500000n)
495
+ await charge(store, channelId, 300000n)
496
+
497
+ const chBefore = await store.getChannel(channelId)
498
+ expect(chBefore!.spent).toBe(800000n)
499
+ expect(chBefore!.units).toBe(2)
500
+
501
+ const { serializedTransaction: topUpTx } = await signTopUpChannel({
502
+ escrow: escrowContract,
503
+ payer,
504
+ channelId,
505
+ token: currency,
506
+ amount: 5000000n,
507
+ })
508
+
509
+ const receipt = (await server.verify({
510
+ credential: {
511
+ challenge: makeChallenge({ id: 'challenge-topup', channelId }),
512
+ payload: {
513
+ action: 'topUp' as const,
514
+ type: 'transaction' as const,
515
+ channelId,
516
+ transaction: topUpTx,
517
+ additionalDeposit: '5000000',
518
+ },
519
+ },
520
+ request: makeRequest(),
521
+ })) as StreamReceipt
522
+
523
+ expect(receipt.status).toBe('success')
524
+ expect(receipt.spent).toBe('800000')
525
+ expect(receipt.units).toBe(2)
526
+
527
+ const chAfter = await store.getChannel(channelId)
528
+ expect(chAfter!.spent).toBe(800000n)
529
+ expect(chAfter!.units).toBe(2)
530
+ expect(chAfter!.deposit).toBe(15000000n)
531
+ })
532
+
533
+ test('rejects topUp on unknown channel', async () => {
534
+ const { channelId } = await createSignedOpenTransaction(10000000n)
535
+ const server = createServer()
536
+
537
+ await expect(
538
+ server.verify({
539
+ credential: {
540
+ challenge: makeChallenge({ channelId }),
541
+ payload: {
542
+ action: 'topUp' as const,
543
+ type: 'transaction' as const,
544
+ channelId,
545
+ transaction: '0xabcdef' as Hex,
546
+ additionalDeposit: '5000000',
547
+ },
548
+ },
549
+ request: makeRequest(),
550
+ }),
551
+ ).rejects.toThrow(ChannelNotFoundError)
552
+ })
553
+ })
554
+
555
+ describe('close', () => {
556
+ async function openServerChannel(
557
+ server: ReturnType<typeof createServer>,
558
+ channelId: Hex,
559
+ serializedTransaction: Hex,
560
+ ) {
561
+ await server.verify({
562
+ credential: {
563
+ challenge: makeChallenge({ id: 'open-challenge', channelId }),
564
+ payload: {
565
+ action: 'open' as const,
566
+ type: 'transaction' as const,
567
+ channelId,
568
+ transaction: serializedTransaction,
569
+ cumulativeAmount: '1000000',
570
+ signature: await signTestVoucher(channelId, 1000000n),
571
+ },
572
+ },
573
+ request: makeRequest(),
574
+ })
575
+ }
576
+
577
+ test('accepts close with final voucher >= highest', async () => {
578
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
579
+ const server = createServer()
580
+ await openServerChannel(server, channelId, serializedTransaction)
581
+
582
+ const receipt = await server.verify({
583
+ credential: {
584
+ challenge: makeChallenge({ id: 'challenge-2', channelId }),
585
+ payload: {
586
+ action: 'close' as const,
587
+ channelId,
588
+ cumulativeAmount: '1000000',
589
+ signature: await signTestVoucher(channelId, 1000000n),
590
+ },
591
+ },
592
+ request: makeRequest(),
593
+ })
594
+
595
+ expect(receipt.status).toBe('success')
596
+
597
+ const ch = await store.getChannel(channelId)
598
+ expect(ch).not.toBeNull()
599
+ expect(ch!.highestVoucherAmount).toBe(1000000n)
600
+ })
601
+
602
+ test('accepts close with voucher higher than previous highest', async () => {
603
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
604
+ const server = createServer()
605
+ await openServerChannel(server, channelId, serializedTransaction)
606
+
607
+ const receipt = await server.verify({
608
+ credential: {
609
+ challenge: makeChallenge({ id: 'challenge-2', channelId }),
610
+ payload: {
611
+ action: 'close' as const,
612
+ channelId,
613
+ cumulativeAmount: '5000000',
614
+ signature: await signTestVoucher(channelId, 5000000n),
615
+ },
616
+ },
617
+ request: makeRequest(),
618
+ })
619
+
620
+ expect(receipt.status).toBe('success')
621
+
622
+ const ch = await store.getChannel(channelId)
623
+ expect(ch!.highestVoucherAmount).toBe(5000000n)
624
+ })
625
+
626
+ test('rejects close with voucher below highest', async () => {
627
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
628
+ const server = createServer()
629
+ await openServerChannel(server, channelId, serializedTransaction)
630
+
631
+ await server.verify({
632
+ credential: {
633
+ challenge: makeChallenge({ id: 'challenge-2', channelId }),
634
+ payload: {
635
+ action: 'voucher' as const,
636
+ channelId,
637
+ cumulativeAmount: '3000000',
638
+ signature: await signTestVoucher(channelId, 3000000n),
639
+ },
640
+ },
641
+ request: makeRequest(),
642
+ })
643
+
644
+ await expect(
645
+ server.verify({
646
+ credential: {
647
+ challenge: makeChallenge({ id: 'challenge-3', channelId }),
648
+ payload: {
649
+ action: 'close' as const,
650
+ channelId,
651
+ cumulativeAmount: '2000000',
652
+ signature: await signTestVoucher(channelId, 2000000n),
653
+ },
654
+ },
655
+ request: makeRequest(),
656
+ }),
657
+ ).rejects.toThrow('close voucher amount must be >= highest accepted voucher')
658
+ })
659
+
660
+ test('rejects close exceeding on-chain deposit', async () => {
661
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
662
+ const server = createServer()
663
+ await openServerChannel(server, channelId, serializedTransaction)
664
+
665
+ await expect(
666
+ server.verify({
667
+ credential: {
668
+ challenge: makeChallenge({ id: 'challenge-2', channelId }),
669
+ payload: {
670
+ action: 'close' as const,
671
+ channelId,
672
+ cumulativeAmount: '99999999',
673
+ signature: await signTestVoucher(channelId, 99999999n),
674
+ },
675
+ },
676
+ request: makeRequest(),
677
+ }),
678
+ ).rejects.toThrow('close voucher amount exceeds on-chain deposit')
679
+ })
680
+
681
+ test('close re-reads on-chain deposit (not stale stored value)', async () => {
682
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
683
+ const server = createServer()
684
+ await openServerChannel(server, channelId, serializedTransaction)
685
+
686
+ await topUpChannel({
687
+ escrow: escrowContract,
688
+ payer,
689
+ channelId,
690
+ token: currency,
691
+ amount: 10000000n,
692
+ })
693
+
694
+ const receipt = await server.verify({
695
+ credential: {
696
+ challenge: makeChallenge({ id: 'challenge-2', channelId }),
697
+ payload: {
698
+ action: 'close' as const,
699
+ channelId,
700
+ cumulativeAmount: '15000000',
701
+ signature: await signTestVoucher(channelId, 15000000n),
702
+ },
703
+ },
704
+ request: makeRequest(),
705
+ })
706
+
707
+ expect(receipt.status).toBe('success')
708
+ })
709
+
710
+ test('rejects close on unknown channel', async () => {
711
+ const { channelId } = await createSignedOpenTransaction(10000000n)
712
+ const server = createServer()
713
+
714
+ await expect(
715
+ server.verify({
716
+ credential: {
717
+ challenge: makeChallenge({ channelId }),
718
+ payload: {
719
+ action: 'close' as const,
720
+ channelId,
721
+ cumulativeAmount: '1000000',
722
+ signature: await signTestVoucher(channelId, 1000000n),
723
+ },
724
+ },
725
+ request: makeRequest(),
726
+ }),
727
+ ).rejects.toThrow(ChannelNotFoundError)
728
+ })
729
+
730
+ test('close submits on-chain and returns txHash', async () => {
731
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
732
+ const server = createServer({ getClient: () => client })
733
+ await openServerChannel(server, channelId, serializedTransaction)
734
+
735
+ const receipt = await server.verify({
736
+ credential: {
737
+ challenge: makeChallenge({ id: 'challenge-2', channelId }),
738
+ payload: {
739
+ action: 'close' as const,
740
+ channelId,
741
+ cumulativeAmount: '1000000',
742
+ signature: await signTestVoucher(channelId, 1000000n),
743
+ },
744
+ },
745
+ request: makeRequest(),
746
+ })
747
+
748
+ expect(receipt.status).toBe('success')
749
+ expect((receipt as StreamReceipt).txHash).toMatch(/^0x/)
750
+
751
+ const ch = await store.getChannel(channelId)
752
+ expect(ch!.finalized).toBe(true)
753
+ })
754
+
755
+ test('close throws when client has no account', async () => {
756
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
757
+ const server = createServer({
758
+ getClient: () => createClient({ chain, transport: http() }),
759
+ })
760
+ await openServerChannel(server, channelId, serializedTransaction)
761
+
762
+ await expect(
763
+ server.verify({
764
+ credential: {
765
+ challenge: makeChallenge({ id: 'challenge-2', channelId }),
766
+ payload: {
767
+ action: 'close' as const,
768
+ channelId,
769
+ cumulativeAmount: '1000000',
770
+ signature: await signTestVoucher(channelId, 1000000n),
771
+ },
772
+ },
773
+ request: makeRequest(),
774
+ }),
775
+ ).rejects.toThrow('Cannot close channel: no account available')
776
+ })
777
+ })
778
+
779
+ describe('full lifecycle', () => {
780
+ test('open -> voucher -> voucher -> close', async () => {
781
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
782
+ const server = createServer()
783
+
784
+ await server.verify({
785
+ credential: {
786
+ challenge: makeChallenge({ id: 'c1', channelId }),
787
+ payload: {
788
+ action: 'open' as const,
789
+ type: 'transaction' as const,
790
+ channelId,
791
+ transaction: serializedTransaction,
792
+ cumulativeAmount: '1000000',
793
+ signature: await signTestVoucher(channelId, 1000000n),
794
+ },
795
+ },
796
+ request: makeRequest(),
797
+ })
798
+
799
+ const r2 = await server.verify({
800
+ credential: {
801
+ challenge: makeChallenge({ id: 'c2', channelId }),
802
+ payload: {
803
+ action: 'voucher' as const,
804
+ channelId,
805
+ cumulativeAmount: '3000000',
806
+ signature: await signTestVoucher(channelId, 3000000n),
807
+ },
808
+ },
809
+ request: makeRequest(),
810
+ })
811
+ expect(r2.status).toBe('success')
812
+
813
+ const r3 = await server.verify({
814
+ credential: {
815
+ challenge: makeChallenge({ id: 'c3', channelId }),
816
+ payload: {
817
+ action: 'voucher' as const,
818
+ channelId,
819
+ cumulativeAmount: '7000000',
820
+ signature: await signTestVoucher(channelId, 7000000n),
821
+ },
822
+ },
823
+ request: makeRequest(),
824
+ })
825
+ expect(r3.status).toBe('success')
826
+
827
+ const ch = await store.getChannel(channelId)
828
+ expect(ch!.highestVoucherAmount).toBe(7000000n)
829
+
830
+ const r4 = await server.verify({
831
+ credential: {
832
+ challenge: makeChallenge({ id: 'c4', channelId }),
833
+ payload: {
834
+ action: 'close' as const,
835
+ channelId,
836
+ cumulativeAmount: '7000000',
837
+ signature: await signTestVoucher(channelId, 7000000n),
838
+ },
839
+ },
840
+ request: makeRequest(),
841
+ })
842
+ expect(r4.status).toBe('success')
843
+ expect(r4.reference).toBe(channelId)
844
+
845
+ const chAfter = await store.getChannel(channelId)
846
+ expect(chAfter).not.toBeNull()
847
+ expect(chAfter!.highestVoucherAmount).toBe(7000000n)
848
+ })
849
+ })
850
+
851
+ describe('charge', () => {
852
+ test('deducts balance from session', async () => {
853
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
854
+ const server = createServer()
855
+
856
+ await server.verify({
857
+ credential: {
858
+ challenge: makeChallenge({ id: 'c1', channelId }),
859
+ payload: {
860
+ action: 'open' as const,
861
+ type: 'transaction' as const,
862
+ channelId,
863
+ transaction: serializedTransaction,
864
+ cumulativeAmount: '5000000',
865
+ signature: await signTestVoucher(channelId, 5000000n),
866
+ },
867
+ },
868
+ request: makeRequest(),
869
+ })
870
+
871
+ const result = await charge(store, channelId, 1000000n)
872
+ expect(result.spent).toBe(1000000n)
873
+ expect(result.units).toBe(1)
874
+
875
+ const result2 = await charge(store, channelId, 2000000n)
876
+ expect(result2.spent).toBe(3000000n)
877
+ expect(result2.units).toBe(2)
878
+ })
879
+
880
+ test('rejects overdraft with InsufficientBalanceError', async () => {
881
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
882
+ const server = createServer()
883
+
884
+ await server.verify({
885
+ credential: {
886
+ challenge: makeChallenge({ id: 'c1', channelId }),
887
+ payload: {
888
+ action: 'open' as const,
889
+ type: 'transaction' as const,
890
+ channelId,
891
+ transaction: serializedTransaction,
892
+ cumulativeAmount: '1000000',
893
+ signature: await signTestVoucher(channelId, 1000000n),
894
+ },
895
+ },
896
+ request: makeRequest(),
897
+ })
898
+
899
+ await expect(charge(store, channelId, 2000000n)).rejects.toThrow(InsufficientBalanceError)
900
+ })
901
+
902
+ test('rejects charge on missing channel', async () => {
903
+ const fakeChannelId =
904
+ '0x0000000000000000000000000000000000000000000000000000000000000000' as Hex
905
+ await expect(charge(store, fakeChannelId, 100n)).rejects.toThrow(ChannelClosedError)
906
+ })
907
+ })
908
+
909
+ describe('settle', () => {
910
+ test('one-shot settle updates settledOnChain', async () => {
911
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
912
+ const server = createServer()
913
+
914
+ await server.verify({
915
+ credential: {
916
+ challenge: makeChallenge({ id: 'c1', channelId }),
917
+ payload: {
918
+ action: 'open' as const,
919
+ type: 'transaction' as const,
920
+ channelId,
921
+ transaction: serializedTransaction,
922
+ cumulativeAmount: '5000000',
923
+ signature: await signTestVoucher(channelId, 5000000n),
924
+ },
925
+ },
926
+ request: makeRequest(),
927
+ })
928
+
929
+ const settleTxHash = await settle(store, client, channelId, escrowContract)
930
+ expect(settleTxHash).toMatch(/^0x/)
931
+
932
+ const ch = await store.getChannel(channelId)
933
+ expect(ch!.settledOnChain).toBe(5000000n)
934
+ })
935
+
936
+ test('settle rejects when no channel found', async () => {
937
+ const fakeChannelId =
938
+ '0x0000000000000000000000000000000000000000000000000000000000000000' as Hex
939
+ await expect(settle(store, client, fakeChannelId, escrowContract)).rejects.toThrow(
940
+ ChannelNotFoundError,
941
+ )
942
+ })
943
+ })
944
+
945
+ describe('structured errors', () => {
946
+ test('ChannelNotFoundError on unknown channel', async () => {
947
+ const { channelId } = await createSignedOpenTransaction(10000000n)
948
+ const server = createServer()
949
+
950
+ try {
951
+ await server.verify({
952
+ credential: {
953
+ challenge: makeChallenge({ channelId }),
954
+ payload: {
955
+ action: 'voucher' as const,
956
+ channelId,
957
+ cumulativeAmount: '1000000',
958
+ signature: await signTestVoucher(channelId, 1000000n),
959
+ },
960
+ },
961
+ request: makeRequest(),
962
+ })
963
+ expect.unreachable()
964
+ } catch (e) {
965
+ expect(e).toBeInstanceOf(ChannelNotFoundError)
966
+ expect((e as ChannelNotFoundError).status).toBe(410)
967
+ }
968
+ })
969
+
970
+ test('InvalidSignatureError has status 402', async () => {
971
+ const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
972
+ const server = createServer()
973
+
974
+ try {
975
+ await server.verify({
976
+ credential: {
977
+ challenge: makeChallenge({ channelId }),
978
+ payload: {
979
+ action: 'open' as const,
980
+ type: 'transaction' as const,
981
+ channelId,
982
+ transaction: serializedTransaction,
983
+ cumulativeAmount: '1000000',
984
+ signature: `0x${'ab'.repeat(65)}` as Hex,
985
+ },
986
+ },
987
+ request: makeRequest(),
988
+ })
989
+ expect.unreachable()
990
+ } catch (e) {
991
+ expect(e).toBeInstanceOf(InvalidSignatureError)
992
+ expect((e as InvalidSignatureError).status).toBe(402)
993
+ }
994
+ })
995
+ })
996
+
997
+ describe('respond', () => {
998
+ test('returns 204 for POST with open action', () => {
999
+ const server = createServer()
1000
+ const result = server.respond!({
1001
+ credential: {
1002
+ challenge: makeChallenge({
1003
+ channelId: '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex,
1004
+ }),
1005
+ payload: { action: 'open' },
1006
+ },
1007
+ input: new Request('http://localhost', { method: 'POST' }),
1008
+ } as any)
1009
+ expect(result).toBeInstanceOf(Response)
1010
+ expect((result as Response).status).toBe(204)
1011
+ })
1012
+
1013
+ test('returns 204 for POST with topUp action', () => {
1014
+ const server = createServer()
1015
+ const result = server.respond!({
1016
+ credential: {
1017
+ challenge: makeChallenge({
1018
+ channelId: '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex,
1019
+ }),
1020
+ payload: { action: 'topUp' },
1021
+ },
1022
+ input: new Request('http://localhost', { method: 'POST' }),
1023
+ } as any)
1024
+ expect(result).toBeInstanceOf(Response)
1025
+ expect((result as Response).status).toBe(204)
1026
+ })
1027
+
1028
+ test('returns 204 for POST with close action', () => {
1029
+ const server = createServer()
1030
+ const result = server.respond!({
1031
+ credential: {
1032
+ challenge: makeChallenge({
1033
+ channelId: '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex,
1034
+ }),
1035
+ payload: { action: 'close' },
1036
+ },
1037
+ input: new Request('http://localhost', { method: 'POST' }),
1038
+ } as any)
1039
+ expect(result).toBeInstanceOf(Response)
1040
+ expect((result as Response).status).toBe(204)
1041
+ })
1042
+
1043
+ test('returns 204 for GET with close action', () => {
1044
+ const server = createServer()
1045
+ const result = server.respond!({
1046
+ credential: {
1047
+ challenge: makeChallenge({
1048
+ channelId: '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex,
1049
+ }),
1050
+ payload: { action: 'close' },
1051
+ },
1052
+ input: new Request('http://localhost', { method: 'GET' }),
1053
+ } as any)
1054
+ expect(result).toBeInstanceOf(Response)
1055
+ expect((result as Response).status).toBe(204)
1056
+ })
1057
+
1058
+ test('returns 204 for POST with voucher action', () => {
1059
+ const server = createServer()
1060
+ const result = server.respond!({
1061
+ credential: {
1062
+ challenge: makeChallenge({
1063
+ channelId: '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex,
1064
+ }),
1065
+ payload: { action: 'voucher' },
1066
+ },
1067
+ input: new Request('http://localhost', { method: 'POST' }),
1068
+ } as any)
1069
+ expect(result).toBeInstanceOf(Response)
1070
+ expect((result as Response).status).toBe(204)
1071
+ })
1072
+
1073
+ test('returns undefined for GET with open action (management actions only gated on POST)', () => {
1074
+ const server = createServer()
1075
+ const result = server.respond!({
1076
+ credential: {
1077
+ challenge: makeChallenge({
1078
+ channelId: '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex,
1079
+ }),
1080
+ payload: { action: 'open' },
1081
+ },
1082
+ input: new Request('http://localhost', { method: 'GET' }),
1083
+ } as any)
1084
+ expect(result).toBeUndefined()
1085
+ })
1086
+
1087
+ test('returns undefined for GET with voucher action (auto-mode)', () => {
1088
+ const server = createServer()
1089
+ const result = server.respond!({
1090
+ credential: {
1091
+ challenge: makeChallenge({
1092
+ channelId: '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex,
1093
+ }),
1094
+ payload: { action: 'voucher' },
1095
+ },
1096
+ input: new Request('http://localhost', { method: 'GET' }),
1097
+ } as any)
1098
+ expect(result).toBeUndefined()
1099
+ })
1100
+ })
1101
+
1102
+ describe('SSE', () => {
1103
+ test('behavior: withReceipt accepts async generator and returns Response', async () => {
1104
+ const handler = Mppx_server.create({
1105
+ methods: [
1106
+ tempo_server.session({
1107
+ account: accounts[0],
1108
+ currency: asset,
1109
+ escrowContract,
1110
+ getClient: () => client,
1111
+ stream: true,
1112
+ }),
1113
+ ],
1114
+ realm: 'api.example.com',
1115
+ secretKey: 'secret',
1116
+ })
1117
+
1118
+ const result = await handler.session({
1119
+ amount: '1000',
1120
+ decimals: 6,
1121
+ unitType: 'token',
1122
+ })(new Request('https://example.com'))
1123
+
1124
+ if (result.status === 200) {
1125
+ // async generator function should be accepted and return Response
1126
+ const response = result.withReceipt(async function* (_stream) {
1127
+ yield 'token'
1128
+ })
1129
+ expectTypeOf(response).toEqualTypeOf<Response>()
1130
+
1131
+ // plain Response should also be accepted
1132
+ const response2 = result.withReceipt(new Response())
1133
+ expectTypeOf(response2).toEqualTypeOf<Response>()
1134
+
1135
+ // async iterable should also be accepted
1136
+ const iterable: AsyncIterable<string> = (async function* () {
1137
+ yield 'token'
1138
+ })()
1139
+ const response3 = result.withReceipt(iterable)
1140
+ expectTypeOf(response3).toEqualTypeOf<Response>()
1141
+
1142
+ // no-arg form should return Response
1143
+ const response4 = result.withReceipt()
1144
+ expectTypeOf(response4).toEqualTypeOf<Response>()
1145
+ }
1146
+ })
1147
+
1148
+ test('behavior: non-stream session withReceipt only accepts Response', async () => {
1149
+ const handler = Mppx_server.create({
1150
+ methods: [
1151
+ tempo_server.session({
1152
+ account: accounts[0],
1153
+ currency: asset,
1154
+ escrowContract,
1155
+ getClient: () => client,
1156
+ }),
1157
+ ],
1158
+ realm: 'api.example.com',
1159
+ secretKey: 'secret',
1160
+ })
1161
+
1162
+ const result = await handler.session({
1163
+ amount: '1000',
1164
+ decimals: 6,
1165
+ unitType: 'token',
1166
+ })(new Request('https://example.com'))
1167
+
1168
+ if (result.status === 200) {
1169
+ const response = result.withReceipt(new Response())
1170
+ expectTypeOf(response).toEqualTypeOf<Response>()
1171
+ }
1172
+ })
1173
+
1174
+ test('behavior: charge withReceipt returns Response', async () => {
1175
+ const handler = Mppx_server.create({
1176
+ methods: [tempo_server.charge({ account: accounts[0], currency: asset })],
1177
+ realm: 'api.example.com',
1178
+ secretKey: 'secret',
1179
+ })
1180
+
1181
+ const result = await handler.charge({
1182
+ amount: '1000',
1183
+ decimals: 6,
1184
+ })(new Request('https://example.com'))
1185
+
1186
+ if (result.status === 200) {
1187
+ const response = result.withReceipt(new Response())
1188
+ expectTypeOf(response).toEqualTypeOf<Response>()
1189
+ }
1190
+ })
1191
+ })
1192
+ })
1193
+
1194
+ describe('monotonicity and TOCTOU (unit tests)', () => {
1195
+ const testChannelId = '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex
1196
+
1197
+ function seedChannel(
1198
+ store: ChannelStore.ChannelStore,
1199
+ overrides: Partial<ChannelStore.State> = {},
1200
+ ) {
1201
+ return store.updateChannel(testChannelId, () => ({
1202
+ channelId: testChannelId,
1203
+ payer: '0x0000000000000000000000000000000000000001' as Address,
1204
+ payee: '0x0000000000000000000000000000000000000002' as Address,
1205
+ token: '0x0000000000000000000000000000000000000003' as Address,
1206
+ authorizedSigner: '0x0000000000000000000000000000000000000004' as Address,
1207
+ chainId: 42431,
1208
+ escrowContract: '0x542831e3E4Ace07559b7C8787395f4Fb99F70787' as Address,
1209
+ deposit: 10000000n,
1210
+ settledOnChain: 0n,
1211
+ highestVoucherAmount: 5000000n,
1212
+ highestVoucher: {
1213
+ channelId: testChannelId,
1214
+ cumulativeAmount: 5000000n,
1215
+ signature: '0xdeadbeef' as Hex,
1216
+ },
1217
+ spent: 0n,
1218
+ units: 0,
1219
+ finalized: false,
1220
+ createdAt: new Date().toISOString(),
1221
+ ...overrides,
1222
+ }))
1223
+ }
1224
+
1225
+ test('charge does not allow highestVoucherAmount to decrease', async () => {
1226
+ const store = ChannelStore.fromStore(Store.memory())
1227
+ await seedChannel(store, { highestVoucherAmount: 5000000n, spent: 0n, units: 0 })
1228
+
1229
+ const channel = await charge(store, testChannelId, 1000000n)
1230
+ expect(channel.spent).toBe(1000000n)
1231
+ expect(channel.highestVoucherAmount).toBe(5000000n)
1232
+ })
1233
+
1234
+ test('settle uses max(settledOnChain) and does not regress', async () => {
1235
+ const store = ChannelStore.fromStore(Store.memory())
1236
+ await seedChannel(store, { settledOnChain: 3000000n })
1237
+
1238
+ await store.updateChannel(testChannelId, (current) => {
1239
+ if (!current) return null
1240
+ const settledAmount = 2000000n
1241
+ const nextSettled =
1242
+ settledAmount > current.settledOnChain ? settledAmount : current.settledOnChain
1243
+ return { ...current, settledOnChain: nextSettled }
1244
+ })
1245
+
1246
+ const ch = await store.getChannel(testChannelId)
1247
+ expect(ch!.settledOnChain).toBe(3000000n)
1248
+ })
1249
+
1250
+ test('settle updates settledOnChain when higher', async () => {
1251
+ const store = ChannelStore.fromStore(Store.memory())
1252
+ await seedChannel(store, { settledOnChain: 1000000n })
1253
+
1254
+ await store.updateChannel(testChannelId, (current) => {
1255
+ if (!current) return null
1256
+ const settledAmount = 5000000n
1257
+ const nextSettled =
1258
+ settledAmount > current.settledOnChain ? settledAmount : current.settledOnChain
1259
+ return { ...current, settledOnChain: nextSettled }
1260
+ })
1261
+
1262
+ const ch = await store.getChannel(testChannelId)
1263
+ expect(ch!.settledOnChain).toBe(5000000n)
1264
+ })
1265
+
1266
+ test('acceptVoucher is monotonic — lower value does not decrease highestVoucherAmount', async () => {
1267
+ const store = ChannelStore.fromStore(Store.memory())
1268
+ await seedChannel(store, { highestVoucherAmount: 5000000n, spent: 2000000n, units: 3 })
1269
+
1270
+ const channel = await store.updateChannel(testChannelId, (existing) => {
1271
+ if (!existing) return null
1272
+ const nextHighest =
1273
+ 3000000n > existing.highestVoucherAmount ? 3000000n : existing.highestVoucherAmount
1274
+ return { ...existing, highestVoucherAmount: nextHighest }
1275
+ })
1276
+
1277
+ expect(channel!.highestVoucherAmount).toBe(5000000n)
1278
+ expect(channel!.spent).toBe(2000000n)
1279
+ expect(channel!.units).toBe(3)
1280
+ })
1281
+ })
1282
+
1283
+ function nextSalt(): Hex {
1284
+ saltCounter++
1285
+ return `0x${saltCounter.toString(16).padStart(64, '0')}` as Hex
1286
+ }
1287
+
1288
+ function makeChallenge(opts: { id?: string; channelId: Hex }) {
1289
+ return {
1290
+ id: opts.id ?? 'challenge-1',
1291
+ realm: 'test.example.com',
1292
+ method: 'tempo' as const,
1293
+ intent: 'session' as const,
1294
+ request: {
1295
+ amount: '1000000',
1296
+ unitType: 'token',
1297
+ currency: currency as string,
1298
+ decimals: 6,
1299
+ recipient: recipient as string,
1300
+ suggestedDeposit: undefined as string | undefined,
1301
+ methodDetails: {
1302
+ escrowContract: escrowContract as string,
1303
+ channelId: undefined as string | undefined,
1304
+ minVoucherDelta: undefined as string | undefined,
1305
+ chainId: chain.id as number | undefined,
1306
+ feePayer: undefined as boolean | undefined,
1307
+ },
1308
+ },
1309
+ }
1310
+ }
1311
+
1312
+ function makeRequest() {
1313
+ return {
1314
+ amount: '1000000',
1315
+ unitType: 'token',
1316
+ currency: currency as string,
1317
+ decimals: 6,
1318
+ recipient: recipient as string,
1319
+ escrowContract: escrowContract as string,
1320
+ chainId: chain.id,
1321
+ }
1322
+ }
1323
+
1324
+ async function signTestVoucher(channelId: Hex, amount: bigint) {
1325
+ return signVoucher(
1326
+ client,
1327
+ payer,
1328
+ { channelId, cumulativeAmount: amount },
1329
+ escrowContract,
1330
+ chain.id,
1331
+ )
1332
+ }
1333
+
1334
+ async function createSignedOpenTransaction(
1335
+ deposit: bigint,
1336
+ opts?: { payee?: Address; authorizedSigner?: Address },
1337
+ ) {
1338
+ const salt = nextSalt()
1339
+ const { channelId, serializedTransaction } = await signOpenChannel({
1340
+ escrow: escrowContract,
1341
+ payer,
1342
+ payee: opts?.payee ?? recipient,
1343
+ token: currency,
1344
+ deposit,
1345
+ salt,
1346
+ ...(opts?.authorizedSigner !== undefined && { authorizedSigner: opts.authorizedSigner }),
1347
+ })
1348
+ return { channelId, serializedTransaction }
1349
+ }