tapimo 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/App.d.ts +11333 -9563
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +39 -3
- package/dist/App.js.map +1 -1
- package/dist/Scope.d.ts +2 -0
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +17 -0
- 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 +1064 -924
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +80 -0
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +54 -0
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/earn-vaults.d.ts +63 -15
- package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.js +2 -1
- package/dist/admin/apps/earn-vaults.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +92 -3
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/organizations.js +69 -7
- package/dist/admin/apps/organizations.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +7 -7
- 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 +151 -151
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/apps/Zones.d.ts +28 -28
- package/dist/apps/Zones.js +7 -7
- package/dist/apps/data/App.d.ts +2909 -2523
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +26 -21
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/ZoneSelection.d.ts +8 -8
- package/dist/apps/data/ZoneSelection.d.ts.map +1 -1
- package/dist/apps/data/ZoneSelection.js +1 -1
- package/dist/apps/data/ZoneSelection.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +588 -584
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +1561 -1239
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +22 -22
- package/dist/apps/data/routes/balances.d.ts +26 -26
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +43 -16
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +44 -44
- package/dist/apps/data/routes/blocks.d.ts.map +1 -1
- package/dist/apps/data/routes/blocks.js +21 -11
- package/dist/apps/data/routes/blocks.js.map +1 -1
- package/dist/apps/data/routes/coingecko.d.ts +3 -3
- package/dist/apps/data/routes/earn.d.ts +201 -25
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +434 -75
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +160 -145
- 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 +2 -2
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +18 -19
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/receipts.d.ts +155 -55
- package/dist/apps/data/routes/receipts.d.ts.map +1 -1
- package/dist/apps/data/routes/receipts.js +28 -10
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +13 -13
- package/dist/apps/data/routes/tokens.d.ts +5 -5
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +51 -20
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +7 -2
- package/dist/apps/data/routes/transactions.d.ts.map +1 -1
- package/dist/apps/data/routes/transactions.js +37 -17
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +13 -13
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +18 -12
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/valuation.d.ts +31 -31
- package/dist/apps/data/routes/valuation.d.ts.map +1 -1
- package/dist/apps/data/routes/valuation.js +12 -7
- package/dist/apps/data/routes/valuation.js.map +1 -1
- package/dist/apps/data/routes/verified-tokens.d.ts +8 -8
- package/dist/apps/data/routes/webhooks.d.ts +816 -113
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +47 -5
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.d.ts +14 -14
- package/dist/apps/data/routes/zones.js +1 -1
- package/dist/apps/funding/App.d.ts +466 -44
- package/dist/apps/funding/App.d.ts.map +1 -1
- package/dist/apps/funding/App.js +1 -1
- package/dist/apps/funding/App.js.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.d.ts +17 -17
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js +17 -12
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
- package/dist/apps/funding/routes/transfers.d.ts +650 -57
- package/dist/apps/funding/routes/transfers.d.ts.map +1 -1
- package/dist/apps/funding/routes/transfers.js +238 -24
- package/dist/apps/funding/routes/transfers.js.map +1 -1
- package/dist/apps/management/App.d.ts +5120 -4152
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/Billing.d.ts +1 -1
- package/dist/apps/management/Billing.d.ts.map +1 -1
- package/dist/apps/management/Billing.js +17 -1
- package/dist/apps/management/Billing.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +632 -81
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +236 -4
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +76 -76
- package/dist/apps/management/routes/faucet.d.ts +6 -6
- package/dist/apps/management/routes/invitations.d.ts +24 -24
- package/dist/apps/management/routes/invite-links.d.ts +4 -4
- package/dist/apps/management/routes/members.d.ts +16 -16
- package/dist/apps/management/routes/orgs.d.ts +20 -20
- package/dist/apps/management/routes/projects.d.ts +4 -4
- package/dist/apps/management/routes/usage.d.ts +8 -8
- package/dist/apps/management/routes/verified-token-requests.d.ts +17 -17
- package/dist/apps/management/routes/webhooks.d.ts +675 -25
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/management/routes/webhooks.js +21 -21
- package/dist/apps/management/routes/webhooks.js.map +1 -1
- package/dist/apps/relay/App.d.ts.map +1 -1
- package/dist/apps/relay/App.js +115 -23
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
- package/dist/apps/relay/Sponsorships.js +27 -1
- package/dist/apps/relay/Sponsorships.js.map +1 -1
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +34 -1
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +11 -5
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +21 -3
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/fundingCatalog.d.ts +4 -2
- package/dist/db/tables/fundingCatalog.d.ts.map +1 -1
- package/dist/db/tables/fundingCatalog.js +5 -1
- package/dist/db/tables/fundingCatalog.js.map +1 -1
- package/dist/db/tables/fundingTransferTransactions.d.ts +38 -0
- package/dist/db/tables/fundingTransferTransactions.d.ts.map +1 -1
- package/dist/db/tables/fundingTransferTransactions.js +44 -3
- package/dist/db/tables/fundingTransferTransactions.js.map +1 -1
- package/dist/db/tables/fundingTransfers.d.ts +17 -0
- package/dist/db/tables/fundingTransfers.d.ts.map +1 -1
- package/dist/db/tables/fundingTransfers.js +21 -0
- package/dist/db/tables/fundingTransfers.js.map +1 -1
- package/dist/db/tables/organizations.d.ts +24 -0
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +27 -0
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/projects.d.ts +25 -1
- package/dist/db/tables/projects.d.ts.map +1 -1
- package/dist/db/tables/projects.js +26 -0
- package/dist/db/tables/projects.js.map +1 -1
- package/dist/db/tables/sponsoredTransactions.d.ts +44 -0
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +146 -13
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/db/tables/webhookSubscriptions.d.ts +10 -6
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +9 -6
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/handlers/internal/utils.d.ts.map +1 -1
- package/dist/handlers/internal/utils.js +9 -0
- package/dist/handlers/internal/utils.js.map +1 -1
- package/dist/internal/Auth.d.ts +3 -1
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +6 -3
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/EarnVaults.d.ts +3 -3
- package/dist/internal/EarnVaults.js +5 -5
- package/dist/internal/EarnVaults.js.map +1 -1
- package/dist/internal/Fetch.d.ts +23 -0
- package/dist/internal/Fetch.d.ts.map +1 -0
- package/dist/internal/Fetch.js +26 -0
- package/dist/internal/Fetch.js.map +1 -0
- package/dist/internal/Log.d.ts +4 -2
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +16 -3
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +5 -3
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +13 -3
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Response.d.ts +1 -1
- package/dist/internal/Response.d.ts.map +1 -1
- package/dist/internal/Schema.d.ts +6 -3
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +8 -2
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Store.d.ts +16 -3
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +82 -16
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +7 -2
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/Timeout.d.ts +36 -0
- package/dist/internal/Timeout.d.ts.map +1 -0
- package/dist/internal/Timeout.js +37 -0
- package/dist/internal/Timeout.js.map +1 -0
- package/dist/internal/Timing.d.ts +9 -0
- package/dist/internal/Timing.d.ts.map +1 -1
- package/dist/internal/Timing.js +7 -0
- package/dist/internal/Timing.js.map +1 -1
- package/dist/internal/Viem.d.ts +16 -1
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +12 -2
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/WebhookDestination.d.ts.map +1 -1
- package/dist/internal/WebhookDestination.js +3 -0
- package/dist/internal/WebhookDestination.js.map +1 -1
- package/dist/internal/funding/Action.d.ts +9 -0
- package/dist/internal/funding/Action.d.ts.map +1 -1
- package/dist/internal/funding/Action.js +16 -8
- package/dist/internal/funding/Action.js.map +1 -1
- package/dist/internal/funding/Catalog.d.ts +9 -2
- package/dist/internal/funding/Catalog.d.ts.map +1 -1
- package/dist/internal/funding/Catalog.js +19 -8
- package/dist/internal/funding/Catalog.js.map +1 -1
- package/dist/internal/funding/DepositWebhook.d.ts.map +1 -1
- package/dist/internal/funding/DepositWebhook.js +2 -1
- package/dist/internal/funding/DepositWebhook.js.map +1 -1
- package/dist/internal/funding/Evidence.d.ts.map +1 -1
- package/dist/internal/funding/Evidence.js +2 -10
- package/dist/internal/funding/Evidence.js.map +1 -1
- package/dist/internal/funding/Provider.d.ts +108 -13
- package/dist/internal/funding/Provider.d.ts.map +1 -1
- package/dist/internal/funding/Provider.js +34 -8
- package/dist/internal/funding/Provider.js.map +1 -1
- package/dist/internal/funding/SourceObservation.d.ts.map +1 -1
- package/dist/internal/funding/SourceObservation.js +5 -7
- package/dist/internal/funding/SourceObservation.js.map +1 -1
- package/dist/internal/funding/Transfer.d.ts +114 -18
- package/dist/internal/funding/Transfer.d.ts.map +1 -1
- package/dist/internal/funding/Transfer.js +185 -55
- package/dist/internal/funding/Transfer.js.map +1 -1
- package/dist/internal/funding/TransferReconciliation.d.ts +52 -0
- package/dist/internal/funding/TransferReconciliation.d.ts.map +1 -0
- package/dist/internal/funding/TransferReconciliation.js +192 -0
- package/dist/internal/funding/TransferReconciliation.js.map +1 -0
- package/dist/internal/funding/TransferWebhook.d.ts +21 -0
- package/dist/internal/funding/TransferWebhook.d.ts.map +1 -0
- package/dist/internal/funding/TransferWebhook.js +43 -0
- package/dist/internal/funding/TransferWebhook.js.map +1 -0
- package/dist/internal/funding/index.d.ts +1 -0
- package/dist/internal/funding/index.d.ts.map +1 -1
- package/dist/internal/funding/index.js +1 -0
- package/dist/internal/funding/index.js.map +1 -1
- package/dist/internal/funding/providers/relay.d.ts.map +1 -1
- package/dist/internal/funding/providers/relay.js +3 -12
- package/dist/internal/funding/providers/relay.js.map +1 -1
- package/dist/internal/funding/providers/stargate.d.ts +127 -9
- package/dist/internal/funding/providers/stargate.d.ts.map +1 -1
- package/dist/internal/funding/providers/stargate.js +548 -112
- package/dist/internal/funding/providers/stargate.js.map +1 -1
- package/dist/internal/index.d.ts +1 -0
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +1 -0
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/webhookDestinations/slack.d.ts.map +1 -1
- package/dist/internal/webhookDestinations/slack.js +16 -0
- package/dist/internal/webhookDestinations/slack.js.map +1 -1
- package/package.json +2 -2
- package/src/App.test.ts +100 -0
- package/src/App.ts +43 -3
- package/src/Client.test-d.ts +129 -2
- package/src/Scope.test.ts +20 -0
- package/src/Scope.ts +16 -0
- package/src/Webhooks.ts +2 -2
- package/src/admin/apps/api-keys.test.ts +75 -0
- package/src/admin/apps/api-keys.ts +64 -0
- package/src/admin/apps/earn-vaults.test.ts +1 -1
- package/src/admin/apps/earn-vaults.ts +2 -1
- package/src/admin/apps/organizations.test.ts +53 -1
- package/src/admin/apps/organizations.ts +90 -5
- package/src/admin/ui/src/lib/api-keys.ts +19 -0
- package/src/admin/ui/src/lib/organizations.test.ts +112 -1
- package/src/admin/ui/src/lib/organizations.ts +55 -0
- package/src/admin/ui/src/routes/api-keys.tsx +89 -2
- package/src/admin/ui/src/routes/organizations_.$orgId.tsx +116 -0
- package/src/admin/ui.generated.ts +189 -151
- package/src/apps/Zones.test.ts +7 -7
- package/src/apps/Zones.ts +7 -7
- package/src/apps/composition.test.ts +2 -1
- package/src/apps/data/App.test.ts +41 -0
- package/src/apps/data/App.ts +30 -21
- package/src/apps/data/ZoneSelection.test.ts +2 -2
- package/src/apps/data/ZoneSelection.ts +8 -8
- package/src/apps/data/routes/activities.test.ts +1526 -12
- package/src/apps/data/routes/activities.ts +1774 -1356
- package/src/apps/data/routes/balances.ts +133 -97
- package/src/apps/data/routes/blocks.test.ts +533 -0
- package/src/apps/data/routes/blocks.ts +35 -16
- package/src/apps/data/routes/earn.test.ts +373 -16
- package/src/apps/data/routes/earn.ts +548 -79
- package/src/apps/data/routes/earn.zone-access.test.ts +6 -6
- package/src/apps/data/routes/exchanges.test.ts +740 -15
- package/src/apps/data/routes/exchanges.ts +954 -753
- package/src/apps/data/routes/fee-amm.ts +17 -18
- package/src/apps/data/routes/receipts.ts +47 -16
- package/src/apps/data/routes/tokens.test.ts +578 -0
- package/src/apps/data/routes/tokens.ts +283 -225
- package/src/apps/data/routes/transactions.ts +68 -29
- package/src/apps/data/routes/transfers.test.ts +421 -0
- package/src/apps/data/routes/transfers.ts +45 -27
- package/src/apps/data/routes/valuation.ts +94 -78
- package/src/apps/data/routes/webhooks.test.ts +21 -5
- package/src/apps/data/routes/webhooks.ts +63 -8
- package/src/apps/data/routes/zones.test.ts +2 -2
- package/src/apps/data/routes/zones.ts +1 -1
- package/src/apps/funding/App.ts +12 -2
- package/src/apps/funding/routes/deposit-addresses.test.ts +29 -7
- package/src/apps/funding/routes/deposit-addresses.ts +19 -14
- package/src/apps/funding/routes/quotes.test.ts +5 -9
- package/src/apps/funding/routes/transfers.test.ts +901 -23
- package/src/apps/funding/routes/transfers.ts +347 -64
- package/src/apps/management/Billing.ts +18 -5
- package/src/apps/management/routes/api-keys.test.ts +220 -0
- package/src/apps/management/routes/api-keys.ts +280 -3
- package/src/apps/management/routes/webhooks.test.ts +3 -3
- package/src/apps/management/routes/webhooks.ts +24 -25
- package/src/apps/relay/App.test.ts +212 -1
- package/src/apps/relay/App.ts +158 -29
- package/src/apps/relay/Sponsorships.ts +24 -1
- package/src/db/Db.ts +48 -1
- package/src/db/Schema.ts +22 -4
- package/src/db/tables/earnVaults.test.ts +9 -9
- package/src/db/tables/fundingCatalog.test.ts +16 -2
- package/src/db/tables/fundingCatalog.ts +9 -3
- package/src/db/tables/fundingTransferTransactions.test.ts +43 -0
- package/src/db/tables/fundingTransferTransactions.ts +79 -3
- package/src/db/tables/fundingTransfers.test.ts +26 -0
- package/src/db/tables/fundingTransfers.ts +36 -0
- package/src/db/tables/organizations.test.ts +41 -0
- package/src/db/tables/organizations.ts +49 -0
- package/src/db/tables/projects.test.ts +55 -0
- package/src/db/tables/projects.ts +46 -1
- package/src/db/tables/sponsoredTransactions.test.ts +338 -0
- package/src/db/tables/sponsoredTransactions.ts +197 -13
- package/src/db/tables/webhookSubscriptions.ts +18 -11
- package/src/handlers/internal/utils.test.ts +53 -0
- package/src/handlers/internal/utils.ts +15 -0
- package/src/internal/Auth.ts +12 -4
- package/src/internal/EarnVaults.capabilities.test.ts +2 -2
- package/src/internal/EarnVaults.test.ts +1 -1
- package/src/internal/EarnVaults.ts +5 -5
- package/src/internal/Fetch.test.ts +46 -0
- package/src/internal/Fetch.ts +56 -0
- package/src/internal/Log.test-d.ts +2 -0
- package/src/internal/Log.test.ts +21 -0
- package/src/internal/Log.ts +22 -5
- package/src/internal/OpenApi.ts +14 -4
- package/src/internal/Response.ts +1 -1
- package/src/internal/Schema.test.ts +2 -0
- package/src/internal/Schema.ts +12 -2
- package/src/internal/Store.test.ts +178 -0
- package/src/internal/Store.ts +104 -18
- package/src/internal/Tidx.test.ts +2 -1
- package/src/internal/Tidx.ts +7 -2
- package/src/internal/Timeout.test.ts +35 -0
- package/src/internal/Timeout.ts +73 -0
- package/src/internal/Timing.ts +16 -0
- package/src/internal/Viem.ts +39 -3
- package/src/internal/WebhookDestination.ts +3 -0
- package/src/internal/Webhooks.test.ts +45 -0
- package/src/internal/funding/Action.test.ts +41 -1
- package/src/internal/funding/Action.ts +69 -41
- package/src/internal/funding/Catalog.test.ts +34 -0
- package/src/internal/funding/Catalog.ts +28 -11
- package/src/internal/funding/DepositWebhook.ts +2 -1
- package/src/internal/funding/Evidence.ts +2 -13
- package/src/internal/funding/Provider.test.ts +73 -0
- package/src/internal/funding/Provider.ts +125 -28
- package/src/internal/funding/SourceObservation.ts +5 -9
- package/src/internal/funding/Transfer.test.ts +155 -0
- package/src/internal/funding/Transfer.ts +387 -147
- package/src/internal/funding/TransferReconciliation.test.ts +147 -0
- package/src/internal/funding/TransferReconciliation.ts +264 -0
- package/src/internal/funding/TransferWebhook.test.ts +128 -0
- package/src/internal/funding/TransferWebhook.ts +67 -0
- package/src/internal/funding/index.test.ts +1 -0
- package/src/internal/funding/index.ts +1 -0
- package/src/internal/funding/providers/index.test.ts +4 -167
- package/src/internal/funding/providers/relay.test.ts +2 -2
- package/src/internal/funding/providers/relay.ts +3 -15
- package/src/internal/funding/providers/stargate.test.ts +328 -0
- package/src/internal/funding/providers/stargate.ts +807 -135
- package/src/internal/index.test.ts +1 -0
- package/src/internal/index.ts +1 -0
- package/src/internal/webhookDestinations/slack.ts +15 -0
package/src/apps/relay/App.ts
CHANGED
|
@@ -7,9 +7,11 @@ import { Addresses, TokenId, Transaction } from 'viem/tempo'
|
|
|
7
7
|
import type * as App from '../../App.js'
|
|
8
8
|
import * as BillingSettings from '../../db/tables/billingSettings.js'
|
|
9
9
|
import * as Db from '../../db/Db.js'
|
|
10
|
+
import * as Organizations from '../../db/tables/organizations.js'
|
|
11
|
+
import * as Projects from '../../db/tables/projects.js'
|
|
12
|
+
import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js'
|
|
10
13
|
import * as Fees from '../../internal/Fees.js'
|
|
11
14
|
import * as Log from '../../internal/Log.js'
|
|
12
|
-
import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js'
|
|
13
15
|
import * as Billing from '../management/Billing.js'
|
|
14
16
|
import * as Handler from '../../Handler.js'
|
|
15
17
|
import * as Auth from '../../internal/Auth.js'
|
|
@@ -85,6 +87,7 @@ function middleware(options: middleware.Options) {
|
|
|
85
87
|
: 0n
|
|
86
88
|
return createMiddleware<App.Environment>(async (c) => {
|
|
87
89
|
const principal = Auth.getPrincipal(c)
|
|
90
|
+
const requestedAt = new Date().toISOString()
|
|
88
91
|
const verifiedTokens = c.get('verifiedTokens')
|
|
89
92
|
const zoneToken_header = c.req.header(ZoneRpcAuthentication.headerName)
|
|
90
93
|
const zoneToken = zoneToken_header?.trim() ? zoneToken_header : undefined
|
|
@@ -97,7 +100,11 @@ function middleware(options: middleware.Options) {
|
|
|
97
100
|
|
|
98
101
|
const recordRejection = (options: middleware.Rejection) => {
|
|
99
102
|
const id =
|
|
100
|
-
|
|
103
|
+
options.chainId !== undefined &&
|
|
104
|
+
Number.isSafeInteger(options.chainId) &&
|
|
105
|
+
options.chainId >= 0
|
|
106
|
+
? options.chainId
|
|
107
|
+
: null
|
|
101
108
|
if (chainId !== null) chainId = chainId === undefined || chainId === id ? id : null
|
|
102
109
|
if (options.reason === 'internal_error') internalErrors = Math.min(internalErrors + 1, 65_535)
|
|
103
110
|
if (reason !== null)
|
|
@@ -121,22 +128,49 @@ function middleware(options: middleware.Options) {
|
|
|
121
128
|
throw new Error('Internal error', { cause: new Error(options.message) })
|
|
122
129
|
}
|
|
123
130
|
|
|
124
|
-
const feePayer = (() => {
|
|
131
|
+
const feePayer = await (async () => {
|
|
125
132
|
if (!options.feePayer) return undefined
|
|
126
|
-
const
|
|
127
|
-
principal
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
...(principal.projectId === undefined ? {} : { projectId: principal.projectId }),
|
|
133
|
-
}
|
|
134
|
-
: undefined
|
|
133
|
+
const resolved = await resolveAttribution(Db.get(c.get('db')), {
|
|
134
|
+
principal,
|
|
135
|
+
projectId: c.req.header('tempo-project-id')?.trim(),
|
|
136
|
+
})
|
|
137
|
+
const attribution = resolved?.attribution
|
|
138
|
+
const attributionError = resolved?.error
|
|
135
139
|
const sponsorable =
|
|
136
140
|
attribution !== undefined &&
|
|
137
141
|
principal?.type === 'api_key' &&
|
|
138
142
|
(principal.apiKey.scopes.includes('rpc-relay:sponsor') ||
|
|
139
143
|
principal.apiKey.scopes.includes(Scope.wildcard))
|
|
144
|
+
let promotionState_promise: Promise<middleware.PromotionState>
|
|
145
|
+
const promotionState = () =>
|
|
146
|
+
(promotionState_promise ??= (async () => {
|
|
147
|
+
if (!attribution) return { status: 'inactive' }
|
|
148
|
+
const db = Db.get(c.get('db'))
|
|
149
|
+
const organization = await Organizations.get(db, attribution.orgId)
|
|
150
|
+
if (!organization || organization.sponsorshipSubsidyDurationDays === null)
|
|
151
|
+
return { status: 'inactive' }
|
|
152
|
+
// Missing project attribution opts out of the subsidy but must not interrupt sponsorship.
|
|
153
|
+
if (!attribution.projectId) return { status: 'inactive' }
|
|
154
|
+
const project = await Projects.get(db, attribution.projectId)
|
|
155
|
+
if (!project) return { message: 'Attributed project disappeared.', status: 'error' }
|
|
156
|
+
if (project.orgId !== attribution.orgId)
|
|
157
|
+
return { message: 'Attributed project changed ownership.', status: 'error' }
|
|
158
|
+
if (
|
|
159
|
+
(project.sponsorshipSubsidyStartsAt === null) !==
|
|
160
|
+
(project.sponsorshipSubsidyEndsAt === null)
|
|
161
|
+
)
|
|
162
|
+
return { message: 'Project promotion window is inconsistent.', status: 'error' }
|
|
163
|
+
if (
|
|
164
|
+
project.sponsorshipSubsidyEndsAt !== null &&
|
|
165
|
+
Date.parse(requestedAt) >= Date.parse(project.sponsorshipSubsidyEndsAt)
|
|
166
|
+
)
|
|
167
|
+
return { status: 'inactive' }
|
|
168
|
+
return { status: 'active' }
|
|
169
|
+
})())
|
|
170
|
+
const validatePromotion = async (chainId: number) => {
|
|
171
|
+
const state = await promotionState()
|
|
172
|
+
if (state.status === 'error') failSponsorship({ chainId, message: state.message })
|
|
173
|
+
}
|
|
140
174
|
// Lifted so validation and the sponsored-token default read one value.
|
|
141
175
|
const feeToken_sponsor = options.feePayer.feeToken ?? Addresses.pathUsd
|
|
142
176
|
return {
|
|
@@ -147,6 +181,12 @@ function middleware(options: middleware.Options) {
|
|
|
147
181
|
// only mainnet: the relay resolves chainId from the request body, so
|
|
148
182
|
// the query-string chain guard never sees it.
|
|
149
183
|
const id = Number(transaction.chainId)
|
|
184
|
+
if (attributionError)
|
|
185
|
+
rejectSponsorship({
|
|
186
|
+
chainId: id,
|
|
187
|
+
message: 'Project attribution rejected.',
|
|
188
|
+
reason: attributionError,
|
|
189
|
+
})
|
|
150
190
|
if (!attribution) {
|
|
151
191
|
if (sponsorshipRequired)
|
|
152
192
|
rejectSponsorship({
|
|
@@ -186,15 +226,9 @@ function middleware(options: middleware.Options) {
|
|
|
186
226
|
})
|
|
187
227
|
return false
|
|
188
228
|
}
|
|
189
|
-
|
|
190
|
-
// Enforcement reads hit the primary, never the cached replica: a
|
|
191
|
-
// stale `past_due`, a lowered limit, or another isolate's pending
|
|
192
|
-
// spend must not keep the gate open.
|
|
193
229
|
const db = Db.get(c.get('db'))
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
// `past_due` gets its own reason: the caller must settle an invoice,
|
|
197
|
-
// not attach a card.
|
|
230
|
+
// Enforcement reads hit the primary, never the cached replica: a
|
|
231
|
+
// stale `past_due` or another isolate's state must not keep the gate open.
|
|
198
232
|
const billing = await Billing.status(db, attribution.orgId)
|
|
199
233
|
if (billing === 'past_due') {
|
|
200
234
|
if (sponsorshipRequired)
|
|
@@ -214,6 +248,7 @@ function middleware(options: middleware.Options) {
|
|
|
214
248
|
})
|
|
215
249
|
return 'billing_required'
|
|
216
250
|
}
|
|
251
|
+
await validatePromotion(id)
|
|
217
252
|
|
|
218
253
|
const feeToken = (() => {
|
|
219
254
|
try {
|
|
@@ -286,7 +321,6 @@ function middleware(options: middleware.Options) {
|
|
|
286
321
|
// reconciliation convention; pending rows charge it to spend limits.
|
|
287
322
|
const feeMax = Fees.maxOf(Transaction.deserialize(event.transaction as `0x76${string}`))
|
|
288
323
|
const db = Db.get(c.get('db'))
|
|
289
|
-
|
|
290
324
|
const feeToken = (() => {
|
|
291
325
|
try {
|
|
292
326
|
return event.feeToken !== undefined ? TokenId.toAddress(event.feeToken) : undefined
|
|
@@ -318,13 +352,54 @@ function middleware(options: middleware.Options) {
|
|
|
318
352
|
// Re-read settings on the authoritative path: the limit may have
|
|
319
353
|
// changed since `validate`, and only mainnet spend is limited.
|
|
320
354
|
const settings = mainnet ? await BillingSettings.get(db, attribution.orgId) : undefined
|
|
321
|
-
|
|
355
|
+
const billingLimit =
|
|
356
|
+
settings?.spendLimit == null
|
|
357
|
+
? undefined
|
|
358
|
+
: {
|
|
359
|
+
chainIds: [Viem.chainId.mainnet],
|
|
360
|
+
max: Billing.toBaseUnits(settings.spendLimit),
|
|
361
|
+
since: Billing.periodStart(settings.period),
|
|
362
|
+
}
|
|
363
|
+
if (
|
|
364
|
+
mainnet &&
|
|
365
|
+
attribution.environment === 'production' &&
|
|
366
|
+
attribution.projectId !== undefined
|
|
367
|
+
)
|
|
368
|
+
try {
|
|
369
|
+
const promotion = await SponsoredTransactions.reservePromotion(
|
|
370
|
+
db,
|
|
371
|
+
{ ...input, billable: false },
|
|
372
|
+
{
|
|
373
|
+
at: new Date().toISOString(),
|
|
374
|
+
...(billingLimit ? { billingLimit } : {}),
|
|
375
|
+
chainIds: [Viem.chainId.mainnet],
|
|
376
|
+
projectId: attribution.projectId,
|
|
377
|
+
},
|
|
378
|
+
)
|
|
379
|
+
if (promotion.status !== 'ineligible') return
|
|
380
|
+
} catch (error) {
|
|
381
|
+
if (error instanceof SponsoredTransactions.AttributionConflictError)
|
|
382
|
+
rejectSponsorship({
|
|
383
|
+
chainId: event.chainId,
|
|
384
|
+
message: 'Project attribution rejected.',
|
|
385
|
+
reason: 'project_id_invalid',
|
|
386
|
+
})
|
|
387
|
+
if (error instanceof SponsoredTransactions.PromotionActivationError)
|
|
388
|
+
failSponsorship({
|
|
389
|
+
chainId: event.chainId,
|
|
390
|
+
message: 'Project promotion activation failed.',
|
|
391
|
+
})
|
|
392
|
+
if (error instanceof SponsoredTransactions.PeriodSpendLimitError)
|
|
393
|
+
rejectSponsorship({
|
|
394
|
+
chainId: event.chainId,
|
|
395
|
+
message: 'Spend limit exceeded.',
|
|
396
|
+
reason: 'spend_limit_exceeded',
|
|
397
|
+
})
|
|
398
|
+
throw error
|
|
399
|
+
}
|
|
400
|
+
if (!billingLimit) return void (await SponsoredTransactions.upsert(db, input))
|
|
322
401
|
try {
|
|
323
|
-
await SponsoredTransactions.reserve(db, input,
|
|
324
|
-
chainIds: [Viem.chainId.mainnet],
|
|
325
|
-
max: Billing.toBaseUnits(settings.spendLimit),
|
|
326
|
-
since: Billing.periodStart(settings.period),
|
|
327
|
-
})
|
|
402
|
+
await SponsoredTransactions.reserve(db, input, billingLimit)
|
|
328
403
|
} catch (error) {
|
|
329
404
|
if (error instanceof SponsoredTransactions.PeriodSpendLimitError)
|
|
330
405
|
rejectSponsorship({
|
|
@@ -518,6 +593,57 @@ function sponsorshipMethod(value: unknown): Log.SponsorshipMethod | undefined {
|
|
|
518
593
|
}
|
|
519
594
|
}
|
|
520
595
|
|
|
596
|
+
/** Resolves optional request-level project attribution within the API key's organization. */
|
|
597
|
+
async function resolveAttribution(
|
|
598
|
+
db: Db.Db,
|
|
599
|
+
options: resolveAttribution.Options,
|
|
600
|
+
): Promise<resolveAttribution.Result | undefined> {
|
|
601
|
+
if (options.principal?.type !== 'api_key') return undefined
|
|
602
|
+
const { principal } = options
|
|
603
|
+
const attribution = {
|
|
604
|
+
apiKeyId: principal.id,
|
|
605
|
+
environment: principal.environment,
|
|
606
|
+
orgId: principal.orgId,
|
|
607
|
+
...(principal.projectId === undefined ? {} : { projectId: principal.projectId }),
|
|
608
|
+
}
|
|
609
|
+
if (!options.projectId) return { attribution }
|
|
610
|
+
if (principal.projectId !== undefined) return { attribution, error: 'project_id_invalid' }
|
|
611
|
+
const project = await Projects.get(db, options.projectId)
|
|
612
|
+
if (!project || project.orgId !== principal.orgId)
|
|
613
|
+
return { attribution, error: 'project_id_invalid' }
|
|
614
|
+
return { attribution: { ...attribution, projectId: project.id } }
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
declare namespace resolveAttribution {
|
|
618
|
+
/** Inputs for resolving API-key project attribution. */
|
|
619
|
+
type Options = {
|
|
620
|
+
/** Authenticated request principal. */
|
|
621
|
+
principal: Auth.Principal | null
|
|
622
|
+
/** Optional project id supplied by the caller. */
|
|
623
|
+
projectId?: string | undefined
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/** Resolved API-key attribution. */
|
|
627
|
+
type Attribution = {
|
|
628
|
+
/** API key id. */
|
|
629
|
+
apiKeyId: string
|
|
630
|
+
/** API key environment. */
|
|
631
|
+
environment: 'production' | 'sandbox'
|
|
632
|
+
/** API key organization id. */
|
|
633
|
+
orgId: string
|
|
634
|
+
/** Attributed project id. */
|
|
635
|
+
projectId?: string | undefined
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/** Attribution result with an optional policy error. */
|
|
639
|
+
type Result = {
|
|
640
|
+
/** Attribution retained for request diagnostics. */
|
|
641
|
+
attribution: Attribution
|
|
642
|
+
/** Project attribution error detected before transaction validation. */
|
|
643
|
+
error?: 'project_id_invalid' | undefined
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
521
647
|
declare namespace middleware {
|
|
522
648
|
/** One invariant sponsorship failure. */
|
|
523
649
|
type Failure = {
|
|
@@ -535,10 +661,13 @@ declare namespace middleware {
|
|
|
535
661
|
path: 'rpc/relay' | 'rpc/sponsor'
|
|
536
662
|
}
|
|
537
663
|
|
|
664
|
+
/** Cached promotion validation state; request accounting is applied per batch item. */
|
|
665
|
+
type PromotionState = { status: 'active' | 'inactive' } | { message: string; status: 'error' }
|
|
666
|
+
|
|
538
667
|
/** One sponsorship rejection recorded for request logging. */
|
|
539
668
|
type Rejection = {
|
|
540
669
|
/** Candidate transaction chain, validated before it enters the log entry. */
|
|
541
|
-
chainId
|
|
670
|
+
chainId?: number | undefined
|
|
542
671
|
/** Stable client error code. */
|
|
543
672
|
reason: Log.SponsorshipReason
|
|
544
673
|
}
|
|
@@ -546,7 +675,7 @@ declare namespace middleware {
|
|
|
546
675
|
/** One client-visible sponsorship refusal. */
|
|
547
676
|
type Refusal = {
|
|
548
677
|
/** Candidate transaction chain. */
|
|
549
|
-
chainId
|
|
678
|
+
chainId?: number | undefined
|
|
550
679
|
/** Client-safe error message. */
|
|
551
680
|
message: string
|
|
552
681
|
/** Stable client error code. */
|
|
@@ -11,6 +11,7 @@ import { getTransactionReceipt } from 'viem/actions'
|
|
|
11
11
|
|
|
12
12
|
import * as Db from '../../db/Db.js'
|
|
13
13
|
import * as BillingSettings from '../../db/tables/billingSettings.js'
|
|
14
|
+
import * as Projects from '../../db/tables/projects.js'
|
|
14
15
|
import * as Fees from '../../internal/Fees.js'
|
|
15
16
|
import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js'
|
|
16
17
|
import * as Tidx from '../../internal/Tidx.js'
|
|
@@ -292,7 +293,29 @@ export function createFinalizer(options: createFinalizer.Options): createFinaliz
|
|
|
292
293
|
getClient,
|
|
293
294
|
getTidx: (chainId) => getTidx(chainId as Viem.ChainId),
|
|
294
295
|
limitFor: async (intent) => {
|
|
295
|
-
if (!
|
|
296
|
+
if (!Viem.isMainnet(intent.chainId)) return undefined
|
|
297
|
+
if (!intent.billable) {
|
|
298
|
+
if (!intent.projectId) return undefined
|
|
299
|
+
const project = await Projects.get(db, intent.projectId)
|
|
300
|
+
// Missing promotion state cannot prove the intent remains within Tempo's budget.
|
|
301
|
+
if (!project || project.sponsorshipSubsidyStartsAt === null)
|
|
302
|
+
return {
|
|
303
|
+
billable: false,
|
|
304
|
+
chainIds: [Viem.chainId.mainnet],
|
|
305
|
+
max: -1n,
|
|
306
|
+
projectId: intent.projectId,
|
|
307
|
+
since: intent.createdAt,
|
|
308
|
+
}
|
|
309
|
+
if (project.sponsorshipSpendLimit === null) return undefined
|
|
310
|
+
return {
|
|
311
|
+
billable: false,
|
|
312
|
+
chainIds: [Viem.chainId.mainnet],
|
|
313
|
+
max: BigInt(project.sponsorshipSpendLimit),
|
|
314
|
+
projectId: intent.projectId,
|
|
315
|
+
since: project.sponsorshipSubsidyStartsAt,
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (!intent.billable) return undefined
|
|
296
319
|
const settings = await BillingSettings.get(db, intent.orgId)
|
|
297
320
|
if (settings?.spendLimit == null) return undefined
|
|
298
321
|
return {
|
package/src/db/Db.ts
CHANGED
|
@@ -1483,6 +1483,46 @@ export const migrations: readonly Migration[] = [
|
|
|
1483
1483
|
sql`ALTER TABLE earn_vaults ADD COLUMN IF NOT EXISTS zones jsonb NOT NULL DEFAULT '[]'::jsonb`,
|
|
1484
1484
|
),
|
|
1485
1485
|
},
|
|
1486
|
+
{
|
|
1487
|
+
name: '0137_funding_routes_configuration',
|
|
1488
|
+
up: (db) =>
|
|
1489
|
+
raw(db, sql`ALTER TABLE funding_routes ADD COLUMN IF NOT EXISTS configuration jsonb`),
|
|
1490
|
+
},
|
|
1491
|
+
{
|
|
1492
|
+
name: '0138_organizations_sponsorship_subsidy',
|
|
1493
|
+
up: (db) =>
|
|
1494
|
+
raw(
|
|
1495
|
+
db,
|
|
1496
|
+
sql`ALTER TABLE organizations
|
|
1497
|
+
ADD COLUMN IF NOT EXISTS sponsorship_subsidy_duration_days integer,
|
|
1498
|
+
ADD COLUMN IF NOT EXISTS sponsorship_subsidy_project_spend_limit text`,
|
|
1499
|
+
),
|
|
1500
|
+
},
|
|
1501
|
+
{
|
|
1502
|
+
name: '0139_projects_sponsorship_activation',
|
|
1503
|
+
up: (db) =>
|
|
1504
|
+
raw(
|
|
1505
|
+
db,
|
|
1506
|
+
sql`ALTER TABLE projects
|
|
1507
|
+
ADD COLUMN IF NOT EXISTS sponsorship_subsidy_starts_at text,
|
|
1508
|
+
ADD COLUMN IF NOT EXISTS sponsorship_subsidy_ends_at text,
|
|
1509
|
+
DROP CONSTRAINT IF EXISTS projects_sponsorship_subsidy_window_consistent,
|
|
1510
|
+
ADD CONSTRAINT projects_sponsorship_subsidy_window_consistent CHECK ((sponsorship_subsidy_starts_at IS NULL) = (sponsorship_subsidy_ends_at IS NULL))`,
|
|
1511
|
+
),
|
|
1512
|
+
},
|
|
1513
|
+
{
|
|
1514
|
+
name: '0140_projects_sponsorship_spend_limit',
|
|
1515
|
+
up: (db) =>
|
|
1516
|
+
raw(db, sql`ALTER TABLE projects ADD COLUMN IF NOT EXISTS sponsorship_spend_limit text`),
|
|
1517
|
+
},
|
|
1518
|
+
{
|
|
1519
|
+
name: '0141_sponsored_transactions_promotion_spend_index',
|
|
1520
|
+
up: (db) =>
|
|
1521
|
+
raw(
|
|
1522
|
+
db,
|
|
1523
|
+
sql`CREATE INDEX IF NOT EXISTS sponsored_transactions_promotion_spend_idx ON sponsored_transactions (org_id, project_id, status, created_at) WHERE NOT billable`,
|
|
1524
|
+
),
|
|
1525
|
+
},
|
|
1486
1526
|
]
|
|
1487
1527
|
|
|
1488
1528
|
/**
|
|
@@ -1620,7 +1660,14 @@ declare namespace fromDialect {
|
|
|
1620
1660
|
*/
|
|
1621
1661
|
export function postgres(options: postgres.Options): Db {
|
|
1622
1662
|
const { connectionString, schema } = options
|
|
1623
|
-
|
|
1663
|
+
// Bounded connect and pool-checkout wait: the pg default (0) waits forever,
|
|
1664
|
+
// so an unresponsive origin parks callers and their invocation memory
|
|
1665
|
+
// invisibly instead of failing into the retry paths.
|
|
1666
|
+
const pool = new pg.Pool({
|
|
1667
|
+
connectionString,
|
|
1668
|
+
connectionTimeoutMillis: 30_000,
|
|
1669
|
+
...(schema ? { max: 1 } : {}),
|
|
1670
|
+
})
|
|
1624
1671
|
return fromDialect(new PostgresDialect({ pool }), { schema })
|
|
1625
1672
|
}
|
|
1626
1673
|
|
package/src/db/Schema.ts
CHANGED
|
@@ -219,6 +219,12 @@ export const Organization = z.object({
|
|
|
219
219
|
name: z
|
|
220
220
|
.string()
|
|
221
221
|
.check(z.describe('Human-readable organization name.'), z.meta({ examples: ['Acme, Inc.'] })),
|
|
222
|
+
sponsorshipSubsidyDurationDays: z
|
|
223
|
+
.nullable(z.number().check(z.int(), z.positive()))
|
|
224
|
+
.check(z.describe('Promotional sponsorship duration in days, or null when disabled.')),
|
|
225
|
+
sponsorshipSubsidyProjectSpendLimit: z
|
|
226
|
+
.nullable(z.string())
|
|
227
|
+
.check(z.describe('Per-project promotional spend cap in decimal USD, or null when disabled.')),
|
|
222
228
|
updatedAt: z.string().check(z.describe('When the organization was last updated (ISO 8601).')),
|
|
223
229
|
userId: z
|
|
224
230
|
.nullable(z.string())
|
|
@@ -243,6 +249,15 @@ export const Project = z.object({
|
|
|
243
249
|
.string()
|
|
244
250
|
.check(z.describe('Human-readable project name.'), z.meta({ examples: ['Checkout'] })),
|
|
245
251
|
orgId: z.string().check(z.describe('Owning organization id (`org_…`).')),
|
|
252
|
+
sponsorshipSubsidyStartsAt: z
|
|
253
|
+
.nullable(z.string())
|
|
254
|
+
.check(z.describe('When the project first activated promotional sponsorship, or null.')),
|
|
255
|
+
sponsorshipSubsidyEndsAt: z
|
|
256
|
+
.nullable(z.string())
|
|
257
|
+
.check(z.describe('When the project promotional sponsorship ends, or null.')),
|
|
258
|
+
sponsorshipSpendLimit: z
|
|
259
|
+
.nullable(z.string())
|
|
260
|
+
.check(z.describe('Promotional spend cap in fee-token base units, or null.')),
|
|
246
261
|
updatedAt: z.string().check(z.describe('When the project was last updated (ISO 8601).')),
|
|
247
262
|
})
|
|
248
263
|
|
|
@@ -453,11 +468,14 @@ export type FundingChainToken = z.output<typeof FundingChainToken>
|
|
|
453
468
|
|
|
454
469
|
/** One source-to-destination route supported by a funding provider. */
|
|
455
470
|
export const FundingRoute = z.object({
|
|
456
|
-
destinationChainId: z.string().check(z.describe('Destination funding chain identifier.')),
|
|
457
|
-
destinationTokenId: z.string().check(z.describe('Destination funding token identifier.')),
|
|
458
471
|
capabilities: z
|
|
459
472
|
.nullable(z.custom<funding_Catalog.Capabilities>())
|
|
460
473
|
.check(z.describe('Executable capability (`jsonb`), or null for indicative-only routes.')),
|
|
474
|
+
configuration: z
|
|
475
|
+
.nullable(z.custom<funding_Catalog.Configuration>())
|
|
476
|
+
.check(z.describe('Provider-specific route configuration (`jsonb`), or null.')),
|
|
477
|
+
destinationChainId: z.string().check(z.describe('Destination funding chain identifier.')),
|
|
478
|
+
destinationTokenId: z.string().check(z.describe('Destination funding token identifier.')),
|
|
461
479
|
providerId: z.string().check(z.describe('Funding provider identifier.')),
|
|
462
480
|
sourceChainId: z.string().check(z.describe('Source funding chain identifier.')),
|
|
463
481
|
sourceTokenId: z.string().check(z.describe('Source funding token identifier.')),
|
|
@@ -637,8 +655,8 @@ export const FundingTransfer = z.object({
|
|
|
637
655
|
.check(z.describe('Attributed project id (`prj_…`), or null for organization-attributed keys.')), // prettier-ignore
|
|
638
656
|
providerId: z.string().check(z.describe('Selected funding provider id.')),
|
|
639
657
|
providerState: z
|
|
640
|
-
.nullable(z.custom<
|
|
641
|
-
.check(z.describe('Private provider
|
|
658
|
+
.nullable(z.custom<funding_Transfer.ProviderState>())
|
|
659
|
+
.check(z.describe('Private provider state (`jsonb`), or null. Never serialized publicly.')), // prettier-ignore
|
|
642
660
|
quoteExpiresAt: z
|
|
643
661
|
.string()
|
|
644
662
|
.check(z.describe('When the quoted terms stop being executable (ISO 8601).')),
|
|
@@ -13,9 +13,9 @@ const input = {
|
|
|
13
13
|
vaultAddress: '0x2f05f4d3625B2eFFb383eFbEDAFd2D8F04371636',
|
|
14
14
|
zones: [
|
|
15
15
|
{
|
|
16
|
-
chainId:
|
|
17
|
-
deploymentBlock:
|
|
18
|
-
earnRouter: '
|
|
16
|
+
chainId: 1_424_310_003,
|
|
17
|
+
deploymentBlock: 31_730_087,
|
|
18
|
+
earnRouter: '0xeBEdC74FFeEAD186ACC6caC05Eb82fB9e093a7c7',
|
|
19
19
|
},
|
|
20
20
|
],
|
|
21
21
|
} as const satisfies EarnVaults.upsert.Options
|
|
@@ -45,9 +45,9 @@ describe('upsert', () => {
|
|
|
45
45
|
"vaultAddress": "0x2f05f4d3625b2effb383efbedafd2d8f04371636",
|
|
46
46
|
"zones": [
|
|
47
47
|
{
|
|
48
|
-
"chainId":
|
|
49
|
-
"deploymentBlock":
|
|
50
|
-
"earnRouter": "
|
|
48
|
+
"chainId": 1424310003,
|
|
49
|
+
"deploymentBlock": 31730087,
|
|
50
|
+
"earnRouter": "0xebedc74ffeead186acc6cac05eb82fb9e093a7c7",
|
|
51
51
|
},
|
|
52
52
|
],
|
|
53
53
|
}
|
|
@@ -93,9 +93,9 @@ describe('upsert', () => {
|
|
|
93
93
|
"updatedAt": "2026-01-02T00:00:00.000Z",
|
|
94
94
|
"zones": [
|
|
95
95
|
{
|
|
96
|
-
"chainId":
|
|
97
|
-
"deploymentBlock":
|
|
98
|
-
"earnRouter": "
|
|
96
|
+
"chainId": 1424310003,
|
|
97
|
+
"deploymentBlock": 31730087,
|
|
98
|
+
"earnRouter": "0xebedc74ffeead186acc6cac05eb82fb9e093a7c7",
|
|
99
99
|
},
|
|
100
100
|
],
|
|
101
101
|
}
|
|
@@ -71,8 +71,12 @@ describe('publish', () => {
|
|
|
71
71
|
catalog: head,
|
|
72
72
|
chainTokens: catalog.chainTokens,
|
|
73
73
|
chains: catalog.chains,
|
|
74
|
-
//
|
|
75
|
-
routes: catalog.routes.map((route) => ({
|
|
74
|
+
// Optional route JSON persists as null.
|
|
75
|
+
routes: catalog.routes.map((route) => ({
|
|
76
|
+
...route,
|
|
77
|
+
capabilities: null,
|
|
78
|
+
configuration: null,
|
|
79
|
+
})),
|
|
76
80
|
tokens: catalog.tokens,
|
|
77
81
|
})
|
|
78
82
|
})
|
|
@@ -87,6 +91,16 @@ describe('publish', () => {
|
|
|
87
91
|
expect((await FundingCatalog.read(db)).routes[0]?.capabilities).toStrictEqual(capabilities)
|
|
88
92
|
})
|
|
89
93
|
|
|
94
|
+
test('round-trips route configuration', async () => {
|
|
95
|
+
const db = TestApp.database()
|
|
96
|
+
const configuration = { poolAddress: `0x${'11'.repeat(20)}` }
|
|
97
|
+
await FundingCatalog.publish(db, {
|
|
98
|
+
...catalog,
|
|
99
|
+
routes: [{ ...catalog.routes[0]!, configuration }],
|
|
100
|
+
})
|
|
101
|
+
expect((await FundingCatalog.read(db)).routes[0]?.configuration).toStrictEqual(configuration)
|
|
102
|
+
})
|
|
103
|
+
|
|
90
104
|
test('replaces rows and advances the catalog version', async () => {
|
|
91
105
|
const db = TestApp.database()
|
|
92
106
|
const first = await FundingCatalog.publish(db, catalog)
|
|
@@ -28,10 +28,12 @@ export type ChainTokenTable = db_Schema.FundingChainToken
|
|
|
28
28
|
/** Columns of the `funding_routes` table. */
|
|
29
29
|
export type RouteTable = db_Schema.FundingRoute
|
|
30
30
|
|
|
31
|
-
/** A route row whose
|
|
32
|
-
export type RouteRow = Omit<db_Schema.FundingRoute, 'capabilities'> & {
|
|
31
|
+
/** A route row whose optional JSON fields may be absent in validated input. */
|
|
32
|
+
export type RouteRow = Omit<db_Schema.FundingRoute, 'capabilities' | 'configuration'> & {
|
|
33
33
|
/** Executable capability; absent and null both mean indicative-only. */
|
|
34
34
|
capabilities?: db_Schema.FundingRoute['capabilities'] | undefined
|
|
35
|
+
/** Provider-specific route configuration; absent and null are equivalent. */
|
|
36
|
+
configuration?: db_Schema.FundingRoute['configuration'] | undefined
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
/** Stored funding catalog rows and version metadata. */
|
|
@@ -117,7 +119,11 @@ export function publish(db: Db.Db, input: publish.Input): Promise<db_Schema.Fund
|
|
|
117
119
|
await tx.kysely
|
|
118
120
|
.insertInto('funding_routes')
|
|
119
121
|
.values(
|
|
120
|
-
input.routes.map((route) => ({
|
|
122
|
+
input.routes.map((route) => ({
|
|
123
|
+
...route,
|
|
124
|
+
capabilities: route.capabilities ?? null,
|
|
125
|
+
configuration: route.configuration ?? null,
|
|
126
|
+
})),
|
|
121
127
|
)
|
|
122
128
|
.execute()
|
|
123
129
|
|
|
@@ -67,3 +67,46 @@ describe('insert', () => {
|
|
|
67
67
|
])
|
|
68
68
|
})
|
|
69
69
|
})
|
|
70
|
+
|
|
71
|
+
describe('claimSource', () => {
|
|
72
|
+
test('returns claimed, replayed, and conflicting ownership outcomes', async () => {
|
|
73
|
+
const first = await createTransfer('ftr_claim_first')
|
|
74
|
+
const second = await createTransfer('ftr_claim_second')
|
|
75
|
+
const record = {
|
|
76
|
+
chainId: 'eip155:8453',
|
|
77
|
+
createdAt: '2026-01-01T00:00:01.000Z',
|
|
78
|
+
role: 'source' as const,
|
|
79
|
+
transactionRef: `0x${'cd'.repeat(32)}`,
|
|
80
|
+
transferId: first.id,
|
|
81
|
+
}
|
|
82
|
+
const claimed = await FundingTransferTransactions.claimSource(db, record)
|
|
83
|
+
const replayed = await FundingTransferTransactions.claimSource(db, record)
|
|
84
|
+
const conflict = await FundingTransferTransactions.claimSource(db, {
|
|
85
|
+
...record,
|
|
86
|
+
transferId: second.id,
|
|
87
|
+
})
|
|
88
|
+
expect([claimed.type, replayed.type, conflict.type]).toEqual([
|
|
89
|
+
'claimed',
|
|
90
|
+
'replayed',
|
|
91
|
+
'conflict',
|
|
92
|
+
])
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
describe('getSource', () => {
|
|
97
|
+
test('resolves EVM hashes case-insensitively', async () => {
|
|
98
|
+
const transfer = await createTransfer('ftr_get_source')
|
|
99
|
+
await FundingTransferTransactions.claimSource(db, {
|
|
100
|
+
chainId: 'eip155:8453',
|
|
101
|
+
createdAt: '2026-01-01T00:00:01.000Z',
|
|
102
|
+
role: 'source',
|
|
103
|
+
transactionRef: `0x${'ce'.repeat(32)}`,
|
|
104
|
+
transferId: transfer.id,
|
|
105
|
+
})
|
|
106
|
+
const record = await FundingTransferTransactions.getSource(db, {
|
|
107
|
+
chainId: 'eip155:8453',
|
|
108
|
+
transactionRef: `0x${'CE'.repeat(32)}`,
|
|
109
|
+
})
|
|
110
|
+
expect(record?.transferId).toBe(transfer.id)
|
|
111
|
+
})
|
|
112
|
+
})
|