mppx 0.6.30 → 0.7.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 (483) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/Challenge.d.ts.map +1 -1
  3. package/dist/Challenge.js +9 -7
  4. package/dist/Challenge.js.map +1 -1
  5. package/dist/Constants.d.ts +46 -0
  6. package/dist/Constants.d.ts.map +1 -0
  7. package/dist/Constants.js +46 -0
  8. package/dist/Constants.js.map +1 -0
  9. package/dist/Credential.d.ts.map +1 -1
  10. package/dist/Credential.js +5 -4
  11. package/dist/Credential.js.map +1 -1
  12. package/dist/Method.d.ts +32 -4
  13. package/dist/Method.d.ts.map +1 -1
  14. package/dist/Method.js +5 -2
  15. package/dist/Method.js.map +1 -1
  16. package/dist/Receipt.d.ts.map +1 -1
  17. package/dist/Receipt.js +3 -2
  18. package/dist/Receipt.js.map +1 -1
  19. package/dist/cli/cli.d.ts.map +1 -1
  20. package/dist/cli/cli.js +19 -11
  21. package/dist/cli/cli.js.map +1 -1
  22. package/dist/cli/plugins/tempo.d.ts.map +1 -1
  23. package/dist/cli/plugins/tempo.js +17 -6
  24. package/dist/cli/plugins/tempo.js.map +1 -1
  25. package/dist/cli/utils.d.ts +5 -0
  26. package/dist/cli/utils.d.ts.map +1 -1
  27. package/dist/cli/utils.js +10 -0
  28. package/dist/cli/utils.js.map +1 -1
  29. package/dist/client/Methods.d.ts +5 -2
  30. package/dist/client/Methods.d.ts.map +1 -1
  31. package/dist/client/Methods.js +5 -2
  32. package/dist/client/Methods.js.map +1 -1
  33. package/dist/client/Transport.d.ts.map +1 -1
  34. package/dist/client/Transport.js +4 -5
  35. package/dist/client/Transport.js.map +1 -1
  36. package/dist/client/index.d.ts +2 -1
  37. package/dist/client/index.d.ts.map +1 -1
  38. package/dist/client/index.js +2 -1
  39. package/dist/client/index.js.map +1 -1
  40. package/dist/client/internal/Fetch.d.ts.map +1 -1
  41. package/dist/client/internal/Fetch.js +14 -6
  42. package/dist/client/internal/Fetch.js.map +1 -1
  43. package/dist/evm/server/Methods.d.ts +1 -1
  44. package/dist/evm/server/Methods.d.ts.map +1 -1
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +1 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/internal/AcceptPayment.d.ts +3 -0
  50. package/dist/internal/AcceptPayment.d.ts.map +1 -1
  51. package/dist/internal/AcceptPayment.js +15 -11
  52. package/dist/internal/AcceptPayment.js.map +1 -1
  53. package/dist/mcp-sdk/client/McpClient.d.ts +12 -5
  54. package/dist/mcp-sdk/client/McpClient.d.ts.map +1 -1
  55. package/dist/mcp-sdk/client/McpClient.js +55 -42
  56. package/dist/mcp-sdk/client/McpClient.js.map +1 -1
  57. package/dist/server/Mppx.d.ts +11 -3
  58. package/dist/server/Mppx.d.ts.map +1 -1
  59. package/dist/server/Mppx.js +76 -27
  60. package/dist/server/Mppx.js.map +1 -1
  61. package/dist/server/Request.js +24 -10
  62. package/dist/server/Request.js.map +1 -1
  63. package/dist/server/Response.d.ts.map +1 -1
  64. package/dist/server/Response.js +2 -1
  65. package/dist/server/Response.js.map +1 -1
  66. package/dist/server/Transport.d.ts.map +1 -1
  67. package/dist/server/Transport.js +4 -3
  68. package/dist/server/Transport.js.map +1 -1
  69. package/dist/server/index.d.ts +1 -0
  70. package/dist/server/index.d.ts.map +1 -1
  71. package/dist/server/index.js +1 -0
  72. package/dist/server/index.js.map +1 -1
  73. package/dist/stripe/client/Charge.d.ts +1 -1
  74. package/dist/stripe/client/Charge.d.ts.map +1 -1
  75. package/dist/stripe/client/Charge.js +3 -1
  76. package/dist/stripe/client/Charge.js.map +1 -1
  77. package/dist/stripe/server/Charge.d.ts +1 -1
  78. package/dist/stripe/server/Charge.d.ts.map +1 -1
  79. package/dist/stripe/server/Charge.js +9 -2
  80. package/dist/stripe/server/Charge.js.map +1 -1
  81. package/dist/stripe/server/Methods.d.ts +1 -1
  82. package/dist/stripe/server/Methods.d.ts.map +1 -1
  83. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  84. package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
  85. package/dist/stripe/server/internal/html.gen.js +1 -1
  86. package/dist/stripe/server/internal/html.gen.js.map +1 -1
  87. package/dist/tempo/Methods.d.ts +18 -0
  88. package/dist/tempo/Methods.d.ts.map +1 -1
  89. package/dist/tempo/Methods.js +16 -1
  90. package/dist/tempo/Methods.js.map +1 -1
  91. package/dist/tempo/client/Charge.d.ts +6 -0
  92. package/dist/tempo/client/Charge.d.ts.map +1 -1
  93. package/dist/tempo/client/Charge.js +9 -2
  94. package/dist/tempo/client/Charge.js.map +1 -1
  95. package/dist/tempo/client/Methods.d.ts +36 -7
  96. package/dist/tempo/client/Methods.d.ts.map +1 -1
  97. package/dist/tempo/client/Methods.js +12 -5
  98. package/dist/tempo/client/Methods.js.map +1 -1
  99. package/dist/tempo/client/index.d.ts +7 -4
  100. package/dist/tempo/client/index.d.ts.map +1 -1
  101. package/dist/tempo/client/index.js +5 -3
  102. package/dist/tempo/client/index.js.map +1 -1
  103. package/dist/tempo/index.d.ts +1 -0
  104. package/dist/tempo/index.d.ts.map +1 -1
  105. package/dist/tempo/index.js +1 -0
  106. package/dist/tempo/index.js.map +1 -1
  107. package/dist/tempo/internal/fee-payer.d.ts +21 -1
  108. package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
  109. package/dist/tempo/internal/fee-payer.js +109 -4
  110. package/dist/tempo/internal/fee-payer.js.map +1 -1
  111. package/dist/tempo/{client → legacy/client}/ChannelOps.d.ts +19 -6
  112. package/dist/tempo/legacy/client/ChannelOps.d.ts.map +1 -0
  113. package/dist/tempo/{client → legacy/client}/ChannelOps.js +9 -3
  114. package/dist/tempo/legacy/client/ChannelOps.js.map +1 -0
  115. package/dist/tempo/{client → legacy/client}/Session.d.ts +23 -4
  116. package/dist/tempo/legacy/client/Session.d.ts.map +1 -0
  117. package/dist/tempo/{client → legacy/client}/Session.js +14 -7
  118. package/dist/tempo/legacy/client/Session.js.map +1 -0
  119. package/dist/tempo/{client → legacy/client}/SessionManager.d.ts +20 -5
  120. package/dist/tempo/legacy/client/SessionManager.d.ts.map +1 -0
  121. package/dist/tempo/{client → legacy/client}/SessionManager.js +20 -16
  122. package/dist/tempo/legacy/client/SessionManager.js.map +1 -0
  123. package/dist/tempo/legacy/client/index.d.ts +7 -0
  124. package/dist/tempo/legacy/client/index.d.ts.map +1 -0
  125. package/dist/tempo/legacy/client/index.js +5 -0
  126. package/dist/tempo/legacy/client/index.js.map +1 -0
  127. package/dist/tempo/legacy/index.d.ts +7 -0
  128. package/dist/tempo/legacy/index.d.ts.map +1 -0
  129. package/dist/tempo/legacy/index.js +7 -0
  130. package/dist/tempo/legacy/index.js.map +1 -0
  131. package/dist/tempo/{server → legacy/server}/Session.d.ts +28 -11
  132. package/dist/tempo/legacy/server/Session.d.ts.map +1 -0
  133. package/dist/tempo/{server → legacy/server}/Session.js +28 -23
  134. package/dist/tempo/legacy/server/Session.js.map +1 -0
  135. package/dist/tempo/legacy/server/index.d.ts +5 -0
  136. package/dist/tempo/legacy/server/index.d.ts.map +1 -0
  137. package/dist/tempo/legacy/server/index.js +5 -0
  138. package/dist/tempo/legacy/server/index.js.map +1 -0
  139. package/dist/tempo/{session → legacy/session}/Chain.d.ts +30 -23
  140. package/dist/tempo/legacy/session/Chain.d.ts.map +1 -0
  141. package/dist/tempo/{session → legacy/session}/Chain.js +12 -11
  142. package/dist/tempo/legacy/session/Chain.js.map +1 -0
  143. package/dist/tempo/{session → legacy/session}/Channel.d.ts +1 -0
  144. package/dist/tempo/legacy/session/Channel.d.ts.map +1 -0
  145. package/dist/tempo/legacy/session/Channel.js.map +1 -0
  146. package/dist/tempo/legacy/session/ChannelStore.d.ts +22 -0
  147. package/dist/tempo/legacy/session/ChannelStore.d.ts.map +1 -0
  148. package/dist/tempo/legacy/session/ChannelStore.js +6 -0
  149. package/dist/tempo/legacy/session/ChannelStore.js.map +1 -0
  150. package/dist/tempo/legacy/session/Types.d.ts +73 -0
  151. package/dist/tempo/legacy/session/Types.d.ts.map +1 -0
  152. package/dist/tempo/legacy/session/Types.js.map +1 -0
  153. package/dist/tempo/{session → legacy/session}/Voucher.d.ts +4 -4
  154. package/dist/tempo/legacy/session/Voucher.d.ts.map +1 -0
  155. package/dist/tempo/{session → legacy/session}/Voucher.js +1 -1
  156. package/dist/tempo/legacy/session/Voucher.js.map +1 -0
  157. package/dist/tempo/{session → legacy/session}/escrow.abi.d.ts +1 -0
  158. package/dist/tempo/{session → legacy/session}/escrow.abi.d.ts.map +1 -1
  159. package/dist/tempo/{session → legacy/session}/escrow.abi.js +1 -0
  160. package/dist/tempo/legacy/session/escrow.abi.js.map +1 -0
  161. package/dist/tempo/legacy/session/index.d.ts +9 -0
  162. package/dist/tempo/legacy/session/index.d.ts.map +1 -0
  163. package/dist/tempo/legacy/session/index.js +9 -0
  164. package/dist/tempo/legacy/session/index.js.map +1 -0
  165. package/dist/tempo/server/Charge.d.ts +1 -1
  166. package/dist/tempo/server/Charge.d.ts.map +1 -1
  167. package/dist/tempo/server/Charge.js +13 -16
  168. package/dist/tempo/server/Charge.js.map +1 -1
  169. package/dist/tempo/server/Methods.d.ts +63 -6
  170. package/dist/tempo/server/Methods.d.ts.map +1 -1
  171. package/dist/tempo/server/Methods.js +36 -8
  172. package/dist/tempo/server/Methods.js.map +1 -1
  173. package/dist/tempo/server/Subscription.d.ts +1 -1
  174. package/dist/tempo/server/Subscription.d.ts.map +1 -1
  175. package/dist/tempo/server/index.d.ts +6 -5
  176. package/dist/tempo/server/index.d.ts.map +1 -1
  177. package/dist/tempo/server/index.js +5 -5
  178. package/dist/tempo/server/index.js.map +1 -1
  179. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  180. package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
  181. package/dist/tempo/server/internal/html.gen.js +1 -1
  182. package/dist/tempo/server/internal/html.gen.js.map +1 -1
  183. package/dist/tempo/server/internal/request-body.d.ts +7 -2
  184. package/dist/tempo/server/internal/request-body.d.ts.map +1 -1
  185. package/dist/tempo/server/internal/request-body.js +20 -3
  186. package/dist/tempo/server/internal/request-body.js.map +1 -1
  187. package/dist/tempo/server/internal/transport.d.ts +8 -4
  188. package/dist/tempo/server/internal/transport.d.ts.map +1 -1
  189. package/dist/tempo/server/internal/transport.js +8 -7
  190. package/dist/tempo/server/internal/transport.js.map +1 -1
  191. package/dist/tempo/session/Snapshot.d.ts +32 -0
  192. package/dist/tempo/session/Snapshot.d.ts.map +1 -0
  193. package/dist/tempo/session/Snapshot.js +37 -0
  194. package/dist/tempo/session/Snapshot.js.map +1 -0
  195. package/dist/tempo/session/client/ChannelOps.d.ts +82 -0
  196. package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -0
  197. package/dist/tempo/session/client/ChannelOps.js +204 -0
  198. package/dist/tempo/session/client/ChannelOps.js.map +1 -0
  199. package/dist/tempo/session/client/CredentialState.d.ts +262 -0
  200. package/dist/tempo/session/client/CredentialState.d.ts.map +1 -0
  201. package/dist/tempo/session/client/CredentialState.js +417 -0
  202. package/dist/tempo/session/client/CredentialState.js.map +1 -0
  203. package/dist/tempo/session/client/ReceiptCoordinator.d.ts +26 -0
  204. package/dist/tempo/session/client/ReceiptCoordinator.d.ts.map +1 -0
  205. package/dist/tempo/session/client/ReceiptCoordinator.js +61 -0
  206. package/dist/tempo/session/client/ReceiptCoordinator.js.map +1 -0
  207. package/dist/tempo/session/client/Runtime.d.ts +464 -0
  208. package/dist/tempo/session/client/Runtime.d.ts.map +1 -0
  209. package/dist/tempo/session/client/Runtime.js +499 -0
  210. package/dist/tempo/session/client/Runtime.js.map +1 -0
  211. package/dist/tempo/session/client/Session.d.ts +132 -0
  212. package/dist/tempo/session/client/Session.d.ts.map +1 -0
  213. package/dist/tempo/session/client/Session.js +55 -0
  214. package/dist/tempo/session/client/Session.js.map +1 -0
  215. package/dist/tempo/session/client/SessionManager.d.ts +120 -0
  216. package/dist/tempo/session/client/SessionManager.d.ts.map +1 -0
  217. package/dist/tempo/session/client/SessionManager.js +627 -0
  218. package/dist/tempo/session/client/SessionManager.js.map +1 -0
  219. package/dist/tempo/session/client/Transports.d.ts +449 -0
  220. package/dist/tempo/session/client/Transports.d.ts.map +1 -0
  221. package/dist/tempo/session/client/Transports.js +721 -0
  222. package/dist/tempo/session/client/Transports.js.map +1 -0
  223. package/dist/tempo/session/client/index.d.ts +12 -0
  224. package/dist/tempo/session/client/index.d.ts.map +1 -0
  225. package/dist/tempo/session/client/index.js +5 -0
  226. package/dist/tempo/session/client/index.js.map +1 -0
  227. package/dist/tempo/session/index.d.ts +7 -8
  228. package/dist/tempo/session/index.d.ts.map +1 -1
  229. package/dist/tempo/session/index.js +7 -8
  230. package/dist/tempo/session/index.js.map +1 -1
  231. package/dist/tempo/session/precompile/Chain.d.ts +319 -0
  232. package/dist/tempo/session/precompile/Chain.d.ts.map +1 -0
  233. package/dist/tempo/session/precompile/Chain.js +492 -0
  234. package/dist/tempo/session/precompile/Chain.js.map +1 -0
  235. package/dist/tempo/session/precompile/Channel.d.ts +46 -0
  236. package/dist/tempo/session/precompile/Channel.d.ts.map +1 -0
  237. package/dist/tempo/session/precompile/Channel.js +56 -0
  238. package/dist/tempo/session/precompile/Channel.js.map +1 -0
  239. package/dist/tempo/session/precompile/Protocol.d.ts +308 -0
  240. package/dist/tempo/session/precompile/Protocol.d.ts.map +1 -0
  241. package/dist/tempo/session/precompile/Protocol.js +264 -0
  242. package/dist/tempo/session/precompile/Protocol.js.map +1 -0
  243. package/dist/tempo/session/precompile/Voucher.d.ts +40 -0
  244. package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -0
  245. package/dist/tempo/session/precompile/Voucher.js +126 -0
  246. package/dist/tempo/session/precompile/Voucher.js.map +1 -0
  247. package/dist/tempo/session/precompile/escrow.abi.d.ts +522 -0
  248. package/dist/tempo/session/precompile/escrow.abi.d.ts.map +1 -0
  249. package/dist/tempo/session/precompile/escrow.abi.js +224 -0
  250. package/dist/tempo/session/precompile/escrow.abi.js.map +1 -0
  251. package/dist/tempo/session/precompile/index.d.ts +24 -0
  252. package/dist/tempo/session/precompile/index.d.ts.map +1 -0
  253. package/dist/tempo/session/precompile/index.js +22 -0
  254. package/dist/tempo/session/precompile/index.js.map +1 -0
  255. package/dist/tempo/session/server/ChannelOps.d.ts +56 -0
  256. package/dist/tempo/session/server/ChannelOps.d.ts.map +1 -0
  257. package/dist/tempo/session/server/ChannelOps.js +91 -0
  258. package/dist/tempo/session/server/ChannelOps.js.map +1 -0
  259. package/dist/tempo/session/server/ChannelStore.d.ts +347 -0
  260. package/dist/tempo/session/server/ChannelStore.d.ts.map +1 -0
  261. package/dist/tempo/session/server/ChannelStore.js +404 -0
  262. package/dist/tempo/session/server/ChannelStore.js.map +1 -0
  263. package/dist/tempo/session/server/CredentialVerification.d.ts +85 -0
  264. package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -0
  265. package/dist/tempo/session/server/CredentialVerification.js +494 -0
  266. package/dist/tempo/session/server/CredentialVerification.js.map +1 -0
  267. package/dist/tempo/session/server/MeteredStream.d.ts +40 -0
  268. package/dist/tempo/session/server/MeteredStream.d.ts.map +1 -0
  269. package/dist/tempo/session/server/MeteredStream.js +42 -0
  270. package/dist/tempo/session/server/MeteredStream.js.map +1 -0
  271. package/dist/tempo/session/server/RequestState.d.ts +208 -0
  272. package/dist/tempo/session/server/RequestState.d.ts.map +1 -0
  273. package/dist/tempo/session/server/RequestState.js +252 -0
  274. package/dist/tempo/session/server/RequestState.js.map +1 -0
  275. package/dist/tempo/session/server/Session.d.ts +169 -0
  276. package/dist/tempo/session/server/Session.d.ts.map +1 -0
  277. package/dist/tempo/session/server/Session.js +351 -0
  278. package/dist/tempo/session/server/Session.js.map +1 -0
  279. package/dist/tempo/session/server/Settlement.d.ts +185 -0
  280. package/dist/tempo/session/server/Settlement.d.ts.map +1 -0
  281. package/dist/tempo/session/server/Settlement.js +250 -0
  282. package/dist/tempo/session/server/Settlement.js.map +1 -0
  283. package/dist/tempo/session/{Sse.d.ts → server/Sse.d.ts} +9 -56
  284. package/dist/tempo/session/server/Sse.d.ts.map +1 -0
  285. package/dist/tempo/session/server/Sse.js +184 -0
  286. package/dist/tempo/session/server/Sse.js.map +1 -0
  287. package/dist/tempo/session/server/Transports.d.ts +89 -0
  288. package/dist/tempo/session/server/Transports.d.ts.map +1 -0
  289. package/dist/tempo/session/server/Transports.js +149 -0
  290. package/dist/tempo/session/server/Transports.js.map +1 -0
  291. package/dist/tempo/session/server/Ws.d.ts +48 -0
  292. package/dist/tempo/session/server/Ws.d.ts.map +1 -0
  293. package/dist/tempo/session/server/Ws.js +244 -0
  294. package/dist/tempo/session/server/Ws.js.map +1 -0
  295. package/dist/tempo/session/server/index.d.ts +4 -0
  296. package/dist/tempo/session/server/index.d.ts.map +1 -0
  297. package/dist/tempo/session/server/index.js +2 -0
  298. package/dist/tempo/session/server/index.js.map +1 -0
  299. package/package.json +8 -3
  300. package/src/Challenge.ts +9 -7
  301. package/src/Constants.ts +58 -0
  302. package/src/Credential.ts +5 -4
  303. package/src/Method.ts +46 -5
  304. package/src/Receipt.ts +3 -2
  305. package/src/cli/cli.test.ts +23 -28
  306. package/src/cli/cli.ts +23 -10
  307. package/src/cli/mcp.test.ts +21 -7
  308. package/src/cli/plugins/tempo.ts +21 -8
  309. package/src/cli/utils.test.ts +25 -1
  310. package/src/cli/utils.ts +10 -0
  311. package/src/client/Methods.ts +5 -2
  312. package/src/client/Mppx.test-d.ts +10 -0
  313. package/src/client/Mppx.test.ts +75 -0
  314. package/src/client/Transport.ts +4 -5
  315. package/src/client/index.ts +11 -1
  316. package/src/client/internal/Fetch.test.ts +29 -4
  317. package/src/client/internal/Fetch.ts +17 -5
  318. package/src/env.d.ts +1 -1
  319. package/src/index.ts +1 -0
  320. package/src/internal/AcceptPayment.test.ts +61 -0
  321. package/src/internal/AcceptPayment.ts +21 -14
  322. package/src/mcp-sdk/client/McpClient.integration.test.ts +8 -7
  323. package/src/mcp-sdk/client/McpClient.test-d.ts +7 -0
  324. package/src/mcp-sdk/client/McpClient.ts +99 -67
  325. package/src/mcp-sdk/client/McpClient.unit.test.ts +131 -0
  326. package/src/middlewares/elysia.test.ts +8 -4
  327. package/src/middlewares/express.test.ts +8 -4
  328. package/src/middlewares/hono.test.ts +4 -4
  329. package/src/middlewares/nextjs.test.ts +8 -4
  330. package/src/proxy/Proxy.test.ts +8 -8
  331. package/src/server/Mppx.test-d.ts +54 -0
  332. package/src/server/Mppx.test.ts +274 -7
  333. package/src/server/Mppx.ts +487 -406
  334. package/src/server/Request.test.ts +81 -0
  335. package/src/server/Request.ts +23 -9
  336. package/src/server/Response.ts +2 -1
  337. package/src/server/Transport.ts +4 -3
  338. package/src/server/index.ts +1 -0
  339. package/src/stripe/client/Charge.test.ts +20 -5
  340. package/src/stripe/client/Charge.ts +6 -2
  341. package/src/stripe/server/Charge.test.ts +114 -1
  342. package/src/stripe/server/Charge.ts +13 -2
  343. package/src/stripe/server/internal/html/package.json +1 -1
  344. package/src/stripe/server/internal/html.gen.ts +1 -1
  345. package/src/tempo/AccessKeyAuthorization.test.ts +4 -94
  346. package/src/tempo/Methods.test.ts +45 -17
  347. package/src/tempo/Methods.ts +22 -0
  348. package/src/tempo/PublicExports.test-d.ts +105 -0
  349. package/src/tempo/client/Charge.test.ts +85 -0
  350. package/src/tempo/client/Charge.ts +19 -2
  351. package/src/tempo/client/Methods.ts +18 -6
  352. package/src/tempo/client/index.ts +15 -4
  353. package/src/tempo/index.ts +1 -0
  354. package/src/tempo/internal/fee-payer.test.ts +241 -17
  355. package/src/tempo/internal/fee-payer.ts +150 -4
  356. package/src/tempo/internal/fee-token.test.ts +14 -9
  357. package/src/tempo/legacy/AccessKeyAuthorization.test.ts +162 -0
  358. package/src/tempo/legacy/README.md +9 -0
  359. package/src/tempo/{client → legacy/client}/ChannelOps.test.ts +6 -7
  360. package/src/tempo/{client → legacy/client}/ChannelOps.ts +22 -9
  361. package/src/tempo/{client → legacy/client}/Session.test.ts +51 -9
  362. package/src/tempo/{client → legacy/client}/Session.ts +25 -11
  363. package/src/tempo/{client → legacy/client}/SessionManager.test.ts +81 -9
  364. package/src/tempo/{client → legacy/client}/SessionManager.ts +41 -20
  365. package/src/tempo/legacy/client/index.ts +6 -0
  366. package/src/tempo/legacy/index.ts +6 -0
  367. package/src/tempo/{server → legacy/server}/Session.test.ts +162 -63
  368. package/src/tempo/{server → legacy/server}/Session.ts +49 -37
  369. package/src/tempo/legacy/server/index.ts +4 -0
  370. package/src/tempo/{session → legacy/session}/Chain.test.ts +3 -4
  371. package/src/tempo/{session → legacy/session}/Chain.ts +94 -63
  372. package/src/tempo/{session → legacy/session}/Channel.ts +1 -0
  373. package/src/tempo/legacy/session/ChannelStore.test.ts +58 -0
  374. package/src/tempo/legacy/session/ChannelStore.ts +39 -0
  375. package/src/tempo/legacy/session/Types.ts +91 -0
  376. package/src/tempo/{session → legacy/session}/Voucher.ts +12 -8
  377. package/src/tempo/{session → legacy/session}/escrow.abi.ts +1 -0
  378. package/src/tempo/legacy/session/index.ts +8 -0
  379. package/src/tempo/server/AtomicStore.test-d.ts +16 -11
  380. package/src/tempo/server/Charge.test.ts +92 -14
  381. package/src/tempo/server/Charge.ts +18 -16
  382. package/src/tempo/server/Methods.ts +54 -8
  383. package/src/tempo/server/Sse.test.ts +2 -2
  384. package/src/tempo/server/index.ts +6 -5
  385. package/src/tempo/server/internal/html/package.json +1 -1
  386. package/src/tempo/server/internal/html.gen.ts +1 -1
  387. package/src/tempo/server/internal/request-body.test.ts +37 -4
  388. package/src/tempo/server/internal/request-body.ts +25 -6
  389. package/src/tempo/server/internal/transport.test.ts +4 -4
  390. package/src/tempo/server/internal/transport.ts +19 -10
  391. package/src/tempo/session/Snapshot.test.ts +41 -0
  392. package/src/tempo/session/Snapshot.ts +74 -0
  393. package/src/tempo/session/client/ChannelOps.test.ts +163 -0
  394. package/src/tempo/session/client/ChannelOps.ts +344 -0
  395. package/src/tempo/session/client/CredentialState.test.ts +645 -0
  396. package/src/tempo/session/client/CredentialState.ts +814 -0
  397. package/src/tempo/session/client/ReceiptCoordinator.ts +95 -0
  398. package/src/tempo/session/client/Runtime.test.ts +1092 -0
  399. package/src/tempo/session/client/Runtime.ts +986 -0
  400. package/src/tempo/session/client/Session.test.ts +734 -0
  401. package/src/tempo/session/client/Session.ts +97 -0
  402. package/src/tempo/session/client/SessionManager.test.ts +1308 -0
  403. package/src/tempo/session/client/SessionManager.ts +845 -0
  404. package/src/tempo/session/client/Transports.test.ts +837 -0
  405. package/src/tempo/session/client/Transports.ts +1292 -0
  406. package/src/tempo/session/client/index.ts +37 -0
  407. package/src/tempo/session/index.ts +7 -8
  408. package/src/tempo/session/precompile/Chain.integration.test.ts +321 -0
  409. package/src/tempo/session/precompile/Chain.test.ts +1258 -0
  410. package/src/tempo/session/precompile/Chain.ts +979 -0
  411. package/src/tempo/session/precompile/Channel.test.ts +138 -0
  412. package/src/tempo/session/precompile/Channel.ts +103 -0
  413. package/src/tempo/session/precompile/Protocol.test.ts +358 -0
  414. package/src/tempo/session/precompile/Protocol.ts +520 -0
  415. package/src/tempo/session/precompile/Voucher.test.ts +316 -0
  416. package/src/tempo/session/precompile/Voucher.ts +160 -0
  417. package/src/tempo/session/precompile/escrow.abi.ts +226 -0
  418. package/src/tempo/session/precompile/index.ts +33 -0
  419. package/src/tempo/session/server/ChannelOps.test.ts +129 -0
  420. package/src/tempo/session/server/ChannelOps.ts +157 -0
  421. package/src/tempo/session/{ChannelStore.test.ts → server/ChannelStore.test.ts} +536 -29
  422. package/src/tempo/session/server/ChannelStore.ts +835 -0
  423. package/src/tempo/session/server/CredentialVerification.test.ts +146 -0
  424. package/src/tempo/session/server/CredentialVerification.ts +710 -0
  425. package/src/tempo/session/server/MeteredStream.ts +88 -0
  426. package/src/tempo/session/server/RequestState.test.ts +531 -0
  427. package/src/tempo/session/server/RequestState.ts +499 -0
  428. package/src/tempo/session/server/Session.integration.test.ts +444 -0
  429. package/src/tempo/session/server/Session.test.ts +3253 -0
  430. package/src/tempo/session/server/Session.ts +543 -0
  431. package/src/tempo/session/server/Settlement.test.ts +242 -0
  432. package/src/tempo/session/server/Settlement.ts +470 -0
  433. package/src/tempo/session/{Sse.test.ts → server/Sse.test.ts} +37 -3
  434. package/src/tempo/session/server/Sse.ts +256 -0
  435. package/src/tempo/session/server/Transports.test.ts +346 -0
  436. package/src/tempo/session/server/Transports.ts +255 -0
  437. package/src/tempo/session/{Ws.test.ts → server/Ws.test.ts} +4 -4
  438. package/src/tempo/session/server/Ws.ts +384 -0
  439. package/src/tempo/session/server/index.ts +8 -0
  440. package/dist/tempo/client/ChannelOps.d.ts.map +0 -1
  441. package/dist/tempo/client/ChannelOps.js.map +0 -1
  442. package/dist/tempo/client/Session.d.ts.map +0 -1
  443. package/dist/tempo/client/Session.js.map +0 -1
  444. package/dist/tempo/client/SessionManager.d.ts.map +0 -1
  445. package/dist/tempo/client/SessionManager.js.map +0 -1
  446. package/dist/tempo/server/Session.d.ts.map +0 -1
  447. package/dist/tempo/server/Session.js.map +0 -1
  448. package/dist/tempo/session/Chain.d.ts.map +0 -1
  449. package/dist/tempo/session/Chain.js.map +0 -1
  450. package/dist/tempo/session/Channel.d.ts.map +0 -1
  451. package/dist/tempo/session/Channel.js.map +0 -1
  452. package/dist/tempo/session/ChannelStore.d.ts +0 -117
  453. package/dist/tempo/session/ChannelStore.d.ts.map +0 -1
  454. package/dist/tempo/session/ChannelStore.js +0 -172
  455. package/dist/tempo/session/ChannelStore.js.map +0 -1
  456. package/dist/tempo/session/Receipt.d.ts +0 -22
  457. package/dist/tempo/session/Receipt.d.ts.map +0 -1
  458. package/dist/tempo/session/Receipt.js +0 -34
  459. package/dist/tempo/session/Receipt.js.map +0 -1
  460. package/dist/tempo/session/Sse.d.ts.map +0 -1
  461. package/dist/tempo/session/Sse.js +0 -363
  462. package/dist/tempo/session/Sse.js.map +0 -1
  463. package/dist/tempo/session/Types.d.ts +0 -78
  464. package/dist/tempo/session/Types.d.ts.map +0 -1
  465. package/dist/tempo/session/Types.js.map +0 -1
  466. package/dist/tempo/session/Voucher.d.ts.map +0 -1
  467. package/dist/tempo/session/Voucher.js.map +0 -1
  468. package/dist/tempo/session/Ws.d.ts +0 -87
  469. package/dist/tempo/session/Ws.d.ts.map +0 -1
  470. package/dist/tempo/session/Ws.js +0 -443
  471. package/dist/tempo/session/Ws.js.map +0 -1
  472. package/dist/tempo/session/escrow.abi.js.map +0 -1
  473. package/src/tempo/session/ChannelStore.ts +0 -308
  474. package/src/tempo/session/Receipt.test.ts +0 -89
  475. package/src/tempo/session/Receipt.ts +0 -46
  476. package/src/tempo/session/Sse.ts +0 -462
  477. package/src/tempo/session/Types.ts +0 -86
  478. package/src/tempo/session/Ws.ts +0 -576
  479. /package/dist/tempo/{session → legacy/session}/Channel.js +0 -0
  480. /package/dist/tempo/{session → legacy/session}/Types.js +0 -0
  481. /package/src/tempo/{session → legacy/session}/Channel.test.ts +0 -0
  482. /package/src/tempo/{session → legacy/session}/Voucher.test.ts +0 -0
  483. /package/src/tempo/session/{Sse.fuzz.test.ts → server/Sse.fuzz.test.ts} +0 -0
