tapimo 0.10.0 → 0.10.2

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 (295) hide show
  1. package/dist/ApiKey.d.ts +2 -1
  2. package/dist/ApiKey.d.ts.map +1 -1
  3. package/dist/ApiKeys.d.ts +4 -2
  4. package/dist/ApiKeys.d.ts.map +1 -1
  5. package/dist/ApiKeys.js +26 -1
  6. package/dist/ApiKeys.js.map +1 -1
  7. package/dist/App.d.ts +6007 -5003
  8. package/dist/App.d.ts.map +1 -1
  9. package/dist/App.js +1 -0
  10. package/dist/App.js.map +1 -1
  11. package/dist/Webhooks.d.ts +1 -1
  12. package/dist/Webhooks.d.ts.map +1 -1
  13. package/dist/Webhooks.js +6 -6
  14. package/dist/Webhooks.js.map +1 -1
  15. package/dist/admin/App.d.ts +91 -84
  16. package/dist/admin/App.d.ts.map +1 -1
  17. package/dist/admin/apps/api-keys.d.ts +22 -13
  18. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  19. package/dist/admin/apps/api-keys.js +6 -0
  20. package/dist/admin/apps/api-keys.js.map +1 -1
  21. package/dist/admin/apps/organizations.d.ts +4 -2
  22. package/dist/admin/apps/organizations.d.ts.map +1 -1
  23. package/dist/admin/ui.generated.d.ts +1 -1
  24. package/dist/admin/ui.generated.js +69 -69
  25. package/dist/admin/ui.generated.js.map +1 -1
  26. package/dist/analytics/Analytics.d.ts.map +1 -1
  27. package/dist/analytics/Analytics.js +232 -0
  28. package/dist/analytics/Analytics.js.map +1 -1
  29. package/dist/analytics/tables/requestEvents.d.ts +4 -0
  30. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  31. package/dist/analytics/tables/requestEvents.js +2 -0
  32. package/dist/analytics/tables/requestEvents.js.map +1 -1
  33. package/dist/apps/Zones.d.ts +4266 -82
  34. package/dist/apps/Zones.d.ts.map +1 -1
  35. package/dist/apps/Zones.js +27 -8
  36. package/dist/apps/Zones.js.map +1 -1
  37. package/dist/apps/data/App.d.ts +3467 -2696
  38. package/dist/apps/data/App.d.ts.map +1 -1
  39. package/dist/apps/data/App.js +2 -0
  40. package/dist/apps/data/App.js.map +1 -1
  41. package/dist/apps/data/routes/activities.d.ts +2999 -100
  42. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  43. package/dist/apps/data/routes/activities.js +340 -2
  44. package/dist/apps/data/routes/activities.js.map +1 -1
  45. package/dist/apps/data/routes/addresses.d.ts +514 -0
  46. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  47. package/dist/apps/data/routes/balances.d.ts +26 -26
  48. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  49. package/dist/apps/data/routes/balances.js +7 -3
  50. package/dist/apps/data/routes/balances.js.map +1 -1
  51. package/dist/apps/data/routes/blocks.d.ts +4 -4
  52. package/dist/apps/data/routes/earn.d.ts +75 -75
  53. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  54. package/dist/apps/data/routes/earn.js +65 -36
  55. package/dist/apps/data/routes/earn.js.map +1 -1
  56. package/dist/apps/data/routes/exchanges.d.ts +124 -120
  57. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  58. package/dist/apps/data/routes/exchanges.js +115 -77
  59. package/dist/apps/data/routes/exchanges.js.map +1 -1
  60. package/dist/apps/data/routes/fee-amm.d.ts +24 -24
  61. package/dist/apps/data/routes/indexer.d.ts +2 -1
  62. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  63. package/dist/apps/data/routes/indexer.js +3 -2
  64. package/dist/apps/data/routes/indexer.js.map +1 -1
  65. package/dist/apps/data/routes/receipts.d.ts +4 -3
  66. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  67. package/dist/apps/data/routes/receipts.js +15 -14
  68. package/dist/apps/data/routes/receipts.js.map +1 -1
  69. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  70. package/dist/apps/data/routes/rpc.js +5 -1
  71. package/dist/apps/data/routes/rpc.js.map +1 -1
  72. package/dist/apps/data/routes/tokenlist.d.ts +12 -12
  73. package/dist/apps/data/routes/tokens.d.ts +1 -1
  74. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  75. package/dist/apps/data/routes/tokens.js +4 -3
  76. package/dist/apps/data/routes/tokens.js.map +1 -1
  77. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  78. package/dist/apps/data/routes/transfers.js +25 -7
  79. package/dist/apps/data/routes/transfers.js.map +1 -1
  80. package/dist/apps/data/routes/verified-tokens.d.ts +37 -37
  81. package/dist/apps/data/routes/webhooks.d.ts +71 -1
  82. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  83. package/dist/apps/data/routes/webhooks.js +177 -23
  84. package/dist/apps/data/routes/webhooks.js.map +1 -1
  85. package/dist/apps/data/routes/zones.js +1 -1
  86. package/dist/apps/data/routes/zones.js.map +1 -1
  87. package/dist/apps/funding/App.d.ts +322 -89
  88. package/dist/apps/funding/App.d.ts.map +1 -1
  89. package/dist/apps/funding/routes/deposit-addresses.d.ts +375 -45
  90. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
  91. package/dist/apps/funding/routes/deposit-addresses.js +208 -12
  92. package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
  93. package/dist/apps/funding/routes/transfers.d.ts +32 -32
  94. package/dist/apps/funding/routes/transfers.js +1 -1
  95. package/dist/apps/funding/routes/transfers.js.map +1 -1
  96. package/dist/apps/management/App.d.ts +418 -418
  97. package/dist/apps/management/routes/api-keys.d.ts +50 -50
  98. package/dist/apps/management/routes/billing.d.ts +72 -72
  99. package/dist/apps/management/routes/billing.js +1 -1
  100. package/dist/apps/management/routes/billing.js.map +1 -1
  101. package/dist/apps/management/routes/faucet.d.ts +8 -8
  102. package/dist/apps/management/routes/faucet.js +2 -2
  103. package/dist/apps/management/routes/faucet.js.map +1 -1
  104. package/dist/apps/management/routes/invite-links.d.ts +42 -42
  105. package/dist/apps/management/routes/me.d.ts +6 -6
  106. package/dist/apps/management/routes/members.d.ts +14 -14
  107. package/dist/apps/management/routes/projects.d.ts +40 -40
  108. package/dist/apps/management/routes/usage.d.ts +8 -8
  109. package/dist/apps/management/routes/webhooks.d.ts +49 -49
  110. package/dist/apps/mcp.d.ts.map +1 -1
  111. package/dist/apps/mcp.js +4 -1
  112. package/dist/apps/mcp.js.map +1 -1
  113. package/dist/db/Db.d.ts +2 -0
  114. package/dist/db/Db.d.ts.map +1 -1
  115. package/dist/db/Db.js +90 -0
  116. package/dist/db/Db.js.map +1 -1
  117. package/dist/db/Schema.d.ts +19 -0
  118. package/dist/db/Schema.d.ts.map +1 -1
  119. package/dist/db/Schema.js +24 -0
  120. package/dist/db/Schema.js.map +1 -1
  121. package/dist/db/tables/fundingDepositAddresses.d.ts +20 -0
  122. package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -1
  123. package/dist/db/tables/fundingDepositAddresses.js +17 -0
  124. package/dist/db/tables/fundingDepositAddresses.js.map +1 -1
  125. package/dist/db/tables/fundingDeposits.d.ts +41 -0
  126. package/dist/db/tables/fundingDeposits.d.ts.map +1 -1
  127. package/dist/db/tables/fundingDeposits.js +69 -0
  128. package/dist/db/tables/fundingDeposits.js.map +1 -1
  129. package/dist/db/tables/webhookQueueEvents.d.ts +122 -0
  130. package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -0
  131. package/dist/db/tables/webhookQueueEvents.js +160 -0
  132. package/dist/db/tables/webhookQueueEvents.js.map +1 -0
  133. package/dist/db/tables/webhookSubscriptions.d.ts +2 -0
  134. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  135. package/dist/db/tables/webhookSubscriptions.js +10 -0
  136. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  137. package/dist/internal/Auth.d.ts +3 -26
  138. package/dist/internal/Auth.d.ts.map +1 -1
  139. package/dist/internal/Auth.js +6 -37
  140. package/dist/internal/Auth.js.map +1 -1
  141. package/dist/internal/Credential.d.ts +27 -0
  142. package/dist/internal/Credential.d.ts.map +1 -0
  143. package/dist/internal/Credential.js +34 -0
  144. package/dist/internal/Credential.js.map +1 -0
  145. package/dist/internal/EarnRates.js +11 -7
  146. package/dist/internal/EarnRates.js.map +1 -1
  147. package/dist/internal/Log.d.ts +8 -0
  148. package/dist/internal/Log.d.ts.map +1 -1
  149. package/dist/internal/Log.js +4 -0
  150. package/dist/internal/Log.js.map +1 -1
  151. package/dist/internal/MetricSink.d.ts +51 -0
  152. package/dist/internal/MetricSink.d.ts.map +1 -1
  153. package/dist/internal/MetricSink.js +54 -2
  154. package/dist/internal/MetricSink.js.map +1 -1
  155. package/dist/internal/RateLimit.d.ts +4 -6
  156. package/dist/internal/RateLimit.d.ts.map +1 -1
  157. package/dist/internal/RateLimit.js +15 -14
  158. package/dist/internal/RateLimit.js.map +1 -1
  159. package/dist/internal/Store.d.ts +2 -1
  160. package/dist/internal/Store.d.ts.map +1 -1
  161. package/dist/internal/Store.js +57 -18
  162. package/dist/internal/Store.js.map +1 -1
  163. package/dist/internal/Tidx.d.ts +4 -3
  164. package/dist/internal/Tidx.d.ts.map +1 -1
  165. package/dist/internal/Tidx.js +2 -2
  166. package/dist/internal/Tidx.js.map +1 -1
  167. package/dist/internal/Value.d.ts +2 -0
  168. package/dist/internal/Value.d.ts.map +1 -1
  169. package/dist/internal/Value.js +25 -0
  170. package/dist/internal/Value.js.map +1 -1
  171. package/dist/internal/Viem.d.ts +4 -3
  172. package/dist/internal/Viem.d.ts.map +1 -1
  173. package/dist/internal/Viem.js +3 -2
  174. package/dist/internal/Viem.js.map +1 -1
  175. package/dist/internal/WebhookTransfer.d.ts +20 -0
  176. package/dist/internal/WebhookTransfer.d.ts.map +1 -1
  177. package/dist/internal/WebhookTransfer.js +7 -4
  178. package/dist/internal/WebhookTransfer.js.map +1 -1
  179. package/dist/internal/Webhooks.d.ts +141 -0
  180. package/dist/internal/Webhooks.d.ts.map +1 -1
  181. package/dist/internal/Webhooks.js +139 -10
  182. package/dist/internal/Webhooks.js.map +1 -1
  183. package/dist/internal/funding/providers/relay.d.ts +3 -4
  184. package/dist/internal/funding/providers/relay.d.ts.map +1 -1
  185. package/dist/internal/funding/providers/relay.js +59 -8
  186. package/dist/internal/funding/providers/relay.js.map +1 -1
  187. package/dist/internal/index.d.ts +1 -0
  188. package/dist/internal/index.d.ts.map +1 -1
  189. package/dist/internal/index.js +1 -0
  190. package/dist/internal/index.js.map +1 -1
  191. package/package.json +3 -3
  192. package/src/ApiKeys.test.ts +20 -0
  193. package/src/ApiKeys.ts +28 -1
  194. package/src/App.test.ts +40 -8
  195. package/src/App.ts +1 -0
  196. package/src/Client.test-d.ts +34 -1
  197. package/src/Webhooks.ts +20 -18
  198. package/src/admin/apps/api-keys.test.ts +9 -0
  199. package/src/admin/apps/api-keys.ts +15 -0
  200. package/src/admin/ui/src/lib/query-client.ts +3 -2
  201. package/src/admin/ui/src/routes/api-keys.tsx +31 -9
  202. package/src/admin/ui.generated.ts +69 -69
  203. package/src/analytics/Analytics.test.ts +42 -0
  204. package/src/analytics/Analytics.ts +232 -0
  205. package/src/analytics/tables/requestEvents.ts +6 -0
  206. package/src/apps/Zones.test.ts +20 -3
  207. package/src/apps/Zones.ts +28 -8
  208. package/src/apps/composition.test.ts +2 -0
  209. package/src/apps/data/App.test.ts +2 -2
  210. package/src/apps/data/App.ts +2 -0
  211. package/src/apps/data/routes/activities.bench.ts +41 -0
  212. package/src/apps/data/routes/activities.test.ts +103 -2
  213. package/src/apps/data/routes/activities.ts +401 -2
  214. package/src/apps/data/routes/balances.bench.ts +21 -0
  215. package/src/apps/data/routes/balances.ts +7 -3
  216. package/src/apps/data/routes/blocks.bench.ts +34 -0
  217. package/src/apps/data/routes/coingecko.bench.ts +53 -0
  218. package/src/apps/data/routes/earn.bench.ts +71 -0
  219. package/src/apps/data/routes/earn.ts +66 -36
  220. package/src/apps/data/routes/exchanges.bench.ts +85 -0
  221. package/src/apps/data/routes/exchanges.test.ts +8 -3
  222. package/src/apps/data/routes/exchanges.ts +134 -82
  223. package/src/apps/data/routes/fee-amm.bench.ts +13 -0
  224. package/src/apps/data/routes/indexer.bench.ts +8 -0
  225. package/src/apps/data/routes/indexer.test.ts +2 -1
  226. package/src/apps/data/routes/indexer.ts +3 -2
  227. package/src/apps/data/routes/receipts.bench.ts +39 -0
  228. package/src/apps/data/routes/receipts.ts +16 -14
  229. package/src/apps/data/routes/rpc.bench.ts +14 -0
  230. package/src/apps/data/routes/rpc.test.ts +27 -0
  231. package/src/apps/data/routes/rpc.ts +5 -1
  232. package/src/apps/data/routes/tokenlist.bench.ts +3 -0
  233. package/src/apps/data/routes/tokens.bench.ts +76 -0
  234. package/src/apps/data/routes/tokens.ts +3 -2
  235. package/src/apps/data/routes/transactions.bench.ts +48 -0
  236. package/src/apps/data/routes/transfers.bench.ts +28 -0
  237. package/src/apps/data/routes/transfers.ts +36 -10
  238. package/src/apps/data/routes/valuation.bench.ts +14 -0
  239. package/src/apps/data/routes/verified-tokens.bench.ts +8 -0
  240. package/src/apps/data/routes/webhooks.bench.ts +26 -0
  241. package/src/apps/data/routes/webhooks.scan.test.ts +326 -0
  242. package/src/apps/data/routes/webhooks.test.ts +53 -6
  243. package/src/apps/data/routes/webhooks.ts +252 -24
  244. package/src/apps/data/routes/zones.bench.ts +32 -0
  245. package/src/apps/data/routes/zones.test.ts +1 -1
  246. package/src/apps/data/routes/zones.ts +1 -1
  247. package/src/apps/funding/routes/chains.test.ts +1 -1
  248. package/src/apps/funding/routes/deposit-addresses.test.ts +217 -3
  249. package/src/apps/funding/routes/deposit-addresses.ts +255 -19
  250. package/src/apps/funding/routes/providers.test.ts +1 -1
  251. package/src/apps/funding/routes/quotes.test.ts +1 -1
  252. package/src/apps/funding/routes/transfers.test.ts +20 -8
  253. package/src/apps/funding/routes/transfers.ts +1 -1
  254. package/src/apps/management/routes/billing.ts +1 -1
  255. package/src/apps/management/routes/faucet.ts +2 -2
  256. package/src/apps/management/routes/me.test.ts +31 -0
  257. package/src/apps/mcp.ts +4 -1
  258. package/src/db/Db.ts +117 -0
  259. package/src/db/Schema.ts +32 -0
  260. package/src/db/tables/fundingDepositAddresses.test.ts +28 -0
  261. package/src/db/tables/fundingDepositAddresses.ts +39 -0
  262. package/src/db/tables/fundingDeposits.test.ts +82 -0
  263. package/src/db/tables/fundingDeposits.ts +130 -0
  264. package/src/db/tables/webhookQueueEvents.ts +285 -0
  265. package/src/db/tables/webhookSubscriptions.ts +15 -0
  266. package/src/internal/Auth.test.ts +61 -98
  267. package/src/internal/Auth.ts +9 -63
  268. package/src/internal/Credential.test.ts +51 -0
  269. package/src/internal/Credential.ts +58 -0
  270. package/src/internal/EarnRates.ts +13 -7
  271. package/src/internal/EarnVaults.test.ts +2 -2
  272. package/src/internal/Log.test.ts +3 -1
  273. package/src/internal/Log.ts +14 -0
  274. package/src/internal/MetricSink.test.ts +108 -2
  275. package/src/internal/MetricSink.ts +112 -2
  276. package/src/internal/RateLimit.test.ts +23 -18
  277. package/src/internal/RateLimit.ts +23 -15
  278. package/src/internal/Store.test.ts +99 -13
  279. package/src/internal/Store.ts +52 -17
  280. package/src/internal/Tidx.test.ts +4 -4
  281. package/src/internal/Tidx.ts +5 -4
  282. package/src/internal/Value.test.ts +25 -0
  283. package/src/internal/Value.ts +29 -0
  284. package/src/internal/Viem.test.ts +4 -0
  285. package/src/internal/Viem.ts +6 -4
  286. package/src/internal/WebhookTransfer.test.ts +89 -0
  287. package/src/internal/WebhookTransfer.ts +11 -7
  288. package/src/internal/Webhooks.test.ts +488 -0
  289. package/src/internal/Webhooks.ts +291 -10
  290. package/src/internal/funding/Provider.test.ts +1 -1
  291. package/src/internal/funding/providers/index.test.ts +73 -3
  292. package/src/internal/funding/providers/relay.test.ts +1 -5
  293. package/src/internal/funding/providers/relay.ts +56 -12
  294. package/src/internal/index.test.ts +1 -0
  295. package/src/internal/index.ts +1 -0
