@absol-labs/agent 0.11.0 → 0.12.0-rc.1

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 (350) hide show
  1. package/README.md +42 -50
  2. package/dist/capability/invocation-capability.d.ts +2 -2
  3. package/dist/capability/probe-capability.d.ts +225 -0
  4. package/dist/capability/probe-capability.d.ts.map +1 -0
  5. package/dist/capability/probe-capability.js +248 -0
  6. package/dist/capability/probe-capability.js.map +1 -0
  7. package/dist/cli/args.d.ts +11 -0
  8. package/dist/cli/args.d.ts.map +1 -1
  9. package/dist/cli/args.js +23 -0
  10. package/dist/cli/args.js.map +1 -1
  11. package/dist/cli/commands/claim.js +1 -1
  12. package/dist/cli/commands/claim.js.map +1 -1
  13. package/dist/cli/commands/close.d.ts.map +1 -1
  14. package/dist/cli/commands/close.js +35 -3
  15. package/dist/cli/commands/close.js.map +1 -1
  16. package/dist/cli/commands/invoke.d.ts +2 -0
  17. package/dist/cli/commands/invoke.d.ts.map +1 -1
  18. package/dist/cli/commands/invoke.js +2 -2
  19. package/dist/cli/commands/invoke.js.map +1 -1
  20. package/dist/cli/commands/mcp.d.ts.map +1 -1
  21. package/dist/cli/commands/mcp.js +15 -2
  22. package/dist/cli/commands/mcp.js.map +1 -1
  23. package/dist/cli/commands/open.d.ts.map +1 -1
  24. package/dist/cli/commands/open.js +58 -4
  25. package/dist/cli/commands/open.js.map +1 -1
  26. package/dist/cli/commands/pay.d.ts +19 -0
  27. package/dist/cli/commands/pay.d.ts.map +1 -0
  28. package/dist/cli/commands/pay.js +355 -0
  29. package/dist/cli/commands/pay.js.map +1 -0
  30. package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -1
  31. package/dist/cli/commands/reclaim-unverified.js +3 -2
  32. package/dist/cli/commands/reclaim-unverified.js.map +1 -1
  33. package/dist/cli/commands/reclaim.d.ts +23 -2
  34. package/dist/cli/commands/reclaim.d.ts.map +1 -1
  35. package/dist/cli/commands/reclaim.js +184 -16
  36. package/dist/cli/commands/reclaim.js.map +1 -1
  37. package/dist/cli/commands/refund.d.ts +13 -0
  38. package/dist/cli/commands/refund.d.ts.map +1 -0
  39. package/dist/cli/commands/refund.js +224 -0
  40. package/dist/cli/commands/refund.js.map +1 -0
  41. package/dist/cli/commands/status.d.ts.map +1 -1
  42. package/dist/cli/commands/status.js +10 -1
  43. package/dist/cli/commands/status.js.map +1 -1
  44. package/dist/cli/commands/streams.js +8 -12
  45. package/dist/cli/commands/streams.js.map +1 -1
  46. package/dist/cli/commands/watch.d.ts.map +1 -1
  47. package/dist/cli/commands/watch.js +2 -1
  48. package/dist/cli/commands/watch.js.map +1 -1
  49. package/dist/cli/config.d.ts +44 -26
  50. package/dist/cli/config.d.ts.map +1 -1
  51. package/dist/cli/config.js +88 -38
  52. package/dist/cli/config.js.map +1 -1
  53. package/dist/cli/deps.d.ts +33 -0
  54. package/dist/cli/deps.d.ts.map +1 -1
  55. package/dist/cli/deps.js +37 -1
  56. package/dist/cli/deps.js.map +1 -1
  57. package/dist/cli/errors.d.ts.map +1 -1
  58. package/dist/cli/errors.js +21 -1
  59. package/dist/cli/errors.js.map +1 -1
  60. package/dist/cli/escape-hatch.d.ts +6 -4
  61. package/dist/cli/escape-hatch.d.ts.map +1 -1
  62. package/dist/cli/escape-hatch.js +6 -2
  63. package/dist/cli/escape-hatch.js.map +1 -1
  64. package/dist/cli/listings.d.ts +18 -0
  65. package/dist/cli/listings.d.ts.map +1 -1
  66. package/dist/cli/listings.js +22 -12
  67. package/dist/cli/listings.js.map +1 -1
  68. package/dist/cli/preflight.d.ts +20 -14
  69. package/dist/cli/preflight.d.ts.map +1 -1
  70. package/dist/cli/preflight.js +34 -20
  71. package/dist/cli/preflight.js.map +1 -1
  72. package/dist/cli/privy.d.ts.map +1 -1
  73. package/dist/cli/privy.js +4 -2
  74. package/dist/cli/privy.js.map +1 -1
  75. package/dist/cli/reclaimability.d.ts +82 -0
  76. package/dist/cli/reclaimability.d.ts.map +1 -0
  77. package/dist/cli/reclaimability.js +80 -0
  78. package/dist/cli/reclaimability.js.map +1 -0
  79. package/dist/cli/run.d.ts +2 -1
  80. package/dist/cli/run.d.ts.map +1 -1
  81. package/dist/cli/run.js +40 -11
  82. package/dist/cli/run.js.map +1 -1
  83. package/dist/cli/scan-range.d.ts +2 -2
  84. package/dist/cli/scan-range.js +2 -2
  85. package/dist/cli/status.d.ts +3 -1
  86. package/dist/cli/status.d.ts.map +1 -1
  87. package/dist/cli/status.js +15 -3
  88. package/dist/cli/status.js.map +1 -1
  89. package/dist/cli/wallet.d.ts +1 -1
  90. package/dist/cli/wallet.js +2 -2
  91. package/dist/cli/wallet.js.map +1 -1
  92. package/dist/discovery/rate-band.d.ts +74 -0
  93. package/dist/discovery/rate-band.d.ts.map +1 -0
  94. package/dist/discovery/rate-band.js +114 -0
  95. package/dist/discovery/rate-band.js.map +1 -0
  96. package/dist/escrow-generation.d.ts +81 -0
  97. package/dist/escrow-generation.d.ts.map +1 -0
  98. package/dist/escrow-generation.js +76 -0
  99. package/dist/escrow-generation.js.map +1 -0
  100. package/dist/frameworks/agentkit.d.ts +8 -4
  101. package/dist/frameworks/agentkit.d.ts.map +1 -1
  102. package/dist/frameworks/agentkit.js +27 -10
  103. package/dist/frameworks/agentkit.js.map +1 -1
  104. package/dist/frameworks/eliza.d.ts +3 -3
  105. package/dist/frameworks/eliza.d.ts.map +1 -1
  106. package/dist/frameworks/eliza.js +15 -6
  107. package/dist/frameworks/eliza.js.map +1 -1
  108. package/dist/frameworks/langchain.d.ts.map +1 -1
  109. package/dist/frameworks/langchain.js +8 -0
  110. package/dist/frameworks/langchain.js.map +1 -1
  111. package/dist/gateway/caller-auth-gateway.d.ts +83 -31
  112. package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
  113. package/dist/gateway/caller-auth-gateway.js +180 -27
  114. package/dist/gateway/caller-auth-gateway.js.map +1 -1
  115. package/dist/gateway/http-server.d.ts +111 -44
  116. package/dist/gateway/http-server.d.ts.map +1 -1
  117. package/dist/gateway/http-server.js +439 -155
  118. package/dist/gateway/http-server.js.map +1 -1
  119. package/dist/gateway/probe-auth.d.ts +112 -0
  120. package/dist/gateway/probe-auth.d.ts.map +1 -0
  121. package/dist/gateway/probe-auth.js +127 -0
  122. package/dist/gateway/probe-auth.js.map +1 -0
  123. package/dist/gateway/signed-rate-bounds.d.ts +51 -0
  124. package/dist/gateway/signed-rate-bounds.d.ts.map +1 -0
  125. package/dist/gateway/signed-rate-bounds.js +94 -0
  126. package/dist/gateway/signed-rate-bounds.js.map +1 -0
  127. package/dist/gateway/x402-opener-mode.d.ts +215 -0
  128. package/dist/gateway/x402-opener-mode.d.ts.map +1 -0
  129. package/dist/gateway/x402-opener-mode.js +639 -0
  130. package/dist/gateway/x402-opener-mode.js.map +1 -0
  131. package/dist/index.d.ts +6 -2
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.js +10 -5
  134. package/dist/index.js.map +1 -1
  135. package/dist/mcp/server.d.ts +52 -1
  136. package/dist/mcp/server.d.ts.map +1 -1
  137. package/dist/mcp/server.js +228 -37
  138. package/dist/mcp/server.js.map +1 -1
  139. package/dist/mcp/x402-tools.d.ts +104 -0
  140. package/dist/mcp/x402-tools.d.ts.map +1 -0
  141. package/dist/mcp/x402-tools.js +434 -0
  142. package/dist/mcp/x402-tools.js.map +1 -0
  143. package/dist/network.d.ts +119 -0
  144. package/dist/network.d.ts.map +1 -0
  145. package/dist/network.js +199 -0
  146. package/dist/network.js.map +1 -0
  147. package/dist/sdk/invoke.d.ts +4 -0
  148. package/dist/sdk/invoke.d.ts.map +1 -1
  149. package/dist/sdk/invoke.js +2 -5
  150. package/dist/sdk/invoke.js.map +1 -1
  151. package/dist/wallet/autonomous-wallet-broker.d.ts +3 -0
  152. package/dist/wallet/autonomous-wallet-broker.d.ts.map +1 -1
  153. package/dist/wallet/autonomous-wallet-broker.js +27 -23
  154. package/dist/wallet/autonomous-wallet-broker.js.map +1 -1
  155. package/dist/wallet/autonomous-wallet-protocol.d.ts +8 -2
  156. package/dist/wallet/autonomous-wallet-protocol.d.ts.map +1 -1
  157. package/dist/wallet/autonomous-wallet-protocol.js +9 -2
  158. package/dist/wallet/autonomous-wallet-protocol.js.map +1 -1
  159. package/dist/wallet/autonomous-wallet.d.ts.map +1 -1
  160. package/dist/wallet/autonomous-wallet.js +3 -2
  161. package/dist/wallet/autonomous-wallet.js.map +1 -1
  162. package/dist/wallet/broker-escrows.d.ts +222 -0
  163. package/dist/wallet/broker-escrows.d.ts.map +1 -0
  164. package/dist/wallet/broker-escrows.js +36 -0
  165. package/dist/wallet/broker-escrows.js.map +1 -0
  166. package/dist/wallet/lifecycle.d.ts +21 -5
  167. package/dist/wallet/lifecycle.d.ts.map +1 -1
  168. package/dist/wallet/lifecycle.js +32 -12
  169. package/dist/wallet/lifecycle.js.map +1 -1
  170. package/dist/wallet/privy-broker-server.d.ts.map +1 -1
  171. package/dist/wallet/privy-broker-server.js +31 -26
  172. package/dist/wallet/privy-broker-server.js.map +1 -1
  173. package/dist/wallet/privy-session-broker.d.ts +14 -5
  174. package/dist/wallet/privy-session-broker.d.ts.map +1 -1
  175. package/dist/wallet/privy-session-broker.js +29 -17
  176. package/dist/wallet/privy-session-broker.js.map +1 -1
  177. package/dist/wallet/privy-session-provider.d.ts +3 -1
  178. package/dist/wallet/privy-session-provider.d.ts.map +1 -1
  179. package/dist/wallet/privy-session-provider.js +5 -3
  180. package/dist/wallet/privy-session-provider.js.map +1 -1
  181. package/dist/wallet/provider.d.ts.map +1 -1
  182. package/dist/wallet/provider.js +15 -8
  183. package/dist/wallet/provider.js.map +1 -1
  184. package/dist/x402/facilitator.d.ts +3 -30
  185. package/dist/x402/facilitator.d.ts.map +1 -1
  186. package/dist/x402/facilitator.js +0 -27
  187. package/dist/x402/facilitator.js.map +1 -1
  188. package/dist/x402-buyer/guarded-fetch.d.ts +51 -0
  189. package/dist/x402-buyer/guarded-fetch.d.ts.map +1 -0
  190. package/dist/x402-buyer/guarded-fetch.js +147 -0
  191. package/dist/x402-buyer/guarded-fetch.js.map +1 -0
  192. package/dist/x402-buyer/index.d.ts +10 -0
  193. package/dist/x402-buyer/index.d.ts.map +1 -0
  194. package/dist/x402-buyer/index.js +10 -0
  195. package/dist/x402-buyer/index.js.map +1 -0
  196. package/dist/x402-buyer/pay.d.ts +135 -0
  197. package/dist/x402-buyer/pay.d.ts.map +1 -0
  198. package/dist/x402-buyer/pay.js +336 -0
  199. package/dist/x402-buyer/pay.js.map +1 -0
  200. package/dist/x402-opener/abi.d.ts +684 -0
  201. package/dist/x402-opener/abi.d.ts.map +1 -0
  202. package/dist/x402-opener/abi.js +880 -0
  203. package/dist/x402-opener/abi.js.map +1 -0
  204. package/dist/x402-opener/facilitator-entry.d.ts +3 -0
  205. package/dist/x402-opener/facilitator-entry.d.ts.map +1 -0
  206. package/dist/x402-opener/facilitator-entry.js +37 -0
  207. package/dist/x402-opener/facilitator-entry.js.map +1 -0
  208. package/dist/x402-opener/facilitator-server.d.ts +180 -0
  209. package/dist/x402-opener/facilitator-server.d.ts.map +1 -0
  210. package/dist/x402-opener/facilitator-server.js +696 -0
  211. package/dist/x402-opener/facilitator-server.js.map +1 -0
  212. package/dist/x402-opener/index.d.ts +14 -0
  213. package/dist/x402-opener/index.d.ts.map +1 -0
  214. package/dist/x402-opener/index.js +14 -0
  215. package/dist/x402-opener/index.js.map +1 -0
  216. package/dist/x402-opener/listing-resolver.d.ts +55 -0
  217. package/dist/x402-opener/listing-resolver.d.ts.map +1 -0
  218. package/dist/x402-opener/listing-resolver.js +125 -0
  219. package/dist/x402-opener/listing-resolver.js.map +1 -0
  220. package/dist/x402-opener/refund-relay.d.ts +240 -0
  221. package/dist/x402-opener/refund-relay.d.ts.map +1 -0
  222. package/dist/x402-opener/refund-relay.js +1240 -0
  223. package/dist/x402-opener/refund-relay.js.map +1 -0
  224. package/dist/x402-opener/relayer-queue.d.ts +14 -0
  225. package/dist/x402-opener/relayer-queue.d.ts.map +1 -0
  226. package/dist/x402-opener/relayer-queue.js +18 -0
  227. package/dist/x402-opener/relayer-queue.js.map +1 -0
  228. package/dist/x402-opener/scheme.d.ts +217 -0
  229. package/dist/x402-opener/scheme.d.ts.map +1 -0
  230. package/dist/x402-opener/scheme.js +749 -0
  231. package/dist/x402-opener/scheme.js.map +1 -0
  232. package/dist/x402-opener/terms.d.ts +105 -0
  233. package/dist/x402-opener/terms.d.ts.map +1 -0
  234. package/dist/x402-opener/terms.js +212 -0
  235. package/dist/x402-opener/terms.js.map +1 -0
  236. package/dist/zktls/reclaim.d.ts +2 -2
  237. package/dist/zktls/t2-delivery-proof.d.ts +6 -6
  238. package/docs/agent-layer.md +3 -2
  239. package/docs/autonomous-privy-wallet.md +188 -0
  240. package/docs/cli.md +184 -31
  241. package/docs/langchain.md +5 -0
  242. package/docs/mcp-reference.md +75 -5
  243. package/docs/probe-capability.md +191 -0
  244. package/docs/quickstart.md +36 -1
  245. package/docs/threat-model.md +20 -0
  246. package/docs/x402.md +628 -595
  247. package/package.json +19 -17
  248. package/src/capability/probe-capability.ts +355 -0
  249. package/src/cli/args.ts +29 -0
  250. package/src/cli/commands/claim.ts +1 -1
  251. package/src/cli/commands/close.ts +36 -3
  252. package/src/cli/commands/invoke.ts +5 -2
  253. package/src/cli/commands/mcp.ts +18 -2
  254. package/src/cli/commands/open.ts +75 -4
  255. package/src/cli/commands/pay.ts +442 -0
  256. package/src/cli/commands/reclaim-unverified.ts +8 -2
  257. package/src/cli/commands/reclaim.ts +252 -14
  258. package/src/cli/commands/refund.ts +277 -0
  259. package/src/cli/commands/status.ts +17 -1
  260. package/src/cli/commands/streams.ts +10 -14
  261. package/src/cli/commands/watch.ts +7 -1
  262. package/src/cli/config.ts +139 -52
  263. package/src/cli/deps.ts +74 -1
  264. package/src/cli/errors.ts +24 -1
  265. package/src/cli/escape-hatch.ts +19 -5
  266. package/src/cli/listings.ts +43 -10
  267. package/src/cli/preflight.ts +36 -21
  268. package/src/cli/privy.ts +4 -2
  269. package/src/cli/reclaimability.ts +154 -0
  270. package/src/cli/run.ts +54 -11
  271. package/src/cli/scan-range.ts +2 -2
  272. package/src/cli/status.ts +14 -1
  273. package/src/cli/wallet.ts +2 -2
  274. package/src/discovery/rate-band.ts +172 -0
  275. package/src/escrow-generation.ts +135 -0
  276. package/src/frameworks/agentkit.ts +45 -11
  277. package/src/frameworks/eliza.ts +27 -5
  278. package/src/frameworks/langchain.ts +14 -0
  279. package/src/gateway/caller-auth-gateway.ts +320 -68
  280. package/src/gateway/http-server.ts +629 -220
  281. package/src/gateway/probe-auth.ts +191 -0
  282. package/src/gateway/signed-rate-bounds.ts +164 -0
  283. package/src/gateway/x402-opener-mode.ts +974 -0
  284. package/src/index.ts +82 -19
  285. package/src/mcp/server.ts +319 -42
  286. package/src/mcp/x402-tools.ts +621 -0
  287. package/src/network.ts +302 -0
  288. package/src/sdk/invoke.ts +6 -5
  289. package/src/wallet/autonomous-wallet-broker.ts +46 -20
  290. package/src/wallet/autonomous-wallet-protocol.ts +11 -2
  291. package/src/wallet/autonomous-wallet.ts +4 -2
  292. package/src/wallet/broker-escrows.ts +66 -0
  293. package/src/wallet/lifecycle.ts +53 -18
  294. package/src/wallet/privy-broker-server.ts +35 -25
  295. package/src/wallet/privy-session-broker.ts +58 -25
  296. package/src/wallet/privy-session-provider.ts +7 -3
  297. package/src/wallet/provider.ts +20 -15
  298. package/src/x402/facilitator.ts +3 -43
  299. package/src/x402-buyer/guarded-fetch.ts +213 -0
  300. package/src/x402-buyer/index.ts +9 -0
  301. package/src/x402-buyer/pay.ts +517 -0
  302. package/src/x402-opener/abi.ts +881 -0
  303. package/src/x402-opener/facilitator-entry.ts +45 -0
  304. package/src/x402-opener/facilitator-server.ts +1014 -0
  305. package/src/x402-opener/index.ts +13 -0
  306. package/src/x402-opener/listing-resolver.ts +202 -0
  307. package/src/x402-opener/refund-relay.ts +1743 -0
  308. package/src/x402-opener/relayer-queue.ts +18 -0
  309. package/src/x402-opener/scheme.ts +1133 -0
  310. package/src/x402-opener/terms.ts +300 -0
  311. package/dist/gateway/x402-mode.d.ts +0 -123
  312. package/dist/gateway/x402-mode.d.ts.map +0 -1
  313. package/dist/gateway/x402-mode.js +0 -212
  314. package/dist/gateway/x402-mode.js.map +0 -1
  315. package/dist/x402/delivery-oracle.d.ts +0 -108
  316. package/dist/x402/delivery-oracle.d.ts.map +0 -1
  317. package/dist/x402/delivery-oracle.js +0 -174
  318. package/dist/x402/delivery-oracle.js.map +0 -1
  319. package/dist/x402/facilitator-server-entry.d.ts +0 -3
  320. package/dist/x402/facilitator-server-entry.d.ts.map +0 -1
  321. package/dist/x402/facilitator-server-entry.js +0 -27
  322. package/dist/x402/facilitator-server-entry.js.map +0 -1
  323. package/dist/x402/facilitator-server.d.ts +0 -73
  324. package/dist/x402/facilitator-server.d.ts.map +0 -1
  325. package/dist/x402/facilitator-server.js +0 -305
  326. package/dist/x402/facilitator-server.js.map +0 -1
  327. package/dist/x402/payment-requirements.d.ts +0 -106
  328. package/dist/x402/payment-requirements.d.ts.map +0 -1
  329. package/dist/x402/payment-requirements.js +0 -138
  330. package/dist/x402/payment-requirements.js.map +0 -1
  331. package/dist/x402/relayer.d.ts +0 -41
  332. package/dist/x402/relayer.d.ts.map +0 -1
  333. package/dist/x402/relayer.js +0 -64
  334. package/dist/x402/relayer.js.map +0 -1
  335. package/dist/x402/resource-index.d.ts +0 -75
  336. package/dist/x402/resource-index.d.ts.map +0 -1
  337. package/dist/x402/resource-index.js +0 -126
  338. package/dist/x402/resource-index.js.map +0 -1
  339. package/dist/x402/verified-delivery-facilitator.d.ts +0 -285
  340. package/dist/x402/verified-delivery-facilitator.d.ts.map +0 -1
  341. package/dist/x402/verified-delivery-facilitator.js +0 -423
  342. package/dist/x402/verified-delivery-facilitator.js.map +0 -1
  343. package/src/gateway/x402-mode.ts +0 -350
  344. package/src/x402/delivery-oracle.ts +0 -285
  345. package/src/x402/facilitator-server-entry.ts +0 -35
  346. package/src/x402/facilitator-server.ts +0 -445
  347. package/src/x402/payment-requirements.ts +0 -231
  348. package/src/x402/relayer.ts +0 -122
  349. package/src/x402/resource-index.ts +0 -228
  350. package/src/x402/verified-delivery-facilitator.ts +0 -744
