tapimo 0.14.0 → 0.15.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 (1141) hide show
  1. package/README.md +7 -4
  2. package/dist/ApiKey.js +1 -1
  3. package/dist/ApiKey.js.map +1 -1
  4. package/dist/ApiKeys.d.ts +95 -14
  5. package/dist/ApiKeys.d.ts.map +1 -1
  6. package/dist/ApiKeys.js +360 -49
  7. package/dist/ApiKeys.js.map +1 -1
  8. package/dist/App.d.ts +12457 -10365
  9. package/dist/App.d.ts.map +1 -1
  10. package/dist/App.js +300 -22
  11. package/dist/App.js.map +1 -1
  12. package/dist/Assets.d.ts.map +1 -1
  13. package/dist/Assets.js +2 -1
  14. package/dist/Assets.js.map +1 -1
  15. package/dist/Handler.d.ts +0 -1
  16. package/dist/Handler.d.ts.map +1 -1
  17. package/dist/Handler.js +0 -1
  18. package/dist/Handler.js.map +1 -1
  19. package/dist/Scope.d.ts +13 -5
  20. package/dist/Scope.d.ts.map +1 -1
  21. package/dist/Scope.js +16 -5
  22. package/dist/Scope.js.map +1 -1
  23. package/dist/Webhooks.js +1 -1
  24. package/dist/Webhooks.js.map +1 -1
  25. package/dist/admin/App.d.ts +3057 -549
  26. package/dist/admin/App.d.ts.map +1 -1
  27. package/dist/admin/App.js +26 -5
  28. package/dist/admin/App.js.map +1 -1
  29. package/dist/admin/apps/api-keys.d.ts +159 -7
  30. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  31. package/dist/admin/apps/api-keys.js +95 -43
  32. package/dist/admin/apps/api-keys.js.map +1 -1
  33. package/dist/admin/apps/earn-vaults.d.ts +1398 -0
  34. package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
  35. package/dist/admin/apps/earn-vaults.js +463 -7
  36. package/dist/admin/apps/earn-vaults.js.map +1 -1
  37. package/dist/admin/apps/organizations.d.ts +267 -15
  38. package/dist/admin/apps/organizations.d.ts.map +1 -1
  39. package/dist/admin/apps/organizations.js +241 -25
  40. package/dist/admin/apps/organizations.js.map +1 -1
  41. package/dist/admin/apps/routes-support.d.ts +95 -0
  42. package/dist/admin/apps/routes-support.d.ts.map +1 -0
  43. package/dist/admin/apps/routes-support.js +151 -0
  44. package/dist/admin/apps/routes-support.js.map +1 -0
  45. package/dist/admin/apps/routes.d.ts +2764 -0
  46. package/dist/admin/apps/routes.d.ts.map +1 -0
  47. package/dist/admin/apps/routes.js +728 -0
  48. package/dist/admin/apps/routes.js.map +1 -0
  49. package/dist/admin/apps/subsidies.d.ts +501 -0
  50. package/dist/admin/apps/subsidies.d.ts.map +1 -0
  51. package/dist/admin/apps/subsidies.js +384 -0
  52. package/dist/admin/apps/subsidies.js.map +1 -0
  53. package/dist/admin/apps/verified-tokens.d.ts +88 -88
  54. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  55. package/dist/admin/apps/verified-tokens.js +3 -3
  56. package/dist/admin/apps/verified-tokens.js.map +1 -1
  57. package/dist/admin/ui.generated.d.ts +1 -1
  58. package/dist/admin/ui.generated.d.ts.map +1 -1
  59. package/dist/admin/ui.generated.js +129 -69
  60. package/dist/admin/ui.generated.js.map +1 -1
  61. package/dist/analytics/Analytics.d.ts +5 -2
  62. package/dist/analytics/Analytics.d.ts.map +1 -1
  63. package/dist/analytics/Analytics.js +31 -1
  64. package/dist/analytics/Analytics.js.map +1 -1
  65. package/dist/analytics/tables/requestEvents.d.ts +2 -1
  66. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  67. package/dist/analytics/tables/requestEvents.js +10 -1
  68. package/dist/analytics/tables/requestEvents.js.map +1 -1
  69. package/dist/analytics/tables/routesLifecycleIntervals.d.ts +72 -0
  70. package/dist/analytics/tables/routesLifecycleIntervals.d.ts.map +1 -0
  71. package/dist/analytics/tables/routesLifecycleIntervals.js +56 -0
  72. package/dist/analytics/tables/routesLifecycleIntervals.js.map +1 -0
  73. package/dist/apps/Zones.d.ts +21 -12526
  74. package/dist/apps/Zones.d.ts.map +1 -1
  75. package/dist/apps/Zones.js +37 -5
  76. package/dist/apps/Zones.js.map +1 -1
  77. package/dist/apps/data/App.d.ts +3971 -2595
  78. package/dist/apps/data/App.d.ts.map +1 -1
  79. package/dist/apps/data/App.js +76 -21
  80. package/dist/apps/data/App.js.map +1 -1
  81. package/dist/apps/data/routes/activities.d.ts +364 -34
  82. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  83. package/dist/apps/data/routes/activities.js +495 -190
  84. package/dist/apps/data/routes/activities.js.map +1 -1
  85. package/dist/apps/data/routes/addresses.d.ts +72 -0
  86. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  87. package/dist/apps/data/routes/blocks.d.ts +37 -37
  88. package/dist/apps/data/routes/coingecko.d.ts.map +1 -1
  89. package/dist/apps/data/routes/coingecko.js +47 -21
  90. package/dist/apps/data/routes/coingecko.js.map +1 -1
  91. package/dist/apps/data/routes/earn.d.ts +1450 -337
  92. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  93. package/dist/apps/data/routes/earn.js +1226 -144
  94. package/dist/apps/data/routes/earn.js.map +1 -1
  95. package/dist/apps/data/routes/fee-amm.d.ts +5 -0
  96. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  97. package/dist/apps/data/routes/fee-amm.js +31 -19
  98. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  99. package/dist/apps/data/routes/indexer.d.ts +125 -0
  100. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  101. package/dist/apps/data/routes/indexer.js +83 -27
  102. package/dist/apps/data/routes/indexer.js.map +1 -1
  103. package/dist/apps/data/routes/rpc.d.ts +507 -33
  104. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  105. package/dist/apps/data/routes/rpc.js +266 -156
  106. package/dist/apps/data/routes/rpc.js.map +1 -1
  107. package/dist/apps/data/routes/tokenlist.d.ts +47 -15
  108. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
  109. package/dist/apps/data/routes/tokenlist.js +68 -11
  110. package/dist/apps/data/routes/tokenlist.js.map +1 -1
  111. package/dist/apps/data/routes/tokens.d.ts +2 -0
  112. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  113. package/dist/apps/data/routes/tokens.js +26 -14
  114. package/dist/apps/data/routes/tokens.js.map +1 -1
  115. package/dist/apps/data/routes/transfers.d.ts +28 -12
  116. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  117. package/dist/apps/data/routes/transfers.js +9 -7
  118. package/dist/apps/data/routes/transfers.js.map +1 -1
  119. package/dist/apps/data/routes/valuation.d.ts +30 -30
  120. package/dist/apps/data/routes/verified-tokens.d.ts +9 -9
  121. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
  122. package/dist/apps/data/routes/verified-tokens.js +64 -20
  123. package/dist/apps/data/routes/verified-tokens.js.map +1 -1
  124. package/dist/apps/data/routes/webhooks.d.ts +613 -176
  125. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  126. package/dist/apps/data/routes/webhooks.js +291 -195
  127. package/dist/apps/data/routes/webhooks.js.map +1 -1
  128. package/dist/apps/data/routes/zones.d.ts.map +1 -1
  129. package/dist/apps/data/routes/zones.js +20 -12
  130. package/dist/apps/data/routes/zones.js.map +1 -1
  131. package/dist/apps/docs/reference.js +2 -2
  132. package/dist/apps/docs/reference.js.map +1 -1
  133. package/dist/apps/index.d.ts +1 -1
  134. package/dist/apps/index.d.ts.map +1 -1
  135. package/dist/apps/index.js +1 -1
  136. package/dist/apps/index.js.map +1 -1
  137. package/dist/apps/management/App.d.ts +3763 -3385
  138. package/dist/apps/management/App.d.ts.map +1 -1
  139. package/dist/apps/management/App.js +5 -14
  140. package/dist/apps/management/App.js.map +1 -1
  141. package/dist/apps/management/Billing.d.ts +48 -7
  142. package/dist/apps/management/Billing.d.ts.map +1 -1
  143. package/dist/apps/management/Billing.js +204 -25
  144. package/dist/apps/management/Billing.js.map +1 -1
  145. package/dist/apps/management/routes/api-keys.d.ts +106 -4
  146. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  147. package/dist/apps/management/routes/api-keys.js +43 -15
  148. package/dist/apps/management/routes/api-keys.js.map +1 -1
  149. package/dist/apps/management/routes/billing.d.ts +290 -4
  150. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  151. package/dist/apps/management/routes/billing.js +274 -49
  152. package/dist/apps/management/routes/billing.js.map +1 -1
  153. package/dist/apps/management/routes/faucet.d.ts +6 -6
  154. package/dist/apps/management/routes/faucet.js +1 -1
  155. package/dist/apps/management/routes/faucet.js.map +1 -1
  156. package/dist/apps/management/routes/invitations.d.ts +77 -2
  157. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  158. package/dist/apps/management/routes/invitations.js +117 -13
  159. package/dist/apps/management/routes/invitations.js.map +1 -1
  160. package/dist/apps/management/routes/invite-links.d.ts +4 -4
  161. package/dist/apps/management/routes/me.d.ts.map +1 -1
  162. package/dist/apps/management/routes/me.js +6 -2
  163. package/dist/apps/management/routes/me.js.map +1 -1
  164. package/dist/apps/management/routes/members.d.ts +30 -30
  165. package/dist/apps/management/routes/orgs.d.ts +18 -2
  166. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  167. package/dist/apps/management/routes/orgs.js +52 -12
  168. package/dist/apps/management/routes/orgs.js.map +1 -1
  169. package/dist/apps/management/routes/projects.d.ts +4 -4
  170. package/dist/apps/management/routes/projects.d.ts.map +1 -1
  171. package/dist/apps/management/routes/projects.js +6 -1
  172. package/dist/apps/management/routes/projects.js.map +1 -1
  173. package/dist/apps/management/routes/scopes.d.ts.map +1 -1
  174. package/dist/apps/management/routes/scopes.js +4 -1
  175. package/dist/apps/management/routes/scopes.js.map +1 -1
  176. package/dist/apps/management/routes/usage.d.ts +24 -24
  177. package/dist/apps/management/routes/usage.js +21 -4
  178. package/dist/apps/management/routes/usage.js.map +1 -1
  179. package/dist/apps/management/routes/verified-token-requests.d.ts +2 -2
  180. package/dist/apps/management/routes/webhooks.d.ts +280 -214
  181. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  182. package/dist/apps/management/routes/webhooks.js +58 -12
  183. package/dist/apps/management/routes/webhooks.js.map +1 -1
  184. package/dist/apps/mcp.d.ts +1 -1
  185. package/dist/apps/mcp.js +7 -7
  186. package/dist/apps/mcp.js.map +1 -1
  187. package/dist/apps/mpp/App.d.ts.map +1 -1
  188. package/dist/apps/mpp/App.js +35 -7
  189. package/dist/apps/mpp/App.js.map +1 -1
  190. package/dist/apps/mpp/internal/Adapter.d.ts +2 -0
  191. package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
  192. package/dist/apps/mpp/internal/Adapter.js +30 -1
  193. package/dist/apps/mpp/internal/Adapter.js.map +1 -1
  194. package/dist/apps/mpp/internal/Policy.d.ts +22 -4
  195. package/dist/apps/mpp/internal/Policy.d.ts.map +1 -1
  196. package/dist/apps/mpp/internal/Policy.js +24 -6
  197. package/dist/apps/mpp/internal/Policy.js.map +1 -1
  198. package/dist/apps/mpp/internal/Sponsorship.d.ts +3 -0
  199. package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -1
  200. package/dist/apps/mpp/internal/Sponsorship.js +6 -3
  201. package/dist/apps/mpp/internal/Sponsorship.js.map +1 -1
  202. package/dist/apps/relay/App.d.ts +2 -0
  203. package/dist/apps/relay/App.d.ts.map +1 -1
  204. package/dist/apps/relay/App.js +134 -75
  205. package/dist/apps/relay/App.js.map +1 -1
  206. package/dist/apps/relay/Sponsorships.d.ts +5 -3
  207. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  208. package/dist/apps/relay/Sponsorships.js +209 -28
  209. package/dist/apps/relay/Sponsorships.js.map +1 -1
  210. package/dist/apps/{funding → routes}/Access.d.ts +4 -4
  211. package/dist/apps/routes/Access.d.ts.map +1 -0
  212. package/dist/apps/{funding → routes}/Access.js +4 -4
  213. package/dist/apps/routes/Access.js.map +1 -0
  214. package/dist/apps/{funding → routes}/App.d.ts +418 -124
  215. package/dist/apps/{funding → routes}/App.d.ts.map +1 -1
  216. package/dist/apps/{funding → routes}/App.js +16 -16
  217. package/dist/apps/routes/App.js.map +1 -0
  218. package/dist/apps/routes/Inventory.d.ts.map +1 -0
  219. package/dist/apps/{funding → routes}/Inventory.js +2 -2
  220. package/dist/apps/routes/Inventory.js.map +1 -0
  221. package/dist/apps/routes/index.d.ts +2 -0
  222. package/dist/apps/routes/index.d.ts.map +1 -0
  223. package/dist/apps/routes/index.js +2 -0
  224. package/dist/apps/routes/index.js.map +1 -0
  225. package/dist/apps/{funding → routes}/routes/chains.d.ts +7 -7
  226. package/dist/apps/routes/routes/chains.d.ts.map +1 -0
  227. package/dist/apps/{funding → routes}/routes/chains.js +25 -25
  228. package/dist/apps/routes/routes/chains.js.map +1 -0
  229. package/dist/apps/{funding → routes}/routes/deposit-addresses.d.ts +107 -43
  230. package/dist/apps/routes/routes/deposit-addresses.d.ts.map +1 -0
  231. package/dist/apps/{funding → routes}/routes/deposit-addresses.js +497 -222
  232. package/dist/apps/routes/routes/deposit-addresses.js.map +1 -0
  233. package/dist/apps/{funding → routes}/routes/providers.d.ts +8 -8
  234. package/dist/apps/routes/routes/providers.d.ts.map +1 -0
  235. package/dist/apps/routes/routes/providers.js +57 -0
  236. package/dist/apps/routes/routes/providers.js.map +1 -0
  237. package/dist/apps/{funding → routes}/routes/quotes.d.ts +19 -16
  238. package/dist/apps/routes/routes/quotes.d.ts.map +1 -0
  239. package/dist/apps/{funding → routes}/routes/quotes.js +55 -38
  240. package/dist/apps/routes/routes/quotes.js.map +1 -0
  241. package/dist/apps/{funding → routes}/routes/transfers.d.ts +379 -74
  242. package/dist/apps/routes/routes/transfers.d.ts.map +1 -0
  243. package/dist/apps/routes/routes/transfers.js +1140 -0
  244. package/dist/apps/routes/routes/transfers.js.map +1 -0
  245. package/dist/cli/Cli.d.ts +30 -0
  246. package/dist/cli/Cli.d.ts.map +1 -1
  247. package/dist/cli/commands/admin.d.ts +36 -0
  248. package/dist/cli/commands/admin.d.ts.map +1 -1
  249. package/dist/cli/commands/admin.js +273 -0
  250. package/dist/cli/commands/admin.js.map +1 -1
  251. package/dist/cloudflare.d.ts +16 -14
  252. package/dist/cloudflare.d.ts.map +1 -1
  253. package/dist/cloudflare.js +26 -21
  254. package/dist/cloudflare.js.map +1 -1
  255. package/dist/db/Db.d.ts +63 -21
  256. package/dist/db/Db.d.ts.map +1 -1
  257. package/dist/db/Db.js +1030 -15
  258. package/dist/db/Db.js.map +1 -1
  259. package/dist/db/Schema.d.ts +780 -133
  260. package/dist/db/Schema.d.ts.map +1 -1
  261. package/dist/db/Schema.js +575 -162
  262. package/dist/db/Schema.js.map +1 -1
  263. package/dist/db/tables/apiKeyAdmissionBootstraps.d.ts +4 -0
  264. package/dist/db/tables/apiKeyAdmissionBootstraps.d.ts.map +1 -0
  265. package/dist/db/tables/apiKeyAdmissionBootstraps.js +2 -0
  266. package/dist/db/tables/apiKeyAdmissionBootstraps.js.map +1 -0
  267. package/dist/db/tables/apiKeyAdmissions.d.ts +73 -0
  268. package/dist/db/tables/apiKeyAdmissions.d.ts.map +1 -0
  269. package/dist/db/tables/apiKeyAdmissions.js +142 -0
  270. package/dist/db/tables/apiKeyAdmissions.js.map +1 -0
  271. package/dist/db/tables/apiKeyOwnerAdmissions.d.ts +4 -0
  272. package/dist/db/tables/apiKeyOwnerAdmissions.d.ts.map +1 -0
  273. package/dist/db/tables/apiKeyOwnerAdmissions.js +2 -0
  274. package/dist/db/tables/apiKeyOwnerAdmissions.js.map +1 -0
  275. package/dist/db/tables/apiKeyOwnerTombstones.d.ts +20 -0
  276. package/dist/db/tables/apiKeyOwnerTombstones.d.ts.map +1 -0
  277. package/dist/db/tables/apiKeyOwnerTombstones.js +57 -0
  278. package/dist/db/tables/apiKeyOwnerTombstones.js.map +1 -0
  279. package/dist/db/tables/apiKeyRevocations.d.ts +21 -0
  280. package/dist/db/tables/apiKeyRevocations.d.ts.map +1 -0
  281. package/dist/db/tables/apiKeyRevocations.js +98 -0
  282. package/dist/db/tables/apiKeyRevocations.js.map +1 -0
  283. package/dist/db/tables/authAccounts.d.ts +47 -0
  284. package/dist/db/tables/authAccounts.d.ts.map +1 -0
  285. package/dist/db/tables/authAccounts.js +72 -0
  286. package/dist/db/tables/authAccounts.js.map +1 -0
  287. package/dist/db/tables/authSessions.d.ts +12 -0
  288. package/dist/db/tables/authSessions.d.ts.map +1 -0
  289. package/dist/db/tables/authSessions.js +2 -0
  290. package/dist/db/tables/authSessions.js.map +1 -0
  291. package/dist/db/tables/authVerifications.d.ts +12 -0
  292. package/dist/db/tables/authVerifications.d.ts.map +1 -0
  293. package/dist/db/tables/authVerifications.js +2 -0
  294. package/dist/db/tables/authVerifications.js.map +1 -0
  295. package/dist/db/tables/invitations.d.ts +17 -1
  296. package/dist/db/tables/invitations.d.ts.map +1 -1
  297. package/dist/db/tables/invitations.js +123 -43
  298. package/dist/db/tables/invitations.js.map +1 -1
  299. package/dist/db/tables/memberships.d.ts +8 -0
  300. package/dist/db/tables/memberships.d.ts.map +1 -1
  301. package/dist/db/tables/memberships.js +51 -2
  302. package/dist/db/tables/memberships.js.map +1 -1
  303. package/dist/db/tables/organizations.d.ts +32 -10
  304. package/dist/db/tables/organizations.d.ts.map +1 -1
  305. package/dist/db/tables/organizations.js +110 -49
  306. package/dist/db/tables/organizations.js.map +1 -1
  307. package/dist/db/tables/projects.d.ts +2 -0
  308. package/dist/db/tables/projects.d.ts.map +1 -1
  309. package/dist/db/tables/projects.js +12 -3
  310. package/dist/db/tables/projects.js.map +1 -1
  311. package/dist/db/tables/requestUsage.d.ts.map +1 -1
  312. package/dist/db/tables/requestUsage.js.map +1 -1
  313. package/dist/db/tables/rewardAccounts.d.ts +86 -0
  314. package/dist/db/tables/rewardAccounts.d.ts.map +1 -0
  315. package/dist/db/tables/rewardAccounts.js +88 -0
  316. package/dist/db/tables/rewardAccounts.js.map +1 -0
  317. package/dist/db/tables/rewardCampaignDistributors.d.ts +36 -0
  318. package/dist/db/tables/rewardCampaignDistributors.d.ts.map +1 -0
  319. package/dist/db/tables/rewardCampaignDistributors.js +30 -0
  320. package/dist/db/tables/rewardCampaignDistributors.js.map +1 -0
  321. package/dist/db/tables/rewardCampaigns.d.ts +171 -0
  322. package/dist/db/tables/rewardCampaigns.d.ts.map +1 -0
  323. package/dist/db/tables/rewardCampaigns.js +392 -0
  324. package/dist/db/tables/rewardCampaigns.js.map +1 -0
  325. package/dist/db/tables/rewardCredits.d.ts +53 -0
  326. package/dist/db/tables/rewardCredits.d.ts.map +1 -0
  327. package/dist/db/tables/rewardCredits.js +63 -0
  328. package/dist/db/tables/rewardCredits.js.map +1 -0
  329. package/dist/db/tables/rewardEligibilityAssociations.d.ts +61 -0
  330. package/dist/db/tables/rewardEligibilityAssociations.d.ts.map +1 -0
  331. package/dist/db/tables/rewardEligibilityAssociations.js +103 -0
  332. package/dist/db/tables/rewardEligibilityAssociations.js.map +1 -0
  333. package/dist/db/tables/rewardRuns.d.ts +188 -0
  334. package/dist/db/tables/rewardRuns.d.ts.map +1 -0
  335. package/dist/db/tables/rewardRuns.js +278 -0
  336. package/dist/db/tables/rewardRuns.js.map +1 -0
  337. package/dist/db/tables/rewardTransactionAttempts.d.ts +100 -0
  338. package/dist/db/tables/rewardTransactionAttempts.d.ts.map +1 -0
  339. package/dist/db/tables/rewardTransactionAttempts.js +163 -0
  340. package/dist/db/tables/rewardTransactionAttempts.js.map +1 -0
  341. package/dist/db/tables/routesCatalog.d.ts +58 -0
  342. package/dist/db/tables/routesCatalog.d.ts.map +1 -0
  343. package/dist/db/tables/{fundingCatalog.js → routesCatalog.js} +19 -19
  344. package/dist/db/tables/routesCatalog.js.map +1 -0
  345. package/dist/db/tables/routesDepositAddresses.d.ts +476 -0
  346. package/dist/db/tables/routesDepositAddresses.d.ts.map +1 -0
  347. package/dist/db/tables/routesDepositAddresses.js +587 -0
  348. package/dist/db/tables/routesDepositAddresses.js.map +1 -0
  349. package/dist/db/tables/{fundingDepositRequestObservations.d.ts → routesDepositRequestObservations.d.ts} +6 -6
  350. package/dist/db/tables/routesDepositRequestObservations.d.ts.map +1 -0
  351. package/dist/db/tables/{fundingDepositRequestObservations.js → routesDepositRequestObservations.js} +6 -6
  352. package/dist/db/tables/routesDepositRequestObservations.js.map +1 -0
  353. package/dist/db/tables/{fundingDeposits.d.ts → routesDeposits.d.ts} +210 -22
  354. package/dist/db/tables/routesDeposits.d.ts.map +1 -0
  355. package/dist/db/tables/routesDeposits.js +576 -0
  356. package/dist/db/tables/routesDeposits.js.map +1 -0
  357. package/dist/db/tables/routesIdempotency.d.ts +166 -0
  358. package/dist/db/tables/routesIdempotency.d.ts.map +1 -0
  359. package/dist/db/tables/routesIdempotency.js +330 -0
  360. package/dist/db/tables/routesIdempotency.js.map +1 -0
  361. package/dist/db/tables/routesSubsidies.d.ts +27 -0
  362. package/dist/db/tables/routesSubsidies.d.ts.map +1 -0
  363. package/dist/db/tables/routesSubsidies.js +45 -0
  364. package/dist/db/tables/routesSubsidies.js.map +1 -0
  365. package/dist/db/tables/{fundingTransferEvents.d.ts → routesTransferEvents.d.ts} +3 -3
  366. package/dist/db/tables/routesTransferEvents.d.ts.map +1 -0
  367. package/dist/db/tables/{fundingTransferEvents.js → routesTransferEvents.js} +3 -3
  368. package/dist/db/tables/routesTransferEvents.js.map +1 -0
  369. package/dist/db/tables/routesTransferSubsidies.d.ts +19 -0
  370. package/dist/db/tables/routesTransferSubsidies.d.ts.map +1 -0
  371. package/dist/db/tables/routesTransferSubsidies.js +17 -0
  372. package/dist/db/tables/routesTransferSubsidies.js.map +1 -0
  373. package/dist/db/tables/routesTransferSubsidyNonces.d.ts +51 -0
  374. package/dist/db/tables/routesTransferSubsidyNonces.d.ts.map +1 -0
  375. package/dist/db/tables/routesTransferSubsidyNonces.js +82 -0
  376. package/dist/db/tables/routesTransferSubsidyNonces.js.map +1 -0
  377. package/dist/db/tables/routesTransferTransactions.d.ts +77 -0
  378. package/dist/db/tables/routesTransferTransactions.d.ts.map +1 -0
  379. package/dist/db/tables/{fundingTransferTransactions.js → routesTransferTransactions.js} +31 -13
  380. package/dist/db/tables/routesTransferTransactions.js.map +1 -0
  381. package/dist/db/tables/routesTransfers.d.ts +319 -0
  382. package/dist/db/tables/routesTransfers.d.ts.map +1 -0
  383. package/dist/db/tables/routesTransfers.js +372 -0
  384. package/dist/db/tables/routesTransfers.js.map +1 -0
  385. package/dist/db/tables/sponsoredTransactions.d.ts +75 -20
  386. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  387. package/dist/db/tables/sponsoredTransactions.js +124 -54
  388. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  389. package/dist/db/tables/sponsorshipAttributions.d.ts +69 -0
  390. package/dist/db/tables/sponsorshipAttributions.d.ts.map +1 -0
  391. package/dist/db/tables/sponsorshipAttributions.js +77 -0
  392. package/dist/db/tables/sponsorshipAttributions.js.map +1 -0
  393. package/dist/db/tables/sponsorshipReconciliationCursors.d.ts +22 -0
  394. package/dist/db/tables/sponsorshipReconciliationCursors.d.ts.map +1 -0
  395. package/dist/db/tables/sponsorshipReconciliationCursors.js +45 -0
  396. package/dist/db/tables/sponsorshipReconciliationCursors.js.map +1 -0
  397. package/dist/db/tables/stripeCustomers.d.ts +11 -0
  398. package/dist/db/tables/stripeCustomers.d.ts.map +1 -1
  399. package/dist/db/tables/stripeCustomers.js +13 -0
  400. package/dist/db/tables/stripeCustomers.js.map +1 -1
  401. package/dist/db/tables/users.d.ts +17 -1
  402. package/dist/db/tables/users.d.ts.map +1 -1
  403. package/dist/db/tables/users.js +47 -2
  404. package/dist/db/tables/users.js.map +1 -1
  405. package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
  406. package/dist/db/tables/webhookQueueEvents.js +5 -1
  407. package/dist/db/tables/webhookQueueEvents.js.map +1 -1
  408. package/dist/db/tables/webhookSubscriptions.d.ts +19 -11
  409. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  410. package/dist/db/tables/webhookSubscriptions.js +45 -13
  411. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  412. package/dist/handlers/internal/executionError.d.ts +2 -2
  413. package/dist/handlers/internal/executionError.d.ts.map +1 -1
  414. package/dist/handlers/internal/executionError.js +1 -2
  415. package/dist/handlers/internal/executionError.js.map +1 -1
  416. package/dist/handlers/internal/multisig.d.ts +4 -232
  417. package/dist/handlers/internal/multisig.d.ts.map +1 -1
  418. package/dist/handlers/internal/multisig.js +11 -537
  419. package/dist/handlers/internal/multisig.js.map +1 -1
  420. package/dist/handlers/internal/sponsorship.d.ts +19 -8
  421. package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
  422. package/dist/handlers/internal/sponsorship.js +13 -9
  423. package/dist/handlers/internal/sponsorship.js.map +1 -1
  424. package/dist/handlers/relay.d.ts +9 -12
  425. package/dist/handlers/relay.d.ts.map +1 -1
  426. package/dist/handlers/relay.js +81 -85
  427. package/dist/handlers/relay.js.map +1 -1
  428. package/dist/index.d.ts +0 -17
  429. package/dist/index.d.ts.map +1 -1
  430. package/dist/index.js +0 -17
  431. package/dist/index.js.map +1 -1
  432. package/dist/internal/AssetResponse.d.ts +12 -0
  433. package/dist/internal/AssetResponse.d.ts.map +1 -0
  434. package/dist/internal/AssetResponse.js +21 -0
  435. package/dist/internal/AssetResponse.js.map +1 -0
  436. package/dist/internal/Auth.d.ts +48 -12
  437. package/dist/internal/Auth.d.ts.map +1 -1
  438. package/dist/internal/Auth.js +603 -175
  439. package/dist/internal/Auth.js.map +1 -1
  440. package/dist/internal/BetterAuth.d.ts +1488 -0
  441. package/dist/internal/BetterAuth.d.ts.map +1 -0
  442. package/dist/internal/BetterAuth.js +253 -0
  443. package/dist/internal/BetterAuth.js.map +1 -0
  444. package/dist/internal/Cache.d.ts +2 -0
  445. package/dist/internal/Cache.d.ts.map +1 -1
  446. package/dist/internal/Cache.js +2 -0
  447. package/dist/internal/Cache.js.map +1 -1
  448. package/dist/internal/EarnRates.d.ts +26 -3
  449. package/dist/internal/EarnRates.d.ts.map +1 -1
  450. package/dist/internal/EarnRates.js +84 -19
  451. package/dist/internal/EarnRates.js.map +1 -1
  452. package/dist/internal/EarnSharePrices.d.ts +120 -0
  453. package/dist/internal/EarnSharePrices.d.ts.map +1 -0
  454. package/dist/internal/EarnSharePrices.js +179 -0
  455. package/dist/internal/EarnSharePrices.js.map +1 -0
  456. package/dist/internal/EarnVaults.d.ts +4 -0
  457. package/dist/internal/EarnVaults.d.ts.map +1 -1
  458. package/dist/internal/EarnVaults.js +27 -10
  459. package/dist/internal/EarnVaults.js.map +1 -1
  460. package/dist/internal/Email.d.ts +37 -0
  461. package/dist/internal/Email.d.ts.map +1 -0
  462. package/dist/internal/Email.js +16 -0
  463. package/dist/internal/Email.js.map +1 -0
  464. package/dist/internal/Log.d.ts +32 -28
  465. package/dist/internal/Log.d.ts.map +1 -1
  466. package/dist/internal/Log.js +18 -11
  467. package/dist/internal/Log.js.map +1 -1
  468. package/dist/internal/MetricSink.d.ts +3 -0
  469. package/dist/internal/MetricSink.d.ts.map +1 -1
  470. package/dist/internal/MetricSink.js +67 -8
  471. package/dist/internal/MetricSink.js.map +1 -1
  472. package/dist/internal/Mpp.d.ts.map +1 -1
  473. package/dist/internal/Mpp.js +20 -25
  474. package/dist/internal/Mpp.js.map +1 -1
  475. package/dist/internal/OpenApi.d.ts +26 -3
  476. package/dist/internal/OpenApi.d.ts.map +1 -1
  477. package/dist/internal/OpenApi.js +25 -10
  478. package/dist/internal/OpenApi.js.map +1 -1
  479. package/dist/internal/Provider.d.ts +1 -1
  480. package/dist/internal/Provider.d.ts.map +1 -1
  481. package/dist/internal/RateLimit.d.ts.map +1 -1
  482. package/dist/internal/RateLimit.js +13 -1
  483. package/dist/internal/RateLimit.js.map +1 -1
  484. package/dist/internal/Store.d.ts +16 -0
  485. package/dist/internal/Store.d.ts.map +1 -1
  486. package/dist/internal/Store.js +59 -8
  487. package/dist/internal/Store.js.map +1 -1
  488. package/dist/internal/Tidx.d.ts +9 -2
  489. package/dist/internal/Tidx.d.ts.map +1 -1
  490. package/dist/internal/Tidx.js +78 -11
  491. package/dist/internal/Tidx.js.map +1 -1
  492. package/dist/internal/Timing.d.ts +26 -0
  493. package/dist/internal/Timing.d.ts.map +1 -1
  494. package/dist/internal/Timing.js +63 -5
  495. package/dist/internal/Timing.js.map +1 -1
  496. package/dist/internal/VercelOidc.d.ts +94 -0
  497. package/dist/internal/VercelOidc.d.ts.map +1 -0
  498. package/dist/internal/VercelOidc.js +128 -0
  499. package/dist/internal/VercelOidc.js.map +1 -0
  500. package/dist/internal/VerifiedTokens.d.ts +2 -8
  501. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  502. package/dist/internal/VerifiedTokens.js +24 -24
  503. package/dist/internal/VerifiedTokens.js.map +1 -1
  504. package/dist/internal/Viem.d.ts +30 -2067
  505. package/dist/internal/Viem.d.ts.map +1 -1
  506. package/dist/internal/Viem.js +167 -17
  507. package/dist/internal/Viem.js.map +1 -1
  508. package/dist/internal/Webhooks.d.ts +12 -4
  509. package/dist/internal/Webhooks.d.ts.map +1 -1
  510. package/dist/internal/Webhooks.js +9 -3
  511. package/dist/internal/Webhooks.js.map +1 -1
  512. package/dist/internal/ZoneRpc.d.ts +7 -0
  513. package/dist/internal/ZoneRpc.d.ts.map +1 -0
  514. package/dist/internal/ZoneRpc.js +49 -0
  515. package/dist/internal/ZoneRpc.js.map +1 -0
  516. package/dist/internal/index.d.ts +3 -1
  517. package/dist/internal/index.d.ts.map +1 -1
  518. package/dist/internal/index.js +3 -1
  519. package/dist/internal/index.js.map +1 -1
  520. package/dist/internal/rewards/Audit.d.ts +88 -0
  521. package/dist/internal/rewards/Audit.d.ts.map +1 -0
  522. package/dist/internal/rewards/Audit.js +134 -0
  523. package/dist/internal/rewards/Audit.js.map +1 -0
  524. package/dist/internal/rewards/Campaigns.d.ts +572 -0
  525. package/dist/internal/rewards/Campaigns.d.ts.map +1 -0
  526. package/dist/internal/rewards/Campaigns.js +531 -0
  527. package/dist/internal/rewards/Campaigns.js.map +1 -0
  528. package/dist/internal/rewards/Credits.d.ts +76 -0
  529. package/dist/internal/rewards/Credits.d.ts.map +1 -0
  530. package/dist/internal/rewards/Credits.js +95 -0
  531. package/dist/internal/rewards/Credits.js.map +1 -0
  532. package/dist/internal/rewards/Events.d.ts +100 -0
  533. package/dist/internal/rewards/Events.d.ts.map +1 -0
  534. package/dist/internal/rewards/Events.js +340 -0
  535. package/dist/internal/rewards/Events.js.map +1 -0
  536. package/dist/internal/rewards/Projection.d.ts +237 -0
  537. package/dist/internal/rewards/Projection.d.ts.map +1 -0
  538. package/dist/internal/rewards/Projection.js +616 -0
  539. package/dist/internal/rewards/Projection.js.map +1 -0
  540. package/dist/internal/rewards/Rates.d.ts +44 -0
  541. package/dist/internal/rewards/Rates.d.ts.map +1 -0
  542. package/dist/internal/rewards/Rates.js +60 -0
  543. package/dist/internal/rewards/Rates.js.map +1 -0
  544. package/dist/internal/rewards/Read.d.ts +158 -0
  545. package/dist/internal/rewards/Read.d.ts.map +1 -0
  546. package/dist/internal/rewards/Read.js +292 -0
  547. package/dist/internal/rewards/Read.js.map +1 -0
  548. package/dist/internal/rewards/Runner.d.ts +164 -0
  549. package/dist/internal/rewards/Runner.d.ts.map +1 -0
  550. package/dist/internal/rewards/Runner.js +1778 -0
  551. package/dist/internal/rewards/Runner.js.map +1 -0
  552. package/dist/internal/rewards/Signer.d.ts +224 -0
  553. package/dist/internal/rewards/Signer.d.ts.map +1 -0
  554. package/dist/internal/rewards/Signer.js +135 -0
  555. package/dist/internal/rewards/Signer.js.map +1 -0
  556. package/dist/internal/rewards/index.d.ts +15 -0
  557. package/dist/internal/rewards/index.d.ts.map +1 -0
  558. package/dist/internal/rewards/index.js +15 -0
  559. package/dist/internal/rewards/index.js.map +1 -0
  560. package/dist/internal/{funding → routes}/Action.d.ts +12 -1
  561. package/dist/internal/routes/Action.d.ts.map +1 -0
  562. package/dist/internal/{funding → routes}/Action.js +17 -9
  563. package/dist/internal/routes/Action.js.map +1 -0
  564. package/dist/internal/{funding → routes}/Catalog.d.ts +20 -20
  565. package/dist/internal/routes/Catalog.d.ts.map +1 -0
  566. package/dist/internal/{funding → routes}/Catalog.js +29 -29
  567. package/dist/internal/routes/Catalog.js.map +1 -0
  568. package/dist/internal/{funding → routes}/Chain.d.ts +10 -10
  569. package/dist/internal/routes/Chain.d.ts.map +1 -0
  570. package/dist/internal/{funding → routes}/Chain.js +3 -3
  571. package/dist/internal/routes/Chain.js.map +1 -0
  572. package/dist/internal/{funding → routes}/Deposit.d.ts +45 -23
  573. package/dist/internal/routes/Deposit.d.ts.map +1 -0
  574. package/dist/internal/{funding → routes}/Deposit.js +60 -27
  575. package/dist/internal/routes/Deposit.js.map +1 -0
  576. package/dist/internal/{funding → routes}/DepositAddress.d.ts +56 -27
  577. package/dist/internal/routes/DepositAddress.d.ts.map +1 -0
  578. package/dist/internal/{funding → routes}/DepositAddress.js +95 -33
  579. package/dist/internal/routes/DepositAddress.js.map +1 -0
  580. package/dist/internal/{funding → routes}/DepositWebhook.d.ts +4 -4
  581. package/dist/internal/routes/DepositWebhook.d.ts.map +1 -0
  582. package/dist/internal/{funding → routes}/DepositWebhook.js +4 -4
  583. package/dist/internal/routes/DepositWebhook.js.map +1 -0
  584. package/dist/internal/routes/Economics.d.ts +25 -0
  585. package/dist/internal/routes/Economics.d.ts.map +1 -0
  586. package/dist/internal/routes/Economics.js +28 -0
  587. package/dist/internal/routes/Economics.js.map +1 -0
  588. package/dist/internal/{funding → routes}/Evidence.d.ts +3 -3
  589. package/dist/internal/routes/Evidence.d.ts.map +1 -0
  590. package/dist/internal/{funding → routes}/Evidence.js +7 -7
  591. package/dist/internal/routes/Evidence.js.map +1 -0
  592. package/dist/internal/routes/Observability.d.ts +154 -0
  593. package/dist/internal/routes/Observability.d.ts.map +1 -0
  594. package/dist/internal/routes/Observability.js +389 -0
  595. package/dist/internal/routes/Observability.js.map +1 -0
  596. package/dist/internal/{funding → routes}/Provider.d.ts +311 -105
  597. package/dist/internal/routes/Provider.d.ts.map +1 -0
  598. package/dist/internal/{funding → routes}/Provider.js +229 -98
  599. package/dist/internal/routes/Provider.js.map +1 -0
  600. package/dist/internal/{funding → routes}/Reconciliation.d.ts +11 -6
  601. package/dist/internal/routes/Reconciliation.d.ts.map +1 -0
  602. package/dist/internal/{funding → routes}/Reconciliation.js +408 -158
  603. package/dist/internal/routes/Reconciliation.js.map +1 -0
  604. package/dist/internal/routes/Route.d.ts +21 -0
  605. package/dist/internal/routes/Route.d.ts.map +1 -0
  606. package/dist/internal/{funding → routes}/Route.js +1 -1
  607. package/dist/internal/routes/Route.js.map +1 -0
  608. package/dist/internal/{funding → routes}/SourceObservation.d.ts +6 -6
  609. package/dist/internal/routes/SourceObservation.d.ts.map +1 -0
  610. package/dist/internal/{funding → routes}/SourceObservation.js +8 -8
  611. package/dist/internal/routes/SourceObservation.js.map +1 -0
  612. package/dist/internal/{funding → routes}/Subsidy.d.ts +48 -3
  613. package/dist/internal/routes/Subsidy.d.ts.map +1 -0
  614. package/dist/internal/{funding → routes}/Subsidy.js +43 -3
  615. package/dist/internal/routes/Subsidy.js.map +1 -0
  616. package/dist/internal/{funding → routes}/Token.d.ts +4 -4
  617. package/dist/internal/routes/Token.d.ts.map +1 -0
  618. package/dist/internal/{funding → routes}/Token.js +3 -3
  619. package/dist/internal/routes/Token.js.map +1 -0
  620. package/dist/internal/{funding → routes}/Transfer.d.ts +241 -46
  621. package/dist/internal/routes/Transfer.d.ts.map +1 -0
  622. package/dist/internal/routes/Transfer.js +728 -0
  623. package/dist/internal/routes/Transfer.js.map +1 -0
  624. package/dist/internal/{funding → routes}/TransferReconciliation.d.ts +13 -10
  625. package/dist/internal/routes/TransferReconciliation.d.ts.map +1 -0
  626. package/dist/internal/routes/TransferReconciliation.js +422 -0
  627. package/dist/internal/routes/TransferReconciliation.js.map +1 -0
  628. package/dist/internal/routes/TransferSubsidy.d.ts +201 -0
  629. package/dist/internal/routes/TransferSubsidy.d.ts.map +1 -0
  630. package/dist/internal/routes/TransferSubsidy.js +256 -0
  631. package/dist/internal/routes/TransferSubsidy.js.map +1 -0
  632. package/dist/internal/{funding → routes}/TransferWebhook.d.ts +5 -5
  633. package/dist/internal/routes/TransferWebhook.d.ts.map +1 -0
  634. package/dist/internal/{funding → routes}/TransferWebhook.js +3 -3
  635. package/dist/internal/routes/TransferWebhook.js.map +1 -0
  636. package/dist/internal/{funding → routes}/index.d.ts +2 -0
  637. package/dist/internal/routes/index.d.ts.map +1 -0
  638. package/dist/internal/{funding → routes}/index.js +2 -0
  639. package/dist/internal/routes/index.js.map +1 -0
  640. package/dist/internal/{funding → routes}/providers/across.d.ts +4 -4
  641. package/dist/internal/routes/providers/across.d.ts.map +1 -0
  642. package/dist/internal/{funding → routes}/providers/across.js +13 -13
  643. package/dist/internal/routes/providers/across.js.map +1 -0
  644. package/dist/internal/{funding → routes}/providers/relay.d.ts +21 -12
  645. package/dist/internal/routes/providers/relay.d.ts.map +1 -0
  646. package/dist/internal/{funding → routes}/providers/relay.js +194 -110
  647. package/dist/internal/routes/providers/relay.js.map +1 -0
  648. package/dist/internal/{funding → routes}/providers/rhino.d.ts +4 -4
  649. package/dist/internal/routes/providers/rhino.d.ts.map +1 -0
  650. package/dist/internal/{funding → routes}/providers/rhino.js +10 -10
  651. package/dist/internal/routes/providers/rhino.js.map +1 -0
  652. package/dist/internal/{funding → routes}/providers/squid.d.ts +4 -4
  653. package/dist/internal/routes/providers/squid.d.ts.map +1 -0
  654. package/dist/internal/{funding → routes}/providers/squid.js +14 -14
  655. package/dist/internal/routes/providers/squid.js.map +1 -0
  656. package/dist/internal/{funding → routes}/providers/stargate.d.ts +46 -18
  657. package/dist/internal/routes/providers/stargate.d.ts.map +1 -0
  658. package/dist/internal/routes/providers/stargate.js +898 -0
  659. package/dist/internal/routes/providers/stargate.js.map +1 -0
  660. package/dist/internal/{funding → routes}/providers/symbiosis.d.ts +4 -4
  661. package/dist/internal/routes/providers/symbiosis.d.ts.map +1 -0
  662. package/dist/internal/{funding → routes}/providers/symbiosis.js +30 -20
  663. package/dist/internal/routes/providers/symbiosis.js.map +1 -0
  664. package/dist/internal/webhookDestinations/slack.js +12 -12
  665. package/dist/internal/webhookDestinations/slack.js.map +1 -1
  666. package/dist/providers/index.d.ts +12 -12
  667. package/dist/providers/index.d.ts.map +1 -1
  668. package/dist/providers/index.js +12 -12
  669. package/dist/providers/index.js.map +1 -1
  670. package/dist/server.d.ts +19 -0
  671. package/dist/server.d.ts.map +1 -0
  672. package/dist/server.js +19 -0
  673. package/dist/server.js.map +1 -0
  674. package/package.json +15 -4
  675. package/src/ApiKey.ts +1 -1
  676. package/src/ApiKeys.test.ts +1033 -3
  677. package/src/ApiKeys.ts +489 -48
  678. package/src/App.test-d.ts +1 -1
  679. package/src/App.test.ts +216 -25
  680. package/src/App.ts +334 -31
  681. package/src/Assets.test.ts +2 -2
  682. package/src/Assets.ts +3 -1
  683. package/src/Auth.test-d.ts +1 -1
  684. package/src/Client.test-d.ts +539 -110
  685. package/src/Handler.ts +0 -1
  686. package/src/OpenApi.contract.test.ts +195 -0
  687. package/src/Scope.ts +17 -5
  688. package/src/Webhooks.ts +1 -1
  689. package/src/admin/App.test.ts +30 -2
  690. package/src/admin/App.ts +42 -6
  691. package/src/admin/apps/api-keys.test.ts +267 -5
  692. package/src/admin/apps/api-keys.ts +100 -50
  693. package/src/admin/apps/earn-vaults.test.ts +158 -0
  694. package/src/admin/apps/earn-vaults.ts +679 -6
  695. package/src/admin/apps/organizations.test.ts +306 -17
  696. package/src/admin/apps/organizations.ts +299 -24
  697. package/src/admin/apps/routes-support.test.ts +200 -0
  698. package/src/admin/apps/routes-support.ts +201 -0
  699. package/src/admin/apps/routes.test.ts +450 -0
  700. package/src/admin/apps/routes.ts +896 -0
  701. package/src/admin/apps/scopes.test.ts +15 -5
  702. package/src/admin/apps/subsidies.test.ts +306 -0
  703. package/src/admin/apps/subsidies.ts +457 -0
  704. package/src/admin/apps/verified-tokens.test.ts +8 -3
  705. package/src/admin/apps/verified-tokens.ts +3 -3
  706. package/src/admin/ui/plugin.ts +1 -2
  707. package/src/admin/ui/src/components/RewardCampaignForm.tsx +1009 -0
  708. package/src/admin/ui/src/lib/config.ts +11 -2
  709. package/src/admin/ui/src/lib/earn-vaults.ts +2 -2
  710. package/src/admin/ui/src/lib/organizations.test.ts +83 -50
  711. package/src/admin/ui/src/lib/organizations.ts +106 -25
  712. package/src/admin/ui/src/lib/query-client.ts +1 -1
  713. package/src/admin/ui/src/lib/reward-campaigns.ts +136 -0
  714. package/src/admin/ui/src/lib/routes.test.ts +88 -0
  715. package/src/admin/ui/src/lib/routes.ts +304 -0
  716. package/src/admin/ui/src/lib/subsidies.ts +75 -0
  717. package/src/admin/ui/src/routeTree.gen.ts +63 -0
  718. package/src/admin/ui/src/routes/__root.tsx +12 -1
  719. package/src/admin/ui/src/routes/api-keys.tsx +196 -84
  720. package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +105 -70
  721. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +239 -23
  722. package/src/admin/ui/src/routes/routes.tsx +1615 -0
  723. package/src/admin/ui/src/routes/routes_.support.tsx +248 -0
  724. package/src/admin/ui/src/routes/subsidies.tsx +799 -0
  725. package/src/admin/ui.generated.ts +144 -69
  726. package/src/analytics/Analytics.test.ts +108 -18
  727. package/src/analytics/Analytics.ts +38 -6
  728. package/src/analytics/tables/requestEvents.test.ts +100 -0
  729. package/src/analytics/tables/requestEvents.ts +10 -1
  730. package/src/analytics/tables/routesLifecycleIntervals.ts +140 -0
  731. package/src/apps/Zones.test.ts +15 -0
  732. package/src/apps/Zones.ts +52 -5
  733. package/src/apps/composition.test.ts +68 -60
  734. package/src/apps/data/App.test.ts +154 -2
  735. package/src/apps/data/App.ts +88 -28
  736. package/src/apps/data/routes/activities.test.ts +419 -7
  737. package/src/apps/data/routes/activities.ts +683 -216
  738. package/src/apps/data/routes/balances.test.ts +2 -2
  739. package/src/apps/data/routes/blocks.test.ts +46 -1
  740. package/src/apps/data/routes/coingecko.test.ts +419 -1
  741. package/src/apps/data/routes/coingecko.ts +309 -253
  742. package/src/apps/data/routes/earn.discovery.test.ts +274 -0
  743. package/src/apps/data/routes/earn.test.ts +1294 -17
  744. package/src/apps/data/routes/earn.ts +1838 -321
  745. package/src/apps/data/routes/exchanges.test.ts +12 -15
  746. package/src/apps/data/routes/fee-amm.test.ts +224 -0
  747. package/src/apps/data/routes/fee-amm.ts +191 -160
  748. package/src/apps/data/routes/indexer.test.ts +389 -0
  749. package/src/apps/data/routes/indexer.ts +165 -69
  750. package/src/apps/data/routes/receipts.test.ts +1 -1
  751. package/src/apps/data/routes/rpc.test.ts +352 -8
  752. package/src/apps/data/routes/rpc.ts +407 -219
  753. package/src/apps/data/routes/rpc.zone.test.ts +340 -0
  754. package/src/apps/data/routes/tokenlist.test.ts +285 -0
  755. package/src/apps/data/routes/tokenlist.ts +173 -88
  756. package/src/apps/data/routes/tokens.test.ts +93 -3
  757. package/src/apps/data/routes/tokens.ts +39 -23
  758. package/src/apps/data/routes/transactions.test.ts +1 -1
  759. package/src/apps/data/routes/transfers.test.ts +56 -24
  760. package/src/apps/data/routes/transfers.ts +19 -8
  761. package/src/apps/data/routes/valuation.test.ts +1 -1
  762. package/src/apps/data/routes/verified-tokens.test.ts +345 -0
  763. package/src/apps/data/routes/verified-tokens.ts +99 -29
  764. package/src/apps/data/routes/webhooks.test.ts +645 -83
  765. package/src/apps/data/routes/webhooks.ts +1089 -857
  766. package/src/apps/data/routes/zones.test.ts +148 -71
  767. package/src/apps/data/routes/zones.ts +71 -55
  768. package/src/apps/docs/reference.ts +2 -2
  769. package/src/apps/index.ts +1 -1
  770. package/src/apps/management/App.ts +9 -51
  771. package/src/apps/management/Billing.test.ts +375 -14
  772. package/src/apps/management/Billing.ts +265 -29
  773. package/src/apps/management/routes/api-keys.test.ts +269 -7
  774. package/src/apps/management/routes/api-keys.ts +61 -20
  775. package/src/apps/management/routes/billing.test.ts +477 -15
  776. package/src/apps/management/routes/billing.ts +377 -50
  777. package/src/apps/management/routes/faucet.test.ts +1 -1
  778. package/src/apps/management/routes/faucet.ts +1 -1
  779. package/src/apps/management/routes/invitations.test.ts +270 -3
  780. package/src/apps/management/routes/invitations.ts +185 -15
  781. package/src/apps/management/routes/invoicePreview.test.ts +549 -0
  782. package/src/apps/management/routes/me.test.ts +246 -5
  783. package/src/apps/management/routes/me.ts +7 -2
  784. package/src/apps/management/routes/members.test.ts +108 -0
  785. package/src/apps/management/routes/orgs.test.ts +191 -3
  786. package/src/apps/management/routes/orgs.ts +58 -11
  787. package/src/apps/management/routes/projects.test.ts +33 -3
  788. package/src/apps/management/routes/projects.ts +6 -1
  789. package/src/apps/management/routes/scopes.test.ts +30 -2
  790. package/src/apps/management/routes/scopes.ts +4 -1
  791. package/src/apps/management/routes/usage.test.ts +32 -0
  792. package/src/apps/management/routes/usage.ts +22 -4
  793. package/src/apps/management/routes/webhooks.test.ts +49 -26
  794. package/src/apps/management/routes/webhooks.ts +78 -22
  795. package/src/apps/mcp.test.ts +6 -6
  796. package/src/apps/mcp.ts +7 -7
  797. package/src/apps/mpp/App.ts +37 -7
  798. package/src/apps/mpp/Mpp.test.ts +49 -8
  799. package/src/apps/mpp/Mpp.zone.test.ts +219 -0
  800. package/src/apps/mpp/internal/Adapter.ts +34 -0
  801. package/src/apps/mpp/internal/Policy.test.ts +86 -31
  802. package/src/apps/mpp/internal/Policy.ts +49 -7
  803. package/src/apps/mpp/internal/Sponsorship.test.ts +207 -0
  804. package/src/apps/mpp/internal/Sponsorship.ts +9 -3
  805. package/src/apps/relay/App.test.ts +839 -64
  806. package/src/apps/relay/App.ts +148 -81
  807. package/src/apps/relay/App.zone-billing.test.ts +257 -0
  808. package/src/apps/relay/Sponsorships.test.ts +67 -4
  809. package/src/apps/relay/Sponsorships.ts +233 -32
  810. package/src/apps/relay/Sponsorships.zone.test.ts +102 -0
  811. package/src/apps/{funding → routes}/Access.ts +6 -6
  812. package/src/apps/routes/App.test.ts +165 -0
  813. package/src/apps/routes/App.ts +96 -0
  814. package/src/apps/{funding → routes}/Inventory.ts +2 -2
  815. package/src/apps/routes/index.ts +1 -0
  816. package/src/apps/{funding → routes}/routes/chains.test.ts +66 -66
  817. package/src/apps/{funding → routes}/routes/chains.ts +23 -23
  818. package/src/apps/{funding → routes}/routes/deposit-addresses.test.ts +1358 -458
  819. package/src/apps/{funding → routes}/routes/deposit-addresses.ts +566 -246
  820. package/src/apps/{funding → routes}/routes/providers.test.ts +27 -33
  821. package/src/apps/{funding → routes}/routes/providers.ts +23 -23
  822. package/src/apps/{funding → routes}/routes/quotes.test.ts +298 -41
  823. package/src/apps/{funding → routes}/routes/quotes.ts +57 -40
  824. package/src/apps/{funding → routes}/routes/transfers.test.ts +1100 -353
  825. package/src/apps/routes/routes/transfers.ts +1332 -0
  826. package/src/cli/Cli.test.ts +1 -1
  827. package/src/cli/commands/admin.test.ts +748 -1
  828. package/src/cli/commands/admin.ts +278 -0
  829. package/src/cloudflare.ts +52 -47
  830. package/src/db/Db.test.ts +245 -2
  831. package/src/db/Db.ts +1476 -34
  832. package/src/db/Schema.ts +729 -228
  833. package/src/db/tables/apiKeyAdmissionBootstraps.ts +4 -0
  834. package/src/db/tables/apiKeyAdmissions.ts +218 -0
  835. package/src/db/tables/apiKeyOwnerAdmissions.ts +4 -0
  836. package/src/db/tables/apiKeyOwnerTombstones.ts +81 -0
  837. package/src/db/tables/apiKeyRevocations.test.ts +74 -0
  838. package/src/db/tables/apiKeyRevocations.ts +121 -0
  839. package/src/db/tables/authAccounts.ts +105 -0
  840. package/src/db/tables/authSessions.ts +13 -0
  841. package/src/db/tables/authVerifications.ts +13 -0
  842. package/src/db/tables/invitations.ts +142 -45
  843. package/src/db/tables/memberships.ts +79 -3
  844. package/src/db/tables/organizations.test.ts +690 -13
  845. package/src/db/tables/organizations.ts +139 -55
  846. package/src/db/tables/projects.test.ts +16 -0
  847. package/src/db/tables/projects.ts +13 -3
  848. package/src/db/tables/requestUsage.ts +4 -1
  849. package/src/db/tables/rewardAccounts.ts +178 -0
  850. package/src/db/tables/rewardCampaignDistributors.ts +68 -0
  851. package/src/db/tables/rewardCampaigns.test.ts +1388 -0
  852. package/src/db/tables/rewardCampaigns.ts +647 -0
  853. package/src/db/tables/rewardCredits.ts +110 -0
  854. package/src/db/tables/rewardEligibilityAssociations.test.ts +271 -0
  855. package/src/db/tables/rewardEligibilityAssociations.ts +184 -0
  856. package/src/db/tables/rewardRuns.ts +514 -0
  857. package/src/db/tables/rewardTransactionAttempts.ts +272 -0
  858. package/src/db/tables/routesCatalog.test.ts +185 -0
  859. package/src/db/tables/routesCatalog.ts +163 -0
  860. package/src/db/tables/routesDepositAddresses.test.ts +1048 -0
  861. package/src/db/tables/routesDepositAddresses.ts +1008 -0
  862. package/src/db/tables/{fundingDepositRequestObservations.test.ts → routesDepositRequestObservations.test.ts} +13 -14
  863. package/src/db/tables/{fundingDepositRequestObservations.ts → routesDepositRequestObservations.ts} +10 -10
  864. package/src/db/tables/{fundingDeposits.test.ts → routesDeposits.test.ts} +316 -46
  865. package/src/db/tables/routesDeposits.ts +1062 -0
  866. package/src/db/tables/routesIdempotency.test.ts +796 -0
  867. package/src/db/tables/routesIdempotency.ts +496 -0
  868. package/src/db/tables/routesSubsidies.test.ts +120 -0
  869. package/src/db/tables/routesSubsidies.ts +71 -0
  870. package/src/db/tables/{fundingTransferEvents.ts → routesTransferEvents.ts} +4 -4
  871. package/src/db/tables/routesTransferSubsidies.ts +38 -0
  872. package/src/db/tables/routesTransferSubsidyNonces.ts +124 -0
  873. package/src/db/tables/routesTransferTransactions.test.ts +195 -0
  874. package/src/db/tables/{fundingTransferTransactions.ts → routesTransferTransactions.ts} +63 -18
  875. package/src/db/tables/routesTransfers.test.ts +338 -0
  876. package/src/db/tables/routesTransfers.ts +662 -0
  877. package/src/db/tables/sponsoredTransactions.test.ts +269 -45
  878. package/src/db/tables/sponsoredTransactions.ts +225 -65
  879. package/src/db/tables/sponsorshipAttributions.test.ts +160 -0
  880. package/src/db/tables/sponsorshipAttributions.ts +168 -0
  881. package/src/db/tables/sponsorshipReconciliationCursors.test.ts +115 -0
  882. package/src/db/tables/sponsorshipReconciliationCursors.ts +67 -0
  883. package/src/db/tables/stripeCustomers.test.ts +85 -0
  884. package/src/db/tables/stripeCustomers.ts +24 -0
  885. package/src/db/tables/users.test.ts +129 -0
  886. package/src/db/tables/users.ts +49 -3
  887. package/src/db/tables/webhookQueueEvents.ts +8 -1
  888. package/src/db/tables/webhookSubscriptions.ts +95 -20
  889. package/src/handlers/internal/executionError.ts +3 -4
  890. package/src/handlers/internal/multisig.ts +13 -816
  891. package/src/handlers/internal/sponsorship.ts +26 -10
  892. package/src/handlers/relay.test-d.ts +9 -1
  893. package/src/handlers/relay.test.ts +823 -3
  894. package/src/handlers/relay.ts +127 -107
  895. package/src/index.test-d.ts +7 -0
  896. package/src/index.ts +0 -17
  897. package/src/internal/AssetResponse.ts +26 -0
  898. package/src/internal/Auth.test.ts +1564 -9
  899. package/src/internal/Auth.ts +833 -239
  900. package/src/internal/BetterAuth.test.ts +242 -0
  901. package/src/internal/BetterAuth.ts +349 -0
  902. package/src/internal/Cache.ts +2 -0
  903. package/src/internal/EarnRates.test.ts +17 -0
  904. package/src/internal/EarnRates.ts +124 -31
  905. package/src/internal/EarnSharePrices.test.ts +182 -0
  906. package/src/internal/EarnSharePrices.ts +313 -0
  907. package/src/internal/EarnVaults.test.ts +21 -0
  908. package/src/internal/EarnVaults.ts +33 -11
  909. package/src/internal/Email.ts +54 -0
  910. package/src/internal/Idempotency.test.ts +2 -2
  911. package/src/internal/Log.test-d.ts +8 -7
  912. package/src/internal/Log.test.ts +114 -16
  913. package/src/internal/Log.ts +53 -40
  914. package/src/internal/MetricSink.test.ts +313 -26
  915. package/src/internal/MetricSink.ts +73 -8
  916. package/src/internal/Mpp.test.ts +31 -0
  917. package/src/internal/Mpp.ts +21 -24
  918. package/src/internal/OpenApi.ts +45 -12
  919. package/src/internal/Provider.test.ts +1 -1
  920. package/src/internal/Provider.ts +1 -1
  921. package/src/internal/RateLimit.test.ts +21 -3
  922. package/src/internal/RateLimit.ts +12 -1
  923. package/src/internal/Store.test-d.ts +1 -1
  924. package/src/internal/Store.test.ts +151 -8
  925. package/src/internal/Store.ts +76 -9
  926. package/src/internal/Tidx.test.ts +59 -8
  927. package/src/internal/Tidx.ts +86 -14
  928. package/src/internal/Timing.test.ts +77 -0
  929. package/src/internal/Timing.ts +83 -6
  930. package/src/internal/VercelOidc.test.ts +188 -0
  931. package/src/internal/VercelOidc.ts +193 -0
  932. package/src/internal/VerifiedTokens.test.ts +60 -6
  933. package/src/internal/VerifiedTokens.ts +21 -24
  934. package/src/internal/Viem.batch.test.ts +80 -0
  935. package/src/internal/Viem.test.ts +201 -2
  936. package/src/internal/Viem.ts +221 -25
  937. package/src/internal/Webhooks.test.ts +85 -6
  938. package/src/internal/Webhooks.ts +22 -4
  939. package/src/internal/ZoneRpc.ts +50 -0
  940. package/src/internal/exchange/Provider.test.ts +2 -2
  941. package/src/internal/index.test.ts +3 -1
  942. package/src/internal/index.ts +3 -1
  943. package/src/internal/rewards/Audit.ts +231 -0
  944. package/src/internal/rewards/Campaigns.test.ts +1234 -0
  945. package/src/internal/rewards/Campaigns.ts +1205 -0
  946. package/src/internal/rewards/Credits.ts +165 -0
  947. package/src/internal/rewards/Events.local.test.ts +113 -0
  948. package/src/internal/rewards/Events.test.ts +152 -0
  949. package/src/internal/rewards/Events.ts +468 -0
  950. package/src/internal/rewards/Projection.test.ts +681 -0
  951. package/src/internal/rewards/Projection.ts +987 -0
  952. package/src/internal/rewards/Rates.test.ts +171 -0
  953. package/src/internal/rewards/Rates.ts +110 -0
  954. package/src/internal/rewards/Read.test.ts +501 -0
  955. package/src/internal/rewards/Read.ts +476 -0
  956. package/src/internal/rewards/Runner.checkpoint.test.ts +80 -0
  957. package/src/internal/rewards/Runner.states.test.ts +188 -0
  958. package/src/internal/rewards/Runner.test.ts +90 -0
  959. package/src/internal/rewards/Runner.ts +2229 -0
  960. package/src/internal/rewards/Signer.test.ts +79 -0
  961. package/src/internal/rewards/Signer.ts +180 -0
  962. package/src/internal/rewards/index.ts +14 -0
  963. package/src/internal/{funding → routes}/Action.test.ts +63 -14
  964. package/src/internal/{funding → routes}/Action.ts +35 -8
  965. package/src/internal/{funding → routes}/Catalog.test.ts +3 -3
  966. package/src/internal/{funding → routes}/Catalog.ts +39 -39
  967. package/src/internal/{funding → routes}/Chain.test.ts +1 -1
  968. package/src/internal/{funding → routes}/Chain.ts +11 -11
  969. package/src/internal/{funding → routes}/Deposit.test.ts +1 -1
  970. package/src/internal/{funding → routes}/Deposit.ts +93 -45
  971. package/src/internal/{funding → routes}/DepositAddress.test.ts +11 -3
  972. package/src/internal/{funding → routes}/DepositAddress.ts +132 -53
  973. package/src/internal/{funding → routes}/DepositWebhook.test.ts +61 -18
  974. package/src/internal/{funding → routes}/DepositWebhook.ts +9 -9
  975. package/src/internal/routes/Economics.test.ts +42 -0
  976. package/src/internal/routes/Economics.ts +47 -0
  977. package/src/internal/{funding → routes}/Evidence.test.ts +6 -6
  978. package/src/internal/{funding → routes}/Evidence.ts +9 -9
  979. package/src/internal/routes/Observability.test.ts +636 -0
  980. package/src/internal/routes/Observability.ts +668 -0
  981. package/src/internal/{funding → routes}/Provider.test.ts +489 -153
  982. package/src/internal/{funding → routes}/Provider.ts +489 -186
  983. package/src/internal/{funding → routes}/Reconciliation.local.test.ts +84 -12
  984. package/src/internal/{funding → routes}/Reconciliation.test.ts +1213 -279
  985. package/src/internal/{funding → routes}/Reconciliation.ts +501 -203
  986. package/src/internal/routes/Reconciliation.unattributed.test.ts +250 -0
  987. package/src/internal/{funding → routes}/Route.ts +7 -7
  988. package/src/internal/{funding → routes}/SourceObservation.test.ts +13 -14
  989. package/src/internal/{funding → routes}/SourceObservation.ts +13 -13
  990. package/src/internal/{funding → routes}/Subsidy.local.test.ts +7 -7
  991. package/src/internal/routes/Subsidy.test.ts +169 -0
  992. package/src/internal/{funding → routes}/Subsidy.ts +86 -6
  993. package/src/internal/{funding → routes}/Token.test.ts +1 -1
  994. package/src/internal/{funding → routes}/Token.ts +5 -5
  995. package/src/internal/routes/Transfer.test.ts +1199 -0
  996. package/src/internal/routes/Transfer.ts +1206 -0
  997. package/src/internal/routes/TransferReconciliation.test.ts +731 -0
  998. package/src/internal/routes/TransferReconciliation.ts +527 -0
  999. package/src/internal/routes/TransferSubsidy.test.ts +172 -0
  1000. package/src/internal/routes/TransferSubsidy.ts +447 -0
  1001. package/src/internal/{funding → routes}/TransferWebhook.test.ts +13 -13
  1002. package/src/internal/{funding → routes}/TransferWebhook.ts +7 -7
  1003. package/src/internal/{funding → routes}/index.test.ts +5 -3
  1004. package/src/internal/{funding → routes}/index.ts +2 -0
  1005. package/src/internal/{funding → routes}/providers/across.ts +16 -16
  1006. package/src/internal/{funding → routes}/providers/index.test.ts +157 -44
  1007. package/src/internal/{funding → routes}/providers/relay.test.ts +165 -32
  1008. package/src/internal/{funding → routes}/providers/relay.ts +228 -121
  1009. package/src/internal/{funding → routes}/providers/rhino.ts +16 -16
  1010. package/src/internal/{funding → routes}/providers/squid.ts +17 -17
  1011. package/src/internal/routes/providers/stargate.test.ts +728 -0
  1012. package/src/internal/routes/providers/stargate.ts +1276 -0
  1013. package/src/internal/{funding → routes}/providers/symbiosis.ts +64 -24
  1014. package/src/internal/webhookDestinations/slack.ts +10 -10
  1015. package/src/providers/index.ts +13 -13
  1016. package/src/server.ts +18 -0
  1017. package/dist/apps/funding/Access.d.ts.map +0 -1
  1018. package/dist/apps/funding/Access.js.map +0 -1
  1019. package/dist/apps/funding/App.js.map +0 -1
  1020. package/dist/apps/funding/Inventory.d.ts.map +0 -1
  1021. package/dist/apps/funding/Inventory.js.map +0 -1
  1022. package/dist/apps/funding/ProviderAttempts.d.ts +0 -5
  1023. package/dist/apps/funding/ProviderAttempts.d.ts.map +0 -1
  1024. package/dist/apps/funding/ProviderAttempts.js +0 -24
  1025. package/dist/apps/funding/ProviderAttempts.js.map +0 -1
  1026. package/dist/apps/funding/index.d.ts +0 -2
  1027. package/dist/apps/funding/index.d.ts.map +0 -1
  1028. package/dist/apps/funding/index.js +0 -2
  1029. package/dist/apps/funding/index.js.map +0 -1
  1030. package/dist/apps/funding/routes/chains.d.ts.map +0 -1
  1031. package/dist/apps/funding/routes/chains.js.map +0 -1
  1032. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +0 -1
  1033. package/dist/apps/funding/routes/deposit-addresses.js.map +0 -1
  1034. package/dist/apps/funding/routes/providers.d.ts.map +0 -1
  1035. package/dist/apps/funding/routes/providers.js +0 -57
  1036. package/dist/apps/funding/routes/providers.js.map +0 -1
  1037. package/dist/apps/funding/routes/quotes.d.ts.map +0 -1
  1038. package/dist/apps/funding/routes/quotes.js.map +0 -1
  1039. package/dist/apps/funding/routes/transfers.d.ts.map +0 -1
  1040. package/dist/apps/funding/routes/transfers.js +0 -828
  1041. package/dist/apps/funding/routes/transfers.js.map +0 -1
  1042. package/dist/db/tables/fundingCatalog.d.ts +0 -58
  1043. package/dist/db/tables/fundingCatalog.d.ts.map +0 -1
  1044. package/dist/db/tables/fundingCatalog.js.map +0 -1
  1045. package/dist/db/tables/fundingDepositAddresses.d.ts +0 -192
  1046. package/dist/db/tables/fundingDepositAddresses.d.ts.map +0 -1
  1047. package/dist/db/tables/fundingDepositAddresses.js +0 -235
  1048. package/dist/db/tables/fundingDepositAddresses.js.map +0 -1
  1049. package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +0 -1
  1050. package/dist/db/tables/fundingDepositRequestObservations.js.map +0 -1
  1051. package/dist/db/tables/fundingDeposits.d.ts.map +0 -1
  1052. package/dist/db/tables/fundingDeposits.js +0 -295
  1053. package/dist/db/tables/fundingDeposits.js.map +0 -1
  1054. package/dist/db/tables/fundingIdempotency.d.ts +0 -91
  1055. package/dist/db/tables/fundingIdempotency.d.ts.map +0 -1
  1056. package/dist/db/tables/fundingIdempotency.js +0 -103
  1057. package/dist/db/tables/fundingIdempotency.js.map +0 -1
  1058. package/dist/db/tables/fundingTransferEvents.d.ts.map +0 -1
  1059. package/dist/db/tables/fundingTransferEvents.js.map +0 -1
  1060. package/dist/db/tables/fundingTransferTransactions.d.ts +0 -53
  1061. package/dist/db/tables/fundingTransferTransactions.d.ts.map +0 -1
  1062. package/dist/db/tables/fundingTransferTransactions.js.map +0 -1
  1063. package/dist/db/tables/fundingTransfers.d.ts +0 -99
  1064. package/dist/db/tables/fundingTransfers.d.ts.map +0 -1
  1065. package/dist/db/tables/fundingTransfers.js +0 -110
  1066. package/dist/db/tables/fundingTransfers.js.map +0 -1
  1067. package/dist/internal/funding/Action.d.ts.map +0 -1
  1068. package/dist/internal/funding/Action.js.map +0 -1
  1069. package/dist/internal/funding/Catalog.d.ts.map +0 -1
  1070. package/dist/internal/funding/Catalog.js.map +0 -1
  1071. package/dist/internal/funding/Chain.d.ts.map +0 -1
  1072. package/dist/internal/funding/Chain.js.map +0 -1
  1073. package/dist/internal/funding/Deposit.d.ts.map +0 -1
  1074. package/dist/internal/funding/Deposit.js.map +0 -1
  1075. package/dist/internal/funding/DepositAddress.d.ts.map +0 -1
  1076. package/dist/internal/funding/DepositAddress.js.map +0 -1
  1077. package/dist/internal/funding/DepositWebhook.d.ts.map +0 -1
  1078. package/dist/internal/funding/DepositWebhook.js.map +0 -1
  1079. package/dist/internal/funding/Evidence.d.ts.map +0 -1
  1080. package/dist/internal/funding/Evidence.js.map +0 -1
  1081. package/dist/internal/funding/Provider.d.ts.map +0 -1
  1082. package/dist/internal/funding/Provider.js.map +0 -1
  1083. package/dist/internal/funding/Reconciliation.d.ts.map +0 -1
  1084. package/dist/internal/funding/Reconciliation.js.map +0 -1
  1085. package/dist/internal/funding/Route.d.ts +0 -21
  1086. package/dist/internal/funding/Route.d.ts.map +0 -1
  1087. package/dist/internal/funding/Route.js.map +0 -1
  1088. package/dist/internal/funding/SourceObservation.d.ts.map +0 -1
  1089. package/dist/internal/funding/SourceObservation.js.map +0 -1
  1090. package/dist/internal/funding/Subsidy.d.ts.map +0 -1
  1091. package/dist/internal/funding/Subsidy.js.map +0 -1
  1092. package/dist/internal/funding/Token.d.ts.map +0 -1
  1093. package/dist/internal/funding/Token.js.map +0 -1
  1094. package/dist/internal/funding/Transfer.d.ts.map +0 -1
  1095. package/dist/internal/funding/Transfer.js +0 -462
  1096. package/dist/internal/funding/Transfer.js.map +0 -1
  1097. package/dist/internal/funding/TransferReconciliation.d.ts.map +0 -1
  1098. package/dist/internal/funding/TransferReconciliation.js +0 -192
  1099. package/dist/internal/funding/TransferReconciliation.js.map +0 -1
  1100. package/dist/internal/funding/TransferWebhook.d.ts.map +0 -1
  1101. package/dist/internal/funding/TransferWebhook.js.map +0 -1
  1102. package/dist/internal/funding/index.d.ts.map +0 -1
  1103. package/dist/internal/funding/index.js.map +0 -1
  1104. package/dist/internal/funding/providers/across.d.ts.map +0 -1
  1105. package/dist/internal/funding/providers/across.js.map +0 -1
  1106. package/dist/internal/funding/providers/relay.d.ts.map +0 -1
  1107. package/dist/internal/funding/providers/relay.js.map +0 -1
  1108. package/dist/internal/funding/providers/rhino.d.ts.map +0 -1
  1109. package/dist/internal/funding/providers/rhino.js.map +0 -1
  1110. package/dist/internal/funding/providers/squid.d.ts.map +0 -1
  1111. package/dist/internal/funding/providers/squid.js.map +0 -1
  1112. package/dist/internal/funding/providers/stargate.d.ts.map +0 -1
  1113. package/dist/internal/funding/providers/stargate.js +0 -579
  1114. package/dist/internal/funding/providers/stargate.js.map +0 -1
  1115. package/dist/internal/funding/providers/symbiosis.d.ts.map +0 -1
  1116. package/dist/internal/funding/providers/symbiosis.js.map +0 -1
  1117. package/src/apps/funding/App.test.ts +0 -78
  1118. package/src/apps/funding/App.ts +0 -88
  1119. package/src/apps/funding/ProviderAttempts.ts +0 -26
  1120. package/src/apps/funding/index.ts +0 -1
  1121. package/src/apps/funding/routes/transfers.ts +0 -987
  1122. package/src/db/tables/fundingCatalog.test.ts +0 -122
  1123. package/src/db/tables/fundingCatalog.ts +0 -163
  1124. package/src/db/tables/fundingDepositAddresses.test.ts +0 -608
  1125. package/src/db/tables/fundingDepositAddresses.ts +0 -443
  1126. package/src/db/tables/fundingDeposits.ts +0 -572
  1127. package/src/db/tables/fundingIdempotency.test.ts +0 -182
  1128. package/src/db/tables/fundingIdempotency.ts +0 -180
  1129. package/src/db/tables/fundingTransferTransactions.test.ts +0 -112
  1130. package/src/db/tables/fundingTransfers.test.ts +0 -115
  1131. package/src/db/tables/fundingTransfers.ts +0 -189
  1132. package/src/handlers/internal/multisig.test.ts +0 -818
  1133. package/src/internal/funding/Subsidy.test.ts +0 -75
  1134. package/src/internal/funding/Transfer.test.ts +0 -524
  1135. package/src/internal/funding/Transfer.ts +0 -762
  1136. package/src/internal/funding/TransferReconciliation.test.ts +0 -147
  1137. package/src/internal/funding/TransferReconciliation.ts +0 -264
  1138. package/src/internal/funding/providers/stargate.test.ts +0 -328
  1139. package/src/internal/funding/providers/stargate.ts +0 -856
  1140. /package/dist/apps/{funding → routes}/Inventory.d.ts +0 -0
  1141. /package/src/internal/{funding → routes}/Route.test.ts +0 -0