@@ -27,11 +27,8 @@ const db = TestApp.database()
27
27
  /** Providers exercised against executable calls on a forked source chain. */
28
28
  const executableProviders = [
29
29
  {
30
- create: (sender: string) =>
31
- Relay.relay({
32
- ...(process.env.RELAY_API_KEY ? { apiKey: process.env.RELAY_API_KEY } : {}),
33
- userAddress: sender,
34
- }),
30
+ create: (_sender: string) =>
31
+ Relay.relay(process.env.RELAY_API_KEY ? { apiKey: process.env.RELAY_API_KEY } : {}),
35
32
  id: 'relay',
36
33
  },
37
34
  ] as const
@@ -282,7 +279,7 @@ describe('POST /funding/transfers', () => {
282
279
  providerResponse(),
283
280
  ) {
284
281
  return FundingProvider.from({
285
- ...Relay.relay({ userAddress: '0x1111111111111111111111111111111111111111' }),
282
+ ...Relay.relay(),
286
283
  prepareTransfer,
287
284
  })
288
285
  }
@@ -844,6 +841,7 @@ describe('funding transfers OpenAPI', () => {
844
841
  deposit: summarize(spec.paths[depositPath]!.get!),
845
842
  depositAddress: summarize(spec.paths[depositAddressPath]!.get!),
846
843
  depositAddressCreate: summarize(spec.paths['/v1/funding/deposit-addresses']!.post!),
844
+ depositAddressList: summarize(spec.paths['/v1/funding/deposit-addresses']!.get!),
847
845
  depositAddressReconcile: summarize(spec.paths[reconcilePath]!.post!),
848
846
  deposits: summarize(spec.paths['/v1/funding/deposits']!.get!),
849
847
  detail: summarize(spec.paths[detailPath]!.get!),
@@ -892,12 +890,16 @@ describe('funding transfers OpenAPI', () => {
892
890
  "description": "Creates or returns a reusable deposit address for funding a Tempo account.",
893
891
  "summary": "Create deposit address",
894
892
  },
893
+ "depositAddressList": {
894
+ "description": "Lists reusable funding deposit addresses, newest first.",
895
+ "summary": "List deposit addresses",
896
+ },
895
897
  "depositAddressReconcile": {
896
898
  "description": "Queues an immediate provider reconciliation for one reusable funding deposit address.",
897
899
  "summary": "Reconcile deposit address",
898
900
  },
899
901
  "deposits": {
900
- "description": "Lists deposits by deposit address or Tempo recipient, newest first.",
902
+ "description": "Lists visible deposits, optionally filtered, newest first.",
901
903
  "summary": "List deposits",
902
904
  },
903
905
  "detail": {
@@ -929,6 +931,7 @@ describe('funding transfers OpenAPI', () => {
929
931
  "Get quotes",
930
932
  "Get chains",
931
933
  "Get providers",
934
+ "List deposit addresses",
932
935
  "Create deposit address",
933
936
  "List transfers",
934
937
  "Create transfer",
@@ -1090,7 +1093,11 @@ describe('funding transfers OpenAPI', () => {
1090
1093
  const operation = spec.paths['/v1/funding/transfers']!.get
1091
1094
  const fields = Object.keys(operation.responses['200']!.content['application/json']!.example.data[0]!) // prettier-ignore
1092
1095
  expect(fields).toEqual([...fields].sort())
1093
- for (const route of [operation, spec.paths['/v1/funding/deposits']!.get])
1096
+ for (const route of [
1097
+ operation,
1098
+ spec.paths['/v1/funding/deposit-addresses']!.get,
1099
+ spec.paths['/v1/funding/deposits']!.get,
1100
+ ])
1094
1101
  for (const parameter of route.parameters ?? [])
1095
1102
  expect(parameter.example ?? parameter.schema?.examples, parameter.name).toBeDefined()
1096
1103
  })
@@ -1129,6 +1136,9 @@ describe('funding transfers OpenAPI', () => {
1129
1136
  depositAddressCreate: spec.paths['/v1/funding/deposit-addresses']?.post?.operationId,
1130
1137
  depositAddressCreateScopes:
1131
1138
  spec.paths['/v1/funding/deposit-addresses']?.post?.['x-required-scopes'],
1139
+ depositAddressList: spec.paths['/v1/funding/deposit-addresses']?.get.operationId,
1140
+ depositAddressListScopes:
1141
+ spec.paths['/v1/funding/deposit-addresses']?.get['x-required-scopes'],
1132
1142
  depositAddressReconcile: reconcilePath && spec.paths[reconcilePath]?.post?.operationId,
1133
1143
  depositAddressReconcileScopes:
1134
1144
  reconcilePath && spec.paths[reconcilePath]?.post?.['x-required-scopes'],
@@ -1153,6 +1163,8 @@ describe('funding transfers OpenAPI', () => {
1153
1163
  depositAddress: 'getFundingDepositAddress',
1154
1164
  depositAddressCreate: 'createFundingDepositAddress',
1155
1165
  depositAddressCreateScopes: ['funding:write'],
1166
+ depositAddressList: 'listFundingDepositAddresses',
1167
+ depositAddressListScopes: ['funding:read'],
1156
1168
  depositAddressReconcile: 'reconcileFundingDepositAddress',
1157
1169
  depositAddressReconcileScopes: ['funding:write'],
1158
1170
  depositAddressScopes: ['funding:read'],
@@ -110,7 +110,7 @@ export namespace schema {
110
110
  .check(
111
111
  z.regex(/^eip155:\d+$/),
112
112
  z.describe('Configured Zone CAIP-2 id.'),
113
- z.meta({ examples: ['eip155:421700001'] }),
113
+ z.meta({ examples: ['eip155:1424310001'] }),
114
114
  ),
115
115
  destinationToken: FundingTransfer.schema.CreateRequest.shape.destinationToken,
116
116
  mode: destinationCreateFields.mode,
@@ -699,7 +699,7 @@ export function billing() {
699
699
  Auth.policy({
700
700
  apiKey: false,
701
701
  mpp: false,
702
- public: { rateLimit: { perMinute: 600 } },
702
+ public: { rateLimit: { limit: 600, period: 'minute' } },
703
703
  session: false,
704
704
  }),
705
705
  OpenApi.describeRoute({ hide: true, summary: 'Stripe webhook' }),
@@ -10,8 +10,8 @@ import * as Value from '../../../internal/Value.js'
10
10
  import * as Viem from '../../../internal/Viem.js'
11
11
  import type { Environment } from '../App.js'
12
12
 
13
- const addressRateLimit = { perMinute: 1 } satisfies RateLimit.Limit
14
- const userRateLimit = { perMinute: 3 } satisfies RateLimit.Limit
13
+ const addressRateLimit = { limit: 1, period: 'minute' } satisfies RateLimit.Limit
14
+ const userRateLimit = { limit: 3, period: 'minute' } satisfies RateLimit.Limit
15
15
 
16
16
  /** OpenAPI schemas owned by the sandbox faucet. */
17
17
  export namespace schema {
@@ -1,7 +1,10 @@
1
1
  import { serve } from '@hono/node-server'
2
2
  import * as Accounts from 'accounts/server'
3
3
  import { exportJWK, generateKeyPair } from 'jose'
4
+ import { P256 } from 'ox'
4
5
  import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
6
+ import { Account } from 'viem/tempo'
7
+ import { tempoTestnet } from 'viem/tempo/chains'
5
8
 
6
9
  import * as TestApp from '../../../../test/App.js'
7
10
  import * as Me from './me.js'
@@ -18,6 +21,34 @@ function createApp() {
18
21
  })
19
22
  }
20
23
 
24
+ describe('SIWE verification', () => {
25
+ test('surfaces configured RPC failures as dependency errors', async () => {
26
+ const account = Account.fromHeadlessWebAuthn(P256.randomPrivateKey(), {
27
+ origin,
28
+ rpId: 'localhost',
29
+ })
30
+ let chainId_seen: number | undefined
31
+ const app = TestApp.create({
32
+ rpc: ({ chainId }) => {
33
+ chainId_seen = chainId
34
+ return { url: 'http://127.0.0.1:1' }
35
+ },
36
+ })
37
+
38
+ const { verifyResponse } = await TestApp.signIn(app, account, {
39
+ chainId: tempoTestnet.id,
40
+ })
41
+
42
+ expect(verifyResponse.status).toBe(502)
43
+ expect(chainId_seen).toMatchInlineSnapshot(`42431`)
44
+ expect(await verifyResponse.json()).toMatchInlineSnapshot(`
45
+ {
46
+ "error": "signature verification unavailable",
47
+ }
48
+ `)
49
+ })
50
+ })
51
+
21
52
  describe('GET /me', () => {
22
53
  test('returns the signed-in user', async () => {
23
54
  const app = createApp()
package/src/apps/mcp.ts CHANGED
@@ -138,7 +138,10 @@ export function mcp(options: mcp.Options) {
138
138
  const app = new Hono<App.Environment>()
139
139
  .post(
140
140
  '/mcp',
141
- Auth.policy({ apiKey: { scopes: [] }, public: { rateLimit: { perMinute: 100 } } }),
141
+ Auth.policy({
142
+ apiKey: { scopes: [] },
143
+ public: { rateLimit: { limit: 100, period: 'minute' } },
144
+ }),
142
145
  OpenApi.describeRoute({
143
146
  description:
144
147
  "A stateless streamable-HTTP MCP server: send JSON-RPC 2.0 messages as `POST` with `Accept: application/json, text/event-stream`. Tools mirror the API's data domain, plus `docs_*` documentation search. Anonymous requests allow 100 requests per minute; send `Authorization: Bearer <api key>` for protected tools.",
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 WebhookQueueEvents from './tables/webhookQueueEvents.js'
39
40
  import type * as WebhookSubscriptions from './tables/webhookSubscriptions.js'
40
41
 
41
42
  /**
@@ -82,6 +83,7 @@ export type Database = {
82
83
  verified_token_requests: VerifiedTokenRequests.Table
83
84
  verified_tokens: VerifiedTokens.Table
84
85
  webhook_deliveries: WebhookDeliveries.Table
86
+ webhook_queue_events: WebhookQueueEvents.Table
85
87
  webhook_subscriptions: WebhookSubscriptions.Table
86
88
  }
87
89
 
@@ -1281,6 +1283,121 @@ export const migrations: readonly Migration[] = [
1281
1283
  .on('fundingDepositAddresses')
1282
1284
  .columns(['orgId', 'environment', 'address', 'projectId']),
1283
1285
  },
1286
+ {
1287
+ name: '0115_webhook_queue_events',
1288
+ up: (db) =>
1289
+ db.schema
1290
+ .createTable('webhookQueueEvents')
1291
+ .ifNotExists()
1292
+ .addColumn('subscriptionId', 'text', (c) =>
1293
+ c.notNull().references('webhookSubscriptions.id').onDelete('cascade'),
1294
+ )
1295
+ .addColumn('eventId', 'text', (c) => c.notNull())
1296
+ .addColumn('envelope', 'jsonb', (c) => c.notNull())
1297
+ .addColumn('createdAt', 'text', (c) => c.notNull())
1298
+ .addColumn('expiresAt', 'text', (c) => c.notNull())
1299
+ .addPrimaryKeyConstraint('webhook_queue_events_pkey', ['subscriptionId', 'eventId']),
1300
+ },
1301
+ {
1302
+ name: '0116_webhook_queue_events_expires_index',
1303
+ up: (db) =>
1304
+ db.schema
1305
+ .createIndex('webhook_queue_events_expires_idx')
1306
+ .ifNotExists()
1307
+ .on('webhookQueueEvents')
1308
+ .column('expiresAt'),
1309
+ },
1310
+ {
1311
+ name: '0117_webhook_queue_events_ledger_columns',
1312
+ up: (db) =>
1313
+ raw(
1314
+ db,
1315
+ sql`ALTER TABLE webhook_queue_events
1316
+ ADD COLUMN IF NOT EXISTS status text NOT NULL DEFAULT 'pending',
1317
+ ADD COLUMN IF NOT EXISTS attempt_count integer NOT NULL DEFAULT 0,
1318
+ ADD COLUMN IF NOT EXISTS attempting_at text,
1319
+ ADD COLUMN IF NOT EXISTS next_attempt_at text`,
1320
+ ),
1321
+ },
1322
+ {
1323
+ name: '0118_webhook_queue_events_status_check',
1324
+ up: (db) =>
1325
+ raw(
1326
+ db,
1327
+ sql`ALTER TABLE webhook_queue_events DROP CONSTRAINT IF EXISTS webhook_queue_events_status_check, ADD CONSTRAINT webhook_queue_events_status_check CHECK (status IN ('failed', 'pending', 'skipped', 'succeeded'))`,
1328
+ ),
1329
+ },
1330
+ {
1331
+ name: '0119_webhook_queue_events_pending_index',
1332
+ up: (db) =>
1333
+ // NULLS FIRST matches the due-sweep order (null = due immediately);
1334
+ // INCLUDE lets the pending gauge take min(created_at) index-only.
1335
+ raw(
1336
+ db,
1337
+ sql`CREATE INDEX IF NOT EXISTS webhook_queue_events_pending_idx ON webhook_queue_events (next_attempt_at NULLS FIRST) INCLUDE (created_at) WHERE status = 'pending'`,
1338
+ ),
1339
+ },
1340
+ {
1341
+ name: '0120_webhook_queue_events_pending_index_drop',
1342
+ up: (db) => raw(db, sql`DROP INDEX IF EXISTS webhook_queue_events_pending_idx`),
1343
+ },
1344
+ {
1345
+ name: '0121_webhook_queue_events_pending_index_expiry',
1346
+ up: (db) =>
1347
+ // Recreated with expires_at so claimable and gauge queries, which all
1348
+ // filter expiry at query time, stay index-only.
1349
+ raw(
1350
+ db,
1351
+ sql`CREATE INDEX IF NOT EXISTS webhook_queue_events_pending_idx ON webhook_queue_events (next_attempt_at NULLS FIRST) INCLUDE (created_at, expires_at) WHERE status = 'pending'`,
1352
+ ),
1353
+ },
1354
+ {
1355
+ name: '0122_webhook_queue_events_expired_pending_cleanup',
1356
+ up: (db) =>
1357
+ // Pre-ledger rows already past their staging TTL were dead under the
1358
+ // old read path; pending rows no longer expire, so drop them once
1359
+ // instead of resurrecting them as eternal obligations.
1360
+ raw(
1361
+ db,
1362
+ sql`DELETE FROM webhook_queue_events WHERE status = 'pending' AND attempting_at IS NULL AND expires_at <= to_char(now() AT TIME ZONE 'utc', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')`,
1363
+ ),
1364
+ },
1365
+ {
1366
+ name: '0123_funding_deposit_addresses_owner_list_index',
1367
+ up: (db) =>
1368
+ db.schema
1369
+ .createIndex('funding_deposit_addresses_owner_list_idx')
1370
+ .ifNotExists()
1371
+ .on('fundingDepositAddresses')
1372
+ .columns(['orgId', 'environment', 'createdAt', 'id']),
1373
+ },
1374
+ {
1375
+ name: '0124_funding_deposit_addresses_project_list_index',
1376
+ up: (db) =>
1377
+ db.schema
1378
+ .createIndex('funding_deposit_addresses_project_list_idx')
1379
+ .ifNotExists()
1380
+ .on('fundingDepositAddresses')
1381
+ .columns(['orgId', 'environment', 'projectId', 'createdAt', 'id']),
1382
+ },
1383
+ {
1384
+ name: '0125_funding_deposits_owner_list_index',
1385
+ up: (db) =>
1386
+ db.schema
1387
+ .createIndex('funding_deposits_owner_list_idx')
1388
+ .ifNotExists()
1389
+ .on('fundingDeposits')
1390
+ .columns(['orgId', 'environment', 'createdAt', 'id']),
1391
+ },
1392
+ {
1393
+ name: '0126_funding_deposits_project_list_index',
1394
+ up: (db) =>
1395
+ db.schema
1396
+ .createIndex('funding_deposits_project_list_idx')
1397
+ .ifNotExists()
1398
+ .on('fundingDeposits')
1399
+ .columns(['orgId', 'environment', 'projectId', 'createdAt', 'id']),
1400
+ },
1284
1401
  ]
1285
1402
 
1286
1403
  /**
package/src/db/Schema.ts CHANGED
@@ -874,3 +874,35 @@ export const WebhookDelivery = z.object({
874
874
 
875
875
  /** Columns of the `webhook_deliveries` table. */
876
876
  export type WebhookDelivery = z.output<typeof WebhookDelivery>
877
+
878
+ /** A durable webhook envelope staged before its compact Queue reference is admitted. */
879
+ export const WebhookQueueEvent = z.object({
880
+ attemptCount: z.number().check(z.describe('Delivery attempts claimed so far.')),
881
+ attemptingAt: z
882
+ .nullable(z.string())
883
+ .check(z.describe('ISO timestamp of the current claim, or null when unclaimed.')),
884
+ createdAt: z.string().check(z.describe('ISO timestamp when the envelope was staged.')),
885
+ envelope: z
886
+ .custom<Webhooks.Envelope>()
887
+ .check(z.describe('The full webhook envelope retained outside Cloudflare Queues.')),
888
+ eventId: z.string().check(z.describe('Idempotent event id (`evt_...`).')),
889
+ expiresAt: z
890
+ .string()
891
+ .check(
892
+ z.describe(
893
+ 'ISO dedupe retention deadline, restarted at terminal transitions; inert while pending.',
894
+ ),
895
+ ),
896
+ nextAttemptAt: z
897
+ .nullable(z.string())
898
+ .check(z.describe('ISO retry due time, or null when due immediately.')),
899
+ status: z
900
+ .enum(['failed', 'pending', 'skipped', 'succeeded'])
901
+ .check(z.describe('Delivery-obligation lifecycle status.')),
902
+ subscriptionId: z
903
+ .string()
904
+ .check(z.describe('Owning subscription id (`wh_...`); FK with `ON DELETE CASCADE`.')),
905
+ })
906
+
907
+ /** Columns of the `webhook_queue_events` table. */
908
+ export type WebhookQueueEvent = z.output<typeof WebhookQueueEvent>
@@ -578,3 +578,31 @@ describe('renewPoll', () => {
578
578
  `)
579
579
  })
580
580
  })
581
+
582
+ describe('list', () => {
583
+ test('pages organization addresses and narrows project-attributed owners', async () => {
584
+ const listOwner = { environment: owner.environment, orgId: 'org_address_list' } as const
585
+ const first = await seed({ orgId: listOwner.orgId })
586
+ const project = await seed({ orgId: listOwner.orgId, projectId: 'prj_address_list' })
587
+ const sibling = await seed({ orgId: listOwner.orgId, projectId: 'prj_address_sibling' })
588
+ await seed({ orgId: 'org_address_list_foreign' })
589
+ await seed({ environment: 'sandbox', orgId: listOwner.orgId })
590
+
591
+ const page = await FundingDepositAddresses.list(db, { limit: 2, owner: listOwner })
592
+ expect(page.map((record) => record.id)).toEqual([sibling.id, project.id])
593
+ const rest = await FundingDepositAddresses.list(db, {
594
+ cursor: { createdAt: project.createdAt, id: project.id },
595
+ limit: 2,
596
+ owner: listOwner,
597
+ })
598
+ expect(rest.map((record) => record.id)).toEqual([first.id])
599
+ expect(
600
+ (
601
+ await FundingDepositAddresses.list(db, {
602
+ limit: 10,
603
+ owner: { ...listOwner, projectId: 'prj_address_list' },
604
+ })
605
+ ).map((record) => record.id),
606
+ ).toEqual([project.id])
607
+ })
608
+ })
@@ -148,6 +148,45 @@ export function getOwned(db: Db.Db, owner: Owner, id: string): Promise<Record |
148
148
  return query.executeTakeFirst()
149
149
  }
150
150
 
151
+ /** Lists owner-visible funding deposit addresses newest-first. */
152
+ export function list(db: Db.Db, options: list.Options): Promise<Record[]> {
153
+ const { cursor, limit, owner } = options
154
+ let query = db.kysely
155
+ .selectFrom('funding_deposit_addresses')
156
+ .selectAll()
157
+ .where('environment', '=', owner.environment)
158
+ .where('orgId', '=', owner.orgId)
159
+ if (owner.projectId !== undefined) query = query.where('projectId', '=', owner.projectId)
160
+ if (cursor)
161
+ query = query.where((eb) =>
162
+ eb.or([
163
+ eb('createdAt', '<', cursor.createdAt),
164
+ eb.and([eb('createdAt', '=', cursor.createdAt), eb('id', '<', cursor.id)]),
165
+ ]),
166
+ )
167
+ return query.orderBy('createdAt', 'desc').orderBy('id', 'desc').limit(limit).execute()
168
+ }
169
+
170
+ export declare namespace list {
171
+ /** Ownership and paging options for funding deposit addresses. */
172
+ type Options = {
173
+ /** Exclusive lower bound from the previous page. */
174
+ cursor?: Cursor | undefined
175
+ /** Maximum rows to return. */
176
+ limit: number
177
+ /** Ownership filter derived from the authenticated API key. */
178
+ owner: Owner
179
+ }
180
+
181
+ /** Cursor fields for the last address returned by the previous page. */
182
+ type Cursor = {
183
+ /** Deposit address creation time. */
184
+ createdAt: string
185
+ /** Deposit address id, used as a deterministic tie-breaker. */
186
+ id: string
187
+ }
188
+ }
189
+
151
190
  /** Gets the reusable address that matches one owner and route identity. */
152
191
  export function getMatching(db: Db.Db, match: Match): Promise<Record | undefined> {
153
192
  let query = db.kysely
@@ -498,3 +498,85 @@ describe('toPublic', () => {
498
498
  })
499
499
  })
500
500
  })
501
+
502
+ describe('list', () => {
503
+ test('pages organization deposits and narrows project-attributed owners', async () => {
504
+ const listOwner = { environment: owner.environment, orgId: 'org_deposit_list' } as const
505
+ const firstAddress = await seedAddress({ orgId: listOwner.orgId })
506
+ const projectAddress = await seedAddress({
507
+ orgId: listOwner.orgId,
508
+ projectId: 'prj_deposit_list',
509
+ })
510
+ const siblingAddress = await seedAddress({
511
+ orgId: listOwner.orgId,
512
+ projectId: 'prj_deposit_sibling',
513
+ })
514
+ const first = await seedDeposit(firstAddress)
515
+ const project = await seedDeposit(projectAddress)
516
+ const sibling = await seedDeposit(siblingAddress)
517
+ const foreignAddress = await seedAddress({ orgId: 'org_deposit_list_foreign' })
518
+ await seedDeposit(foreignAddress)
519
+ const sandboxAddress = await seedAddress({
520
+ environment: 'sandbox',
521
+ orgId: listOwner.orgId,
522
+ })
523
+ await seedDeposit(sandboxAddress)
524
+
525
+ const page = await FundingDeposits.list(db, { limit: 2, owner: listOwner })
526
+ expect(page.map((record) => record.id)).toEqual([sibling.id, project.id])
527
+ const rest = await FundingDeposits.list(db, {
528
+ cursor: { createdAt: project.createdAt, id: project.id },
529
+ limit: 2,
530
+ owner: listOwner,
531
+ })
532
+ expect(rest.map((record) => record.id)).toEqual([first.id])
533
+ expect(
534
+ (
535
+ await FundingDeposits.list(db, {
536
+ limit: 10,
537
+ owner: { ...listOwner, projectId: 'prj_deposit_list' },
538
+ })
539
+ ).map((record) => record.id),
540
+ ).toEqual([project.id])
541
+ })
542
+
543
+ test('AND-combines filters and counts through the same ownership scope', async () => {
544
+ const listOwner = { environment: owner.environment, orgId: 'org_deposit_filters' } as const
545
+ const recipient = `0x${'ab'.repeat(20)}`
546
+ const address = await seedAddress({
547
+ orgId: listOwner.orgId,
548
+ snapshot: TestFunding.depositAddressSnapshot({
549
+ provider: { id: 'relay_filters', name: 'Relay' },
550
+ recipient,
551
+ }),
552
+ })
553
+ const match = await seedDeposit(address)
554
+ const unrelatedAddress = await seedAddress({ orgId: listOwner.orgId })
555
+ await seedDeposit(unrelatedAddress)
556
+ const filters = {
557
+ depositAddress: address.address,
558
+ destinationTokenKeys: [address.destinationTokenKey],
559
+ providerId: address.providerId,
560
+ recipient: recipient.toLowerCase(),
561
+ sourceChainIds: [address.sourceChainId],
562
+ sourceTokenKeys: [address.sourceTokenKey],
563
+ status: 'detected',
564
+ } as const
565
+
566
+ expect(
567
+ (await FundingDeposits.list(db, { ...filters, limit: 10, owner: listOwner })).map(
568
+ (record) => record.id,
569
+ ),
570
+ ).toEqual([match.id])
571
+ expect(await FundingDeposits.count(db, { ...filters, cap: 10, owner: listOwner })).toBe(1)
572
+ expect(await FundingDeposits.count(db, { ...filters, cap: 0, owner: listOwner })).toBe(1)
573
+ expect(
574
+ await FundingDeposits.count(db, {
575
+ ...filters,
576
+ destinationTokenKeys: [],
577
+ cap: 10,
578
+ owner: listOwner,
579
+ }),
580
+ ).toBe(0)
581
+ })
582
+ })
@@ -27,6 +27,24 @@ export type Owner = {
27
27
  projectId?: string | undefined
28
28
  }
29
29
 
30
+ /** Optional filters shared by funding deposit list and count queries. */
31
+ export type Filters = {
32
+ /** Reusable source-chain address that received the funds. */
33
+ depositAddress?: string | undefined
34
+ /** Canonical destination token keys accepted by the filter. */
35
+ destinationTokenKeys?: readonly string[] | undefined
36
+ /** Funding provider id. */
37
+ providerId?: string | undefined
38
+ /** Tempo account that receives completed deposits. */
39
+ recipient?: string | undefined
40
+ /** Canonical source chain ids accepted by the filter. */
41
+ sourceChainIds?: readonly string[] | undefined
42
+ /** Canonical source token keys accepted by the filter. */
43
+ sourceTokenKeys?: readonly string[] | undefined
44
+ /** Deposit lifecycle status. */
45
+ status?: Record['status'] | undefined
46
+ }
47
+
30
48
  /** Inserts one provider-observed source transfer as a funding deposit. */
31
49
  export function insert(db: Db.Db, record: Record): Promise<Record> {
32
50
  return db.kysely
@@ -234,6 +252,118 @@ export function getOwned(db: Db.Db, owner: Owner, id: string): Promise<Record |
234
252
  return query.executeTakeFirst()
235
253
  }
236
254
 
255
+ /** Lists owner-visible funding deposits newest-first. */
256
+ export function list(db: Db.Db, options: list.Options): Promise<Record[]> {
257
+ const { cursor, limit } = options
258
+ let builder = query(db, options).selectAll('funding_deposits')
259
+ if (cursor)
260
+ builder = builder.where((eb) =>
261
+ eb.or([
262
+ eb('funding_deposits.createdAt', '<', cursor.createdAt),
263
+ eb.and([
264
+ eb('funding_deposits.createdAt', '=', cursor.createdAt),
265
+ eb('funding_deposits.id', '<', cursor.id),
266
+ ]),
267
+ ]),
268
+ )
269
+ return builder
270
+ .orderBy('funding_deposits.createdAt', 'desc')
271
+ .orderBy('funding_deposits.id', 'desc')
272
+ .limit(limit)
273
+ .execute()
274
+ }
275
+
276
+ export declare namespace list {
277
+ /** Ownership and paging options for funding deposits. */
278
+ type Options = Filters & {
279
+ /** Exclusive lower bound from the previous page. */
280
+ cursor?: listByAddress.Cursor | undefined
281
+ /** Maximum rows to return. */
282
+ limit: number
283
+ /** Ownership filter derived from the authenticated API key. */
284
+ owner: Owner
285
+ }
286
+ }
287
+
288
+ /** Counts owner-visible funding deposits through the list's shared filters. */
289
+ export async function count(db: Db.Db, options: count.Options): Promise<number> {
290
+ const rows = query(db, options)
291
+ .select('funding_deposits.id')
292
+ .limit(options.cap + 1)
293
+ .as('rows')
294
+ const result = await db.kysely
295
+ .selectFrom(rows)
296
+ .select(sql<number>`count(*)::int`.as('count'))
297
+ .executeTakeFirstOrThrow()
298
+ return result.count
299
+ }
300
+
301
+ export declare namespace count {
302
+ /** Ownership, filter, and cap options for counting funding deposits. */
303
+ type Options = Filters & {
304
+ /** Maximum exact count before returning the cap plus one. */
305
+ cap: number
306
+ /** Ownership filter derived from the authenticated API key. */
307
+ owner: Owner
308
+ }
309
+ }
310
+
311
+ function query(db: Db.Db, options: query.Options) {
312
+ const owner = options.owner
313
+ let builder = db.kysely
314
+ .selectFrom('funding_deposits')
315
+ .innerJoin(
316
+ 'funding_deposit_addresses',
317
+ 'funding_deposit_addresses.id',
318
+ 'funding_deposits.depositAddressId',
319
+ )
320
+ .where('funding_deposit_addresses.environment', '=', owner.environment)
321
+ .where('funding_deposit_addresses.orgId', '=', owner.orgId)
322
+ .where('funding_deposits.environment', '=', owner.environment)
323
+ .where('funding_deposits.orgId', '=', owner.orgId)
324
+ if (owner.projectId !== undefined)
325
+ builder = builder
326
+ .where('funding_deposit_addresses.projectId', '=', owner.projectId)
327
+ .where('funding_deposits.projectId', '=', owner.projectId)
328
+ if (options.depositAddress !== undefined) {
329
+ const address = /^0x[0-9a-fA-F]{40}$/.test(options.depositAddress)
330
+ ? options.depositAddress.toLowerCase()
331
+ : options.depositAddress
332
+ builder = builder.where('funding_deposit_addresses.address', '=', address)
333
+ }
334
+ if (options.destinationTokenKeys !== undefined)
335
+ builder = options.destinationTokenKeys.length
336
+ ? builder.where(
337
+ 'funding_deposit_addresses.destinationTokenKey',
338
+ 'in',
339
+ options.destinationTokenKeys,
340
+ )
341
+ : builder.where(sql<boolean>`false`)
342
+ if (options.providerId !== undefined)
343
+ builder = builder.where('funding_deposit_addresses.providerId', '=', options.providerId)
344
+ if (options.recipient !== undefined)
345
+ builder = builder.where(
346
+ sql<boolean>`lower(funding_deposit_addresses.recipient) = ${options.recipient.toLowerCase()}`,
347
+ )
348
+ if (options.sourceChainIds !== undefined)
349
+ builder = options.sourceChainIds.length
350
+ ? builder.where('funding_deposit_addresses.sourceChainId', 'in', options.sourceChainIds)
351
+ : builder.where(sql<boolean>`false`)
352
+ if (options.sourceTokenKeys !== undefined)
353
+ builder = options.sourceTokenKeys.length
354
+ ? builder.where('funding_deposit_addresses.sourceTokenKey', 'in', options.sourceTokenKeys)
355
+ : builder.where(sql<boolean>`false`)
356
+ if (options.status !== undefined)
357
+ builder = builder.where('funding_deposits.status', '=', options.status)
358
+ return builder
359
+ }
360
+
361
+ declare namespace query {
362
+ type Options = Filters & {
363
+ owner: Owner
364
+ }
365
+ }
366
+
237
367
  /** Lists one owner-visible address's deposits newest-first. */
238
368
  export function listByAddress(
239
369
  db: Db.Db,