@@ -0,0 +1,749 @@
1
+ import { streamEscrowV2Abi } from "@absol-labs/shared";
2
+ import { deepEqual } from "@x402/core/utils";
3
+ import { isEIP3009Payload, toFacilitatorEvmSigner, } from "@x402/evm";
4
+ import { ExactEvmScheme } from "@x402/evm/exact/facilitator";
5
+ import { BaseError, ContractFunctionRevertedError, decodeEventLog, getAddress, isAddress, isErc6492Signature, isHex, } from "viem";
6
+ import { x402StreamOpenerAbi, x402StreamOpenerFactoryAbi } from "./abi.js";
7
+ import { RelayerQueue } from "./relayer-queue.js";
8
+ import { chainIdFromNetwork, maxEarnable, OPENER_EXTRA_KEY, OPENER_SCHEME, parseUintString, termsFromExtra, termsTuple, } from "./terms.js";
9
+ /**
10
+ * `MetrikOpenerExactScheme` — an x402 v2 `exact` facilitator scheme that
11
+ * settles every payment INTO a `StreamEscrowV2` stream through the ownerless
12
+ * `X402StreamOpenerFactory` (metrik-agent#116, metrik-contracts#48).
13
+ *
14
+ * ## What it composes, and what it refuses
15
+ *
16
+ * Standard `exact` checks (EIP-712 signature, recipient == `payTo`, value ==
17
+ * amount, validity window, asset is a contract, balance / nonce simulation of
18
+ * the transfer) are delegated to the official `@x402/evm` `ExactEvmScheme`
19
+ * verifier, never reimplemented. On top of it, every payment must satisfy:
20
+ *
21
+ * - `assetTransferMethod` is EIP-3009. Permit2 and every other method are
22
+ * refused: the opener only ever pulls an EIP-3009 authorization.
23
+ * - The signature is NOT ERC-6492-wrapped. A counterfactual smart wallet would
24
+ * need a factory deploy the opener cannot perform; refused with a reason.
25
+ * - `asset` is on the allowlist AND is the factory's own settlement token.
26
+ * - `payTo == factory.predict(terms)`, recomputed on-chain from the configured
27
+ * factory for the terms in `extra.metrikStreamOpener` — never compared by
28
+ * code or codehash (a byte-identical clone outside the factory proves
29
+ * nothing). The factory named in `extra` must be the configured one.
30
+ * - `(operator, serviceRef)` resolve to exactly ONE operator-signed listing via
31
+ * the existing discovery signature verification; the rate is inside the
32
+ * listing's signed commercial bounds when it declares them.
33
+ * - `0 < amount <= ratePerSecond * maxDuration`.
34
+ * - `factory.deployAndSettle` simulates cleanly from the relayer.
35
+ *
36
+ * Any doubt — an RPC error, an unreachable registry, a malformed field — is an
37
+ * invalid verdict. Nothing here ever fails open.
38
+ *
39
+ * ## Settlement
40
+ *
41
+ * `settle` re-runs the full verification, then calls `deployAndSettle` from the
42
+ * relayer, waits for the receipt, and returns success ONLY when the mined
43
+ * receipt carries both the opener's `StreamOpenedFor` and the escrow's
44
+ * `StreamOpened` for exactly this payer, nonce, amount and terms. The stock
45
+ * `ExactEvmScheme.settle` is never called: it would transfer the USDC straight
46
+ * to `payTo`, and USDC that reaches an opener any other way than
47
+ * `settleAndOpen` is unrecoverable. The signer handed to the stock scheme
48
+ * cannot broadcast at all, so that path is closed structurally too.
49
+ *
50
+ * ## At most one success per authorization
51
+ *
52
+ * An EIP-3009 authorization (chain + asset + payer + nonce) receives AT MOST
53
+ * ONE successful `SettleResponse` from this instance, ever. A settle of an
54
+ * authorization that is in flight, or already settled, returns `success:
55
+ * false` with `duplicate_settlement` (carrying the tx hash when there is one),
56
+ * and `verify` refuses it the same way — so one payment can never buy two
57
+ * served responses. After a `settlement_pending` (receipt wait timed out) the
58
+ * next settle of the SAME payload reconciles against the broadcast tx and may
59
+ * receive the one success, if none was delivered before. The ledger is
60
+ * in-memory and per instance: a multi-instance deployment needs a shared store
61
+ * and does not have one today.
62
+ *
63
+ * Relayer submissions (simulate + write) are serialized so concurrent payers
64
+ * never race for the relayer's account nonce.
65
+ */
66
+ /** Reasons this scheme adds to the stock `exact` ones. All fail closed. */
67
+ export const OpenerInvalidReason = {
68
+ unsupportedScheme: "metrik_opener_unsupported_scheme",
69
+ unsupportedNetwork: "metrik_opener_unsupported_network",
70
+ unsupportedVersion: "metrik_opener_unsupported_x402_version",
71
+ acceptedMismatch: "metrik_opener_accepted_requirements_mismatch",
72
+ unsupportedAssetTransferMethod: "metrik_opener_asset_transfer_method_not_eip3009",
73
+ erc6492Unsupported: "metrik_opener_erc6492_signature_unsupported",
74
+ missingSignature: "metrik_opener_missing_signature",
75
+ assetNotAllowed: "metrik_opener_asset_not_allowed",
76
+ invalidTerms: "metrik_opener_invalid_terms",
77
+ factoryMismatch: "metrik_opener_factory_mismatch",
78
+ payToMismatch: "metrik_opener_pay_to_not_predicted_opener",
79
+ invalidAmount: "metrik_opener_invalid_amount",
80
+ amountExceedsTerms: "metrik_opener_amount_exceeds_rate_times_duration",
81
+ listingUnavailable: "metrik_opener_listing_registry_unavailable",
82
+ listingNotFound: "metrik_opener_listing_not_found",
83
+ listingAmbiguous: "metrik_opener_listing_ambiguous",
84
+ listingRateOutOfBounds: "metrik_opener_listing_rate_out_of_bounds",
85
+ simulationFailed: "metrik_opener_settle_simulation_failed",
86
+ verifyError: "metrik_opener_verify_error",
87
+ transactionReverted: "metrik_opener_transaction_reverted",
88
+ streamEventMismatch: "metrik_opener_stream_event_mismatch",
89
+ settlementPending: "settlement_pending",
90
+ settleError: "metrik_opener_settle_error",
91
+ amountBelowMinimum: "metrik_opener_amount_below_minimum",
92
+ /**
93
+ * This authorization is already being settled, or already settled, by this
94
+ * facilitator instance. May carry the settlement transaction hash. Never a
95
+ * second success (see "At most one success" on the class).
96
+ */
97
+ duplicateSettlement: "duplicate_settlement",
98
+ settlementCapacity: "metrik_opener_settlement_capacity_exhausted",
99
+ };
100
+ /** Default facilitator minimum payment: 10000 base units = 0.01 USDC. */
101
+ export const DEFAULT_OPENER_MIN_AMOUNT = 10000n;
102
+ /** Hard cap on remembered authorizations; beyond it new settles fail closed. */
103
+ const MAX_LEDGER_ENTRIES = 100_000;
104
+ /** Grace after an authorization's `validBefore` before its ledger entry is dropped. */
105
+ const LEDGER_GRACE_SECONDS = 600n;
106
+ export class MetrikOpenerExactScheme {
107
+ config;
108
+ scheme = OPENER_SCHEME;
109
+ caipFamily = "eip155:*";
110
+ stock;
111
+ chainId;
112
+ allowlist;
113
+ receiptTimeoutMs;
114
+ minAmount;
115
+ now;
116
+ ledger = new Map();
117
+ /** Serializes relayer submissions (simulate + write) so nonces never collide. */
118
+ relayerQueue;
119
+ binding;
120
+ constructor(config) {
121
+ this.config = config;
122
+ this.chainId = chainIdFromNetwork(config.network);
123
+ if (!isAddress(config.factoryAddress)) {
124
+ throw new Error("factoryAddress must be an EVM address");
125
+ }
126
+ if (!isAddress(config.escrowAddress)) {
127
+ throw new Error("escrowAddress must be an EVM address");
128
+ }
129
+ if (config.assetAllowlist.length === 0) {
130
+ throw new Error("assetAllowlist must not be empty");
131
+ }
132
+ for (const asset of config.assetAllowlist) {
133
+ if (!isAddress(asset)) {
134
+ throw new Error(`assetAllowlist entry is not an address: ${asset}`);
135
+ }
136
+ }
137
+ this.allowlist = new Set(config.assetAllowlist.map((asset) => asset.toLowerCase()));
138
+ this.receiptTimeoutMs = config.receiptTimeoutMs ?? 60_000;
139
+ if (!Number.isInteger(this.receiptTimeoutMs) ||
140
+ this.receiptTimeoutMs <= 0) {
141
+ throw new Error("receiptTimeoutMs must be a positive integer");
142
+ }
143
+ this.minAmount = config.minAmount ?? DEFAULT_OPENER_MIN_AMOUNT;
144
+ if (this.minAmount <= 0n) {
145
+ throw new Error("minAmount must be > 0");
146
+ }
147
+ this.now = config.now ?? (() => Math.floor(Date.now() / 1000));
148
+ this.relayerQueue = config.relayerQueue ?? new RelayerQueue();
149
+ // The stock verifier gets a READ-ONLY signer. Its settle path would send the
150
+ // USDC straight to payTo; making writes impossible closes that path even if
151
+ // a future refactor called it by mistake.
152
+ const publicClient = config.publicClient;
153
+ const refuseWrite = async () => {
154
+ throw new Error("MetrikOpenerExactScheme: the stock exact scheme may not broadcast; opener payments settle only via deployAndSettle");
155
+ };
156
+ this.stock = new ExactEvmScheme(toFacilitatorEvmSigner({
157
+ address: config.walletClient.account.address,
158
+ readContract: (args) => publicClient.readContract(args),
159
+ verifyTypedData: (args) => publicClient.verifyTypedData(args),
160
+ getCode: (args) => publicClient.getCode(args),
161
+ waitForTransactionReceipt: refuseWrite,
162
+ writeContract: refuseWrite,
163
+ sendTransaction: refuseWrite,
164
+ }),
165
+ // ERC-6492 factory deployment disabled: 6492 signatures are refused
166
+ // before the stock verifier is ever reached.
167
+ { eip6492AllowedFactories: [] });
168
+ }
169
+ /**
170
+ * Proves the on-chain wiring before the facilitator serves anything: the RPC
171
+ * is on the configured chain, the factory has code, points at the configured
172
+ * escrow, and its token is allowlisted. Throws otherwise (the service refuses
173
+ * to start).
174
+ */
175
+ async init() {
176
+ const { publicClient, factoryAddress, escrowAddress } = this.config;
177
+ const rpcChainId = await publicClient.getChainId();
178
+ if (rpcChainId !== this.chainId) {
179
+ throw new Error(`RPC chain id ${rpcChainId} does not match network ${this.config.network}`);
180
+ }
181
+ const code = await publicClient.getCode({ address: factoryAddress });
182
+ if (code === undefined || code === "0x") {
183
+ throw new Error(`no contract at factory address ${factoryAddress}`);
184
+ }
185
+ const [factoryEscrow, factoryUsdc] = await Promise.all([
186
+ publicClient.readContract({
187
+ address: factoryAddress,
188
+ abi: x402StreamOpenerFactoryAbi,
189
+ functionName: "escrow",
190
+ }),
191
+ publicClient.readContract({
192
+ address: factoryAddress,
193
+ abi: x402StreamOpenerFactoryAbi,
194
+ functionName: "usdc",
195
+ }),
196
+ ]);
197
+ if (getAddress(factoryEscrow) !== getAddress(escrowAddress)) {
198
+ throw new Error(`factory escrow ${factoryEscrow} is not the configured escrow ${escrowAddress}`);
199
+ }
200
+ if (!this.allowlist.has(factoryUsdc.toLowerCase())) {
201
+ throw new Error(`factory settlement token ${factoryUsdc} is not on the asset allowlist`);
202
+ }
203
+ this.binding = {
204
+ chainId: this.chainId,
205
+ factory: getAddress(factoryAddress),
206
+ escrow: getAddress(factoryEscrow),
207
+ usdc: getAddress(factoryUsdc),
208
+ relayer: this.config.walletClient.account.address,
209
+ };
210
+ return this.binding;
211
+ }
212
+ /** `/supported` extra: the marker a resource server checks before quoting an opener. */
213
+ getExtra(_network) {
214
+ const binding = this.binding;
215
+ if (binding === undefined) {
216
+ return undefined;
217
+ }
218
+ const marker = {
219
+ settlement: "x402-stream-opener",
220
+ factory: binding.factory,
221
+ escrow: binding.escrow,
222
+ usdc: binding.usdc,
223
+ chainId: binding.chainId,
224
+ minAmount: this.minAmount.toString(),
225
+ receiptTimeoutSeconds: Math.ceil(this.receiptTimeoutMs / 1000),
226
+ ...(this.config.advertiseRefundRelay === true
227
+ ? { refundRelay: true }
228
+ : {}),
229
+ };
230
+ return { [OPENER_EXTRA_KEY]: marker };
231
+ }
232
+ getSigners(_network) {
233
+ return [this.config.walletClient.account.address];
234
+ }
235
+ async verify(payload, requirements, context) {
236
+ try {
237
+ const key = this.ledgerKey(payload);
238
+ const entry = key === undefined ? undefined : this.ledger.get(key);
239
+ if (entry !== undefined) {
240
+ if (entry.state === "pending" &&
241
+ deepEqual(entry.payload, payload) &&
242
+ deepEqual(entry.requirements, requirements)) {
243
+ // Submitted earlier, receipt not yet seen: the authorization may
244
+ // already be used on-chain, so the on-chain checks would refuse it.
245
+ // The retry is valid only so that settle can reconcile THIS tx.
246
+ return { isValid: true, payer: entry.auth.authorization.from };
247
+ }
248
+ return invalid(OpenerInvalidReason.duplicateSettlement, "this authorization is already being settled or was settled", payerOf(payload));
249
+ }
250
+ const checked = await this.check(payload, requirements, context);
251
+ if (!checked.ok) {
252
+ return checked.response;
253
+ }
254
+ return { isValid: true, payer: checked.auth.authorization.from };
255
+ }
256
+ catch (error) {
257
+ return invalid(OpenerInvalidReason.verifyError, describe(error), payerOf(payload));
258
+ }
259
+ }
260
+ async settle(payload, requirements, context) {
261
+ const network = requirements.network;
262
+ const payer = payerOf(payload);
263
+ const key = this.ledgerKey(payload);
264
+ if (key === undefined) {
265
+ // Not a parseable EIP-3009 authorization: the full check refuses it.
266
+ const checked = await this.check(payload, requirements, context).catch((error) => ({
267
+ ok: false,
268
+ response: invalid(OpenerInvalidReason.verifyError, describe(error), payer),
269
+ }));
270
+ return checked.ok
271
+ ? failure(OpenerInvalidReason.verifyError, network, payer, "unparseable authorization")
272
+ : refusal(checked.response, network, payer);
273
+ }
274
+ // ── At most one success per authorization ─────────────────────────────
275
+ // Everything from here to the ledger write is synchronous, so two
276
+ // concurrent settles of the same authorization can never both claim it.
277
+ this.pruneLedger();
278
+ const existing = this.ledger.get(key);
279
+ if (existing !== undefined) {
280
+ if (existing.state === "pending" &&
281
+ deepEqual(existing.payload, payload) &&
282
+ deepEqual(existing.requirements, requirements)) {
283
+ this.ledger.set(key, {
284
+ state: "in-flight",
285
+ validBefore: existing.validBefore,
286
+ hash: existing.hash,
287
+ });
288
+ return this.finish(key, payload, requirements, existing.validBefore, await this.awaitSettlement(existing.hash, existing.auth, existing.terms, existing.opener, network), existing);
289
+ }
290
+ return failure(OpenerInvalidReason.duplicateSettlement, network, payer, "this authorization is already being settled or was settled", existing.hash ?? "");
291
+ }
292
+ if (this.ledger.size >= MAX_LEDGER_ENTRIES) {
293
+ return failure(OpenerInvalidReason.settlementCapacity, network, payer, undefined);
294
+ }
295
+ const validBefore = BigInt(eip3009(payload).authorization.validBefore);
296
+ const entry = { state: "in-flight", validBefore };
297
+ this.ledger.set(key, entry);
298
+ try {
299
+ const checked = await this.check(payload, requirements, context);
300
+ if (!checked.ok) {
301
+ this.ledger.delete(key);
302
+ return refusal(checked.response, network, payer);
303
+ }
304
+ const { auth, terms, opener } = checked;
305
+ let hash;
306
+ try {
307
+ hash = await this.submitSerialized(auth, terms);
308
+ }
309
+ catch (error) {
310
+ this.ledger.delete(key);
311
+ return failure(OpenerInvalidReason.simulationFailed, network, auth.authorization.from, revertReason(error));
312
+ }
313
+ entry.hash = hash;
314
+ return this.finish(key, payload, requirements, validBefore, await this.awaitSettlement(hash, auth, terms, opener, network), { auth, terms, opener });
315
+ }
316
+ catch (error) {
317
+ // Unknown state after a possible broadcast: keep the authorization
318
+ // blocked (never a second success), but only if something was sent.
319
+ if (entry.hash === undefined) {
320
+ this.ledger.delete(key);
321
+ }
322
+ else {
323
+ this.ledger.set(key, { state: "final", validBefore, hash: entry.hash });
324
+ }
325
+ return failure(OpenerInvalidReason.settleError, network, payer, describe(error), entry.hash ?? "");
326
+ }
327
+ }
328
+ /** Records the outcome of one settle attempt in the ledger and returns it. */
329
+ finish(key, payload, requirements, validBefore, result, context) {
330
+ if (result.success) {
331
+ // THE one success for this authorization. Nothing succeeds again.
332
+ this.ledger.set(key, {
333
+ state: "final",
334
+ validBefore,
335
+ hash: result.transaction,
336
+ });
337
+ return result;
338
+ }
339
+ if (result.errorReason === OpenerInvalidReason.settlementPending) {
340
+ this.ledger.set(key, {
341
+ state: "pending",
342
+ validBefore,
343
+ hash: result.transaction,
344
+ payload,
345
+ requirements,
346
+ auth: context.auth,
347
+ terms: context.terms,
348
+ opener: context.opener,
349
+ });
350
+ return result;
351
+ }
352
+ if (result.errorReason === OpenerInvalidReason.transactionReverted) {
353
+ // A reverted deployAndSettle consumed nothing (the whole call reverts),
354
+ // so the authorization may be settled again.
355
+ this.ledger.delete(key);
356
+ return result;
357
+ }
358
+ // Mined but not the expected stream, or anything else terminal: block.
359
+ this.ledger.set(key, {
360
+ state: "final",
361
+ validBefore,
362
+ hash: result.transaction || "",
363
+ });
364
+ return result;
365
+ }
366
+ /**
367
+ * Simulate + broadcast `deployAndSettle`, one at a time. Concurrent
368
+ * submissions from one relayer would otherwise race for the same account
369
+ * nonce and all but one would fail after the upstream already did the work.
370
+ */
371
+ submitSerialized(auth, terms) {
372
+ const run = async () => {
373
+ const { request } = await this.config.publicClient.simulateContract({
374
+ account: this.config.walletClient.account,
375
+ address: this.config.factoryAddress,
376
+ abi: x402StreamOpenerFactoryAbi,
377
+ functionName: "deployAndSettle",
378
+ args: [termsTuple(terms), authorizationTuple(auth)],
379
+ });
380
+ return this.config.walletClient.writeContract({
381
+ ...request,
382
+ chain: this.config.walletClient.chain ?? null,
383
+ });
384
+ };
385
+ return this.relayerQueue.run(run);
386
+ }
387
+ /** chain + asset + payer + authorization nonce, or undefined if unparseable. */
388
+ ledgerKey(payload) {
389
+ const auth = eip3009(payload);
390
+ const asset = payload.accepted?.asset;
391
+ if (auth === undefined || typeof asset !== "string")
392
+ return undefined;
393
+ return [
394
+ this.chainId,
395
+ asset.toLowerCase(),
396
+ auth.authorization.from.toLowerCase(),
397
+ auth.authorization.nonce.toLowerCase(),
398
+ ].join(":");
399
+ }
400
+ /**
401
+ * Drops entries whose authorization can no longer be settled by anyone
402
+ * (past `validBefore` plus a grace). Never drops an in-flight entry.
403
+ */
404
+ pruneLedger() {
405
+ const now = BigInt(this.now());
406
+ for (const [key, entry] of this.ledger) {
407
+ if (entry.state !== "in-flight" &&
408
+ entry.validBefore + LEDGER_GRACE_SECONDS < now) {
409
+ this.ledger.delete(key);
410
+ }
411
+ }
412
+ }
413
+ async awaitSettlement(hash, auth, terms, opener, network) {
414
+ const payer = getAddress(auth.authorization.from);
415
+ let receipt;
416
+ try {
417
+ receipt = await this.config.publicClient.waitForTransactionReceipt({
418
+ hash,
419
+ timeout: this.receiptTimeoutMs,
420
+ });
421
+ }
422
+ catch (error) {
423
+ // Broadcast but unconfirmed: keep the pending entry so a retry
424
+ // reconciles against this hash. Never success without a receipt.
425
+ return failure(OpenerInvalidReason.settlementPending, network, payer, describe(error), hash);
426
+ }
427
+ if (receipt.status !== "success") {
428
+ return failure(OpenerInvalidReason.transactionReverted, network, payer, "deployAndSettle reverted on-chain", hash);
429
+ }
430
+ const opened = findStreamOpened(receipt.logs, opener, getAddress(this.config.escrowAddress), auth, terms);
431
+ if (opened === undefined) {
432
+ return failure(OpenerInvalidReason.streamEventMismatch, network, payer, "the mined receipt does not show the expected stream", hash);
433
+ }
434
+ return {
435
+ success: true,
436
+ transaction: hash,
437
+ network,
438
+ payer,
439
+ amount: auth.authorization.value,
440
+ extra: {
441
+ [OPENER_EXTRA_KEY]: {
442
+ streamId: opened.streamId,
443
+ opener,
444
+ escrow: getAddress(this.config.escrowAddress),
445
+ deposit: auth.authorization.value,
446
+ ratePerSecond: terms.ratePerSecond.toString(),
447
+ // A JSON NUMBER: the SDK buyer (payWithX402) validates it as one.
448
+ expiresAt: Number(opened.expiresAt),
449
+ },
450
+ },
451
+ };
452
+ }
453
+ /** Every Metrik check plus the stock verifier. Cheapest, most local first. */
454
+ async check(payload, requirements, context) {
455
+ const payer = payerOf(payload);
456
+ const refuse = (reason, message) => ({ ok: false, response: invalid(reason, message, payer) });
457
+ if (payload.x402Version !== 2) {
458
+ return refuse(OpenerInvalidReason.unsupportedVersion);
459
+ }
460
+ if (requirements.scheme !== OPENER_SCHEME) {
461
+ return refuse(OpenerInvalidReason.unsupportedScheme);
462
+ }
463
+ if (requirements.network !== this.config.network ||
464
+ payload.accepted?.network !== this.config.network) {
465
+ return refuse(OpenerInvalidReason.unsupportedNetwork);
466
+ }
467
+ // The payer signed against `accepted`; settle exactly that, nothing else.
468
+ if (!deepEqual(payload.accepted, requirements)) {
469
+ return refuse(OpenerInvalidReason.acceptedMismatch);
470
+ }
471
+ const method = requirements.extra?.assetTransferMethod;
472
+ if (method !== undefined && method !== "eip3009") {
473
+ return refuse(OpenerInvalidReason.unsupportedAssetTransferMethod);
474
+ }
475
+ const auth = eip3009(payload);
476
+ if (auth === undefined) {
477
+ return refuse(OpenerInvalidReason.unsupportedAssetTransferMethod);
478
+ }
479
+ if (auth.signature === undefined || !isHex(auth.signature)) {
480
+ return refuse(OpenerInvalidReason.missingSignature);
481
+ }
482
+ if (isErc6492Signature(auth.signature)) {
483
+ return refuse(OpenerInvalidReason.erc6492Unsupported, "ERC-6492 (counterfactual smart wallet) signatures cannot be settled through the stream opener; deploy the wallet first");
484
+ }
485
+ const binding = this.binding;
486
+ if (binding === undefined) {
487
+ return refuse(OpenerInvalidReason.verifyError, "facilitator not initialized");
488
+ }
489
+ if (!isAddress(requirements.asset) ||
490
+ !this.allowlist.has(requirements.asset.toLowerCase()) ||
491
+ getAddress(requirements.asset) !== binding.usdc) {
492
+ return refuse(OpenerInvalidReason.assetNotAllowed);
493
+ }
494
+ let terms;
495
+ let factory;
496
+ try {
497
+ ({ terms, factory } = termsFromExtra(requirements.extra));
498
+ }
499
+ catch (error) {
500
+ return refuse(OpenerInvalidReason.invalidTerms, describe(error));
501
+ }
502
+ if (factory !== binding.factory) {
503
+ return refuse(OpenerInvalidReason.factoryMismatch);
504
+ }
505
+ let amount;
506
+ try {
507
+ amount = parseUintString(requirements.amount, "amount");
508
+ }
509
+ catch (error) {
510
+ return refuse(OpenerInvalidReason.invalidAmount, describe(error));
511
+ }
512
+ if (amount <= 0n) {
513
+ return refuse(OpenerInvalidReason.invalidAmount, "amount must be > 0");
514
+ }
515
+ if (amount < this.minAmount) {
516
+ return refuse(OpenerInvalidReason.amountBelowMinimum, `amount ${amount} is below this facilitator's minimum ${this.minAmount}`);
517
+ }
518
+ if (amount > maxEarnable(terms)) {
519
+ return refuse(OpenerInvalidReason.amountExceedsTerms);
520
+ }
521
+ const predicted = await this.config.publicClient.readContract({
522
+ address: binding.factory,
523
+ abi: x402StreamOpenerFactoryAbi,
524
+ functionName: "predict",
525
+ args: [termsTuple(terms)],
526
+ });
527
+ if (!isAddress(requirements.payTo) ||
528
+ getAddress(requirements.payTo) !== getAddress(predicted)) {
529
+ return refuse(OpenerInvalidReason.payToMismatch);
530
+ }
531
+ const listing = await this.config.listingResolver.resolve(terms);
532
+ if (!listing.ok) {
533
+ const reason = {
534
+ "listing-registry-unavailable": OpenerInvalidReason.listingUnavailable,
535
+ "listing-not-found": OpenerInvalidReason.listingNotFound,
536
+ "listing-ambiguous": OpenerInvalidReason.listingAmbiguous,
537
+ "listing-rate-out-of-bounds": OpenerInvalidReason.listingRateOutOfBounds,
538
+ }[listing.reason];
539
+ return refuse(reason, listing.message);
540
+ }
541
+ // Standard exact checks: signature, to == payTo, value == amount, window,
542
+ // asset contract, and a simulation of the EIP-3009 transfer itself.
543
+ const stock = await this.stock.verify(payload, requirements, context);
544
+ if (!stock.isValid) {
545
+ return { ok: false, response: stock };
546
+ }
547
+ const signed = auth;
548
+ try {
549
+ await this.config.publicClient.simulateContract({
550
+ account: this.config.walletClient.account,
551
+ address: binding.factory,
552
+ abi: x402StreamOpenerFactoryAbi,
553
+ functionName: "deployAndSettle",
554
+ args: [termsTuple(terms), authorizationTuple(signed)],
555
+ });
556
+ }
557
+ catch (error) {
558
+ return refuse(OpenerInvalidReason.simulationFailed, revertReason(error));
559
+ }
560
+ return { ok: true, auth: signed, terms, opener: getAddress(predicted) };
561
+ }
562
+ }
563
+ function authorizationTuple(auth) {
564
+ return {
565
+ from: getAddress(auth.authorization.from),
566
+ value: BigInt(auth.authorization.value),
567
+ validAfter: BigInt(auth.authorization.validAfter),
568
+ validBefore: BigInt(auth.authorization.validBefore),
569
+ nonce: auth.authorization.nonce,
570
+ signature: auth.signature,
571
+ };
572
+ }
573
+ /**
574
+ * Finds the pair of events a genuine opener settlement emits in ONE receipt:
575
+ * the escrow's `StreamOpened` with `buyer == opener` and exactly these terms,
576
+ * and the opener's `StreamOpenedFor` binding that stream to this payer, nonce
577
+ * and amount.
578
+ */
579
+ function findStreamOpened(logs, opener, escrow, auth, terms) {
580
+ const value = BigInt(auth.authorization.value);
581
+ const payer = getAddress(auth.authorization.from);
582
+ const escrowStreams = new Map();
583
+ for (const log of logs) {
584
+ if (getAddress(log.address) !== escrow)
585
+ continue;
586
+ try {
587
+ const decoded = decodeEventLog({
588
+ abi: streamEscrowV2Abi,
589
+ data: log.data,
590
+ topics: log.topics,
591
+ });
592
+ if (decoded.eventName !== "StreamOpened")
593
+ continue;
594
+ const args = decoded.args;
595
+ if (getAddress(args.buyer) === opener &&
596
+ getAddress(args.operator) === getAddress(terms.operator) &&
597
+ args.serviceRef.toLowerCase() === terms.serviceRef.toLowerCase() &&
598
+ args.deposit === value &&
599
+ args.ratePerSecond === terms.ratePerSecond) {
600
+ escrowStreams.set(args.streamId.toLowerCase(), BigInt(args.expiresAt));
601
+ }
602
+ }
603
+ catch {
604
+ continue;
605
+ }
606
+ }
607
+ for (const log of logs) {
608
+ if (getAddress(log.address) !== opener)
609
+ continue;
610
+ try {
611
+ const decoded = decodeEventLog({
612
+ abi: x402StreamOpenerAbi,
613
+ data: log.data,
614
+ topics: log.topics,
615
+ });
616
+ if (decoded.eventName !== "StreamOpenedFor")
617
+ continue;
618
+ const args = decoded.args;
619
+ const expiresAt = escrowStreams.get(args.streamId.toLowerCase());
620
+ if (expiresAt !== undefined &&
621
+ getAddress(args.payer) === payer &&
622
+ args.authorizationNonce.toLowerCase() ===
623
+ auth.authorization.nonce.toLowerCase() &&
624
+ args.deposit === value) {
625
+ return { streamId: args.streamId, expiresAt };
626
+ }
627
+ }
628
+ catch {
629
+ continue;
630
+ }
631
+ }
632
+ return undefined;
633
+ }
634
+ /**
635
+ * Reads an opener facilitator marker out of a `/supported` kind's `extra`, or
636
+ * `undefined` when the facilitator does not advertise opener settlement.
637
+ */
638
+ export function readOpenerFacilitatorMarker(extra) {
639
+ const raw = extra?.[OPENER_EXTRA_KEY];
640
+ if (typeof raw !== "object" || raw === null)
641
+ return undefined;
642
+ const marker = raw;
643
+ if (marker.settlement !== "x402-stream-opener" ||
644
+ typeof marker.factory !== "string" ||
645
+ !isAddress(marker.factory) ||
646
+ typeof marker.escrow !== "string" ||
647
+ !isAddress(marker.escrow) ||
648
+ typeof marker.usdc !== "string" ||
649
+ !isAddress(marker.usdc) ||
650
+ typeof marker.chainId !== "number" ||
651
+ typeof marker.minAmount !== "string" ||
652
+ !/^[1-9]\d*$/.test(marker.minAmount) ||
653
+ typeof marker.receiptTimeoutSeconds !== "number" ||
654
+ !Number.isInteger(marker.receiptTimeoutSeconds) ||
655
+ marker.receiptTimeoutSeconds <= 0) {
656
+ return undefined;
657
+ }
658
+ return {
659
+ settlement: "x402-stream-opener",
660
+ factory: getAddress(marker.factory),
661
+ escrow: getAddress(marker.escrow),
662
+ usdc: getAddress(marker.usdc),
663
+ chainId: marker.chainId,
664
+ minAmount: marker.minAmount,
665
+ receiptTimeoutSeconds: marker.receiptTimeoutSeconds,
666
+ ...(marker.refundRelay === true ? { refundRelay: true } : {}),
667
+ };
668
+ }
669
+ function eip3009(payload) {
670
+ const inner = payload.payload;
671
+ if (typeof inner !== "object" || inner === null)
672
+ return undefined;
673
+ const candidate = inner;
674
+ if (!isEIP3009Payload(candidate))
675
+ return undefined;
676
+ const authorization = candidate.authorization;
677
+ if (typeof authorization !== "object" ||
678
+ authorization === null ||
679
+ typeof authorization.from !== "string" ||
680
+ !isAddress(authorization.from) ||
681
+ typeof authorization.to !== "string" ||
682
+ typeof authorization.value !== "string" ||
683
+ typeof authorization.validAfter !== "string" ||
684
+ typeof authorization.validBefore !== "string" ||
685
+ typeof authorization.nonce !== "string" ||
686
+ !isHex(authorization.nonce) ||
687
+ authorization.nonce.length !== 66) {
688
+ return undefined;
689
+ }
690
+ try {
691
+ parseUintString(authorization.value, "authorization.value");
692
+ parseUintString(authorization.validAfter, "authorization.validAfter");
693
+ parseUintString(authorization.validBefore, "authorization.validBefore");
694
+ }
695
+ catch {
696
+ return undefined;
697
+ }
698
+ return candidate;
699
+ }
700
+ function payerOf(payload) {
701
+ const from = payload.payload?.authorization?.from;
702
+ return typeof from === "string" && isAddress(from)
703
+ ? getAddress(from)
704
+ : undefined;
705
+ }
706
+ function invalid(reason, message, payer) {
707
+ return {
708
+ isValid: false,
709
+ invalidReason: reason,
710
+ ...(message === undefined ? {} : { invalidMessage: message }),
711
+ ...(payer === undefined ? {} : { payer }),
712
+ };
713
+ }
714
+ function failure(reason, network, payer, message, transaction = "") {
715
+ return {
716
+ success: false,
717
+ errorReason: reason,
718
+ ...(message === undefined ? {} : { errorMessage: message }),
719
+ transaction,
720
+ network,
721
+ ...(payer === undefined ? {} : { payer }),
722
+ };
723
+ }
724
+ function refusal(response, network, payer) {
725
+ return {
726
+ success: false,
727
+ errorReason: response.invalidReason ?? OpenerInvalidReason.verifyError,
728
+ ...(response.invalidMessage === undefined
729
+ ? {}
730
+ : { errorMessage: response.invalidMessage }),
731
+ transaction: "",
732
+ network,
733
+ ...(payer === undefined ? {} : { payer }),
734
+ };
735
+ }
736
+ function revertReason(error) {
737
+ if (error instanceof BaseError) {
738
+ const reverted = error.walk((cause) => cause instanceof ContractFunctionRevertedError);
739
+ if (reverted instanceof ContractFunctionRevertedError) {
740
+ return (reverted.data?.errorName ?? reverted.reason ?? reverted.shortMessage);
741
+ }
742
+ return error.shortMessage;
743
+ }
744
+ return describe(error);
745
+ }
746
+ function describe(error) {
747
+ return error instanceof Error ? error.message : String(error);
748
+ }
749
+ //# sourceMappingURL=scheme.js.map