tapimo 0.11.0 → 0.12.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 (339) hide show
  1. package/dist/App.d.ts +2296 -1010
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js +2 -2
  4. package/dist/App.js.map +1 -1
  5. package/dist/Assets.d.ts +15 -0
  6. package/dist/Assets.d.ts.map +1 -1
  7. package/dist/Assets.js +7 -0
  8. package/dist/Assets.js.map +1 -1
  9. package/dist/admin/App.d.ts +53 -4
  10. package/dist/admin/App.d.ts.map +1 -1
  11. package/dist/admin/apps/api-keys.d.ts +4 -4
  12. package/dist/admin/apps/earn-vaults.d.ts +114 -0
  13. package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
  14. package/dist/admin/apps/earn-vaults.js +59 -6
  15. package/dist/admin/apps/earn-vaults.js.map +1 -1
  16. package/dist/admin/apps/organizations.d.ts.map +1 -1
  17. package/dist/admin/apps/organizations.js +4 -2
  18. package/dist/admin/apps/organizations.js.map +1 -1
  19. package/dist/admin/apps/verified-tokens.d.ts +9 -9
  20. package/dist/admin/ui.generated.d.ts +1 -1
  21. package/dist/admin/ui.generated.d.ts.map +1 -1
  22. package/dist/admin/ui.generated.js +151 -151
  23. package/dist/admin/ui.generated.js.map +1 -1
  24. package/dist/apps/data/App.d.ts +1699 -749
  25. package/dist/apps/data/App.d.ts.map +1 -1
  26. package/dist/apps/data/App.js +56 -7
  27. package/dist/apps/data/App.js.map +1 -1
  28. package/dist/apps/data/ZoneSelection.d.ts +62 -0
  29. package/dist/apps/data/ZoneSelection.d.ts.map +1 -0
  30. package/dist/apps/data/ZoneSelection.js +44 -0
  31. package/dist/apps/data/ZoneSelection.js.map +1 -0
  32. package/dist/apps/data/routes/activities.d.ts +2367 -4
  33. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  34. package/dist/apps/data/routes/activities.js +368 -87
  35. package/dist/apps/data/routes/activities.js.map +1 -1
  36. package/dist/apps/data/routes/addresses.d.ts +471 -340
  37. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  38. package/dist/apps/data/routes/balances.d.ts +28 -28
  39. package/dist/apps/data/routes/balances.js +1 -1
  40. package/dist/apps/data/routes/balances.js.map +1 -1
  41. package/dist/apps/data/routes/blocks.d.ts +4 -4
  42. package/dist/apps/data/routes/blocks.js +1 -1
  43. package/dist/apps/data/routes/blocks.js.map +1 -1
  44. package/dist/apps/data/routes/coingecko.d.ts +176 -176
  45. package/dist/apps/data/routes/earn.d.ts +131 -6
  46. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  47. package/dist/apps/data/routes/earn.js +157 -25
  48. package/dist/apps/data/routes/earn.js.map +1 -1
  49. package/dist/apps/data/routes/exchanges.d.ts +825 -119
  50. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  51. package/dist/apps/data/routes/exchanges.js +16 -13
  52. package/dist/apps/data/routes/exchanges.js.map +1 -1
  53. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  54. package/dist/apps/data/routes/fee-amm.js +9 -11
  55. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  56. package/dist/apps/data/routes/indexer.d.ts +34 -34
  57. package/dist/apps/data/routes/receipts.d.ts +230 -230
  58. package/dist/apps/data/routes/receipts.js +1 -1
  59. package/dist/apps/data/routes/receipts.js.map +1 -1
  60. package/dist/apps/data/routes/rpc.js +24 -10
  61. package/dist/apps/data/routes/rpc.js.map +1 -1
  62. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
  63. package/dist/apps/data/routes/tokenlist.js +16 -15
  64. package/dist/apps/data/routes/tokenlist.js.map +1 -1
  65. package/dist/apps/data/routes/tokens.d.ts +14 -0
  66. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  67. package/dist/apps/data/routes/tokens.js +34 -15
  68. package/dist/apps/data/routes/tokens.js.map +1 -1
  69. package/dist/apps/data/routes/transactions.js +1 -1
  70. package/dist/apps/data/routes/transactions.js.map +1 -1
  71. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  72. package/dist/apps/data/routes/transfers.js +7 -9
  73. package/dist/apps/data/routes/transfers.js.map +1 -1
  74. package/dist/apps/data/routes/webhooks.d.ts +4 -4
  75. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  76. package/dist/apps/data/routes/webhooks.js +2 -2
  77. package/dist/apps/data/routes/webhooks.js.map +1 -1
  78. package/dist/apps/data/routes/zones.d.ts +30 -30
  79. package/dist/apps/funding/App.d.ts +27 -27
  80. package/dist/apps/funding/routes/chains.d.ts.map +1 -1
  81. package/dist/apps/funding/routes/chains.js +5 -4
  82. package/dist/apps/funding/routes/chains.js.map +1 -1
  83. package/dist/apps/funding/routes/deposit-addresses.d.ts +16 -16
  84. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
  85. package/dist/apps/funding/routes/deposit-addresses.js +8 -8
  86. package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
  87. package/dist/apps/funding/routes/providers.d.ts +13 -13
  88. package/dist/apps/funding/routes/providers.d.ts.map +1 -1
  89. package/dist/apps/funding/routes/providers.js +3 -2
  90. package/dist/apps/funding/routes/providers.js.map +1 -1
  91. package/dist/apps/funding/routes/quotes.d.ts.map +1 -1
  92. package/dist/apps/funding/routes/quotes.js +2 -2
  93. package/dist/apps/funding/routes/quotes.js.map +1 -1
  94. package/dist/apps/management/App.d.ts +627 -293
  95. package/dist/apps/management/App.d.ts.map +1 -1
  96. package/dist/apps/management/Billing.d.ts.map +1 -1
  97. package/dist/apps/management/Billing.js +19 -6
  98. package/dist/apps/management/Billing.js.map +1 -1
  99. package/dist/apps/management/routes/api-keys.d.ts +114 -54
  100. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  101. package/dist/apps/management/routes/api-keys.js +76 -30
  102. package/dist/apps/management/routes/api-keys.js.map +1 -1
  103. package/dist/apps/management/routes/billing.d.ts +103 -30
  104. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  105. package/dist/apps/management/routes/billing.js +91 -32
  106. package/dist/apps/management/routes/billing.js.map +1 -1
  107. package/dist/apps/management/routes/faucet.d.ts +33 -33
  108. package/dist/apps/management/routes/faucet.d.ts.map +1 -1
  109. package/dist/apps/management/routes/faucet.js +23 -12
  110. package/dist/apps/management/routes/faucet.js.map +1 -1
  111. package/dist/apps/management/routes/invitations.d.ts +46 -13
  112. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  113. package/dist/apps/management/routes/invitations.js +79 -29
  114. package/dist/apps/management/routes/invitations.js.map +1 -1
  115. package/dist/apps/management/routes/invite-links.d.ts +86 -41
  116. package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
  117. package/dist/apps/management/routes/invite-links.js +100 -41
  118. package/dist/apps/management/routes/invite-links.js.map +1 -1
  119. package/dist/apps/management/routes/me.d.ts +5 -5
  120. package/dist/apps/management/routes/me.d.ts.map +1 -1
  121. package/dist/apps/management/routes/me.js +14 -10
  122. package/dist/apps/management/routes/me.js.map +1 -1
  123. package/dist/apps/management/routes/members.d.ts +93 -72
  124. package/dist/apps/management/routes/members.d.ts.map +1 -1
  125. package/dist/apps/management/routes/members.js +37 -18
  126. package/dist/apps/management/routes/members.js.map +1 -1
  127. package/dist/apps/management/routes/orgs.d.ts +48 -8
  128. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  129. package/dist/apps/management/routes/orgs.js +48 -19
  130. package/dist/apps/management/routes/orgs.js.map +1 -1
  131. package/dist/apps/management/routes/projects.d.ts +90 -28
  132. package/dist/apps/management/routes/projects.d.ts.map +1 -1
  133. package/dist/apps/management/routes/projects.js +72 -21
  134. package/dist/apps/management/routes/projects.js.map +1 -1
  135. package/dist/apps/management/routes/scopes.d.ts.map +1 -1
  136. package/dist/apps/management/routes/scopes.js +8 -5
  137. package/dist/apps/management/routes/scopes.js.map +1 -1
  138. package/dist/apps/management/routes/usage.d.ts +78 -78
  139. package/dist/apps/management/routes/usage.d.ts.map +1 -1
  140. package/dist/apps/management/routes/usage.js +41 -31
  141. package/dist/apps/management/routes/usage.js.map +1 -1
  142. package/dist/apps/management/routes/verified-token-requests.d.ts +34 -6
  143. package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -1
  144. package/dist/apps/management/routes/verified-token-requests.js +36 -17
  145. package/dist/apps/management/routes/verified-token-requests.js.map +1 -1
  146. package/dist/apps/management/routes/webhooks.d.ts +147 -130
  147. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  148. package/dist/apps/management/routes/webhooks.js +80 -35
  149. package/dist/apps/management/routes/webhooks.js.map +1 -1
  150. package/dist/apps/mpp/internal/Adapter.d.ts +1 -1
  151. package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
  152. package/dist/apps/mpp/internal/Adapter.js +12 -0
  153. package/dist/apps/mpp/internal/Adapter.js.map +1 -1
  154. package/dist/db/Db.d.ts +2 -0
  155. package/dist/db/Db.d.ts.map +1 -1
  156. package/dist/db/Db.js +40 -0
  157. package/dist/db/Db.js.map +1 -1
  158. package/dist/db/Schema.d.ts +23 -1
  159. package/dist/db/Schema.d.ts.map +1 -1
  160. package/dist/db/Schema.js +19 -3
  161. package/dist/db/Schema.js.map +1 -1
  162. package/dist/db/tables/earnVaults.d.ts +5 -1
  163. package/dist/db/tables/earnVaults.d.ts.map +1 -1
  164. package/dist/db/tables/earnVaults.js +5 -0
  165. package/dist/db/tables/earnVaults.js.map +1 -1
  166. package/dist/db/tables/webhookDeliveries.d.ts.map +1 -1
  167. package/dist/db/tables/webhookDeliveries.js +10 -0
  168. package/dist/db/tables/webhookDeliveries.js.map +1 -1
  169. package/dist/db/tables/webhookQueueCompletions.d.ts +15 -0
  170. package/dist/db/tables/webhookQueueCompletions.d.ts.map +1 -0
  171. package/dist/db/tables/webhookQueueCompletions.js +32 -0
  172. package/dist/db/tables/webhookQueueCompletions.js.map +1 -0
  173. package/dist/db/tables/webhookQueueEvents.d.ts +14 -20
  174. package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
  175. package/dist/db/tables/webhookQueueEvents.js +68 -47
  176. package/dist/db/tables/webhookQueueEvents.js.map +1 -1
  177. package/dist/db/tables/webhookSubscriptions.d.ts +16 -5
  178. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  179. package/dist/db/tables/webhookSubscriptions.js +29 -5
  180. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  181. package/dist/internal/Auth.d.ts +8 -1
  182. package/dist/internal/Auth.d.ts.map +1 -1
  183. package/dist/internal/Auth.js +13 -3
  184. package/dist/internal/Auth.js.map +1 -1
  185. package/dist/internal/Cache.d.ts.map +1 -1
  186. package/dist/internal/Cache.js +11 -7
  187. package/dist/internal/Cache.js.map +1 -1
  188. package/dist/internal/EarnVaults.d.ts +41 -2
  189. package/dist/internal/EarnVaults.d.ts.map +1 -1
  190. package/dist/internal/EarnVaults.js +167 -0
  191. package/dist/internal/EarnVaults.js.map +1 -1
  192. package/dist/internal/OpenApi.d.ts +26 -22
  193. package/dist/internal/OpenApi.d.ts.map +1 -1
  194. package/dist/internal/OpenApi.js +128 -39
  195. package/dist/internal/OpenApi.js.map +1 -1
  196. package/dist/internal/Response.d.ts +13 -8
  197. package/dist/internal/Response.d.ts.map +1 -1
  198. package/dist/internal/Response.js +16 -5
  199. package/dist/internal/Response.js.map +1 -1
  200. package/dist/internal/Schema.d.ts +5 -7
  201. package/dist/internal/Schema.d.ts.map +1 -1
  202. package/dist/internal/Schema.js +6 -7
  203. package/dist/internal/Schema.js.map +1 -1
  204. package/dist/internal/Store.d.ts +15 -6
  205. package/dist/internal/Store.d.ts.map +1 -1
  206. package/dist/internal/Store.js +45 -15
  207. package/dist/internal/Store.js.map +1 -1
  208. package/dist/internal/Tidx.d.ts +2 -0
  209. package/dist/internal/Tidx.d.ts.map +1 -1
  210. package/dist/internal/Tidx.js +16 -4
  211. package/dist/internal/Tidx.js.map +1 -1
  212. package/dist/internal/Webhooks.d.ts +29 -12
  213. package/dist/internal/Webhooks.d.ts.map +1 -1
  214. package/dist/internal/Webhooks.js +37 -20
  215. package/dist/internal/Webhooks.js.map +1 -1
  216. package/dist/internal/funding/Deposit.d.ts +2 -2
  217. package/dist/internal/funding/Deposit.d.ts.map +1 -1
  218. package/dist/internal/funding/Deposit.js +9 -8
  219. package/dist/internal/funding/Deposit.js.map +1 -1
  220. package/dist/internal/funding/DepositAddress.d.ts +4 -4
  221. package/dist/internal/funding/DepositAddress.d.ts.map +1 -1
  222. package/dist/internal/funding/DepositAddress.js +9 -8
  223. package/dist/internal/funding/DepositAddress.js.map +1 -1
  224. package/dist/internal/funding/Provider.d.ts.map +1 -1
  225. package/dist/internal/funding/Provider.js +13 -12
  226. package/dist/internal/funding/Provider.js.map +1 -1
  227. package/dist/internal/funding/providers/stargate.d.ts +19 -0
  228. package/dist/internal/funding/providers/stargate.d.ts.map +1 -0
  229. package/dist/internal/funding/providers/stargate.js +143 -0
  230. package/dist/internal/funding/providers/stargate.js.map +1 -0
  231. package/dist/providers/index.d.ts +7 -0
  232. package/dist/providers/index.d.ts.map +1 -1
  233. package/dist/providers/index.js +3 -0
  234. package/dist/providers/index.js.map +1 -1
  235. package/package.json +2 -2
  236. package/src/App.test.ts +48 -0
  237. package/src/App.ts +4 -2
  238. package/src/Assets.test.ts +18 -0
  239. package/src/Assets.ts +22 -0
  240. package/src/Client.test-d.ts +265 -16
  241. package/src/admin/apps/earn-vaults.test.ts +143 -0
  242. package/src/admin/apps/earn-vaults.ts +83 -6
  243. package/src/admin/apps/organizations.ts +6 -2
  244. package/src/admin/ui/src/routes/api-keys.tsx +36 -0
  245. package/src/admin/ui.generated.ts +151 -189
  246. package/src/apps/data/App.test.ts +31 -0
  247. package/src/apps/data/App.ts +56 -8
  248. package/src/apps/data/ZoneSelection.test.ts +196 -0
  249. package/src/apps/data/ZoneSelection.ts +108 -0
  250. package/src/apps/data/routes/activities.test.ts +328 -1
  251. package/src/apps/data/routes/activities.ts +498 -93
  252. package/src/apps/data/routes/balances.ts +1 -1
  253. package/src/apps/data/routes/blocks.ts +1 -1
  254. package/src/apps/data/routes/earn.test.ts +183 -23
  255. package/src/apps/data/routes/earn.ts +225 -29
  256. package/src/apps/data/routes/earn.zone-access.test.ts +46 -0
  257. package/src/apps/data/routes/exchanges.test.ts +31 -1
  258. package/src/apps/data/routes/exchanges.ts +19 -15
  259. package/src/apps/data/routes/fee-amm.ts +18 -11
  260. package/src/apps/data/routes/receipts.ts +1 -1
  261. package/src/apps/data/routes/rpc.test.ts +75 -0
  262. package/src/apps/data/routes/rpc.ts +23 -9
  263. package/src/apps/data/routes/tokenlist.ts +21 -18
  264. package/src/apps/data/routes/tokens.test.ts +29 -0
  265. package/src/apps/data/routes/tokens.ts +38 -15
  266. package/src/apps/data/routes/transactions.ts +1 -1
  267. package/src/apps/data/routes/transfers.test.ts +3 -3
  268. package/src/apps/data/routes/transfers.ts +12 -9
  269. package/src/apps/data/routes/webhooks.ts +6 -2
  270. package/src/apps/data/routes/zones.test.ts +19 -13
  271. package/src/apps/funding/routes/chains.test.ts +211 -7
  272. package/src/apps/funding/routes/chains.ts +19 -12
  273. package/src/apps/funding/routes/deposit-addresses.test.ts +394 -0
  274. package/src/apps/funding/routes/deposit-addresses.ts +84 -72
  275. package/src/apps/funding/routes/providers.ts +9 -5
  276. package/src/apps/funding/routes/quotes.test.ts +46 -0
  277. package/src/apps/funding/routes/quotes.ts +8 -5
  278. package/src/apps/funding/routes/transfers.test.ts +35 -32
  279. package/src/apps/management/Billing.test.ts +26 -0
  280. package/src/apps/management/Billing.ts +24 -7
  281. package/src/apps/management/routes/api-keys.test.ts +178 -0
  282. package/src/apps/management/routes/api-keys.ts +203 -144
  283. package/src/apps/management/routes/billing.test.ts +209 -0
  284. package/src/apps/management/routes/billing.ts +254 -177
  285. package/src/apps/management/routes/faucet.test.ts +39 -0
  286. package/src/apps/management/routes/faucet.ts +75 -41
  287. package/src/apps/management/routes/invitations.test.ts +216 -0
  288. package/src/apps/management/routes/invitations.ts +186 -109
  289. package/src/apps/management/routes/invite-links.test.ts +263 -1
  290. package/src/apps/management/routes/invite-links.ts +295 -214
  291. package/src/apps/management/routes/me.test.ts +49 -0
  292. package/src/apps/management/routes/me.ts +42 -32
  293. package/src/apps/management/routes/members.test.ts +124 -1
  294. package/src/apps/management/routes/members.ts +81 -51
  295. package/src/apps/management/routes/orgs.test.ts +176 -0
  296. package/src/apps/management/routes/orgs.ts +101 -59
  297. package/src/apps/management/routes/projects.test.ts +183 -0
  298. package/src/apps/management/routes/projects.ts +124 -60
  299. package/src/apps/management/routes/scopes.test.ts +44 -0
  300. package/src/apps/management/routes/scopes.ts +37 -28
  301. package/src/apps/management/routes/usage.test.ts +92 -0
  302. package/src/apps/management/routes/usage.ts +186 -151
  303. package/src/apps/management/routes/verified-token-requests.test.ts +114 -0
  304. package/src/apps/management/routes/verified-token-requests.ts +166 -133
  305. package/src/apps/management/routes/webhooks.test.ts +234 -0
  306. package/src/apps/management/routes/webhooks.ts +191 -78
  307. package/src/apps/mpp/internal/Adapter.ts +13 -1
  308. package/src/db/Db.test.ts +41 -0
  309. package/src/db/Db.ts +62 -0
  310. package/src/db/Schema.ts +28 -3
  311. package/src/db/tables/earnVaults.test.ts +22 -0
  312. package/src/db/tables/earnVaults.ts +11 -1
  313. package/src/db/tables/webhookDeliveries.ts +13 -0
  314. package/src/db/tables/webhookQueueCompletions.ts +55 -0
  315. package/src/db/tables/webhookQueueEvents.ts +95 -61
  316. package/src/db/tables/webhookSubscriptions.ts +45 -6
  317. package/src/internal/Auth.ts +23 -5
  318. package/src/internal/Cache.ts +14 -7
  319. package/src/internal/EarnVaults.capabilities.test.ts +33 -0
  320. package/src/internal/EarnVaults.test.ts +79 -0
  321. package/src/internal/EarnVaults.ts +277 -0
  322. package/src/internal/OpenApi.ts +165 -49
  323. package/src/internal/Response.test.ts +95 -0
  324. package/src/internal/Response.ts +40 -2
  325. package/src/internal/Schema.test.ts +16 -0
  326. package/src/internal/Schema.ts +7 -7
  327. package/src/internal/Store.test.ts +186 -0
  328. package/src/internal/Store.ts +57 -17
  329. package/src/internal/Tidx.test.ts +19 -0
  330. package/src/internal/Tidx.ts +16 -4
  331. package/src/internal/Webhooks.test.ts +122 -8
  332. package/src/internal/Webhooks.ts +59 -29
  333. package/src/internal/funding/Deposit.ts +71 -64
  334. package/src/internal/funding/DepositAddress.ts +50 -43
  335. package/src/internal/funding/Provider.ts +164 -142
  336. package/src/internal/funding/providers/index.test.ts +185 -0
  337. package/src/internal/funding/providers/stargate.ts +184 -0
  338. package/src/providers/index.test.ts +1 -0
  339. package/src/providers/index.ts +9 -0
