tapimo 0.11.0 → 0.13.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 +6396 -4575
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +39 -4
- package/dist/App.js.map +1 -1
- package/dist/Assets.d.ts +15 -0
- package/dist/Assets.d.ts.map +1 -1
- package/dist/Assets.js +7 -0
- package/dist/Assets.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/admin/App.d.ts +176 -78
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +84 -4
- 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 +123 -9
- package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.js +60 -7
- package/dist/admin/apps/earn-vaults.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/organizations.js +4 -2
- package/dist/admin/apps/organizations.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +90 -90
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +71 -71
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/apps/Zones.d.ts +16 -16
- package/dist/apps/Zones.js +4 -4
- package/dist/apps/data/App.d.ts +1699 -749
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +58 -9
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/ZoneSelection.d.ts +62 -0
- package/dist/apps/data/ZoneSelection.d.ts.map +1 -0
- package/dist/apps/data/ZoneSelection.js +44 -0
- package/dist/apps/data/ZoneSelection.js.map +1 -0
- package/dist/apps/data/routes/activities.d.ts +2417 -50
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +1896 -1293
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +471 -340
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +12 -12
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +44 -17
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +42 -42
- package/dist/apps/data/routes/blocks.d.ts.map +1 -1
- package/dist/apps/data/routes/blocks.js +22 -12
- package/dist/apps/data/routes/blocks.js.map +1 -1
- package/dist/apps/data/routes/coingecko.d.ts +179 -179
- package/dist/apps/data/routes/earn.d.ts +144 -19
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +160 -28
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +719 -13
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +16 -13
- 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 +9 -11
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +34 -34
- package/dist/apps/data/routes/receipts.d.ts +363 -263
- package/dist/apps/data/routes/receipts.d.ts.map +1 -1
- package/dist/apps/data/routes/receipts.js +29 -11
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/rpc.js +24 -10
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +13 -13
- package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
- package/dist/apps/data/routes/tokenlist.js +16 -15
- package/dist/apps/data/routes/tokenlist.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts +112 -98
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +85 -35
- 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 +38 -18
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +1 -1
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +25 -21
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/valuation.d.ts +1 -1
- 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 +3 -3
- package/dist/apps/data/routes/webhooks.d.ts +15 -15
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +2 -2
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.d.ts +32 -32
- package/dist/apps/data/routes/zones.js +1 -1
- package/dist/apps/funding/App.d.ts +38 -27
- package/dist/apps/funding/App.d.ts.map +1 -1
- package/dist/apps/funding/routes/chains.d.ts.map +1 -1
- package/dist/apps/funding/routes/chains.js +5 -4
- package/dist/apps/funding/routes/chains.js.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.d.ts +16 -16
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js +24 -19
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
- package/dist/apps/funding/routes/providers.d.ts +13 -13
- package/dist/apps/funding/routes/providers.d.ts.map +1 -1
- package/dist/apps/funding/routes/providers.js +3 -2
- package/dist/apps/funding/routes/providers.js.map +1 -1
- package/dist/apps/funding/routes/quotes.d.ts.map +1 -1
- package/dist/apps/funding/routes/quotes.js +2 -2
- package/dist/apps/funding/routes/quotes.js.map +1 -1
- package/dist/apps/funding/routes/transfers.d.ts +60 -48
- package/dist/apps/funding/routes/transfers.d.ts.map +1 -1
- package/dist/apps/funding/routes/transfers.js +27 -19
- package/dist/apps/funding/routes/transfers.js.map +1 -1
- package/dist/apps/management/App.d.ts +4558 -3700
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/Billing.d.ts.map +1 -1
- package/dist/apps/management/Billing.js +19 -6
- package/dist/apps/management/Billing.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +786 -175
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +312 -34
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +103 -30
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +91 -32
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +35 -35
- package/dist/apps/management/routes/faucet.d.ts.map +1 -1
- package/dist/apps/management/routes/faucet.js +23 -12
- package/dist/apps/management/routes/faucet.js.map +1 -1
- package/dist/apps/management/routes/invitations.d.ts +46 -13
- package/dist/apps/management/routes/invitations.d.ts.map +1 -1
- package/dist/apps/management/routes/invitations.js +79 -29
- package/dist/apps/management/routes/invitations.js.map +1 -1
- package/dist/apps/management/routes/invite-links.d.ts +120 -75
- package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
- package/dist/apps/management/routes/invite-links.js +100 -41
- package/dist/apps/management/routes/invite-links.js.map +1 -1
- package/dist/apps/management/routes/me.d.ts +5 -5
- package/dist/apps/management/routes/me.d.ts.map +1 -1
- package/dist/apps/management/routes/me.js +14 -10
- package/dist/apps/management/routes/me.js.map +1 -1
- package/dist/apps/management/routes/members.d.ts +99 -78
- package/dist/apps/management/routes/members.d.ts.map +1 -1
- package/dist/apps/management/routes/members.js +37 -18
- package/dist/apps/management/routes/members.js.map +1 -1
- package/dist/apps/management/routes/orgs.d.ts +48 -8
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +48 -19
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts +108 -46
- package/dist/apps/management/routes/projects.d.ts.map +1 -1
- package/dist/apps/management/routes/projects.js +72 -21
- 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 +8 -5
- package/dist/apps/management/routes/scopes.js.map +1 -1
- package/dist/apps/management/routes/usage.d.ts +82 -82
- package/dist/apps/management/routes/usage.d.ts.map +1 -1
- package/dist/apps/management/routes/usage.js +41 -31
- package/dist/apps/management/routes/usage.js.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.d.ts +35 -7
- package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.js +36 -17
- package/dist/apps/management/routes/verified-token-requests.js.map +1 -1
- package/dist/apps/management/routes/webhooks.d.ts +200 -183
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/management/routes/webhooks.js +80 -35
- package/dist/apps/management/routes/webhooks.js.map +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Adapter.js +12 -0
- package/dist/apps/mpp/internal/Adapter.js.map +1 -1
- package/dist/db/Db.d.ts +2 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +52 -1
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +26 -3
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +24 -5
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/earnVaults.d.ts +5 -1
- package/dist/db/tables/earnVaults.d.ts.map +1 -1
- package/dist/db/tables/earnVaults.js +5 -0
- package/dist/db/tables/earnVaults.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/webhookDeliveries.d.ts.map +1 -1
- package/dist/db/tables/webhookDeliveries.js +10 -0
- package/dist/db/tables/webhookDeliveries.js.map +1 -1
- package/dist/db/tables/webhookQueueCompletions.d.ts +15 -0
- package/dist/db/tables/webhookQueueCompletions.d.ts.map +1 -0
- package/dist/db/tables/webhookQueueCompletions.js +32 -0
- package/dist/db/tables/webhookQueueCompletions.js.map +1 -0
- package/dist/db/tables/webhookQueueEvents.d.ts +14 -20
- package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
- package/dist/db/tables/webhookQueueEvents.js +68 -47
- package/dist/db/tables/webhookQueueEvents.js.map +1 -1
- package/dist/db/tables/webhookSubscriptions.d.ts +16 -5
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +29 -5
- 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 +11 -2
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +19 -6
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Cache.d.ts.map +1 -1
- package/dist/internal/Cache.js +11 -7
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/EarnVaults.d.ts +43 -4
- package/dist/internal/EarnVaults.d.ts.map +1 -1
- package/dist/internal/EarnVaults.js +167 -0
- 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 +3 -1
- 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 +30 -24
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +140 -41
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Response.d.ts +14 -9
- package/dist/internal/Response.d.ts.map +1 -1
- package/dist/internal/Response.js +16 -5
- package/dist/internal/Response.js.map +1 -1
- package/dist/internal/Schema.d.ts +11 -10
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +14 -9
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Store.d.ts +28 -6
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +121 -25
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Tidx.d.ts +2 -0
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +23 -6
- 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/WebhookDestination.d.ts.map +1 -1
- package/dist/internal/WebhookDestination.js +3 -0
- package/dist/internal/WebhookDestination.js.map +1 -1
- package/dist/internal/Webhooks.d.ts +29 -12
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +37 -20
- package/dist/internal/Webhooks.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/Deposit.d.ts +2 -2
- package/dist/internal/funding/Deposit.d.ts.map +1 -1
- package/dist/internal/funding/Deposit.js +9 -8
- package/dist/internal/funding/Deposit.js.map +1 -1
- package/dist/internal/funding/DepositAddress.d.ts +4 -4
- package/dist/internal/funding/DepositAddress.d.ts.map +1 -1
- package/dist/internal/funding/DepositAddress.js +9 -8
- package/dist/internal/funding/DepositAddress.js.map +1 -1
- package/dist/internal/funding/Provider.d.ts +68 -13
- package/dist/internal/funding/Provider.d.ts.map +1 -1
- package/dist/internal/funding/Provider.js +42 -20
- package/dist/internal/funding/Provider.js.map +1 -1
- package/dist/internal/funding/Transfer.d.ts +13 -12
- package/dist/internal/funding/Transfer.d.ts.map +1 -1
- package/dist/internal/funding/Transfer.js +23 -21
- package/dist/internal/funding/Transfer.js.map +1 -1
- package/dist/internal/funding/providers/stargate.d.ts +38 -0
- package/dist/internal/funding/providers/stargate.d.ts.map +1 -0
- package/dist/internal/funding/providers/stargate.js +277 -0
- package/dist/internal/funding/providers/stargate.js.map +1 -0
- 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/providers/index.d.ts +7 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -0
- package/dist/providers/index.js.map +1 -1
- package/package.json +3 -3
- package/src/App.test.ts +148 -0
- package/src/App.ts +45 -4
- package/src/Assets.test.ts +18 -0
- package/src/Assets.ts +22 -0
- package/src/Client.test-d.ts +311 -16
- package/src/Scope.test.ts +20 -0
- package/src/Scope.ts +16 -0
- 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 +143 -0
- package/src/admin/apps/earn-vaults.ts +84 -7
- package/src/admin/apps/organizations.ts +6 -2
- package/src/admin/ui/src/lib/api-keys.ts +19 -0
- package/src/admin/ui/src/routes/api-keys.tsx +125 -2
- package/src/admin/ui.generated.ts +71 -71
- package/src/apps/Zones.test.ts +4 -4
- package/src/apps/Zones.ts +4 -4
- package/src/apps/composition.test.ts +1 -1
- package/src/apps/data/App.test.ts +72 -0
- package/src/apps/data/App.ts +58 -10
- package/src/apps/data/ZoneSelection.test.ts +196 -0
- package/src/apps/data/ZoneSelection.ts +108 -0
- package/src/apps/data/routes/activities.test.ts +1843 -2
- package/src/apps/data/routes/activities.ts +2211 -1388
- package/src/apps/data/routes/balances.ts +134 -98
- package/src/apps/data/routes/blocks.test.ts +533 -0
- package/src/apps/data/routes/blocks.ts +36 -17
- package/src/apps/data/routes/earn.test.ts +187 -25
- package/src/apps/data/routes/earn.ts +228 -32
- package/src/apps/data/routes/earn.zone-access.test.ts +46 -0
- package/src/apps/data/routes/exchanges.test.ts +31 -1
- package/src/apps/data/routes/exchanges.ts +19 -15
- package/src/apps/data/routes/fee-amm.ts +18 -11
- package/src/apps/data/routes/receipts.ts +48 -17
- package/src/apps/data/routes/rpc.test.ts +75 -0
- package/src/apps/data/routes/rpc.ts +23 -9
- package/src/apps/data/routes/tokenlist.ts +21 -18
- package/src/apps/data/routes/tokens.test.ts +607 -0
- package/src/apps/data/routes/tokens.ts +312 -231
- package/src/apps/data/routes/transactions.ts +69 -30
- package/src/apps/data/routes/transfers.test.ts +424 -3
- package/src/apps/data/routes/transfers.ts +57 -36
- package/src/apps/data/routes/valuation.ts +94 -78
- package/src/apps/data/routes/webhooks.ts +6 -2
- package/src/apps/data/routes/zones.test.ts +21 -15
- package/src/apps/data/routes/zones.ts +1 -1
- package/src/apps/funding/routes/chains.test.ts +211 -7
- package/src/apps/funding/routes/chains.ts +19 -12
- package/src/apps/funding/routes/deposit-addresses.test.ts +416 -0
- package/src/apps/funding/routes/deposit-addresses.ts +101 -84
- package/src/apps/funding/routes/providers.ts +9 -5
- package/src/apps/funding/routes/quotes.test.ts +42 -0
- package/src/apps/funding/routes/quotes.ts +8 -5
- package/src/apps/funding/routes/transfers.test.ts +430 -40
- package/src/apps/funding/routes/transfers.ts +76 -59
- package/src/apps/management/Billing.test.ts +26 -0
- package/src/apps/management/Billing.ts +24 -7
- package/src/apps/management/routes/api-keys.test.ts +398 -0
- package/src/apps/management/routes/api-keys.ts +481 -145
- package/src/apps/management/routes/billing.test.ts +209 -0
- package/src/apps/management/routes/billing.ts +254 -177
- package/src/apps/management/routes/faucet.test.ts +39 -0
- package/src/apps/management/routes/faucet.ts +75 -41
- package/src/apps/management/routes/invitations.test.ts +216 -0
- package/src/apps/management/routes/invitations.ts +186 -109
- package/src/apps/management/routes/invite-links.test.ts +263 -1
- package/src/apps/management/routes/invite-links.ts +295 -214
- package/src/apps/management/routes/me.test.ts +49 -0
- package/src/apps/management/routes/me.ts +42 -32
- package/src/apps/management/routes/members.test.ts +124 -1
- package/src/apps/management/routes/members.ts +81 -51
- package/src/apps/management/routes/orgs.test.ts +176 -0
- package/src/apps/management/routes/orgs.ts +101 -59
- package/src/apps/management/routes/projects.test.ts +183 -0
- package/src/apps/management/routes/projects.ts +124 -60
- package/src/apps/management/routes/scopes.test.ts +44 -0
- package/src/apps/management/routes/scopes.ts +37 -28
- package/src/apps/management/routes/usage.test.ts +92 -0
- package/src/apps/management/routes/usage.ts +186 -151
- package/src/apps/management/routes/verified-token-requests.test.ts +114 -0
- package/src/apps/management/routes/verified-token-requests.ts +166 -133
- package/src/apps/management/routes/webhooks.test.ts +234 -0
- package/src/apps/management/routes/webhooks.ts +191 -78
- package/src/apps/mpp/internal/Adapter.ts +13 -1
- package/src/db/Db.test.ts +41 -0
- package/src/db/Db.ts +75 -1
- package/src/db/Schema.ts +33 -5
- package/src/db/tables/earnVaults.test.ts +22 -0
- package/src/db/tables/earnVaults.ts +11 -1
- package/src/db/tables/fundingCatalog.test.ts +16 -2
- package/src/db/tables/fundingCatalog.ts +9 -3
- package/src/db/tables/webhookDeliveries.ts +13 -0
- package/src/db/tables/webhookQueueCompletions.ts +55 -0
- package/src/db/tables/webhookQueueEvents.ts +95 -61
- package/src/db/tables/webhookSubscriptions.ts +45 -6
- package/src/handlers/internal/utils.test.ts +53 -0
- package/src/handlers/internal/utils.ts +15 -0
- package/src/internal/Auth.ts +35 -9
- package/src/internal/Cache.ts +14 -7
- package/src/internal/EarnVaults.capabilities.test.ts +33 -0
- package/src/internal/EarnVaults.test.ts +79 -0
- package/src/internal/EarnVaults.ts +277 -0
- package/src/internal/Fetch.test.ts +46 -0
- package/src/internal/Fetch.ts +56 -0
- package/src/internal/Log.test.ts +21 -0
- package/src/internal/Log.ts +20 -5
- package/src/internal/OpenApi.ts +177 -51
- package/src/internal/Response.test.ts +95 -0
- package/src/internal/Response.ts +41 -3
- package/src/internal/Schema.test.ts +18 -0
- package/src/internal/Schema.ts +19 -9
- package/src/internal/Store.test.ts +364 -0
- package/src/internal/Store.ts +153 -27
- package/src/internal/Tidx.test.ts +21 -1
- package/src/internal/Tidx.ts +23 -6
- 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/WebhookDestination.ts +3 -0
- package/src/internal/Webhooks.test.ts +122 -8
- package/src/internal/Webhooks.ts +59 -29
- 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/Deposit.ts +71 -64
- package/src/internal/funding/DepositAddress.ts +50 -43
- package/src/internal/funding/Provider.test.ts +73 -0
- package/src/internal/funding/Provider.ts +246 -170
- package/src/internal/funding/Transfer.ts +133 -110
- package/src/internal/funding/providers/index.test.ts +22 -0
- package/src/internal/funding/providers/relay.test.ts +2 -2
- package/src/internal/funding/providers/stargate.test.ts +163 -0
- package/src/internal/funding/providers/stargate.ts +409 -0
- package/src/internal/index.test.ts +1 -0
- package/src/internal/index.ts +1 -0
- package/src/providers/index.test.ts +1 -0
- package/src/providers/index.ts +9 -0
|
@@ -10,7 +10,7 @@ const slugSuffix = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', 8)
|
|
|
10
10
|
/** Columns of the `earn_vaults` table. */
|
|
11
11
|
export type Table = Omit<
|
|
12
12
|
db_Schema.EarnVault,
|
|
13
|
-
'chainId' | 'privateInputTokens' | 'privateOutputTokens'
|
|
13
|
+
'chainId' | 'privateInputTokens' | 'privateOutputTokens' | 'zones'
|
|
14
14
|
> & {
|
|
15
15
|
/** Bigint chain id, read as a string from pg and written as a number. */
|
|
16
16
|
chainId: ColumnType<string, number, number>
|
|
@@ -18,6 +18,8 @@ export type Table = Omit<
|
|
|
18
18
|
privateInputTokens: JSONColumnType<db_Schema.EarnVault['privateInputTokens'], string, string>
|
|
19
19
|
/** Tokens supported for private redemptions. */
|
|
20
20
|
privateOutputTokens: JSONColumnType<db_Schema.EarnVault['privateOutputTokens'], string, string>
|
|
21
|
+
/** Curated private Zone routes. */
|
|
22
|
+
zones: JSONColumnType<db_Schema.EarnVault['zones'], string, string>
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
/** A stored curated Earn vault. */
|
|
@@ -76,6 +78,7 @@ export async function upsert(db: Db.Db, options: upsert.Options): Promise<Record
|
|
|
76
78
|
slug,
|
|
77
79
|
updatedAt: now,
|
|
78
80
|
vaultAddress: normalize(options.vaultAddress),
|
|
81
|
+
zones: JSON.stringify(normalizeZones(options.zones)),
|
|
79
82
|
})
|
|
80
83
|
.onConflict((oc) =>
|
|
81
84
|
oc.columns(['chainId', 'vaultAddress']).doUpdateSet({
|
|
@@ -85,6 +88,7 @@ export async function upsert(db: Db.Db, options: upsert.Options): Promise<Record
|
|
|
85
88
|
privateOutputTokens: JSON.stringify(options.privateOutputTokens),
|
|
86
89
|
...(options.slug !== undefined ? { slug } : {}),
|
|
87
90
|
updatedAt: now,
|
|
91
|
+
zones: JSON.stringify(normalizeZones(options.zones)),
|
|
88
92
|
}),
|
|
89
93
|
)
|
|
90
94
|
.returningAll()
|
|
@@ -109,6 +113,8 @@ export declare namespace upsert {
|
|
|
109
113
|
slug?: string | undefined
|
|
110
114
|
/** Earn vault address. */
|
|
111
115
|
vaultAddress: Address
|
|
116
|
+
/** Curated private Zone routes. */
|
|
117
|
+
zones: db_Schema.EarnVault['zones']
|
|
112
118
|
}
|
|
113
119
|
}
|
|
114
120
|
|
|
@@ -149,3 +155,7 @@ function toRecord(row: Selectable<Table>): Record {
|
|
|
149
155
|
function normalize(address: string): Address {
|
|
150
156
|
return address.toLowerCase() as Address
|
|
151
157
|
}
|
|
158
|
+
|
|
159
|
+
function normalizeZones(zones: db_Schema.EarnVault['zones']): db_Schema.EarnVault['zones'] {
|
|
160
|
+
return zones.map((zone) => ({ ...zone, earnRouter: normalize(zone.earnRouter) }))
|
|
161
|
+
}
|
|
@@ -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
|
|
|
@@ -225,6 +225,10 @@ export async function get(
|
|
|
225
225
|
return row ? toDelivery(row) : null
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
// Per-statement delete bound: prune runs every minute, so a backlog drains in
|
|
229
|
+
// a few ticks instead of one unbounded DELETE churning the origin.
|
|
230
|
+
const pruneBatchLimit = 50_000
|
|
231
|
+
|
|
228
232
|
/**
|
|
229
233
|
* Deletes deliveries past their retention deadline. Called best-effort from
|
|
230
234
|
* the poller tick.
|
|
@@ -236,7 +240,16 @@ export async function get(
|
|
|
236
240
|
export async function pruneExpired(db: Db.Db, now: string): Promise<number> {
|
|
237
241
|
const result = await db.kysely
|
|
238
242
|
.deleteFrom('webhook_deliveries')
|
|
243
|
+
// Repeated on the outer delete: the row recheck under concurrency re-runs
|
|
244
|
+
// only this predicate, so a row renewed mid-statement survives.
|
|
239
245
|
.where('expiresAt', '<=', now)
|
|
246
|
+
.where('id', 'in', (eb) =>
|
|
247
|
+
eb
|
|
248
|
+
.selectFrom('webhook_deliveries')
|
|
249
|
+
.select('id')
|
|
250
|
+
.where('expiresAt', '<=', now)
|
|
251
|
+
.limit(pruneBatchLimit),
|
|
252
|
+
)
|
|
240
253
|
.executeTakeFirst()
|
|
241
254
|
return Number(result.numDeletedRows)
|
|
242
255
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type * as Webhooks from '../../internal/Webhooks.js'
|
|
2
|
+
import type * as Db from '../Db.js'
|
|
3
|
+
import type * as db_Schema from '../Schema.js'
|
|
4
|
+
|
|
5
|
+
/** Columns of the `webhook_queue_completions` table, derived from `Schema.WebhookQueueCompletion`. */
|
|
6
|
+
export type Table = db_Schema.WebhookQueueCompletion
|
|
7
|
+
|
|
8
|
+
/** A terminal delivery-obligation outcome. */
|
|
9
|
+
export type TerminalStatus = db_Schema.WebhookQueueCompletion['status']
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Reads the unexpired completion for a reference, if any. Rows are written by
|
|
13
|
+
* the queue-event terminal transition and only ever read for dedupe.
|
|
14
|
+
*/
|
|
15
|
+
export async function get(
|
|
16
|
+
db: Db.Db,
|
|
17
|
+
reference: Webhooks.QueueReference,
|
|
18
|
+
now: string,
|
|
19
|
+
): Promise<Table | null> {
|
|
20
|
+
return (
|
|
21
|
+
(await db.kysely
|
|
22
|
+
.selectFrom('webhook_queue_completions')
|
|
23
|
+
.selectAll()
|
|
24
|
+
.where('eventId', '=', reference.eventId)
|
|
25
|
+
.where('subscriptionId', '=', reference.subscriptionId)
|
|
26
|
+
.where('expiresAt', '>', now)
|
|
27
|
+
.executeTakeFirst()) ?? null
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Per-statement delete bound: prune runs every minute, so a backlog drains in
|
|
32
|
+
// a few ticks instead of one unbounded DELETE churning the origin.
|
|
33
|
+
const pruneBatchLimit = 50_000
|
|
34
|
+
|
|
35
|
+
/** Deletes expired completions, bounded per call; expiry is filtered at query time, so pruning is space-only. */
|
|
36
|
+
export async function pruneExpired(db: Db.Db, now: string): Promise<number> {
|
|
37
|
+
const result = await db.kysely
|
|
38
|
+
.deleteFrom('webhook_queue_completions')
|
|
39
|
+
// Repeated on the outer delete: the row recheck under concurrency re-runs
|
|
40
|
+
// only this predicate, so a completion renewed mid-statement survives.
|
|
41
|
+
.where('expiresAt', '<=', now)
|
|
42
|
+
.where(({ eb, refTuple, selectFrom }) =>
|
|
43
|
+
eb(
|
|
44
|
+
refTuple('subscriptionId', 'eventId'),
|
|
45
|
+
'in',
|
|
46
|
+
selectFrom('webhook_queue_completions')
|
|
47
|
+
.select(['subscriptionId', 'eventId'])
|
|
48
|
+
.where('expiresAt', '<=', now)
|
|
49
|
+
.limit(pruneBatchLimit)
|
|
50
|
+
.$asTuple('subscriptionId', 'eventId'),
|
|
51
|
+
),
|
|
52
|
+
)
|
|
53
|
+
.executeTakeFirst()
|
|
54
|
+
return Number(result.numDeletedRows)
|
|
55
|
+
}
|
|
@@ -2,17 +2,20 @@ import { type ExpressionBuilder, type Generated, type Insertable, type Selectabl
|
|
|
2
2
|
|
|
3
3
|
import type * as Webhooks from '../../internal/Webhooks.js'
|
|
4
4
|
import type * as Db from '../Db.js'
|
|
5
|
+
import * as WebhookQueueCompletions from './webhookQueueCompletions.js'
|
|
5
6
|
import type * as db_Schema from '../Schema.js'
|
|
6
7
|
|
|
7
8
|
/** Columns of the `webhook_queue_events` table, derived from `Schema.WebhookQueueEvent`. */
|
|
8
9
|
export type Table = Omit<
|
|
9
10
|
db_Schema.WebhookQueueEvent,
|
|
10
|
-
'attemptCount' | 'attemptingAt' | 'nextAttemptAt' | 'observedAt' | 'status'
|
|
11
|
+
'attemptCount' | 'attemptingAt' | 'expiresAt' | 'nextAttemptAt' | 'observedAt' | 'status'
|
|
11
12
|
> & {
|
|
12
13
|
/** Delivery attempts claimed so far; database default 0. */
|
|
13
14
|
attemptCount: Generated<number>
|
|
14
15
|
/** ISO timestamp of the current claim; database default null. */
|
|
15
16
|
attemptingAt: Generated<string | null>
|
|
17
|
+
/** Legacy dedupe deadline; unwritten since completions took over. */
|
|
18
|
+
expiresAt: Generated<string | null>
|
|
16
19
|
/** ISO retry due time; database default null (due immediately). */
|
|
17
20
|
nextAttemptAt: Generated<string | null>
|
|
18
21
|
/** ISO head-observation stamp; database default null (replays carry none). */
|
|
@@ -24,12 +27,9 @@ export type Table = Omit<
|
|
|
24
27
|
/** A stored webhook Queue event. */
|
|
25
28
|
export type Record = Selectable<Table>
|
|
26
29
|
|
|
27
|
-
/** A terminal delivery-obligation outcome. */
|
|
28
|
-
export type TerminalStatus = Exclude<db_Schema.WebhookQueueEvent['status'], 'pending'>
|
|
29
|
-
|
|
30
30
|
/**
|
|
31
|
-
* Reads one pending envelope.
|
|
32
|
-
*
|
|
31
|
+
* Reads one pending envelope. The status filter only ever excludes rows
|
|
32
|
+
* completed before the completions split; completions now leave the table.
|
|
33
33
|
*/
|
|
34
34
|
export async function get(
|
|
35
35
|
db: Db.Db,
|
|
@@ -47,42 +47,56 @@ export async function get(
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
* Stages envelopes as fresh obligations.
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* dedupe row pruning has not yet reclaimed never blocks a replay.
|
|
50
|
+
* Stages envelopes as fresh obligations. Unexpired completions and any
|
|
51
|
+
* existing row suppress the replay; a replay stages only when neither exists,
|
|
52
|
+
* which a completion past its dedupe window guarantees for completed work.
|
|
54
53
|
*/
|
|
55
54
|
export async function ensure(db: Db.Db, rows: readonly Insertable<Table>[]): Promise<number> {
|
|
56
55
|
if (rows.length === 0) return 0
|
|
56
|
+
const values = sql.join(
|
|
57
|
+
rows.map(
|
|
58
|
+
(row) =>
|
|
59
|
+
sql`(${row.subscriptionId}::text, ${row.eventId}::text, ${JSON.stringify(row.envelope)}::jsonb, ${row.createdAt}::text, ${row.nextAttemptAt ?? null}::text, ${row.observedAt ?? null}::text)`,
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
// The VALUES source is raw because kysely has no table constructor for it,
|
|
63
|
+
// but both real tables stay in the builder so WithSchemaPlugin qualifies
|
|
64
|
+
// them (previews isolate by schema; raw identifiers would escape it). The
|
|
65
|
+
// completion check must ride the insert: a separate read would let a
|
|
66
|
+
// completion commit between the statements and re-stage just-finished work.
|
|
67
|
+
// `column1..6` are Postgres's default VALUES names, in the row order above;
|
|
68
|
+
// the window comparison uses each row's own staging time, not a shared clock.
|
|
69
|
+
// Conflicts only ever hit pending rows (or pre-split terminal rows during
|
|
70
|
+
// the drain), and both suppress the replay; a legitimate post-expiry replay
|
|
71
|
+
// meets no conflict because its completed row was deleted.
|
|
57
72
|
const result = await db.kysely
|
|
58
73
|
.insertInto('webhook_queue_events')
|
|
59
|
-
.
|
|
60
|
-
.
|
|
61
|
-
|
|
62
|
-
.
|
|
63
|
-
.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
.columns(['subscriptionId', 'eventId', 'envelope', 'createdAt', 'nextAttemptAt', 'observedAt'])
|
|
75
|
+
.expression((eb) =>
|
|
76
|
+
eb
|
|
77
|
+
.selectFrom(sql`(VALUES ${values})`.as('v'))
|
|
78
|
+
.select([
|
|
79
|
+
sql`v.column1`.as('subscriptionId'),
|
|
80
|
+
sql`v.column2`.as('eventId'),
|
|
81
|
+
sql`v.column3`.as('envelope'),
|
|
82
|
+
sql`v.column4`.as('createdAt'),
|
|
83
|
+
sql`v.column5`.as('nextAttemptAt'),
|
|
84
|
+
sql`v.column6`.as('observedAt'),
|
|
85
|
+
])
|
|
86
|
+
.where(({ exists, not, selectFrom }) =>
|
|
87
|
+
not(
|
|
88
|
+
exists(
|
|
89
|
+
selectFrom('webhook_queue_completions')
|
|
90
|
+
.select('eventId')
|
|
91
|
+
.where('subscriptionId', '=', sql<string>`v.column1`)
|
|
92
|
+
.where('eventId', '=', sql<string>`v.column2`)
|
|
93
|
+
.where('expiresAt', '>', sql<string>`v.column4`),
|
|
94
|
+
),
|
|
95
|
+
),
|
|
81
96
|
),
|
|
82
97
|
)
|
|
98
|
+
.onConflict((oc) => oc.columns(['subscriptionId', 'eventId']).doNothing())
|
|
83
99
|
.executeTakeFirst()
|
|
84
|
-
// The command tag counts inserted rows plus replaced expired rows; live
|
|
85
|
-
// conflicts fall out of the conditional update and are not counted.
|
|
86
100
|
return Number(result.numInsertedOrUpdatedRows ?? 0n)
|
|
87
101
|
}
|
|
88
102
|
|
|
@@ -114,13 +128,23 @@ export async function claim(
|
|
|
114
128
|
.executeTakeFirst()
|
|
115
129
|
if (claimed) return { record: claimed, type: 'claimed' }
|
|
116
130
|
|
|
131
|
+
// Queue first, completions second: a complete() landing between the reads
|
|
132
|
+
// then still surfaces its completion, so a finished obligation never reads
|
|
133
|
+
// as missing, the one claim outcome alerts treat as a fault.
|
|
117
134
|
const existing = await db.kysely
|
|
118
135
|
.selectFrom('webhook_queue_events')
|
|
119
136
|
.select(['attemptingAt', 'nextAttemptAt', 'status'])
|
|
120
137
|
.where('subscriptionId', '=', reference.subscriptionId)
|
|
121
138
|
.where('eventId', '=', reference.eventId)
|
|
122
139
|
.executeTakeFirst()
|
|
123
|
-
if (!existing)
|
|
140
|
+
if (!existing) {
|
|
141
|
+
// An unexpired completion reads as terminal so claim-outcome metrics keep
|
|
142
|
+
// their meaning; an expired one reads as missing, like a pruned row.
|
|
143
|
+
const completed = await WebhookQueueCompletions.get(db, reference, options.now)
|
|
144
|
+
if (completed) return { status: completed.status, type: 'terminal' }
|
|
145
|
+
return { type: 'missing' }
|
|
146
|
+
}
|
|
147
|
+
// Reachable only for rows completed before the completions split.
|
|
124
148
|
if (existing.status !== 'pending') return { status: existing.status, type: 'terminal' }
|
|
125
149
|
if (existing.attemptingAt !== null && existing.attemptingAt > options.staleBefore)
|
|
126
150
|
return { type: 'attempting' }
|
|
@@ -142,12 +166,12 @@ export declare namespace claim {
|
|
|
142
166
|
| { record: Record; type: 'claimed' }
|
|
143
167
|
| { type: 'missing' }
|
|
144
168
|
| { nextAttemptAt: string | null; type: 'scheduled' }
|
|
145
|
-
| { status: TerminalStatus; type: 'terminal' }
|
|
169
|
+
| { status: WebhookQueueCompletions.TerminalStatus; type: 'terminal' }
|
|
146
170
|
}
|
|
147
171
|
|
|
148
172
|
/**
|
|
149
|
-
* Marks a claimed obligation terminal
|
|
150
|
-
*
|
|
173
|
+
* Marks a claimed obligation terminal: one atomic statement deletes the queue
|
|
174
|
+
* row and writes the insert-only completion that starts the dedupe window.
|
|
151
175
|
* The `attemptingAt` predicate fences stale claimants.
|
|
152
176
|
*/
|
|
153
177
|
export async function complete(
|
|
@@ -156,14 +180,38 @@ export async function complete(
|
|
|
156
180
|
options: complete.Options,
|
|
157
181
|
): Promise<boolean> {
|
|
158
182
|
const result = await db.kysely
|
|
159
|
-
.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
183
|
+
.with('done', (qb) =>
|
|
184
|
+
qb
|
|
185
|
+
.deleteFrom('webhook_queue_events')
|
|
186
|
+
.where('subscriptionId', '=', reference.subscriptionId)
|
|
187
|
+
.where('eventId', '=', reference.eventId)
|
|
188
|
+
.where('status', '=', 'pending')
|
|
189
|
+
.where('attemptingAt', '=', options.claimedAt)
|
|
190
|
+
.returning(['subscriptionId', 'eventId']),
|
|
191
|
+
)
|
|
192
|
+
.insertInto('webhook_queue_completions')
|
|
193
|
+
.columns(['subscriptionId', 'eventId', 'status', 'expiresAt'])
|
|
194
|
+
.expression((eb) =>
|
|
195
|
+
eb
|
|
196
|
+
.selectFrom('done')
|
|
197
|
+
.select((s) => [
|
|
198
|
+
'subscriptionId',
|
|
199
|
+
'eventId',
|
|
200
|
+
s.val(options.status).as('status'),
|
|
201
|
+
s.val(options.expiresAt).as('expiresAt'),
|
|
202
|
+
]),
|
|
203
|
+
)
|
|
204
|
+
// Re-completing a replayed obligation: the latest delivery outcome wins
|
|
205
|
+
// and retention restarts, matching pre-split replay semantics; doNothing
|
|
206
|
+
// would also report zero rows and fail a fence that won.
|
|
207
|
+
.onConflict((oc) =>
|
|
208
|
+
oc.columns(['subscriptionId', 'eventId']).doUpdateSet((eb) => ({
|
|
209
|
+
expiresAt: eb.ref('excluded.expiresAt'),
|
|
210
|
+
status: eb.ref('excluded.status'),
|
|
211
|
+
})),
|
|
212
|
+
)
|
|
165
213
|
.executeTakeFirst()
|
|
166
|
-
return result.
|
|
214
|
+
return result.numInsertedOrUpdatedRows === 1n
|
|
167
215
|
}
|
|
168
216
|
|
|
169
217
|
export declare namespace complete {
|
|
@@ -171,10 +219,10 @@ export declare namespace complete {
|
|
|
171
219
|
type Options = {
|
|
172
220
|
/** Claim marker returned by {@link claim}; fences stale claimants. */
|
|
173
221
|
claimedAt: string
|
|
174
|
-
/**
|
|
222
|
+
/** ISO retention deadline for the dedupe completion. */
|
|
175
223
|
expiresAt: string
|
|
176
224
|
/** Terminal outcome. */
|
|
177
|
-
status: TerminalStatus
|
|
225
|
+
status: WebhookQueueCompletions.TerminalStatus
|
|
178
226
|
}
|
|
179
227
|
}
|
|
180
228
|
|
|
@@ -275,17 +323,3 @@ export declare namespace pending {
|
|
|
275
323
|
oldestDueAt: string | null
|
|
276
324
|
}
|
|
277
325
|
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Deletes terminal dedupe rows past their retention deadline. Pending rows
|
|
281
|
-
* never expire: owed work drains only through terminal transitions
|
|
282
|
-
* (delivery, permanent failure, or the skip after auto-disable).
|
|
283
|
-
*/
|
|
284
|
-
export async function pruneExpired(db: Db.Db, now: string): Promise<number> {
|
|
285
|
-
const result = await db.kysely
|
|
286
|
-
.deleteFrom('webhook_queue_events')
|
|
287
|
-
.where('expiresAt', '<=', now)
|
|
288
|
-
.where('status', '!=', 'pending')
|
|
289
|
-
.executeTakeFirst()
|
|
290
|
-
return Number(result.numDeletedRows)
|
|
291
|
-
}
|
|
@@ -447,37 +447,57 @@ export async function getCursor(db: Db.Db, id: string): Promise<string | null> {
|
|
|
447
447
|
|
|
448
448
|
/**
|
|
449
449
|
* Records a successful delivery: resets the consecutive-failure counter and
|
|
450
|
-
* stamps `lastDeliveryAt`.
|
|
450
|
+
* stamps `lastDeliveryAt`. Skipped when neither field would move, so
|
|
451
|
+
* deliveries stop contending on the row's lock.
|
|
451
452
|
*
|
|
452
453
|
* @param db - The database.
|
|
453
454
|
* @param id - The subscription id (`wh_…`).
|
|
454
|
-
* @param
|
|
455
|
-
* @returns The updated subscription, or `undefined` when
|
|
455
|
+
* @param options - Delivery timestamp and stamp-staleness bound.
|
|
456
|
+
* @returns The updated subscription, or `undefined` when nothing was written.
|
|
456
457
|
*/
|
|
457
458
|
export async function recordSuccess(
|
|
458
459
|
db: Db.Db,
|
|
459
460
|
id: string,
|
|
460
|
-
|
|
461
|
+
options: recordSuccess.Options,
|
|
461
462
|
): Promise<Webhooks.Subscription | undefined> {
|
|
463
|
+
const { now, staleBefore } = options
|
|
462
464
|
const row = await db.kysely
|
|
463
465
|
.updateTable('webhook_subscriptions')
|
|
464
466
|
.set({ failureCount: 0, lastDeliveryAt: now, updatedAt: now })
|
|
465
467
|
.where('id', '=', id)
|
|
468
|
+
.where((eb) =>
|
|
469
|
+
eb.or([
|
|
470
|
+
eb('failureCount', '>', 0),
|
|
471
|
+
eb('lastDeliveryAt', 'is', null),
|
|
472
|
+
eb('lastDeliveryAt', '<', staleBefore),
|
|
473
|
+
]),
|
|
474
|
+
)
|
|
466
475
|
.returningAll()
|
|
467
476
|
.executeTakeFirst()
|
|
468
477
|
return row ? toSubscription(row) : undefined
|
|
469
478
|
}
|
|
470
479
|
|
|
480
|
+
export declare namespace recordSuccess {
|
|
481
|
+
/** Clock bounds for one success record. */
|
|
482
|
+
type Options = {
|
|
483
|
+
/** Delivery timestamp (ISO). */
|
|
484
|
+
now: string
|
|
485
|
+
/** Rewrite the stamp only when it predates this (ISO). */
|
|
486
|
+
staleBefore: string
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
471
490
|
/**
|
|
472
491
|
* Records a failed delivery: atomically increments the consecutive-failure
|
|
473
492
|
* counter and disables the subscription once it reaches `maxFailures` (raw
|
|
474
493
|
* fragments use storage column names — they bypass the camelCase mapping).
|
|
494
|
+
* Skipped once disabled at the cap, when nothing would move.
|
|
475
495
|
*
|
|
476
496
|
* @param db - The database.
|
|
477
497
|
* @param id - The subscription id (`wh_…`).
|
|
478
498
|
* @param maxFailures - Consecutive failures before auto-disabling.
|
|
479
499
|
* @param now - Failure timestamp (ISO).
|
|
480
|
-
* @returns The updated subscription, or `undefined` when
|
|
500
|
+
* @returns The updated subscription, or `undefined` when nothing was written.
|
|
481
501
|
*/
|
|
482
502
|
export async function recordFailure(
|
|
483
503
|
db: Db.Db,
|
|
@@ -493,11 +513,19 @@ export async function recordFailure(
|
|
|
493
513
|
updatedAt: now,
|
|
494
514
|
})
|
|
495
515
|
.where('id', '=', id)
|
|
516
|
+
// In-flight failures race the disable and would queue on the row lock. A
|
|
517
|
+
// re-enabled subscription keeps its capped counter; the status arm lets
|
|
518
|
+
// its next failure re-disable it.
|
|
519
|
+
.where((eb) => eb.or([eb('failureCount', '<', maxFailures), eb('status', '!=', 'disabled')]))
|
|
496
520
|
.returningAll()
|
|
497
521
|
.executeTakeFirst()
|
|
498
522
|
return row ? toSubscription(row) : undefined
|
|
499
523
|
}
|
|
500
524
|
|
|
525
|
+
// Smaller than the ledger-table bounds: each subscription delete cascades
|
|
526
|
+
// into deliveries, queue events, and completions, fanning the statement out.
|
|
527
|
+
const pruneBatchLimit = 1_000
|
|
528
|
+
|
|
501
529
|
/**
|
|
502
530
|
* Deletes expired subscriptions (their delivery rows cascade). Called
|
|
503
531
|
* best-effort on a schedule.
|
|
@@ -507,10 +535,21 @@ export async function recordFailure(
|
|
|
507
535
|
* @returns The number of rows deleted.
|
|
508
536
|
*/
|
|
509
537
|
export async function pruneExpired(db: Db.Db, now: string): Promise<number> {
|
|
538
|
+
// Bounded like the ledger prunes: this delete cascades into deliveries,
|
|
539
|
+
// queue events, and completions, so it fans out the widest of the four.
|
|
510
540
|
const result = await db.kysely
|
|
511
541
|
.deleteFrom('webhook_subscriptions')
|
|
512
|
-
|
|
542
|
+
// Repeated on the outer delete: the row recheck under concurrency re-runs
|
|
543
|
+
// only this predicate, so a subscription renewed mid-statement survives.
|
|
513
544
|
.where('expiresAt', '<=', now)
|
|
545
|
+
.where('id', 'in', (eb) =>
|
|
546
|
+
eb
|
|
547
|
+
.selectFrom('webhook_subscriptions')
|
|
548
|
+
.select('id')
|
|
549
|
+
.where('expiresAt', 'is not', null)
|
|
550
|
+
.where('expiresAt', '<=', now)
|
|
551
|
+
.limit(pruneBatchLimit),
|
|
552
|
+
)
|
|
514
553
|
.executeTakeFirst()
|
|
515
554
|
return Number(result.numDeletedRows)
|
|
516
555
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { RpcRequest } from 'ox'
|
|
2
|
+
|
|
3
|
+
import * as Utils from './utils.js'
|
|
4
|
+
|
|
5
|
+
const request = RpcRequest.from({ id: 1, method: 'eth_sendRawTransaction', params: ['0x'] })
|
|
6
|
+
|
|
7
|
+
describe('rpcErrorJson', () => {
|
|
8
|
+
test('maps upstream expired transactions to a transaction-rejected error', () => {
|
|
9
|
+
const error = {
|
|
10
|
+
cause: {
|
|
11
|
+
code: -32603,
|
|
12
|
+
message:
|
|
13
|
+
'Revm error: transaction expired: current block timestamp 1755616740 >= validBefore 1755616739',
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
expect(Utils.rpcErrorJson(request, error)).toMatchInlineSnapshot(`
|
|
18
|
+
{
|
|
19
|
+
"error": {
|
|
20
|
+
"code": -32003,
|
|
21
|
+
"data": {
|
|
22
|
+
"code": "transaction_expired",
|
|
23
|
+
},
|
|
24
|
+
"message": "Transaction expired.",
|
|
25
|
+
},
|
|
26
|
+
"id": 1,
|
|
27
|
+
"jsonrpc": "2.0",
|
|
28
|
+
}
|
|
29
|
+
`)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('preserves unrelated upstream internal errors', () => {
|
|
33
|
+
const error = {
|
|
34
|
+
code: -32603,
|
|
35
|
+
data: { reason: 'database unavailable' },
|
|
36
|
+
message: 'Revm error: database unavailable',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
expect(Utils.rpcErrorJson(request, error)).toMatchInlineSnapshot(`
|
|
40
|
+
{
|
|
41
|
+
"error": {
|
|
42
|
+
"code": -32603,
|
|
43
|
+
"data": {
|
|
44
|
+
"reason": "database unavailable",
|
|
45
|
+
},
|
|
46
|
+
"message": "Revm error: database unavailable",
|
|
47
|
+
},
|
|
48
|
+
"id": 1,
|
|
49
|
+
"jsonrpc": "2.0",
|
|
50
|
+
}
|
|
51
|
+
`)
|
|
52
|
+
})
|
|
53
|
+
})
|
|
@@ -127,6 +127,21 @@ export function rpcErrorJson(request: RpcRequest.RpcRequest, error: unknown) {
|
|
|
127
127
|
{ request },
|
|
128
128
|
)
|
|
129
129
|
|
|
130
|
+
if (
|
|
131
|
+
inner.code === RpcResponse.InternalError.code &&
|
|
132
|
+
/^Revm error: transaction expired(?:\.|: .+)?$/.test(inner.message)
|
|
133
|
+
)
|
|
134
|
+
return RpcResponse.from(
|
|
135
|
+
{
|
|
136
|
+
error: {
|
|
137
|
+
code: RpcResponse.TransactionRejectedError.code,
|
|
138
|
+
data: { code: 'transaction_expired' },
|
|
139
|
+
message: 'Transaction expired.',
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{ request },
|
|
143
|
+
)
|
|
144
|
+
|
|
130
145
|
const { code, data, message } = inner
|
|
131
146
|
return RpcResponse.from(
|
|
132
147
|
{
|