tapimo 0.0.2 → 0.2.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 (517) hide show
  1. package/README.md +38 -16
  2. package/dist/ApiKey.d.ts +14 -11
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +11 -12
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +207 -0
  7. package/dist/ApiKeys.d.ts.map +1 -0
  8. package/dist/ApiKeys.js +282 -0
  9. package/dist/ApiKeys.js.map +1 -0
  10. package/dist/App.d.ts +6771 -3819
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +145 -317
  13. package/dist/App.js.map +1 -1
  14. package/dist/Handler.d.ts +86 -0
  15. package/dist/Handler.d.ts.map +1 -0
  16. package/dist/Handler.js +83 -0
  17. package/dist/Handler.js.map +1 -0
  18. package/dist/Scope.d.ts +14 -4
  19. package/dist/Scope.d.ts.map +1 -1
  20. package/dist/Scope.js +16 -4
  21. package/dist/Scope.js.map +1 -1
  22. package/dist/Webhooks.d.ts +5 -7
  23. package/dist/Webhooks.d.ts.map +1 -1
  24. package/dist/Webhooks.js +11 -4
  25. package/dist/Webhooks.js.map +1 -1
  26. package/dist/admin/Access.d.ts +1 -3
  27. package/dist/admin/Access.d.ts.map +1 -1
  28. package/dist/admin/Access.js +1 -3
  29. package/dist/admin/Access.js.map +1 -1
  30. package/dist/admin/App.d.ts +167 -205
  31. package/dist/admin/App.d.ts.map +1 -1
  32. package/dist/admin/App.js +2 -4
  33. package/dist/admin/App.js.map +1 -1
  34. package/dist/admin/apps/api-keys.d.ts +105 -5
  35. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  36. package/dist/admin/apps/api-keys.js +75 -9
  37. package/dist/admin/apps/api-keys.js.map +1 -1
  38. package/dist/admin/apps/scopes.d.ts +6 -0
  39. package/dist/admin/apps/scopes.d.ts.map +1 -1
  40. package/dist/admin/apps/verified-tokens.d.ts +35 -149
  41. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  42. package/dist/admin/apps/verified-tokens.js +18 -39
  43. package/dist/admin/apps/verified-tokens.js.map +1 -1
  44. package/dist/admin/ui.generated.d.ts +1 -1
  45. package/dist/admin/ui.generated.js +1 -1
  46. package/dist/apps/data/App.d.ts +19176 -0
  47. package/dist/apps/data/App.d.ts.map +1 -0
  48. package/dist/apps/data/App.js +236 -0
  49. package/dist/apps/data/App.js.map +1 -0
  50. package/dist/apps/data/index.d.ts +3 -0
  51. package/dist/apps/data/index.d.ts.map +1 -0
  52. package/dist/apps/data/index.js +3 -0
  53. package/dist/apps/data/index.js.map +1 -0
  54. package/dist/apps/{activities.d.ts → data/routes/activities.d.ts} +32 -32
  55. package/dist/apps/{activities.d.ts.map → data/routes/activities.d.ts.map} +1 -1
  56. package/dist/apps/{activities.js → data/routes/activities.js} +13 -13
  57. package/dist/apps/data/routes/activities.js.map +1 -0
  58. package/dist/apps/{addresses.d.ts → data/routes/addresses.d.ts} +22 -22
  59. package/dist/apps/{addresses.d.ts.map → data/routes/addresses.d.ts.map} +1 -1
  60. package/dist/apps/{addresses.js → data/routes/addresses.js} +5 -5
  61. package/dist/apps/data/routes/addresses.js.map +1 -0
  62. package/dist/apps/{balances.d.ts → data/routes/balances.d.ts} +49 -49
  63. package/dist/apps/data/routes/balances.d.ts.map +1 -0
  64. package/dist/apps/{balances.js → data/routes/balances.js} +42 -11
  65. package/dist/apps/data/routes/balances.js.map +1 -0
  66. package/dist/apps/{blocks.d.ts → data/routes/blocks.d.ts} +47 -47
  67. package/dist/apps/data/routes/blocks.d.ts.map +1 -0
  68. package/dist/apps/{blocks.js → data/routes/blocks.js} +13 -13
  69. package/dist/apps/data/routes/blocks.js.map +1 -0
  70. package/dist/apps/{coingecko.d.ts → data/routes/coingecko.d.ts} +183 -183
  71. package/dist/apps/data/routes/coingecko.d.ts.map +1 -0
  72. package/dist/apps/{coingecko.js → data/routes/coingecko.js} +13 -13
  73. package/dist/apps/data/routes/coingecko.js.map +1 -0
  74. package/dist/apps/{exchanges.d.ts → data/routes/exchanges.d.ts} +460 -460
  75. package/dist/apps/data/routes/exchanges.d.ts.map +1 -0
  76. package/dist/apps/{exchanges.js → data/routes/exchanges.js} +19 -19
  77. package/dist/apps/data/routes/exchanges.js.map +1 -0
  78. package/dist/apps/{fee-amm.d.ts → data/routes/fee-amm.d.ts} +94 -94
  79. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -0
  80. package/dist/apps/{fee-amm.js → data/routes/fee-amm.js} +17 -17
  81. package/dist/apps/data/routes/fee-amm.js.map +1 -0
  82. package/dist/apps/{indexer.d.ts → data/routes/indexer.d.ts} +4 -4
  83. package/dist/apps/data/routes/indexer.d.ts.map +1 -0
  84. package/dist/apps/{indexer.js → data/routes/indexer.js} +13 -9
  85. package/dist/apps/data/routes/indexer.js.map +1 -0
  86. package/dist/apps/{receipts.d.ts → data/routes/receipts.d.ts} +6 -6
  87. package/dist/apps/data/routes/receipts.d.ts.map +1 -0
  88. package/dist/apps/{receipts.js → data/routes/receipts.js} +12 -12
  89. package/dist/apps/data/routes/receipts.js.map +1 -0
  90. package/dist/apps/{rpc.d.ts → data/routes/rpc.d.ts} +4 -4
  91. package/dist/apps/data/routes/rpc.d.ts.map +1 -0
  92. package/dist/apps/{rpc.js → data/routes/rpc.js} +6 -6
  93. package/dist/apps/data/routes/rpc.js.map +1 -0
  94. package/dist/apps/{tokenlist.d.ts → data/routes/tokenlist.d.ts} +4 -4
  95. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -0
  96. package/dist/apps/{tokenlist.js → data/routes/tokenlist.js} +8 -8
  97. package/dist/apps/data/routes/tokenlist.js.map +1 -0
  98. package/dist/apps/{tokens.d.ts → data/routes/tokens.d.ts} +11 -11
  99. package/dist/apps/data/routes/tokens.d.ts.map +1 -0
  100. package/dist/apps/{tokens.js → data/routes/tokens.js} +19 -19
  101. package/dist/apps/data/routes/tokens.js.map +1 -0
  102. package/dist/apps/{transactions.d.ts → data/routes/transactions.d.ts} +288 -288
  103. package/dist/apps/data/routes/transactions.d.ts.map +1 -0
  104. package/dist/apps/{transactions.js → data/routes/transactions.js} +13 -13
  105. package/dist/apps/data/routes/transactions.js.map +1 -0
  106. package/dist/apps/{transfers.d.ts → data/routes/transfers.d.ts} +35 -35
  107. package/dist/apps/data/routes/transfers.d.ts.map +1 -0
  108. package/dist/apps/{transfers.js → data/routes/transfers.js} +13 -13
  109. package/dist/apps/data/routes/transfers.js.map +1 -0
  110. package/dist/apps/{verified-tokens.d.ts → data/routes/verified-tokens.d.ts} +42 -42
  111. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -0
  112. package/dist/apps/{verified-tokens.js → data/routes/verified-tokens.js} +9 -9
  113. package/dist/apps/data/routes/verified-tokens.js.map +1 -0
  114. package/dist/apps/{webhooks.d.ts → data/routes/webhooks.d.ts} +66 -63
  115. package/dist/apps/{webhooks.d.ts.map → data/routes/webhooks.d.ts.map} +1 -1
  116. package/dist/apps/{webhooks.js → data/routes/webhooks.js} +67 -59
  117. package/dist/apps/data/routes/webhooks.js.map +1 -0
  118. package/dist/apps/docs/App.d.ts +27 -0
  119. package/dist/apps/docs/App.d.ts.map +1 -0
  120. package/dist/apps/docs/App.js +122 -0
  121. package/dist/apps/docs/App.js.map +1 -0
  122. package/dist/{internal/OpenApi.fonts.d.ts → apps/docs/fonts.d.ts} +1 -1
  123. package/dist/apps/docs/fonts.d.ts.map +1 -0
  124. package/dist/{internal/OpenApi.fonts.js → apps/docs/fonts.js} +1 -1
  125. package/dist/apps/docs/fonts.js.map +1 -0
  126. package/dist/apps/docs/index.d.ts +2 -0
  127. package/dist/apps/docs/index.d.ts.map +1 -0
  128. package/dist/apps/docs/index.js +2 -0
  129. package/dist/apps/docs/index.js.map +1 -0
  130. package/dist/{internal/OpenApi.logo.d.ts → apps/docs/logo.d.ts} +1 -1
  131. package/dist/apps/docs/logo.d.ts.map +1 -0
  132. package/dist/{internal/OpenApi.logo.js → apps/docs/logo.js} +1 -1
  133. package/dist/apps/docs/logo.js.map +1 -0
  134. package/dist/apps/index.d.ts +4 -0
  135. package/dist/apps/index.d.ts.map +1 -0
  136. package/dist/apps/index.js +4 -0
  137. package/dist/apps/index.js.map +1 -0
  138. package/dist/apps/metadata.d.ts +7 -0
  139. package/dist/apps/metadata.d.ts.map +1 -0
  140. package/dist/apps/metadata.js +15 -0
  141. package/dist/apps/metadata.js.map +1 -0
  142. package/dist/apps/platform/App.d.ts +2985 -0
  143. package/dist/apps/platform/App.d.ts.map +1 -0
  144. package/dist/apps/platform/App.js +74 -0
  145. package/dist/apps/platform/App.js.map +1 -0
  146. package/dist/apps/platform/index.d.ts +2 -0
  147. package/dist/apps/platform/index.d.ts.map +1 -0
  148. package/dist/apps/platform/index.js +2 -0
  149. package/dist/apps/platform/index.js.map +1 -0
  150. package/dist/apps/platform/routes/api-keys.d.ts +681 -0
  151. package/dist/apps/platform/routes/api-keys.d.ts.map +1 -0
  152. package/dist/apps/platform/routes/api-keys.js +328 -0
  153. package/dist/apps/platform/routes/api-keys.js.map +1 -0
  154. package/dist/apps/platform/routes/invitations.d.ts +733 -0
  155. package/dist/apps/platform/routes/invitations.d.ts.map +1 -0
  156. package/dist/apps/platform/routes/invitations.js +413 -0
  157. package/dist/apps/platform/routes/invitations.js.map +1 -0
  158. package/dist/apps/platform/routes/me.d.ts +95 -0
  159. package/dist/apps/platform/routes/me.d.ts.map +1 -0
  160. package/dist/apps/platform/routes/me.js +81 -0
  161. package/dist/apps/platform/routes/me.js.map +1 -0
  162. package/dist/apps/platform/routes/members.d.ts +554 -0
  163. package/dist/apps/platform/routes/members.d.ts.map +1 -0
  164. package/dist/apps/platform/routes/members.js +251 -0
  165. package/dist/apps/platform/routes/members.js.map +1 -0
  166. package/dist/apps/platform/routes/orgs.d.ts +582 -0
  167. package/dist/apps/platform/routes/orgs.d.ts.map +1 -0
  168. package/dist/apps/platform/routes/orgs.js +265 -0
  169. package/dist/apps/platform/routes/orgs.js.map +1 -0
  170. package/dist/apps/platform/routes/projects.d.ts +740 -0
  171. package/dist/apps/platform/routes/projects.d.ts.map +1 -0
  172. package/dist/apps/platform/routes/projects.js +286 -0
  173. package/dist/apps/platform/routes/projects.js.map +1 -0
  174. package/dist/apps/relay/App.d.ts +41 -0
  175. package/dist/apps/relay/App.d.ts.map +1 -0
  176. package/dist/apps/relay/App.js +103 -0
  177. package/dist/apps/relay/App.js.map +1 -0
  178. package/dist/apps/relay/Sponsorships.d.ts +107 -0
  179. package/dist/apps/relay/Sponsorships.d.ts.map +1 -0
  180. package/dist/apps/relay/Sponsorships.js +193 -0
  181. package/dist/apps/relay/Sponsorships.js.map +1 -0
  182. package/dist/apps/relay/index.d.ts +3 -0
  183. package/dist/apps/relay/index.d.ts.map +1 -0
  184. package/dist/apps/relay/index.js +3 -0
  185. package/dist/apps/relay/index.js.map +1 -0
  186. package/dist/{Cli.d.ts → cli/Cli.d.ts} +13 -2
  187. package/dist/cli/Cli.d.ts.map +1 -0
  188. package/dist/cli/Cli.js +19 -0
  189. package/dist/cli/Cli.js.map +1 -0
  190. package/dist/cli/bin.d.ts +3 -0
  191. package/dist/cli/bin.d.ts.map +1 -0
  192. package/dist/cli/bin.js +4 -0
  193. package/dist/cli/bin.js.map +1 -0
  194. package/dist/cli/commands/admin.d.ts +15 -0
  195. package/dist/cli/commands/admin.d.ts.map +1 -0
  196. package/dist/cli/commands/admin.js +60 -0
  197. package/dist/cli/commands/admin.js.map +1 -0
  198. package/dist/cloudflare.d.ts +7 -0
  199. package/dist/cloudflare.d.ts.map +1 -1
  200. package/dist/cloudflare.js +9 -0
  201. package/dist/cloudflare.js.map +1 -1
  202. package/dist/db/Db.d.ts +113 -0
  203. package/dist/db/Db.d.ts.map +1 -0
  204. package/dist/db/Db.js +402 -0
  205. package/dist/db/Db.js.map +1 -0
  206. package/dist/db/Schema.d.ts +168 -0
  207. package/dist/db/Schema.d.ts.map +1 -0
  208. package/dist/db/Schema.js +229 -0
  209. package/dist/db/Schema.js.map +1 -0
  210. package/dist/db/tables/invitations.d.ts +84 -0
  211. package/dist/db/tables/invitations.d.ts.map +1 -0
  212. package/dist/db/tables/invitations.js +165 -0
  213. package/dist/db/tables/invitations.js.map +1 -0
  214. package/dist/db/tables/memberships.d.ts +108 -0
  215. package/dist/db/tables/memberships.d.ts.map +1 -0
  216. package/dist/db/tables/memberships.js +153 -0
  217. package/dist/db/tables/memberships.js.map +1 -0
  218. package/dist/db/tables/organizations.d.ts +123 -0
  219. package/dist/db/tables/organizations.d.ts.map +1 -0
  220. package/dist/db/tables/organizations.js +170 -0
  221. package/dist/db/tables/organizations.js.map +1 -0
  222. package/dist/db/tables/projects.d.ts +65 -0
  223. package/dist/db/tables/projects.d.ts.map +1 -0
  224. package/dist/db/tables/projects.js +75 -0
  225. package/dist/db/tables/projects.js.map +1 -0
  226. package/dist/db/tables/sponsoredTransactions.d.ts +120 -0
  227. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -0
  228. package/dist/db/tables/sponsoredTransactions.js +161 -0
  229. package/dist/db/tables/sponsoredTransactions.js.map +1 -0
  230. package/dist/db/tables/users.d.ts +48 -0
  231. package/dist/db/tables/users.d.ts.map +1 -0
  232. package/dist/db/tables/users.js +65 -0
  233. package/dist/db/tables/users.js.map +1 -0
  234. package/dist/db/tables/verifiedTokens.d.ts +53 -0
  235. package/dist/db/tables/verifiedTokens.d.ts.map +1 -0
  236. package/dist/db/tables/verifiedTokens.js +74 -0
  237. package/dist/db/tables/verifiedTokens.js.map +1 -0
  238. package/dist/db/tables/webhookDeliveries.d.ts +71 -0
  239. package/dist/db/tables/webhookDeliveries.d.ts.map +1 -0
  240. package/dist/db/tables/webhookDeliveries.js +119 -0
  241. package/dist/db/tables/webhookDeliveries.js.map +1 -0
  242. package/dist/db/tables/webhookSubscriptions.d.ts +158 -0
  243. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -0
  244. package/dist/db/tables/webhookSubscriptions.js +272 -0
  245. package/dist/db/tables/webhookSubscriptions.js.map +1 -0
  246. package/dist/handlers/internal/executionError.d.ts +25 -0
  247. package/dist/handlers/internal/executionError.d.ts.map +1 -0
  248. package/dist/handlers/internal/executionError.js +186 -0
  249. package/dist/handlers/internal/executionError.js.map +1 -0
  250. package/dist/handlers/internal/multisig.d.ts +233 -0
  251. package/dist/handlers/internal/multisig.d.ts.map +1 -0
  252. package/dist/handlers/internal/multisig.js +542 -0
  253. package/dist/handlers/internal/multisig.js.map +1 -0
  254. package/dist/handlers/internal/requestListener.d.ts +124 -0
  255. package/dist/handlers/internal/requestListener.d.ts.map +1 -0
  256. package/dist/handlers/internal/requestListener.js +173 -0
  257. package/dist/handlers/internal/requestListener.js.map +1 -0
  258. package/dist/handlers/internal/sponsorship.d.ts +113 -0
  259. package/dist/handlers/internal/sponsorship.d.ts.map +1 -0
  260. package/dist/handlers/internal/sponsorship.js +128 -0
  261. package/dist/handlers/internal/sponsorship.js.map +1 -0
  262. package/dist/handlers/internal/types.d.ts +284 -0
  263. package/dist/handlers/internal/types.d.ts.map +1 -0
  264. package/dist/handlers/internal/types.js +2 -0
  265. package/dist/handlers/internal/types.js.map +1 -0
  266. package/dist/handlers/internal/utils.d.ts +34 -0
  267. package/dist/handlers/internal/utils.d.ts.map +1 -0
  268. package/dist/handlers/internal/utils.js +131 -0
  269. package/dist/handlers/internal/utils.js.map +1 -0
  270. package/dist/handlers/relay.d.ts +123 -0
  271. package/dist/handlers/relay.d.ts.map +1 -0
  272. package/dist/handlers/relay.js +1278 -0
  273. package/dist/handlers/relay.js.map +1 -0
  274. package/dist/index.d.ts +4 -2
  275. package/dist/index.d.ts.map +1 -1
  276. package/dist/index.js +4 -2
  277. package/dist/index.js.map +1 -1
  278. package/dist/internal/Auth.d.ts +224 -16
  279. package/dist/internal/Auth.d.ts.map +1 -1
  280. package/dist/internal/Auth.js +572 -33
  281. package/dist/internal/Auth.js.map +1 -1
  282. package/dist/internal/Log.d.ts +7 -3
  283. package/dist/internal/Log.d.ts.map +1 -1
  284. package/dist/internal/Log.js +4 -0
  285. package/dist/internal/Log.js.map +1 -1
  286. package/dist/internal/OpenApi.d.ts +0 -19
  287. package/dist/internal/OpenApi.d.ts.map +1 -1
  288. package/dist/internal/OpenApi.js +0 -57
  289. package/dist/internal/OpenApi.js.map +1 -1
  290. package/dist/internal/RpcSchema.d.ts +10 -0
  291. package/dist/internal/RpcSchema.d.ts.map +1 -0
  292. package/dist/internal/RpcSchema.js +2 -0
  293. package/dist/internal/RpcSchema.js.map +1 -0
  294. package/dist/internal/Store.d.ts +49 -4
  295. package/dist/internal/Store.d.ts.map +1 -1
  296. package/dist/internal/Store.js +81 -5
  297. package/dist/internal/Store.js.map +1 -1
  298. package/dist/internal/VerifiedTokens.d.ts +15 -34
  299. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  300. package/dist/internal/VerifiedTokens.js +115 -159
  301. package/dist/internal/VerifiedTokens.js.map +1 -1
  302. package/dist/internal/Viem.d.ts +84 -0
  303. package/dist/internal/Viem.d.ts.map +1 -1
  304. package/dist/internal/Webhooks.d.ts +50 -36
  305. package/dist/internal/Webhooks.d.ts.map +1 -1
  306. package/dist/internal/Webhooks.js +92 -205
  307. package/dist/internal/Webhooks.js.map +1 -1
  308. package/package.json +37 -6
  309. package/src/ApiKey.test.ts +3 -3
  310. package/src/ApiKey.ts +11 -12
  311. package/src/ApiKeys.test.ts +278 -0
  312. package/src/ApiKeys.ts +358 -0
  313. package/src/App.test-d.ts +1 -1
  314. package/src/App.test.ts +40508 -19083
  315. package/src/App.ts +239 -416
  316. package/src/Client.test-d.ts +307 -16
  317. package/src/Handler.ts +154 -0
  318. package/src/Scope.ts +17 -4
  319. package/src/Webhooks.test.ts +8 -7
  320. package/src/Webhooks.ts +13 -7
  321. package/src/admin/Access.ts +1 -3
  322. package/src/admin/App.ts +19 -24
  323. package/src/admin/apps/api-keys.test.ts +70 -18
  324. package/src/admin/apps/api-keys.ts +92 -9
  325. package/src/admin/apps/scopes.test.ts +8 -0
  326. package/src/admin/apps/verified-tokens.test.ts +3 -30
  327. package/src/admin/apps/verified-tokens.ts +18 -37
  328. package/src/admin/ui.generated.ts +1 -1
  329. package/src/apps/composition.test.ts +66 -0
  330. package/src/apps/data/App.ts +295 -0
  331. package/src/apps/data/index.ts +2 -0
  332. package/src/apps/{activities.test.ts → data/routes/activities.test.ts} +3 -3
  333. package/src/apps/{activities.ts → data/routes/activities.ts} +15 -15
  334. package/src/apps/{addresses.ts → data/routes/addresses.ts} +6 -6
  335. package/src/apps/{balances.test.ts → data/routes/balances.test.ts} +6 -5
  336. package/src/apps/{balances.ts → data/routes/balances.ts} +46 -12
  337. package/src/apps/{blocks.test.ts → data/routes/blocks.test.ts} +4 -4
  338. package/src/apps/{blocks.ts → data/routes/blocks.ts} +14 -14
  339. package/src/apps/{coingecko.test.ts → data/routes/coingecko.test.ts} +2 -2
  340. package/src/apps/{coingecko.ts → data/routes/coingecko.ts} +14 -14
  341. package/src/apps/{exchanges.test.ts → data/routes/exchanges.test.ts} +2 -2
  342. package/src/apps/{exchanges.ts → data/routes/exchanges.ts} +20 -20
  343. package/src/apps/{fee-amm.test.ts → data/routes/fee-amm.test.ts} +3 -3
  344. package/src/apps/{fee-amm.ts → data/routes/fee-amm.ts} +18 -18
  345. package/src/apps/{indexer.test.ts → data/routes/indexer.test.ts} +27 -6
  346. package/src/apps/{indexer.ts → data/routes/indexer.ts} +13 -10
  347. package/src/apps/{receipts.test.ts → data/routes/receipts.test.ts} +4 -4
  348. package/src/apps/{receipts.ts → data/routes/receipts.ts} +13 -13
  349. package/src/apps/{rpc.test.ts → data/routes/rpc.test.ts} +142 -13
  350. package/src/apps/{rpc.ts → data/routes/rpc.ts} +7 -7
  351. package/src/apps/{tokenlist.test.ts → data/routes/tokenlist.test.ts} +8 -10
  352. package/src/apps/{tokenlist.ts → data/routes/tokenlist.ts} +9 -9
  353. package/src/apps/{tokens.test.ts → data/routes/tokens.test.ts} +6 -5
  354. package/src/apps/{tokens.ts → data/routes/tokens.ts} +20 -20
  355. package/src/apps/{transactions.test.ts → data/routes/transactions.test.ts} +4 -4
  356. package/src/apps/{transactions.ts → data/routes/transactions.ts} +15 -15
  357. package/src/apps/{transfers.test.ts → data/routes/transfers.test.ts} +3 -3
  358. package/src/apps/{transfers.ts → data/routes/transfers.ts} +15 -15
  359. package/src/apps/{verified-tokens.test.ts → data/routes/verified-tokens.test.ts} +27 -15
  360. package/src/apps/{verified-tokens.ts → data/routes/verified-tokens.ts} +10 -10
  361. package/src/apps/{webhooks.test.ts → data/routes/webhooks.test.ts} +137 -150
  362. package/src/apps/{webhooks.ts → data/routes/webhooks.ts} +83 -81
  363. package/src/apps/docs/App.ts +136 -0
  364. package/src/apps/docs/index.ts +1 -0
  365. package/src/apps/index.ts +3 -0
  366. package/src/apps/metadata.ts +23 -0
  367. package/src/apps/platform/App.ts +130 -0
  368. package/src/apps/platform/index.ts +1 -0
  369. package/src/apps/platform/routes/api-keys.test.ts +330 -0
  370. package/src/apps/platform/routes/api-keys.ts +427 -0
  371. package/src/apps/platform/routes/invitations.test.ts +361 -0
  372. package/src/apps/platform/routes/invitations.ts +519 -0
  373. package/src/apps/platform/routes/me.test.ts +121 -0
  374. package/src/apps/platform/routes/me.ts +106 -0
  375. package/src/apps/platform/routes/members.test.ts +195 -0
  376. package/src/apps/platform/routes/members.ts +313 -0
  377. package/src/apps/platform/routes/orgs.test.ts +313 -0
  378. package/src/apps/platform/routes/orgs.ts +343 -0
  379. package/src/apps/platform/routes/projects.test.ts +181 -0
  380. package/src/apps/platform/routes/projects.ts +367 -0
  381. package/src/apps/relay/App.test.ts +484 -0
  382. package/src/apps/relay/App.ts +128 -0
  383. package/src/apps/relay/Sponsorships.test.ts +375 -0
  384. package/src/apps/relay/Sponsorships.ts +305 -0
  385. package/src/apps/relay/index.ts +2 -0
  386. package/src/{Cli.test.ts → cli/Cli.test.ts} +10 -9
  387. package/src/{Cli.ts → cli/Cli.ts} +16 -9
  388. package/src/cli/bin.ts +4 -0
  389. package/src/cli/commands/admin.test.ts +71 -0
  390. package/src/cli/commands/admin.ts +60 -0
  391. package/src/cloudflare.ts +10 -0
  392. package/src/db/Db.test.ts +84 -0
  393. package/src/db/Db.ts +542 -0
  394. package/src/db/Schema.ts +301 -0
  395. package/src/db/tables/invitations.ts +198 -0
  396. package/src/db/tables/memberships.ts +208 -0
  397. package/src/db/tables/organizations.test.ts +160 -0
  398. package/src/db/tables/organizations.ts +232 -0
  399. package/src/db/tables/projects.ts +107 -0
  400. package/src/db/tables/sponsoredTransactions.test.ts +218 -0
  401. package/src/db/tables/sponsoredTransactions.ts +236 -0
  402. package/src/db/tables/users.ts +85 -0
  403. package/src/db/tables/verifiedTokens.ts +111 -0
  404. package/src/db/tables/webhookDeliveries.ts +159 -0
  405. package/src/db/tables/webhookSubscriptions.ts +358 -0
  406. package/src/handlers/internal/executionError.ts +207 -0
  407. package/src/handlers/internal/multisig.test.ts +818 -0
  408. package/src/handlers/internal/multisig.ts +823 -0
  409. package/src/handlers/internal/requestListener.ts +273 -0
  410. package/src/handlers/internal/sponsorship.ts +245 -0
  411. package/src/handlers/internal/types.ts +378 -0
  412. package/src/handlers/internal/utils.ts +153 -0
  413. package/src/handlers/relay.test.ts +2035 -0
  414. package/src/handlers/relay.ts +1623 -0
  415. package/src/index.ts +4 -2
  416. package/src/internal/Auth.test.ts +337 -21
  417. package/src/internal/Auth.ts +783 -44
  418. package/src/internal/Cursor.test.ts +3 -2
  419. package/src/internal/Log.test.ts +1 -1
  420. package/src/internal/Log.ts +11 -3
  421. package/src/internal/MetricSink.ts +1 -1
  422. package/src/internal/OpenApi.ts +0 -59
  423. package/src/internal/RateLimit.test.ts +1 -1
  424. package/src/internal/RpcSchema.ts +10 -0
  425. package/src/internal/Store.test.ts +150 -10
  426. package/src/internal/Store.ts +136 -11
  427. package/src/internal/Tidx.local.test.ts +1 -1
  428. package/src/internal/VerifiedTokens.test.ts +99 -115
  429. package/src/internal/VerifiedTokens.ts +123 -175
  430. package/src/internal/WebhookDestination.test.ts +1 -1
  431. package/src/internal/Webhooks.test.ts +111 -111
  432. package/src/internal/Webhooks.ts +103 -221
  433. package/dist/ApiKeySource.d.ts +0 -166
  434. package/dist/ApiKeySource.d.ts.map +0 -1
  435. package/dist/ApiKeySource.js +0 -234
  436. package/dist/ApiKeySource.js.map +0 -1
  437. package/dist/Cli.d.ts.map +0 -1
  438. package/dist/Cli.js +0 -13
  439. package/dist/Cli.js.map +0 -1
  440. package/dist/apps/activities.js.map +0 -1
  441. package/dist/apps/addresses.js.map +0 -1
  442. package/dist/apps/balances.d.ts.map +0 -1
  443. package/dist/apps/balances.js.map +0 -1
  444. package/dist/apps/blocks.d.ts.map +0 -1
  445. package/dist/apps/blocks.js.map +0 -1
  446. package/dist/apps/coingecko.d.ts.map +0 -1
  447. package/dist/apps/coingecko.js.map +0 -1
  448. package/dist/apps/exchanges.d.ts.map +0 -1
  449. package/dist/apps/exchanges.js.map +0 -1
  450. package/dist/apps/fee-amm.d.ts.map +0 -1
  451. package/dist/apps/fee-amm.js.map +0 -1
  452. package/dist/apps/indexer.d.ts.map +0 -1
  453. package/dist/apps/indexer.js.map +0 -1
  454. package/dist/apps/receipts.d.ts.map +0 -1
  455. package/dist/apps/receipts.js.map +0 -1
  456. package/dist/apps/rpc.d.ts.map +0 -1
  457. package/dist/apps/rpc.js.map +0 -1
  458. package/dist/apps/tokenlist.d.ts.map +0 -1
  459. package/dist/apps/tokenlist.js.map +0 -1
  460. package/dist/apps/tokens.d.ts.map +0 -1
  461. package/dist/apps/tokens.js.map +0 -1
  462. package/dist/apps/transactions.d.ts.map +0 -1
  463. package/dist/apps/transactions.js.map +0 -1
  464. package/dist/apps/transfers.d.ts.map +0 -1
  465. package/dist/apps/transfers.js.map +0 -1
  466. package/dist/apps/verified-tokens.d.ts.map +0 -1
  467. package/dist/apps/verified-tokens.js.map +0 -1
  468. package/dist/apps/webhooks.js.map +0 -1
  469. package/dist/internal/OpenApi.fonts.d.ts.map +0 -1
  470. package/dist/internal/OpenApi.fonts.js.map +0 -1
  471. package/dist/internal/OpenApi.logo.d.ts.map +0 -1
  472. package/dist/internal/OpenApi.logo.js.map +0 -1
  473. package/seeds/31318/icons/0x20c0000000000000000000000000000000000000.svg +0 -1
  474. package/seeds/31318/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  475. package/seeds/31318/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  476. package/seeds/31318/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  477. package/seeds/31318/verified-tokens.json +0 -30
  478. package/seeds/4217/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  479. package/seeds/4217/icons/0x20c000000000000000000000048c8f36df1c9a4a.svg +0 -1
  480. package/seeds/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg +0 -1
  481. package/seeds/4217/icons/0x20c0000000000000000000000a6da882d075a4c3.svg +0 -4
  482. package/seeds/4217/icons/0x20c000000000000000000000111111111e910f0f.svg +0 -30
  483. package/seeds/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg +0 -1
  484. package/seeds/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg +0 -1
  485. package/seeds/4217/icons/0x20c0000000000000000000002f52d5cc21a3207b.svg +0 -1
  486. package/seeds/4217/icons/0x20c0000000000000000000003158081efd85bfc2.svg +0 -11
  487. package/seeds/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg +0 -1
  488. package/seeds/4217/icons/0x20c000000000000000000000383a23bacb546ab9.svg +0 -1
  489. package/seeds/4217/icons/0x20c00000000000000000000042109aef2f8b28e1.png +0 -0
  490. package/seeds/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg +0 -1
  491. package/seeds/4217/icons/0x20c0000000000000000000006fd9a167923ba194.png +0 -0
  492. package/seeds/4217/icons/0x20c0000000000000000000007f7ba549dd0251b9.svg +0 -1
  493. package/seeds/4217/icons/0x20c0000000000000000000008191667423f70e67.svg +0 -5
  494. package/seeds/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg +0 -1
  495. package/seeds/4217/icons/0x20c0000000000000000000009a4a4b17e0dc6651.svg +0 -1
  496. package/seeds/4217/icons/0x20c000000000000000000000ab02d39df30bd17e.svg +0 -1
  497. package/seeds/4217/icons/0x20c000000000000000000000ae247a1130450f09.svg +0 -1
  498. package/seeds/4217/icons/0x20c000000000000000000000aeed2ec36a54d0e5.svg +0 -1
  499. package/seeds/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg +0 -1
  500. package/seeds/4217/icons/0x20c000000000000000000000bd95bfb69fbe6ce3.svg +0 -1
  501. package/seeds/4217/icons/0x20c000000000000000000000c412ec89d0c08be5.svg +0 -1
  502. package/seeds/4217/icons/0x20c000000000000000000000f047dd7018e50367.png +0 -0
  503. package/seeds/4217/verified-tokens.json +0 -177
  504. package/seeds/42431/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  505. package/seeds/42431/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  506. package/seeds/42431/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  507. package/seeds/42431/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  508. package/seeds/42431/icons/0x20c0000000000000000000009e8d7eb59b783726.svg +0 -1
  509. package/seeds/42431/icons/0x20c000000000000000000000d72572838bbee59c.svg +0 -1
  510. package/seeds/42431/verified-tokens.json +0 -44
  511. package/seeds/openrpc/README.md +0 -27
  512. package/seeds/openrpc/execution-apis.json +0 -8989
  513. package/src/ApiKeySource.test-d.ts +0 -11
  514. package/src/ApiKeySource.test.ts +0 -295
  515. package/src/ApiKeySource.ts +0 -303
  516. /package/src/{internal/OpenApi.fonts.ts → apps/docs/fonts.ts} +0 -0
  517. /package/src/{internal/OpenApi.logo.ts → apps/docs/logo.ts} +0 -0