@@ -0,0 +1,710 @@
1
+ import {
2
+ isAddress,
3
+ isAddressEqual,
4
+ zeroAddress,
5
+ type Account as viem_Account,
6
+ type Address,
7
+ type Hex,
8
+ } from 'viem'
9
+
10
+ import type * as Challenge from '../../../Challenge.js'
11
+ import {
12
+ AmountExceedsDepositError,
13
+ ChannelClosedError,
14
+ ChannelNotFoundError,
15
+ InsufficientBalanceError,
16
+ InvalidSignatureError,
17
+ VerificationFailedError,
18
+ } from '../../../Errors.js'
19
+ import type * as FeePayer from '../../internal/fee-payer.js'
20
+ import * as Chain from '../precompile/Chain.js'
21
+ import { readChannelClosedReceiptFields } from '../precompile/Chain.js'
22
+ import * as Channel from '../precompile/Channel.js'
23
+ import {
24
+ createSessionReceipt,
25
+ uint96,
26
+ type ChannelDescriptor,
27
+ type SessionCredentialPayload,
28
+ type SessionReceipt,
29
+ } from '../precompile/Protocol.js'
30
+ import * as Voucher from '../precompile/Voucher.js'
31
+ import * as ChannelStore from './ChannelStore.js'
32
+ import { getChallengePaymentFields } from './RequestState.js'
33
+ import { assertSettlementSender, getClientAccount } from './Settlement.js'
34
+
35
+ /** Returns the effective voucher signer for a TIP-1034 descriptor. */
36
+ export function authorizedSigner(descriptor: Channel.ChannelDescriptor): Address {
37
+ return isAddressEqual(descriptor.authorizedSigner, zeroAddress)
38
+ ? descriptor.payer
39
+ : descriptor.authorizedSigner
40
+ }
41
+
42
+ /** Asserts that a credential payload includes a TIP-1034 descriptor. */
43
+ export function assertDescriptor(payload: {
44
+ descriptor?: Channel.ChannelDescriptor | undefined
45
+ }): asserts payload is { descriptor: Channel.ChannelDescriptor } {
46
+ if (!payload.descriptor)
47
+ throw new VerificationFailedError({
48
+ reason: 'descriptor required for TIP-1034 session action',
49
+ })
50
+ }
51
+
52
+ /** Asserts that two TIP-1034 descriptors identify the same channel. */
53
+ export function assertSameDescriptor(a: Channel.ChannelDescriptor, b: Channel.ChannelDescriptor) {
54
+ if (
55
+ !isAddressEqual(a.payer, b.payer) ||
56
+ !isAddressEqual(a.payee, b.payee) ||
57
+ !isAddressEqual(a.operator, b.operator) ||
58
+ !isAddressEqual(a.token, b.token) ||
59
+ !isAddressEqual(a.authorizedSigner, b.authorizedSigner) ||
60
+ a.salt.toLowerCase() !== b.salt.toLowerCase() ||
61
+ a.expiringNonceHash.toLowerCase() !== b.expiringNonceHash.toLowerCase()
62
+ )
63
+ throw new VerificationFailedError({
64
+ reason: 'credential descriptor does not match stored channel',
65
+ })
66
+ }
67
+
68
+ /**
69
+ * Validates a TIP-1034 descriptor against channel ID, server destination, and token.
70
+ */
71
+ export function validateChannelDescriptor(
72
+ descriptor: Channel.ChannelDescriptor,
73
+ channelId: Address | `0x${string}`,
74
+ chainId: number,
75
+ escrow: Address,
76
+ recipient: Address,
77
+ currency: Address,
78
+ expectedOperator?: Address | undefined,
79
+ ): void {
80
+ const computed = Channel.computeId({ ...descriptor, chainId, escrow })
81
+ if (computed.toLowerCase() !== channelId.toLowerCase()) {
82
+ throw new VerificationFailedError({ reason: 'channel descriptor does not match channelId' })
83
+ }
84
+ if (!isAddressEqual(descriptor.payee, recipient)) {
85
+ throw new VerificationFailedError({
86
+ reason: 'channel descriptor payee does not match server destination',
87
+ })
88
+ }
89
+ if (!isAddressEqual(descriptor.token, currency)) {
90
+ throw new VerificationFailedError({
91
+ reason: 'channel descriptor token does not match server token',
92
+ })
93
+ }
94
+ if (expectedOperator !== undefined && !isAddressEqual(descriptor.operator, expectedOperator)) {
95
+ throw new VerificationFailedError({
96
+ reason: 'channel descriptor operator does not match server operator',
97
+ })
98
+ }
99
+ }
100
+
101
+ /** Validates on-chain channel state before accepting or charging a credential. */
102
+ export function validateChannelState(state: Chain.ChannelState, amount?: bigint): void {
103
+ if (state.deposit === 0n) {
104
+ throw new ChannelNotFoundError({ reason: 'channel not funded on-chain' })
105
+ }
106
+ if (state.closeRequestedAt !== 0) {
107
+ throw new ChannelClosedError({ reason: 'channel has a pending close request' })
108
+ }
109
+ if (amount !== undefined && state.deposit - state.settled < amount) {
110
+ throw new InsufficientBalanceError({
111
+ reason: 'channel available balance insufficient for requested amount',
112
+ })
113
+ }
114
+ }
115
+
116
+ const sessionCredentialActions = [
117
+ 'open',
118
+ 'topUp',
119
+ 'voucher',
120
+ 'close',
121
+ ] as const satisfies readonly SessionCredentialPayload['action'][]
122
+ const sessionCredentialActionSet = new Set<string>(sessionCredentialActions)
123
+
124
+ /** Shared action and channel fields required on every session credential payload. */
125
+ export type SessionCredentialPayloadHeader = {
126
+ /** Credential action discriminator. */
127
+ action: SessionCredentialPayload['action']
128
+ /** Channel ID the credential acts on. */
129
+ channelId: Hex
130
+ }
131
+
132
+ type SessionCredentialPayloadData = {
133
+ candidate: Record<string, unknown>
134
+ header: SessionCredentialPayloadHeader
135
+ }
136
+
137
+ function isObject(value: unknown): value is Record<string, unknown> {
138
+ return value !== null && typeof value === 'object'
139
+ }
140
+
141
+ function isSessionCredentialAction(value: unknown): value is SessionCredentialPayload['action'] {
142
+ return typeof value === 'string' && sessionCredentialActionSet.has(value)
143
+ }
144
+
145
+ function isHex(value: unknown): value is Hex {
146
+ return typeof value === 'string' && /^0x[0-9a-fA-F]*$/.test(value)
147
+ }
148
+
149
+ function isHash(value: unknown): value is Hex {
150
+ return typeof value === 'string' && /^0x[0-9a-fA-F]{64}$/.test(value)
151
+ }
152
+
153
+ function readAddress(value: unknown, field: string): Address {
154
+ if (typeof value === 'string' && isAddress(value, { strict: false })) return value
155
+ throw new VerificationFailedError({ reason: `invalid session credential ${field}` })
156
+ }
157
+
158
+ function readHash(value: unknown, field: string): Hex {
159
+ if (isHash(value)) return value
160
+ throw new VerificationFailedError({ reason: `invalid session credential ${field}` })
161
+ }
162
+
163
+ function readHex(value: unknown, field: string): Hex {
164
+ if (isHex(value)) return value
165
+ throw new VerificationFailedError({ reason: `invalid session credential ${field}` })
166
+ }
167
+
168
+ function readRawAmount(value: unknown, field: string): string {
169
+ if (typeof value === 'string' && /^[0-9]+$/.test(value)) return value
170
+ throw new VerificationFailedError({ reason: `invalid session credential ${field}` })
171
+ }
172
+
173
+ function readPayloadObject(payload: unknown): Record<string, unknown> {
174
+ if (!isObject(payload))
175
+ throw new VerificationFailedError({ reason: 'invalid session credential payload' })
176
+ return payload
177
+ }
178
+
179
+ function readDescriptor(value: unknown): ChannelDescriptor {
180
+ if (value === undefined)
181
+ throw new VerificationFailedError({
182
+ reason: 'descriptor required for TIP-1034 session action',
183
+ })
184
+ const candidate = readPayloadObject(value)
185
+ return {
186
+ payer: readAddress(candidate.payer, 'descriptor.payer'),
187
+ payee: readAddress(candidate.payee, 'descriptor.payee'),
188
+ operator: readAddress(candidate.operator, 'descriptor.operator'),
189
+ token: readAddress(candidate.token, 'descriptor.token'),
190
+ salt: readHash(candidate.salt, 'descriptor.salt'),
191
+ authorizedSigner: readAddress(candidate.authorizedSigner, 'descriptor.authorizedSigner'),
192
+ expiringNonceHash: readHash(candidate.expiringNonceHash, 'descriptor.expiringNonceHash'),
193
+ }
194
+ }
195
+
196
+ function readPayloadHeader(payload: unknown): SessionCredentialPayloadData {
197
+ const candidate = readPayloadObject(payload)
198
+ if (!isSessionCredentialAction(candidate.action)) {
199
+ throw new VerificationFailedError({ reason: 'invalid session credential action' })
200
+ }
201
+ return {
202
+ candidate,
203
+ header: {
204
+ action: candidate.action,
205
+ channelId: ChannelStore.normalizeChannelId(readHash(candidate.channelId, 'channelId')),
206
+ },
207
+ }
208
+ }
209
+
210
+ /** Validates the action discriminator for a TIP-1034 session credential payload. */
211
+ export function requireSessionCredentialAction(
212
+ payload: unknown,
213
+ ): SessionCredentialPayload['action'] {
214
+ const candidate = readPayloadObject(payload)
215
+ if (!isSessionCredentialAction(candidate.action)) {
216
+ throw new VerificationFailedError({ reason: 'invalid session credential action' })
217
+ }
218
+ return candidate.action
219
+ }
220
+
221
+ /** Validates the shared action and channel fields for a TIP-1034 session credential payload. */
222
+ export function requireSessionCredentialPayloadHeader(
223
+ payload: unknown,
224
+ ): SessionCredentialPayloadHeader {
225
+ return readPayloadHeader(payload).header
226
+ }
227
+
228
+ /** Validates action-specific fields for a TIP-1034 session credential payload. */
229
+ export function requireSessionCredentialPayload(payload: unknown): SessionCredentialPayload {
230
+ const { candidate, header } = readPayloadHeader(payload)
231
+ switch (header.action) {
232
+ case 'open':
233
+ return {
234
+ action: 'open',
235
+ type: readTransactionType(candidate.type),
236
+ channelId: header.channelId,
237
+ transaction: readHex(candidate.transaction, 'transaction'),
238
+ signature: readHex(candidate.signature, 'signature'),
239
+ descriptor: readDescriptor(candidate.descriptor),
240
+ cumulativeAmount: readRawAmount(candidate.cumulativeAmount, 'cumulativeAmount'),
241
+ ...(candidate.authorizedSigner === undefined
242
+ ? {}
243
+ : {
244
+ authorizedSigner: readAddress(candidate.authorizedSigner, 'authorizedSigner'),
245
+ }),
246
+ }
247
+ case 'topUp':
248
+ return {
249
+ action: 'topUp',
250
+ type: readTransactionType(candidate.type),
251
+ channelId: header.channelId,
252
+ transaction: readHex(candidate.transaction, 'transaction'),
253
+ descriptor: readDescriptor(candidate.descriptor),
254
+ additionalDeposit: readRawAmount(candidate.additionalDeposit, 'additionalDeposit'),
255
+ }
256
+ case 'voucher':
257
+ return {
258
+ action: 'voucher',
259
+ channelId: header.channelId,
260
+ descriptor: readDescriptor(candidate.descriptor),
261
+ cumulativeAmount: readRawAmount(candidate.cumulativeAmount, 'cumulativeAmount'),
262
+ signature: readHex(candidate.signature, 'signature'),
263
+ }
264
+ case 'close':
265
+ return {
266
+ action: 'close',
267
+ channelId: header.channelId,
268
+ descriptor: readDescriptor(candidate.descriptor),
269
+ cumulativeAmount: readRawAmount(candidate.cumulativeAmount, 'cumulativeAmount'),
270
+ signature: readHex(candidate.signature, 'signature'),
271
+ }
272
+ }
273
+ }
274
+
275
+ function readTransactionType(value: unknown): 'transaction' {
276
+ if (value === 'transaction') return value
277
+ throw new VerificationFailedError({ reason: 'invalid session credential transaction type' })
278
+ }
279
+
280
+ /** Shared inputs required to verify a single precompile session credential payload. */
281
+ export type VerifyCredentialPayloadParameters = {
282
+ /** Optional account override used for payee-side close settlement. */
283
+ account?: viem_Account | undefined
284
+ /** Challenge echoed by the credential. */
285
+ challenge: Challenge.Challenge
286
+ /** Milliseconds before voucher verification refreshes on-chain channel state. */
287
+ channelStateTtl: number
288
+ /** Chain ID used for channel ID derivation and voucher domain separation. */
289
+ chainId: number
290
+ /** viem client used for precompile reads and transaction broadcasts. */
291
+ client: Chain.TransactionClient
292
+ /** TIP20EscrowChannel precompile address for this session method. */
293
+ escrow: Address
294
+ /** Operator address advertised in the HMAC-bound challenge details. */
295
+ expectedOperator?: Address | undefined
296
+ /** Optional fee-payer account for fee-sponsored management transactions. */
297
+ feePayer?: viem_Account | undefined
298
+ /** Optional policy for fee-sponsored close/open/top-up transactions. */
299
+ feePayerPolicy?: Partial<FeePayer.Policy> | undefined
300
+ /** Optional fee token override for close transactions. */
301
+ feeToken?: Address | undefined
302
+ /** Last successful on-chain refresh timestamp per channel ID. */
303
+ lastOnChainVerified: Map<Hex, number>
304
+ /** Minimum allowed voucher delta in raw units. */
305
+ minVoucherDelta: bigint
306
+ /** Discriminated session credential payload to verify. */
307
+ payload: SessionCredentialPayload
308
+ /** Server-side channel store. */
309
+ store: ChannelStore.ChannelStore
310
+ }
311
+
312
+ /** Narrows shared credential verification inputs to one payload action. */
313
+ export type VerifyCredentialActionParameters<action extends SessionCredentialPayload['action']> =
314
+ Omit<VerifyCredentialPayloadParameters, 'payload'> & {
315
+ /** Credential payload for the selected action. */
316
+ payload: Extract<SessionCredentialPayload, { action: action }>
317
+ }
318
+
319
+ /** Inputs for verifying an open transaction credential and initial voucher. */
320
+ export type OpenCredentialActionParameters = VerifyCredentialActionParameters<'open'>
321
+
322
+ /** Inputs for verifying a top-up transaction credential. */
323
+ export type TopUpCredentialActionParameters = VerifyCredentialActionParameters<'topUp'>
324
+
325
+ /** Inputs for verifying and accepting an incremental voucher credential. */
326
+ export type VoucherCredentialActionParameters = VerifyCredentialActionParameters<'voucher'>
327
+
328
+ /** Inputs for verifying and settling a cooperative close credential. */
329
+ export type CloseCredentialActionParameters = VerifyCredentialActionParameters<'close'>
330
+
331
+ const refreshOnChainVerificationCache = {
332
+ close: false,
333
+ open: true,
334
+ topUp: true,
335
+ voucher: false,
336
+ } satisfies Record<SessionCredentialPayload['action'], boolean>
337
+
338
+ /** Verifies a session credential payload and applies the action-specific state transition. */
339
+ export async function verifyCredentialPayload(
340
+ context: VerifyCredentialPayloadParameters,
341
+ ): Promise<SessionReceipt> {
342
+ const receipt = await verifyCredentialAction(context)
343
+ if (refreshOnChainVerificationCache[context.payload.action])
344
+ context.lastOnChainVerified.set(receipt.channelId, Date.now())
345
+ return receipt
346
+ }
347
+
348
+ function verifyCredentialAction(
349
+ context: VerifyCredentialPayloadParameters,
350
+ ): Promise<SessionReceipt> {
351
+ const { payload } = context
352
+ switch (payload.action) {
353
+ case 'open':
354
+ return handleOpenCredential(actionContext(context, payload))
355
+ case 'topUp':
356
+ return handleTopUpCredential(actionContext(context, payload))
357
+ case 'voucher':
358
+ return handleVoucherCredential(actionContext(context, payload))
359
+ case 'close':
360
+ return handleCloseCredential(actionContext(context, payload))
361
+ }
362
+ }
363
+
364
+ function actionContext<action extends SessionCredentialPayload['action']>(
365
+ context: VerifyCredentialPayloadParameters,
366
+ payload: Extract<SessionCredentialPayload, { action: action }>,
367
+ ): VerifyCredentialActionParameters<action> {
368
+ return { ...context, payload }
369
+ }
370
+
371
+ async function handleOpenCredential(
372
+ parameters: OpenCredentialActionParameters,
373
+ ): Promise<SessionReceipt> {
374
+ const { store, client, challenge, payload, chainId, escrow } = parameters
375
+ const request = getChallengePaymentFields(challenge)
376
+ const expectedOperator = parameters.expectedOperator ?? zeroAddress
377
+ const cumulativeAmount = uint96(BigInt(payload.cumulativeAmount))
378
+ assertDescriptor(payload)
379
+ if (
380
+ payload.authorizedSigner !== undefined &&
381
+ !isAddressEqual(payload.authorizedSigner, payload.descriptor.authorizedSigner)
382
+ )
383
+ throw new VerificationFailedError({
384
+ reason: 'credential authorizedSigner does not match descriptor',
385
+ })
386
+ const channelId = ChannelStore.normalizeChannelId(payload.channelId)
387
+ validateChannelDescriptor(
388
+ payload.descriptor,
389
+ channelId,
390
+ chainId,
391
+ escrow,
392
+ request.recipient,
393
+ request.currency,
394
+ expectedOperator,
395
+ )
396
+
397
+ const result = await Chain.broadcastOpenTransaction({
398
+ challengeExpires: challenge.expires,
399
+ chainId,
400
+ client,
401
+ escrowContract: escrow,
402
+ expectedAuthorizedSigner: payload.descriptor.authorizedSigner,
403
+ expectedChannelId: channelId,
404
+ expectedCurrency: request.currency,
405
+ expectedOperator,
406
+ expectedPayee: request.recipient,
407
+ expectedExpiringNonceHash: payload.descriptor.expiringNonceHash,
408
+ expectedPayer: payload.descriptor.payer,
409
+ feePayer: parameters.feePayer,
410
+ feePayerPolicy: parameters.feePayerPolicy,
411
+ serializedTransaction: payload.transaction,
412
+ async beforeBroadcast(prepared) {
413
+ assertSameDescriptor(prepared.descriptor, payload.descriptor)
414
+ if (cumulativeAmount > prepared.openDeposit)
415
+ throw new AmountExceedsDepositError({ reason: 'voucher amount exceeds open deposit' })
416
+ const valid = await Voucher.verifyVoucher(
417
+ escrow,
418
+ chainId,
419
+ { channelId, cumulativeAmount: cumulativeAmount, signature: payload.signature },
420
+ authorizedSigner(prepared.descriptor),
421
+ )
422
+ if (!valid) throw new InvalidSignatureError({ reason: 'invalid voucher signature' })
423
+ },
424
+ })
425
+ const { descriptor, state } = result
426
+ assertSameDescriptor(descriptor, payload.descriptor)
427
+ validateChannelState(state, request.amount)
428
+
429
+ const updated = await store.updateChannel(channelId, (current) =>
430
+ ChannelStore.openChannelState({
431
+ authorizedSigner: authorizedSigner(descriptor),
432
+ chainId,
433
+ channelId,
434
+ current,
435
+ descriptor,
436
+ escrow,
437
+ expiringNonceHash: result.expiringNonceHash,
438
+ cumulativeAmount,
439
+ signature: payload.signature,
440
+ state,
441
+ }),
442
+ )
443
+ if (!updated) throw new VerificationFailedError({ reason: 'failed to create channel' })
444
+ return createSessionReceipt({
445
+ challengeId: challenge.id,
446
+ channelId,
447
+ acceptedCumulative: updated.highestVoucherAmount,
448
+ spent: updated.spent,
449
+ units: updated.units,
450
+ txHash: result.txHash,
451
+ })
452
+ }
453
+
454
+ async function handleTopUpCredential(
455
+ parameters: TopUpCredentialActionParameters,
456
+ ): Promise<SessionReceipt> {
457
+ const { store, client, challenge, payload, chainId, escrow } = parameters
458
+ const request = getChallengePaymentFields(challenge)
459
+ const expectedOperator = parameters.expectedOperator ?? zeroAddress
460
+ const additionalDeposit = uint96(BigInt(payload.additionalDeposit))
461
+ assertDescriptor(payload)
462
+ const channelId = ChannelStore.normalizeChannelId(payload.channelId)
463
+ validateChannelDescriptor(
464
+ payload.descriptor,
465
+ channelId,
466
+ chainId,
467
+ escrow,
468
+ request.recipient,
469
+ request.currency,
470
+ expectedOperator,
471
+ )
472
+ const channel = await ChannelStore.loadPrecompileChannel({
473
+ descriptor: payload.descriptor,
474
+ channelId,
475
+ chainId,
476
+ escrow,
477
+ store,
478
+ validateDescriptor: true,
479
+ })
480
+ const result = await Chain.broadcastTopUpTransaction({
481
+ additionalDeposit,
482
+ challengeExpires: challenge.expires,
483
+ chainId,
484
+ client,
485
+ descriptor: channel.descriptor,
486
+ escrowContract: escrow,
487
+ expectedChannelId: channelId,
488
+ expectedCurrency: request.currency,
489
+ feePayer: parameters.feePayer,
490
+ feePayerPolicy: parameters.feePayerPolicy,
491
+ serializedTransaction: payload.transaction,
492
+ })
493
+ const { state } = result
494
+ validateChannelState(state)
495
+
496
+ const updated = await store.updateChannel(channelId, (current) =>
497
+ ChannelStore.topUpChannelState({ current, state }),
498
+ )
499
+ return createSessionReceipt({
500
+ challengeId: challenge.id,
501
+ channelId,
502
+ acceptedCumulative: updated?.highestVoucherAmount ?? channel.highestVoucherAmount,
503
+ spent: updated?.spent ?? channel.spent,
504
+ units: updated?.units ?? channel.units,
505
+ txHash: result.txHash,
506
+ })
507
+ }
508
+
509
+ async function handleVoucherCredential(
510
+ parameters: VoucherCredentialActionParameters,
511
+ ): Promise<SessionReceipt> {
512
+ const {
513
+ store,
514
+ client,
515
+ challenge,
516
+ payload,
517
+ chainId,
518
+ escrow,
519
+ minVoucherDelta,
520
+ channelStateTtl,
521
+ lastOnChainVerified,
522
+ } = parameters
523
+ const request = getChallengePaymentFields(challenge)
524
+ const expectedOperator = parameters.expectedOperator ?? zeroAddress
525
+ const channelId = ChannelStore.normalizeChannelId(payload.channelId)
526
+ const voucher = Voucher.parseVoucherFromPayload(
527
+ channelId,
528
+ payload.cumulativeAmount,
529
+ payload.signature,
530
+ )
531
+ assertDescriptor(payload)
532
+ validateChannelDescriptor(
533
+ payload.descriptor,
534
+ channelId,
535
+ chainId,
536
+ escrow,
537
+ request.recipient,
538
+ request.currency,
539
+ expectedOperator,
540
+ )
541
+ const channel = await ChannelStore.loadPrecompileChannel({
542
+ descriptor: payload.descriptor,
543
+ channelId,
544
+ chainId,
545
+ escrow,
546
+ store,
547
+ validateDescriptor: true,
548
+ })
549
+ if (channel.finalized) throw new ChannelClosedError({ reason: 'channel is finalized' })
550
+ const isStale = Date.now() - (lastOnChainVerified.get(channelId) ?? 0) > channelStateTtl
551
+ const state = isStale ? await Chain.getChannelState(client, channelId, escrow) : undefined
552
+ if (state) lastOnChainVerified.set(channelId, Date.now())
553
+ const channelState = {
554
+ deposit: state?.deposit ?? uint96(channel.deposit),
555
+ settled: state?.settled ?? uint96(channel.settledOnChain),
556
+ closeRequestedAt: state?.closeRequestedAt ?? Number(channel.closeRequestedAt),
557
+ }
558
+ if (channelState.closeRequestedAt !== 0) {
559
+ await store.updateChannel(channelId, (current) =>
560
+ current
561
+ ? {
562
+ ...current,
563
+ closeRequestedAt:
564
+ BigInt(channelState.closeRequestedAt) > current.closeRequestedAt
565
+ ? BigInt(channelState.closeRequestedAt)
566
+ : current.closeRequestedAt,
567
+ }
568
+ : current,
569
+ )
570
+ }
571
+ return ChannelStore.verifyAndAcceptVoucher({
572
+ store,
573
+ minVoucherDelta,
574
+ challenge,
575
+ channel,
576
+ voucher,
577
+ channelState,
578
+ methodDetails: { chainId, escrowContract: escrow },
579
+ })
580
+ }
581
+
582
+ async function handleCloseCredential(
583
+ parameters: CloseCredentialActionParameters,
584
+ ): Promise<SessionReceipt> {
585
+ const { store, client, challenge, payload, chainId, escrow } = parameters
586
+ const request = getChallengePaymentFields(challenge)
587
+ const expectedOperator = parameters.expectedOperator ?? zeroAddress
588
+ const cumulativeAmount = uint96(BigInt(payload.cumulativeAmount))
589
+ const channelId = ChannelStore.normalizeChannelId(payload.channelId)
590
+ assertDescriptor(payload)
591
+ validateChannelDescriptor(
592
+ payload.descriptor,
593
+ channelId,
594
+ chainId,
595
+ escrow,
596
+ request.recipient,
597
+ request.currency,
598
+ expectedOperator,
599
+ )
600
+ const channel = await ChannelStore.loadPrecompileChannel({
601
+ descriptor: payload.descriptor,
602
+ channelId,
603
+ chainId,
604
+ escrow,
605
+ store,
606
+ })
607
+ if (channel.finalized) throw new ChannelClosedError({ reason: 'channel is already finalized' })
608
+ const state = await Chain.getChannelState(client, channelId, escrow)
609
+ if (state.closeRequestedAt !== 0)
610
+ throw new ChannelClosedError({ reason: 'channel has a pending close request' })
611
+ if (state.deposit === 0n && (cumulativeAmount !== 0n || channel.spent !== 0n))
612
+ throw new ChannelClosedError({ reason: 'channel deposit is zero (settled)' })
613
+ if (cumulativeAmount < channel.spent)
614
+ throw new VerificationFailedError({
615
+ reason: `close voucher amount must be >= ${channel.spent} (spent)`,
616
+ })
617
+ if (cumulativeAmount < state.settled)
618
+ throw new VerificationFailedError({
619
+ reason: `close voucher amount must be >= ${state.settled} (on-chain settled)`,
620
+ })
621
+ const valid = await Voucher.verifyVoucher(
622
+ escrow,
623
+ chainId,
624
+ { channelId, cumulativeAmount: cumulativeAmount, signature: payload.signature },
625
+ channel.authorizedSigner,
626
+ )
627
+ if (!valid) throw new InvalidSignatureError({ reason: 'invalid voucher signature' })
628
+ let captureAmount = uint96(channel.spent > state.settled ? channel.spent : state.settled)
629
+ if (captureAmount > state.deposit)
630
+ throw new AmountExceedsDepositError({ reason: 'close capture amount exceeds on-chain deposit' })
631
+ const pendingCloseStartedAt = BigInt(Math.floor(Date.now() / 1000) || 1)
632
+ const previousCloseRequestedAt = channel.closeRequestedAt
633
+ let pendingCloseMarked = false
634
+ await store.updateChannel(channelId, (current) => {
635
+ const next = ChannelStore.markPendingClose({
636
+ closeRequestedAt: pendingCloseStartedAt,
637
+ cumulativeAmount,
638
+ current,
639
+ onChainDeposit: state.deposit,
640
+ onChainSettled: state.settled,
641
+ })
642
+ if (next.state) {
643
+ captureAmount = next.captureAmount
644
+ pendingCloseMarked = true
645
+ }
646
+ return next.state
647
+ })
648
+ const account = parameters.account ?? getClientAccount(client)
649
+ let txHash: Hex | undefined
650
+ let receipt: Awaited<ReturnType<typeof Chain.waitForSuccessfulReceipt>>
651
+ try {
652
+ assertSettlementSender({
653
+ operation: 'close',
654
+ channelId,
655
+ operator: channel.operator,
656
+ payee: channel.payee,
657
+ sender: account?.address,
658
+ })
659
+ txHash = await Chain.closeOnChain(
660
+ client,
661
+ channel.descriptor,
662
+ cumulativeAmount,
663
+ captureAmount,
664
+ payload.signature,
665
+ escrow,
666
+ account
667
+ ? {
668
+ account,
669
+ ...(parameters.feePayer ? { feePayer: parameters.feePayer } : {}),
670
+ ...(parameters.feePayerPolicy ? { feePayerPolicy: parameters.feePayerPolicy } : {}),
671
+ ...(parameters.feeToken ? { feeToken: parameters.feeToken } : {}),
672
+ candidateFeeTokens: [channel.token],
673
+ }
674
+ : undefined,
675
+ )
676
+ receipt = await Chain.waitForSuccessfulReceipt(client, txHash)
677
+ } catch (error) {
678
+ if (pendingCloseMarked) {
679
+ await store.updateChannel(channelId, (current) =>
680
+ current && current.closeRequestedAt === pendingCloseStartedAt
681
+ ? { ...current, closeRequestedAt: previousCloseRequestedAt }
682
+ : current,
683
+ )
684
+ }
685
+ throw error
686
+ }
687
+ const closed = readChannelClosedReceiptFields(
688
+ Chain.getChannelEvent(receipt, 'ChannelClosed', channelId),
689
+ )
690
+ const { refundedToPayer, settledToPayee } = closed
691
+ if (settledToPayee > captureAmount || settledToPayee + refundedToPayer > state.deposit)
692
+ throw new VerificationFailedError({ reason: 'ChannelClosed amounts do not match state' })
693
+ const updated = await store.updateChannel(channelId, (current) =>
694
+ ChannelStore.finalizeClosedChannelState({
695
+ captureAmount,
696
+ channelId,
697
+ cumulativeAmount,
698
+ current,
699
+ signature: payload.signature,
700
+ }),
701
+ )
702
+ return createSessionReceipt({
703
+ challengeId: challenge.id,
704
+ channelId,
705
+ acceptedCumulative: cumulativeAmount,
706
+ spent: updated?.spent ?? channel.spent,
707
+ units: updated?.units ?? channel.units,
708
+ txHash,
709
+ })
710
+ }