tapimo 0.13.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.
- package/README.md +7 -4
- package/dist/ApiKey.js +1 -1
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +95 -14
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +360 -49
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +21383 -18056
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +301 -22
- package/dist/App.js.map +1 -1
- package/dist/Assets.d.ts.map +1 -1
- package/dist/Assets.js +2 -1
- package/dist/Assets.js.map +1 -1
- package/dist/Handler.d.ts +0 -1
- package/dist/Handler.d.ts.map +1 -1
- package/dist/Handler.js +0 -1
- package/dist/Handler.js.map +1 -1
- package/dist/Scope.d.ts +13 -5
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +16 -5
- package/dist/Scope.js.map +1 -1
- package/dist/Webhooks.js +2 -2
- package/dist/Webhooks.js.map +1 -1
- package/dist/admin/App.d.ts +3936 -1337
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/App.js +26 -5
- package/dist/admin/App.js.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +159 -7
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +95 -43
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/earn-vaults.d.ts +1446 -0
- package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.js +465 -8
- package/dist/admin/apps/earn-vaults.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +344 -3
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/organizations.js +288 -10
- package/dist/admin/apps/organizations.js.map +1 -1
- package/dist/admin/apps/routes-support.d.ts +95 -0
- package/dist/admin/apps/routes-support.d.ts.map +1 -0
- package/dist/admin/apps/routes-support.js +151 -0
- package/dist/admin/apps/routes-support.js.map +1 -0
- package/dist/admin/apps/routes.d.ts +2764 -0
- package/dist/admin/apps/routes.d.ts.map +1 -0
- package/dist/admin/apps/routes.js +728 -0
- package/dist/admin/apps/routes.js.map +1 -0
- package/dist/admin/apps/subsidies.d.ts +501 -0
- package/dist/admin/apps/subsidies.d.ts.map +1 -0
- package/dist/admin/apps/subsidies.js +384 -0
- package/dist/admin/apps/subsidies.js.map +1 -0
- package/dist/admin/apps/verified-tokens.d.ts +16 -16
- package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.js +3 -3
- package/dist/admin/apps/verified-tokens.js.map +1 -1
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.d.ts.map +1 -1
- package/dist/admin/ui.generated.js +129 -69
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts +5 -2
- package/dist/analytics/Analytics.d.ts.map +1 -1
- package/dist/analytics/Analytics.js +31 -1
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/analytics/tables/requestEvents.d.ts +2 -1
- package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
- package/dist/analytics/tables/requestEvents.js +10 -1
- package/dist/analytics/tables/requestEvents.js.map +1 -1
- package/dist/analytics/tables/routesLifecycleIntervals.d.ts +72 -0
- package/dist/analytics/tables/routesLifecycleIntervals.d.ts.map +1 -0
- package/dist/analytics/tables/routesLifecycleIntervals.js +56 -0
- package/dist/analytics/tables/routesLifecycleIntervals.js.map +1 -0
- package/dist/apps/Zones.d.ts +21 -12526
- package/dist/apps/Zones.d.ts.map +1 -1
- package/dist/apps/Zones.js +40 -8
- package/dist/apps/Zones.js.map +1 -1
- package/dist/apps/data/App.d.ts +4310 -2548
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +86 -26
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +364 -34
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +495 -190
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +94 -22
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +37 -37
- package/dist/apps/data/routes/coingecko.d.ts.map +1 -1
- package/dist/apps/data/routes/coingecko.js +47 -21
- package/dist/apps/data/routes/coingecko.js.map +1 -1
- package/dist/apps/data/routes/earn.d.ts +1641 -352
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +1561 -120
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +15 -0
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +180 -114
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts +5 -0
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +49 -38
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +125 -0
- package/dist/apps/data/routes/indexer.d.ts.map +1 -1
- package/dist/apps/data/routes/indexer.js +83 -27
- package/dist/apps/data/routes/indexer.js.map +1 -1
- package/dist/apps/data/routes/rpc.d.ts +507 -33
- package/dist/apps/data/routes/rpc.d.ts.map +1 -1
- package/dist/apps/data/routes/rpc.js +266 -156
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +47 -15
- package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
- package/dist/apps/data/routes/tokenlist.js +68 -11
- package/dist/apps/data/routes/tokenlist.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts +95 -93
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +26 -14
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +16 -0
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +9 -7
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/verified-tokens.d.ts +4 -4
- package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/verified-tokens.js +64 -20
- package/dist/apps/data/routes/verified-tokens.js.map +1 -1
- package/dist/apps/data/routes/webhooks.d.ts +1210 -70
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +301 -163
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.d.ts.map +1 -1
- package/dist/apps/data/routes/zones.js +20 -12
- package/dist/apps/data/routes/zones.js.map +1 -1
- package/dist/apps/docs/reference.js +2 -2
- package/dist/apps/docs/reference.js.map +1 -1
- package/dist/apps/index.d.ts +1 -1
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/apps/index.js +1 -1
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/management/App.d.ts +4119 -3297
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/App.js +5 -14
- package/dist/apps/management/App.js.map +1 -1
- package/dist/apps/management/Billing.d.ts +49 -8
- package/dist/apps/management/Billing.d.ts.map +1 -1
- package/dist/apps/management/Billing.js +221 -26
- package/dist/apps/management/Billing.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +174 -72
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +43 -15
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +364 -78
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +274 -49
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +6 -6
- package/dist/apps/management/routes/faucet.js +1 -1
- package/dist/apps/management/routes/faucet.js.map +1 -1
- package/dist/apps/management/routes/invitations.d.ts +99 -24
- package/dist/apps/management/routes/invitations.d.ts.map +1 -1
- package/dist/apps/management/routes/invitations.js +117 -13
- package/dist/apps/management/routes/invitations.js.map +1 -1
- package/dist/apps/management/routes/invite-links.d.ts +42 -42
- package/dist/apps/management/routes/me.d.ts.map +1 -1
- package/dist/apps/management/routes/me.js +6 -2
- package/dist/apps/management/routes/me.js.map +1 -1
- package/dist/apps/management/routes/members.d.ts +16 -16
- package/dist/apps/management/routes/orgs.d.ts +36 -20
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +52 -12
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts +40 -40
- package/dist/apps/management/routes/projects.d.ts.map +1 -1
- package/dist/apps/management/routes/projects.js +6 -1
- package/dist/apps/management/routes/projects.js.map +1 -1
- package/dist/apps/management/routes/scopes.d.ts.map +1 -1
- package/dist/apps/management/routes/scopes.js +4 -1
- package/dist/apps/management/routes/scopes.js.map +1 -1
- package/dist/apps/management/routes/usage.d.ts +8 -8
- package/dist/apps/management/routes/usage.js +21 -4
- package/dist/apps/management/routes/usage.js.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.d.ts +16 -16
- package/dist/apps/management/routes/webhooks.d.ts +931 -215
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/management/routes/webhooks.js +71 -25
- package/dist/apps/management/routes/webhooks.js.map +1 -1
- package/dist/apps/mcp.d.ts +1 -1
- package/dist/apps/mcp.js +7 -7
- package/dist/apps/mcp.js.map +1 -1
- package/dist/apps/mpp/App.d.ts.map +1 -1
- package/dist/apps/mpp/App.js +35 -7
- package/dist/apps/mpp/App.js.map +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts +2 -0
- package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Adapter.js +30 -1
- package/dist/apps/mpp/internal/Adapter.js.map +1 -1
- package/dist/apps/mpp/internal/Policy.d.ts +22 -4
- package/dist/apps/mpp/internal/Policy.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Policy.js +24 -6
- package/dist/apps/mpp/internal/Policy.js.map +1 -1
- package/dist/apps/mpp/internal/Sponsorship.d.ts +3 -0
- package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Sponsorship.js +6 -3
- package/dist/apps/mpp/internal/Sponsorship.js.map +1 -1
- package/dist/apps/relay/App.d.ts +2 -0
- package/dist/apps/relay/App.d.ts.map +1 -1
- package/dist/apps/relay/App.js +177 -26
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/apps/relay/Sponsorships.d.ts +5 -3
- package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
- package/dist/apps/relay/Sponsorships.js +232 -25
- package/dist/apps/relay/Sponsorships.js.map +1 -1
- package/dist/apps/{funding → routes}/Access.d.ts +4 -4
- package/dist/apps/routes/Access.d.ts.map +1 -0
- package/dist/apps/{funding → routes}/Access.js +4 -4
- package/dist/apps/routes/Access.js.map +1 -0
- package/dist/apps/{funding → routes}/App.d.ts +841 -136
- package/dist/apps/{funding → routes}/App.d.ts.map +1 -1
- package/dist/apps/{funding → routes}/App.js +17 -17
- package/dist/apps/routes/App.js.map +1 -0
- package/dist/apps/routes/Inventory.d.ts.map +1 -0
- package/dist/apps/{funding → routes}/Inventory.js +2 -2
- package/dist/apps/routes/Inventory.js.map +1 -0
- package/dist/apps/routes/index.d.ts +2 -0
- package/dist/apps/routes/index.d.ts.map +1 -0
- package/dist/apps/routes/index.js +2 -0
- package/dist/apps/routes/index.js.map +1 -0
- package/dist/apps/{funding → routes}/routes/chains.d.ts +7 -7
- package/dist/apps/routes/routes/chains.d.ts.map +1 -0
- package/dist/apps/{funding → routes}/routes/chains.js +25 -25
- package/dist/apps/routes/routes/chains.js.map +1 -0
- package/dist/apps/{funding → routes}/routes/deposit-addresses.d.ts +123 -59
- package/dist/apps/routes/routes/deposit-addresses.d.ts.map +1 -0
- package/dist/apps/{funding → routes}/routes/deposit-addresses.js +498 -223
- package/dist/apps/routes/routes/deposit-addresses.js.map +1 -0
- package/dist/apps/{funding → routes}/routes/providers.d.ts +8 -8
- package/dist/apps/routes/routes/providers.d.ts.map +1 -0
- package/dist/apps/routes/routes/providers.js +57 -0
- package/dist/apps/routes/routes/providers.js.map +1 -0
- package/dist/apps/{funding → routes}/routes/quotes.d.ts +19 -16
- package/dist/apps/routes/routes/quotes.d.ts.map +1 -0
- package/dist/apps/{funding → routes}/routes/quotes.js +55 -38
- package/dist/apps/routes/routes/quotes.js.map +1 -0
- package/dist/apps/{funding → routes}/routes/transfers.d.ts +957 -71
- package/dist/apps/routes/routes/transfers.d.ts.map +1 -0
- package/dist/apps/routes/routes/transfers.js +1140 -0
- package/dist/apps/routes/routes/transfers.js.map +1 -0
- package/dist/cli/Cli.d.ts +30 -0
- package/dist/cli/Cli.d.ts.map +1 -1
- package/dist/cli/commands/admin.d.ts +36 -0
- package/dist/cli/commands/admin.d.ts.map +1 -1
- package/dist/cli/commands/admin.js +273 -0
- package/dist/cli/commands/admin.js.map +1 -1
- package/dist/cloudflare.d.ts +16 -14
- package/dist/cloudflare.d.ts.map +1 -1
- package/dist/cloudflare.js +26 -21
- package/dist/cloudflare.js.map +1 -1
- package/dist/db/Db.d.ts +63 -21
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +1052 -15
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +785 -133
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +590 -162
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/apiKeyAdmissionBootstraps.d.ts +4 -0
- package/dist/db/tables/apiKeyAdmissionBootstraps.d.ts.map +1 -0
- package/dist/db/tables/apiKeyAdmissionBootstraps.js +2 -0
- package/dist/db/tables/apiKeyAdmissionBootstraps.js.map +1 -0
- package/dist/db/tables/apiKeyAdmissions.d.ts +73 -0
- package/dist/db/tables/apiKeyAdmissions.d.ts.map +1 -0
- package/dist/db/tables/apiKeyAdmissions.js +142 -0
- package/dist/db/tables/apiKeyAdmissions.js.map +1 -0
- package/dist/db/tables/apiKeyOwnerAdmissions.d.ts +4 -0
- package/dist/db/tables/apiKeyOwnerAdmissions.d.ts.map +1 -0
- package/dist/db/tables/apiKeyOwnerAdmissions.js +2 -0
- package/dist/db/tables/apiKeyOwnerAdmissions.js.map +1 -0
- package/dist/db/tables/apiKeyOwnerTombstones.d.ts +20 -0
- package/dist/db/tables/apiKeyOwnerTombstones.d.ts.map +1 -0
- package/dist/db/tables/apiKeyOwnerTombstones.js +57 -0
- package/dist/db/tables/apiKeyOwnerTombstones.js.map +1 -0
- package/dist/db/tables/apiKeyRevocations.d.ts +21 -0
- package/dist/db/tables/apiKeyRevocations.d.ts.map +1 -0
- package/dist/db/tables/apiKeyRevocations.js +98 -0
- package/dist/db/tables/apiKeyRevocations.js.map +1 -0
- package/dist/db/tables/authAccounts.d.ts +47 -0
- package/dist/db/tables/authAccounts.d.ts.map +1 -0
- package/dist/db/tables/authAccounts.js +72 -0
- package/dist/db/tables/authAccounts.js.map +1 -0
- package/dist/db/tables/authSessions.d.ts +12 -0
- package/dist/db/tables/authSessions.d.ts.map +1 -0
- package/dist/db/tables/authSessions.js +2 -0
- package/dist/db/tables/authSessions.js.map +1 -0
- package/dist/db/tables/authVerifications.d.ts +12 -0
- package/dist/db/tables/authVerifications.d.ts.map +1 -0
- package/dist/db/tables/authVerifications.js +2 -0
- package/dist/db/tables/authVerifications.js.map +1 -0
- package/dist/db/tables/invitations.d.ts +17 -1
- package/dist/db/tables/invitations.d.ts.map +1 -1
- package/dist/db/tables/invitations.js +123 -43
- package/dist/db/tables/invitations.js.map +1 -1
- package/dist/db/tables/memberships.d.ts +8 -0
- package/dist/db/tables/memberships.d.ts.map +1 -1
- package/dist/db/tables/memberships.js +51 -2
- package/dist/db/tables/memberships.js.map +1 -1
- package/dist/db/tables/organizations.d.ts +51 -5
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +135 -47
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/projects.d.ts +27 -1
- package/dist/db/tables/projects.d.ts.map +1 -1
- package/dist/db/tables/projects.js +38 -3
- package/dist/db/tables/projects.js.map +1 -1
- package/dist/db/tables/requestUsage.d.ts.map +1 -1
- package/dist/db/tables/requestUsage.js.map +1 -1
- package/dist/db/tables/rewardAccounts.d.ts +86 -0
- package/dist/db/tables/rewardAccounts.d.ts.map +1 -0
- package/dist/db/tables/rewardAccounts.js +88 -0
- package/dist/db/tables/rewardAccounts.js.map +1 -0
- package/dist/db/tables/rewardCampaignDistributors.d.ts +36 -0
- package/dist/db/tables/rewardCampaignDistributors.d.ts.map +1 -0
- package/dist/db/tables/rewardCampaignDistributors.js +30 -0
- package/dist/db/tables/rewardCampaignDistributors.js.map +1 -0
- package/dist/db/tables/rewardCampaigns.d.ts +171 -0
- package/dist/db/tables/rewardCampaigns.d.ts.map +1 -0
- package/dist/db/tables/rewardCampaigns.js +392 -0
- package/dist/db/tables/rewardCampaigns.js.map +1 -0
- package/dist/db/tables/rewardCredits.d.ts +53 -0
- package/dist/db/tables/rewardCredits.d.ts.map +1 -0
- package/dist/db/tables/rewardCredits.js +63 -0
- package/dist/db/tables/rewardCredits.js.map +1 -0
- package/dist/db/tables/rewardEligibilityAssociations.d.ts +61 -0
- package/dist/db/tables/rewardEligibilityAssociations.d.ts.map +1 -0
- package/dist/db/tables/rewardEligibilityAssociations.js +103 -0
- package/dist/db/tables/rewardEligibilityAssociations.js.map +1 -0
- package/dist/db/tables/rewardRuns.d.ts +188 -0
- package/dist/db/tables/rewardRuns.d.ts.map +1 -0
- package/dist/db/tables/rewardRuns.js +278 -0
- package/dist/db/tables/rewardRuns.js.map +1 -0
- package/dist/db/tables/rewardTransactionAttempts.d.ts +100 -0
- package/dist/db/tables/rewardTransactionAttempts.d.ts.map +1 -0
- package/dist/db/tables/rewardTransactionAttempts.js +163 -0
- package/dist/db/tables/rewardTransactionAttempts.js.map +1 -0
- package/dist/db/tables/routesCatalog.d.ts +58 -0
- package/dist/db/tables/routesCatalog.d.ts.map +1 -0
- package/dist/db/tables/{fundingCatalog.js → routesCatalog.js} +19 -19
- package/dist/db/tables/routesCatalog.js.map +1 -0
- package/dist/db/tables/routesDepositAddresses.d.ts +476 -0
- package/dist/db/tables/routesDepositAddresses.d.ts.map +1 -0
- package/dist/db/tables/routesDepositAddresses.js +587 -0
- package/dist/db/tables/routesDepositAddresses.js.map +1 -0
- package/dist/db/tables/{fundingDepositRequestObservations.d.ts → routesDepositRequestObservations.d.ts} +6 -6
- package/dist/db/tables/routesDepositRequestObservations.d.ts.map +1 -0
- package/dist/db/tables/{fundingDepositRequestObservations.js → routesDepositRequestObservations.js} +6 -6
- package/dist/db/tables/routesDepositRequestObservations.js.map +1 -0
- package/dist/db/tables/{fundingDeposits.d.ts → routesDeposits.d.ts} +210 -22
- package/dist/db/tables/routesDeposits.d.ts.map +1 -0
- package/dist/db/tables/routesDeposits.js +576 -0
- package/dist/db/tables/routesDeposits.js.map +1 -0
- package/dist/db/tables/routesIdempotency.d.ts +166 -0
- package/dist/db/tables/routesIdempotency.d.ts.map +1 -0
- package/dist/db/tables/routesIdempotency.js +330 -0
- package/dist/db/tables/routesIdempotency.js.map +1 -0
- package/dist/db/tables/routesSubsidies.d.ts +27 -0
- package/dist/db/tables/routesSubsidies.d.ts.map +1 -0
- package/dist/db/tables/routesSubsidies.js +45 -0
- package/dist/db/tables/routesSubsidies.js.map +1 -0
- package/dist/db/tables/{fundingTransferEvents.d.ts → routesTransferEvents.d.ts} +3 -3
- package/dist/db/tables/routesTransferEvents.d.ts.map +1 -0
- package/dist/db/tables/{fundingTransferEvents.js → routesTransferEvents.js} +3 -3
- package/dist/db/tables/routesTransferEvents.js.map +1 -0
- package/dist/db/tables/routesTransferSubsidies.d.ts +19 -0
- package/dist/db/tables/routesTransferSubsidies.d.ts.map +1 -0
- package/dist/db/tables/routesTransferSubsidies.js +17 -0
- package/dist/db/tables/routesTransferSubsidies.js.map +1 -0
- package/dist/db/tables/routesTransferSubsidyNonces.d.ts +51 -0
- package/dist/db/tables/routesTransferSubsidyNonces.d.ts.map +1 -0
- package/dist/db/tables/routesTransferSubsidyNonces.js +82 -0
- package/dist/db/tables/routesTransferSubsidyNonces.js.map +1 -0
- package/dist/db/tables/routesTransferTransactions.d.ts +77 -0
- package/dist/db/tables/routesTransferTransactions.d.ts.map +1 -0
- package/dist/db/tables/routesTransferTransactions.js +86 -0
- package/dist/db/tables/routesTransferTransactions.js.map +1 -0
- package/dist/db/tables/routesTransfers.d.ts +319 -0
- package/dist/db/tables/routesTransfers.d.ts.map +1 -0
- package/dist/db/tables/routesTransfers.js +372 -0
- package/dist/db/tables/routesTransfers.js.map +1 -0
- package/dist/db/tables/sponsoredTransactions.d.ts +112 -13
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +241 -38
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/db/tables/sponsorshipAttributions.d.ts +69 -0
- package/dist/db/tables/sponsorshipAttributions.d.ts.map +1 -0
- package/dist/db/tables/sponsorshipAttributions.js +77 -0
- package/dist/db/tables/sponsorshipAttributions.js.map +1 -0
- package/dist/db/tables/sponsorshipReconciliationCursors.d.ts +22 -0
- package/dist/db/tables/sponsorshipReconciliationCursors.d.ts.map +1 -0
- package/dist/db/tables/sponsorshipReconciliationCursors.js +45 -0
- package/dist/db/tables/sponsorshipReconciliationCursors.js.map +1 -0
- package/dist/db/tables/stripeCustomers.d.ts +11 -0
- package/dist/db/tables/stripeCustomers.d.ts.map +1 -1
- package/dist/db/tables/stripeCustomers.js +13 -0
- package/dist/db/tables/stripeCustomers.js.map +1 -1
- package/dist/db/tables/users.d.ts +17 -1
- package/dist/db/tables/users.d.ts.map +1 -1
- package/dist/db/tables/users.js +47 -2
- package/dist/db/tables/users.js.map +1 -1
- package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
- package/dist/db/tables/webhookQueueEvents.js +5 -1
- package/dist/db/tables/webhookQueueEvents.js.map +1 -1
- package/dist/db/tables/webhookSubscriptions.d.ts +22 -10
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +50 -15
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/handlers/internal/executionError.d.ts +2 -2
- package/dist/handlers/internal/executionError.d.ts.map +1 -1
- package/dist/handlers/internal/executionError.js +1 -2
- package/dist/handlers/internal/executionError.js.map +1 -1
- package/dist/handlers/internal/multisig.d.ts +4 -232
- package/dist/handlers/internal/multisig.d.ts.map +1 -1
- package/dist/handlers/internal/multisig.js +11 -537
- package/dist/handlers/internal/multisig.js.map +1 -1
- package/dist/handlers/internal/sponsorship.d.ts +19 -8
- package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
- package/dist/handlers/internal/sponsorship.js +13 -9
- package/dist/handlers/internal/sponsorship.js.map +1 -1
- package/dist/handlers/relay.d.ts +9 -12
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +81 -85
- package/dist/handlers/relay.js.map +1 -1
- package/dist/index.d.ts +0 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -17
- package/dist/index.js.map +1 -1
- package/dist/internal/AssetResponse.d.ts +12 -0
- package/dist/internal/AssetResponse.d.ts.map +1 -0
- package/dist/internal/AssetResponse.js +21 -0
- package/dist/internal/AssetResponse.js.map +1 -0
- package/dist/internal/Auth.d.ts +48 -12
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +603 -175
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/BetterAuth.d.ts +1488 -0
- package/dist/internal/BetterAuth.d.ts.map +1 -0
- package/dist/internal/BetterAuth.js +253 -0
- package/dist/internal/BetterAuth.js.map +1 -0
- package/dist/internal/Cache.d.ts +2 -0
- package/dist/internal/Cache.d.ts.map +1 -1
- package/dist/internal/Cache.js +2 -0
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/EarnRates.d.ts +26 -3
- package/dist/internal/EarnRates.d.ts.map +1 -1
- package/dist/internal/EarnRates.js +84 -19
- package/dist/internal/EarnRates.js.map +1 -1
- package/dist/internal/EarnSharePrices.d.ts +120 -0
- package/dist/internal/EarnSharePrices.d.ts.map +1 -0
- package/dist/internal/EarnSharePrices.js +179 -0
- package/dist/internal/EarnSharePrices.js.map +1 -0
- package/dist/internal/EarnVaults.d.ts +4 -0
- package/dist/internal/EarnVaults.d.ts.map +1 -1
- package/dist/internal/EarnVaults.js +31 -14
- package/dist/internal/EarnVaults.js.map +1 -1
- package/dist/internal/Email.d.ts +37 -0
- package/dist/internal/Email.d.ts.map +1 -0
- package/dist/internal/Email.js +16 -0
- package/dist/internal/Email.js.map +1 -0
- package/dist/internal/Log.d.ts +32 -28
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +18 -11
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/MetricSink.d.ts +3 -0
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +67 -8
- package/dist/internal/MetricSink.js.map +1 -1
- package/dist/internal/Mpp.d.ts.map +1 -1
- package/dist/internal/Mpp.js +20 -25
- package/dist/internal/Mpp.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +26 -3
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +25 -10
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Provider.d.ts +1 -1
- package/dist/internal/Provider.d.ts.map +1 -1
- package/dist/internal/RateLimit.d.ts.map +1 -1
- package/dist/internal/RateLimit.js +13 -1
- package/dist/internal/RateLimit.js.map +1 -1
- package/dist/internal/Store.d.ts +16 -0
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +59 -8
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Tidx.d.ts +9 -2
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +78 -11
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/Timing.d.ts +26 -0
- package/dist/internal/Timing.d.ts.map +1 -1
- package/dist/internal/Timing.js +63 -5
- package/dist/internal/Timing.js.map +1 -1
- package/dist/internal/VercelOidc.d.ts +94 -0
- package/dist/internal/VercelOidc.d.ts.map +1 -0
- package/dist/internal/VercelOidc.js +128 -0
- package/dist/internal/VercelOidc.js.map +1 -0
- package/dist/internal/VerifiedTokens.d.ts +2 -8
- package/dist/internal/VerifiedTokens.d.ts.map +1 -1
- package/dist/internal/VerifiedTokens.js +24 -24
- package/dist/internal/VerifiedTokens.js.map +1 -1
- package/dist/internal/Viem.d.ts +46 -2068
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +171 -11
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/Webhooks.d.ts +12 -4
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +9 -3
- package/dist/internal/Webhooks.js.map +1 -1
- package/dist/internal/ZoneRpc.d.ts +7 -0
- package/dist/internal/ZoneRpc.d.ts.map +1 -0
- package/dist/internal/ZoneRpc.js +49 -0
- package/dist/internal/ZoneRpc.js.map +1 -0
- package/dist/internal/index.d.ts +3 -1
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +3 -1
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/rewards/Audit.d.ts +88 -0
- package/dist/internal/rewards/Audit.d.ts.map +1 -0
- package/dist/internal/rewards/Audit.js +134 -0
- package/dist/internal/rewards/Audit.js.map +1 -0
- package/dist/internal/rewards/Campaigns.d.ts +572 -0
- package/dist/internal/rewards/Campaigns.d.ts.map +1 -0
- package/dist/internal/rewards/Campaigns.js +531 -0
- package/dist/internal/rewards/Campaigns.js.map +1 -0
- package/dist/internal/rewards/Credits.d.ts +76 -0
- package/dist/internal/rewards/Credits.d.ts.map +1 -0
- package/dist/internal/rewards/Credits.js +95 -0
- package/dist/internal/rewards/Credits.js.map +1 -0
- package/dist/internal/rewards/Events.d.ts +100 -0
- package/dist/internal/rewards/Events.d.ts.map +1 -0
- package/dist/internal/rewards/Events.js +340 -0
- package/dist/internal/rewards/Events.js.map +1 -0
- package/dist/internal/rewards/Projection.d.ts +237 -0
- package/dist/internal/rewards/Projection.d.ts.map +1 -0
- package/dist/internal/rewards/Projection.js +616 -0
- package/dist/internal/rewards/Projection.js.map +1 -0
- package/dist/internal/rewards/Rates.d.ts +44 -0
- package/dist/internal/rewards/Rates.d.ts.map +1 -0
- package/dist/internal/rewards/Rates.js +60 -0
- package/dist/internal/rewards/Rates.js.map +1 -0
- package/dist/internal/rewards/Read.d.ts +158 -0
- package/dist/internal/rewards/Read.d.ts.map +1 -0
- package/dist/internal/rewards/Read.js +292 -0
- package/dist/internal/rewards/Read.js.map +1 -0
- package/dist/internal/rewards/Runner.d.ts +164 -0
- package/dist/internal/rewards/Runner.d.ts.map +1 -0
- package/dist/internal/rewards/Runner.js +1778 -0
- package/dist/internal/rewards/Runner.js.map +1 -0
- package/dist/internal/rewards/Signer.d.ts +224 -0
- package/dist/internal/rewards/Signer.d.ts.map +1 -0
- package/dist/internal/rewards/Signer.js +135 -0
- package/dist/internal/rewards/Signer.js.map +1 -0
- package/dist/internal/rewards/index.d.ts +15 -0
- package/dist/internal/rewards/index.d.ts.map +1 -0
- package/dist/internal/rewards/index.js +15 -0
- package/dist/internal/rewards/index.js.map +1 -0
- package/dist/internal/{funding → routes}/Action.d.ts +12 -1
- package/dist/internal/routes/Action.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Action.js +17 -9
- package/dist/internal/routes/Action.js.map +1 -0
- package/dist/internal/{funding → routes}/Catalog.d.ts +20 -20
- package/dist/internal/routes/Catalog.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Catalog.js +29 -29
- package/dist/internal/routes/Catalog.js.map +1 -0
- package/dist/internal/{funding → routes}/Chain.d.ts +10 -10
- package/dist/internal/routes/Chain.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Chain.js +3 -3
- package/dist/internal/routes/Chain.js.map +1 -0
- package/dist/internal/{funding → routes}/Deposit.d.ts +45 -23
- package/dist/internal/routes/Deposit.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Deposit.js +60 -27
- package/dist/internal/routes/Deposit.js.map +1 -0
- package/dist/internal/{funding → routes}/DepositAddress.d.ts +56 -27
- package/dist/internal/routes/DepositAddress.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/DepositAddress.js +95 -33
- package/dist/internal/routes/DepositAddress.js.map +1 -0
- package/dist/internal/{funding → routes}/DepositWebhook.d.ts +4 -4
- package/dist/internal/routes/DepositWebhook.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/DepositWebhook.js +4 -3
- package/dist/internal/routes/DepositWebhook.js.map +1 -0
- package/dist/internal/routes/Economics.d.ts +25 -0
- package/dist/internal/routes/Economics.d.ts.map +1 -0
- package/dist/internal/routes/Economics.js +28 -0
- package/dist/internal/routes/Economics.js.map +1 -0
- package/dist/internal/{funding → routes}/Evidence.d.ts +3 -3
- package/dist/internal/routes/Evidence.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Evidence.js +9 -17
- package/dist/internal/routes/Evidence.js.map +1 -0
- package/dist/internal/routes/Observability.d.ts +154 -0
- package/dist/internal/routes/Observability.d.ts.map +1 -0
- package/dist/internal/routes/Observability.js +389 -0
- package/dist/internal/routes/Observability.js.map +1 -0
- package/dist/internal/{funding → routes}/Provider.d.ts +350 -104
- package/dist/internal/routes/Provider.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Provider.js +233 -97
- package/dist/internal/routes/Provider.js.map +1 -0
- package/dist/internal/{funding → routes}/Reconciliation.d.ts +11 -6
- package/dist/internal/routes/Reconciliation.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Reconciliation.js +408 -158
- package/dist/internal/routes/Reconciliation.js.map +1 -0
- package/dist/internal/routes/Route.d.ts +21 -0
- package/dist/internal/routes/Route.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Route.js +1 -1
- package/dist/internal/routes/Route.js.map +1 -0
- package/dist/internal/{funding → routes}/SourceObservation.d.ts +6 -6
- package/dist/internal/routes/SourceObservation.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/SourceObservation.js +13 -15
- package/dist/internal/routes/SourceObservation.js.map +1 -0
- package/dist/internal/{funding → routes}/Subsidy.d.ts +48 -3
- package/dist/internal/routes/Subsidy.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Subsidy.js +43 -3
- package/dist/internal/routes/Subsidy.js.map +1 -0
- package/dist/internal/{funding → routes}/Token.d.ts +4 -4
- package/dist/internal/routes/Token.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Token.js +3 -3
- package/dist/internal/routes/Token.js.map +1 -0
- package/dist/internal/{funding → routes}/Transfer.d.ts +324 -34
- package/dist/internal/routes/Transfer.d.ts.map +1 -0
- package/dist/internal/routes/Transfer.js +728 -0
- package/dist/internal/routes/Transfer.js.map +1 -0
- package/dist/internal/routes/TransferReconciliation.d.ts +55 -0
- package/dist/internal/routes/TransferReconciliation.d.ts.map +1 -0
- package/dist/internal/routes/TransferReconciliation.js +422 -0
- package/dist/internal/routes/TransferReconciliation.js.map +1 -0
- package/dist/internal/routes/TransferSubsidy.d.ts +201 -0
- package/dist/internal/routes/TransferSubsidy.d.ts.map +1 -0
- package/dist/internal/routes/TransferSubsidy.js +256 -0
- package/dist/internal/routes/TransferSubsidy.js.map +1 -0
- package/dist/internal/routes/TransferWebhook.d.ts +21 -0
- package/dist/internal/routes/TransferWebhook.d.ts.map +1 -0
- package/dist/internal/routes/TransferWebhook.js +43 -0
- package/dist/internal/routes/TransferWebhook.js.map +1 -0
- package/dist/internal/{funding → routes}/index.d.ts +3 -0
- package/dist/internal/routes/index.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/index.js +3 -0
- package/dist/internal/routes/index.js.map +1 -0
- package/dist/internal/{funding → routes}/providers/across.d.ts +4 -4
- package/dist/internal/routes/providers/across.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/providers/across.js +13 -13
- package/dist/internal/routes/providers/across.js.map +1 -0
- package/dist/internal/{funding → routes}/providers/relay.d.ts +21 -12
- package/dist/internal/routes/providers/relay.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/providers/relay.js +197 -122
- package/dist/internal/routes/providers/relay.js.map +1 -0
- package/dist/internal/{funding → routes}/providers/rhino.d.ts +4 -4
- package/dist/internal/routes/providers/rhino.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/providers/rhino.js +10 -10
- package/dist/internal/routes/providers/rhino.js.map +1 -0
- package/dist/internal/{funding → routes}/providers/squid.d.ts +4 -4
- package/dist/internal/routes/providers/squid.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/providers/squid.js +14 -14
- package/dist/internal/routes/providers/squid.js.map +1 -0
- package/dist/internal/routes/providers/stargate.d.ts +165 -0
- package/dist/internal/routes/providers/stargate.d.ts.map +1 -0
- package/dist/internal/routes/providers/stargate.js +898 -0
- package/dist/internal/routes/providers/stargate.js.map +1 -0
- package/dist/internal/{funding → routes}/providers/symbiosis.d.ts +4 -4
- package/dist/internal/routes/providers/symbiosis.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/providers/symbiosis.js +30 -20
- package/dist/internal/routes/providers/symbiosis.js.map +1 -0
- package/dist/internal/webhookDestinations/slack.d.ts.map +1 -1
- package/dist/internal/webhookDestinations/slack.js +22 -6
- package/dist/internal/webhookDestinations/slack.js.map +1 -1
- package/dist/providers/index.d.ts +12 -12
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +12 -12
- package/dist/providers/index.js.map +1 -1
- package/dist/server.d.ts +19 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +19 -0
- package/dist/server.js.map +1 -0
- package/package.json +15 -4
- package/src/ApiKey.ts +1 -1
- package/src/ApiKeys.test.ts +1033 -3
- package/src/ApiKeys.ts +489 -48
- package/src/App.test-d.ts +1 -1
- package/src/App.test.ts +216 -25
- package/src/App.ts +335 -31
- package/src/Assets.test.ts +2 -2
- package/src/Assets.ts +3 -1
- package/src/Auth.test-d.ts +1 -1
- package/src/Client.test-d.ts +611 -101
- package/src/Handler.ts +0 -1
- package/src/OpenApi.contract.test.ts +195 -0
- package/src/Scope.ts +17 -5
- package/src/Webhooks.ts +2 -2
- package/src/admin/App.test.ts +30 -2
- package/src/admin/App.ts +42 -6
- package/src/admin/apps/api-keys.test.ts +267 -5
- package/src/admin/apps/api-keys.ts +100 -50
- package/src/admin/apps/earn-vaults.test.ts +158 -0
- package/src/admin/apps/earn-vaults.ts +681 -7
- package/src/admin/apps/organizations.test.ts +342 -1
- package/src/admin/apps/organizations.ts +368 -8
- package/src/admin/apps/routes-support.test.ts +200 -0
- package/src/admin/apps/routes-support.ts +201 -0
- package/src/admin/apps/routes.test.ts +450 -0
- package/src/admin/apps/routes.ts +896 -0
- package/src/admin/apps/scopes.test.ts +15 -5
- package/src/admin/apps/subsidies.test.ts +306 -0
- package/src/admin/apps/subsidies.ts +457 -0
- package/src/admin/apps/verified-tokens.test.ts +8 -3
- package/src/admin/apps/verified-tokens.ts +3 -3
- package/src/admin/ui/plugin.ts +1 -2
- package/src/admin/ui/src/components/RewardCampaignForm.tsx +1009 -0
- package/src/admin/ui/src/lib/config.ts +11 -2
- package/src/admin/ui/src/lib/earn-vaults.ts +2 -2
- package/src/admin/ui/src/lib/organizations.test.ts +147 -3
- package/src/admin/ui/src/lib/organizations.ts +136 -0
- package/src/admin/ui/src/lib/query-client.ts +1 -1
- package/src/admin/ui/src/lib/reward-campaigns.ts +136 -0
- package/src/admin/ui/src/lib/routes.test.ts +88 -0
- package/src/admin/ui/src/lib/routes.ts +304 -0
- package/src/admin/ui/src/lib/subsidies.ts +75 -0
- package/src/admin/ui/src/routeTree.gen.ts +63 -0
- package/src/admin/ui/src/routes/__root.tsx +12 -1
- package/src/admin/ui/src/routes/api-keys.tsx +196 -84
- package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +105 -70
- package/src/admin/ui/src/routes/organizations_.$orgId.tsx +334 -2
- package/src/admin/ui/src/routes/routes.tsx +1615 -0
- package/src/admin/ui/src/routes/routes_.support.tsx +248 -0
- package/src/admin/ui/src/routes/subsidies.tsx +799 -0
- package/src/admin/ui.generated.ts +144 -69
- package/src/analytics/Analytics.test.ts +108 -18
- package/src/analytics/Analytics.ts +38 -6
- package/src/analytics/tables/requestEvents.test.ts +100 -0
- package/src/analytics/tables/requestEvents.ts +10 -1
- package/src/analytics/tables/routesLifecycleIntervals.ts +140 -0
- package/src/apps/Zones.test.ts +18 -3
- package/src/apps/Zones.ts +55 -8
- package/src/apps/composition.test.ts +68 -59
- package/src/apps/data/App.test.ts +154 -2
- package/src/apps/data/App.ts +102 -33
- package/src/apps/data/routes/activities.test.ts +419 -7
- package/src/apps/data/routes/activities.ts +683 -216
- package/src/apps/data/routes/balances.test.ts +2 -2
- package/src/apps/data/routes/blocks.test.ts +46 -1
- package/src/apps/data/routes/coingecko.test.ts +419 -1
- package/src/apps/data/routes/coingecko.ts +309 -253
- package/src/apps/data/routes/earn.discovery.test.ts +274 -0
- package/src/apps/data/routes/earn.test.ts +1661 -29
- package/src/apps/data/routes/earn.ts +2314 -328
- package/src/apps/data/routes/exchanges.test.ts +752 -30
- package/src/apps/data/routes/exchanges.ts +954 -753
- package/src/apps/data/routes/fee-amm.test.ts +224 -0
- package/src/apps/data/routes/fee-amm.ts +208 -178
- package/src/apps/data/routes/indexer.test.ts +389 -0
- package/src/apps/data/routes/indexer.ts +165 -69
- package/src/apps/data/routes/receipts.test.ts +1 -1
- package/src/apps/data/routes/rpc.test.ts +352 -8
- package/src/apps/data/routes/rpc.ts +407 -219
- package/src/apps/data/routes/rpc.zone.test.ts +340 -0
- package/src/apps/data/routes/tokenlist.test.ts +285 -0
- package/src/apps/data/routes/tokenlist.ts +173 -88
- package/src/apps/data/routes/tokens.test.ts +93 -3
- package/src/apps/data/routes/tokens.ts +39 -23
- package/src/apps/data/routes/transactions.test.ts +1 -1
- package/src/apps/data/routes/transfers.test.ts +56 -24
- package/src/apps/data/routes/transfers.ts +19 -8
- package/src/apps/data/routes/valuation.test.ts +1 -1
- package/src/apps/data/routes/verified-tokens.test.ts +345 -0
- package/src/apps/data/routes/verified-tokens.ts +99 -29
- package/src/apps/data/routes/webhooks.test.ts +659 -81
- package/src/apps/data/routes/webhooks.ts +1104 -817
- package/src/apps/data/routes/zones.test.ts +148 -71
- package/src/apps/data/routes/zones.ts +71 -55
- package/src/apps/docs/reference.ts +2 -2
- package/src/apps/index.ts +1 -1
- package/src/apps/management/App.ts +9 -51
- package/src/apps/management/Billing.test.ts +375 -14
- package/src/apps/management/Billing.ts +283 -34
- package/src/apps/management/routes/api-keys.test.ts +269 -7
- package/src/apps/management/routes/api-keys.ts +61 -20
- package/src/apps/management/routes/billing.test.ts +477 -15
- package/src/apps/management/routes/billing.ts +377 -50
- package/src/apps/management/routes/faucet.test.ts +1 -1
- package/src/apps/management/routes/faucet.ts +1 -1
- package/src/apps/management/routes/invitations.test.ts +270 -3
- package/src/apps/management/routes/invitations.ts +185 -15
- package/src/apps/management/routes/invoicePreview.test.ts +549 -0
- package/src/apps/management/routes/me.test.ts +246 -5
- package/src/apps/management/routes/me.ts +7 -2
- package/src/apps/management/routes/members.test.ts +108 -0
- package/src/apps/management/routes/orgs.test.ts +191 -3
- package/src/apps/management/routes/orgs.ts +58 -11
- package/src/apps/management/routes/projects.test.ts +33 -3
- package/src/apps/management/routes/projects.ts +6 -1
- package/src/apps/management/routes/scopes.test.ts +30 -2
- package/src/apps/management/routes/scopes.ts +4 -1
- package/src/apps/management/routes/usage.test.ts +32 -0
- package/src/apps/management/routes/usage.ts +22 -4
- package/src/apps/management/routes/webhooks.test.ts +49 -26
- package/src/apps/management/routes/webhooks.ts +93 -38
- package/src/apps/mcp.test.ts +6 -6
- package/src/apps/mcp.ts +7 -7
- package/src/apps/mpp/App.ts +37 -7
- package/src/apps/mpp/Mpp.test.ts +49 -8
- package/src/apps/mpp/Mpp.zone.test.ts +219 -0
- package/src/apps/mpp/internal/Adapter.ts +34 -0
- package/src/apps/mpp/internal/Policy.test.ts +86 -31
- package/src/apps/mpp/internal/Policy.ts +49 -7
- package/src/apps/mpp/internal/Sponsorship.test.ts +207 -0
- package/src/apps/mpp/internal/Sponsorship.ts +9 -3
- package/src/apps/relay/App.test.ts +1024 -38
- package/src/apps/relay/App.ts +228 -32
- package/src/apps/relay/App.zone-billing.test.ts +257 -0
- package/src/apps/relay/Sponsorships.test.ts +67 -4
- package/src/apps/relay/Sponsorships.ts +253 -29
- package/src/apps/relay/Sponsorships.zone.test.ts +102 -0
- package/src/apps/{funding → routes}/Access.ts +6 -6
- package/src/apps/routes/App.test.ts +165 -0
- package/src/apps/routes/App.ts +96 -0
- package/src/apps/{funding → routes}/Inventory.ts +2 -2
- package/src/apps/routes/index.ts +1 -0
- package/src/apps/{funding → routes}/routes/chains.test.ts +66 -66
- package/src/apps/{funding → routes}/routes/chains.ts +23 -23
- package/src/apps/{funding → routes}/routes/deposit-addresses.test.ts +1362 -462
- package/src/apps/{funding → routes}/routes/deposit-addresses.ts +568 -248
- package/src/apps/{funding → routes}/routes/providers.test.ts +27 -33
- package/src/apps/{funding → routes}/routes/providers.ts +23 -23
- package/src/apps/{funding → routes}/routes/quotes.test.ts +298 -41
- package/src/apps/{funding → routes}/routes/quotes.ts +57 -40
- package/src/apps/routes/routes/transfers.test.ts +2876 -0
- package/src/apps/routes/routes/transfers.ts +1332 -0
- package/src/cli/Cli.test.ts +1 -1
- package/src/cli/commands/admin.test.ts +748 -1
- package/src/cli/commands/admin.ts +278 -0
- package/src/cloudflare.ts +52 -47
- package/src/db/Db.test.ts +245 -2
- package/src/db/Db.ts +1511 -34
- package/src/db/Schema.ts +744 -228
- package/src/db/tables/apiKeyAdmissionBootstraps.ts +4 -0
- package/src/db/tables/apiKeyAdmissions.ts +218 -0
- package/src/db/tables/apiKeyOwnerAdmissions.ts +4 -0
- package/src/db/tables/apiKeyOwnerTombstones.ts +81 -0
- package/src/db/tables/apiKeyRevocations.test.ts +74 -0
- package/src/db/tables/apiKeyRevocations.ts +121 -0
- package/src/db/tables/authAccounts.ts +105 -0
- package/src/db/tables/authSessions.ts +13 -0
- package/src/db/tables/authVerifications.ts +13 -0
- package/src/db/tables/invitations.ts +142 -45
- package/src/db/tables/memberships.ts +79 -3
- package/src/db/tables/organizations.test.ts +725 -7
- package/src/db/tables/organizations.ts +182 -49
- package/src/db/tables/projects.test.ts +71 -0
- package/src/db/tables/projects.ts +59 -4
- package/src/db/tables/requestUsage.ts +4 -1
- package/src/db/tables/rewardAccounts.ts +178 -0
- package/src/db/tables/rewardCampaignDistributors.ts +68 -0
- package/src/db/tables/rewardCampaigns.test.ts +1388 -0
- package/src/db/tables/rewardCampaigns.ts +647 -0
- package/src/db/tables/rewardCredits.ts +110 -0
- package/src/db/tables/rewardEligibilityAssociations.test.ts +271 -0
- package/src/db/tables/rewardEligibilityAssociations.ts +184 -0
- package/src/db/tables/rewardRuns.ts +514 -0
- package/src/db/tables/rewardTransactionAttempts.ts +272 -0
- package/src/db/tables/routesCatalog.test.ts +185 -0
- package/src/db/tables/routesCatalog.ts +163 -0
- package/src/db/tables/routesDepositAddresses.test.ts +1048 -0
- package/src/db/tables/routesDepositAddresses.ts +1008 -0
- package/src/db/tables/{fundingDepositRequestObservations.test.ts → routesDepositRequestObservations.test.ts} +13 -14
- package/src/db/tables/{fundingDepositRequestObservations.ts → routesDepositRequestObservations.ts} +10 -10
- package/src/db/tables/{fundingDeposits.test.ts → routesDeposits.test.ts} +316 -46
- package/src/db/tables/routesDeposits.ts +1062 -0
- package/src/db/tables/routesIdempotency.test.ts +796 -0
- package/src/db/tables/routesIdempotency.ts +496 -0
- package/src/db/tables/routesSubsidies.test.ts +120 -0
- package/src/db/tables/routesSubsidies.ts +71 -0
- package/src/db/tables/{fundingTransferEvents.ts → routesTransferEvents.ts} +4 -4
- package/src/db/tables/routesTransferSubsidies.ts +38 -0
- package/src/db/tables/routesTransferSubsidyNonces.ts +124 -0
- package/src/db/tables/routesTransferTransactions.test.ts +195 -0
- package/src/db/tables/routesTransferTransactions.ts +158 -0
- package/src/db/tables/routesTransfers.test.ts +338 -0
- package/src/db/tables/routesTransfers.ts +662 -0
- package/src/db/tables/sponsoredTransactions.test.ts +563 -1
- package/src/db/tables/sponsoredTransactions.ts +386 -42
- package/src/db/tables/sponsorshipAttributions.test.ts +160 -0
- package/src/db/tables/sponsorshipAttributions.ts +168 -0
- package/src/db/tables/sponsorshipReconciliationCursors.test.ts +115 -0
- package/src/db/tables/sponsorshipReconciliationCursors.ts +67 -0
- package/src/db/tables/stripeCustomers.test.ts +85 -0
- package/src/db/tables/stripeCustomers.ts +24 -0
- package/src/db/tables/users.test.ts +129 -0
- package/src/db/tables/users.ts +49 -3
- package/src/db/tables/webhookQueueEvents.ts +8 -1
- package/src/db/tables/webhookSubscriptions.ts +103 -21
- package/src/handlers/internal/executionError.ts +3 -4
- package/src/handlers/internal/multisig.ts +13 -816
- package/src/handlers/internal/sponsorship.ts +26 -10
- package/src/handlers/relay.test-d.ts +9 -1
- package/src/handlers/relay.test.ts +823 -3
- package/src/handlers/relay.ts +127 -107
- package/src/index.test-d.ts +7 -0
- package/src/index.ts +0 -17
- package/src/internal/AssetResponse.ts +26 -0
- package/src/internal/Auth.test.ts +1564 -9
- package/src/internal/Auth.ts +833 -239
- package/src/internal/BetterAuth.test.ts +242 -0
- package/src/internal/BetterAuth.ts +349 -0
- package/src/internal/Cache.ts +2 -0
- package/src/internal/EarnRates.test.ts +17 -0
- package/src/internal/EarnRates.ts +124 -31
- package/src/internal/EarnSharePrices.test.ts +182 -0
- package/src/internal/EarnSharePrices.ts +313 -0
- package/src/internal/EarnVaults.test.ts +21 -0
- package/src/internal/EarnVaults.ts +37 -15
- package/src/internal/Email.ts +54 -0
- package/src/internal/Idempotency.test.ts +2 -2
- package/src/internal/Log.test-d.ts +10 -7
- package/src/internal/Log.test.ts +114 -16
- package/src/internal/Log.ts +55 -40
- package/src/internal/MetricSink.test.ts +313 -26
- package/src/internal/MetricSink.ts +73 -8
- package/src/internal/Mpp.test.ts +31 -0
- package/src/internal/Mpp.ts +21 -24
- package/src/internal/OpenApi.ts +45 -12
- package/src/internal/Provider.test.ts +1 -1
- package/src/internal/Provider.ts +1 -1
- package/src/internal/RateLimit.test.ts +21 -3
- package/src/internal/RateLimit.ts +12 -1
- package/src/internal/Store.test-d.ts +1 -1
- package/src/internal/Store.test.ts +151 -8
- package/src/internal/Store.ts +76 -9
- package/src/internal/Tidx.test.ts +59 -8
- package/src/internal/Tidx.ts +86 -14
- package/src/internal/Timing.test.ts +77 -0
- package/src/internal/Timing.ts +83 -6
- package/src/internal/VercelOidc.test.ts +188 -0
- package/src/internal/VercelOidc.ts +193 -0
- package/src/internal/VerifiedTokens.test.ts +60 -6
- package/src/internal/VerifiedTokens.ts +21 -24
- package/src/internal/Viem.batch.test.ts +80 -0
- package/src/internal/Viem.test.ts +201 -2
- package/src/internal/Viem.ts +249 -17
- package/src/internal/Webhooks.test.ts +125 -1
- package/src/internal/Webhooks.ts +22 -4
- package/src/internal/ZoneRpc.ts +50 -0
- package/src/internal/exchange/Provider.test.ts +2 -2
- package/src/internal/index.test.ts +3 -1
- package/src/internal/index.ts +3 -1
- package/src/internal/rewards/Audit.ts +231 -0
- package/src/internal/rewards/Campaigns.test.ts +1234 -0
- package/src/internal/rewards/Campaigns.ts +1205 -0
- package/src/internal/rewards/Credits.ts +165 -0
- package/src/internal/rewards/Events.local.test.ts +113 -0
- package/src/internal/rewards/Events.test.ts +152 -0
- package/src/internal/rewards/Events.ts +468 -0
- package/src/internal/rewards/Projection.test.ts +681 -0
- package/src/internal/rewards/Projection.ts +987 -0
- package/src/internal/rewards/Rates.test.ts +171 -0
- package/src/internal/rewards/Rates.ts +110 -0
- package/src/internal/rewards/Read.test.ts +501 -0
- package/src/internal/rewards/Read.ts +476 -0
- package/src/internal/rewards/Runner.checkpoint.test.ts +80 -0
- package/src/internal/rewards/Runner.states.test.ts +188 -0
- package/src/internal/rewards/Runner.test.ts +90 -0
- package/src/internal/rewards/Runner.ts +2229 -0
- package/src/internal/rewards/Signer.test.ts +79 -0
- package/src/internal/rewards/Signer.ts +180 -0
- package/src/internal/rewards/index.ts +14 -0
- package/src/internal/{funding → routes}/Action.test.ts +63 -14
- package/src/internal/{funding → routes}/Action.ts +35 -8
- package/src/internal/{funding → routes}/Catalog.test.ts +3 -3
- package/src/internal/{funding → routes}/Catalog.ts +39 -39
- package/src/internal/{funding → routes}/Chain.test.ts +1 -1
- package/src/internal/{funding → routes}/Chain.ts +11 -11
- package/src/internal/{funding → routes}/Deposit.test.ts +1 -1
- package/src/internal/{funding → routes}/Deposit.ts +93 -45
- package/src/internal/{funding → routes}/DepositAddress.test.ts +11 -3
- package/src/internal/{funding → routes}/DepositAddress.ts +132 -53
- package/src/internal/{funding → routes}/DepositWebhook.test.ts +61 -18
- package/src/internal/{funding → routes}/DepositWebhook.ts +9 -8
- package/src/internal/routes/Economics.test.ts +42 -0
- package/src/internal/routes/Economics.ts +47 -0
- package/src/internal/{funding → routes}/Evidence.test.ts +6 -6
- package/src/internal/{funding → routes}/Evidence.ts +11 -22
- package/src/internal/routes/Observability.test.ts +636 -0
- package/src/internal/routes/Observability.ts +668 -0
- package/src/internal/{funding → routes}/Provider.test.ts +489 -153
- package/src/internal/{funding → routes}/Provider.ts +531 -185
- package/src/internal/{funding → routes}/Reconciliation.local.test.ts +84 -12
- package/src/internal/{funding → routes}/Reconciliation.test.ts +1213 -279
- package/src/internal/{funding → routes}/Reconciliation.ts +501 -203
- package/src/internal/routes/Reconciliation.unattributed.test.ts +250 -0
- package/src/internal/{funding → routes}/Route.ts +7 -7
- package/src/internal/{funding → routes}/SourceObservation.test.ts +13 -14
- package/src/internal/{funding → routes}/SourceObservation.ts +18 -22
- package/src/internal/{funding → routes}/Subsidy.local.test.ts +7 -7
- package/src/internal/routes/Subsidy.test.ts +169 -0
- package/src/internal/{funding → routes}/Subsidy.ts +86 -6
- package/src/internal/{funding → routes}/Token.test.ts +1 -1
- package/src/internal/{funding → routes}/Token.ts +5 -5
- package/src/internal/routes/Transfer.test.ts +1199 -0
- package/src/internal/routes/Transfer.ts +1206 -0
- package/src/internal/routes/TransferReconciliation.test.ts +731 -0
- package/src/internal/routes/TransferReconciliation.ts +527 -0
- package/src/internal/routes/TransferSubsidy.test.ts +172 -0
- package/src/internal/routes/TransferSubsidy.ts +447 -0
- package/src/internal/routes/TransferWebhook.test.ts +128 -0
- package/src/internal/routes/TransferWebhook.ts +67 -0
- package/src/internal/{funding → routes}/index.test.ts +6 -3
- package/src/internal/{funding → routes}/index.ts +3 -0
- package/src/internal/{funding → routes}/providers/across.ts +16 -16
- package/src/internal/{funding → routes}/providers/index.test.ts +157 -44
- package/src/internal/{funding → routes}/providers/relay.test.ts +165 -32
- package/src/internal/{funding → routes}/providers/relay.ts +231 -136
- package/src/internal/{funding → routes}/providers/rhino.ts +16 -16
- package/src/internal/{funding → routes}/providers/squid.ts +17 -17
- package/src/internal/routes/providers/stargate.test.ts +728 -0
- package/src/internal/routes/providers/stargate.ts +1276 -0
- package/src/internal/{funding → routes}/providers/symbiosis.ts +64 -24
- package/src/internal/webhookDestinations/slack.ts +20 -5
- package/src/providers/index.ts +13 -13
- package/src/server.ts +18 -0
- package/dist/apps/funding/Access.d.ts.map +0 -1
- package/dist/apps/funding/Access.js.map +0 -1
- package/dist/apps/funding/App.js.map +0 -1
- package/dist/apps/funding/Inventory.d.ts.map +0 -1
- package/dist/apps/funding/Inventory.js.map +0 -1
- package/dist/apps/funding/ProviderAttempts.d.ts +0 -5
- package/dist/apps/funding/ProviderAttempts.d.ts.map +0 -1
- package/dist/apps/funding/ProviderAttempts.js +0 -24
- package/dist/apps/funding/ProviderAttempts.js.map +0 -1
- package/dist/apps/funding/index.d.ts +0 -2
- package/dist/apps/funding/index.d.ts.map +0 -1
- package/dist/apps/funding/index.js +0 -2
- package/dist/apps/funding/index.js.map +0 -1
- package/dist/apps/funding/routes/chains.d.ts.map +0 -1
- package/dist/apps/funding/routes/chains.js.map +0 -1
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +0 -1
- package/dist/apps/funding/routes/deposit-addresses.js.map +0 -1
- package/dist/apps/funding/routes/providers.d.ts.map +0 -1
- package/dist/apps/funding/routes/providers.js +0 -57
- package/dist/apps/funding/routes/providers.js.map +0 -1
- package/dist/apps/funding/routes/quotes.d.ts.map +0 -1
- package/dist/apps/funding/routes/quotes.js.map +0 -1
- package/dist/apps/funding/routes/transfers.d.ts.map +0 -1
- package/dist/apps/funding/routes/transfers.js +0 -622
- package/dist/apps/funding/routes/transfers.js.map +0 -1
- package/dist/db/tables/fundingCatalog.d.ts +0 -58
- package/dist/db/tables/fundingCatalog.d.ts.map +0 -1
- package/dist/db/tables/fundingCatalog.js.map +0 -1
- package/dist/db/tables/fundingDepositAddresses.d.ts +0 -192
- package/dist/db/tables/fundingDepositAddresses.d.ts.map +0 -1
- package/dist/db/tables/fundingDepositAddresses.js +0 -235
- package/dist/db/tables/fundingDepositAddresses.js.map +0 -1
- package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +0 -1
- package/dist/db/tables/fundingDepositRequestObservations.js.map +0 -1
- package/dist/db/tables/fundingDeposits.d.ts.map +0 -1
- package/dist/db/tables/fundingDeposits.js +0 -295
- package/dist/db/tables/fundingDeposits.js.map +0 -1
- package/dist/db/tables/fundingIdempotency.d.ts +0 -91
- package/dist/db/tables/fundingIdempotency.d.ts.map +0 -1
- package/dist/db/tables/fundingIdempotency.js +0 -103
- package/dist/db/tables/fundingIdempotency.js.map +0 -1
- package/dist/db/tables/fundingTransferEvents.d.ts.map +0 -1
- package/dist/db/tables/fundingTransferEvents.js.map +0 -1
- package/dist/db/tables/fundingTransferTransactions.d.ts +0 -15
- package/dist/db/tables/fundingTransferTransactions.d.ts.map +0 -1
- package/dist/db/tables/fundingTransferTransactions.js +0 -27
- package/dist/db/tables/fundingTransferTransactions.js.map +0 -1
- package/dist/db/tables/fundingTransfers.d.ts +0 -82
- package/dist/db/tables/fundingTransfers.d.ts.map +0 -1
- package/dist/db/tables/fundingTransfers.js +0 -89
- package/dist/db/tables/fundingTransfers.js.map +0 -1
- package/dist/internal/funding/Action.d.ts.map +0 -1
- package/dist/internal/funding/Action.js.map +0 -1
- package/dist/internal/funding/Catalog.d.ts.map +0 -1
- package/dist/internal/funding/Catalog.js.map +0 -1
- package/dist/internal/funding/Chain.d.ts.map +0 -1
- package/dist/internal/funding/Chain.js.map +0 -1
- package/dist/internal/funding/Deposit.d.ts.map +0 -1
- package/dist/internal/funding/Deposit.js.map +0 -1
- package/dist/internal/funding/DepositAddress.d.ts.map +0 -1
- package/dist/internal/funding/DepositAddress.js.map +0 -1
- package/dist/internal/funding/DepositWebhook.d.ts.map +0 -1
- package/dist/internal/funding/DepositWebhook.js.map +0 -1
- package/dist/internal/funding/Evidence.d.ts.map +0 -1
- package/dist/internal/funding/Evidence.js.map +0 -1
- package/dist/internal/funding/Provider.d.ts.map +0 -1
- package/dist/internal/funding/Provider.js.map +0 -1
- package/dist/internal/funding/Reconciliation.d.ts.map +0 -1
- package/dist/internal/funding/Reconciliation.js.map +0 -1
- package/dist/internal/funding/Route.d.ts +0 -21
- package/dist/internal/funding/Route.d.ts.map +0 -1
- package/dist/internal/funding/Route.js.map +0 -1
- package/dist/internal/funding/SourceObservation.d.ts.map +0 -1
- package/dist/internal/funding/SourceObservation.js.map +0 -1
- package/dist/internal/funding/Subsidy.d.ts.map +0 -1
- package/dist/internal/funding/Subsidy.js.map +0 -1
- package/dist/internal/funding/Token.d.ts.map +0 -1
- package/dist/internal/funding/Token.js.map +0 -1
- package/dist/internal/funding/Transfer.d.ts.map +0 -1
- package/dist/internal/funding/Transfer.js +0 -334
- package/dist/internal/funding/Transfer.js.map +0 -1
- package/dist/internal/funding/index.d.ts.map +0 -1
- package/dist/internal/funding/index.js.map +0 -1
- package/dist/internal/funding/providers/across.d.ts.map +0 -1
- package/dist/internal/funding/providers/across.js.map +0 -1
- package/dist/internal/funding/providers/relay.d.ts.map +0 -1
- package/dist/internal/funding/providers/relay.js.map +0 -1
- package/dist/internal/funding/providers/rhino.d.ts.map +0 -1
- package/dist/internal/funding/providers/rhino.js.map +0 -1
- package/dist/internal/funding/providers/squid.d.ts.map +0 -1
- package/dist/internal/funding/providers/squid.js.map +0 -1
- package/dist/internal/funding/providers/stargate.d.ts +0 -38
- package/dist/internal/funding/providers/stargate.d.ts.map +0 -1
- package/dist/internal/funding/providers/stargate.js +0 -277
- package/dist/internal/funding/providers/stargate.js.map +0 -1
- package/dist/internal/funding/providers/symbiosis.d.ts.map +0 -1
- package/dist/internal/funding/providers/symbiosis.js.map +0 -1
- package/src/apps/funding/App.test.ts +0 -78
- package/src/apps/funding/App.ts +0 -78
- package/src/apps/funding/ProviderAttempts.ts +0 -26
- package/src/apps/funding/index.ts +0 -1
- package/src/apps/funding/routes/transfers.test.ts +0 -1638
- package/src/apps/funding/routes/transfers.ts +0 -721
- package/src/db/tables/fundingCatalog.test.ts +0 -122
- package/src/db/tables/fundingCatalog.ts +0 -163
- package/src/db/tables/fundingDepositAddresses.test.ts +0 -608
- package/src/db/tables/fundingDepositAddresses.ts +0 -443
- package/src/db/tables/fundingDeposits.ts +0 -572
- package/src/db/tables/fundingIdempotency.test.ts +0 -182
- package/src/db/tables/fundingIdempotency.ts +0 -180
- package/src/db/tables/fundingTransferTransactions.test.ts +0 -69
- package/src/db/tables/fundingTransferTransactions.ts +0 -37
- package/src/db/tables/fundingTransfers.test.ts +0 -89
- package/src/db/tables/fundingTransfers.ts +0 -153
- package/src/handlers/internal/multisig.test.ts +0 -818
- package/src/internal/funding/Subsidy.test.ts +0 -75
- package/src/internal/funding/Transfer.test.ts +0 -369
- package/src/internal/funding/Transfer.ts +0 -545
- package/src/internal/funding/providers/stargate.test.ts +0 -163
- package/src/internal/funding/providers/stargate.ts +0 -409
- /package/dist/apps/{funding → routes}/Inventory.d.ts +0 -0
- /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
|
+
}
|