tapimo 0.12.0 → 0.14.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 (398) hide show
  1. package/dist/App.d.ts +11333 -9563
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js +39 -3
  4. package/dist/App.js.map +1 -1
  5. package/dist/Scope.d.ts +2 -0
  6. package/dist/Scope.d.ts.map +1 -1
  7. package/dist/Scope.js +17 -0
  8. package/dist/Scope.js.map +1 -1
  9. package/dist/Webhooks.js +2 -2
  10. package/dist/Webhooks.js.map +1 -1
  11. package/dist/admin/App.d.ts +1064 -924
  12. package/dist/admin/App.d.ts.map +1 -1
  13. package/dist/admin/apps/api-keys.d.ts +80 -0
  14. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  15. package/dist/admin/apps/api-keys.js +54 -0
  16. package/dist/admin/apps/api-keys.js.map +1 -1
  17. package/dist/admin/apps/earn-vaults.d.ts +63 -15
  18. package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
  19. package/dist/admin/apps/earn-vaults.js +2 -1
  20. package/dist/admin/apps/earn-vaults.js.map +1 -1
  21. package/dist/admin/apps/organizations.d.ts +92 -3
  22. package/dist/admin/apps/organizations.d.ts.map +1 -1
  23. package/dist/admin/apps/organizations.js +69 -7
  24. package/dist/admin/apps/organizations.js.map +1 -1
  25. package/dist/admin/apps/verified-tokens.d.ts +7 -7
  26. package/dist/admin/ui.generated.d.ts +1 -1
  27. package/dist/admin/ui.generated.d.ts.map +1 -1
  28. package/dist/admin/ui.generated.js +151 -151
  29. package/dist/admin/ui.generated.js.map +1 -1
  30. package/dist/apps/Zones.d.ts +28 -28
  31. package/dist/apps/Zones.js +7 -7
  32. package/dist/apps/data/App.d.ts +2909 -2523
  33. package/dist/apps/data/App.d.ts.map +1 -1
  34. package/dist/apps/data/App.js +26 -21
  35. package/dist/apps/data/App.js.map +1 -1
  36. package/dist/apps/data/ZoneSelection.d.ts +8 -8
  37. package/dist/apps/data/ZoneSelection.d.ts.map +1 -1
  38. package/dist/apps/data/ZoneSelection.js +1 -1
  39. package/dist/apps/data/ZoneSelection.js.map +1 -1
  40. package/dist/apps/data/routes/activities.d.ts +588 -584
  41. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  42. package/dist/apps/data/routes/activities.js +1561 -1239
  43. package/dist/apps/data/routes/activities.js.map +1 -1
  44. package/dist/apps/data/routes/addresses.d.ts +22 -22
  45. package/dist/apps/data/routes/balances.d.ts +26 -26
  46. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  47. package/dist/apps/data/routes/balances.js +43 -16
  48. package/dist/apps/data/routes/balances.js.map +1 -1
  49. package/dist/apps/data/routes/blocks.d.ts +44 -44
  50. package/dist/apps/data/routes/blocks.d.ts.map +1 -1
  51. package/dist/apps/data/routes/blocks.js +21 -11
  52. package/dist/apps/data/routes/blocks.js.map +1 -1
  53. package/dist/apps/data/routes/coingecko.d.ts +3 -3
  54. package/dist/apps/data/routes/earn.d.ts +201 -25
  55. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  56. package/dist/apps/data/routes/earn.js +434 -75
  57. package/dist/apps/data/routes/earn.js.map +1 -1
  58. package/dist/apps/data/routes/exchanges.d.ts +160 -145
  59. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  60. package/dist/apps/data/routes/exchanges.js +180 -114
  61. package/dist/apps/data/routes/exchanges.js.map +1 -1
  62. package/dist/apps/data/routes/fee-amm.d.ts +2 -2
  63. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  64. package/dist/apps/data/routes/fee-amm.js +18 -19
  65. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  66. package/dist/apps/data/routes/receipts.d.ts +155 -55
  67. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  68. package/dist/apps/data/routes/receipts.js +28 -10
  69. package/dist/apps/data/routes/receipts.js.map +1 -1
  70. package/dist/apps/data/routes/tokenlist.d.ts +13 -13
  71. package/dist/apps/data/routes/tokens.d.ts +5 -5
  72. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  73. package/dist/apps/data/routes/tokens.js +51 -20
  74. package/dist/apps/data/routes/tokens.js.map +1 -1
  75. package/dist/apps/data/routes/transactions.d.ts +7 -2
  76. package/dist/apps/data/routes/transactions.d.ts.map +1 -1
  77. package/dist/apps/data/routes/transactions.js +37 -17
  78. package/dist/apps/data/routes/transactions.js.map +1 -1
  79. package/dist/apps/data/routes/transfers.d.ts +13 -13
  80. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  81. package/dist/apps/data/routes/transfers.js +18 -12
  82. package/dist/apps/data/routes/transfers.js.map +1 -1
  83. package/dist/apps/data/routes/valuation.d.ts +31 -31
  84. package/dist/apps/data/routes/valuation.d.ts.map +1 -1
  85. package/dist/apps/data/routes/valuation.js +12 -7
  86. package/dist/apps/data/routes/valuation.js.map +1 -1
  87. package/dist/apps/data/routes/verified-tokens.d.ts +8 -8
  88. package/dist/apps/data/routes/webhooks.d.ts +816 -113
  89. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  90. package/dist/apps/data/routes/webhooks.js +47 -5
  91. package/dist/apps/data/routes/webhooks.js.map +1 -1
  92. package/dist/apps/data/routes/zones.d.ts +14 -14
  93. package/dist/apps/data/routes/zones.js +1 -1
  94. package/dist/apps/funding/App.d.ts +466 -44
  95. package/dist/apps/funding/App.d.ts.map +1 -1
  96. package/dist/apps/funding/App.js +1 -1
  97. package/dist/apps/funding/App.js.map +1 -1
  98. package/dist/apps/funding/routes/deposit-addresses.d.ts +17 -17
  99. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
  100. package/dist/apps/funding/routes/deposit-addresses.js +17 -12
  101. package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
  102. package/dist/apps/funding/routes/transfers.d.ts +650 -57
  103. package/dist/apps/funding/routes/transfers.d.ts.map +1 -1
  104. package/dist/apps/funding/routes/transfers.js +238 -24
  105. package/dist/apps/funding/routes/transfers.js.map +1 -1
  106. package/dist/apps/management/App.d.ts +5120 -4152
  107. package/dist/apps/management/App.d.ts.map +1 -1
  108. package/dist/apps/management/Billing.d.ts +1 -1
  109. package/dist/apps/management/Billing.d.ts.map +1 -1
  110. package/dist/apps/management/Billing.js +17 -1
  111. package/dist/apps/management/Billing.js.map +1 -1
  112. package/dist/apps/management/routes/api-keys.d.ts +632 -81
  113. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  114. package/dist/apps/management/routes/api-keys.js +236 -4
  115. package/dist/apps/management/routes/api-keys.js.map +1 -1
  116. package/dist/apps/management/routes/billing.d.ts +76 -76
  117. package/dist/apps/management/routes/faucet.d.ts +6 -6
  118. package/dist/apps/management/routes/invitations.d.ts +24 -24
  119. package/dist/apps/management/routes/invite-links.d.ts +4 -4
  120. package/dist/apps/management/routes/members.d.ts +16 -16
  121. package/dist/apps/management/routes/orgs.d.ts +20 -20
  122. package/dist/apps/management/routes/projects.d.ts +4 -4
  123. package/dist/apps/management/routes/usage.d.ts +8 -8
  124. package/dist/apps/management/routes/verified-token-requests.d.ts +17 -17
  125. package/dist/apps/management/routes/webhooks.d.ts +675 -25
  126. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  127. package/dist/apps/management/routes/webhooks.js +21 -21
  128. package/dist/apps/management/routes/webhooks.js.map +1 -1
  129. package/dist/apps/relay/App.d.ts.map +1 -1
  130. package/dist/apps/relay/App.js +115 -23
  131. package/dist/apps/relay/App.js.map +1 -1
  132. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  133. package/dist/apps/relay/Sponsorships.js +27 -1
  134. package/dist/apps/relay/Sponsorships.js.map +1 -1
  135. package/dist/db/Db.d.ts.map +1 -1
  136. package/dist/db/Db.js +34 -1
  137. package/dist/db/Db.js.map +1 -1
  138. package/dist/db/Schema.d.ts +11 -5
  139. package/dist/db/Schema.d.ts.map +1 -1
  140. package/dist/db/Schema.js +21 -3
  141. package/dist/db/Schema.js.map +1 -1
  142. package/dist/db/tables/fundingCatalog.d.ts +4 -2
  143. package/dist/db/tables/fundingCatalog.d.ts.map +1 -1
  144. package/dist/db/tables/fundingCatalog.js +5 -1
  145. package/dist/db/tables/fundingCatalog.js.map +1 -1
  146. package/dist/db/tables/fundingTransferTransactions.d.ts +38 -0
  147. package/dist/db/tables/fundingTransferTransactions.d.ts.map +1 -1
  148. package/dist/db/tables/fundingTransferTransactions.js +44 -3
  149. package/dist/db/tables/fundingTransferTransactions.js.map +1 -1
  150. package/dist/db/tables/fundingTransfers.d.ts +17 -0
  151. package/dist/db/tables/fundingTransfers.d.ts.map +1 -1
  152. package/dist/db/tables/fundingTransfers.js +21 -0
  153. package/dist/db/tables/fundingTransfers.js.map +1 -1
  154. package/dist/db/tables/organizations.d.ts +24 -0
  155. package/dist/db/tables/organizations.d.ts.map +1 -1
  156. package/dist/db/tables/organizations.js +27 -0
  157. package/dist/db/tables/organizations.js.map +1 -1
  158. package/dist/db/tables/projects.d.ts +25 -1
  159. package/dist/db/tables/projects.d.ts.map +1 -1
  160. package/dist/db/tables/projects.js +26 -0
  161. package/dist/db/tables/projects.js.map +1 -1
  162. package/dist/db/tables/sponsoredTransactions.d.ts +44 -0
  163. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  164. package/dist/db/tables/sponsoredTransactions.js +146 -13
  165. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  166. package/dist/db/tables/webhookSubscriptions.d.ts +10 -6
  167. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  168. package/dist/db/tables/webhookSubscriptions.js +9 -6
  169. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  170. package/dist/handlers/internal/utils.d.ts.map +1 -1
  171. package/dist/handlers/internal/utils.js +9 -0
  172. package/dist/handlers/internal/utils.js.map +1 -1
  173. package/dist/internal/Auth.d.ts +3 -1
  174. package/dist/internal/Auth.d.ts.map +1 -1
  175. package/dist/internal/Auth.js +6 -3
  176. package/dist/internal/Auth.js.map +1 -1
  177. package/dist/internal/EarnVaults.d.ts +3 -3
  178. package/dist/internal/EarnVaults.js +5 -5
  179. package/dist/internal/EarnVaults.js.map +1 -1
  180. package/dist/internal/Fetch.d.ts +23 -0
  181. package/dist/internal/Fetch.d.ts.map +1 -0
  182. package/dist/internal/Fetch.js +26 -0
  183. package/dist/internal/Fetch.js.map +1 -0
  184. package/dist/internal/Log.d.ts +4 -2
  185. package/dist/internal/Log.d.ts.map +1 -1
  186. package/dist/internal/Log.js +16 -3
  187. package/dist/internal/Log.js.map +1 -1
  188. package/dist/internal/OpenApi.d.ts +5 -3
  189. package/dist/internal/OpenApi.d.ts.map +1 -1
  190. package/dist/internal/OpenApi.js +13 -3
  191. package/dist/internal/OpenApi.js.map +1 -1
  192. package/dist/internal/Response.d.ts +1 -1
  193. package/dist/internal/Response.d.ts.map +1 -1
  194. package/dist/internal/Schema.d.ts +6 -3
  195. package/dist/internal/Schema.d.ts.map +1 -1
  196. package/dist/internal/Schema.js +8 -2
  197. package/dist/internal/Schema.js.map +1 -1
  198. package/dist/internal/Store.d.ts +16 -3
  199. package/dist/internal/Store.d.ts.map +1 -1
  200. package/dist/internal/Store.js +82 -16
  201. package/dist/internal/Store.js.map +1 -1
  202. package/dist/internal/Tidx.d.ts.map +1 -1
  203. package/dist/internal/Tidx.js +7 -2
  204. package/dist/internal/Tidx.js.map +1 -1
  205. package/dist/internal/Timeout.d.ts +36 -0
  206. package/dist/internal/Timeout.d.ts.map +1 -0
  207. package/dist/internal/Timeout.js +37 -0
  208. package/dist/internal/Timeout.js.map +1 -0
  209. package/dist/internal/Timing.d.ts +9 -0
  210. package/dist/internal/Timing.d.ts.map +1 -1
  211. package/dist/internal/Timing.js +7 -0
  212. package/dist/internal/Timing.js.map +1 -1
  213. package/dist/internal/Viem.d.ts +16 -1
  214. package/dist/internal/Viem.d.ts.map +1 -1
  215. package/dist/internal/Viem.js +12 -2
  216. package/dist/internal/Viem.js.map +1 -1
  217. package/dist/internal/WebhookDestination.d.ts.map +1 -1
  218. package/dist/internal/WebhookDestination.js +3 -0
  219. package/dist/internal/WebhookDestination.js.map +1 -1
  220. package/dist/internal/funding/Action.d.ts +9 -0
  221. package/dist/internal/funding/Action.d.ts.map +1 -1
  222. package/dist/internal/funding/Action.js +16 -8
  223. package/dist/internal/funding/Action.js.map +1 -1
  224. package/dist/internal/funding/Catalog.d.ts +9 -2
  225. package/dist/internal/funding/Catalog.d.ts.map +1 -1
  226. package/dist/internal/funding/Catalog.js +19 -8
  227. package/dist/internal/funding/Catalog.js.map +1 -1
  228. package/dist/internal/funding/DepositWebhook.d.ts.map +1 -1
  229. package/dist/internal/funding/DepositWebhook.js +2 -1
  230. package/dist/internal/funding/DepositWebhook.js.map +1 -1
  231. package/dist/internal/funding/Evidence.d.ts.map +1 -1
  232. package/dist/internal/funding/Evidence.js +2 -10
  233. package/dist/internal/funding/Evidence.js.map +1 -1
  234. package/dist/internal/funding/Provider.d.ts +108 -13
  235. package/dist/internal/funding/Provider.d.ts.map +1 -1
  236. package/dist/internal/funding/Provider.js +34 -8
  237. package/dist/internal/funding/Provider.js.map +1 -1
  238. package/dist/internal/funding/SourceObservation.d.ts.map +1 -1
  239. package/dist/internal/funding/SourceObservation.js +5 -7
  240. package/dist/internal/funding/SourceObservation.js.map +1 -1
  241. package/dist/internal/funding/Transfer.d.ts +114 -18
  242. package/dist/internal/funding/Transfer.d.ts.map +1 -1
  243. package/dist/internal/funding/Transfer.js +185 -55
  244. package/dist/internal/funding/Transfer.js.map +1 -1
  245. package/dist/internal/funding/TransferReconciliation.d.ts +52 -0
  246. package/dist/internal/funding/TransferReconciliation.d.ts.map +1 -0
  247. package/dist/internal/funding/TransferReconciliation.js +192 -0
  248. package/dist/internal/funding/TransferReconciliation.js.map +1 -0
  249. package/dist/internal/funding/TransferWebhook.d.ts +21 -0
  250. package/dist/internal/funding/TransferWebhook.d.ts.map +1 -0
  251. package/dist/internal/funding/TransferWebhook.js +43 -0
  252. package/dist/internal/funding/TransferWebhook.js.map +1 -0
  253. package/dist/internal/funding/index.d.ts +1 -0
  254. package/dist/internal/funding/index.d.ts.map +1 -1
  255. package/dist/internal/funding/index.js +1 -0
  256. package/dist/internal/funding/index.js.map +1 -1
  257. package/dist/internal/funding/providers/relay.d.ts.map +1 -1
  258. package/dist/internal/funding/providers/relay.js +3 -12
  259. package/dist/internal/funding/providers/relay.js.map +1 -1
  260. package/dist/internal/funding/providers/stargate.d.ts +127 -9
  261. package/dist/internal/funding/providers/stargate.d.ts.map +1 -1
  262. package/dist/internal/funding/providers/stargate.js +548 -112
  263. package/dist/internal/funding/providers/stargate.js.map +1 -1
  264. package/dist/internal/index.d.ts +1 -0
  265. package/dist/internal/index.d.ts.map +1 -1
  266. package/dist/internal/index.js +1 -0
  267. package/dist/internal/index.js.map +1 -1
  268. package/dist/internal/webhookDestinations/slack.d.ts.map +1 -1
  269. package/dist/internal/webhookDestinations/slack.js +16 -0
  270. package/dist/internal/webhookDestinations/slack.js.map +1 -1
  271. package/package.json +2 -2
  272. package/src/App.test.ts +100 -0
  273. package/src/App.ts +43 -3
  274. package/src/Client.test-d.ts +129 -2
  275. package/src/Scope.test.ts +20 -0
  276. package/src/Scope.ts +16 -0
  277. package/src/Webhooks.ts +2 -2
  278. package/src/admin/apps/api-keys.test.ts +75 -0
  279. package/src/admin/apps/api-keys.ts +64 -0
  280. package/src/admin/apps/earn-vaults.test.ts +1 -1
  281. package/src/admin/apps/earn-vaults.ts +2 -1
  282. package/src/admin/apps/organizations.test.ts +53 -1
  283. package/src/admin/apps/organizations.ts +90 -5
  284. package/src/admin/ui/src/lib/api-keys.ts +19 -0
  285. package/src/admin/ui/src/lib/organizations.test.ts +112 -1
  286. package/src/admin/ui/src/lib/organizations.ts +55 -0
  287. package/src/admin/ui/src/routes/api-keys.tsx +89 -2
  288. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +116 -0
  289. package/src/admin/ui.generated.ts +189 -151
  290. package/src/apps/Zones.test.ts +7 -7
  291. package/src/apps/Zones.ts +7 -7
  292. package/src/apps/composition.test.ts +2 -1
  293. package/src/apps/data/App.test.ts +41 -0
  294. package/src/apps/data/App.ts +30 -21
  295. package/src/apps/data/ZoneSelection.test.ts +2 -2
  296. package/src/apps/data/ZoneSelection.ts +8 -8
  297. package/src/apps/data/routes/activities.test.ts +1526 -12
  298. package/src/apps/data/routes/activities.ts +1774 -1356
  299. package/src/apps/data/routes/balances.ts +133 -97
  300. package/src/apps/data/routes/blocks.test.ts +533 -0
  301. package/src/apps/data/routes/blocks.ts +35 -16
  302. package/src/apps/data/routes/earn.test.ts +373 -16
  303. package/src/apps/data/routes/earn.ts +548 -79
  304. package/src/apps/data/routes/earn.zone-access.test.ts +6 -6
  305. package/src/apps/data/routes/exchanges.test.ts +740 -15
  306. package/src/apps/data/routes/exchanges.ts +954 -753
  307. package/src/apps/data/routes/fee-amm.ts +17 -18
  308. package/src/apps/data/routes/receipts.ts +47 -16
  309. package/src/apps/data/routes/tokens.test.ts +578 -0
  310. package/src/apps/data/routes/tokens.ts +283 -225
  311. package/src/apps/data/routes/transactions.ts +68 -29
  312. package/src/apps/data/routes/transfers.test.ts +421 -0
  313. package/src/apps/data/routes/transfers.ts +45 -27
  314. package/src/apps/data/routes/valuation.ts +94 -78
  315. package/src/apps/data/routes/webhooks.test.ts +21 -5
  316. package/src/apps/data/routes/webhooks.ts +63 -8
  317. package/src/apps/data/routes/zones.test.ts +2 -2
  318. package/src/apps/data/routes/zones.ts +1 -1
  319. package/src/apps/funding/App.ts +12 -2
  320. package/src/apps/funding/routes/deposit-addresses.test.ts +29 -7
  321. package/src/apps/funding/routes/deposit-addresses.ts +19 -14
  322. package/src/apps/funding/routes/quotes.test.ts +5 -9
  323. package/src/apps/funding/routes/transfers.test.ts +901 -23
  324. package/src/apps/funding/routes/transfers.ts +347 -64
  325. package/src/apps/management/Billing.ts +18 -5
  326. package/src/apps/management/routes/api-keys.test.ts +220 -0
  327. package/src/apps/management/routes/api-keys.ts +280 -3
  328. package/src/apps/management/routes/webhooks.test.ts +3 -3
  329. package/src/apps/management/routes/webhooks.ts +24 -25
  330. package/src/apps/relay/App.test.ts +212 -1
  331. package/src/apps/relay/App.ts +158 -29
  332. package/src/apps/relay/Sponsorships.ts +24 -1
  333. package/src/db/Db.ts +48 -1
  334. package/src/db/Schema.ts +22 -4
  335. package/src/db/tables/earnVaults.test.ts +9 -9
  336. package/src/db/tables/fundingCatalog.test.ts +16 -2
  337. package/src/db/tables/fundingCatalog.ts +9 -3
  338. package/src/db/tables/fundingTransferTransactions.test.ts +43 -0
  339. package/src/db/tables/fundingTransferTransactions.ts +79 -3
  340. package/src/db/tables/fundingTransfers.test.ts +26 -0
  341. package/src/db/tables/fundingTransfers.ts +36 -0
  342. package/src/db/tables/organizations.test.ts +41 -0
  343. package/src/db/tables/organizations.ts +49 -0
  344. package/src/db/tables/projects.test.ts +55 -0
  345. package/src/db/tables/projects.ts +46 -1
  346. package/src/db/tables/sponsoredTransactions.test.ts +338 -0
  347. package/src/db/tables/sponsoredTransactions.ts +197 -13
  348. package/src/db/tables/webhookSubscriptions.ts +18 -11
  349. package/src/handlers/internal/utils.test.ts +53 -0
  350. package/src/handlers/internal/utils.ts +15 -0
  351. package/src/internal/Auth.ts +12 -4
  352. package/src/internal/EarnVaults.capabilities.test.ts +2 -2
  353. package/src/internal/EarnVaults.test.ts +1 -1
  354. package/src/internal/EarnVaults.ts +5 -5
  355. package/src/internal/Fetch.test.ts +46 -0
  356. package/src/internal/Fetch.ts +56 -0
  357. package/src/internal/Log.test-d.ts +2 -0
  358. package/src/internal/Log.test.ts +21 -0
  359. package/src/internal/Log.ts +22 -5
  360. package/src/internal/OpenApi.ts +14 -4
  361. package/src/internal/Response.ts +1 -1
  362. package/src/internal/Schema.test.ts +2 -0
  363. package/src/internal/Schema.ts +12 -2
  364. package/src/internal/Store.test.ts +178 -0
  365. package/src/internal/Store.ts +104 -18
  366. package/src/internal/Tidx.test.ts +2 -1
  367. package/src/internal/Tidx.ts +7 -2
  368. package/src/internal/Timeout.test.ts +35 -0
  369. package/src/internal/Timeout.ts +73 -0
  370. package/src/internal/Timing.ts +16 -0
  371. package/src/internal/Viem.ts +39 -3
  372. package/src/internal/WebhookDestination.ts +3 -0
  373. package/src/internal/Webhooks.test.ts +45 -0
  374. package/src/internal/funding/Action.test.ts +41 -1
  375. package/src/internal/funding/Action.ts +69 -41
  376. package/src/internal/funding/Catalog.test.ts +34 -0
  377. package/src/internal/funding/Catalog.ts +28 -11
  378. package/src/internal/funding/DepositWebhook.ts +2 -1
  379. package/src/internal/funding/Evidence.ts +2 -13
  380. package/src/internal/funding/Provider.test.ts +73 -0
  381. package/src/internal/funding/Provider.ts +125 -28
  382. package/src/internal/funding/SourceObservation.ts +5 -9
  383. package/src/internal/funding/Transfer.test.ts +155 -0
  384. package/src/internal/funding/Transfer.ts +387 -147
  385. package/src/internal/funding/TransferReconciliation.test.ts +147 -0
  386. package/src/internal/funding/TransferReconciliation.ts +264 -0
  387. package/src/internal/funding/TransferWebhook.test.ts +128 -0
  388. package/src/internal/funding/TransferWebhook.ts +67 -0
  389. package/src/internal/funding/index.test.ts +1 -0
  390. package/src/internal/funding/index.ts +1 -0
  391. package/src/internal/funding/providers/index.test.ts +4 -167
  392. package/src/internal/funding/providers/relay.test.ts +2 -2
  393. package/src/internal/funding/providers/relay.ts +3 -15
  394. package/src/internal/funding/providers/stargate.test.ts +328 -0
  395. package/src/internal/funding/providers/stargate.ts +807 -135
  396. package/src/internal/index.test.ts +1 -0
  397. package/src/internal/index.ts +1 -0
  398. package/src/internal/webhookDestinations/slack.ts +15 -0
