tapimo 0.5.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (376) hide show
  1. package/dist/App.d.ts +15830 -7220
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js +9 -1
  4. package/dist/App.js.map +1 -1
  5. package/dist/Scope.d.ts +4 -0
  6. package/dist/Scope.d.ts.map +1 -1
  7. package/dist/Scope.js +5 -0
  8. package/dist/Scope.js.map +1 -1
  9. package/dist/admin/App.d.ts +1011 -412
  10. package/dist/admin/App.d.ts.map +1 -1
  11. package/dist/admin/App.js +4 -0
  12. package/dist/admin/App.js.map +1 -1
  13. package/dist/admin/apps/api-keys.d.ts +2 -1
  14. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  15. package/dist/admin/apps/early-access.d.ts +148 -0
  16. package/dist/admin/apps/early-access.d.ts.map +1 -0
  17. package/dist/admin/apps/early-access.js +133 -0
  18. package/dist/admin/apps/early-access.js.map +1 -0
  19. package/dist/admin/apps/organizations.d.ts +124 -1
  20. package/dist/admin/apps/organizations.d.ts.map +1 -1
  21. package/dist/admin/apps/organizations.js +97 -6
  22. package/dist/admin/apps/organizations.js.map +1 -1
  23. package/dist/admin/apps/verified-tokens.d.ts +526 -1
  24. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  25. package/dist/admin/apps/verified-tokens.js +214 -1
  26. package/dist/admin/apps/verified-tokens.js.map +1 -1
  27. package/dist/admin/ui.generated.d.ts +1 -1
  28. package/dist/admin/ui.generated.d.ts.map +1 -1
  29. package/dist/admin/ui.generated.js +95 -75
  30. package/dist/admin/ui.generated.js.map +1 -1
  31. package/dist/analytics/Analytics.d.ts.map +1 -1
  32. package/dist/analytics/Analytics.js +20 -0
  33. package/dist/analytics/Analytics.js.map +1 -1
  34. package/dist/analytics/tables/requestEvents.d.ts +18 -0
  35. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  36. package/dist/analytics/tables/requestEvents.js +36 -7
  37. package/dist/analytics/tables/requestEvents.js.map +1 -1
  38. package/dist/apps/data/App.d.ts +6260 -4924
  39. package/dist/apps/data/App.d.ts.map +1 -1
  40. package/dist/apps/data/App.js +11 -6
  41. package/dist/apps/data/App.js.map +1 -1
  42. package/dist/apps/data/FundingQuoteProviders.d.ts +34 -1
  43. package/dist/apps/data/FundingQuoteProviders.d.ts.map +1 -1
  44. package/dist/apps/data/FundingQuoteProviders.js +194 -16
  45. package/dist/apps/data/FundingQuoteProviders.js.map +1 -1
  46. package/dist/apps/data/FxOracle.d.ts +71 -0
  47. package/dist/apps/data/FxOracle.d.ts.map +1 -0
  48. package/dist/apps/data/FxOracle.js +97 -0
  49. package/dist/apps/data/FxOracle.js.map +1 -0
  50. package/dist/apps/data/index.d.ts +1 -0
  51. package/dist/apps/data/index.d.ts.map +1 -1
  52. package/dist/apps/data/index.js +1 -0
  53. package/dist/apps/data/index.js.map +1 -1
  54. package/dist/apps/data/routes/activities.d.ts +3599 -1382
  55. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  56. package/dist/apps/data/routes/activities.js +250 -88
  57. package/dist/apps/data/routes/activities.js.map +1 -1
  58. package/dist/apps/data/routes/addresses.d.ts +872 -251
  59. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  60. package/dist/apps/data/routes/addresses.js +11 -9
  61. package/dist/apps/data/routes/addresses.js.map +1 -1
  62. package/dist/apps/data/routes/balances.d.ts +77 -26
  63. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  64. package/dist/apps/data/routes/balances.js +88 -45
  65. package/dist/apps/data/routes/balances.js.map +1 -1
  66. package/dist/apps/data/routes/blocks.d.ts +39 -39
  67. package/dist/apps/data/routes/coingecko.d.ts +54 -54
  68. package/dist/apps/data/routes/exchanges.d.ts +856 -105
  69. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  70. package/dist/apps/data/routes/exchanges.js +518 -76
  71. package/dist/apps/data/routes/exchanges.js.map +1 -1
  72. package/dist/apps/data/routes/fee-amm.d.ts +81 -67
  73. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  74. package/dist/apps/data/routes/fee-amm.js +76 -91
  75. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  76. package/dist/apps/data/routes/funding.d.ts +80 -36
  77. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  78. package/dist/apps/data/routes/funding.js +65 -31
  79. package/dist/apps/data/routes/funding.js.map +1 -1
  80. package/dist/apps/data/routes/receipts.d.ts +392 -315
  81. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  82. package/dist/apps/data/routes/receipts.js +152 -77
  83. package/dist/apps/data/routes/receipts.js.map +1 -1
  84. package/dist/apps/data/routes/tokens.d.ts +167 -174
  85. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  86. package/dist/apps/data/routes/tokens.js +15 -13
  87. package/dist/apps/data/routes/tokens.js.map +1 -1
  88. package/dist/apps/data/routes/transactions.d.ts +277 -536
  89. package/dist/apps/data/routes/transactions.d.ts.map +1 -1
  90. package/dist/apps/data/routes/transactions.js +96 -77
  91. package/dist/apps/data/routes/transactions.js.map +1 -1
  92. package/dist/apps/data/routes/transfers.d.ts +157 -81
  93. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  94. package/dist/apps/data/routes/transfers.js +272 -130
  95. package/dist/apps/data/routes/transfers.js.map +1 -1
  96. package/dist/apps/data/routes/valuation.d.ts +307 -0
  97. package/dist/apps/data/routes/valuation.d.ts.map +1 -0
  98. package/dist/apps/data/routes/valuation.js +292 -0
  99. package/dist/apps/data/routes/valuation.js.map +1 -0
  100. package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
  101. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
  102. package/dist/apps/data/routes/verified-tokens.js +7 -5
  103. package/dist/apps/data/routes/verified-tokens.js.map +1 -1
  104. package/dist/apps/data/routes/webhooks.d.ts +103 -123
  105. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  106. package/dist/apps/data/routes/webhooks.js +124 -25
  107. package/dist/apps/data/routes/webhooks.js.map +1 -1
  108. package/dist/apps/index.d.ts +2 -1
  109. package/dist/apps/index.d.ts.map +1 -1
  110. package/dist/apps/index.js +2 -1
  111. package/dist/apps/index.js.map +1 -1
  112. package/dist/apps/management/App.d.ts +8554 -1692
  113. package/dist/apps/management/App.d.ts.map +1 -1
  114. package/dist/apps/management/App.js +12 -1
  115. package/dist/apps/management/App.js.map +1 -1
  116. package/dist/apps/management/routes/api-keys.d.ts +7 -0
  117. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  118. package/dist/apps/management/routes/api-keys.js +17 -3
  119. package/dist/apps/management/routes/api-keys.js.map +1 -1
  120. package/dist/apps/management/routes/billing.d.ts +157 -162
  121. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  122. package/dist/apps/management/routes/billing.js +31 -23
  123. package/dist/apps/management/routes/billing.js.map +1 -1
  124. package/dist/apps/management/routes/faucet.d.ts +232 -0
  125. package/dist/apps/management/routes/faucet.d.ts.map +1 -0
  126. package/dist/apps/management/routes/faucet.js +154 -0
  127. package/dist/apps/management/routes/faucet.js.map +1 -0
  128. package/dist/apps/management/routes/invitations.d.ts +60 -60
  129. package/dist/apps/management/routes/invite-links.d.ts +49 -20
  130. package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
  131. package/dist/apps/management/routes/invite-links.js +26 -1
  132. package/dist/apps/management/routes/invite-links.js.map +1 -1
  133. package/dist/apps/management/routes/me.d.ts +6 -6
  134. package/dist/apps/management/routes/members.d.ts +6 -6
  135. package/dist/apps/management/routes/orgs.d.ts +42 -42
  136. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  137. package/dist/apps/management/routes/orgs.js +5 -0
  138. package/dist/apps/management/routes/orgs.js.map +1 -1
  139. package/dist/apps/management/routes/projects.d.ts +44 -44
  140. package/dist/apps/management/routes/usage.d.ts +3 -0
  141. package/dist/apps/management/routes/usage.d.ts.map +1 -1
  142. package/dist/apps/management/routes/usage.js +4 -0
  143. package/dist/apps/management/routes/usage.js.map +1 -1
  144. package/dist/apps/management/routes/verified-token-requests.d.ts +697 -0
  145. package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -0
  146. package/dist/apps/management/routes/verified-token-requests.js +271 -0
  147. package/dist/apps/management/routes/verified-token-requests.js.map +1 -0
  148. package/dist/apps/management/routes/webhooks.d.ts +6977 -0
  149. package/dist/apps/management/routes/webhooks.d.ts.map +1 -0
  150. package/dist/apps/management/routes/webhooks.js +573 -0
  151. package/dist/apps/management/routes/webhooks.js.map +1 -0
  152. package/dist/apps/mpp/App.d.ts +363 -0
  153. package/dist/apps/mpp/App.d.ts.map +1 -0
  154. package/dist/apps/mpp/App.js +72 -0
  155. package/dist/apps/mpp/App.js.map +1 -0
  156. package/dist/apps/mpp/Mpp.d.ts +178 -0
  157. package/dist/apps/mpp/Mpp.d.ts.map +1 -0
  158. package/dist/apps/mpp/Mpp.js +183 -0
  159. package/dist/apps/mpp/Mpp.js.map +1 -0
  160. package/dist/apps/mpp/index.d.ts +3 -0
  161. package/dist/apps/mpp/index.d.ts.map +1 -0
  162. package/dist/apps/mpp/index.js +3 -0
  163. package/dist/apps/mpp/index.js.map +1 -0
  164. package/dist/apps/relay/App.d.ts.map +1 -1
  165. package/dist/apps/relay/App.js +56 -9
  166. package/dist/apps/relay/App.js.map +1 -1
  167. package/dist/db/Db.d.ts +6 -0
  168. package/dist/db/Db.d.ts.map +1 -1
  169. package/dist/db/Db.js +110 -0
  170. package/dist/db/Db.js.map +1 -1
  171. package/dist/db/Schema.d.ts +53 -0
  172. package/dist/db/Schema.d.ts.map +1 -1
  173. package/dist/db/Schema.js +56 -0
  174. package/dist/db/Schema.js.map +1 -1
  175. package/dist/db/tables/earlyAccess.d.ts +50 -0
  176. package/dist/db/tables/earlyAccess.d.ts.map +1 -0
  177. package/dist/db/tables/earlyAccess.js +63 -0
  178. package/dist/db/tables/earlyAccess.js.map +1 -0
  179. package/dist/db/tables/enabledBillingSources.d.ts +73 -0
  180. package/dist/db/tables/enabledBillingSources.d.ts.map +1 -0
  181. package/dist/db/tables/enabledBillingSources.js +69 -0
  182. package/dist/db/tables/enabledBillingSources.js.map +1 -0
  183. package/dist/db/tables/invitations.d.ts.map +1 -1
  184. package/dist/db/tables/invitations.js +3 -0
  185. package/dist/db/tables/invitations.js.map +1 -1
  186. package/dist/db/tables/inviteLinks.d.ts +10 -2
  187. package/dist/db/tables/inviteLinks.d.ts.map +1 -1
  188. package/dist/db/tables/inviteLinks.js +10 -1
  189. package/dist/db/tables/inviteLinks.js.map +1 -1
  190. package/dist/db/tables/organizations.d.ts +5 -2
  191. package/dist/db/tables/organizations.d.ts.map +1 -1
  192. package/dist/db/tables/organizations.js +13 -2
  193. package/dist/db/tables/organizations.js.map +1 -1
  194. package/dist/db/tables/sponsoredTransactions.d.ts +2 -0
  195. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  196. package/dist/db/tables/sponsoredTransactions.js +2 -0
  197. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  198. package/dist/db/tables/verifiedTokenRequests.d.ts +94 -0
  199. package/dist/db/tables/verifiedTokenRequests.d.ts.map +1 -0
  200. package/dist/db/tables/verifiedTokenRequests.js +120 -0
  201. package/dist/db/tables/verifiedTokenRequests.js.map +1 -0
  202. package/dist/db/tables/webhookSubscriptions.d.ts +14 -0
  203. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  204. package/dist/db/tables/webhookSubscriptions.js +16 -0
  205. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  206. package/dist/handlers/internal/utils.d.ts +1 -16
  207. package/dist/handlers/internal/utils.d.ts.map +1 -1
  208. package/dist/handlers/internal/utils.js +17 -6
  209. package/dist/handlers/internal/utils.js.map +1 -1
  210. package/dist/handlers/relay.d.ts.map +1 -1
  211. package/dist/handlers/relay.js +37 -5
  212. package/dist/handlers/relay.js.map +1 -1
  213. package/dist/internal/Auth.d.ts +11 -2
  214. package/dist/internal/Auth.d.ts.map +1 -1
  215. package/dist/internal/Auth.js +14 -47
  216. package/dist/internal/Auth.js.map +1 -1
  217. package/dist/internal/FundingQuotes.d.ts +147 -47
  218. package/dist/internal/FundingQuotes.d.ts.map +1 -1
  219. package/dist/internal/FundingQuotes.js +119 -33
  220. package/dist/internal/FundingQuotes.js.map +1 -1
  221. package/dist/internal/Log.d.ts +37 -5
  222. package/dist/internal/Log.d.ts.map +1 -1
  223. package/dist/internal/Log.js +52 -3
  224. package/dist/internal/Log.js.map +1 -1
  225. package/dist/internal/MetricSink.d.ts.map +1 -1
  226. package/dist/internal/MetricSink.js +24 -0
  227. package/dist/internal/MetricSink.js.map +1 -1
  228. package/dist/internal/OpenApi.d.ts +2 -2
  229. package/dist/internal/OpenApi.d.ts.map +1 -1
  230. package/dist/internal/OpenApi.js +3 -0
  231. package/dist/internal/OpenApi.js.map +1 -1
  232. package/dist/internal/Response.d.ts +2 -2
  233. package/dist/internal/Response.d.ts.map +1 -1
  234. package/dist/internal/Response.js +12 -4
  235. package/dist/internal/Response.js.map +1 -1
  236. package/dist/internal/Schema.d.ts +14 -5
  237. package/dist/internal/Schema.d.ts.map +1 -1
  238. package/dist/internal/Schema.js +23 -5
  239. package/dist/internal/Schema.js.map +1 -1
  240. package/dist/internal/Value.d.ts +18 -0
  241. package/dist/internal/Value.d.ts.map +1 -1
  242. package/dist/internal/Value.js +11 -1
  243. package/dist/internal/Value.js.map +1 -1
  244. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  245. package/dist/internal/VerifiedTokens.js +10 -1
  246. package/dist/internal/VerifiedTokens.js.map +1 -1
  247. package/dist/internal/Webhooks.d.ts +24 -3
  248. package/dist/internal/Webhooks.d.ts.map +1 -1
  249. package/dist/internal/Webhooks.js +34 -8
  250. package/dist/internal/Webhooks.js.map +1 -1
  251. package/package.json +7 -2
  252. package/src/App.test.ts +9 -4
  253. package/src/App.ts +23 -3
  254. package/src/Client.test-d.ts +733 -20
  255. package/src/Scope.ts +5 -0
  256. package/src/admin/App.test.ts +4 -0
  257. package/src/admin/App.ts +4 -0
  258. package/src/admin/apps/early-access.test.ts +125 -0
  259. package/src/admin/apps/early-access.ts +176 -0
  260. package/src/admin/apps/organizations.test.ts +86 -2
  261. package/src/admin/apps/organizations.ts +155 -31
  262. package/src/admin/apps/scopes.test.ts +5 -0
  263. package/src/admin/apps/verified-tokens.test.ts +201 -0
  264. package/src/admin/apps/verified-tokens.ts +532 -286
  265. package/src/admin/ui/plugin.ts +2 -1
  266. package/src/admin/ui/src/lib/early-access.ts +63 -0
  267. package/src/admin/ui/src/lib/organizations.test.ts +81 -1
  268. package/src/admin/ui/src/lib/organizations.ts +92 -0
  269. package/src/admin/ui/src/lib/verified-tokens.ts +79 -0
  270. package/src/admin/ui/src/routeTree.gen.ts +33 -2
  271. package/src/admin/ui/src/routes/__root.tsx +2 -1
  272. package/src/admin/ui/src/routes/api-keys.tsx +1 -1
  273. package/src/admin/ui/src/routes/early-access.tsx +162 -0
  274. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +103 -0
  275. package/src/admin/ui/src/routes/verified-tokens.tsx +230 -19
  276. package/src/admin/ui.generated.ts +95 -94
  277. package/src/analytics/Analytics.test.ts +83 -0
  278. package/src/analytics/Analytics.ts +20 -0
  279. package/src/analytics/tables/requestEvents.ts +60 -7
  280. package/src/apps/composition.test.ts +11 -2
  281. package/src/apps/data/App.test.ts +1 -1
  282. package/src/apps/data/App.ts +22 -14
  283. package/src/apps/data/FundingQuoteProviders.test.ts +297 -4
  284. package/src/apps/data/FundingQuoteProviders.ts +255 -29
  285. package/src/apps/data/FxOracle.test.ts +118 -0
  286. package/src/apps/data/FxOracle.ts +153 -0
  287. package/src/apps/data/index.ts +1 -0
  288. package/src/apps/data/routes/activities.test.ts +177 -3
  289. package/src/apps/data/routes/activities.ts +441 -132
  290. package/src/apps/data/routes/addresses.ts +19 -9
  291. package/src/apps/data/routes/balances.test.ts +148 -11
  292. package/src/apps/data/routes/balances.ts +140 -51
  293. package/src/apps/data/routes/exchanges.test.ts +544 -9
  294. package/src/apps/data/routes/exchanges.ts +690 -103
  295. package/src/apps/data/routes/fee-amm.test.ts +47 -23
  296. package/src/apps/data/routes/fee-amm.ts +98 -119
  297. package/src/apps/data/routes/funding.test.ts +113 -8
  298. package/src/apps/data/routes/funding.ts +77 -51
  299. package/src/apps/data/routes/receipts.test.ts +129 -23
  300. package/src/apps/data/routes/receipts.ts +205 -100
  301. package/src/apps/data/routes/tokens.test.ts +43 -0
  302. package/src/apps/data/routes/tokens.ts +28 -13
  303. package/src/apps/data/routes/transactions.test.ts +72 -19
  304. package/src/apps/data/routes/transactions.ts +162 -92
  305. package/src/apps/data/routes/transfers.test.ts +341 -31
  306. package/src/apps/data/routes/transfers.ts +358 -158
  307. package/src/apps/data/routes/valuation.test.ts +437 -0
  308. package/src/apps/data/routes/valuation.ts +423 -0
  309. package/src/apps/data/routes/verified-tokens.ts +14 -2
  310. package/src/apps/data/routes/webhooks.test.ts +56 -8
  311. package/src/apps/data/routes/webhooks.ts +144 -27
  312. package/src/apps/index.ts +2 -1
  313. package/src/apps/management/App.ts +13 -0
  314. package/src/apps/management/routes/api-keys.test.ts +14 -0
  315. package/src/apps/management/routes/api-keys.ts +27 -3
  316. package/src/apps/management/routes/billing.test.ts +152 -20
  317. package/src/apps/management/routes/billing.ts +34 -18
  318. package/src/apps/management/routes/faucet.test.ts +87 -0
  319. package/src/apps/management/routes/faucet.ts +201 -0
  320. package/src/apps/management/routes/invite-links.test.ts +117 -0
  321. package/src/apps/management/routes/invite-links.ts +43 -1
  322. package/src/apps/management/routes/me.test.ts +9 -99
  323. package/src/apps/management/routes/orgs.test.ts +45 -2
  324. package/src/apps/management/routes/orgs.ts +6 -0
  325. package/src/apps/management/routes/scopes.test.ts +1 -0
  326. package/src/apps/management/routes/usage.test.ts +6 -0
  327. package/src/apps/management/routes/usage.ts +7 -0
  328. package/src/apps/management/routes/verified-token-requests.test.ts +377 -0
  329. package/src/apps/management/routes/verified-token-requests.ts +397 -0
  330. package/src/apps/management/routes/webhooks.test.ts +426 -0
  331. package/src/apps/management/routes/webhooks.ts +672 -0
  332. package/src/apps/mpp/App.ts +86 -0
  333. package/src/apps/mpp/Mpp.test.ts +193 -0
  334. package/src/apps/mpp/Mpp.ts +353 -0
  335. package/src/apps/mpp/index.ts +2 -0
  336. package/src/apps/mpp/internal/Adapter.ts +11 -0
  337. package/src/apps/mpp/internal/Error.ts +9 -0
  338. package/src/apps/mpp/internal/Idempotency.ts +7 -0
  339. package/src/apps/mpp/internal/Screening.ts +10 -0
  340. package/src/apps/relay/App.test.ts +88 -6
  341. package/src/apps/relay/App.ts +61 -9
  342. package/src/db/Db.test.ts +19 -0
  343. package/src/db/Db.ts +125 -0
  344. package/src/db/Schema.ts +68 -0
  345. package/src/db/tables/earlyAccess.ts +86 -0
  346. package/src/db/tables/enabledBillingSources.test.ts +303 -0
  347. package/src/db/tables/enabledBillingSources.ts +118 -0
  348. package/src/db/tables/invitations.ts +3 -0
  349. package/src/db/tables/inviteLinks.ts +22 -3
  350. package/src/db/tables/organizations.test.ts +103 -0
  351. package/src/db/tables/organizations.ts +18 -2
  352. package/src/db/tables/sponsoredTransactions.ts +4 -0
  353. package/src/db/tables/verifiedTokenRequests.test.ts +200 -0
  354. package/src/db/tables/verifiedTokenRequests.ts +182 -0
  355. package/src/db/tables/webhookSubscriptions.ts +31 -0
  356. package/src/handlers/internal/utils.ts +22 -6
  357. package/src/handlers/relay.errors.test.ts +153 -0
  358. package/src/handlers/relay.ts +42 -7
  359. package/src/internal/Auth.test.ts +3 -3
  360. package/src/internal/Auth.ts +15 -47
  361. package/src/internal/FundingQuotes.test.ts +161 -6
  362. package/src/internal/FundingQuotes.ts +141 -39
  363. package/src/internal/Log.test-d.ts +21 -0
  364. package/src/internal/Log.test.ts +141 -0
  365. package/src/internal/Log.ts +93 -6
  366. package/src/internal/MetricSink.test.ts +215 -0
  367. package/src/internal/MetricSink.ts +24 -0
  368. package/src/internal/OpenApi.ts +3 -0
  369. package/src/internal/Response.test.ts +28 -0
  370. package/src/internal/Response.ts +12 -4
  371. package/src/internal/Schema.ts +50 -5
  372. package/src/internal/Value.test.ts +20 -0
  373. package/src/internal/Value.ts +24 -1
  374. package/src/internal/VerifiedTokens.ts +9 -1
  375. package/src/internal/Webhooks.test.ts +140 -0
  376. package/src/internal/Webhooks.ts +53 -9