@@ -0,0 +1,2229 @@
1
+ import {
2
+ erc20Abi,
3
+ getAddress,
4
+ isAddress,
5
+ isAddressEqual,
6
+ parseAbi,
7
+ parseEventLogs,
8
+ type Address,
9
+ type Hex,
10
+ } from 'viem'
11
+
12
+ import * as RewardAccounts from '../../db/tables/rewardAccounts.js'
13
+ import * as RewardCampaigns from '../../db/tables/rewardCampaigns.js'
14
+ import * as RewardEligibilityAssociations from '../../db/tables/rewardEligibilityAssociations.js'
15
+ import * as RewardRuns from '../../db/tables/rewardRuns.js'
16
+ import * as RewardTransactionAttempts from '../../db/tables/rewardTransactionAttempts.js'
17
+ import * as EarnVaultRecords from '../../db/tables/earnVaults.js'
18
+ import type * as Db from '../../db/Db.js'
19
+ import * as Campaigns from './Campaigns.js'
20
+ import * as Events from './Events.js'
21
+ import * as Projection from './Projection.js'
22
+ import * as Rates from './Rates.js'
23
+ import * as Signer from './Signer.js'
24
+ import type * as Tidx from '../Tidx.js'
25
+ import * as Viem from '../Viem.js'
26
+
27
+ const claimDeadline = Number((1n << 40n) - 1n)
28
+ const maximumIntervalsPerRun = 12
29
+ // Each wave produces at most 64 historical multicalls per RPC round.
30
+ const capitalStateBatchSize = 64
31
+ // Keep enough headroom below the Worker's 128 MiB memory limit for account projection,
32
+ // the cumulative Merkle tree, and payout construction to coexist in one invocation.
33
+ const maximumRecipients = 10_000
34
+ // The Earn operator's Tempo gas model budgets 200k per transfer and 250k per
35
+ // push call. Two 60-recipient calls stay below 90% of the signer's 28M cap.
36
+ const payoutBatchSize = 60
37
+ const payoutCallsPerTransaction = 2
38
+ const payoutTransactionConcurrency = 16
39
+ const runLeaseMilliseconds = 2 * 60 * 1_000
40
+ const factoryAbi = parseAbi([
41
+ 'function predictContributionController(address earnVault,address owner) view returns (address)',
42
+ 'function predictMerkleDistributor(address earnVault,address owner,address treasury,uint40 claimDeadline) view returns (address)',
43
+ ])
44
+ const vaultAbi = parseAbi([
45
+ 'function earnFees() view returns (address)',
46
+ 'function engine() view returns (address)',
47
+ 'function previewRedeem(uint256 earnShares) view returns (uint256)',
48
+ 'function totalAssets() view returns (uint256)',
49
+ 'function totalEarnShares() view returns (uint256)',
50
+ ])
51
+ const feesAbi = parseAbi([
52
+ 'function previewAccruedFees() view returns ((uint256 activeAssets,uint256 positiveAccrualAssets,uint256 fixedFeeAssets,uint256 excessFeeAssets,uint256 totalFeeAssets,uint256 totalFeeEarnShares,uint256 preFeeValuePerEarnShare,uint256 postFeeValuePerEarnShare,uint256 targetValuePerEarnShare,uint8 allocationCount,(address account,uint256 feeAssets,uint256 feeEarnShares)[5] allocations) result)',
53
+ ])
54
+ const controllerAbi = parseAbi([
55
+ 'event Funded(address indexed funder,uint256 requestedAssets,uint256 fundedAssets)',
56
+ ])
57
+ const distributorAbi = parseAbi([
58
+ 'function cumulativePaid(address recipient) view returns (uint256)',
59
+ 'function merkleRoot() view returns (bytes32)',
60
+ 'function rootTotalEntitlement() view returns (uint256)',
61
+ 'function rootVersion() view returns (uint64)',
62
+ 'function statementHash() view returns (bytes32)',
63
+ 'function totalPaid() view returns (uint256)',
64
+ ])
65
+
66
+ /** Compact queue reference for one Earn reward campaign. */
67
+ export type Message = {
68
+ /** Tempo chain containing the campaign. */
69
+ chainId: number
70
+ /** Queue message discriminator. */
71
+ type: 'earn:reward:run'
72
+ /** Verified EarnVault address. */
73
+ vaultAddress: Address
74
+ }
75
+
76
+ /** Lists due campaigns as compact queue references. */
77
+ export async function due(db: Db.Db, now = Date.now()): Promise<Message[]> {
78
+ const campaigns: RewardCampaigns.Record[] = []
79
+ const pageSize = 100
80
+ for (let offset = 0; ; offset += pageSize) {
81
+ const page = await RewardCampaigns.listDue(db, {
82
+ limit: pageSize,
83
+ offset,
84
+ through: Math.floor(now / 1_000),
85
+ })
86
+ campaigns.push(...page)
87
+ if (page.length < pageSize) break
88
+ }
89
+ for (let offset = 0; ; offset += pageSize) {
90
+ const page = await RewardCampaigns.listUnprovisioned(db, { limit: pageSize, offset })
91
+ campaigns.push(...page)
92
+ if (page.length < pageSize) break
93
+ }
94
+ return [
95
+ ...new Map(
96
+ campaigns.map((campaign) => [`${campaign.chainId}:${campaign.vaultAddress}`, campaign]),
97
+ ).values(),
98
+ ]
99
+ .filter(
100
+ (campaign) =>
101
+ Number(campaign.deliveredThrough) < Campaigns.finalBoundary(campaign.config) ||
102
+ [campaign.config, campaign.pendingConfig].some(
103
+ (config) =>
104
+ Boolean(config?.targetYield && !campaign.controllerAddress) ||
105
+ Boolean(config?.boostRewards && !campaign.distributorAddress),
106
+ ),
107
+ )
108
+ .map((campaign) => ({
109
+ chainId: campaign.chainId,
110
+ type: 'earn:reward:run',
111
+ vaultAddress: getAddress(campaign.vaultAddress),
112
+ }))
113
+ }
114
+
115
+ /** Selects resumable state only when its eligibility snapshot is unchanged. */
116
+ export function resumeCheckpoint(
117
+ checkpoint: Campaigns.RunCheckpointEvidence | undefined,
118
+ options: resumeCheckpoint.Options,
119
+ ): Campaigns.RunCheckpointEvidence | undefined {
120
+ return checkpoint?.progress.eligibilityVersion === options.eligibilityVersion &&
121
+ checkpoint.progress.exclusionVersion === options.exclusionVersion
122
+ ? checkpoint
123
+ : undefined
124
+ }
125
+
126
+ export declare namespace resumeCheckpoint {
127
+ /** Current calculation identity. */
128
+ type Options = {
129
+ /** Exact current eligibility snapshot identity. */
130
+ eligibilityVersion: Hex
131
+ /** Exact current effective exclusion set identity. */
132
+ exclusionVersion: Hex
133
+ }
134
+ }
135
+
136
+ /** Keeps the committed campaign cursor ahead of replay-only history. */
137
+ export function reconciledCursor(options: reconciledCursor.Options): Campaigns.EventCursor | null {
138
+ return options.committed ?? options.replayed ?? null
139
+ }
140
+
141
+ export declare namespace reconciledCursor {
142
+ /** Historical and committed cursor candidates. */
143
+ type Options = {
144
+ /** Cursor already committed by the campaign. */
145
+ committed?: Campaigns.EventCursor | null | undefined
146
+ /** Last event observed while replaying a new registration. */
147
+ replayed?: Campaigns.EventCursor | undefined
148
+ }
149
+ }
150
+
151
+ /** Restores the target rate selected before a calculation checkpoint. */
152
+ export function checkpointTargetRate(
153
+ checkpoint: Campaigns.RunCheckpointEvidence | undefined,
154
+ config: NonNullable<Campaigns.Config['targetYield']>,
155
+ ) {
156
+ const progress = checkpoint?.progress
157
+ if (progress?.targetRateBps === undefined || !progress.targetRateObservedAt) return undefined
158
+ return {
159
+ annualRateBps: progress.targetRateBps,
160
+ config,
161
+ observedAt: progress.targetRateObservedAt,
162
+ ...(progress.staleRates?.includes('target') ? { stale: true } : {}),
163
+ }
164
+ }
165
+
166
+ /** Advances one campaign through its durable, fenced execution phases. */
167
+ export async function run(options: run.Options): Promise<run.Result> {
168
+ const campaign = await RewardCampaigns.get(options.db, options.message)
169
+ if (!campaign || campaign.paused) return { status: 'idle' }
170
+ if (campaign.chainId !== Viem.chainId.mainnet)
171
+ throw new Error('Reward execution is currently supported only on Tempo mainnet.')
172
+ const { bindings, signer } = await (async () => {
173
+ try {
174
+ const signer = await Signer.health(options.signerFetch)
175
+ const bindings = await provision(options, campaign, signer.signer)
176
+ return { bindings, signer }
177
+ } catch (cause) {
178
+ await RewardCampaigns.setProvisioningError(options.db, {
179
+ chainId: campaign.chainId,
180
+ config: provisioningPlan(campaign).configMatch,
181
+ error: cause instanceof Error ? cause.message : 'Reward provisioning failed.',
182
+ vaultAddress: getAddress(campaign.vaultAddress),
183
+ })
184
+ throw cause
185
+ }
186
+ })()
187
+ const start = Number(campaign.deliveredThrough)
188
+ const scheduleEnd = Campaigns.finalBoundary(campaign.config)
189
+ const intervalSeconds = Campaigns.intervalSeconds(campaign.config)
190
+ const clock = Math.floor(options.now() / 1_000)
191
+ const completeIntervals = Math.max(
192
+ 0,
193
+ Math.floor((Math.min(clock, scheduleEnd) - start) / intervalSeconds),
194
+ )
195
+ let end = start + Math.min(completeIntervals, maximumIntervalsPerRun) * intervalSeconds
196
+ const nextScheduleStart = [
197
+ campaign.config.targetYield?.startTimestamp,
198
+ campaign.config.boostRewards?.startTimestamp,
199
+ ]
200
+ .filter((timestamp): timestamp is number => timestamp !== undefined && timestamp > start)
201
+ .sort((left, right) => left - right)[0]
202
+ if (nextScheduleStart !== undefined) end = Math.min(end, nextScheduleStart)
203
+ if (campaign.pendingEffectiveAt !== null) end = Math.min(end, Number(campaign.pendingEffectiveAt))
204
+ if (end <= start) return { status: 'idle' }
205
+
206
+ const ensured = await RewardRuns.ensure(options.db, {
207
+ chainId: campaign.chainId,
208
+ config: campaign.config,
209
+ endsAt: end,
210
+ startsAfter: start,
211
+ vaultAddress: getAddress(campaign.vaultAddress),
212
+ })
213
+ const leased = await RewardRuns.acquire(options.db, {
214
+ id: ensured.id,
215
+ leaseMilliseconds: runLeaseMilliseconds,
216
+ })
217
+ if (!leased) return { runId: ensured.id, status: 'busy' }
218
+ const heartbeat = maintainLease(options.db, leased, options.now)
219
+ const executionOptions = { ...options, assertLease: () => heartbeat.assert() }
220
+
221
+ try {
222
+ await heartbeat.ready()
223
+ if (leased.phase === 'paying' && leased.statement && leased.evidence) {
224
+ await deliver(
225
+ executionOptions,
226
+ campaign,
227
+ leased,
228
+ bindings,
229
+ leased.statement,
230
+ restoreCalculation(leased.evidence),
231
+ signer.signer,
232
+ )
233
+ const result = {
234
+ notification: await notificationSummary(options.db, campaign, leased.id),
235
+ runId: leased.id,
236
+ staleRates: leased.evidence.staleRates ?? [],
237
+ status: 'delivered' as const,
238
+ }
239
+ const leaseFailure = await heartbeat.stop()
240
+ if (leaseFailure !== undefined) throw leaseFailure
241
+ return result
242
+ }
243
+ if (!['indexing', 'calculating', 'targetYield', 'statement'].includes(leased.phase))
244
+ throw new Error(`Unsupported reward recovery phase ${leased.phase}.`)
245
+ const checkpoint = await RewardRuns.getCheckpoint(options.db, { id: leased.id })
246
+ const calculated =
247
+ leased.evidence && !checkpoint
248
+ ? restoreCalculation(leased.evidence)
249
+ : await calculate(executionOptions, campaign, leased, bindings)
250
+ const targetAttempt =
251
+ leased.phase === 'statement'
252
+ ? undefined
253
+ : await settleTargetYield(
254
+ executionOptions,
255
+ campaign,
256
+ leased,
257
+ bindings,
258
+ calculated,
259
+ signer.signer,
260
+ )
261
+ const settlement = await settleBoost(
262
+ executionOptions,
263
+ campaign,
264
+ leased,
265
+ bindings,
266
+ calculated,
267
+ signer.signer,
268
+ )
269
+ // The leased record and initial calculation predate settlement, so delivery consumes the evidence returned by settlement.
270
+ await deliver(
271
+ executionOptions,
272
+ campaign,
273
+ leased,
274
+ bindings,
275
+ settlement?.statement,
276
+ settlement ? { ...calculated, evidence: settlement.evidence } : calculated,
277
+ signer.signer,
278
+ )
279
+ const result = {
280
+ notification: await notificationSummary(options.db, campaign, leased.id),
281
+ runId: leased.id,
282
+ staleRates: calculated.evidence.staleRates ?? [],
283
+ status: 'delivered' as const,
284
+ targetAttempt,
285
+ }
286
+ const leaseFailure = await heartbeat.stop()
287
+ if (leaseFailure !== undefined) throw leaseFailure
288
+ return result
289
+ } catch (cause) {
290
+ await heartbeat.stop()
291
+ await RewardRuns.releaseWithError(options.db, {
292
+ error: cause instanceof Error ? cause.message : 'Reward run failed.',
293
+ fence: leased.fence,
294
+ id: leased.id,
295
+ })
296
+ throw cause
297
+ }
298
+ }
299
+
300
+ export declare namespace run {
301
+ /** Runtime dependencies injected by the API Worker. */
302
+ type Options = {
303
+ /** Authoritative Postgres connection. */
304
+ db: Db.Db
305
+ /** Reviewed rewards factory. */
306
+ factory: Address
307
+ /** Chain-aware RPC client factory. */
308
+ getClient: Viem.GetClient
309
+ /** Chain-aware TIDX client factory. */
310
+ getTidx: Tidx.GetClient
311
+ /** HTTP implementation for external rate sources. */
312
+ fetch: typeof globalThis.fetch
313
+ /** Compact queue reference. */
314
+ message: Message
315
+ /** Clock used to determine closed interval boundaries. */
316
+ now: () => number
317
+ /** Isolated signer service transport. */
318
+ signerFetch: typeof globalThis.fetch
319
+ /** Internal lease assertion installed after a run is acquired. */
320
+ assertLease?: (() => Promise<void>) | undefined
321
+ /** Hosted Zone metadata used to exclude infrastructure custody. */
322
+ zones: readonly {
323
+ contracts?: Record<string, unknown> | undefined
324
+ sourceId?: number | undefined
325
+ }[]
326
+ }
327
+
328
+ /** Observable queue outcome. */
329
+ type Result = {
330
+ /** Confirmed run details used by operational notifications. */
331
+ notification?: Notification | undefined
332
+ /** Durable run identity when work existed. */
333
+ runId?: string | undefined
334
+ /** Whether another worker owns the lease, no work was due, or delivery completed. */
335
+ status: 'busy' | 'delivered' | 'idle'
336
+ /** Rate sources that reused a bounded prior observation. */
337
+ staleRates?: readonly ('organic' | 'target')[] | undefined
338
+ /** Confirmed target-yield transaction, when one was needed. */
339
+ targetAttempt?: Hex | undefined
340
+ }
341
+
342
+ /** Concise confirmed run details used by operational notifications. */
343
+ type Notification = {
344
+ /** Base-asset decimals. */
345
+ assetDecimals: number
346
+ /** Higher of the organic and active target-yield rates at the closing boundary. */
347
+ baseRateBps: number
348
+ /** Incremental boost annual rate applied at the closing boundary. */
349
+ boostRateBps?: number | undefined
350
+ /** Number of accounting intervals covered by the run. */
351
+ intervalCount: number
352
+ /** Confirmed depositor payout, when this run pushed rewards. */
353
+ payout?: {
354
+ /** Recipients retaining an unpaid cumulative entitlement. */
355
+ deferredRecipients: number
356
+ /** Recipients receiving an EarnShare transfer during this run. */
357
+ paidRecipients: number
358
+ /** Confirmed payout transaction hashes. */
359
+ transactionHashes: readonly Hex[]
360
+ }
361
+ /** Confirmed boost settlement, when this run published a root. */
362
+ settlement?: {
363
+ /** Base-asset units represented by this root increment. */
364
+ assetAmount: string
365
+ /** Recipients receiving a positive allocation. */
366
+ creditedRecipients: number
367
+ /** Newly credited recipients retaining an unpaid entitlement after delivery. */
368
+ deferredRecipients: number
369
+ /** Confirmed root publication transaction hash. */
370
+ transactionHash: Hex
371
+ }
372
+ /** Target-yield evaluation for the run, including a confirmed funding transaction when needed. */
373
+ targetYield?: {
374
+ /** Base-asset units funded across configured funding legs. */
375
+ assetAmount: string
376
+ /** Confirmed funding transaction hash, omitted when no top-up was needed. */
377
+ transactionHash?: Hex | undefined
378
+ }
379
+ /** Curated vault label configured by an administrator. */
380
+ vaultLabel: string
381
+ }
382
+ }
383
+
384
+ type Bindings = {
385
+ controller?: Address | undefined
386
+ distributor?: Address | undefined
387
+ }
388
+
389
+ type Calculation = {
390
+ accounts: Projection.Account[]
391
+ closingCursor?: Campaigns.EventCursor | undefined
392
+ evidence: Campaigns.RunEvidence
393
+ }
394
+
395
+ function provisioningPlan(campaign: RewardCampaigns.Record): {
396
+ boostRewards: Campaigns.Config['boostRewards']
397
+ configMatch: Campaigns.Config
398
+ targetYield: Campaigns.Config['targetYield']
399
+ } {
400
+ const pending = campaign.pendingConfig
401
+ const needsPendingController = Boolean(pending?.targetYield && !campaign.controllerAddress)
402
+ const needsPendingDistributor = Boolean(pending?.boostRewards && !campaign.distributorAddress)
403
+ return {
404
+ boostRewards: campaign.config.boostRewards ?? pending?.boostRewards,
405
+ configMatch:
406
+ pending && (needsPendingController || needsPendingDistributor) ? pending : campaign.config,
407
+ targetYield: campaign.config.targetYield ?? pending?.targetYield,
408
+ }
409
+ }
410
+
411
+ async function provision(
412
+ options: run.Options,
413
+ campaign: RewardCampaigns.Record,
414
+ signer: Address,
415
+ ): Promise<Bindings> {
416
+ const plan = provisioningPlan(campaign)
417
+ if (campaign.signerAddress && !isAddressEqual(campaign.signerAddress, signer))
418
+ throw new Error('Reward signer does not match the campaign signer.')
419
+ const client = options.getClient(campaign.chainId)
420
+ const vault = getAddress(campaign.vaultAddress)
421
+ const treasury = plan.boostRewards?.treasury
422
+ const [predictedController, predictedDistributor] = await Promise.all([
423
+ plan.targetYield
424
+ ? client.readContract({
425
+ abi: factoryAbi,
426
+ address: options.factory,
427
+ args: [vault, signer],
428
+ functionName: 'predictContributionController',
429
+ })
430
+ : undefined,
431
+ plan.boostRewards && treasury
432
+ ? client.readContract({
433
+ abi: factoryAbi,
434
+ address: options.factory,
435
+ args: [vault, signer, treasury, claimDeadline],
436
+ functionName: 'predictMerkleDistributor',
437
+ })
438
+ : undefined,
439
+ ])
440
+ if (
441
+ campaign.controllerAddress &&
442
+ predictedController &&
443
+ !isAddressEqual(campaign.controllerAddress, predictedController)
444
+ )
445
+ throw new Error('Stored contribution controller does not match the campaign signer.')
446
+ if (
447
+ campaign.distributorAddress &&
448
+ predictedDistributor &&
449
+ !isAddressEqual(campaign.distributorAddress, predictedDistributor)
450
+ )
451
+ throw new Error('Stored Merkle distributor does not match the campaign signer.')
452
+ const controller = plan.targetYield
453
+ ? getAddress(campaign.controllerAddress ?? predictedController!)
454
+ : undefined
455
+ const distributor = plan.boostRewards
456
+ ? getAddress(campaign.distributorAddress ?? predictedDistributor!)
457
+ : undefined
458
+ const code = await Promise.all(
459
+ [controller, distributor]
460
+ .filter((address): address is Address => Boolean(address))
461
+ .map((address) => client.getCode({ address })),
462
+ )
463
+ if (code.some((runtime) => !runtime)) {
464
+ const intent = {
465
+ boostRewards: Boolean(plan.boostRewards),
466
+ domain: domain(options, campaign, {
467
+ ...(plan.boostRewards ? { boostRewards: plan.boostRewards } : {}),
468
+ ...(plan.targetYield ? { targetYield: plan.targetYield } : {}),
469
+ }),
470
+ operation: 'deploy' as const,
471
+ targetYield: Boolean(plan.targetYield),
472
+ treasury: treasury ?? '0x0000000000000000000000000000000000000000',
473
+ }
474
+ await execute(options, campaign, undefined, signer, intent)
475
+ }
476
+ for (const address of [controller, distributor])
477
+ if (address && !(await client.getCode({ address })))
478
+ throw new Error(`Reward periphery was not deployed at ${address}.`)
479
+ // A failed first deployment must not permanently bind a misconfigured signer.
480
+ const bound = await RewardCampaigns.bindSigner(options.db, {
481
+ chainId: campaign.chainId,
482
+ signerAddress: signer,
483
+ vaultAddress: vault,
484
+ })
485
+ if (!bound) throw new Error('Reward signer does not match the campaign signer.')
486
+ const stored = await RewardCampaigns.setBindings(options.db, {
487
+ chainId: campaign.chainId,
488
+ config: plan.configMatch,
489
+ ...(controller ? { controllerAddress: controller } : {}),
490
+ ...(distributor ? { distributorAddress: distributor } : {}),
491
+ error: null,
492
+ signerAddress: signer,
493
+ vaultAddress: vault,
494
+ })
495
+ if (!stored) throw new Error('Reward campaign changed during periphery provisioning.')
496
+ return {
497
+ ...(controller ? { controller } : {}),
498
+ ...(distributor ? { distributor } : {}),
499
+ }
500
+ }
501
+
502
+ async function calculate(
503
+ options: run.Options,
504
+ campaign: RewardCampaigns.Record,
505
+ runRecord: RewardRuns.Record,
506
+ bindings: Bindings,
507
+ ): Promise<Calculation> {
508
+ await requireTransition(options.db, runRecord, 'calculating', {})
509
+ const client = options.getClient(campaign.chainId)
510
+ const tidx = options.getTidx(campaign.chainId)
511
+ const startsAfter = Number(runRecord.startsAfter)
512
+ const endsAt = Number(runRecord.endsAt)
513
+ const intervalSeconds = Campaigns.intervalSeconds(campaign.config)
514
+ const openingBoundary = await Events.boundary(tidx, startsAfter)
515
+ const closingBoundary = await Events.boundary(tidx, endsAt)
516
+ const boost = campaign.config.boostRewards
517
+ const boostTracks = Boolean(
518
+ boost && Campaigns.overlapsCompleteIntervals(boost, { endsAt, startsAfter }),
519
+ )
520
+ const storedCheckpoint = await RewardRuns.getCheckpoint(options.db, { id: runRecord.id })
521
+ const eligible = boostTracks ? await allEligible(options.db, campaign) : []
522
+ const eligibilityCheckpoint = eligible.at(-1)?.registrationOrder ?? '0'
523
+ const eligibilityVersion = Campaigns.eligibilityVersion(eligible)
524
+ const excluded = boostTracks ? await exclusions(options, campaign, bindings) : []
525
+ const exclusionVersion = Campaigns.exclusionVersion(excluded)
526
+ const saved = resumeCheckpoint(storedCheckpoint, { eligibilityVersion, exclusionVersion })
527
+ const stored = await RewardAccounts.list(options.db, campaign)
528
+ let accounts = stored.map(Projection.fromRecord)
529
+ let startingCursor = campaign.eventCursor
530
+ if (boost && boostTracks) {
531
+ const excludedSet = new Set(excluded.map((address) => address.toLowerCase()))
532
+ const registrations = eligible
533
+ .filter((entry) => {
534
+ const address = entry.walletAddress.toLowerCase()
535
+ return !excludedSet.has(address)
536
+ })
537
+ .map((entry) => ({
538
+ recipient: entry.walletAddress,
539
+ registeredAt: entry.latestRegisteredAt,
540
+ registrationOrder: BigInt(entry.registrationOrder),
541
+ }))
542
+ const accountsByRecipient = new Map(
543
+ accounts.map((account) => [account.recipient.toLowerCase(), account]),
544
+ )
545
+ const reconcile = registrations.some((entry) => {
546
+ const account = accountsByRecipient.get(entry.recipient.toLowerCase())
547
+ return account?.eligibilityRegisteredAt !== entry.registeredAt
548
+ })
549
+ const replayRegistrations = registrations.filter((entry) => {
550
+ const account = accountsByRecipient.get(entry.recipient.toLowerCase())
551
+ return (
552
+ account?.eligibilityRegisteredAt !== entry.registeredAt &&
553
+ (!account ||
554
+ (account.allocatedPrincipalAssets === 0n &&
555
+ account.lots.length === 0 &&
556
+ account.qualifiedEarnShares === 0n))
557
+ )
558
+ })
559
+ if (reconcile && saved?.stage !== 'calculate') {
560
+ const resume = saved?.stage === 'reconcile' && saved.projection.pass ? saved : undefined
561
+ let cursor = resume?.projection.replayedThrough
562
+ const principalAllocationOrder =
563
+ stored.length === 0 && Number(campaign.deliveredThrough) === boost.startTimestamp
564
+ ? 'deposit'
565
+ : 'registration'
566
+ accounts = [
567
+ ...(
568
+ await Projection.reconcilePages({
569
+ accounts,
570
+ ...(resume
571
+ ? {
572
+ checkpoint: {
573
+ accounts: resume.projection.accounts,
574
+ ...(resume.projection.allocationRecipient
575
+ ? { allocationRecipient: resume.projection.allocationRecipient }
576
+ : {}),
577
+ ...(resume.projection.consumedEarnShares
578
+ ? { consumedEarnShares: resume.projection.consumedEarnShares }
579
+ : {}),
580
+ depositedEarnShares: resume.projection.depositedEarnShares ?? {},
581
+ ...(resume.cursor ? { cursor: resume.cursor } : {}),
582
+ pass: resume.projection.pass!,
583
+ ...(resume.projection.replayedThrough
584
+ ? { replayedThrough: resume.projection.replayedThrough }
585
+ : {}),
586
+ },
587
+ }
588
+ : {}),
589
+ checkpointState: async (projection) => {
590
+ cursor = projection.replayedThrough
591
+ const persisted = await RewardRuns.writeCheckpoint(options.db, {
592
+ evidence: {
593
+ ...(projection.cursor ? { cursor: projection.cursor } : {}),
594
+ kind: 'checkpoint',
595
+ progress: { eligibilityCheckpoint, eligibilityVersion, exclusionVersion },
596
+ projection,
597
+ stage: 'reconcile',
598
+ },
599
+ fence: runRecord.fence,
600
+ id: runRecord.id,
601
+ })
602
+ if (!persisted) throw new Error('Reward run lease was lost.')
603
+ },
604
+ eligible: registrations,
605
+ excluded,
606
+ pages: (pass, after) =>
607
+ pass === 'survival' || principalAllocationOrder === 'deposit'
608
+ ? reconciliationEventPages(
609
+ Events.accountEventPages(tidx, {
610
+ ...(after ? { after } : {}),
611
+ accounts: replayRegistrations.map((entry) => entry.recipient),
612
+ earnShare: campaign.earnShareAddress,
613
+ earnVault: campaign.vaultAddress,
614
+ throughBlock: openingBoundary.blockNumber,
615
+ }),
616
+ )
617
+ : registrationDepositPages(tidx, {
618
+ ...(after ? { after } : {}),
619
+ ...(resume?.projection.allocationRecipient
620
+ ? { afterRecipient: resume.projection.allocationRecipient }
621
+ : {}),
622
+ accounts: replayRegistrations,
623
+ earnVault: campaign.vaultAddress,
624
+ throughBlock: openingBoundary.blockNumber,
625
+ }),
626
+ perUserPrincipalCapAssets: BigInt(boost.perUserPrincipalCapAssets),
627
+ // Initial allocation follows deposits; later registrations share the remaining cap by registration order.
628
+ principalAllocationOrder,
629
+ totalPrincipalCapAssets: BigInt(boost.totalPrincipalCapAssets),
630
+ })
631
+ ).accounts,
632
+ ]
633
+ startingCursor = reconciledCursor({ committed: startingCursor, replayed: cursor })
634
+ }
635
+ }
636
+ const targetConfig = campaign.config.targetYield
637
+ const targetActive = Boolean(
638
+ targetConfig && Campaigns.overlapsCompleteIntervals(targetConfig, { endsAt, startsAfter }),
639
+ )
640
+ if (saved?.stage === 'calculate') {
641
+ accounts = saved.projection.accounts.map(Projection.fromSnapshot)
642
+ startingCursor = saved.cursor ?? startingCursor
643
+ }
644
+ let eventCursor =
645
+ boost && accounts.length > 0
646
+ ? startingCursor
647
+ : {
648
+ blockNumber: openingBoundary.blockNumber,
649
+ logIndex: Number.MAX_SAFE_INTEGER,
650
+ transactionIndex: Number.MAX_SAFE_INTEGER,
651
+ }
652
+ const capitalChanges = targetActive
653
+ ? await Events.capitalChanges(tidx, {
654
+ afterBlock: openingBoundary.blockNumber,
655
+ earnShare: campaign.earnShareAddress,
656
+ throughBlock: closingBoundary.blockNumber,
657
+ })
658
+ : []
659
+ const boostAccrues = Boolean(
660
+ boost &&
661
+ boost.enabled !== false &&
662
+ Campaigns.overlapsCompleteIntervals(boost, { endsAt, startsAfter }),
663
+ )
664
+ // Carry external target observations and exact funding remainders through inactive intervals
665
+ // so a later schedule resumption reads the latest durable evidence.
666
+ const previous = await RewardRuns.latestDelivered(options.db, campaign)
667
+ const target = targetActive
668
+ ? (checkpointTargetRate(saved, targetConfig!) ??
669
+ (await targetRate(options, campaign, runRecord)))
670
+ : undefined
671
+ const targetRateProgress = target
672
+ ? {
673
+ ...(target.stale ? { staleRates: ['target' as const] } : {}),
674
+ targetRateBps: target.annualRateBps,
675
+ targetRateObservedAt: target.observedAt,
676
+ }
677
+ : {}
678
+ const contributions =
679
+ target || boostAccrues
680
+ ? await Events.contributions(tidx, {
681
+ afterBlock: openingBoundary.blockNumber,
682
+ earnVault: campaign.vaultAddress,
683
+ throughBlock: closingBoundary.blockNumber,
684
+ })
685
+ : []
686
+ let latestBoostRate = saved?.progress.boostRateBps ?? 0
687
+ type RateObservation = { observedAt: string; rate: number }
688
+ let latestOrganicRate: RateObservation | undefined =
689
+ saved?.progress.organicRateBps !== undefined && saved.progress.organicRateObservedAt
690
+ ? {
691
+ observedAt: saved.progress.organicRateObservedAt,
692
+ rate: saved.progress.organicRateBps,
693
+ }
694
+ : undefined
695
+ const intervals: Campaigns.RunEvidence['intervals'][number][] = [
696
+ ...(saved?.progress.intervals ?? []),
697
+ ]
698
+ const targetAssets = target
699
+ ? (saved?.progress.targetFundingAssets?.map(BigInt) ?? target.config.funding.map(() => 0n))
700
+ : []
701
+ const carryTargetRemainders = Boolean(
702
+ targetConfig && sameTargetFunding(previous?.config.targetYield?.funding, targetConfig.funding),
703
+ )
704
+ const targetRemainders = targetConfig
705
+ ? (saved?.progress.targetFundingRemainders?.map(BigInt) ??
706
+ targetConfig.funding.map((_, index) =>
707
+ BigInt(
708
+ carryTargetRemainders ? (previous?.evidence?.targetFundingRemainders?.[index] ?? 0) : 0,
709
+ ),
710
+ ))
711
+ : []
712
+ let projectionCheckpoint = saved?.stage === 'calculate' ? saved : undefined
713
+ for (
714
+ let end = startsAfter + (intervals.length + 1) * intervalSeconds;
715
+ end <= endsAt;
716
+ end += intervalSeconds
717
+ ) {
718
+ const start = end - intervalSeconds
719
+ const intervalOpening =
720
+ start === startsAfter ? openingBoundary : await Events.boundary(tidx, start)
721
+ const boundary = end === endsAt ? closingBoundary : await Events.boundary(tidx, end)
722
+ const sharePrice = await client.readContract({
723
+ abi: vaultAbi,
724
+ address: campaign.vaultAddress,
725
+ args: [10n ** 18n],
726
+ blockNumber: BigInt(intervalOpening.blockNumber),
727
+ functionName: 'previewRedeem',
728
+ })
729
+ const closingSharePrice = await client.readContract({
730
+ abi: vaultAbi,
731
+ address: campaign.vaultAddress,
732
+ args: [10n ** 18n],
733
+ blockNumber: BigInt(boundary.blockNumber),
734
+ functionName: 'previewRedeem',
735
+ })
736
+ const targetIntervalActive = Boolean(
737
+ target && end > target.config.startTimestamp && end <= target.config.endTimestamp,
738
+ )
739
+ const boostActive = Boolean(
740
+ boost && boost.enabled !== false && end > boost.startTimestamp && end <= boost.endTimestamp,
741
+ )
742
+ const intervalContributions = contributions
743
+ .filter((entry) => entry.timestamp >= start && entry.timestamp < end)
744
+ .map((entry) => ({ assets: entry.assets, earnShareSupply: entry.earnShareSupply }))
745
+ const growth =
746
+ targetIntervalActive || boostActive
747
+ ? await (async () => {
748
+ const [totalAssets, openingEarnShareSupply] = await Promise.all([
749
+ client.readContract({
750
+ abi: vaultAbi,
751
+ address: campaign.vaultAddress,
752
+ blockNumber: BigInt(intervalOpening.blockNumber),
753
+ functionName: 'totalAssets',
754
+ }),
755
+ feeInclusiveEarnShareSupply(
756
+ client,
757
+ campaign.vaultAddress,
758
+ BigInt(intervalOpening.blockNumber),
759
+ ),
760
+ ])
761
+ const assets = Campaigns.organicGrowth({
762
+ closingValuePerEarnShare: closingSharePrice,
763
+ contributions: intervalContributions,
764
+ openingAssets: totalAssets,
765
+ openingEarnShareSupply,
766
+ openingValuePerEarnShare: sharePrice,
767
+ })
768
+ return {
769
+ annualRateBps: Campaigns.annualRateBps({
770
+ elapsedSeconds: intervalSeconds,
771
+ growthAssets: assets,
772
+ principalAssets: totalAssets,
773
+ }),
774
+ opening: {
775
+ openingAssets: totalAssets,
776
+ openingEarnShareSupply,
777
+ openingValuePerEarnShare: sharePrice,
778
+ },
779
+ }
780
+ })()
781
+ : undefined
782
+ if (boostActive && growth)
783
+ latestOrganicRate = {
784
+ observedAt: new Date(end * 1_000).toISOString(),
785
+ rate: growth.annualRateBps,
786
+ }
787
+ const intervalBoostRate =
788
+ boost && boostActive
789
+ ? Campaigns.rewardRates({
790
+ boostTargetRateBps: boost.targetAnnualRateBps,
791
+ organicRateBps: growth?.annualRateBps ?? 0,
792
+ ...(targetIntervalActive ? { targetRateBps: target!.annualRateBps } : {}),
793
+ }).boostRateBps
794
+ : 0
795
+ if (boostActive) latestBoostRate = intervalBoostRate
796
+ const resume = projectionCheckpoint
797
+ const pages =
798
+ boost && accounts.length > 0
799
+ ? Events.accountEventPages(tidx, {
800
+ ...(eventCursor ? { after: eventCursor } : {}),
801
+ accounts: accounts.map((account) => account.recipient),
802
+ earnShare: campaign.earnShareAddress,
803
+ earnVault: campaign.vaultAddress,
804
+ throughBlock: boundary.blockNumber,
805
+ })
806
+ : emptyEventPages()
807
+ const result = boost
808
+ ? await Projection.calculatePages({
809
+ accounts,
810
+ annualRateBps: intervalBoostRate,
811
+ assetPerEarnShareWad: sharePrice,
812
+ ...(resume
813
+ ? {
814
+ checkpoint: {
815
+ accounts: resume.projection.accounts,
816
+ accruedThrough: resume.projection.accruedThrough ?? {},
817
+ weightedEarnShareSeconds: resume.projection.weightedEarnShareSeconds ?? {},
818
+ },
819
+ }
820
+ : {}),
821
+ checkpointState: async (projection, cursor) => {
822
+ eventCursor = cursor
823
+ const persisted = await RewardRuns.writeCheckpoint(options.db, {
824
+ evidence: {
825
+ cursor,
826
+ kind: 'checkpoint',
827
+ progress: {
828
+ boostRateBps: latestBoostRate,
829
+ eligibilityCheckpoint,
830
+ eligibilityVersion,
831
+ exclusionVersion,
832
+ intervals,
833
+ ...(latestOrganicRate
834
+ ? {
835
+ organicRateBps: latestOrganicRate.rate,
836
+ organicRateObservedAt: latestOrganicRate.observedAt,
837
+ }
838
+ : {}),
839
+ ...targetRateProgress,
840
+ targetFundingAssets: targetAssets.map(String),
841
+ targetFundingRemainders: targetRemainders.map(String),
842
+ },
843
+ projection,
844
+ stage: 'calculate',
845
+ },
846
+ fence: runRecord.fence,
847
+ id: runRecord.id,
848
+ })
849
+ if (!persisted) throw new Error('Reward run lease was lost.')
850
+ },
851
+ endsAt: end,
852
+ excluded,
853
+ pages,
854
+ perUserPrincipalCapAssets: BigInt(boost.perUserPrincipalCapAssets),
855
+ startsAt: start,
856
+ totalPrincipalCapAssets: BigInt(boost.totalPrincipalCapAssets),
857
+ })
858
+ : { accounts, rewards: new Map<Address, bigint>() }
859
+ projectionCheckpoint = undefined
860
+ accounts = [...result.accounts]
861
+ intervals.push({
862
+ assetPerEarnShareWad: sharePrice.toString(),
863
+ assetRewards: Object.fromEntries(
864
+ [...result.rewards].map(([recipient, amount]) => [
865
+ recipient.toLowerCase(),
866
+ amount.toString(),
867
+ ]),
868
+ ),
869
+ blockNumber: boundary.blockNumber,
870
+ ...(boost ? { boostRateBps: intervalBoostRate } : {}),
871
+ endsAt: end,
872
+ ...(growth ? { organicRateBps: growth.annualRateBps } : {}),
873
+ startsAt: start,
874
+ })
875
+ if (target && targetIntervalActive) {
876
+ if (!growth) throw new Error('Target-yield interval lacks organic growth evidence.')
877
+ const periods: Campaigns.targetFunding.Period[] = []
878
+ let opening = {
879
+ ...growth.opening,
880
+ blockNumber: intervalOpening.blockNumber,
881
+ timestamp: start,
882
+ }
883
+ // Read post-change principal so a boundary deposit earns only until its redemption block.
884
+ const states = await capitalStates(client, {
885
+ boundaries: capitalChanges.filter(
886
+ (entry) => entry.timestamp >= start && entry.timestamp < end,
887
+ ),
888
+ vaultAddress: campaign.vaultAddress,
889
+ })
890
+ for (const closing of [
891
+ ...states,
892
+ {
893
+ ...growth.opening,
894
+ blockNumber: boundary.blockNumber,
895
+ openingValuePerEarnShare: closingSharePrice,
896
+ timestamp: end,
897
+ },
898
+ ]) {
899
+ periods.push({
900
+ closingValuePerEarnShare: closing.openingValuePerEarnShare,
901
+ contributions: contributions.filter(
902
+ (entry) =>
903
+ entry.cursor.blockNumber > opening.blockNumber &&
904
+ entry.cursor.blockNumber <= closing.blockNumber,
905
+ ),
906
+ elapsedSeconds: closing.timestamp - opening.timestamp,
907
+ openingAssets: opening.openingAssets,
908
+ openingEarnShareSupply: opening.openingEarnShareSupply,
909
+ openingValuePerEarnShare: opening.openingValuePerEarnShare,
910
+ })
911
+ opening = closing
912
+ }
913
+ const funding = Campaigns.targetFunding({
914
+ annualRateBps: target.annualRateBps,
915
+ funding: target.config.funding,
916
+ periods,
917
+ remainders: targetRemainders,
918
+ })
919
+ funding.assets.forEach((amount, index) => {
920
+ targetAssets[index] = (targetAssets[index] ?? 0n) + amount
921
+ targetRemainders[index] = funding.remainders[index]!
922
+ })
923
+ }
924
+ const persisted = await RewardRuns.writeCheckpoint(options.db, {
925
+ evidence: {
926
+ ...(eventCursor ? { cursor: eventCursor } : {}),
927
+ kind: 'checkpoint',
928
+ progress: {
929
+ boostRateBps: latestBoostRate,
930
+ eligibilityCheckpoint,
931
+ eligibilityVersion,
932
+ exclusionVersion,
933
+ intervals,
934
+ ...(latestOrganicRate
935
+ ? {
936
+ organicRateBps: latestOrganicRate.rate,
937
+ organicRateObservedAt: latestOrganicRate.observedAt,
938
+ }
939
+ : {}),
940
+ ...targetRateProgress,
941
+ targetFundingAssets: targetAssets.map(String),
942
+ targetFundingRemainders: targetRemainders.map(String),
943
+ },
944
+ projection: { accounts: accounts.map(Projection.snapshot) },
945
+ stage: 'calculate',
946
+ },
947
+ fence: runRecord.fence,
948
+ id: runRecord.id,
949
+ })
950
+ if (!persisted) throw new Error('Reward run lease was lost.')
951
+ }
952
+ const priorTarget = (() => {
953
+ if (
954
+ !targetConfig ||
955
+ target ||
956
+ previous?.evidence?.targetRateBps === undefined ||
957
+ !previous.evidence.targetRateObservedAt ||
958
+ !sameRateSource(previous.config.targetYield?.annualRate, targetConfig.annualRate)
959
+ )
960
+ return undefined
961
+ return {
962
+ observedAt: previous.evidence.targetRateObservedAt,
963
+ rate: previous.evidence.targetRateBps,
964
+ }
965
+ })()
966
+ const targetEvidence = (() => {
967
+ if (target)
968
+ return {
969
+ appliedTargetRateBps: target.annualRateBps,
970
+ targetFundingAssets: targetAssets.map(String),
971
+ targetFundingRemainders: targetRemainders.map(String),
972
+ targetPrincipalMethod: 'time-weighted:v1' as const,
973
+ targetRateBps: target.annualRateBps,
974
+ targetRateObservedAt: target.observedAt,
975
+ }
976
+ if (!targetConfig) return {}
977
+ return {
978
+ targetFundingRemainders: targetRemainders.map(String),
979
+ ...(priorTarget
980
+ ? {
981
+ targetRateBps: priorTarget.rate,
982
+ targetRateObservedAt: priorTarget.observedAt,
983
+ }
984
+ : {}),
985
+ }
986
+ })()
987
+ const evidence: Campaigns.RunEvidence = {
988
+ ...(boost ? { boostRateBps: latestBoostRate } : {}),
989
+ closingAccounts: accounts.map(Projection.snapshot),
990
+ eligibilityCheckpoint,
991
+ eligibilityVersion,
992
+ exclusionVersion,
993
+ ...(eventCursor ? { eventCursor } : {}),
994
+ intervals,
995
+ ...(target?.stale
996
+ ? {
997
+ staleRates: ['target' as const],
998
+ }
999
+ : {}),
1000
+ ...(latestOrganicRate
1001
+ ? {
1002
+ organicRateBps: latestOrganicRate.rate,
1003
+ organicRateObservedAt: latestOrganicRate.observedAt,
1004
+ }
1005
+ : {}),
1006
+ stateChecksum: Projection.checksum(accounts),
1007
+ ...targetEvidence,
1008
+ }
1009
+ await requireTransition(options.db, runRecord, 'targetYield', { evidence })
1010
+ return { accounts, ...(eventCursor ? { closingCursor: eventCursor } : {}), evidence }
1011
+ }
1012
+
1013
+ async function settleTargetYield(
1014
+ options: run.Options,
1015
+ campaign: RewardCampaigns.Record,
1016
+ runRecord: RewardRuns.Record,
1017
+ bindings: Bindings,
1018
+ calculation: Calculation,
1019
+ signer: Address,
1020
+ ): Promise<Hex | undefined> {
1021
+ const target = campaign.config.targetYield
1022
+ if (!target || !bindings.controller) return undefined
1023
+ const requestedAssets = Campaigns.targetFundingAssets(calculation.evidence)
1024
+ if (requestedAssets.every((amount) => amount === 0n)) return undefined
1025
+ const active = target.funding.flatMap((leg, index) => {
1026
+ const amount = requestedAssets[index] ?? 0n
1027
+ return amount > 0n ? [{ amount, wallet: leg.wallet }] : []
1028
+ })
1029
+ const previous = await RewardTransactionAttempts.latestRecoverable(options.db, {
1030
+ operation: 'targetYield',
1031
+ runId: runRecord.id,
1032
+ })
1033
+ const intent = previous?.signedBytes
1034
+ ? Signer.schema.Request.parse({
1035
+ attemptId: previous.id,
1036
+ id: previous.intentId,
1037
+ intent: previous.intent.payload,
1038
+ }).intent
1039
+ : {
1040
+ controller: bindings.controller,
1041
+ domain: domain(options, campaign),
1042
+ funders: active.map((leg) => leg.wallet),
1043
+ maxEarnShareSupply: (
1044
+ await feeInclusiveEarnShareSupply(
1045
+ options.getClient(campaign.chainId),
1046
+ campaign.vaultAddress,
1047
+ )
1048
+ ).toString(),
1049
+ operation: 'targetYield' as const,
1050
+ requestedAssets: active.map((leg) => leg.amount.toString()),
1051
+ }
1052
+ if (intent.operation !== 'targetYield')
1053
+ throw new Error('Stored target-yield attempt contains another operation.')
1054
+ const attempt = await execute(options, campaign, runRecord.id, signer, intent)
1055
+ if (!attempt.transactionHash) throw new Error('Target-yield transaction lacks a hash.')
1056
+ const receipt = await options
1057
+ .getClient(campaign.chainId)
1058
+ .getTransactionReceipt({ hash: attempt.transactionHash })
1059
+ const funded = parseEventLogs({
1060
+ abi: controllerAbi,
1061
+ eventName: 'Funded',
1062
+ logs: receipt.logs,
1063
+ strict: true,
1064
+ }).filter((event) => isAddressEqual(event.address, bindings.controller!))
1065
+ const exact =
1066
+ funded.length === active.length &&
1067
+ funded.every((event, index) => {
1068
+ const expected = active[index]!
1069
+ return (
1070
+ isAddressEqual(event.args.funder, expected.wallet) &&
1071
+ event.args.requestedAssets === expected.amount &&
1072
+ event.args.fundedAssets === expected.amount
1073
+ )
1074
+ })
1075
+ if (!exact) {
1076
+ const retryableZeroFunding =
1077
+ active.length === 1 &&
1078
+ funded.length === 1 &&
1079
+ isAddressEqual(funded[0]!.args.funder, active[0]!.wallet) &&
1080
+ funded[0]!.args.requestedAssets === active[0]!.amount &&
1081
+ funded[0]!.args.fundedAssets === 0n
1082
+ if (retryableZeroFunding) {
1083
+ const ineffective = await RewardTransactionAttempts.markIneffective(options.db, attempt.id)
1084
+ if (!ineffective)
1085
+ throw new Error('Confirmed zero target-yield funding could not be marked for retry.')
1086
+ throw new Error('Target-yield funding settled zero assets and will be retried.')
1087
+ }
1088
+ throw new Error('Target-yield funding did not settle every requested asset exactly.')
1089
+ }
1090
+ return attempt.transactionHash ?? undefined
1091
+ }
1092
+
1093
+ /** Reads historical capital states in bounded waves, preserving order and rejecting incomplete evidence. */
1094
+ export async function capitalStates(
1095
+ client: Pick<Viem.getClient.ReturnType, 'readContract'>,
1096
+ options: capitalStates.Options,
1097
+ ): Promise<capitalStates.State[]> {
1098
+ const states: capitalStates.State[] = []
1099
+ for (let offset = 0; offset < options.boundaries.length; offset += capitalStateBatchSize) {
1100
+ const results = await Promise.allSettled(
1101
+ options.boundaries.slice(offset, offset + capitalStateBatchSize).map(async (boundary) => {
1102
+ const blockNumber = BigInt(boundary.blockNumber)
1103
+ const reads = await Promise.allSettled([
1104
+ client.readContract({
1105
+ abi: vaultAbi,
1106
+ address: options.vaultAddress,
1107
+ blockNumber,
1108
+ functionName: 'totalAssets',
1109
+ }),
1110
+ feeInclusiveEarnShareSupply(client, options.vaultAddress, blockNumber),
1111
+ client.readContract({
1112
+ abi: vaultAbi,
1113
+ address: options.vaultAddress,
1114
+ args: [10n ** 18n],
1115
+ blockNumber,
1116
+ functionName: 'previewRedeem',
1117
+ }),
1118
+ ])
1119
+ const [openingAssets, openingEarnShareSupply, openingValuePerEarnShare] = reads.map(
1120
+ (result) => {
1121
+ if (result.status === 'rejected') throw result.reason
1122
+ return result.value
1123
+ },
1124
+ )
1125
+ return {
1126
+ ...boundary,
1127
+ openingAssets: openingAssets!,
1128
+ openingEarnShareSupply: openingEarnShareSupply!,
1129
+ openingValuePerEarnShare: openingValuePerEarnShare!,
1130
+ }
1131
+ }),
1132
+ )
1133
+ for (const result of results) {
1134
+ if (result.status === 'rejected') throw result.reason
1135
+ states.push(result.value)
1136
+ }
1137
+ }
1138
+ return states
1139
+ }
1140
+
1141
+ export declare namespace capitalStates {
1142
+ /** Historical blocks required for one accounting interval. */
1143
+ type Options = {
1144
+ /** Ordered supply-change boundaries. */
1145
+ boundaries: readonly Events.boundary.Result[]
1146
+ /** Vault whose principal and fee-inclusive conversion are sampled. */
1147
+ vaultAddress: Address
1148
+ }
1149
+
1150
+ /** Historical principal and conversion at a supply-change boundary. */
1151
+ type State = Events.boundary.Result &
1152
+ Omit<Campaigns.organicGrowth.Options, 'closingValuePerEarnShare' | 'contributions'>
1153
+ }
1154
+
1155
+ async function feeInclusiveEarnShareSupply(
1156
+ client: Pick<Viem.getClient.ReturnType, 'readContract'>,
1157
+ vault: Address,
1158
+ blockNumber?: bigint,
1159
+ ): Promise<bigint> {
1160
+ const [earnFees, supply] = await Promise.all([
1161
+ client.readContract({ abi: vaultAbi, address: vault, blockNumber, functionName: 'earnFees' }),
1162
+ client.readContract({
1163
+ abi: vaultAbi,
1164
+ address: vault,
1165
+ blockNumber,
1166
+ functionName: 'totalEarnShares',
1167
+ }),
1168
+ ])
1169
+ const preview = await client.readContract({
1170
+ abi: feesAbi,
1171
+ address: earnFees,
1172
+ blockNumber,
1173
+ functionName: 'previewAccruedFees',
1174
+ })
1175
+ return supply + preview.totalFeeEarnShares
1176
+ }
1177
+
1178
+ async function settleBoost(
1179
+ options: run.Options,
1180
+ campaign: RewardCampaigns.Record,
1181
+ runRecord: RewardRuns.Record,
1182
+ bindings: Bindings,
1183
+ calculation: Calculation,
1184
+ signer: Address,
1185
+ ): Promise<{ evidence: Campaigns.RunEvidence; statement: Campaigns.Statement } | undefined> {
1186
+ const boost = campaign.config.boostRewards
1187
+ if (!boost || !bindings.distributor) {
1188
+ await commit(options.db, campaign, runRecord, calculation.accounts, calculation.closingCursor)
1189
+ return undefined
1190
+ }
1191
+ if (runRecord.phase === 'statement')
1192
+ return publishPreparedStatement(options, campaign, runRecord, bindings.distributor, signer)
1193
+ const rewardAssets = calculation.accounts.reduce(
1194
+ (sum, account) => sum + account.pendingRewardAssets,
1195
+ 0n,
1196
+ )
1197
+ if (rewardAssets <= 1n) {
1198
+ const hasDeferred = calculation.accounts.some(
1199
+ (account) => account.cumulativePaid < account.cumulativeEntitlement,
1200
+ )
1201
+ const previous = hasDeferred
1202
+ ? await RewardRuns.latestStatement(options.db, campaign)
1203
+ : undefined
1204
+ if (
1205
+ previous?.statement &&
1206
+ previous.root &&
1207
+ previous.rootVersion &&
1208
+ previous.statementHash &&
1209
+ previous.liability
1210
+ ) {
1211
+ const evidence = {
1212
+ ...calculation.evidence,
1213
+ closingAccounts: calculation.accounts.map(Projection.snapshot),
1214
+ }
1215
+ await requireTransition(options.db, runRecord, 'paying', {
1216
+ evidence,
1217
+ fundedAssets: '0',
1218
+ liability: previous.liability,
1219
+ mintedEarnShares: '0',
1220
+ root: previous.root,
1221
+ rootVersion: previous.rootVersion,
1222
+ statement: previous.statement,
1223
+ statementHash: previous.statementHash,
1224
+ })
1225
+ return { evidence, statement: previous.statement }
1226
+ }
1227
+ await commit(options.db, campaign, runRecord, calculation.accounts, calculation.closingCursor)
1228
+ return undefined
1229
+ }
1230
+ const client = options.getClient(campaign.chainId)
1231
+ const quote = await vaultQuote(client, campaign.vaultAddress)
1232
+ const desiredShares = Campaigns.assetsToEarnShares({ assets: rewardAssets, ...quote })
1233
+ // Preserve sub-share rewards until enough assets accrue to mint at least one EarnShare.
1234
+ if (desiredShares === 0n) {
1235
+ await commit(options.db, campaign, runRecord, calculation.accounts, calculation.closingCursor)
1236
+ return undefined
1237
+ }
1238
+ const allocation = Campaigns.allocate({
1239
+ earnShares: desiredShares,
1240
+ rewards: calculation.accounts.map((account) => ({
1241
+ assets: account.pendingRewardAssets,
1242
+ recipient: account.recipient,
1243
+ })),
1244
+ })
1245
+ const settlementRecipientAddresses = [...allocation.entries()]
1246
+ .filter(([, amount]) => amount > 0n)
1247
+ .map(([recipient]) => recipient)
1248
+ for (const account of calculation.accounts) {
1249
+ account.cumulativeEntitlement += allocation.get(account.recipient) ?? 0n
1250
+ account.pendingRewardAssets = 0n
1251
+ }
1252
+ const statement = Campaigns.buildStatement({
1253
+ campaignId: campaignId(campaign.vaultAddress),
1254
+ chainId: campaign.chainId,
1255
+ distributor: bindings.distributor,
1256
+ entitlements: calculation.accounts
1257
+ .filter((account) => account.cumulativeEntitlement > 0n)
1258
+ .map((account) => ({
1259
+ cumulativeAmount: account.cumulativeEntitlement.toString(),
1260
+ recipient: account.recipient,
1261
+ })),
1262
+ })
1263
+ const [version, entitlement, paid, inventory] = await Promise.all([
1264
+ client.readContract({
1265
+ abi: distributorAbi,
1266
+ address: bindings.distributor,
1267
+ functionName: 'rootVersion',
1268
+ }),
1269
+ client.readContract({
1270
+ abi: distributorAbi,
1271
+ address: bindings.distributor,
1272
+ functionName: 'rootTotalEntitlement',
1273
+ }),
1274
+ client.readContract({
1275
+ abi: distributorAbi,
1276
+ address: bindings.distributor,
1277
+ functionName: 'totalPaid',
1278
+ }),
1279
+ client.readContract({
1280
+ abi: erc20Abi,
1281
+ address: campaign.earnShareAddress,
1282
+ args: [bindings.distributor],
1283
+ functionName: 'balanceOf',
1284
+ }),
1285
+ ])
1286
+ if (paid > entitlement)
1287
+ throw new Error('Reward distributor paid total exceeds its published entitlement.')
1288
+ const outstanding = entitlement - paid
1289
+ if (inventory < outstanding)
1290
+ throw new Error('Reward distributor inventory is below its current unpaid liability.')
1291
+ const unreserved = inventory - outstanding
1292
+ const missingEarnShares = desiredShares > unreserved ? desiredShares - unreserved : 0n
1293
+ const fundingEarnShares = Campaigns.settlementFundingEarnShares(missingEarnShares)
1294
+ const fundedAssets =
1295
+ fundingEarnShares > 0n
1296
+ ? Campaigns.earnSharesToAssets({ earnShares: fundingEarnShares, ...quote })
1297
+ : 0n
1298
+ const prepared = await requireTransition(options.db, runRecord, 'statement', {
1299
+ evidence: {
1300
+ ...calculation.evidence,
1301
+ closingAccounts: calculation.accounts.map(Projection.snapshot),
1302
+ publishedRoot: true,
1303
+ settledRewardAssets: rewardAssets.toString(),
1304
+ settlementRecipientAddresses,
1305
+ settlementRecipients: settlementRecipientAddresses.length,
1306
+ },
1307
+ fundedAssets: fundedAssets.toString(),
1308
+ liability: statement.totalEntitlement,
1309
+ mintedEarnShares: missingEarnShares.toString(),
1310
+ root: statement.root,
1311
+ rootVersion: version.toString(),
1312
+ statement,
1313
+ statementHash: statement.statementHash,
1314
+ })
1315
+ return publishPreparedStatement(options, campaign, prepared, bindings.distributor, signer)
1316
+ }
1317
+
1318
+ async function publishPreparedStatement(
1319
+ options: run.Options,
1320
+ campaign: RewardCampaigns.Record,
1321
+ runRecord: RewardRuns.Record,
1322
+ distributor: Address,
1323
+ signer: Address,
1324
+ ): Promise<{ evidence: Campaigns.RunEvidence; statement: Campaigns.Statement }> {
1325
+ runRecord = await refreshPreparedStatement(options, campaign, runRecord, distributor)
1326
+ const { evidence, fundedAssets, mintedEarnShares, rootVersion, statement } = runRecord
1327
+ if (
1328
+ !evidence ||
1329
+ !statement ||
1330
+ fundedAssets === null ||
1331
+ mintedEarnShares === null ||
1332
+ rootVersion === null
1333
+ )
1334
+ throw new Error('Prepared reward statement is incomplete.')
1335
+ const publish = {
1336
+ distributor,
1337
+ domain: domain(options, campaign),
1338
+ expectedRootVersion: rootVersion,
1339
+ operation: 'publish' as const,
1340
+ statement,
1341
+ }
1342
+ const intent: Signer.Request['intent'] =
1343
+ BigInt(fundedAssets) === 0n
1344
+ ? publish
1345
+ : {
1346
+ assets: fundedAssets,
1347
+ distributor,
1348
+ domain: publish.domain,
1349
+ expectedRootVersion: rootVersion,
1350
+ funder: campaign.config.boostRewards!.funding.wallet,
1351
+ minEarnShares: mintedEarnShares,
1352
+ operation: 'settle',
1353
+ settlementId: Signer.id(publish),
1354
+ statement,
1355
+ }
1356
+ const attempt = await execute(options, campaign, runRecord.id, signer, intent)
1357
+ if (!attempt.transactionHash) throw new Error('Reward root transaction lacks a hash.')
1358
+ const client = options.getClient(campaign.chainId)
1359
+ const [confirmedVersion, confirmedRoot, confirmedStatementHash, confirmedEntitlement] =
1360
+ await Promise.all([
1361
+ client.readContract({
1362
+ abi: distributorAbi,
1363
+ address: distributor,
1364
+ functionName: 'rootVersion',
1365
+ }),
1366
+ client.readContract({
1367
+ abi: distributorAbi,
1368
+ address: distributor,
1369
+ functionName: 'merkleRoot',
1370
+ }),
1371
+ client.readContract({
1372
+ abi: distributorAbi,
1373
+ address: distributor,
1374
+ functionName: 'statementHash',
1375
+ }),
1376
+ client.readContract({
1377
+ abi: distributorAbi,
1378
+ address: distributor,
1379
+ functionName: 'rootTotalEntitlement',
1380
+ }),
1381
+ ])
1382
+ if (
1383
+ confirmedVersion !== BigInt(rootVersion) + 1n ||
1384
+ confirmedRoot !== statement.root ||
1385
+ confirmedStatementHash !== statement.statementHash ||
1386
+ confirmedEntitlement !== BigInt(statement.totalEntitlement)
1387
+ )
1388
+ throw new Error('Confirmed reward statement does not match the prepared root.')
1389
+ await requireTransition(options.db, runRecord, 'paying', {
1390
+ error: null,
1391
+ rootVersion: confirmedVersion.toString(),
1392
+ })
1393
+ return { evidence, statement }
1394
+ }
1395
+
1396
+ async function refreshPreparedStatement(
1397
+ options: run.Options,
1398
+ campaign: RewardCampaigns.Record,
1399
+ runRecord: RewardRuns.Record,
1400
+ distributor: Address,
1401
+ ): Promise<RewardRuns.Record> {
1402
+ const attempts = await RewardTransactionAttempts.listForRun(options.db, runRecord.id)
1403
+ if (
1404
+ attempts.some(
1405
+ (attempt) =>
1406
+ (attempt.intent.operation === 'publish' || attempt.intent.operation === 'settle') &&
1407
+ ['signed', 'broadcast', 'confirmed'].includes(attempt.state),
1408
+ )
1409
+ )
1410
+ return runRecord
1411
+ if (!runRecord.statement || runRecord.rootVersion === null)
1412
+ throw new Error('Prepared reward statement is incomplete.')
1413
+ const client = options.getClient(campaign.chainId)
1414
+ const [version, paid, inventory] = await Promise.all([
1415
+ client.readContract({
1416
+ abi: distributorAbi,
1417
+ address: distributor,
1418
+ functionName: 'rootVersion',
1419
+ }),
1420
+ client.readContract({
1421
+ abi: distributorAbi,
1422
+ address: distributor,
1423
+ functionName: 'totalPaid',
1424
+ }),
1425
+ client.readContract({
1426
+ abi: erc20Abi,
1427
+ address: campaign.earnShareAddress,
1428
+ args: [distributor],
1429
+ functionName: 'balanceOf',
1430
+ }),
1431
+ ])
1432
+ if (version !== BigInt(runRecord.rootVersion))
1433
+ throw new Error('Reward root changed after statement preparation.')
1434
+ const entitlement = BigInt(runRecord.statement.totalEntitlement)
1435
+ if (paid > entitlement) throw new Error('Reward paid total exceeds the prepared entitlement.')
1436
+ const available = paid + inventory
1437
+ const missingEarnShares = entitlement > available ? entitlement - available : 0n
1438
+ const fundingEarnShares = Campaigns.settlementFundingEarnShares(missingEarnShares)
1439
+ const fundedAssets =
1440
+ fundingEarnShares > 0n
1441
+ ? Campaigns.earnSharesToAssets({
1442
+ earnShares: fundingEarnShares,
1443
+ ...(await vaultQuote(client, campaign.vaultAddress)),
1444
+ })
1445
+ : 0n
1446
+ if (
1447
+ runRecord.fundedAssets === fundedAssets.toString() &&
1448
+ runRecord.mintedEarnShares === missingEarnShares.toString()
1449
+ )
1450
+ return runRecord
1451
+ return requireTransition(options.db, runRecord, 'statement', {
1452
+ fundedAssets: fundedAssets.toString(),
1453
+ mintedEarnShares: missingEarnShares.toString(),
1454
+ })
1455
+ }
1456
+
1457
+ async function vaultQuote(
1458
+ client: ReturnType<run.Options['getClient']>,
1459
+ vault: Address,
1460
+ ): Promise<{ totalAssets: bigint; totalEarnShares: bigint }> {
1461
+ const [totalAssets, totalEarnShares] = await Promise.all([
1462
+ client.readContract({ abi: vaultAbi, address: vault, functionName: 'totalAssets' }),
1463
+ feeInclusiveEarnShareSupply(client, vault),
1464
+ ])
1465
+ return { totalAssets, totalEarnShares }
1466
+ }
1467
+
1468
+ async function deliver(
1469
+ options: run.Options,
1470
+ campaign: RewardCampaigns.Record,
1471
+ runRecord: RewardRuns.Record,
1472
+ bindings: Bindings,
1473
+ statement: Campaigns.Statement | undefined,
1474
+ calculation: Calculation,
1475
+ signer: Address,
1476
+ ) {
1477
+ if (!statement || !bindings.distributor) return
1478
+ const { accounts, closingCursor, evidence } = calculation
1479
+ const working = accounts.map((account) => ({
1480
+ ...account,
1481
+ lots: account.lots.map((lot) => ({ ...lot })),
1482
+ }))
1483
+ const client = options.getClient(campaign.chainId)
1484
+ const paidBefore = new Map(
1485
+ working.map((account) => [account.recipient.toLowerCase(), account.cumulativePaid]),
1486
+ )
1487
+ await reconcilePaid(client, { accounts: working, distributor: bindings.distributor })
1488
+ const payoutRecipients = await (async () => {
1489
+ if (evidence.payoutRecipients !== undefined) return evidence.payoutRecipients
1490
+ const endsAt = Number(runRecord.endsAt)
1491
+ // Payout decisions use the immutable schedule that accrued this run's rewards. A pending
1492
+ // schedule applies only after promotion creates a run with its own configuration snapshot.
1493
+ const closingBoost = runRecord.config.boostRewards!
1494
+ const push = Campaigns.shouldPushPayout(closingBoost, {
1495
+ endsAt,
1496
+ startsAfter: Number(runRecord.startsAfter),
1497
+ })
1498
+ const flush = endsAt >= Campaigns.finalBoundary({ boostRewards: closingBoost })
1499
+ const quote = push && !flush ? await vaultQuote(client, campaign.vaultAddress) : undefined
1500
+ // Preserve visible small balance increases while avoiding automatic pushes worth less than gas.
1501
+ const minimumAssets = 10n ** BigInt(Math.max(campaign.assetDecimals - 4, 0))
1502
+ const selected = working
1503
+ .filter(
1504
+ (account) =>
1505
+ push &&
1506
+ account.cumulativePaid < account.cumulativeEntitlement &&
1507
+ (flush ||
1508
+ (quote &&
1509
+ Campaigns.meetsPayoutMinimum({
1510
+ cumulativeEntitlement: account.cumulativeEntitlement,
1511
+ cumulativePaid: account.cumulativePaid,
1512
+ minimumAssets,
1513
+ ...quote,
1514
+ }))),
1515
+ )
1516
+ .map((account) => account.recipient)
1517
+ await requireTransition(options.db, runRecord, 'paying', {
1518
+ evidence: {
1519
+ ...evidence,
1520
+ closingAccounts: working.map(Projection.snapshot),
1521
+ payoutRecipients: selected,
1522
+ },
1523
+ })
1524
+ return selected
1525
+ })()
1526
+ const unpaidRecipients = new Set(payoutRecipients.map((recipient) => recipient.toLowerCase()))
1527
+ const batches = Campaigns.buildPayoutBatches(statement, {
1528
+ maximumSize: payoutBatchSize,
1529
+ recipients: unpaidRecipients,
1530
+ })
1531
+ const bundles = chunks(batches, payoutCallsPerTransaction)
1532
+ const version = await client.readContract({
1533
+ abi: distributorAbi,
1534
+ address: bindings.distributor,
1535
+ functionName: 'rootVersion',
1536
+ })
1537
+ // Recipient-disjoint cumulative pushes commute. Tempo expiring nonces let each packed transaction
1538
+ // confirm independently, while bounded waves avoid overwhelming the signer and RPC.
1539
+ for (const wave of chunks(bundles, payoutTransactionConcurrency)) {
1540
+ const results = await Promise.allSettled(
1541
+ wave.map((bundle) =>
1542
+ execute(options, campaign, runRecord.id, signer, {
1543
+ batches: bundle,
1544
+ distributor: bindings.distributor!,
1545
+ domain: domain(options, campaign),
1546
+ operation: 'push',
1547
+ rootVersion: version.toString(),
1548
+ statementHash: statement.statementHash,
1549
+ }),
1550
+ ),
1551
+ )
1552
+ const failure = results.find((result) => result.status === 'rejected')
1553
+ if (failure?.status === 'rejected') throw failure.reason
1554
+ }
1555
+ await reconcilePaid(client, { accounts: working, distributor: bindings.distributor })
1556
+ for (const account of working)
1557
+ account.deferral = (() => {
1558
+ if (account.cumulativePaid >= account.cumulativeEntitlement) return undefined
1559
+ if (unpaidRecipients.has(account.recipient.toLowerCase())) return 'transfer_unavailable'
1560
+ return account.deferral
1561
+ })()
1562
+ const settlementRecipients = new Set(
1563
+ (evidence.settlementRecipientAddresses ?? []).map((recipient) => recipient.toLowerCase()),
1564
+ )
1565
+ const paidRecipients =
1566
+ evidence.paidRecipients ??
1567
+ (settlementRecipients.size > 0
1568
+ ? working.filter(
1569
+ (account) =>
1570
+ settlementRecipients.has(account.recipient.toLowerCase()) &&
1571
+ account.cumulativePaid >= account.cumulativeEntitlement,
1572
+ ).length
1573
+ : working.filter(
1574
+ (account) =>
1575
+ unpaidRecipients.has(account.recipient.toLowerCase()) &&
1576
+ account.cumulativePaid > (paidBefore.get(account.recipient.toLowerCase()) ?? 0n),
1577
+ ).length)
1578
+ const payoutPaidRecipients =
1579
+ evidence.payoutPaidRecipients ??
1580
+ working.filter(
1581
+ (account) => account.cumulativePaid > (paidBefore.get(account.recipient.toLowerCase()) ?? 0n),
1582
+ ).length
1583
+ const deferredRecipients =
1584
+ evidence.deferredRecipients ??
1585
+ Math.max(
1586
+ (settlementRecipients.size > 0
1587
+ ? settlementRecipients.size
1588
+ : (evidence.settlementRecipients ?? 0)) - paidRecipients,
1589
+ 0,
1590
+ )
1591
+ const pendingRecipients =
1592
+ evidence.pendingRecipients ??
1593
+ working.filter((account) => account.cumulativePaid < account.cumulativeEntitlement).length
1594
+ await requireTransition(options.db, runRecord, 'paying', {
1595
+ evidence: {
1596
+ ...evidence,
1597
+ closingAccounts: working.map(Projection.snapshot),
1598
+ deferredRecipients,
1599
+ paidRecipients,
1600
+ payoutPaidRecipients,
1601
+ payoutRecipients,
1602
+ pendingRecipients,
1603
+ },
1604
+ })
1605
+ await commit(options.db, campaign, runRecord, working, closingCursor, statement)
1606
+ }
1607
+
1608
+ async function reconcilePaid(
1609
+ client: ReturnType<run.Options['getClient']>,
1610
+ options: reconcilePaid.Options,
1611
+ ) {
1612
+ for (const group of chunks(options.accounts, 500)) {
1613
+ const paid = await client.multicall({
1614
+ allowFailure: true,
1615
+ contracts: group.map((account) => ({
1616
+ abi: distributorAbi,
1617
+ address: options.distributor,
1618
+ args: [account.recipient],
1619
+ functionName: 'cumulativePaid' as const,
1620
+ })),
1621
+ })
1622
+ paid.forEach((result, index) => {
1623
+ if (result.status !== 'success') throw new Error('Reward payment reconciliation failed.')
1624
+ group[index]!.cumulativePaid = result.result
1625
+ })
1626
+ }
1627
+ }
1628
+
1629
+ declare namespace reconcilePaid {
1630
+ /** Onchain cumulative-payment reconciliation input. */
1631
+ type Options = {
1632
+ /** Mutable recipient projections updated with onchain paid amounts. */
1633
+ accounts: readonly Projection.Account[]
1634
+ /** Merkle distributor holding cumulative payment state. */
1635
+ distributor: Address
1636
+ }
1637
+ }
1638
+
1639
+ async function execute(
1640
+ options: run.Options,
1641
+ campaign: RewardCampaigns.Record,
1642
+ runId: string | undefined,
1643
+ signer: Address,
1644
+ signerIntent: Signer.Request['intent'],
1645
+ ): Promise<RewardTransactionAttempts.Record> {
1646
+ const intentId = Signer.id(signerIntent)
1647
+ let attempt = await RewardTransactionAttempts.latestForIntent(options.db, {
1648
+ intentId,
1649
+ ...(runId ? { runId } : {}),
1650
+ })
1651
+ if (attempt?.state === 'confirmed') return attempt
1652
+ const replacementOf =
1653
+ attempt?.state === 'expired' ||
1654
+ attempt?.state === 'ineffective' ||
1655
+ attempt?.state === 'reverted'
1656
+ ? attempt.id
1657
+ : undefined
1658
+ if (replacementOf) attempt = undefined
1659
+ if (!attempt) {
1660
+ try {
1661
+ attempt = await RewardTransactionAttempts.create(options.db, {
1662
+ chainId: campaign.chainId,
1663
+ intent: { id: intentId, operation: signerIntent.operation, payload: signerIntent },
1664
+ ...(replacementOf ? { replacementOf } : {}),
1665
+ ...(runId ? { runId } : {}),
1666
+ signer,
1667
+ vaultAddress: campaign.vaultAddress,
1668
+ })
1669
+ } catch (cause) {
1670
+ // Queue redelivery can race before either invocation observes the other's journal row.
1671
+ // The unique live-intent constraint elects one attempt; the loser resumes that evidence.
1672
+ if (!isUniqueViolation(cause)) throw cause
1673
+ attempt = await RewardTransactionAttempts.latestForIntent(options.db, {
1674
+ intentId,
1675
+ ...(runId ? { runId } : {}),
1676
+ })
1677
+ if (!attempt) throw cause
1678
+ }
1679
+ }
1680
+ if (attempt.state === 'created') {
1681
+ const signed = await Signer.sign({
1682
+ fetch: options.signerFetch,
1683
+ request: { attemptId: attempt.id, id: intentId, intent: signerIntent },
1684
+ })
1685
+ if (!isAddressEqual(signed.signer, signer)) throw new Error('Reward signer account changed.')
1686
+ attempt =
1687
+ (await RewardTransactionAttempts.recordSignature(options.db, {
1688
+ expiresAt: signed.expiresAt,
1689
+ id: attempt.id,
1690
+ nonce: signed.nonce,
1691
+ signedBytes: signed.signedBytes,
1692
+ transactionHash: signed.transactionHash,
1693
+ })) ?? attempt
1694
+ }
1695
+ if (!attempt.signedBytes || !attempt.transactionHash)
1696
+ throw new Error('Reward transaction was not durably signed.')
1697
+ const client = options.getClient(campaign.chainId)
1698
+ let receipt = await client
1699
+ .getTransactionReceipt({ hash: attempt.transactionHash })
1700
+ .catch(() => undefined)
1701
+ const expiredBeforeBroadcast =
1702
+ !receipt &&
1703
+ attempt.expiresAt &&
1704
+ Number((await client.getBlock()).timestamp) * 1_000 >= Date.parse(attempt.expiresAt)
1705
+ if (expiredBeforeBroadcast) {
1706
+ const expired = await RewardTransactionAttempts.expire(options.db, attempt.id)
1707
+ if (!expired) throw new Error('Expired reward transaction could not be replaced.')
1708
+ return execute(options, campaign, runId, signer, signerIntent)
1709
+ }
1710
+ if (!receipt && (attempt.state === 'signed' || attempt.state === 'broadcast')) {
1711
+ await options.assertLease?.()
1712
+ try {
1713
+ await client.sendRawTransaction({ serializedTransaction: attempt.signedBytes })
1714
+ } catch (cause) {
1715
+ receipt = await client
1716
+ .getTransactionReceipt({ hash: attempt.transactionHash })
1717
+ .catch(() => undefined)
1718
+ if (
1719
+ !receipt &&
1720
+ (!(cause instanceof Error) ||
1721
+ !/already known|already imported|known transaction/i.test(cause.message))
1722
+ )
1723
+ throw cause
1724
+ }
1725
+ await RewardTransactionAttempts.recordBroadcast(options.db, attempt.id)
1726
+ }
1727
+ if (!receipt)
1728
+ receipt = await client
1729
+ .waitForTransactionReceipt({
1730
+ confirmations: 1,
1731
+ hash: attempt.transactionHash,
1732
+ timeout: 60_000,
1733
+ })
1734
+ .catch(async (cause) => {
1735
+ const recovered = await client
1736
+ .getTransactionReceipt({ hash: attempt.transactionHash! })
1737
+ .catch(() => undefined)
1738
+ if (recovered) return recovered
1739
+ const expiredOnchain =
1740
+ attempt.expiresAt &&
1741
+ Number((await client.getBlock()).timestamp) * 1_000 >= Date.parse(attempt.expiresAt)
1742
+ if (!expiredOnchain) throw cause
1743
+ const expiredAttempt = await RewardTransactionAttempts.expire(options.db, attempt.id)
1744
+ if (!expiredAttempt) throw cause
1745
+ return undefined
1746
+ })
1747
+ if (!receipt) return execute(options, campaign, runId, signer, signerIntent)
1748
+ if (receipt.status === 'pending')
1749
+ throw new Error(`Reward transaction ${attempt.transactionHash} is still pending.`)
1750
+ const canonical = await client.getBlock({ blockNumber: receipt.blockNumber })
1751
+ if (canonical.hash !== receipt.blockHash)
1752
+ throw new Error('Reward receipt block is not canonical.')
1753
+ const confirmed = await RewardTransactionAttempts.confirm(options.db, {
1754
+ id: attempt.id,
1755
+ intent: compactIntent(intentId, signerIntent),
1756
+ receipt: {
1757
+ blockHash: receipt.blockHash,
1758
+ blockNumber: receipt.blockNumber.toString(),
1759
+ gasUsed: receipt.gasUsed.toString(),
1760
+ status: receipt.status,
1761
+ transactionHash: receipt.transactionHash,
1762
+ },
1763
+ })
1764
+ if (!confirmed || confirmed.receipt?.status !== 'success')
1765
+ throw new Error(`Reward transaction ${attempt.transactionHash} reverted.`)
1766
+ return confirmed
1767
+ }
1768
+
1769
+ function isUniqueViolation(cause: unknown): cause is { code: '23505' } {
1770
+ return typeof cause === 'object' && cause !== null && 'code' in cause && cause.code === '23505'
1771
+ }
1772
+
1773
+ function compactIntent(id: Hex, intent: Signer.Request['intent']): Campaigns.TransactionIntent {
1774
+ if (intent.operation === 'push')
1775
+ return {
1776
+ id,
1777
+ operation: intent.operation,
1778
+ payload: {
1779
+ batchCount: intent.batches.length,
1780
+ distributor: intent.distributor,
1781
+ domain: intent.domain,
1782
+ recipientCount: intent.batches.reduce((sum, batch) => sum + batch.recipients.length, 0),
1783
+ rootVersion: intent.rootVersion,
1784
+ statementHash: intent.statementHash,
1785
+ },
1786
+ }
1787
+ if (intent.operation === 'publish' || intent.operation === 'settle') {
1788
+ const { statement, ...payload } = intent
1789
+ return {
1790
+ id,
1791
+ operation: intent.operation,
1792
+ payload: {
1793
+ ...payload,
1794
+ statement: {
1795
+ campaignId: statement.campaignId,
1796
+ chainId: statement.chainId,
1797
+ distributor: statement.distributor,
1798
+ entryCount: statement.entries.length,
1799
+ root: statement.root,
1800
+ schemaVersion: statement.schemaVersion,
1801
+ statementHash: statement.statementHash,
1802
+ totalEntitlement: statement.totalEntitlement,
1803
+ },
1804
+ },
1805
+ }
1806
+ }
1807
+ return { id, operation: intent.operation, payload: intent }
1808
+ }
1809
+
1810
+ function maintainLease(db: Db.Db, runRecord: RewardRuns.Record, now: () => number) {
1811
+ let failure: unknown
1812
+ let stopped = false
1813
+ const extend = async () => {
1814
+ if (stopped || failure !== undefined) return
1815
+ const renewed = await RewardRuns.renew(db, {
1816
+ fence: runRecord.fence,
1817
+ id: runRecord.id,
1818
+ leaseExpiresAt: new Date(now() + runLeaseMilliseconds).toISOString(),
1819
+ })
1820
+ if (!renewed) failure = new Error('Reward run lease was lost.')
1821
+ }
1822
+ const captureFailure = (cause: unknown) => {
1823
+ failure = cause
1824
+ }
1825
+ let renewal = extend().catch(captureFailure)
1826
+ const timer = setInterval(
1827
+ () => {
1828
+ renewal = renewal.then(extend).catch(captureFailure)
1829
+ },
1830
+ Math.floor(runLeaseMilliseconds / 2),
1831
+ )
1832
+ return {
1833
+ async assert() {
1834
+ await renewal
1835
+ await extend()
1836
+ if (failure !== undefined) throw failure
1837
+ },
1838
+ async ready() {
1839
+ await renewal
1840
+ if (failure !== undefined) throw failure
1841
+ },
1842
+ async stop() {
1843
+ stopped = true
1844
+ clearInterval(timer)
1845
+ await renewal
1846
+ return failure
1847
+ },
1848
+ }
1849
+ }
1850
+
1851
+ async function commit(
1852
+ db: Db.Db,
1853
+ campaign: RewardCampaigns.Record,
1854
+ runRecord: RewardRuns.Record,
1855
+ accounts: readonly Projection.Account[],
1856
+ eventCursor: Campaigns.EventCursor | undefined,
1857
+ statement?: Campaigns.Statement,
1858
+ ) {
1859
+ const now = new Date().toISOString()
1860
+ await db.transaction(async (tx) => {
1861
+ await RewardAccounts.replace(tx, {
1862
+ accounts: accounts.map((account) => ({
1863
+ accrualRemainder: account.accrualRemainder.toString(),
1864
+ allocatedPrincipalAssets: account.allocatedPrincipalAssets.toString(),
1865
+ cumulativeEntitlement: account.cumulativeEntitlement.toString(),
1866
+ cumulativePaid: account.cumulativePaid.toString(),
1867
+ deferral: account.deferral ?? null,
1868
+ eligibilityRegisteredAt: account.eligibilityRegisteredAt ?? null,
1869
+ lots: account.lots,
1870
+ pendingRewardAssets: account.pendingRewardAssets.toString(),
1871
+ publicEarnShares: account.publicEarnShares.toString(),
1872
+ qualifiedEarnShares: account.qualifiedEarnShares.toString(),
1873
+ recipient: account.recipient,
1874
+ registrationOrder: account.registrationOrder.toString(),
1875
+ updatedAt: now,
1876
+ })),
1877
+ chainId: campaign.chainId,
1878
+ vaultAddress: campaign.vaultAddress,
1879
+ })
1880
+ const advanced = await RewardCampaigns.advance(tx, {
1881
+ chainId: campaign.chainId,
1882
+ deliveredThrough: Number(runRecord.endsAt),
1883
+ ...(eventCursor ? { eventCursor } : {}),
1884
+ expectedDeliveredThrough: Number(runRecord.startsAfter),
1885
+ vaultAddress: campaign.vaultAddress,
1886
+ })
1887
+ if (!advanced) throw new Error('Reward campaign cursor changed before commit.')
1888
+ const current = await RewardRuns.get(tx, runRecord.id)
1889
+ const delivered = await RewardRuns.transition(tx, {
1890
+ fence: runRecord.fence,
1891
+ id: runRecord.id,
1892
+ patch: {
1893
+ error: null,
1894
+ ...(current?.evidence ? { evidence: Campaigns.compactEvidence(current.evidence) } : {}),
1895
+ },
1896
+ phase: 'delivered',
1897
+ })
1898
+ if (!delivered) throw new Error('Reward run lease changed before commit.')
1899
+ if (statement)
1900
+ await RewardRuns.pruneStatements(tx, {
1901
+ chainId: campaign.chainId,
1902
+ keepRunId: runRecord.id,
1903
+ vaultAddress: campaign.vaultAddress,
1904
+ })
1905
+ })
1906
+ }
1907
+
1908
+ async function requireTransition(
1909
+ db: Db.Db,
1910
+ runRecord: RewardRuns.Record,
1911
+ phase: RewardRuns.Record['phase'],
1912
+ patch: RewardRuns.transition.Options['patch'],
1913
+ ) {
1914
+ const result = await RewardRuns.transition(db, {
1915
+ fence: runRecord.fence,
1916
+ id: runRecord.id,
1917
+ patch,
1918
+ phase,
1919
+ })
1920
+ if (!result) throw new Error('Reward run lease was lost.')
1921
+ return result
1922
+ }
1923
+
1924
+ function restoreCalculation(evidence: Campaigns.RunEvidence): Calculation {
1925
+ return {
1926
+ accounts: evidence.closingAccounts.map(Projection.fromSnapshot),
1927
+ ...(evidence.eventCursor ? { closingCursor: evidence.eventCursor } : {}),
1928
+ evidence,
1929
+ }
1930
+ }
1931
+
1932
+ async function* emptyEventPages(): AsyncGenerator<readonly Projection.Event[]> {}
1933
+
1934
+ async function* reconciliationEventPages(
1935
+ pages: AsyncIterable<readonly Projection.Event[]>,
1936
+ ): AsyncGenerator<Projection.reconcilePages.Page> {
1937
+ for await (const events of pages) yield { events }
1938
+ }
1939
+
1940
+ async function* registrationDepositPages(
1941
+ tidx: Tidx.Client,
1942
+ options: registrationDepositPages.Options,
1943
+ ): AsyncGenerator<Projection.reconcilePages.Page> {
1944
+ const accounts = [...options.accounts].sort((left, right) =>
1945
+ left.registrationOrder < right.registrationOrder
1946
+ ? -1
1947
+ : left.registrationOrder > right.registrationOrder
1948
+ ? 1
1949
+ : left.recipient.toLowerCase().localeCompare(right.recipient.toLowerCase()),
1950
+ )
1951
+ const afterOrder = options.afterRecipient
1952
+ ? accounts.find((entry) => isAddressEqual(entry.recipient, options.afterRecipient!))
1953
+ ?.registrationOrder
1954
+ : undefined
1955
+ if (options.afterRecipient && afterOrder === undefined)
1956
+ throw new Error('Reward reconciliation recipient is no longer registered.')
1957
+ for (const account of accounts) {
1958
+ if (
1959
+ afterOrder !== undefined &&
1960
+ (account.registrationOrder < afterOrder ||
1961
+ (account.registrationOrder === afterOrder && !options.after))
1962
+ )
1963
+ continue
1964
+ const pages = Events.accountDepositPages(tidx, {
1965
+ ...(afterOrder === account.registrationOrder && options.after
1966
+ ? { after: options.after }
1967
+ : {}),
1968
+ account: account.recipient,
1969
+ earnVault: options.earnVault,
1970
+ throughBlock: options.throughBlock,
1971
+ })
1972
+ for await (const events of pages) yield { allocationRecipient: account.recipient, events }
1973
+ yield { allocationRecipient: account.recipient, events: [] }
1974
+ }
1975
+ }
1976
+
1977
+ declare namespace registrationDepositPages {
1978
+ type Options = {
1979
+ after?: Campaigns.EventCursor | undefined
1980
+ afterRecipient?: Address | undefined
1981
+ accounts: readonly { recipient: Address; registrationOrder: bigint }[]
1982
+ earnVault: Address
1983
+ throughBlock: number
1984
+ }
1985
+ }
1986
+
1987
+ async function notificationSummary(
1988
+ db: Db.Db,
1989
+ campaign: RewardCampaigns.Record,
1990
+ runId: string,
1991
+ ): Promise<run.Notification | undefined> {
1992
+ const record = await RewardRuns.get(db, runId)
1993
+ const evidence = record?.evidence
1994
+ if (!record || !evidence) return undefined
1995
+ const [attempts, vault] = await Promise.all([
1996
+ RewardTransactionAttempts.confirmedForRun(db, runId),
1997
+ EarnVaultRecords.get(db, campaign),
1998
+ ])
1999
+ const root = attempts.find(
2000
+ (attempt) => attempt.intent.operation === 'publish' || attempt.intent.operation === 'settle',
2001
+ )
2002
+ const targetAttempt = attempts.find((attempt) => attempt.intent.operation === 'targetYield')
2003
+ const payoutTransactionHashes = attempts
2004
+ .filter((attempt) => attempt.intent.operation === 'push' && attempt.transactionHash)
2005
+ .map((attempt) => attempt.transactionHash!)
2006
+ const closingInterval = evidence.intervals.at(-1)
2007
+ const targetActive = Boolean(
2008
+ record.config.targetYield &&
2009
+ Campaigns.overlapsCompleteIntervals(record.config.targetYield, {
2010
+ endsAt: Number(record.endsAt),
2011
+ startsAfter: Number(record.startsAfter),
2012
+ }),
2013
+ )
2014
+ const targetActiveAtClose = Boolean(
2015
+ record.config.targetYield &&
2016
+ closingInterval &&
2017
+ closingInterval.endsAt > record.config.targetYield.startTimestamp &&
2018
+ closingInterval.endsAt <= record.config.targetYield.endTimestamp,
2019
+ )
2020
+ const boostActiveAtClose = Boolean(
2021
+ record.config.boostRewards &&
2022
+ record.config.boostRewards.enabled !== false &&
2023
+ closingInterval &&
2024
+ closingInterval.endsAt > record.config.boostRewards.startTimestamp &&
2025
+ closingInterval.endsAt <= record.config.boostRewards.endTimestamp,
2026
+ )
2027
+ const targetRateBps = targetActiveAtClose
2028
+ ? (evidence.appliedTargetRateBps ?? evidence.targetRateBps)
2029
+ : undefined
2030
+ const settlement = (() => {
2031
+ if (
2032
+ !evidence.publishedRoot ||
2033
+ evidence.settledRewardAssets === undefined ||
2034
+ evidence.settlementRecipients === undefined ||
2035
+ !root?.transactionHash
2036
+ )
2037
+ return undefined
2038
+ return {
2039
+ assetAmount: evidence.settledRewardAssets,
2040
+ creditedRecipients: evidence.settlementRecipients,
2041
+ deferredRecipients:
2042
+ evidence.deferredRecipients ??
2043
+ Math.max(evidence.settlementRecipients - (evidence.paidRecipients ?? 0), 0),
2044
+ transactionHash: root.transactionHash,
2045
+ }
2046
+ })()
2047
+ if (!targetActive && !settlement && payoutTransactionHashes.length === 0) return undefined
2048
+ return {
2049
+ assetDecimals: campaign.assetDecimals,
2050
+ baseRateBps: Math.max(closingInterval?.organicRateBps ?? 0, targetRateBps ?? 0),
2051
+ ...(boostActiveAtClose ? { boostRateBps: closingInterval?.boostRateBps ?? 0 } : {}),
2052
+ intervalCount: evidence.intervals.length,
2053
+ ...(payoutTransactionHashes.length > 0
2054
+ ? {
2055
+ payout: {
2056
+ deferredRecipients: evidence.pendingRecipients ?? 0,
2057
+ paidRecipients: evidence.payoutPaidRecipients ?? evidence.paidRecipients ?? 0,
2058
+ transactionHashes: payoutTransactionHashes,
2059
+ },
2060
+ }
2061
+ : {}),
2062
+ ...(settlement ? { settlement } : {}),
2063
+ ...(targetActive
2064
+ ? {
2065
+ targetYield: {
2066
+ assetAmount: (evidence.targetFundingAssets ?? [])
2067
+ .reduce((sum, amount) => sum + BigInt(amount), 0n)
2068
+ .toString(),
2069
+ ...(targetAttempt?.transactionHash
2070
+ ? { transactionHash: targetAttempt.transactionHash }
2071
+ : {}),
2072
+ },
2073
+ }
2074
+ : {}),
2075
+ vaultLabel: vault?.label ?? campaign.vaultAddress,
2076
+ }
2077
+ }
2078
+
2079
+ async function allEligible(db: Db.Db, campaign: RewardCampaigns.Record) {
2080
+ const records: RewardEligibilityAssociations.Record[] = []
2081
+ let cursor: string | undefined
2082
+ do {
2083
+ const page = await RewardEligibilityAssociations.list(db, {
2084
+ chainId: campaign.chainId,
2085
+ ...(cursor ? { cursor } : {}),
2086
+ limit: 1_000,
2087
+ vaultAddress: campaign.vaultAddress,
2088
+ })
2089
+ if (records.length + page.data.length > maximumRecipients)
2090
+ throw new Error(`Reward campaign exceeds ${maximumRecipients} registered recipients.`)
2091
+ records.push(...page.data)
2092
+ cursor = page.nextCursor ?? undefined
2093
+ } while (cursor)
2094
+ return records
2095
+ }
2096
+
2097
+ async function targetRate(
2098
+ options: run.Options,
2099
+ campaign: RewardCampaigns.Record,
2100
+ runRecord: RewardRuns.Record,
2101
+ ) {
2102
+ const config = campaign.config.targetYield!
2103
+ const previous = await RewardRuns.latestDelivered(options.db, campaign)
2104
+ const fallback =
2105
+ previous?.evidence?.targetRateBps !== undefined &&
2106
+ previous.evidence.targetRateObservedAt &&
2107
+ sameRateSource(previous.config.targetYield?.annualRate, config.annualRate)
2108
+ ? {
2109
+ annualRateBps: previous.evidence.targetRateBps,
2110
+ observedAt: previous.evidence.targetRateObservedAt,
2111
+ }
2112
+ : undefined
2113
+ const result = await Rates.target({
2114
+ boundary: new Date(Number(runRecord.endsAt) * 1_000).toISOString(),
2115
+ ...(fallback ? { fallback } : {}),
2116
+ fetch: options.fetch,
2117
+ now: () => new Date(options.now()),
2118
+ source: config.annualRate,
2119
+ })
2120
+ return { config, ...result }
2121
+ }
2122
+
2123
+ function sameRateSource(
2124
+ left: NonNullable<Campaigns.Config['targetYield']>['annualRate'] | undefined,
2125
+ right: NonNullable<Campaigns.Config['targetYield']>['annualRate'],
2126
+ ): boolean {
2127
+ if (!left) return false
2128
+ if ('bps' in left || 'bps' in right)
2129
+ return 'bps' in left && 'bps' in right && left.bps === right.bps
2130
+ return (
2131
+ left.morphoVault.toLowerCase() === right.morphoVault.toLowerCase() &&
2132
+ left.minBps === right.minBps &&
2133
+ left.maxBps === right.maxBps &&
2134
+ left.staleAfterSeconds === right.staleAfterSeconds
2135
+ )
2136
+ }
2137
+
2138
+ function sameTargetFunding(
2139
+ left: NonNullable<Campaigns.Config['targetYield']>['funding'] | undefined,
2140
+ right: NonNullable<Campaigns.Config['targetYield']>['funding'],
2141
+ ): boolean {
2142
+ return Boolean(
2143
+ left &&
2144
+ left.length === right.length &&
2145
+ left.every((leg, index) => {
2146
+ const compared = right[index]!
2147
+ if (leg.wallet.toLowerCase() !== compared.wallet.toLowerCase()) return false
2148
+ if ('remainder' in leg || 'remainder' in compared)
2149
+ return 'remainder' in leg && 'remainder' in compared
2150
+ return leg.upToAnnualRate.bps === compared.upToAnnualRate.bps
2151
+ }),
2152
+ )
2153
+ }
2154
+
2155
+ async function exclusions(
2156
+ options: run.Options,
2157
+ campaign: RewardCampaigns.Record,
2158
+ bindings: Bindings,
2159
+ ): Promise<Address[]> {
2160
+ const boost = campaign.config.boostRewards
2161
+ if (!boost) return []
2162
+ const client = options.getClient(campaign.chainId)
2163
+ const [earnFees, engine, vault] = await Promise.all([
2164
+ client.readContract({
2165
+ abi: vaultAbi,
2166
+ address: campaign.vaultAddress,
2167
+ functionName: 'earnFees',
2168
+ }),
2169
+ client.readContract({
2170
+ abi: vaultAbi,
2171
+ address: campaign.vaultAddress,
2172
+ functionName: 'engine',
2173
+ }),
2174
+ EarnVaultRecords.get(options.db, campaign),
2175
+ ])
2176
+ const zoneContracts = options.zones
2177
+ .filter((zone) => zone.sourceId === campaign.chainId)
2178
+ .flatMap((zone) => Object.values(zone.contracts ?? {}))
2179
+ .flatMap(contractAddresses)
2180
+ return [
2181
+ ...new Set(
2182
+ [
2183
+ campaign.vaultAddress,
2184
+ campaign.earnShareAddress,
2185
+ earnFees,
2186
+ engine,
2187
+ boost.treasury,
2188
+ ...(bindings.controller ? [bindings.controller] : []),
2189
+ ...(bindings.distributor ? [bindings.distributor] : []),
2190
+ ...(vault?.zones.map((zone) => zone.earnRouter) ?? []),
2191
+ ...zoneContracts,
2192
+ ...boost.excludedAddresses,
2193
+ ].map((address) => getAddress(address).toLowerCase()),
2194
+ ),
2195
+ ].map(getAddress)
2196
+ }
2197
+
2198
+ function contractAddresses(contract: unknown): Address[] {
2199
+ if (!contract || typeof contract !== 'object') return []
2200
+ const value = contract as Record<string, unknown>
2201
+ const address = value['address']
2202
+ if (typeof address === 'string' && isAddress(address)) return [address]
2203
+ return Object.values(value).flatMap(contractAddresses)
2204
+ }
2205
+
2206
+ function domain(
2207
+ options: run.Options,
2208
+ campaign: RewardCampaigns.Record,
2209
+ config = campaign.config,
2210
+ ): Signer.Domain {
2211
+ return {
2212
+ chainId: campaign.chainId,
2213
+ earnShare: getAddress(campaign.earnShareAddress),
2214
+ earnVault: getAddress(campaign.vaultAddress),
2215
+ factory: options.factory,
2216
+ treasury: config.boostRewards?.treasury ?? '0x0000000000000000000000000000000000000000',
2217
+ }
2218
+ }
2219
+
2220
+ function campaignId(vault: Address): Hex {
2221
+ return `0x${'0'.repeat(24)}${vault.slice(2).toLowerCase()}`
2222
+ }
2223
+
2224
+ function chunks<item>(items: readonly item[], size: number): item[][] {
2225
+ const result: item[][] = []
2226
+ for (let index = 0; index < items.length; index += size)
2227
+ result.push(items.slice(index, index + size))
2228
+ return result
2229
+ }