@@ -71,6 +71,18 @@ export namespace schema {
71
71
  'An organization visible to an authenticated administrator.',
72
72
  )
73
73
 
74
+ const SponsorshipSubsidy = Schema.describe(
75
+ z.object({
76
+ durationDays: z
77
+ .nullable(z.literal(90))
78
+ .check(z.describe('Promotion duration in days, or null when disabled.'), z.meta({ examples: [90] })), // prettier-ignore
79
+ projectSpendLimit: z
80
+ .nullable(z.string())
81
+ .check(z.describe('Optional per-project promotion cap in USD.'), z.meta({ examples: ['1000'] })), // prettier-ignore
82
+ }),
83
+ 'Current per-project sponsorship subsidy policy.',
84
+ )
85
+
74
86
  /** Schemas for the listOrganizations operation. */
75
87
  export namespace listOrganizations {
76
88
  /** Query parameters for organization lookup. */
@@ -287,6 +299,7 @@ export namespace schema {
287
299
  ],
288
300
  }),
289
301
  ),
302
+ sponsorshipSubsidy: SponsorshipSubsidy,
290
303
  }),
291
304
  'Detailed organization information for administrators.',
292
305
  )
@@ -435,8 +448,8 @@ export namespace schema {
435
448
  )
436
449
  }