@@ -656,6 +656,19 @@ describe.skipIf(runtime.mode !== 'localnet')('behavior: sponsorship', () => {
656
656
  // Chain-free (`eth_signRawTransaction` signs locally; the sponsor fee token
657
657
  // short-circuits resolution), so the gate matrix runs in every mode.
658
658
  describe('behavior: mainnet billing gate', () => {
659
+ const fill = {
660
+ calls: [{ to: Tempo.accounts[8]!.address, value: '0x0' }],
661
+ capabilities: { balanceDiffs: false },
662
+ chainId: Hex.fromNumber(Viem.chainId.mainnet),
663
+ feePayer: true,
664
+ feeToken: '0x20c0000000000000000000000000000000000000',
665
+ from: senderAccount.address,
666
+ gas: Hex.fromNumber(100_000),
667
+ maxFeePerGas: Hex.fromNumber(1_000_000),
668
+ maxPriorityFeePerGas: '0x0',
669
+ nonce: '0x0',
670
+ } as const
671
+
659
672
  function createApp(db: Db.Source = TestApp.database()) {
660
673
  return TestApp.create({
661
674
  auth: { keys },
@@ -692,6 +705,64 @@ describe('behavior: mainnet billing gate', () => {
692
705
  expect(body.error?.data).toStrictEqual({ code: 'billing_required' })
693
706
  })
694
707
 
708
+ test('behavior: returns the billing reason for sponsor fills', async () => {
709
+ const body = await relayRequest(createApp(), {
710
+ method: 'eth_fillTransaction',
711
+ params: [fill],
712
+ token: 'secret_sponsor_prod',
713
+ })
714
+ expect(body.error).toStrictEqual({
715
+ code: -32602,
716
+ data: { code: 'billing_required' },
717
+ message: 'Billing required.',
718
+ })
719
+ })
720
+
721
+ test('behavior: relay fills still fall back to self-payment', async () => {
722
+ const server = await Relay.createServer(
723
+ RequestListener.fromFetchHandler(async (request) => {
724
+ type Rpc = {
725
+ id: number
726
+ method: string
727
+ params: readonly [Record<string, unknown>]
728
+ }
729
+ const rpc = (await request.json()) as Rpc
730
+ if (rpc.method !== 'eth_fillTransaction')
731
+ return Response.json({ id: rpc.id, jsonrpc: '2.0', result: '0x' })
732
+ return Response.json({
733
+ id: rpc.id,
734
+ jsonrpc: '2.0',
735
+ result: { capabilities: {}, tx: rpc.params[0] },
736
+ })
737
+ }),
738
+ )
739
+ try {
740
+ const body = await relayRequest(
741
+ TestApp.create({
742
+ auth: { keys },
743
+ db: TestApp.database(),
744
+ relay: { feePayer: { account: feePayerAccount } },
745
+ rpc: { url: () => server.url },
746
+ supportedChainIds: [Viem.chainId.mainnet],
747
+ }),
748
+ {
749
+ method: 'eth_fillTransaction',
750
+ mount: 'relay',
751
+ params: [fill],
752
+ token: 'secret_wildcard_prod',
753
+ },
754
+ )
755
+ type Result = {
756
+ capabilities?: { sponsored?: boolean | undefined } | undefined
757
+ }
758
+ const result = body.result as unknown as Result
759
+ expect(body.error).toBeUndefined()
760
+ expect(result.capabilities?.sponsored).toBe(false)
761
+ } finally {
762
+ await server.closeAsync()
763
+ }
764
+ })
765
+
695
766
  test('behavior: refuses mainnet sponsorship while billing is inactive', async () => {
696
767
  const db = TestApp.database()
697
768
  await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_gate' })
@@ -1110,13 +1181,10 @@ async function signSponsorable(
1110
1181
  })
1111
1182
  }
1112
1183
 
1113
- /** Posts one JSON-RPC request to the composed app's sponsor mount. */
1114
- async function relayRequest(
1115
- app: ReturnType<typeof TestApp.create>,
1116
- options: { method: string; params: readonly unknown[]; token?: string | undefined },
1117
- ) {
1184
+ /** Posts one JSON-RPC request to a composed relay mount. */
1185
+ async function relayRequest(app: ReturnType<typeof TestApp.create>, options: relayRequest.Options) {
1118
1186
  const response = await app.fetch(
1119
- new Request('http://tempo-api.test/rpc/sponsor', {
1187
+ new Request(`http://tempo-api.test/rpc/${options.mount ?? 'sponsor'}`, {
1120
1188
  body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: options.method, params: options.params }), // prettier-ignore
1121
1189
  headers: {
1122
1190
  'content-type': 'application/json',
@@ -1131,6 +1199,20 @@ async function relayRequest(
1131
1199
  }
1132
1200
  }
1133
1201
 
1202
+ declare namespace relayRequest {
1203
+ /** Options for one composed relay request. */
1204
+ type Options = {
1205
+ /** JSON-RPC method. */
1206
+ method: string
1207
+ /** Relay mount to call. */
1208
+ mount?: 'relay' | 'sponsor' | undefined
1209
+ /** JSON-RPC parameters. */
1210
+ params: readonly unknown[]
1211
+ /** API-key credential. */
1212
+ token?: string | undefined
1213
+ }
1214
+ }
1215
+
1134
1216
  // The full billing-to-invoice flow with nothing synthetic: a disposable node
1135
1217
  // runs under mainnet's chain id (dev consensus, funded dev alloc), so a real
1136
1218
  // card attach opens the real gate, a real sponsored transaction mines with a
@@ -8,6 +8,7 @@ import type * as App from '../../App.js'
8
8
  import * as BillingSettings from '../../db/tables/billingSettings.js'
9
9
  import * as Db from '../../db/Db.js'
10
10
  import * as Fees from '../../internal/Fees.js'
11
+ import * as Log from '../../internal/Log.js'
11
12
  import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js'
12
13
  import * as Billing from '../management/Billing.js'
13
14
  import * as Handler from '../../Handler.js'
@@ -75,6 +76,9 @@ export declare namespace relay {
75
76
  // Builds request-aware RPC middleware. Viem clients are memoized upstream, so
76
77
  // constructing `Handler.relay` per request stays cheap.
77
78
  function middleware(options: middleware.Options) {
79
+ // The sponsor mount promises a fee-payer signature. The relay mount may
80
+ // fall back to self-payment when policy refuses sponsorship.
81
+ const sponsorshipRequired = options.path === 'rpc/sponsor'
78
82
  // Platform-wide per-transaction cap; org settings tighten it, never loosen.
79
83
  const txFeeLimit = options.feePayer
80
84
  ? Billing.toBaseUnits(options.feePayer.txFeeLimit ?? '1.00')
@@ -111,12 +115,20 @@ function middleware(options: middleware.Options) {
111
115
  // only mainnet: the relay resolves chainId from the request body, so
112
116
  // the query-string chain guard never sees it.
113
117
  const id = Number(transaction.chainId)
114
- if (!attribution || !sponsorable || !c.get('supportedChainIds').has(id)) return false
118
+ if (!attribution || !sponsorable || !c.get('supportedChainIds').has(id)) {
119
+ if (sponsorshipRequired)
120
+ throw new RpcResponse.InvalidParamsError({ message: 'Sponsorship rejected.' })
121
+ return false
122
+ }
115
123
  if (!Viem.isMainnet(id)) return true
116
124
  // Mainnet is production-only: a sandbox key resolves chainId from the
117
125
  // request body, so the auth-layer sandbox/mainnet guard never sees
118
126
  // it. Refuse here, or sandbox spend would sponsor mainnet unbilled.
119
- if (attribution.environment !== 'production') return false
127
+ if (attribution.environment !== 'production') {
128
+ if (sponsorshipRequired)
129
+ throw new RpcResponse.InvalidParamsError({ message: 'Sponsorship rejected.' })
130
+ return false
131
+ }
120
132
 
121
133
  // Enforcement reads hit the primary, never the cached replica: a
122
134
  // stale `past_due`, a lowered limit, or another isolate's pending
@@ -127,8 +139,22 @@ function middleware(options: middleware.Options) {
127
139
  // `past_due` gets its own reason: the caller must settle an invoice,
128
140
  // not attach a card.
129
141
  const billing = await Billing.status(db, attribution.orgId)
130
- if (billing === 'past_due') return 'billing_past_due'
131
- if (billing !== 'active') return 'billing_required'
142
+ if (billing === 'past_due') {
143
+ if (sponsorshipRequired)
144
+ throw new RpcResponse.InvalidParamsError({
145
+ data: { code: 'billing_past_due' },
146
+ message: 'Billing past due.',
147
+ })
148
+ return 'billing_past_due'
149
+ }
150
+ if (billing !== 'active') {
151
+ if (sponsorshipRequired)
152
+ throw new RpcResponse.InvalidParamsError({
153
+ data: { code: 'billing_required' },
154
+ message: 'Billing required.',
155
+ })
156
+ return 'billing_required'
157
+ }
132
158
 
133
159
  const feeToken = (() => {
134
160
  try {
@@ -144,21 +170,40 @@ function middleware(options: middleware.Options) {
144
170
  !verified ||
145
171
  verified.currency.toLowerCase() !== 'usd' ||
146
172
  verified.decimals !== Fees.tokenDecimals
147
- )
148
- // prettier-ignore
173
+ ) {
174
+ if (sponsorshipRequired)
175
+ throw new RpcResponse.InvalidParamsError({
176
+ data: { code: 'fee_token_unsupported' },
177
+ message: 'Fee token unsupported.',
178
+ })
149
179
  return 'fee_token_unsupported'
180
+ }
150
181
 
151
182
  // Fail closed: a transaction whose signed cap cannot be computed
152
183
  // cannot be bounded, so it never sponsors on mainnet.
153
184
  const feeMax = Fees.maxOf(transaction)
154
- if (feeMax === undefined) return 'tx_fee_limit_exceeded'
185
+ if (feeMax === undefined) {
186
+ if (sponsorshipRequired)
187
+ throw new RpcResponse.InvalidParamsError({
188
+ data: { code: 'tx_fee_limit_exceeded' },
189
+ message: 'Transaction fee limit exceeded.',
190
+ })
191
+ return 'tx_fee_limit_exceeded'
192
+ }
155
193
  const settings = await BillingSettings.get(db, attribution.orgId)
156
194
  const cap = (() => {
157
195
  if (settings?.txFeeLimit == null) return txFeeLimit
158
196
  const org = Billing.toBaseUnits(settings.txFeeLimit)
159
197
  return org < txFeeLimit ? org : txFeeLimit
160
198
  })()
161
- if (feeMax > cap) return 'tx_fee_limit_exceeded'
199
+ if (feeMax > cap) {
200
+ if (sponsorshipRequired)
201
+ throw new RpcResponse.InvalidParamsError({
202
+ data: { code: 'tx_fee_limit_exceeded' },
203
+ message: 'Transaction fee limit exceeded.',
204
+ })
205
+ return 'tx_fee_limit_exceeded'
206
+ }
162
207
  // The period spend limit is enforced atomically at recording time
163
208
  // (`onSponsored` → `SponsoredTransactions.reserve`); a check here
164
209
  // would be a check-then-act race a burst could slip through.
@@ -278,7 +323,14 @@ function middleware(options: middleware.Options) {
278
323
  }
279
324
  : {}),
280
325
  })
281
- return handler.fetch(c.req.raw)
326
+ const response = await handler.fetch(c.req.raw)
327
+ const body: unknown = await response
328
+ .clone()
329
+ .json()
330
+ .catch(() => undefined)
331
+ const rpc = Log.rpcErrors(body)
332
+ if (rpc) c.set('rpcResponse', rpc)
333
+ return response
282
334
  })
283
335
  }
284
336
 
package/src/db/Db.test.ts CHANGED
@@ -1,7 +1,9 @@
1
+ import { sql } from 'kysely'
1
2
  import { nanoid } from 'nanoid'
2
3
 
3
4
  import * as Runtime from '../../test/runtime.js'
4
5
  import * as Db from './Db.js'
6
+ import * as Invitations from './tables/invitations.js'
5
7
  import * as Organizations from './tables/organizations.js'
6
8
 
7
9
  const create = () => Db.postgres({ connectionString: Runtime.postgresUrl, schema: `t_${nanoid()}` })
@@ -15,6 +17,23 @@ describe('postgres', () => {
15
17
  await db.close()
16
18
  })
17
19
 
20
+ test('behavior: legacy invitation inserts do not grant early access', async () => {
21
+ const db = create()
22
+ await db.migrate()
23
+ await Organizations.create(db, { id: 'org_legacy', name: 'Legacy' })
24
+ await sql`
25
+ INSERT INTO invitations (
26
+ id, org_id, email, role, invited_by, expires_at, accepted_at, revoked_at, created_at
27
+ ) VALUES (
28
+ 'inv_legacy', 'org_legacy', 'legacy@example.org', 'member', 'usr_legacy',
29
+ '2027-01-01T00:00:00.000Z', NULL, NULL, '2026-07-20T00:00:00.000Z'
30
+ )
31
+ `.execute(db.kysely)
32
+
33
+ expect((await Invitations.get(db, 'inv_legacy'))?.grantsEarlyAccess).toBe(false)
34
+ await db.close()
35
+ })
36
+
18
37
  test('behavior: creates a Postgres db without schema scoping', async () => {
19
38
  const db = Db.postgres({ connectionString: Runtime.postgresUrl })
20
39
  await db.close()
package/src/db/Db.ts CHANGED
@@ -12,6 +12,8 @@ import * as pg from 'pg'
12
12
 
13
13
  import type * as AdminAuditLogs from './tables/adminAuditLogs.js'
14
14
  import type * as BillingSettings from './tables/billingSettings.js'
15
+ import type * as EarlyAccess from './tables/earlyAccess.js'
16
+ import type * as EnabledBillingSources from './tables/enabledBillingSources.js'
15
17
  import type * as Invitations from './tables/invitations.js'
16
18
  import type * as InviteLinks from './tables/inviteLinks.js'
17
19
  import type * as Memberships from './tables/memberships.js'
@@ -21,6 +23,7 @@ import type * as RequestUsage from './tables/requestUsage.js'
21
23
  import type * as SponsoredTransactions from './tables/sponsoredTransactions.js'
22
24
  import type * as StripeCustomers from './tables/stripeCustomers.js'
23
25
  import type * as Users from './tables/users.js'
26
+ import type * as VerifiedTokenRequests from './tables/verifiedTokenRequests.js'
24
27
  import type * as VerifiedTokens from './tables/verifiedTokens.js'
25
28
  import type * as WebhookDeliveries from './tables/webhookDeliveries.js'
26
29
  import type * as WebhookSubscriptions from './tables/webhookSubscriptions.js'
@@ -32,6 +35,8 @@ import type * as WebhookSubscriptions from './tables/webhookSubscriptions.js'
32
35
  export type Database = {
33
36
  admin_audit_logs: AdminAuditLogs.Table
34
37
  billing_settings: BillingSettings.Table
38
+ early_access: EarlyAccess.Table
39
+ enabled_billing_sources: EnabledBillingSources.Table
35
40
  invitations: Invitations.Table
36
41
  invite_link_redemptions: InviteLinks.RedemptionTable
37
42
  invite_links: InviteLinks.Table
@@ -51,6 +56,7 @@ export type Database = {
51
56
  stripe_customers: StripeCustomers.Table
52
57
  users: Users.Table
53
58
  verified_token_lists: VerifiedTokens.ListTable
59
+ verified_token_requests: VerifiedTokenRequests.Table
54
60
  verified_tokens: VerifiedTokens.Table
55
61
  webhook_deliveries: WebhookDeliveries.Table
56
62
  webhook_subscriptions: WebhookSubscriptions.Table
@@ -645,6 +651,125 @@ export const migrations: readonly Migration[] = [
645
651
  name: '0058_request_usage_sequence_bigint',
646
652
  up: (db) => raw(db, sql`ALTER TABLE request_usage_meter_events ALTER COLUMN sequence TYPE bigint`), // prettier-ignore
647
653
  },
654
+ {
655
+ name: '0059_verified_token_requests',
656
+ up: (db) =>
657
+ db.schema
658
+ .createTable('verifiedTokenRequests')
659
+ .ifNotExists()
660
+ .addColumn('id', 'text', (c) => c.primaryKey())
661
+ .addColumn('orgId', 'text', (c) => c.notNull())
662
+ .addColumn('requestedBy', 'text', (c) => c.notNull())
663
+ .addColumn('chainId', 'integer', (c) => c.notNull())
664
+ .addColumn('address', 'text', (c) => c.notNull())
665
+ .addColumn('symbol', 'text', (c) => c.notNull())
666
+ .addColumn('name', 'text', (c) => c.notNull())
667
+ .addColumn('currency', 'text', (c) => c.notNull())
668
+ .addColumn('decimals', 'integer', (c) => c.notNull())
669
+ .addColumn('logoUri', 'text')
670
+ .addColumn('note', 'text')
671
+ .addColumn('status', 'text', (c) => c.notNull().defaultTo('pending'))
672
+ .addColumn('reviewNote', 'text')
673
+ .addColumn('reviewedBy', 'text')
674
+ .addColumn('reviewedAt', 'text')
675
+ .addColumn('createdAt', 'text', (c) => c.notNull())
676
+ .addColumn('updatedAt', 'text', (c) => c.notNull()),
677
+ },
678
+ {
679
+ name: '0060_verified_token_requests_org_index',
680
+ up: (db) =>
681
+ db.schema
682
+ .createIndex('verified_token_requests_org_idx')
683
+ .ifNotExists()
684
+ .on('verifiedTokenRequests')
685
+ .columns(['orgId', 'createdAt']),
686
+ },
687
+ {
688
+ name: '0061_verified_token_requests_pending_unique',
689
+ // Raw SQL: a partial unique index (snake_case; the CamelCase plugin does
690
+ // not map raw statements). At most one live pending request per
691
+ // (chain, address) globally; addresses are stored lowercase.
692
+ up: (db) =>
693
+ raw(
694
+ db,
695
+ sql`CREATE UNIQUE INDEX IF NOT EXISTS verified_token_requests_pending_unique ON verified_token_requests (chain_id, address) WHERE status = 'pending'`,
696
+ ),
697
+ },
698
+ {
699
+ name: '0062_verified_token_requests_status_index',
700
+ up: (db) =>
701
+ db.schema
702
+ .createIndex('verified_token_requests_status_idx')
703
+ .ifNotExists()
704
+ .on('verifiedTokenRequests')
705
+ .columns(['status', 'createdAt']),
706
+ },
707
+ {
708
+ name: '0063_verified_token_requests_logo',
709
+ up: (db) => raw(db, sql`ALTER TABLE verified_token_requests ADD COLUMN IF NOT EXISTS logo text`), // prettier-ignore
710
+ },
711
+ {
712
+ name: '0064_invitations_grants_early_access',
713
+ up: (db) => raw(db, sql`ALTER TABLE invitations ADD COLUMN IF NOT EXISTS grants_early_access boolean NOT NULL DEFAULT false`), // prettier-ignore
714
+ },
715
+ {
716
+ name: '0065_invitations_grants_early_access_backfill',
717
+ // Pre-gate invitations came from trusted-era users; the fixed cutoff keeps
718
+ // re-runs (test DBs replay every migration) from flipping newer rows.
719
+ up: (db) => raw(db, sql`UPDATE invitations SET grants_early_access = true WHERE created_at < '2026-07-20T00:00:00.000Z'`), // prettier-ignore
720
+ },
721
+ {
722
+ name: '0066_early_access',
723
+ up: (db) =>
724
+ db.schema
725
+ .createTable('earlyAccess')
726
+ .ifNotExists()
727
+ .addColumn('entry', 'text', (c) => c.primaryKey())
728
+ .addColumn('createdAt', 'text', (c) => c.notNull())
729
+ .addColumn('createdBy', 'text', (c) => c.notNull()),
730
+ },
731
+ {
732
+ name: '0067_invitations_grants_early_access_cutover',
733
+ // Every invitation present at deployment came from a trusted-era user.
734
+ up: (db) => raw(db, sql`UPDATE invitations SET grants_early_access = true`),
735
+ },
736
+ {
737
+ name: '0068_invitations_grants_early_access_legacy_default',
738
+ // Old workers omit this column during deploy; new writes set it explicitly.
739
+ up: (db) => raw(db, sql`ALTER TABLE invitations ALTER COLUMN grants_early_access SET DEFAULT true`), // prettier-ignore
740
+ },
741
+ {
742
+ name: '0069_invite_links_allowed_email_domains',
743
+ up: (db) => raw(db, sql`ALTER TABLE invite_links ADD COLUMN IF NOT EXISTS allowed_email_domains jsonb`), // prettier-ignore
744
+ },
745
+ {
746
+ name: '0070_enabled_billing_sources',
747
+ up: (db) =>
748
+ db.schema
749
+ .createTable('enabledBillingSources')
750
+ .ifNotExists()
751
+ .addColumn('createdAt', 'text', (c) => c.notNull())
752
+ .addColumn('createdBy', 'text', (c) => c.notNull())
753
+ .addColumn('orgId', 'text', (c) => c.notNull())
754
+ .addColumn('source', 'text', (c) => c.notNull())
755
+ .addPrimaryKeyConstraint('enabled_billing_sources_pkey', ['orgId', 'source']),
756
+ },
757
+ {
758
+ name: '0071_enabled_billing_sources_stripe_backfill',
759
+ up: (db) => raw(db, sql`INSERT INTO enabled_billing_sources (created_at, created_by, org_id, source) SELECT min(created_at), 'migration', org_id, 'stripe' FROM stripe_customers GROUP BY org_id ON CONFLICT DO NOTHING`), // prettier-ignore
760
+ },
761
+ {
762
+ name: '0072_enabled_billing_sources_stripe_catch_up',
763
+ up: (db) => raw(db, sql`INSERT INTO enabled_billing_sources (created_at, created_by, org_id, source) SELECT min(created_at), 'migration', org_id, 'stripe' FROM stripe_customers GROUP BY org_id ON CONFLICT DO NOTHING`), // prettier-ignore
764
+ },
765
+ {
766
+ name: '0073_enabled_billing_sources_early_access_backfill',
767
+ up: (db) => raw(db, sql`INSERT INTO enabled_billing_sources (created_at, created_by, org_id, source) SELECT organizations.created_at, 'migration', organizations.id, 'stripe' FROM organizations INNER JOIN users ON users.id = organizations.user_id WHERE users.email IS NOT NULL AND EXISTS (SELECT 1 FROM early_access WHERE early_access.entry IN (lower(btrim(users.email)), regexp_replace(lower(btrim(users.email)), '^.*@', ''))) ON CONFLICT DO NOTHING`), // prettier-ignore
768
+ },
769
+ {
770
+ name: '0074_invitations_early_access_default',
771
+ up: (db) => raw(db, sql`ALTER TABLE invitations ALTER COLUMN grants_early_access SET DEFAULT false`), // prettier-ignore
772
+ },
648
773
  ]
649
774
 
650
775
  /**
package/src/db/Schema.ts CHANGED
@@ -52,6 +52,35 @@ export const BillingSettings = z.object({
52
52
  /** Columns of the `billing_settings` table. */
53
53
  export type BillingSettings = z.output<typeof BillingSettings>
54
54
 
55
+ /** An early-access allowlist entry: a lowercase domain or exact email. */
56
+ export const EarlyAccessEntry = z.object({
57
+ createdAt: z.string().check(z.describe('When the entry was added (ISO 8601).')),
58
+ createdBy: z
59
+ .string()
60
+ .check(z.describe('Admin email that added the entry, or `migration` for seeded rows.')),
61
+ entry: z
62
+ .string()
63
+ .check(z.describe('Lowercase domain or exact email (contains `@`) granted early access.')),
64
+ })
65
+
66
+ /** Columns of the `early_access` table. */
67
+ export type EarlyAccessEntry = z.output<typeof EarlyAccessEntry>
68
+
69
+ /** One billing-source type enabled for an organization. */
70
+ export const EnabledBillingSource = z.object({
71
+ createdAt: z.string().check(z.describe('When the billing source was enabled (ISO 8601).')),
72
+ createdBy: z
73
+ .string()
74
+ .check(z.describe('Identity that enabled the source, or `migration` for backfilled rows.')),
75
+ orgId: z.string().check(z.describe('Organization id (`org_…`) this source is enabled for.')),
76
+ source: z
77
+ .enum(['stripe', 'tempo'])
78
+ .check(z.describe('Enabled billing-source type; `stripe` or `tempo`.')),
79
+ })
80
+
81
+ /** Columns of the `enabled_billing_sources` table. */
82
+ export type EnabledBillingSource = z.output<typeof EnabledBillingSource>
83
+
55
84
  /** A pending, expiring offer of organization membership. */
56
85
  export const Invitation = z.object({
57
86
  acceptedAt: z
@@ -62,6 +91,9 @@ export const Invitation = z.object({
62
91
  .string()
63
92
  .check(z.describe('Invitee email (stored lowercase); matched against verified emails.')),
64
93
  expiresAt: z.string().check(z.describe('When the invitation expires (ISO 8601).')),
94
+ grantsEarlyAccess: z
95
+ .boolean()
96
+ .check(z.describe("Whether the invitation grants early access; stamped from the inviter's own access at creation.")), // prettier-ignore
65
97
  id: z
66
98
  .string()
67
99
  .check(
@@ -83,6 +115,9 @@ export type Invitation = z.output<typeof Invitation>
83
115
 
84
116
  /** A reusable bearer link granting organization membership. */
85
117
  export const InviteLink = z.object({
118
+ allowedEmailDomains: z
119
+ .nullable(z.readonly(z.array(z.string())))
120
+ .check(z.describe('Lowercase email domains allowed to redeem, or null for unrestricted.')),
86
121
  createdAt: z.string().check(z.describe('When the link was created (ISO 8601).')),
87
122
  createdBy: z.string().check(z.describe('User id that created the link, or `super_admin`.')),
88
123
  deletedAt: z.nullable(z.string()).check(z.describe('When the link was soft deleted, or null.')),
@@ -363,6 +398,39 @@ export const VerifiedTokenList = z.object({
363
398
  /** Columns of the `verified_token_lists` table. */
364
399
  export type VerifiedTokenList = z.output<typeof VerifiedTokenList>
365
400
 
401
+ /**
402
+ * An organization's request to add a token to the curated verified list.
403
+ * Addresses are stored lowercase; a partial unique index allows at most one
404
+ * pending request per (chain, address) globally.
405
+ */
406
+ export const VerifiedTokenRequest = z.object({
407
+ address: z.string().check(z.describe('Requested TIP-20 token contract address (lowercase).')),
408
+ chainId: z.number().check(z.int(), z.describe('Chain the token belongs to.')),
409
+ createdAt: z.string().check(z.describe('When the request was created (ISO 8601).')),
410
+ currency: z.string().check(z.describe('Display currency, e.g. `USD`.')),
411
+ decimals: z.number().check(z.int(), z.describe('Decimal precision.')),
412
+ id: z.string().check(z.describe('Opaque request id (`vtr_…`).')),
413
+ logo: z
414
+ .nullable(z.string())
415
+ .check(z.describe('Inline SVG logo markup uploaded by the requester, or null.')),
416
+ logoUri: z.nullable(z.string()).check(z.describe('HTTPS logo URL from chain metadata, or null.')),
417
+ name: z.string().check(z.describe('Display name.')),
418
+ note: z.nullable(z.string()).check(z.describe('Requester note to reviewers, or null.')),
419
+ orgId: z.string().check(z.describe('Requesting organization id (`org_…`).')),
420
+ requestedBy: z
421
+ .string()
422
+ .check(z.describe('User id that submitted the request, or `super_admin`.')),
423
+ reviewNote: z.nullable(z.string()).check(z.describe('Reviewer note shown on denial, or null.')),
424
+ reviewedAt: z.nullable(z.string()).check(z.describe('When the request was reviewed, or null.')),
425
+ reviewedBy: z.nullable(z.string()).check(z.describe('Reviewing admin email, or null.')),
426
+ status: z.enum(['approved', 'denied', 'pending']).check(z.describe('Review status.')),
427
+ symbol: z.string().check(z.describe('Ticker symbol.'), z.meta({ examples: ['USDC'] })),
428
+ updatedAt: z.string().check(z.describe('When the request was last changed (ISO 8601).')),
429
+ })
430
+
431
+ /** Columns of the `verified_token_requests` table. */
432
+ export type VerifiedTokenRequest = z.output<typeof VerifiedTokenRequest>
433
+
366
434
  /**
367
435
  * A webhook subscription. The `Webhooks.Owner` union maps to (`ownerType`,
368
436
  * `ownerId`); jsonb object columns round-trip as parsed values. `pollerCursor`
@@ -0,0 +1,86 @@
1
+ import type { Selectable } from 'kysely'
2
+
3
+ import type * as Db from '../Db.js'
4
+ import type * as db_Schema from '../Schema.js'
5
+
6
+ /** Columns of the `early_access` table, derived from `Schema.EarlyAccessEntry`. */
7
+ export type Table = db_Schema.EarlyAccessEntry
8
+
9
+ /** A stored early-access allowlist row. */
10
+ export type Record = Selectable<Table>
11
+
12
+ /**
13
+ * Inserts an allowlist entry (a domain or exact email, normalized lowercase).
14
+ * Returns the stored record, or `undefined` when the entry already exists.
15
+ *
16
+ * @param db - The database.
17
+ * @param input - The entry to insert.
18
+ * @returns The stored record, or `undefined` when already present.
19
+ */
20
+ export function add(db: Db.Db, input: add.Input): Promise<Record | undefined> {
21
+ return db.kysely
22
+ .insertInto('early_access')
23
+ .values({
24
+ createdAt: new Date().toISOString(),
25
+ createdBy: input.createdBy,
26
+ entry: input.entry.trim().toLowerCase(),
27
+ })
28
+ .onConflict((oc) => oc.column('entry').doNothing())
29
+ .returningAll()
30
+ .executeTakeFirst()
31
+ }
32
+
33
+ export declare namespace add {
34
+ /** Fields accepted when inserting an allowlist entry. */
35
+ type Input = {
36
+ /** Admin email adding the entry, or `migration` for seeded rows. */
37
+ createdBy: string
38
+ /** Domain or exact email to grant early access; normalized lowercase. */
39
+ entry: string
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Lists all allowlist entries, ordered by entry.
45
+ *
46
+ * @param db - The database.
47
+ * @returns The records.
48
+ */
49
+ export function list(db: Db.Db): Promise<Record[]> {
50
+ return db.kysely.selectFrom('early_access').selectAll().orderBy('entry', 'asc').execute()
51
+ }
52
+
53
+ /**
54
+ * Removes an allowlist entry.
55
+ *
56
+ * @param db - The database.
57
+ * @param entry - The entry to remove (matched lowercase).
58
+ * @returns The removed record, or `undefined` when absent.
59
+ */
60
+ export function remove(db: Db.Db, entry: string): Promise<Record | undefined> {
61
+ return db.kysely
62
+ .deleteFrom('early_access')
63
+ .where('entry', '=', entry.trim().toLowerCase())
64
+ .returningAll()
65
+ .executeTakeFirst()
66
+ }
67
+
68
+ /**
69
+ * Whether an allowlist entry matches `email`: the exact email or its domain.
70
+ * Two primary-key lookups via `IN`.
71
+ *
72
+ * @param db - The database.
73
+ * @param email - The email to match (case-insensitive).
74
+ * @returns Whether an entry matches.
75
+ */
76
+ export async function matches(db: Db.Db, email: string): Promise<boolean> {
77
+ const normalized = email.trim().toLowerCase()
78
+ const domain = normalized.slice(normalized.lastIndexOf('@') + 1)
79
+ const row = await db.kysely
80
+ .selectFrom('early_access')
81
+ .select('entry')
82
+ .where('entry', 'in', [normalized, domain])
83
+ .limit(1)
84
+ .executeTakeFirst()
85
+ return row !== undefined
86
+ }