@@ -18,7 +18,26 @@ const redactedSlackUrl = 'https://hooks.slack.com/…'
18
18
  /** OpenAPI schemas owned by console webhook management. */
19
19
  export namespace schema {
20
20
  /** Public webhook subscription representation. */
21
- export const Subscription = webhookSchema.Subscription
21
+ export const Subscription = OpenApi.component(
22
+ z
23
+ .object({
24
+ ...webhookSchema.Subscription.shape,
25
+ context: webhookSchema.Subscription.shape.context.check(
26
+ z.meta({ examples: [{ title: 'Production transfers' }] }),
27
+ ),
28
+ destination: webhookSchema.Subscription.shape.destination.check(
29
+ z.meta({ examples: [{ type: 'url', url: 'https://example.com/webhooks' }] }),
30
+ ),
31
+ })
32
+ .check(z.describe('An organization webhook subscription.')),
33
+ 'OrganizationWebhook',
34
+ )
35
+
36
+ /** Webhook delivery data returned in collection rows. */
37
+ export const Delivery = OpenApi.component(
38
+ webhookSchema.Delivery,
39
+ 'OrganizationWebhookDeliverySummary',
40
+ )
22
41
 
23
42
  /** Schemas for creating an organization webhook. */
24
43
  export namespace createWebhook {
@@ -30,61 +49,119 @@ export namespace schema {
30
49
  )
31
50
 
32
51
  /** Organization webhook creation requires an explicit network. */
33
- export const Body = z
34
- .intersection(
35
- webhookSchema.createWebhook.Body,
36
- z.object({
37
- chainId: webhookSchema.Subscription.shape.chainId,
38
- environment: z.optional(Environment),
39
- }),
40
- )
41
- .check(
42
- z.refine(
43
- (body) => body.eventType !== 'funding:deposit.updated' || body.environment !== undefined,
44
- { error: '`environment` is required for funding deposit subscriptions.' },
52
+ export const Body = OpenApi.component(
53
+ z
54
+ .intersection(
55
+ webhookSchema.createWebhook.Body,
56
+ z.object({
57
+ chainId: webhookSchema.Subscription.shape.chainId,
58
+ environment: z.optional(Environment),
59
+ }),
60
+ )
61
+ .check(
62
+ z.refine(
63
+ (body) =>
64
+ body.eventType !== 'funding:deposit.updated' || body.environment !== undefined,
65
+ { error: '`environment` is required for funding deposit subscriptions.' },
66
+ ),
67
+ z.describe('Details for creating an organization webhook subscription.'),
45
68
  ),
46
- z.describe('Details for creating an organization webhook subscription.'),
47
- )
69
+ 'CreateOrganizationWebhookRequest',
70
+ )
48
71
 
49
72
  const secret = webhookSchema.createWebhook.Response.shape.secret
50
- const urlDestination = z.object({
51
- type: z.literal('url'),
52
- url: z
53
- .url()
54
- .check(
55
- z.describe('Your HTTPS endpoint where Tempo sends signed event POSTs.'),
56
- z.meta({ examples: ['https://example.com/webhooks'] }),
57
- ),
58
- })
59
- const slackDestination = z.object({
60
- type: z.literal('slack'),
61
- url: z.literal(redactedSlackUrl).check(z.describe('Redacted Slack webhook URL.')),
62
- })
63
- const betterstackDestination = z.object({
64
- token: z
65
- .literal(redactedBetterstackToken)
66
- .check(z.describe('Redacted Better Stack source token.')),
67
- type: z.literal('betterstack'),
68
- url: z
69
- .url()
70
- .check(
71
- z.describe('Your Better Stack source ingest host URL.'),
72
- z.meta({ examples: ['https://s1234567.eu-nbg-2.betterstackdata.com'] }),
73
- ),
74
- })
73
+ const urlDestination = z
74
+ .object({
75
+ type: z
76
+ .literal('url')
77
+ .check(z.describe('HTTPS destination.'), z.meta({ examples: ['url'] })),
78
+ url: z
79
+ .url()
80
+ .check(
81
+ z.describe('Your HTTPS endpoint where Tempo sends signed event POSTs.'),
82
+ z.meta({ examples: ['https://example.com/webhooks'] }),
83
+ ),
84
+ })
85
+ .check(
86
+ z.describe('HTTPS webhook destination.'),
87
+ z.meta({ examples: [{ type: 'url', url: 'https://example.com/webhooks' }] }),
88
+ )
89
+ const slackDestination = z
90
+ .object({
91
+ type: z
92
+ .literal('slack')
93
+ .check(z.describe('Slack destination.'), z.meta({ examples: ['slack'] })),
94
+ url: z
95
+ .literal(redactedSlackUrl)
96
+ .check(
97
+ z.describe('Redacted Slack webhook URL.'),
98
+ z.meta({ examples: [redactedSlackUrl] }),
99
+ ),
100
+ })
101
+ .check(
102
+ z.describe('Slack webhook destination with its URL redacted.'),
103
+ z.meta({ examples: [{ type: 'slack', url: redactedSlackUrl }] }),
104
+ )
105
+ const betterstackDestination = z
106
+ .object({
107
+ token: z
108
+ .literal(redactedBetterstackToken)
109
+ .check(
110
+ z.describe('Redacted Better Stack source token.'),
111
+ z.meta({ examples: [redactedBetterstackToken] }),
112
+ ),
113
+ type: z
114
+ .literal('betterstack')
115
+ .check(z.describe('Better Stack destination.'), z.meta({ examples: ['betterstack'] })),
116
+ url: z
117
+ .url()
118
+ .check(
119
+ z.describe('Your Better Stack source ingest host URL.'),
120
+ z.meta({ examples: ['https://s1234567.eu-nbg-2.betterstackdata.com'] }),
121
+ ),
122
+ })
123
+ .check(
124
+ z.describe('Better Stack destination with its token redacted.'),
125
+ z.meta({
126
+ examples: [
127
+ {
128
+ token: redactedBetterstackToken,
129
+ type: 'betterstack',
130
+ url: 'https://s1234567.eu-nbg-2.betterstackdata.com',
131
+ },
132
+ ],
133
+ }),
134
+ )
135
+ const UrlResponse = OpenApi.component(
136
+ z
137
+ .object({ ...Subscription.shape, destination: urlDestination, secret })
138
+ .check(z.describe('Created HTTPS webhook with its one-time signing secret.')),
139
+ 'CreateOrganizationWebhookUrlResponse',
140
+ )
141
+ const SlackResponse = OpenApi.component(
142
+ z
143
+ .object({ ...Subscription.shape, destination: slackDestination })
144
+ .check(z.describe('Created Slack webhook with its destination redacted.')),
145
+ 'CreateOrganizationWebhookSlackResponse',
146
+ )
147
+ const BetterstackResponse = OpenApi.component(
148
+ z
149
+ .object({ ...Subscription.shape, destination: betterstackDestination })
150
+ .check(z.describe('Created Better Stack webhook with its credentials redacted.')),
151
+ 'CreateOrganizationWebhookBetterstackResponse',
152
+ )
75
153
 
76
154
  /** Created webhook with transport credentials redacted. */
77
- export const Response = z
78
- .union([
79
- z.object({ ...Subscription.shape, destination: urlDestination, secret }),
80
- z.object({ ...Subscription.shape, destination: slackDestination }),
81
- z.object({ ...Subscription.shape, destination: betterstackDestination }),
82
- ])
83
- .check(
84
- z.describe(
85
- 'The new webhook subscription. HTTPS endpoints include their one-time signing secret.',
155
+ export const Response = OpenApi.component(
156
+ z
157
+ .union([UrlResponse, SlackResponse, BetterstackResponse])
158
+ .check(
159
+ z.describe(
160
+ 'The new webhook subscription. HTTPS endpoints include their one-time signing secret.',
161
+ ),
86
162
  ),
87
- )
163
+ 'CreateOrganizationWebhookResponse',
164
+ )
88
165
  }
89
166
 
90
167
  /** Organization webhook collection parameters. */
@@ -116,7 +193,23 @@ export namespace schema {
116
193
  export namespace listWebhookDeliveries {
117
194
  export const Params = WebhookParams
118
195
  export const Query = webhookSchema.listWebhookDeliveries.Query
119
- export const Response = webhookSchema.listWebhookDeliveries.Response
196
+ export const Response = OpenApi.component(
197
+ z
198
+ .object({
199
+ data: z
200
+ .array(Delivery)
201
+ .check(
202
+ z.describe('Webhook delivery attempts on this page.'),
203
+ z.meta({ examples: [[]] }),
204
+ ),
205
+ meta: webhookSchema.listWebhookDeliveries.Response.shape.meta.check(
206
+ z.meta({ examples: [{ totalCount: 1, totalCountCapped: false }] }),
207
+ ),
208
+ nextCursor: webhookSchema.listWebhookDeliveries.Response.shape.nextCursor,
209
+ })
210
+ .check(z.describe("A page of an organization's webhook delivery attempts.")),
211
+ 'OrganizationWebhookDeliveryList',
212
+ )
120
213
  }
121
214
 
122
215
  const DeliveryParams = z
@@ -130,43 +223,63 @@ export namespace schema {
130
223
  /** Schemas for loading one organization webhook delivery. */
131
224
  export namespace getWebhookDelivery {
132
225
  export const Params = DeliveryParams
133
- export const Response = z
134
- .object({
135
- ...webhookSchema.Delivery.shape,
136
- envelope: webhookSchema.Envelope,
137
- })
138
- .check(z.describe('A webhook delivery attempt with the exact JSON payload Tempo sent.'))
226
+ export const Response = OpenApi.component(
227
+ z
228
+ .object({
229
+ ...Delivery.shape,
230
+ envelope: webhookSchema.Envelope,
231
+ })
232
+ .check(z.describe('A webhook delivery attempt with the exact JSON payload Tempo sent.')),
233
+ 'OrganizationWebhookDelivery',
234
+ )
139
235
  }
140
236
 
141
237
  /** Schemas for replaying one organization webhook delivery. */
142
238
  export namespace retryWebhookDelivery {
143
239
  export const Params = DeliveryParams
144
- export const Response = webhookSchema.retryWebhookDelivery.Response
240
+ export const Response = OpenApi.component(
241
+ webhookSchema.retryWebhookDelivery.Response,
242
+ 'RetryOrganizationWebhookDeliveryResponse',
243
+ )
145
244
  }
146
245
 
147
246
  /** Non-paginated organization webhook list. */
148
- export const ListResponse = z
149
- .object({
150
- data: z.array(Subscription).check(z.describe("The organization's webhooks, newest first.")),
151
- })
152
- .check(z.describe("An organization's webhook subscriptions."))
247
+ export const ListResponse = OpenApi.component(
248
+ z
249
+ .object({
250
+ data: z
251
+ .array(Subscription)
252
+ .check(
253
+ z.describe("The organization's webhooks, newest first."),
254
+ z.meta({ examples: [[]] }),
255
+ ),
256
+ })
257
+ .check(z.describe("An organization's webhook subscriptions.")),
258
+ 'OrganizationWebhookList',
259
+ )
153
260
 
154
261
  /** Lifecycle state editable from the console. */
155
- export const UpdateBody = z
156
- .object({ status: webhookSchema.Status })
157
- .check(z.describe('New delivery state for a webhook subscription.'))
262
+ export const UpdateBody = OpenApi.component(
263
+ z
264
+ .object({ status: webhookSchema.Status })
265
+ .check(z.describe('New delivery state for a webhook subscription.')),
266
+ 'UpdateOrganizationWebhookRequest',
267
+ )
158
268
 
159
269
  /** Webhook deletion confirmation. */
160
- export const DeleteResponse = z
161
- .object({
162
- id: z
163
- .string()
164
- .check(
165
- z.describe('Deleted webhook subscription id.'),
166
- z.meta({ examples: ['wh_001718668800000_2ZPE2gvateYEQ0dQslgvkhjx'] }),
167
- ),
168
- })
169
- .check(z.describe('Confirmation that a webhook subscription was deleted.'))
270
+ export const DeleteResponse = OpenApi.component(
271
+ z
272
+ .object({
273
+ id: z
274
+ .string()
275
+ .check(
276
+ z.describe('Deleted webhook subscription id.'),
277
+ z.meta({ examples: ['wh_001718668800000_2ZPE2gvateYEQ0dQslgvkhjx'] }),
278
+ ),
279
+ })
280
+ .check(z.describe('Confirmation that a webhook subscription was deleted.')),
281
+ 'DeleteOrganizationWebhookResponse',
282
+ )
170
283
  }
171
284
 
172
285
  const bodyValidation = {
@@ -542,7 +655,7 @@ export function webhooks(options: webhooks.Options = {}) {
542
655
  },
543
656
  success: {
544
657
  description: 'Updated webhook.',
545
- schema: webhookSchema.Subscription,
658
+ schema: schema.Subscription,
546
659
  },
547
660
  }),
548
661
  summary: 'Update webhook',
@@ -564,7 +677,7 @@ export function webhooks(options: webhooks.Options = {}) {
564
677
  )
565
678
  if (!subscription) return notFound(c)
566
679
  return c.json(
567
- Response.validated(webhookSchema.Subscription, publicSubscription(subscription)),
680
+ Response.validated(schema.Subscription, publicSubscription(subscription)),
568
681
  200,
569
682
  )
570
683
  } catch (cause) {
@@ -1,9 +1,10 @@
1
1
  import * as Mppx from 'mppx'
2
2
  import { tempo } from 'mppx/server'
3
+ import { maxUint256 } from 'viem'
3
4
  import type { LocalAccount } from 'viem/accounts'
4
5
 
5
- import type * as Viem from '../../../internal/Viem.js'
6
6
  import * as Store from '../../../internal/Store.js'
7
+ import * as Viem from '../../../internal/Viem.js'
7
8
  import * as Mpp from '../Mpp.js'
8
9
  import * as MppError from './Error.js'
9
10
  import * as Screening from './Screening.js'
@@ -37,9 +38,20 @@ export function create(options: create.Options): Adapter {
37
38
  store: screeningStore,
38
39
  ttl: screeningTtl,
39
40
  })
41
+ // Moderato fees exceed MPPX defaults; preserve its gas cap and bound each
42
+ // sponsored transaction to $1.
43
+ const feePayerPolicy =
44
+ chainId === Viem.chainId.testnet
45
+ ? {
46
+ maxFeePerGas: maxUint256,
47
+ maxPriorityFeePerGas: maxUint256,
48
+ maxTotalFee: 1_000_000_000_000_000_000n,
49
+ }
50
+ : undefined
40
51
  const methods = [
41
52
  tempo.charge({
42
53
  ...(feePayer ? { feePayer } : {}),
54
+ ...(feePayerPolicy ? { feePayerPolicy } : {}),
43
55
  getClient: ({ chainId: chainId_option }) =>
44
56
  getClient(chainId_option as Viem.ChainId | undefined),
45
57
  store: Utils.mppxStore(state),
package/src/db/Db.test.ts CHANGED
@@ -3,6 +3,7 @@ import { nanoid } from 'nanoid'
3
3
 
4
4
  import * as Runtime from '../../test/runtime.js'
5
5
  import * as Db from './Db.js'
6
+ import * as Webhooks from '../internal/Webhooks.js'
6
7
  import * as Invitations from './tables/invitations.js'
7
8
  import * as Organizations from './tables/organizations.js'
8
9
  import * as WebhookSubscriptions from './tables/webhookSubscriptions.js'
@@ -35,6 +36,46 @@ describe('postgres', () => {
35
36
  await db.close()
36
37
  })
37
38
 
39
+ test('behavior: raw webhook staging respects schema scoping', async () => {
40
+ // Exercises the one raw statement against a schema-scoped db whose only
41
+ // qualification is WithSchemaPlugin, matching preview Workers: migrate on
42
+ // one pool (whose session gains a search_path), then query from a fresh
43
+ // pool that never ran it.
44
+ const schema = `t_${nanoid()}`
45
+ const migrator = Db.postgres({ connectionString: Runtime.postgresUrl, schema })
46
+ await migrator.migrate()
47
+ await migrator.close()
48
+ const db = Db.postgres({ connectionString: Runtime.postgresUrl, schema })
49
+ const subscription = await Webhooks.createSubscription(db, {
50
+ chainId: 4217,
51
+ destination: { type: 'url', url: 'https://hooks.example.com/endpoint' },
52
+ eventType: 'token:transfer',
53
+ owner: { orgId: 'org_1', type: 'api_key' },
54
+ })
55
+ const envelope = Webhooks.buildEnvelope({
56
+ blockNumber: 123,
57
+ createdAt: new Date(),
58
+ data: { amount: '1' },
59
+ logIndex: 4,
60
+ subscription,
61
+ })
62
+
63
+ const staged = await Webhooks.ensureQueueEvents(db, [{ envelope, subscription }])
64
+ expect(staged.created).toBe(1)
65
+ const claim = await Webhooks.claimQueueEvent(db, staged.references[0]!)
66
+ if (claim.type !== 'claimed') throw new Error(`Unexpected claim: ${claim.type}`)
67
+ await Webhooks.completeQueueEvent(db, staged.references[0]!, {
68
+ claimedAt: claim.record.attemptingAt!,
69
+ status: 'succeeded',
70
+ })
71
+
72
+ // The completion read inside the same raw statement must also resolve
73
+ // inside the schema for the replay to be suppressed.
74
+ const replay = await Webhooks.ensureQueueEvents(db, [{ envelope, subscription }])
75
+ expect(replay.created).toBe(0)
76
+ await db.close()
77
+ })
78
+
38
79
  test('behavior: creates a Postgres db without schema scoping', async () => {
39
80
  const db = Db.postgres({ connectionString: Runtime.postgresUrl })
40
81
  await db.close()
package/src/db/Db.ts CHANGED
@@ -36,6 +36,7 @@ import type * as Users from './tables/users.js'
36
36
  import type * as VerifiedTokenRequests from './tables/verifiedTokenRequests.js'
37
37
  import type * as VerifiedTokens from './tables/verifiedTokens.js'
38
38
  import type * as WebhookDeliveries from './tables/webhookDeliveries.js'
39
+ import type * as WebhookQueueCompletions from './tables/webhookQueueCompletions.js'
39
40
  import type * as WebhookQueueEvents from './tables/webhookQueueEvents.js'
40
41
  import type * as WebhookSubscriptions from './tables/webhookSubscriptions.js'
41
42
 
@@ -83,6 +84,7 @@ export type Database = {
83
84
  verified_token_requests: VerifiedTokenRequests.Table
84
85
  verified_tokens: VerifiedTokens.Table
85
86
  webhook_deliveries: WebhookDeliveries.Table
87
+ webhook_queue_completions: WebhookQueueCompletions.Table
86
88
  webhook_queue_events: WebhookQueueEvents.Table
87
89
  webhook_subscriptions: WebhookSubscriptions.Table
88
90
  }
@@ -1421,6 +1423,66 @@ export const migrations: readonly Migration[] = [
1421
1423
  up: (db) =>
1422
1424
  raw(db, sql`ALTER TABLE webhook_queue_events ADD COLUMN IF NOT EXISTS observed_at text`),
1423
1425
  },
1426
+ {
1427
+ name: '0131_webhook_queue_completions',
1428
+ up: (db) =>
1429
+ db.schema
1430
+ .createTable('webhookQueueCompletions')
1431
+ .ifNotExists()
1432
+ .addColumn('subscriptionId', 'text', (c) =>
1433
+ c.notNull().references('webhookSubscriptions.id').onDelete('cascade'),
1434
+ )
1435
+ .addColumn('eventId', 'text', (c) => c.notNull())
1436
+ .addColumn('status', 'text', (c) => c.notNull())
1437
+ .addColumn('expiresAt', 'text', (c) => c.notNull())
1438
+ .addPrimaryKeyConstraint('webhook_queue_completions_pkey', ['subscriptionId', 'eventId'])
1439
+ .addCheckConstraint(
1440
+ 'webhook_queue_completions_status_check',
1441
+ sql`status IN ('failed', 'skipped', 'succeeded')`,
1442
+ ),
1443
+ },
1444
+ {
1445
+ name: '0132_webhook_queue_completions_expires_index',
1446
+ up: (db) =>
1447
+ db.schema
1448
+ .createIndex('webhook_queue_completions_expires_idx')
1449
+ .ifNotExists()
1450
+ .on('webhookQueueCompletions')
1451
+ .column('expiresAt'),
1452
+ },
1453
+ {
1454
+ // Stock thresholds let ~2M dead tuples accumulate before vacuuming, then
1455
+ // grind gigabytes at once; frequent small vacuums keep indexes usable.
1456
+ name: '0133_webhook_queue_events_autovacuum',
1457
+ up: (db) =>
1458
+ raw(
1459
+ db,
1460
+ sql`ALTER TABLE webhook_queue_events SET (autovacuum_vacuum_scale_factor = 0.01, autovacuum_vacuum_cost_limit = 2000)`,
1461
+ ),
1462
+ },
1463
+ {
1464
+ // New code stops writing queue-event expiry: completions own the dedupe
1465
+ // window, so the column is nullable and inert.
1466
+ name: '0134_webhook_queue_events_expires_nullable',
1467
+ up: (db) =>
1468
+ raw(db, sql`ALTER TABLE webhook_queue_events ALTER COLUMN expires_at DROP NOT NULL`),
1469
+ },
1470
+ {
1471
+ name: '0135_webhook_queue_completions_autovacuum',
1472
+ up: (db) =>
1473
+ raw(
1474
+ db,
1475
+ sql`ALTER TABLE webhook_queue_completions SET (autovacuum_vacuum_scale_factor = 0.01, autovacuum_vacuum_cost_limit = 2000)`,
1476
+ ),
1477
+ },
1478
+ {
1479
+ name: '0136_earn_vault_zones',
1480
+ up: (db) =>
1481
+ raw(
1482
+ db,
1483
+ sql`ALTER TABLE earn_vaults ADD COLUMN IF NOT EXISTS zones jsonb NOT NULL DEFAULT '[]'::jsonb`,
1484
+ ),
1485
+ },
1424
1486
  ]
1425
1487
 
1426
1488
  /**
package/src/db/Schema.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as z from 'zod/mini'
2
2
  import type { Address } from 'viem'
3
3
 
4
+ import type * as EarnVaults from '../internal/EarnVaults.js'
4
5
  import type * as funding_Catalog from '../internal/funding/Catalog.js'
5
6
  import type * as funding_Deposit from '../internal/funding/Deposit.js'
6
7
  import type * as funding_DepositAddress from '../internal/funding/DepositAddress.js'
@@ -106,6 +107,9 @@ export const EarnVault = z
106
107
  slug: z.string().check(z.describe('Stable URL slug assigned when the vault is registered.')),
107
108
  updatedAt: z.string().check(z.describe('When the vault registration last changed (ISO 8601).')),
108
109
  vaultAddress: z.custom<Address>().check(z.describe('Earn vault address (lowercase).')),
110
+ zones: z
111
+ .custom<readonly EarnVaults.ZoneRoute[]>()
112
+ .check(z.describe('Curated private Zone routes (`jsonb`).')),
109
113
  })
110
114
  .check(z.describe('One curated Earn vault row.'))
111
115
 
@@ -881,6 +885,23 @@ export const WebhookDelivery = z.object({
881
885
  /** Columns of the `webhook_deliveries` table. */
882
886
  export type WebhookDelivery = z.output<typeof WebhookDelivery>
883
887
 
888
+ /** An insert-only dedupe record marking a delivery obligation terminal. */
889
+ export const WebhookQueueCompletion = z.object({
890
+ eventId: z.string().check(z.describe('Idempotent event id (`evt_...`).')),
891
+ expiresAt: z
892
+ .string()
893
+ .check(z.describe('ISO dedupe retention deadline; rows past it are invisible and pruned.')),
894
+ status: z
895
+ .enum(['failed', 'skipped', 'succeeded'])
896
+ .check(z.describe('Terminal delivery-obligation outcome.')),
897
+ subscriptionId: z
898
+ .string()
899
+ .check(z.describe('Owning subscription id (`wh_...`); FK with `ON DELETE CASCADE`.')),
900
+ })
901
+
902
+ /** Columns of the `webhook_queue_completions` table. */
903
+ export type WebhookQueueCompletion = z.output<typeof WebhookQueueCompletion>
904
+
884
905
  /** A durable webhook envelope staged before its compact Queue reference is admitted. */
885
906
  export const WebhookQueueEvent = z.object({
886
907
  attemptCount: z.number().check(z.describe('Delivery attempts claimed so far.')),
@@ -893,10 +914,10 @@ export const WebhookQueueEvent = z.object({
893
914
  .check(z.describe('The full webhook envelope retained outside Cloudflare Queues.')),
894
915
  eventId: z.string().check(z.describe('Idempotent event id (`evt_...`).')),
895
916
  expiresAt: z
896
- .string()
917
+ .nullable(z.string())
897
918
  .check(
898
919
  z.describe(
899
- 'ISO dedupe retention deadline, restarted at terminal transitions; inert while pending.',
920
+ 'Unwritten since completions took over dedupe; non-null only on rows staged before the split.',
900
921
  ),
901
922
  ),
902
923
  nextAttemptAt: z
@@ -911,7 +932,11 @@ export const WebhookQueueEvent = z.object({
911
932
  ),
912
933
  status: z
913
934
  .enum(['failed', 'pending', 'skipped', 'succeeded'])
914
- .check(z.describe('Delivery-obligation lifecycle status.')),
935
+ .check(
936
+ z.describe(
937
+ 'Always `pending` since completions took over terminal state; other values occur only on rows completed before the split.',
938
+ ),
939
+ ),
915
940
  subscriptionId: z
916
941
  .string()
917
942
  .check(z.describe('Owning subscription id (`wh_...`); FK with `ON DELETE CASCADE`.')),
@@ -11,6 +11,13 @@ const input = {
11
11
  ],
12
12
  privateOutputTokens: ['0x20c00000000000000000000057F242B21a53ae4D'],
13
13
  vaultAddress: '0x2f05f4d3625B2eFFb383eFbEDAFd2D8F04371636',
14
+ zones: [
15
+ {
16
+ chainId: 1_424_310_001,
17
+ deploymentBlock: 31_039_238,
18
+ earnRouter: '0x9144086A79dea966d8bf1B52125eBED8919fC45c',
19
+ },
20
+ ],
14
21
  } as const satisfies EarnVaults.upsert.Options
15
22
 
16
23
  describe('upsert', () => {
@@ -36,6 +43,13 @@ describe('upsert', () => {
36
43
  ],
37
44
  "updatedAt": "2026-01-01T00:00:00.000Z",
38
45
  "vaultAddress": "0x2f05f4d3625b2effb383efbedafd2d8f04371636",
46
+ "zones": [
47
+ {
48
+ "chainId": 1424310001,
49
+ "deploymentBlock": 31039238,
50
+ "earnRouter": "0x9144086a79dea966d8bf1b52125ebed8919fc45c",
51
+ },
52
+ ],
39
53
  }
40
54
  `)
41
55
  } finally {
@@ -68,6 +82,7 @@ describe('upsert', () => {
68
82
  privateInputTokens: stored.privateInputTokens,
69
83
  privateOutputTokens: stored.privateOutputTokens,
70
84
  updatedAt: stored.updatedAt,
85
+ zones: stored.zones,
71
86
  }).toMatchInlineSnapshot(`
72
87
  {
73
88
  "createdAt": "2026-01-01T00:00:00.000Z",
@@ -76,6 +91,13 @@ describe('upsert', () => {
76
91
  "privateInputTokens": [],
77
92
  "privateOutputTokens": [],
78
93
  "updatedAt": "2026-01-02T00:00:00.000Z",
94
+ "zones": [
95
+ {
96
+ "chainId": 1424310001,
97
+ "deploymentBlock": 31039238,
98
+ "earnRouter": "0x9144086a79dea966d8bf1b52125ebed8919fc45c",
99
+ },
100
+ ],
79
101
  }
80
102
  `)
81
103
 
@@ -10,7 +10,7 @@ const slugSuffix = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', 8)
10
10
  /** Columns of the `earn_vaults` table. */
11
11
  export type Table = Omit<
12
12
  db_Schema.EarnVault,
13
- 'chainId' | 'privateInputTokens' | 'privateOutputTokens'
13
+ 'chainId' | 'privateInputTokens' | 'privateOutputTokens' | 'zones'
14
14
  > & {
15
15
  /** Bigint chain id, read as a string from pg and written as a number. */
16
16
  chainId: ColumnType<string, number, number>
@@ -18,6 +18,8 @@ export type Table = Omit<
18
18
  privateInputTokens: JSONColumnType<db_Schema.EarnVault['privateInputTokens'], string, string>
19
19
  /** Tokens supported for private redemptions. */
20
20
  privateOutputTokens: JSONColumnType<db_Schema.EarnVault['privateOutputTokens'], string, string>
21
+ /** Curated private Zone routes. */
22
+ zones: JSONColumnType<db_Schema.EarnVault['zones'], string, string>
21
23
  }
22
24
 
23
25
  /** A stored curated Earn vault. */
@@ -76,6 +78,7 @@ export async function upsert(db: Db.Db, options: upsert.Options): Promise<Record
76
78
  slug,
77
79
  updatedAt: now,
78
80
  vaultAddress: normalize(options.vaultAddress),
81
+ zones: JSON.stringify(normalizeZones(options.zones)),
79
82
  })
80
83
  .onConflict((oc) =>
81
84
  oc.columns(['chainId', 'vaultAddress']).doUpdateSet({
@@ -85,6 +88,7 @@ export async function upsert(db: Db.Db, options: upsert.Options): Promise<Record
85
88
  privateOutputTokens: JSON.stringify(options.privateOutputTokens),
86
89
  ...(options.slug !== undefined ? { slug } : {}),
87
90
  updatedAt: now,
91
+ zones: JSON.stringify(normalizeZones(options.zones)),
88
92
  }),
89
93
  )
90
94
  .returningAll()
@@ -109,6 +113,8 @@ export declare namespace upsert {
109
113
  slug?: string | undefined
110
114
  /** Earn vault address. */
111
115
  vaultAddress: Address
116
+ /** Curated private Zone routes. */
117
+ zones: db_Schema.EarnVault['zones']
112
118
  }
113
119
  }
114
120
 
@@ -149,3 +155,7 @@ function toRecord(row: Selectable<Table>): Record {
149
155
  function normalize(address: string): Address {
150
156
  return address.toLowerCase() as Address
151
157
  }
158
+
159
+ function normalizeZones(zones: db_Schema.EarnVault['zones']): db_Schema.EarnVault['zones'] {
160
+ return zones.map((zone) => ({ ...zone, earnRouter: normalize(zone.earnRouter) }))
161
+ }