437
450
 
438
- /** Schemas for the getOrganizationSponsorship operation. */
439
- export namespace getOrganizationSponsorship {
451
+ /** Schemas for organization sponsorship operations. */
452
+ export namespace organizationSponsorship {
440
453
  const Environment = z.object({
441
454
  committedSpend: z
442
455
  .string()
@@ -454,7 +467,7 @@ export namespace schema {
454
467
  })
455
468
 
456
469
  /** Current-period sponsorship spend by key environment. */
457
- export const Response = Schema.describe(
470
+ export const GetResponse = Schema.describe(
458
471
  z.object({
459
472
  production: Environment,
460
473
  sandbox: Environment,
@@ -462,6 +475,28 @@ export namespace schema {
462
475
  }),
463
476
  'Current-period sponsorship spend for an organization.',
464
477
  )
478
+
479
+ /** Replacement subsidy policy. */
480
+ export const SetBody = z
481
+ .union([
482
+ z.object({
483
+ enabled: z.literal(true),
484
+ projectSpendLimit: z.optional(
485
+ z
486
+ .string()
487
+ .check(
488
+ z.regex(/^(?:0|[1-9]\d*)(?:\.\d{1,6})?$/),
489
+ z.describe('Optional per-project promotion cap in USD.'),
490
+ z.meta({ examples: ['1000'] }),
491
+ ),
492
+ ),
493
+ }),
494
+ z.object({ enabled: z.literal(false) }),
495
+ ])
496
+ .check(z.describe('Replacement per-project sponsorship subsidy policy.'))
497
+
498
+ /** Current subsidy policy. */
499
+ export const SetResponse = SponsorshipSubsidy
465
500
  }
466
501
 
467
502
  /** Schemas for the getOrganizationWebhooks operation. */
@@ -640,6 +675,56 @@ export function organizations() {
640
675
  production: serializeBilling(productionSettings, productionSource),
641
676
  sandbox: serializeBilling(sandboxSettings, sandboxSource),
642
677
  },
678
+ sponsorshipSubsidy: {
679
+ durationDays: organization.sponsorshipSubsidyDurationDays,
680
+ projectSpendLimit: organization.sponsorshipSubsidyProjectSpendLimit,
681
+ },
682
+ }),
683
+ 200,
684
+ )
685
+ } catch (cause) {
686
+ return Response.upstream(c, cause)
687
+ }
688
+ },
689
+ )
690
+ .put(
691
+ '/:orgId/sponsorship',
692
+ OpenApi.validate('param', schema.getOrganization.Params, {
693
+ code: 'param_invalid',
694
+ message: 'Invalid path parameters',
695
+ }),
696
+ OpenApi.validate('json', schema.organizationSponsorship.SetBody, {
697
+ code: 'body_invalid',
698
+ message: 'Invalid request body',
699
+ }),
700
+ OpenApi.describeRoute({
701
+ operationId: 'setAdminOrganizationSponsorshipSubsidy',
702
+ responses: OpenApi.responses({
703
+ errors: { 404: 'Organization not found.' },
704
+ success: {
705
+ description: 'Current sponsorship subsidy policy.',
706
+ schema: schema.organizationSponsorship.SetResponse,
707
+ },
708
+ }),
709
+ summary: 'Set sponsorship subsidy',
710
+ tags: ['Organizations'],
711
+ }),
712
+ async (c) => {
713
+ const { orgId } = c.req.valid('param')
714
+ const policy = c.req.valid('json')
715
+ try {
716
+ const organization = await Organizations.setSponsorshipSubsidy(
717
+ Db.get(c.get('db')),
718
+ orgId,
719
+ policy.enabled
720
+ ? { durationDays: 90, projectSpendLimit: policy.projectSpendLimit ?? null }
721
+ : { durationDays: null, projectSpendLimit: null },
722
+ )
723
+ if (!organization) return organizationNotFound(c)
724
+ return c.json(
725
+ Response.validated(schema.organizationSponsorship.SetResponse, {
726
+ durationDays: organization.sponsorshipSubsidyDurationDays,
727
+ projectSpendLimit: organization.sponsorshipSubsidyProjectSpendLimit,
643
728
  }),
644
729
  200,
645
730
  )
@@ -933,7 +1018,7 @@ export function organizations() {
933
1018
  errors: { 404: 'Organization not found.' },
934
1019
  success: {
935
1020
  description: 'Recent organization sponsorship activity.',
936
- schema: schema.getOrganizationSponsorship.Response,
1021
+ schema: schema.organizationSponsorship.GetResponse,
937
1022
  },
938
1023
  }),
939
1024
  summary: 'Get sponsorship activity',
@@ -969,7 +1054,7 @@ export function organizations() {
969
1054
  }),
970
1055
  )