@@ -0,0 +1,375 @@
1
+ import { nanoid } from 'nanoid'
2
+ import { TxEnvelopeTempo } from 'ox/tempo'
3
+ import type { Client } from 'viem'
4
+
5
+ import { postgresUrl } from '../../../test/containers.js'
6
+ import * as Db from '../../db/Db.js'
7
+ import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js'
8
+ import * as Sponsorships from './Sponsorships.js'
9
+
10
+ const create = () => Db.postgres({ connectionString: postgresUrl, schema: `t_${nanoid()}` })
11
+
12
+ /** Baseline insert input; tests override per case. */
13
+ const input = {
14
+ apiKeyId: 'key_1',
15
+ billable: true,
16
+ chainId: 42431,
17
+ environment: 'production',
18
+ orgId: 'org_1',
19
+ projectId: 'prj_1',
20
+ signPayload: `0x${'bb'.repeat(32)}`,
21
+ transaction: `0x76${'cc'.repeat(16)}`,
22
+ transactionHash: `0x${'aa'.repeat(32)}`,
23
+ } satisfies SponsoredTransactions.upsert.Input
24
+
25
+ /** Stubs the receipt lookup: hashes present in `receipts` resolve, others null. */
26
+ function client(receipts: Record<string, { effectiveGasPrice: string; gasUsed: string }>): Client {
27
+ return {
28
+ request: async ({ params }: { params: readonly [string] }) => receipts[params[0]] ?? null,
29
+ } as never
30
+ }
31
+
32
+ describe('finalize', () => {
33
+ test('behavior: finalizes mined rows with gasUsed × effectiveGasPrice', async () => {
34
+ const db = create()
35
+ await db.migrate()
36
+ const record = await SponsoredTransactions.upsert(db, input)
37
+
38
+ const result = await Sponsorships.finalize(db, {
39
+ getClient: () =>
40
+ client({ [input.transactionHash]: { effectiveGasPrice: '0x3', gasUsed: '0x5208' } }),
41
+ })
42
+ expect(result).toMatchInlineSnapshot(`
43
+ {
44
+ "failed": 0,
45
+ "finalized": 1,
46
+ "pending": 0,
47
+ }
48
+ `)
49
+ const finalized = await SponsoredTransactions.get(db, record.id)
50
+ expect(finalized?.status).toMatchInlineSnapshot(`"finalized"`)
51
+ // 21000 gas × 3 = 63000 base units.
52
+ expect(finalized?.feeAmount).toMatchInlineSnapshot(`"63000"`)
53
+ expect(finalized?.finalizedAt).not.toBeNull()
54
+ await db.close()
55
+ })
56
+
57
+ test('behavior: leaves young unmined rows pending', async () => {
58
+ const db = create()
59
+ await db.migrate()
60
+ const record = await SponsoredTransactions.upsert(db, input)
61
+
62
+ const result = await Sponsorships.finalize(db, { getClient: () => client({}) })
63
+ expect(result).toMatchInlineSnapshot(`
64
+ {
65
+ "failed": 0,
66
+ "finalized": 0,
67
+ "pending": 1,
68
+ }
69
+ `)
70
+ expect((await SponsoredTransactions.get(db, record.id))?.status).toMatchInlineSnapshot(
71
+ `"pending"`,
72
+ )
73
+ await db.close()
74
+ })
75
+
76
+ test('behavior: hash-less fill intents settle by TTL without a receipt lookup', async () => {
77
+ const db = create()
78
+ await db.migrate()
79
+ const { transactionHash: _, ...intent } = input
80
+ const record = await SponsoredTransactions.upsert(db, intent)
81
+
82
+ // Young intent: left pending; the stub client would throw if queried.
83
+ const young = await Sponsorships.finalize(db, {
84
+ getClient: () => {
85
+ throw new Error('unexpected receipt lookup for a hash-less intent')
86
+ },
87
+ })
88
+ expect(young).toMatchInlineSnapshot(`
89
+ {
90
+ "failed": 0,
91
+ "finalized": 0,
92
+ "pending": 1,
93
+ }
94
+ `)
95
+
96
+ // Past the TTL: fails with no fee.
97
+ const settled = await Sponsorships.finalize(db, {
98
+ getClient: () => {
99
+ throw new Error('unexpected receipt lookup for a hash-less intent')
100
+ },
101
+ now: () => new Date(Date.now() + 2 * 3_600_000),
102
+ })
103
+ expect(settled).toMatchInlineSnapshot(`
104
+ {
105
+ "failed": 1,
106
+ "finalized": 0,
107
+ "pending": 0,
108
+ }
109
+ `)
110
+ expect((await SponsoredTransactions.get(db, record.id))?.status).toMatchInlineSnapshot(
111
+ `"failed"`,
112
+ )
113
+ await db.close()
114
+ })
115
+
116
+ test('behavior: fails unmined rows past the pending TTL', async () => {
117
+ const db = create()
118
+ await db.migrate()
119
+ const record = await SponsoredTransactions.upsert(db, input)
120
+
121
+ const result = await Sponsorships.finalize(db, {
122
+ getClient: () => client({}),
123
+ now: () => new Date(Date.now() + 2 * 3_600_000),
124
+ })
125
+ expect(result).toMatchInlineSnapshot(`
126
+ {
127
+ "failed": 1,
128
+ "finalized": 0,
129
+ "pending": 0,
130
+ }
131
+ `)
132
+ const failed = await SponsoredTransactions.get(db, record.id)
133
+ expect(failed?.status).toMatchInlineSnapshot(`"failed"`)
134
+ expect(failed?.feeAmount).toBeNull()
135
+ await db.close()
136
+ })
137
+
138
+ test('behavior: an erroring receipt lookup leaves the row pending, even past the TTL', async () => {
139
+ const db = create()
140
+ await db.migrate()
141
+ const record = await SponsoredTransactions.upsert(db, input)
142
+
143
+ const result = await Sponsorships.finalize(db, {
144
+ getClient: () =>
145
+ ({
146
+ request: async () => {
147
+ throw new Error('rpc outage')
148
+ },
149
+ }) as never,
150
+ now: () => new Date(Date.now() + 2 * 3_600_000),
151
+ })
152
+ expect(result).toMatchInlineSnapshot(`
153
+ {
154
+ "failed": 0,
155
+ "finalized": 0,
156
+ "pending": 1,
157
+ }
158
+ `)
159
+ expect((await SponsoredTransactions.get(db, record.id))?.status).toMatchInlineSnapshot(
160
+ `"pending"`,
161
+ )
162
+ await db.close()
163
+ })
164
+
165
+ test('behavior: an erroring receipt lookup fails past the error TTL', async () => {
166
+ const db = create()
167
+ await db.migrate()
168
+ const record = await SponsoredTransactions.upsert(db, input)
169
+
170
+ const result = await Sponsorships.finalize(db, {
171
+ getClient: () =>
172
+ ({
173
+ request: async () => {
174
+ throw new Error('rpc outage')
175
+ },
176
+ }) as never,
177
+ now: () => new Date(Date.now() + 25 * 3_600_000),
178
+ })
179
+ expect(result).toMatchInlineSnapshot(`
180
+ {
181
+ "failed": 1,
182
+ "finalized": 0,
183
+ "pending": 0,
184
+ }
185
+ `)
186
+ expect((await SponsoredTransactions.get(db, record.id))?.status).toMatchInlineSnapshot(
187
+ `"failed"`,
188
+ )
189
+ await db.close()
190
+ })
191
+
192
+ test('behavior: batchSize caps rows processed per pass', async () => {
193
+ const db = create()
194
+ await db.migrate()
195
+ await SponsoredTransactions.upsert(db, { ...input, signPayload: `0x${'b1'.repeat(32)}`, transactionHash: `0x${'a1'.repeat(32)}` }) // prettier-ignore
196
+ await SponsoredTransactions.upsert(db, { ...input, signPayload: `0x${'b2'.repeat(32)}`, transactionHash: `0x${'a2'.repeat(32)}` }) // prettier-ignore
197
+
198
+ // Both hashes are mined so the outcome is order-independent.
199
+ const receipts = {
200
+ [`0x${'a1'.repeat(32)}`]: { effectiveGasPrice: '0x1', gasUsed: '0x1' },
201
+ [`0x${'a2'.repeat(32)}`]: { effectiveGasPrice: '0x1', gasUsed: '0x1' },
202
+ }
203
+ const result = await Sponsorships.finalize(db, {
204
+ batchSize: 1,
205
+ getClient: () => client(receipts),
206
+ })
207
+ expect(result).toMatchInlineSnapshot(`
208
+ {
209
+ "failed": 0,
210
+ "finalized": 1,
211
+ "pending": 0,
212
+ }
213
+ `)
214
+ expect(await SponsoredTransactions.listPending(db)).toHaveLength(1)
215
+ await db.close()
216
+ })
217
+ })
218
+
219
+ describe('reconcile', () => {
220
+ const sender = '0x407368320192a21238825437c02c2e2a34ffc38d'
221
+ const minedHash = `0x${'dd'.repeat(32)}`
222
+
223
+ /** The mined sponsored envelope, in ox form. */
224
+ const mined = TxEnvelopeTempo.from({
225
+ calls: [{ data: '0x', to: '0x20c0000000000000000000000000000000000001', value: 0n }],
226
+ chainId: 42431,
227
+ gas: 100_000n,
228
+ maxFeePerGas: 1_000_000n,
229
+ maxPriorityFeePerGas: 0n,
230
+ nonce: 0n,
231
+ nonceKey: 0n,
232
+ })
233
+
234
+ /** The intent's identity, computed exactly as the recording sites do. */
235
+ const signPayload = TxEnvelopeTempo.getFeePayerSignPayload(mined, { sender })
236
+
237
+ /** Fakes the node: `eth_getRawTransactionByHash` serves the mined bytes. */
238
+ const rpc = {
239
+ request: async () => TxEnvelopeTempo.serialize(mined),
240
+ } as never as Client
241
+
242
+ /** Fakes the indexer: the fee payer's recent transactions. */
243
+ const tidx = (rows: readonly { from: string; hash: string }[]) =>
244
+ ({ fetch: async () => ({ rows }) }) as never
245
+
246
+ test('behavior: matches intents by sign payload and fills the hash', async () => {
247
+ const db = create()
248
+ await db.migrate()
249
+ const { transactionHash: _, ...rest } = input
250
+ const intent = await SponsoredTransactions.upsert(db, { ...rest, signPayload })
251
+
252
+ const result = await Sponsorships.reconcile(db, {
253
+ feePayer: '0xFee0000000000000000000000000000000000000',
254
+ getClient: () => rpc,
255
+ getTidx: () => tidx([{ from: sender, hash: minedHash }]),
256
+ })
257
+ expect(result).toMatchInlineSnapshot(`
258
+ {
259
+ "matched": 1,
260
+ "scanned": 1,
261
+ }
262
+ `)
263
+ const matched = await SponsoredTransactions.get(db, intent.id)
264
+ expect(matched?.transactionHash).toBe(minedHash)
265
+ expect(matched?.status).toMatchInlineSnapshot(`"pending"`)
266
+ await db.close()
267
+ })
268
+
269
+ test('behavior: recovers recently failed intents by sign payload', async () => {
270
+ const db = create()
271
+ await db.migrate()
272
+ const { transactionHash: _, ...rest } = input
273
+ const intent = await SponsoredTransactions.upsert(db, { ...rest, signPayload })
274
+ await SponsoredTransactions.fail(db, intent.id, '2026-01-01T00:00:00.000Z')
275
+
276
+ vi.useFakeTimers({ now: new Date('2026-01-01T00:01:00.000Z') })
277
+ const result = await (async () => {
278
+ try {
279
+ return await Sponsorships.reconcile(db, {
280
+ feePayer: '0xFee0000000000000000000000000000000000000',
281
+ getClient: () => rpc,
282
+ getTidx: () => tidx([{ from: sender, hash: minedHash }]),
283
+ })
284
+ } finally {
285
+ vi.useRealTimers()
286
+ }
287
+ })()
288
+ expect(result).toMatchInlineSnapshot(`
289
+ {
290
+ "matched": 1,
291
+ "scanned": 1,
292
+ }
293
+ `)
294
+ const matched = await SponsoredTransactions.get(db, intent.id)
295
+ expect(matched?.transactionHash).toBe(minedHash)
296
+ expect(matched?.status).toMatchInlineSnapshot(`"pending"`)
297
+ expect(matched?.finalizedAt).toBeNull()
298
+ await db.close()
299
+ })
300
+
301
+ test('behavior: unmatched candidates leave intents pending', async () => {
302
+ const db = create()
303
+ await db.migrate()
304
+ const { transactionHash: _, ...rest } = input
305
+ const intent = await SponsoredTransactions.upsert(db, { ...rest, signPayload: `0x${'ee'.repeat(32)}` }) // prettier-ignore
306
+
307
+ const result = await Sponsorships.reconcile(db, {
308
+ feePayer: '0xFee0000000000000000000000000000000000000',
309
+ getClient: () => rpc,
310
+ getTidx: () => tidx([{ from: sender, hash: minedHash }]),
311
+ })
312
+ expect(result).toMatchInlineSnapshot(`
313
+ {
314
+ "matched": 0,
315
+ "scanned": 1,
316
+ }
317
+ `)
318
+ expect((await SponsoredTransactions.get(db, intent.id))?.transactionHash).toBeNull()
319
+ await db.close()
320
+ })
321
+
322
+ test('behavior: queries on-chain candidates oldest first', async () => {
323
+ const db = create()
324
+ await db.migrate()
325
+ const { transactionHash: _, ...rest } = input
326
+ await SponsoredTransactions.upsert(db, { ...rest, signPayload })
327
+ let query: string | undefined
328
+
329
+ await Sponsorships.reconcile(db, {
330
+ candidateCap: 17,
331
+ feePayer: '0xFee0000000000000000000000000000000000000',
332
+ getClient: () => rpc,
333
+ getTidx: () =>
334
+ ({
335
+ fetch: async (options: { query: string }) => {
336
+ query = options.query
337
+ return { rows: [] }
338
+ },
339
+ }) as never,
340
+ })
341
+
342
+ expect(
343
+ query?.replace(/block_timestamp >= '[^']+'/, "block_timestamp >= '<since>'"),
344
+ ).toMatchInlineSnapshot(
345
+ `"SELECT hash, "from" FROM txs WHERE fee_payer = '0xfee0000000000000000000000000000000000000' AND block_timestamp >= '<since>' ORDER BY block_timestamp ASC LIMIT 17"`,
346
+ )
347
+ await db.close()
348
+ })
349
+
350
+ test('behavior: an indexer error leaves intents for the next pass', async () => {
351
+ const db = create()
352
+ await db.migrate()
353
+ const { transactionHash: _, ...rest } = input
354
+ await SponsoredTransactions.upsert(db, { ...rest, signPayload })
355
+
356
+ const result = await Sponsorships.reconcile(db, {
357
+ feePayer: '0xFee0000000000000000000000000000000000000',
358
+ getClient: () => rpc,
359
+ getTidx: () =>
360
+ ({
361
+ fetch: async () => {
362
+ throw new Error('indexer outage')
363
+ },
364
+ }) as never,
365
+ })
366
+ expect(result).toMatchInlineSnapshot(`
367
+ {
368
+ "matched": 0,
369
+ "scanned": 0,
370
+ }
371
+ `)
372
+ expect(await SponsoredTransactions.listIntents(db)).toHaveLength(1)
373
+ await db.close()
374
+ })
375
+ })
@@ -0,0 +1,305 @@
1
+ import type { Hex } from 'ox'
2
+ import { TxEnvelopeTempo } from 'ox/tempo'
3
+ import {
4
+ type Address,
5
+ type Client,
6
+ type EIP1193RequestFn,
7
+ type TransactionReceipt,
8
+ TransactionReceiptNotFoundError,
9
+ } from 'viem'
10
+ import { getTransactionReceipt } from 'viem/actions'
11
+
12
+ import * as Db from '../../db/Db.js'
13
+ import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js'
14
+ import * as Tidx from '../../internal/Tidx.js'
15
+ import * as Viem from '../../internal/Viem.js'
16
+
17
+ /**
18
+ * Runs one finalization pass over pending sponsored transactions: rows with a
19
+ * receipt finalize with the actual fee (`gasUsed × effectiveGasPrice`, in
20
+ * fee-token base units); rows still unmined past the pending TTL fail, as do
21
+ * hash-less fill intents the TTL outlives. Rows whose receipt lookup errors
22
+ * persist past the error TTL fail so they cannot monopolize every batch.
23
+ *
24
+ * @param db - The database holding `sponsored_transactions`.
25
+ * @param options - Finalization options.
26
+ * @returns Counters for the pass.
27
+ */
28
+ export async function finalize(db: Db.Db, options: finalize.Options): Promise<finalize.Result> {
29
+ const {
30
+ batchSize = 100,
31
+ getClient,
32
+ now = () => new Date(),
33
+ pendingTtlMs = 3_600_000,
34
+ receiptErrorTtlMs = 86_400_000,
35
+ } = options
36
+
37
+ const rows = await SponsoredTransactions.listPending(db, { limit: batchSize })
38
+ const result = { failed: 0, finalized: 0, pending: 0 }
39
+
40
+ for (const row of rows) {
41
+ // Fill intents carry no hash until reconciliation matches them on-chain;
42
+ // here only the TTL can settle them.
43
+ if (row.transactionHash === null) {
44
+ if (Date.parse(row.createdAt) + pendingTtlMs <= now().getTime()) {
45
+ await SponsoredTransactions.fail(db, row.id, now().toISOString())
46
+ result.failed++
47
+ } else result.pending++
48
+ continue
49
+ }
50
+
51
+ let receipt: TransactionReceipt | null
52
+ try {
53
+ receipt = await getTransactionReceipt(getClient(row.chainId), {
54
+ hash: row.transactionHash as Hex.Hex,
55
+ })
56
+ } catch (error) {
57
+ // Unmined transactions surface as a typed not-found; let the TTL decide.
58
+ if (error instanceof TransactionReceiptNotFoundError) receipt = null
59
+ else {
60
+ if (Date.parse(row.createdAt) + receiptErrorTtlMs <= now().getTime()) {
61
+ await SponsoredTransactions.fail(db, row.id, now().toISOString())
62
+ result.failed++
63
+ } else result.pending++
64
+ continue
65
+ }
66
+ }
67
+
68
+ if (receipt) {
69
+ // A reverted transaction still finalizes: the fee payer paid its fees.
70
+ const feeAmount = receipt.gasUsed * receipt.effectiveGasPrice
71
+ await SponsoredTransactions.finalize(db, row.id, {
72
+ feeAmount: feeAmount.toString(),
73
+ finalizedAt: now().toISOString(),
74
+ })
75
+ result.finalized++
76
+ } else if (Date.parse(row.createdAt) + pendingTtlMs <= now().getTime()) {
77
+ await SponsoredTransactions.fail(db, row.id, now().toISOString())
78
+ result.failed++
79
+ } else result.pending++
80
+ }
81
+
82
+ return result
83
+ }
84
+
85
+ export declare namespace finalize {
86
+ /** Options for {@link finalize}. */
87
+ type Options = {
88
+ /** Maximum pending rows processed per pass. @default 100 */
89
+ batchSize?: number | undefined
90
+ /** Client resolver keyed by the row's chain id. */
91
+ getClient: (chainId: number) => Client
92
+ /** Clock; injectable for tests. */
93
+ now?: (() => Date) | undefined
94
+ /** Age after which an unmined sponsorship fails. @default 1 hour */
95
+ pendingTtlMs?: number | undefined
96
+ /** Age after which repeated receipt lookup errors fail a row. @default 24 hours */
97
+ receiptErrorTtlMs?: number | undefined
98
+ }
99
+
100
+ /** Counters returned by one finalization pass. */
101
+ type Result = {
102
+ /** Rows marked failed (unmined past the pending TTL). */
103
+ failed: number
104
+ /** Rows finalized with an actual fee. */
105
+ finalized: number
106
+ /** Rows left pending (unmined, or receipt lookup errored within the error TTL). */
107
+ pending: number
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Runs one reconciliation pass over pending fill intents (rows with no
113
+ * transaction hash): fetches the fee payer's recent on-chain transactions
114
+ * from TIDX, recomputes each candidate's fee-payer sign payload, and fills
115
+ * matching intents' hashes so the receipt pass can finalize them. Query or
116
+ * candidate errors leave intents for the next pass.
117
+ *
118
+ * @param db - The database holding `sponsored_transactions`.
119
+ * @param options - Reconciliation options.
120
+ * @returns Counters for the pass.
121
+ */
122
+ export async function reconcile(db: Db.Db, options: reconcile.Options): Promise<reconcile.Result> {
123
+ const {
124
+ batchSize = 100,
125
+ candidateCap = 500,
126
+ failedIntentTtlMs = 86_400_000,
127
+ feePayer,
128
+ getClient,
129
+ getTidx,
130
+ skewMs = 120_000,
131
+ } = options
132
+
133
+ const intents = await SponsoredTransactions.listIntents(db, {
134
+ failedSince: new Date(Date.now() - failedIntentTtlMs).toISOString(),
135
+ limit: batchSize,
136
+ })
137
+ const result = { matched: 0, scanned: 0 }
138
+ if (intents.length === 0) return result
139
+
140
+ const chains = new Map<number, SponsoredTransactions.Record[]>()
141
+ for (const intent of intents) chains.set(intent.chainId, [...(chains.get(intent.chainId) ?? []), intent]) // prettier-ignore
142
+
143
+ for (const [chainId, rows] of chains) {
144
+ const bySignPayload = new Map(rows.map((row) => [row.signPayload, row]))
145
+ // The intent set bounds the scan window: no cursor state to persist.
146
+ const since = new Date(
147
+ Math.min(...rows.map((row) => Date.parse(row.createdAt))) - skewMs,
148
+ ).toISOString()
149
+
150
+ let candidates: readonly globalThis.Record<string, unknown>[]
151
+ try {
152
+ // Dynamic filter values; the tidx client's query type wants literals.
153
+ const fetched = await getTidx(chainId).fetch({
154
+ chainId,
155
+ query:
156
+ `SELECT hash, "from" FROM txs WHERE fee_payer = '${feePayer.toLowerCase()}' AND block_timestamp >= '${since}' ORDER BY block_timestamp ASC LIMIT ${candidateCap}` as string,
157
+ })
158
+ candidates = fetched.rows
159
+ } catch {
160
+ // Indexer outage must not fail intents; retry on the next pass.
161
+ continue
162
+ }
163
+
164
+ for (const candidate of candidates) {
165
+ if (bySignPayload.size === 0) break
166
+ const hash = candidate['hash'] as Hex.Hex | undefined
167
+ const sender = candidate['from'] as Address | undefined
168
+ if (!hash || !sender) continue
169
+ result.scanned++
170
+ try {
171
+ const serialized = await getRawTransaction(getClient(chainId), hash)
172
+ if (!serialized?.startsWith(TxEnvelopeTempo.serializedType)) continue
173
+ const signPayload = TxEnvelopeTempo.getFeePayerSignPayload(
174
+ TxEnvelopeTempo.deserialize(serialized as TxEnvelopeTempo.Serialized),
175
+ { sender },
176
+ )
177
+ const intent = bySignPayload.get(signPayload)
178
+ if (!intent) continue
179
+ await SponsoredTransactions.assignTransactionHash(db, intent.id, hash)
180
+ bySignPayload.delete(signPayload)
181
+ result.matched++
182
+ } catch {
183
+ // Unresolvable candidate (lookup error, malformed envelope): skip it.
184
+ }
185
+ }
186
+ }
187
+
188
+ return result
189
+ }
190
+
191
+ export declare namespace reconcile {
192
+ /** Options for {@link reconcile}. */
193
+ type Options = {
194
+ /** Maximum intents considered per pass. @default 100 */
195
+ batchSize?: number | undefined
196
+ /** Maximum on-chain candidates fetched per chain per pass. @default 500 */
197
+ candidateCap?: number | undefined
198
+ /** How long failed intents remain reconciliation candidates. @default 24 hours */
199
+ failedIntentTtlMs?: number | undefined
200
+ /** Fee payer address whose on-chain transactions are candidates. */
201
+ feePayer: Address
202
+ /** Client resolver keyed by the intent's chain id. */
203
+ getClient: (chainId: number) => Client
204
+ /** TIDX client resolver keyed by the intent's chain id. */
205
+ getTidx: (chainId: number) => Tidx.Client
206
+ /** Lookback slack before the oldest intent's creation. @default 2 minutes */
207
+ skewMs?: number | undefined
208
+ }
209
+
210
+ /** Counters returned by one reconciliation pass. */
211
+ type Result = {
212
+ /** Intents matched to an on-chain transaction. */
213
+ matched: number
214
+ /** On-chain candidates inspected. */
215
+ scanned: number
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Fetches a mined transaction's canonical envelope bytes.
221
+ * `eth_getRawTransactionByHash` is a node extension absent from viem's typed
222
+ * RPC schema, hence the request-fn narrowing.
223
+ */
224
+ function getRawTransaction(client: Client, hash: Hex.Hex) {
225
+ type Request = EIP1193RequestFn<
226
+ [
227
+ {
228
+ Method: 'eth_getRawTransactionByHash'
229
+ Parameters: readonly [Hex.Hex]
230
+ ReturnType: Hex.Hex | null
231
+ },
232
+ ]
233
+ >
234
+ return (client.request as Request)({ method: 'eth_getRawTransactionByHash', params: [hash] })
235
+ }
236
+
237
+ /**
238
+ * Builds a scheduled sponsorship finalizer bound to a database and RPC config.
239
+ * A host runtime drives {@link createFinalizer.Finalizer.tick} on a timer (a
240
+ * Cloudflare `scheduled` cron handler, a self-host `setInterval`, …); each
241
+ * tick reconciles fill intents (when `feePayer` + `tidx` are configured) and
242
+ * runs one {@link finalize} pass, with memoized per-chain clients.
243
+ */
244
+ export function createFinalizer(options: createFinalizer.Options): createFinalizer.Finalizer {
245
+ const clients = new Map<number, Viem.getClient.ReturnType>()
246
+ const getClient = (chainId: number) => {
247
+ let client = clients.get(chainId)
248
+ if (!client) {
249
+ client = Viem.getClient({ chainId: chainId as Viem.ChainId, rpc: options.rpc })
250
+ clients.set(chainId, client)
251
+ }
252
+ return client
253
+ }
254
+ const getTidx = options.tidx
255
+ ? Tidx.createGetClient({ defaultChainId: options.defaultChainId, tidx: options.tidx })
256
+ : undefined
257
+ const feePayer = options.feePayer
258
+
259
+ return {
260
+ async tick() {
261
+ // Resolve per tick so Workers factories build a fresh Hyperdrive-pooled
262
+ // connection each run.
263
+ const db = Db.get(options.db)
264
+ // Reconcile first so freshly matched intents finalize in the same tick.
265
+ const reconciled =
266
+ feePayer && getTidx
267
+ ? await reconcile(db, {
268
+ batchSize: options.batchSize,
269
+ feePayer,
270
+ getClient,
271
+ getTidx: (chainId) => getTidx(chainId as Viem.ChainId),
272
+ })
273
+ : { matched: 0, scanned: 0 }
274
+ const finalized = await finalize(db, {
275
+ batchSize: options.batchSize,
276
+ getClient,
277
+ now: options.now,
278
+ pendingTtlMs: options.pendingTtlMs,
279
+ })
280
+ return { ...finalized, ...reconciled }
281
+ },
282
+ }
283
+ }
284
+
285
+ export declare namespace createFinalizer {
286
+ /** Options for {@link createFinalizer}. */
287
+ type Options = Pick<finalize.Options, 'batchSize' | 'now' | 'pendingTtlMs'> & {
288
+ /** Database holding sponsorship rows, or a factory resolved per tick (Workers/Hyperdrive). */
289
+ db: Db.Source
290
+ /** Default chain id for the TIDX client resolver. */
291
+ defaultChainId?: Viem.ChainId | undefined
292
+ /** Fee payer address whose on-chain transactions reconcile fill intents; omit to skip reconciliation. */
293
+ feePayer?: Address | undefined
294
+ /** RPC options for the receipt-lookup clients. */
295
+ rpc?: Viem.getClient.Rpc | undefined
296
+ /** TIDX query client options for intent reconciliation; omit to skip reconciliation. */
297
+ tidx?: Tidx.getClient.Tidx | undefined
298
+ }
299
+
300
+ /** A scheduled sponsorship finalizer. */
301
+ type Finalizer = {
302
+ /** Runs one reconcile + finalize pass. */
303
+ tick(): Promise<finalize.Result & reconcile.Result>
304
+ }
305
+ }
@@ -0,0 +1,2 @@
1
+ export { relay } from './App.js'
2
+ export * as Sponsorships from './Sponsorships.js'