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
|
@@ -348,6 +348,41 @@ describe('recordSuccess / recordFailure', () => {
|
|
|
348
348
|
expect(read?.lastDeliveryAt).toBe('2026-01-01T00:00:00.000Z')
|
|
349
349
|
})
|
|
350
350
|
|
|
351
|
+
test('recordSuccess refreshes the stamp at most once per precision window', async () => {
|
|
352
|
+
const db = TestApp.database()
|
|
353
|
+
const created = await Webhooks.createSubscription(db, createInput())
|
|
354
|
+
const stamped = await Webhooks.recordSuccess(db, created, { now: fixedNow })
|
|
355
|
+
expect(stamped.lastDeliveryAt).toBe(fixedNow().toISOString())
|
|
356
|
+
|
|
357
|
+
// A success moments later has nothing to move: the counter is already zero
|
|
358
|
+
// and the stamp is inside its window, so the row is never rewritten.
|
|
359
|
+
const soon = new Date(fixedNow().getTime() + 1_000)
|
|
360
|
+
await Webhooks.recordSuccess(db, stamped, { now: () => soon })
|
|
361
|
+
const unmoved = await Webhooks.getSubscription(db, apiKeyOwner, created.id)
|
|
362
|
+
expect(unmoved?.lastDeliveryAt, 'the stored stamp must not move').toBe(fixedNow().toISOString())
|
|
363
|
+
|
|
364
|
+
// Past any plausible window; the exact bound is internal.
|
|
365
|
+
const later = new Date(fixedNow().getTime() + 3_600_000)
|
|
366
|
+
await Webhooks.recordSuccess(db, stamped, { now: () => later })
|
|
367
|
+
const moved = await Webhooks.getSubscription(db, apiKeyOwner, created.id)
|
|
368
|
+
expect(moved?.lastDeliveryAt).toBe(later.toISOString())
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
test('recordSuccess always resets a non-zero failure counter', async () => {
|
|
372
|
+
const db = TestApp.database()
|
|
373
|
+
const created = await Webhooks.createSubscription(db, createInput())
|
|
374
|
+
// Stamp first, so only the counter is out of date: the reset must not be
|
|
375
|
+
// suppressed by a fresh stamp.
|
|
376
|
+
const stamped = await Webhooks.recordSuccess(db, created, { now: fixedNow })
|
|
377
|
+
const failed = await Webhooks.recordFailure(db, stamped, { now: fixedNow })
|
|
378
|
+
expect(failed.failureCount).toBe(1)
|
|
379
|
+
|
|
380
|
+
await Webhooks.recordSuccess(db, failed, { now: fixedNow })
|
|
381
|
+
|
|
382
|
+
const read = await Webhooks.getSubscription(db, apiKeyOwner, created.id)
|
|
383
|
+
expect(read?.failureCount, 'a pending reset must never be skipped').toBe(0)
|
|
384
|
+
})
|
|
385
|
+
|
|
351
386
|
test('recordFailure auto-disables at the threshold and drops from the active index', async () => {
|
|
352
387
|
const db = TestApp.database()
|
|
353
388
|
let subscription = await Webhooks.createSubscription(db, createInput())
|
|
@@ -375,6 +410,37 @@ describe('recordSuccess / recordFailure', () => {
|
|
|
375
410
|
await Webhooks.listActiveForChain(db, { chainId: 4217, eventTypes: ['token:transfer'] }),
|
|
376
411
|
).toMatchInlineSnapshot(`[]`)
|
|
377
412
|
})
|
|
413
|
+
|
|
414
|
+
test('recordFailure leaves an already-disabled subscription alone at the cap', async () => {
|
|
415
|
+
const db = TestApp.database()
|
|
416
|
+
let subscription = await Webhooks.createSubscription(db, createInput())
|
|
417
|
+
subscription = await Webhooks.recordFailure(db, subscription, { maxFailures: 2, now: fixedNow })
|
|
418
|
+
subscription = await Webhooks.recordFailure(db, subscription, { maxFailures: 2, now: fixedNow })
|
|
419
|
+
expect(subscription.status).toBe('disabled')
|
|
420
|
+
|
|
421
|
+
// A straggler failing after the disable has nothing to move: the row is
|
|
422
|
+
// never rewritten.
|
|
423
|
+
const later = new Date(fixedNow().getTime() + 1_000)
|
|
424
|
+
await Webhooks.recordFailure(db, subscription, { maxFailures: 2, now: () => later })
|
|
425
|
+
|
|
426
|
+
const read = await Webhooks.getSubscription(db, apiKeyOwner, subscription.id)
|
|
427
|
+
expect(read?.failureCount, 'the stored counter must not move').toBe(2)
|
|
428
|
+
expect(read?.updatedAt, 'the stored stamp must not move').toBe(fixedNow().toISOString())
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
test('recordFailure re-disables a re-enabled subscription still at the cap', async () => {
|
|
432
|
+
const db = TestApp.database()
|
|
433
|
+
let subscription = await Webhooks.createSubscription(db, createInput())
|
|
434
|
+
subscription = await Webhooks.recordFailure(db, subscription, { maxFailures: 1, now: fixedNow })
|
|
435
|
+
expect(subscription.status).toBe('disabled')
|
|
436
|
+
|
|
437
|
+
await Webhooks.updateSubscription(db, apiKeyOwner, subscription.id, { status: 'active' })
|
|
438
|
+
|
|
439
|
+
// The counter still sits at the cap, so this failure must write again.
|
|
440
|
+
await Webhooks.recordFailure(db, subscription, { maxFailures: 1, now: fixedNow })
|
|
441
|
+
const read = await Webhooks.getSubscription(db, apiKeyOwner, subscription.id)
|
|
442
|
+
expect(read?.status).toBe('disabled')
|
|
443
|
+
})
|
|
378
444
|
})
|
|
379
445
|
|
|
380
446
|
describe('sign / verify', () => {
|
|
@@ -1015,7 +1081,7 @@ describe('ensureQueueEvents', () => {
|
|
|
1015
1081
|
expect(await Webhooks.getQueueEvent(db, reference)).toBeNull()
|
|
1016
1082
|
})
|
|
1017
1083
|
|
|
1018
|
-
test('
|
|
1084
|
+
test('stages a replay once its completion has expired', async () => {
|
|
1019
1085
|
vi.useFakeTimers({ toFake: ['Date'] })
|
|
1020
1086
|
vi.setSystemTime(fixedNow())
|
|
1021
1087
|
try {
|
|
@@ -1031,9 +1097,9 @@ describe('ensureQueueEvents', () => {
|
|
|
1031
1097
|
}),
|
|
1032
1098
|
).toBe(true)
|
|
1033
1099
|
|
|
1034
|
-
// Past the dedupe window an unpruned
|
|
1100
|
+
// Past the dedupe window an unpruned completion must not suppress a
|
|
1035
1101
|
// replay.
|
|
1036
|
-
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.
|
|
1102
|
+
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.dedupeRetentionMs + 1_000))
|
|
1037
1103
|
const replay = await Webhooks.ensureQueueEvents(db, [{ envelope, subscription }])
|
|
1038
1104
|
expect(replay.created).toBe(1)
|
|
1039
1105
|
await expect(Webhooks.claimQueueEvent(db, reference)).resolves.toMatchObject({
|
|
@@ -1044,7 +1110,7 @@ describe('ensureQueueEvents', () => {
|
|
|
1044
1110
|
}
|
|
1045
1111
|
})
|
|
1046
1112
|
|
|
1047
|
-
test('
|
|
1113
|
+
test('stamps a fresh observation on a post-expiry replay', async () => {
|
|
1048
1114
|
vi.useFakeTimers({ toFake: ['Date'] })
|
|
1049
1115
|
vi.setSystemTime(fixedNow())
|
|
1050
1116
|
try {
|
|
@@ -1057,7 +1123,7 @@ describe('ensureQueueEvents', () => {
|
|
|
1057
1123
|
status: 'succeeded',
|
|
1058
1124
|
})
|
|
1059
1125
|
|
|
1060
|
-
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.
|
|
1126
|
+
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.dedupeRetentionMs + 1_000))
|
|
1061
1127
|
const observedAt = Date.now() - 250
|
|
1062
1128
|
await Webhooks.ensureQueueEvents(db, [{ envelope, subscription }], { observedAt })
|
|
1063
1129
|
|
|
@@ -1070,6 +1136,38 @@ describe('ensureQueueEvents', () => {
|
|
|
1070
1136
|
vi.useRealTimers()
|
|
1071
1137
|
}
|
|
1072
1138
|
})
|
|
1139
|
+
|
|
1140
|
+
test('suppresses only the completed obligation when staging a mixed batch', async () => {
|
|
1141
|
+
const db = TestApp.database()
|
|
1142
|
+
const done = await stagedObligation(db, 4)
|
|
1143
|
+
const claim = await Webhooks.claimQueueEvent(db, done.reference)
|
|
1144
|
+
if (claim.type !== 'claimed') throw new Error(`Unexpected claim: ${claim.type}`)
|
|
1145
|
+
await Webhooks.completeQueueEvent(db, done.reference, {
|
|
1146
|
+
claimedAt: claim.record.attemptingAt!,
|
|
1147
|
+
status: 'failed',
|
|
1148
|
+
})
|
|
1149
|
+
|
|
1150
|
+
const fresh = Webhooks.buildEnvelope({
|
|
1151
|
+
blockNumber: 124,
|
|
1152
|
+
createdAt: new Date(),
|
|
1153
|
+
data: { amount: '1' },
|
|
1154
|
+
logIndex: 5,
|
|
1155
|
+
subscription: done.subscription,
|
|
1156
|
+
})
|
|
1157
|
+
const batch = await Webhooks.ensureQueueEvents(db, [
|
|
1158
|
+
{ envelope: done.envelope, subscription: done.subscription },
|
|
1159
|
+
{ envelope: fresh, subscription: done.subscription },
|
|
1160
|
+
])
|
|
1161
|
+
|
|
1162
|
+
expect(batch.created).toBe(1)
|
|
1163
|
+
await expect(Webhooks.claimQueueEvent(db, done.reference)).resolves.toEqual({
|
|
1164
|
+
status: 'failed',
|
|
1165
|
+
type: 'terminal',
|
|
1166
|
+
})
|
|
1167
|
+
await expect(Webhooks.claimQueueEvent(db, batch.references[1]!)).resolves.toMatchObject({
|
|
1168
|
+
type: 'claimed',
|
|
1169
|
+
})
|
|
1170
|
+
})
|
|
1073
1171
|
})
|
|
1074
1172
|
|
|
1075
1173
|
describe('claimQueueEvent', () => {
|
|
@@ -1082,6 +1180,22 @@ describe('claimQueueEvent', () => {
|
|
|
1082
1180
|
vi.useRealTimers()
|
|
1083
1181
|
})
|
|
1084
1182
|
|
|
1183
|
+
test('reads an expired completion as missing', async () => {
|
|
1184
|
+
const db = TestApp.database()
|
|
1185
|
+
const { reference } = await stagedObligation(db)
|
|
1186
|
+
const claim = await Webhooks.claimQueueEvent(db, reference)
|
|
1187
|
+
if (claim.type !== 'claimed') throw new Error(`Unexpected claim: ${claim.type}`)
|
|
1188
|
+
await Webhooks.completeQueueEvent(db, reference, {
|
|
1189
|
+
claimedAt: claim.record.attemptingAt!,
|
|
1190
|
+
status: 'succeeded',
|
|
1191
|
+
})
|
|
1192
|
+
|
|
1193
|
+
// Matches a pruned terminal row: past the dedupe window the completion is
|
|
1194
|
+
// invisible everywhere, not only to staging.
|
|
1195
|
+
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.dedupeRetentionMs + 1_000))
|
|
1196
|
+
await expect(Webhooks.claimQueueEvent(db, reference)).resolves.toEqual({ type: 'missing' })
|
|
1197
|
+
})
|
|
1198
|
+
|
|
1085
1199
|
test('claims a due obligation once and reclaims after staleness', async () => {
|
|
1086
1200
|
const db = TestApp.database()
|
|
1087
1201
|
const { reference } = await stagedObligation(db)
|
|
@@ -1146,17 +1260,17 @@ describe('claimQueueEvent', () => {
|
|
|
1146
1260
|
|
|
1147
1261
|
// Pending obligations never expire; only terminal transitions stamp a
|
|
1148
1262
|
// dedupe deadline.
|
|
1149
|
-
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.
|
|
1263
|
+
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.dedupeRetentionMs * 3))
|
|
1150
1264
|
await expect(Webhooks.claimQueueEvent(db, reference)).resolves.toMatchObject({
|
|
1151
1265
|
type: 'claimed',
|
|
1152
1266
|
})
|
|
1153
1267
|
})
|
|
1154
1268
|
|
|
1155
|
-
test('
|
|
1269
|
+
test('leaves pending obligations claimable across a prune', async () => {
|
|
1156
1270
|
const db = TestApp.database()
|
|
1157
1271
|
const { reference } = await stagedObligation(db)
|
|
1158
1272
|
|
|
1159
|
-
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.
|
|
1273
|
+
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.dedupeRetentionMs + 1_000))
|
|
1160
1274
|
await Webhooks.pruneExpired(db)
|
|
1161
1275
|
const claim = await Webhooks.claimQueueEvent(db, reference)
|
|
1162
1276
|
expect(claim.type).toBe('claimed')
|
package/src/internal/Webhooks.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Hash, Hex } from 'ox'
|
|
|
3
3
|
import type * as app_Webhooks from '../apps/data/routes/webhooks.js'
|
|
4
4
|
import type * as Db from '../db/Db.js'
|
|
5
5
|
import * as WebhookDeliveries from '../db/tables/webhookDeliveries.js'
|
|
6
|
+
import * as WebhookQueueCompletions from '../db/tables/webhookQueueCompletions.js'
|
|
6
7
|
import * as WebhookQueueEvents from '../db/tables/webhookQueueEvents.js'
|
|
7
8
|
import * as WebhookSubscriptions from '../db/tables/webhookSubscriptions.js'
|
|
8
9
|
import type * as Metrics from '../Metrics.js'
|
|
@@ -95,7 +96,8 @@ export type Subscription = {
|
|
|
95
96
|
filters: Record<string, unknown>
|
|
96
97
|
/** Subscription id (`wh_…`). */
|
|
97
98
|
id: string
|
|
98
|
-
/** ISO timestamp of the last successful delivery, if any.
|
|
99
|
+
/** ISO timestamp of the last successful delivery, if any. Display only:
|
|
100
|
+
* refreshed at most once per precision window. */
|
|
99
101
|
lastDeliveryAt?: string | undefined
|
|
100
102
|
/** Authenticated owner. */
|
|
101
103
|
owner: Owner
|
|
@@ -105,7 +107,8 @@ export type Subscription = {
|
|
|
105
107
|
secret: string
|
|
106
108
|
/** Lifecycle status. */
|
|
107
109
|
status: Status
|
|
108
|
-
/** ISO timestamp of the last mutation.
|
|
110
|
+
/** ISO timestamp of the last mutation. May lag repeat deliveries; see
|
|
111
|
+
* `lastDeliveryAt`. */
|
|
109
112
|
updatedAt: string
|
|
110
113
|
}
|
|
111
114
|
|
|
@@ -406,11 +409,18 @@ export async function getCursor(db: Db.Db, subscriptionId: string): Promise<stri
|
|
|
406
409
|
* deliveries past retention. Reads already filter expiry at query time; this
|
|
407
410
|
* is the janitor reclaiming space, run best-effort on a schedule.
|
|
408
411
|
*/
|
|
409
|
-
export async function pruneExpired(
|
|
412
|
+
export async function pruneExpired(
|
|
413
|
+
db: Db.Db,
|
|
414
|
+
options: pruneExpired.Options = {},
|
|
415
|
+
): Promise<pruneExpired.Result> {
|
|
410
416
|
const iso = (options.now ?? (() => new Date()))().toISOString()
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
417
|
+
// Sequential on purpose: the subscription delete cascades into the other
|
|
418
|
+
// tables, so overlapping it with their prunes risks deadlocks.
|
|
419
|
+
return {
|
|
420
|
+
completions: await WebhookQueueCompletions.pruneExpired(db, iso),
|
|
421
|
+
deliveries: await WebhookDeliveries.pruneExpired(db, iso),
|
|
422
|
+
subscriptions: await WebhookSubscriptions.pruneExpired(db, iso),
|
|
423
|
+
}
|
|
414
424
|
}
|
|
415
425
|
|
|
416
426
|
export declare namespace pruneExpired {
|
|
@@ -419,6 +429,16 @@ export declare namespace pruneExpired {
|
|
|
419
429
|
/** Clock used for expiry evaluation; injectable for tests. */
|
|
420
430
|
now?: (() => Date) | undefined
|
|
421
431
|
}
|
|
432
|
+
|
|
433
|
+
/** Rows deleted per table; a count pinned at the batch bound means expiry is outpacing pruning. */
|
|
434
|
+
type Result = {
|
|
435
|
+
/** Deleted `webhook_queue_completions` rows. */
|
|
436
|
+
completions: number
|
|
437
|
+
/** Deleted `webhook_deliveries` rows. */
|
|
438
|
+
deliveries: number
|
|
439
|
+
/** Deleted `webhook_subscriptions` rows. */
|
|
440
|
+
subscriptions: number
|
|
441
|
+
}
|
|
422
442
|
}
|
|
423
443
|
|
|
424
444
|
/**
|
|
@@ -427,6 +447,13 @@ export declare namespace pruneExpired {
|
|
|
427
447
|
*/
|
|
428
448
|
export const deliveryRetentionMs = 7 * 24 * 60 * 60 * 1_000
|
|
429
449
|
|
|
450
|
+
/**
|
|
451
|
+
* Completions dedupe window: replays of a completed obligation are suppressed
|
|
452
|
+
* this long. Deliberately its own literal so per-deployment log-retention
|
|
453
|
+
* tuning cannot move it.
|
|
454
|
+
*/
|
|
455
|
+
export const dedupeRetentionMs = 7 * 24 * 60 * 60 * 1_000
|
|
456
|
+
|
|
430
457
|
/** A compact Cloudflare Queue message referencing a durable staged envelope. */
|
|
431
458
|
export type QueueReference = {
|
|
432
459
|
/** Idempotent event id of the staged envelope. */
|
|
@@ -441,8 +468,6 @@ export type QueueEvent = QueueReference & {
|
|
|
441
468
|
createdAt: string
|
|
442
469
|
/** Full envelope loaded by the Queue consumer. */
|
|
443
470
|
envelope: Envelope
|
|
444
|
-
/** ISO timestamp after which an unconsumed envelope may be pruned. */
|
|
445
|
-
expiresAt: string
|
|
446
471
|
}
|
|
447
472
|
|
|
448
473
|
/** A claim newer than this counts as an in-flight attempt; above the 10s destination timeout default. */
|
|
@@ -467,7 +492,6 @@ export async function ensureQueueEvents(
|
|
|
467
492
|
options: ensureQueueEvents.Options = {},
|
|
468
493
|
): Promise<ensureQueueEvents.Result> {
|
|
469
494
|
const now = (options.now ?? (() => new Date()))()
|
|
470
|
-
const expiresAt = new Date(now.getTime() + (options.ttl ?? deliveryRetentionMs)).toISOString()
|
|
471
495
|
// Carried so the delivery worker can measure observation-to-attempt without
|
|
472
496
|
// re-reading the block; replays have no observation and stay null.
|
|
473
497
|
const observedAt =
|
|
@@ -476,9 +500,6 @@ export async function ensureQueueEvents(
|
|
|
476
500
|
createdAt: now.toISOString(),
|
|
477
501
|
envelope,
|
|
478
502
|
eventId: envelope.id,
|
|
479
|
-
// Inert while pending; the meaningful dedupe deadline is stamped at
|
|
480
|
-
// terminal transitions.
|
|
481
|
-
expiresAt,
|
|
482
503
|
// Due immediately; the value orders the sweep so it is never null here.
|
|
483
504
|
nextAttemptAt: now.toISOString(),
|
|
484
505
|
observedAt,
|
|
@@ -499,14 +520,12 @@ export declare namespace ensureQueueEvents {
|
|
|
499
520
|
subscription: Subscription
|
|
500
521
|
}
|
|
501
522
|
|
|
502
|
-
/**
|
|
523
|
+
/** Staging options. */
|
|
503
524
|
type Options = {
|
|
504
525
|
/** Clock used for staging timestamps. */
|
|
505
526
|
now?: (() => Date) | undefined
|
|
506
527
|
/** Epoch ms the head owing these events was observed; absent on replays. */
|
|
507
528
|
observedAt?: number | undefined
|
|
508
|
-
/** Staged-envelope TTL in ms; defaults to {@link deliveryRetentionMs}. */
|
|
509
|
-
ttl?: number | undefined
|
|
510
529
|
}
|
|
511
530
|
|
|
512
531
|
/** Created-row count plus references for every requested obligation. */
|
|
@@ -542,7 +561,7 @@ export function completeQueueEvent(
|
|
|
542
561
|
): Promise<boolean> {
|
|
543
562
|
return WebhookQueueEvents.complete(db, reference, {
|
|
544
563
|
...options,
|
|
545
|
-
expiresAt: new Date(Date.now() +
|
|
564
|
+
expiresAt: new Date(Date.now() + dedupeRetentionMs).toISOString(),
|
|
546
565
|
})
|
|
547
566
|
}
|
|
548
567
|
|
|
@@ -552,7 +571,7 @@ export declare namespace completeQueueEvent {
|
|
|
552
571
|
/** Claim marker from the claimed record's `attemptingAt`. */
|
|
553
572
|
claimedAt: string
|
|
554
573
|
/** Terminal obligation outcome. */
|
|
555
|
-
status:
|
|
574
|
+
status: WebhookQueueCompletions.TerminalStatus
|
|
556
575
|
}
|
|
557
576
|
}
|
|
558
577
|
|
|
@@ -766,10 +785,17 @@ export async function getDelivery(
|
|
|
766
785
|
*/
|
|
767
786
|
export const maxFailures = 10
|
|
768
787
|
|
|
788
|
+
/**
|
|
789
|
+
* How stale `lastDeliveryAt` may get before a success rewrites it. Read only
|
|
790
|
+
* for display, so exactness is traded for not rewriting the row per delivery.
|
|
791
|
+
*/
|
|
792
|
+
const lastDeliveryPrecisionMs = 60_000
|
|
793
|
+
|
|
769
794
|
/**
|
|
770
795
|
* Records a successful delivery: resets the consecutive-failure counter and
|
|
771
|
-
*
|
|
772
|
-
*
|
|
796
|
+
* refreshes `lastDeliveryAt` at most once per precision window. Returns the
|
|
797
|
+
* written row, or the caller's snapshot when the write was skipped or the row
|
|
798
|
+
* deleted.
|
|
773
799
|
*/
|
|
774
800
|
export async function recordSuccess(
|
|
775
801
|
db: Db.Db,
|
|
@@ -777,11 +803,13 @@ export async function recordSuccess(
|
|
|
777
803
|
options: recordSuccess.Options = {},
|
|
778
804
|
): Promise<Subscription> {
|
|
779
805
|
const now = (options.now ?? (() => new Date()))()
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
806
|
+
const next = await WebhookSubscriptions.recordSuccess(db, subscription.id, {
|
|
807
|
+
now: now.toISOString(),
|
|
808
|
+
staleBefore: new Date(now.getTime() - lastDeliveryPrecisionMs).toISOString(),
|
|
809
|
+
})
|
|
810
|
+
// No row means the write was skipped or the row was deleted; neither
|
|
811
|
+
// resurrects, so keep the caller's timestamps.
|
|
812
|
+
return next ?? { ...subscription, failureCount: 0 }
|
|
785
813
|
}
|
|
786
814
|
|
|
787
815
|
export declare namespace recordSuccess {
|
|
@@ -796,7 +824,9 @@ export declare namespace recordSuccess {
|
|
|
796
824
|
* Records a failed delivery: atomically increments the consecutive-failure
|
|
797
825
|
* counter and, once it reaches `maxFailures`, sets `status: 'disabled'` so the
|
|
798
826
|
* subscription drops out of the partial active index (surfaced via the API).
|
|
799
|
-
*
|
|
827
|
+
* Once disabled at the cap the row is left alone, so an outage's in-flight
|
|
828
|
+
* failures stop contending on its lock. Returns the written row, or the
|
|
829
|
+
* caller's snapshot advanced optimistically.
|
|
800
830
|
*/
|
|
801
831
|
export async function recordFailure(
|
|
802
832
|
db: Db.Db,
|
|
@@ -808,14 +838,14 @@ export async function recordFailure(
|
|
|
808
838
|
const max = options.maxFailures ?? maxFailures
|
|
809
839
|
const next = await WebhookSubscriptions.recordFailure(db, subscription.id, max, iso)
|
|
810
840
|
if (next) return next
|
|
811
|
-
//
|
|
812
|
-
//
|
|
813
|
-
|
|
841
|
+
// No row means skipped (already disabled at the cap) or deleted; neither
|
|
842
|
+
// resurrects, so keep the caller's timestamps. Capped so chained skips
|
|
843
|
+
// cannot grow the counter past what the store would hold.
|
|
844
|
+
const failureCount = Math.min(subscription.failureCount + 1, max)
|
|
814
845
|
return {
|
|
815
846
|
...subscription,
|
|
816
847
|
failureCount,
|
|
817
848
|
...(failureCount >= max ? { status: 'disabled' as const } : {}),
|
|
818
|
-
updatedAt: iso,
|
|
819
849
|
}
|
|
820
850
|
}
|
|
821
851
|
|
|
@@ -132,7 +132,47 @@ describe('validateEvmCalls', () => {
|
|
|
132
132
|
sourceTokenAddress: sourceToken,
|
|
133
133
|
}),
|
|
134
134
|
).toThrowErrorMatchingInlineSnapshot(
|
|
135
|
-
`[Funding.Action.InvalidActionError:
|
|
135
|
+
`[Funding.Action.InvalidActionError: Native value must match a validated provider fee.]`,
|
|
136
136
|
)
|
|
137
137
|
})
|
|
138
|
+
|
|
139
|
+
test('accepts an exact provider-validated native value', () => {
|
|
140
|
+
Action.validateEvmCalls({
|
|
141
|
+
action: {
|
|
142
|
+
calls: [approveCall(1_000_000n), { ...depositCall, value: '0x10' }],
|
|
143
|
+
type: 'evm:calls',
|
|
144
|
+
},
|
|
145
|
+
nativeValues: [{ to: spender, value: '0x10' }],
|
|
146
|
+
sourceAmount: '1000000',
|
|
147
|
+
sourceTokenAddress: sourceToken,
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
test('rejects malformed native value allowances', () => {
|
|
152
|
+
expect(() =>
|
|
153
|
+
Action.validateEvmCalls({
|
|
154
|
+
action: { calls: [approveCall(1_000_000n), depositCall], type: 'evm:calls' },
|
|
155
|
+
nativeValues: [{ to: spender, value: '10' }],
|
|
156
|
+
sourceAmount: '1000000',
|
|
157
|
+
sourceTokenAddress: sourceToken,
|
|
158
|
+
}),
|
|
159
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
160
|
+
`[Funding.Action.InvalidActionError: Malformed native value allowance.]`,
|
|
161
|
+
)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
test('rejects native values outside the exact provider allowance', () => {
|
|
165
|
+
for (const call of [
|
|
166
|
+
{ ...depositCall, to: `0x${'99'.repeat(20)}`, value: '0x10' },
|
|
167
|
+
{ ...depositCall, value: '0x11' },
|
|
168
|
+
])
|
|
169
|
+
expect(() =>
|
|
170
|
+
Action.validateEvmCalls({
|
|
171
|
+
action: { calls: [approveCall(1_000_000n), call], type: 'evm:calls' },
|
|
172
|
+
nativeValues: [{ to: spender, value: '0x10' }],
|
|
173
|
+
sourceAmount: '1000000',
|
|
174
|
+
sourceTokenAddress: sourceToken,
|
|
175
|
+
}),
|
|
176
|
+
).toThrow(Action.InvalidActionError)
|
|
177
|
+
})
|
|
138
178
|
})
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { AbiFunction, Address, type Hex, Solidity } from 'ox'
|
|
2
2
|
import * as z from 'zod/mini'
|
|
3
3
|
|
|
4
|
+
import * as OpenApi from '../OpenApi.js'
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Executable source-chain actions returned by funding transfer creation. A
|
|
6
8
|
* strict discriminated union: transfer reads never include an action.
|
|
@@ -12,49 +14,58 @@ const approveSelector = AbiFunction.getSelector(approve)
|
|
|
12
14
|
/** Zod schemas owned by funding actions. */
|
|
13
15
|
export namespace schema {
|
|
14
16
|
/** One unsigned EVM call, in the exchange quote API's call shape. */
|
|
15
|
-
export const Call =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
17
|
+
export const Call = OpenApi.component(
|
|
18
|
+
z
|
|
19
|
+
.object({
|
|
20
|
+
data: z
|
|
21
|
+
.string()
|
|
22
|
+
.check(
|
|
23
|
+
z.regex(/^0x(?:[0-9a-fA-F]{2})*$/),
|
|
24
|
+
z.describe('ABI-encoded calldata.'),
|
|
25
|
+
z.meta({ examples: ['0x095ea7b3'] }),
|
|
26
|
+
),
|
|
27
|
+
to: z
|
|
28
|
+
.string()
|
|
29
|
+
.check(
|
|
30
|
+
z.regex(/^0x[0-9a-fA-F]{40}$/),
|
|
31
|
+
z.describe('Contract the call targets.'),
|
|
32
|
+
z.meta({ examples: [`0x${'44'.repeat(20)}`] }),
|
|
33
|
+
),
|
|
34
|
+
value: z
|
|
35
|
+
.string()
|
|
36
|
+
.check(
|
|
37
|
+
z.regex(/^0x[0-9a-fA-F]+$/),
|
|
38
|
+
z.describe('Native value to send, as hex.'),
|
|
39
|
+
z.meta({ examples: ['0x0'] }),
|
|
40
|
+
),
|
|
41
|
+
})
|
|
42
|
+
.check(z.describe('One unsigned EVM call.')),
|
|
43
|
+
'FundingActionCall',
|
|
44
|
+
)
|
|
40
45
|
|
|
41
46
|
/** Ordered unsigned EVM calls the caller signs and submits on the source chain. */
|
|
42
|
-
export const EvmCalls =
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
export const EvmCalls = OpenApi.component(
|
|
48
|
+
z
|
|
49
|
+
.object({
|
|
50
|
+
calls: z
|
|
51
|
+
.array(Call)
|
|
52
|
+
.check(
|
|
53
|
+
z.minLength(1),
|
|
54
|
+
z.describe('Calls in submission order; they may require separate transactions.'),
|
|
55
|
+
),
|
|
56
|
+
type: z.literal('evm:calls').check(z.describe('Action kind.')),
|
|
57
|
+
})
|
|
58
|
+
.check(z.describe('Ordered unsigned EVM calls to sign and submit on the source chain.')),
|
|
59
|
+
'FundingEvmCallsAction',
|
|
60
|
+
)
|
|
53
61
|
|
|
54
62
|
/** Executable source-chain action attached to a creation response. */
|
|
55
|
-
export const Action =
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
export const Action = OpenApi.component(
|
|
64
|
+
z
|
|
65
|
+
.discriminatedUnion('type', [EvmCalls])
|
|
66
|
+
.check(z.describe('Executable source-chain action. Never returned by transfer reads.')),
|
|
67
|
+
'FundingAction',
|
|
68
|
+
)
|
|
58
69
|
}
|
|
59
70
|
|
|
60
71
|
/** One unsigned EVM call (see {@link schema.Call}). */
|
|
@@ -66,6 +77,14 @@ export type EvmCalls = z.output<typeof schema.EvmCalls>
|
|
|
66
77
|
/** Executable source-chain action (see {@link schema.Action}). */
|
|
67
78
|
export type Action = z.output<typeof schema.Action>
|
|
68
79
|
|
|
80
|
+
/** One exact native-value allowance established by provider-specific validation. */
|
|
81
|
+
export type NativeValue = {
|
|
82
|
+
/** Contract that may receive the native value. */
|
|
83
|
+
to: string
|
|
84
|
+
/** Exact native value allowed, as a JSON-RPC quantity. */
|
|
85
|
+
value: string
|
|
86
|
+
}
|
|
87
|
+
|
|
69
88
|
/**
|
|
70
89
|
* Validates prepared EVM calls before persistence. Token calls, approval
|
|
71
90
|
* targets, spenders, amounts, selectors, and native value must match the route.
|
|
@@ -73,9 +92,16 @@ export type Action = z.output<typeof schema.Action>
|
|
|
73
92
|
export function validateEvmCalls(options: validateEvmCalls.Options): void {
|
|
74
93
|
const action = schema.EvmCalls.safeParse(options.action)
|
|
75
94
|
if (!action.success) throw new InvalidActionError('Malformed EVM calls.')
|
|
95
|
+
const nativeValues = new Set<string>()
|
|
96
|
+
for (const { to, value } of options.nativeValues ?? []) {
|
|
97
|
+
if (!Address.validate(to, { strict: false }) || !/^0x[0-9a-fA-F]+$/.test(value))
|
|
98
|
+
throw new InvalidActionError('Malformed native value allowance.')
|
|
99
|
+
nativeValues.add(`${to.toLowerCase()}:${BigInt(value).toString()}`)
|
|
100
|
+
}
|
|
76
101
|
for (const [index, call] of action.data.calls.entries()) {
|
|
77
|
-
|
|
78
|
-
|
|
102
|
+
const callValue = BigInt(call.value)
|
|
103
|
+
if (callValue > 0n && !nativeValues.has(`${call.to.toLowerCase()}:${callValue.toString()}`))
|
|
104
|
+
throw new InvalidActionError('Native value must match a validated provider fee.')
|
|
79
105
|
const targetsSourceToken = Address.isEqual(
|
|
80
106
|
call.to as Address.Address,
|
|
81
107
|
options.sourceTokenAddress as Address.Address,
|
|
@@ -104,6 +130,8 @@ export declare namespace validateEvmCalls {
|
|
|
104
130
|
type Options = {
|
|
105
131
|
/** The prepared action to validate. */
|
|
106
132
|
action: EvmCalls
|
|
133
|
+
/** Exact native values established by provider-specific validation. */
|
|
134
|
+
nativeValues?: readonly NativeValue[] | undefined
|
|
107
135
|
/** Source amount in base units. */
|
|
108
136
|
sourceAmount: string
|
|
109
137
|
/** Source token contract address. */
|
|
@@ -309,3 +309,37 @@ describe('route capabilities', () => {
|
|
|
309
309
|
).toThrow()
|
|
310
310
|
})
|
|
311
311
|
})
|
|
312
|
+
|
|
313
|
+
describe('route configuration', () => {
|
|
314
|
+
const configuration = { poolAddress: `0x${'11'.repeat(20)}` }
|
|
315
|
+
|
|
316
|
+
test('carries configuration into hydrated provider routes', () => {
|
|
317
|
+
const snapshot = Catalog.compile(rows({ routes: [{ ...data.routes[0]!, configuration }] }))
|
|
318
|
+
expect(snapshot.routes[0]?.configuration).toStrictEqual(configuration)
|
|
319
|
+
expect(snapshot.routesByProvider.get('relay')?.[0]?.configuration).toStrictEqual(configuration)
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
test('omits configuration when it is absent or null', () => {
|
|
323
|
+
const absent = Catalog.compile(rows())
|
|
324
|
+
const nulled = Catalog.compile(rows({ routes: [{ ...data.routes[0]!, configuration: null }] }))
|
|
325
|
+
expect('configuration' in absent.routes[0]!).toBe(false)
|
|
326
|
+
expect('configuration' in nulled.routes[0]!).toBe(false)
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
test('publishes configuration through the write boundary', async () => {
|
|
330
|
+
const db = TestApp.database()
|
|
331
|
+
const snapshot = await Catalog.publish(db, {
|
|
332
|
+
...data,
|
|
333
|
+
routes: [{ ...data.routes[0]!, configuration }],
|
|
334
|
+
})
|
|
335
|
+
expect(snapshot.routes[0]?.configuration).toStrictEqual(configuration)
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
test('rejects non-object configuration', () => {
|
|
339
|
+
expect(() =>
|
|
340
|
+
Catalog.compile(
|
|
341
|
+
rows({ routes: [{ ...data.routes[0]!, configuration: 'invalid' as never }] }),
|
|
342
|
+
),
|
|
343
|
+
).toThrow()
|
|
344
|
+
})
|
|
345
|
+
})
|