971
1056
  return c.json(
972
- Response.validated(schema.getOrganizationSponsorship.Response, {
1057
+ Response.validated(schema.organizationSponsorship.GetResponse, {
973
1058
  production,
974
1059
  sandbox,
975
1060
  to,
@@ -93,6 +93,25 @@ export namespace update {
93
93
  }
94
94
  }
95
95
 
96
+ /** Rotates an API key while preserving its access and attribution. */
97
+ export namespace rotate {
98
+ export async function fn(id: string) {
99
+ const response = await client['api-keys'][':id'].rotate.$post({ param: { id } })
100
+ if (!response.ok)
101
+ throw new Error(
102
+ await Response.errorMessage(response, { fallback: 'Failed to rotate the API key.' }),
103
+ )
104
+ return response.json()
105
+ }
106
+ export function useMutation() {
107
+ const queryClient = ReactQuery.useQueryClient()
108
+ return ReactQuery.useMutation({
109
+ mutationFn: fn,
110
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: get.baseKey }),
111
+ })
112
+ }
113
+ }
114
+
96
115
  /** Revoke an API key by id. */
97
116
  export namespace revoke {
98
117
  export async function fn(id: string) {
@@ -5,7 +5,11 @@ import { renderToStaticMarkup } from 'react-dom/server'
5
5
  import { describe, expect, it, vi } from 'vite-plus/test'
6
6
 
7
7
  import { routeTree } from '../routeTree.gen.js'
8
- import { BillingSourcesCard, Route as DetailRoute } from '../routes/organizations_.$orgId.js'
8
+ import {
9
+ BillingSourcesCard,
10
+ Route as DetailRoute,
11
+ SponsorshipSubsidyCard,
12
+ } from '../routes/organizations_.$orgId.js'
9
13
  import { Route as ListRoute } from '../routes/organizations.js'
10
14
  import * as Organizations from './organizations.js'
11
15
 
@@ -75,6 +79,7 @@ describe('withEnabledSources', () => {
75
79
  memberCount: 0,
76
80
  name: 'Example',
77
81
  projectCount: 0,
82
+ sponsorshipSubsidy: { durationDays: null, projectSpendLimit: null },
78
83
  updatedAt: '2026-01-01T00:00:00.000Z',
79
84
  userId: null,
80
85
  } satisfies Organizations.Detail
@@ -101,6 +106,50 @@ describe('withEnabledSources', () => {
101
106
  })
102
107
  })
103
108
 
109
+ describe('withSponsorshipSubsidy', () => {
110
+ it('replaces only the organization subsidy policy', () => {
111
+ const detail = {
112
+ billing: {
113
+ enabledSources: [],
114
+ production: { settings: null, source: null },
115
+ sandbox: { settings: null, source: null },
116
+ },
117
+ createdAt: '2026-01-01T00:00:00.000Z',
118
+ createdBy: null,
119
+ id: 'org_example',
120
+ memberCount: 0,
121
+ name: 'Example',
122
+ projectCount: 0,
123
+ sponsorshipSubsidy: { durationDays: null, projectSpendLimit: null },
124
+ updatedAt: '2026-01-01T00:00:00.000Z',
125
+ userId: null,
126
+ } satisfies Organizations.Detail
127
+
128
+ const updated = Organizations.withSponsorshipSubsidy({
129
+ detail,
130
+ sponsorshipSubsidy: { durationDays: 90, projectSpendLimit: '1000' },
131
+ })
132
+
133
+ expect({
134
+ billingPreserved: updated.billing === detail.billing,
135
+ original: detail.sponsorshipSubsidy,
136
+ updated: updated.sponsorshipSubsidy,
137
+ }).toMatchInlineSnapshot(`
138
+ {
139
+ "billingPreserved": true,
140
+ "original": {
141
+ "durationDays": null,
142
+ "projectSpendLimit": null,
143
+ },
144
+ "updated": {
145
+ "durationDays": 90,
146
+ "projectSpendLimit": "1000",
147
+ },
148
+ }
149
+ `)
150
+ })
151
+ })
152
+
104
153
  describe('BillingSourcesCard', () => {
105
154
  it('renders source state and the setup-only warning', () => {
106
155
  const markup = renderToStaticMarkup(
@@ -139,6 +188,68 @@ describe('BillingSourcesCard', () => {
139
188
  })
140
189
  })
141
190
 
191
+ describe('SponsorshipSubsidyCard', () => {
192
+ it('renders an enabled policy with its fixed duration and optional cap', () => {
193
+ const markup = renderToStaticMarkup(
194
+ createElement(SponsorshipSubsidyCard, {
195
+ error: null,
196
+ onSave() {},
197
+ pending: false,
198
+ policy: { durationDays: 90, projectSpendLimit: '1000' },
199
+ }),
200
+ )
201
+ const marker = 'aria-label="Sponsorship subsidy"'
202
+ const markerIndex = markup.indexOf(marker)
203
+ const switchTag = markup.slice(
204
+ markup.lastIndexOf('<', markerIndex),
205
+ markup.indexOf('>', markerIndex),
206
+ )
207
+
208
+ expect({
209
+ cap: markup.includes('value="1000"'),
210
+ duration: markup.includes('value="90 days"'),
211
+ enabled: switchTag.includes('aria-checked="true"'),
212
+ startsAt: markup.includes('first mainnet sponsorship'),
213
+ }).toMatchInlineSnapshot(`
214
+ {
215
+ "cap": true,
216
+ "duration": true,
217
+ "enabled": true,
218
+ "startsAt": true,
219
+ }
220
+ `)
221
+ })
222
+
223
+ it('renders a disabled policy', () => {
224
+ const markup = renderToStaticMarkup(
225
+ createElement(SponsorshipSubsidyCard, {
226
+ error: null,
227
+ onSave() {},
228
+ pending: false,
229
+ policy: { durationDays: null, projectSpendLimit: null },
230
+ }),
231
+ )
232
+ const marker = 'aria-label="Sponsorship subsidy"'
233
+ const markerIndex = markup.indexOf(marker)
234
+ const switchTag = markup.slice(
235
+ markup.lastIndexOf('<', markerIndex),
236
+ markup.indexOf('>', markerIndex),
237
+ )
238
+
239
+ expect({
240
+ billedNormally: markup.includes('billed normally'),
241
+ disabled: switchTag.includes('aria-checked="false"'),
242
+ spendCapHidden: !markup.includes('Per-project spend cap'),
243
+ }).toMatchInlineSnapshot(`
244
+ {
245
+ "billedNormally": true,
246
+ "disabled": true,
247
+ "spendCapHidden": true,
248
+ }
249
+ `)
250
+ })
251
+ })
252
+
142
253
  describe('organization routes', () => {
143
254
  it('matches a direct detail URL without the list route as a parent', () => {
144
255
  const router = createRouter({
@@ -26,6 +26,10 @@ type SponsorshipResponse = InferResponseType<
26
26
  (typeof client)['organizations'][':orgId']['sponsorship']['$get'],
27
27
  200
28
28
  >
29
+ type SponsorshipSubsidyResponse = InferResponseType<
30
+ (typeof client)['organizations'][':orgId']['sponsorship']['$put'],
31
+ 200
32
+ >
29
33
  type UsageResponse = InferResponseType<
30
34
  (typeof client)['organizations'][':orgId']['usage']['$get'],
31
35
  200
@@ -172,6 +176,24 @@ export declare namespace withEnabledSources {
172
176
  }
173
177
  }
174
178
 
179
+ /** Returns organization detail with its sponsorship subsidy policy replaced. */
180
+ export function withSponsorshipSubsidy(options: withSponsorshipSubsidy.Options): Detail {
181
+ return {
182
+ ...options.detail,
183
+ sponsorshipSubsidy: options.sponsorshipSubsidy,
184
+ }
185
+ }
186
+
187
+ export declare namespace withSponsorshipSubsidy {
188
+ /** Inputs for replacing an organization's cached subsidy policy. */
189
+ type Options = {
190
+ /** Organization detail to update. */
191
+ detail: Detail
192
+ /** Current sponsorship subsidy policy. */
193
+ sponsorshipSubsidy: Detail['sponsorshipSubsidy']
194
+ }
195
+ }
196
+
175
197
  /** Enable an organization-wide billing source. */
176
198
  export namespace enableBillingSource {
177
199
  /** Billing source to enable for an organization. */
@@ -236,6 +258,39 @@ export namespace disableBillingSource {
236
258
  }
237
259
  }
238
260
 
261
+ /** Replace an organization's sponsorship subsidy policy. */
262
+ export namespace setSponsorshipSubsidy {
263
+ /** Sponsorship subsidy policy accepted by the admin API. */
264
+ export type Input = { enabled: false } | { enabled: true; projectSpendLimit?: string | undefined }
265
+
266
+ /** Replaces the policy and returns its current value. */
267
+ export async function fn(options: Input & { orgId: string }) {
268
+ const json = ((): Input => {
269
+ if (!options.enabled) return { enabled: false }
270
+ if (!options.projectSpendLimit) return { enabled: true }
271
+ return { enabled: true, projectSpendLimit: options.projectSpendLimit }
272
+ })()
273
+ const response = await client.organizations[':orgId'].sponsorship.$put({
274
+ json,
275
+ param: { orgId: options.orgId },
276
+ })
277
+ if (!response.ok) throw new Error('Failed to update sponsorship subsidy.')
278
+ return (await response.json()) as SponsorshipSubsidyResponse
279
+ }
280
+
281
+ /** Creates a mutation that replaces the subsidy policy. */
282
+ export function useMutation(orgId: string) {
283
+ const queryClient = ReactQuery.useQueryClient()
284
+ return ReactQuery.useMutation({
285
+ mutationFn: (input: Input) => fn({ ...input, orgId }),
286
+ onSuccess: (response) =>
287
+ queryClient.setQueryData(get.key(orgId), (detail: Detail | undefined) =>
288
+ detail ? withSponsorshipSubsidy({ detail, sponsorshipSubsidy: response }) : detail,
289
+ ),
290
+ })
291
+ }
292
+ }
293
+
239
294
  /** List members for one organization. */
240
295
  export namespace members {
241
296
  export function key(orgId: string) {
@@ -8,6 +8,7 @@ import {
8
8
  Copy as CopyIcon,
9
9
  Pencil,
10
10
  Plus,
11
+ RefreshCw,
11
12
  Search,
12
13
  Trash2,
13
14
  TriangleAlert,
@@ -207,13 +208,13 @@ function RouteComponent() {
207
208
  )
208
209
  }
209
210
 
210
- function TokenBanner(props: { onDismiss: () => void; token: string }) {
211
+ function TokenBanner(props: { onDismiss: () => void; title?: string; token: string }) {
211
212
  const { copy, notifying } = useCopy()
212
213
  return (
213
214
  <Card className="flex flex-col gap-3 p-4">
214
215
  <div className="flex items-start justify-between gap-3">
215
216
  <div className="flex flex-col gap-1">
216
- <span className="text-label-14 text-foreground">Key provisioned</span>
217
+ <span className="text-label-14 text-foreground">{props.title ?? 'Key provisioned'}</span>
217
218
  <span className="text-label-12 text-foreground-secondary">
218
219
  Copy this token now, it won't be shown again.
219
220
  </span>
@@ -732,6 +733,7 @@ function KeyRow(props: { currentUser?: string | undefined; record: ApiKeys.ApiKe
732
733
  </dl>
733
734
  <div className="flex justify-end gap-2 border-t border-border px-5 py-3">
734
735
  <EditDialog record={record} />
736
+ <RotateDialog record={record} />
735
737
  <RevokeDialog currentUser={currentUser} record={record} />
736
738
  </div>
737
739
  </Rows.Panel>
@@ -812,6 +814,91 @@ declare namespace EditDialog {
812
814
  }
813
815
  }
814
816
 
817
+ function RotateDialog(options: RotateDialog.Props) {
818
+ const { record } = options
819
+ const [open, setOpen] = useState(false)
820
+ const rotate = ApiKeys.rotate.useMutation()
821
+
822
+ return (
823
+ <Dialog.Root
824
+ onOpenChange={(value) => {
825
+ if (!value && rotate.isPending) return
826
+ setOpen(value)
827
+ if (!value) rotate.reset()
828
+ }}
829
+ open={open}
830
+ >
831
+ <Dialog.Trigger
832
+ render={
833
+ <Button size="small">
834
+ <RefreshCw className="size-4" />
835
+ Rotate key
836
+ </Button>
837
+ }
838
+ />
839
+ <Dialog.Popup>
840
+ {rotate.data ? (
841
+ <>
842
+ <Dialog.Title>Key rotated</Dialog.Title>
843
+ <Dialog.Description>
844
+ Copy the replacement token now. The source key remains active until it is revoked.
845
+ </Dialog.Description>
846
+ <div className="mt-5">
847
+ <TokenBanner
848
+ onDismiss={() => setOpen(false)}
849
+ title="Replacement key provisioned"
850
+ token={rotate.data.token}
851
+ />
852
+ </div>
853
+ </>
854
+ ) : (
855
+ <>
856
+ <Dialog.Title>Rotate {record.name ?? 'API key'}?</Dialog.Title>
857
+ <Dialog.Description>
858
+ This creates a replacement with the same scopes, ownership, limits, and restrictions.
859
+ The source key remains active.
860
+ </Dialog.Description>
861
+ <dl className="mt-5 grid grid-cols-2 gap-4 border border-border bg-secondary p-4">
862
+ <Detail className="col-span-2" label="Key ID">
863
+ <code className="break-all">{record.id}</code>
864
+ </Detail>
865
+ <Detail label="Environment">{record.environment}</Detail>
866
+ <Detail label="Organization">{organization(record)}</Detail>
867
+ <Detail label="Project">{project(record)}</Detail>
868
+ <Detail label="Scopes">{record.scopes.length}</Detail>
869
+ </dl>
870
+ {rotate.error ? (
871
+ <p className="mt-3 text-copy-13 text-negative">{rotate.error.message}</p>
872
+ ) : null}
873
+ <div className="mt-6 flex justify-end gap-2">
874
+ <Dialog.Close
875
+ render={
876
+ <Button disabled={rotate.isPending} type="button">
877
+ Cancel
878
+ </Button>
879
+ }
880
+ />
881
+ <Button
882
+ loading={rotate.isPending}
883
+ onClick={() => rotate.mutate(record.id)}
884
+ variant="primary"
885
+ >
886
+ Rotate key
887
+ </Button>
888
+ </div>
889
+ </>
890
+ )}
891
+ </Dialog.Popup>
892
+ </Dialog.Root>
893
+ )
894
+ }
895
+
896
+ declare namespace RotateDialog {
897
+ type Props = {
898
+ record: ApiKeys.ApiKey
899
+ }
900
+ }
901
+
815
902
  function RevokeDialog(props: { currentUser?: string | undefined; record: ApiKeys.ApiKey }) {
816
903
  const { currentUser, record } = props
817
904
  const [confirmation, setConfirmation] = useState('')
@@ -14,6 +14,7 @@ import {
14
14
  } from '@tanstack/react-router'
15
15
  import { Button, Rows, Select } from 'regen-ui'
16
16
  import { ArrowLeft, RefreshCw } from 'lucide-react'
17
+ import { useEffect, useState } from 'react'
17
18
 
18
19
  import { Card, Notice, Page } from '../components/Page.js'
19
20
  import * as core_Format from '../lib/format.js'
@@ -34,6 +35,7 @@ function PageComponent() {
34
35
  const period = period_search ?? '7d'
35
36
  const disableBillingSource = Organizations.disableBillingSource.useMutation(orgId)
36
37
  const enableBillingSource = Organizations.enableBillingSource.useMutation(orgId)
38
+ const setSponsorshipSubsidy = Organizations.setSponsorshipSubsidy.useMutation(orgId)
37
39
  const organizationQuery = useSuspenseQuery(Organizations.get.options(orgId))
38
40
  const organization = organizationQuery.data
39
41
  const members = useInfiniteQuery(Organizations.members.options(orgId))
@@ -198,6 +200,12 @@ function PageComponent() {
198
200
  onChange={setBillingSource}
199
201
  pending={billingSourcePending}
200
202
  />
203
+ <SponsorshipSubsidyCard
204
+ error={setSponsorshipSubsidy.error}
205
+ onSave={(policy) => setSponsorshipSubsidy.mutate(policy)}
206
+ pending={setSponsorshipSubsidy.isPending}
207
+ policy={organization.sponsorshipSubsidy}
208
+ />
201
209
  <div className="grid gap-3 md:grid-cols-2">
202
210
  <BillingCard
203
211
  environment="Production"
@@ -655,6 +663,114 @@ export declare namespace BillingSourcesCard {
655
663
  }
656
664
  }
657
665
 
666
+ /** Organization-wide per-project sponsorship subsidy controls. */
667
+ export function SponsorshipSubsidyCard(options: SponsorshipSubsidyCard.Props) {
668
+ const [enabled, setEnabled] = useState(options.policy.durationDays !== null)
669
+ const [projectSpendLimit, setProjectSpendLimit] = useState(options.policy.projectSpendLimit ?? '')
670
+
671
+ useEffect(() => {
672
+ setEnabled(options.policy.durationDays !== null)
673
+ setProjectSpendLimit(options.policy.projectSpendLimit ?? '')
674
+ }, [options.policy.durationDays, options.policy.projectSpendLimit])
675
+
676
+ return (
677
+ <Card>
678
+ <form
679
+ onSubmit={(event) => {
680
+ event.preventDefault()
681
+ if (!enabled) return options.onSave({ enabled: false })
682
+ if (!projectSpendLimit) return options.onSave({ enabled: true })
683
+ return options.onSave({ enabled: true, projectSpendLimit })
684
+ }}
685
+ >
686
+ <div className="flex items-start justify-between gap-4 p-4">
687
+ <div className="min-w-0">
688
+ <h3 className="text-label-14 text-foreground">Sponsorship subsidy</h3>
689
+ <p className="mt-1 text-copy-13 text-foreground-secondary">
690
+ Waive sponsored transaction charges for each project's first 90 days.
691
+ </p>
692
+ </div>
693
+ <Switch.Root
694
+ aria-label="Sponsorship subsidy"
695
+ checked={enabled}
696
+ className="flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border border-border bg-secondary p-0.5 outline-none transition-colors hover:bg-secondary-hover focus-visible:border-focus data-[checked]:bg-invert disabled:cursor-not-allowed disabled:opacity-50"
697
+ disabled={options.pending}
698
+ onCheckedChange={setEnabled}
699
+ >
700
+ <Switch.Thumb className="block size-4 translate-x-0 rounded-full bg-foreground-secondary transition-transform data-[checked]:translate-x-4 data-[checked]:bg-on-invert" />
701
+ </Switch.Root>
702
+ </div>
703
+ {enabled ? (
704
+ <div className="grid gap-4 border-t border-border p-4 md:grid-cols-2">
705
+ <label className="flex min-w-0 flex-col gap-2">
706
+ <span className="text-label-13 text-foreground-secondary">Duration per project</span>
707
+ <input
708
+ className="h-10 rounded-body border-2 border-transparent bg-secondary px-3 text-copy-14 text-foreground-secondary"
709
+ disabled
710
+ value="90 days"
711
+ />
712
+ <span className="text-label-12 text-foreground-secondary">
713
+ Starts with the project's first mainnet sponsorship.
714
+ </span>
715
+ </label>
716
+ <label className="flex min-w-0 flex-col gap-2">
717
+ <span className="text-label-13 text-foreground-secondary">
718
+ Per-project spend cap (optional)
719
+ </span>
720
+ <span className="flex h-10 items-center rounded-body border-2 border-transparent bg-secondary px-3 focus-within:border-focus">
721
+ <span className="text-copy-14 text-foreground-secondary">$</span>
722
+ <input
723
+ aria-label="Per-project spend cap"
724
+ className="min-w-0 flex-1 bg-transparent px-2 text-copy-14 text-foreground outline-none"
725
+ disabled={options.pending}
726
+ inputMode="decimal"
727
+ min="0"
728
+ onChange={(event) => setProjectSpendLimit(event.target.value)}
729
+ placeholder="No cap"
730
+ step="0.000001"
731
+ type="number"
732
+ value={projectSpendLimit}
733
+ />
734
+ </span>
735
+ <span className="text-label-12 text-foreground-secondary">
736
+ Leave blank for no spend cap.
737
+ </span>
738
+ </label>
739
+ </div>
740
+ ) : (
741
+ <p className="border-t border-border p-4 text-copy-13 text-foreground-secondary">
742
+ New sponsorships for this organization are billed normally.
743
+ </p>
744
+ )}
745
+ <div className="flex items-center justify-end gap-3 border-t border-border p-4">
746
+ {options.error ? (
747
+ <p className="mr-auto text-copy-13 text-negative" role="alert">
748
+ {options.error.message}
749
+ </p>
750
+ ) : null}
751
+ <Button loading={options.pending} type="submit">
752
+ Save policy
753
+ </Button>
754
+ </div>
755
+ </form>
756
+ </Card>
757
+ )
758
+ }
759
+
760
+ export declare namespace SponsorshipSubsidyCard {
761
+ /** Sponsorship subsidy control properties. */
762
+ type Props = {
763
+ /** Latest mutation error. */
764
+ error: Error | null
765
+ /** Saves a replacement policy. */
766
+ onSave: (policy: Organizations.setSponsorshipSubsidy.Input) => void
767
+ /** Whether a policy change is pending. */
768
+ pending: boolean
769
+ /** Current organization policy. */
770
+ policy: Organizations.Detail['sponsorshipSubsidy']
771
+ }
772
+ }
773
+
658
774
  function BillingCard(options: BillingCard.Props) {
659
775
  const { settings, source } = options.record
660
776
  const remaining =