tapimo 0.15.1 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +137 -0
- package/dist/ApiKey.d.ts +1 -0
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKey.js +4 -2
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +37 -11
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +163 -10
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +13085 -11974
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +46 -15
- package/dist/App.js.map +1 -1
- package/dist/Scope.d.ts +1 -1
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +1 -1
- package/dist/Scope.js.map +1 -1
- package/dist/admin/App.d.ts +893 -191
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/App.js +5 -1
- package/dist/admin/App.js.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +70 -70
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +8 -3
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/earn-vaults.d.ts +66 -27
- package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.js +36 -7
- package/dist/admin/apps/earn-vaults.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +25 -25
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/organizations.js +5 -4
- package/dist/admin/apps/organizations.js.map +1 -1
- package/dist/admin/apps/route-policies.d.ts +632 -0
- package/dist/admin/apps/route-policies.d.ts.map +1 -0
- package/dist/admin/apps/route-policies.js +257 -0
- package/dist/admin/apps/route-policies.js.map +1 -0
- package/dist/admin/apps/routes-support.js +39 -3
- package/dist/admin/apps/routes-support.js.map +1 -1
- package/dist/admin/apps/routes.d.ts +529 -76
- package/dist/admin/apps/routes.d.ts.map +1 -1
- package/dist/admin/apps/routes.js +82 -1
- package/dist/admin/apps/routes.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +157 -157
- package/dist/admin/index.d.ts +0 -1
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +0 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.d.ts.map +1 -1
- package/dist/admin/ui.generated.js +205 -201
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts.map +1 -1
- package/dist/analytics/Analytics.js +65 -1
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/analytics/tables/requestEvents.d.ts +1 -1
- package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
- package/dist/analytics/tables/requestEvents.js +58 -59
- package/dist/analytics/tables/requestEvents.js.map +1 -1
- package/dist/apps/data/App.d.ts +2709 -2670
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +357 -357
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +50 -0
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +248 -248
- package/dist/apps/data/routes/balances.d.ts +22 -22
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +5 -7
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +125 -125
- package/dist/apps/data/routes/coingecko.d.ts +54 -54
- package/dist/apps/data/routes/earn.d.ts +103 -12
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +456 -220
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +267 -97
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts +109 -127
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +83 -54
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +43 -43
- package/dist/apps/data/routes/receipts.d.ts +235 -235
- package/dist/apps/data/routes/rpc.d.ts +30 -30
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +11 -6
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +2 -0
- package/dist/apps/data/routes/transactions.d.ts.map +1 -1
- package/dist/apps/data/routes/transactions.js +30 -22
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +77 -77
- package/dist/apps/data/routes/valuation.d.ts +32 -32
- package/dist/apps/data/routes/verified-tokens.d.ts +86 -86
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +1 -1
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.d.ts +31 -31
- package/dist/apps/management/App.d.ts +2909 -2064
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/App.js +2 -0
- package/dist/apps/management/App.js.map +1 -1
- package/dist/apps/management/Billing.d.ts +2 -4
- package/dist/apps/management/Billing.d.ts.map +1 -1
- package/dist/apps/management/Billing.js +65 -26
- package/dist/apps/management/Billing.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +257 -245
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +70 -11
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +84 -84
- package/dist/apps/management/routes/cli.d.ts +912 -0
- package/dist/apps/management/routes/cli.d.ts.map +1 -0
- package/dist/apps/management/routes/cli.js +416 -0
- package/dist/apps/management/routes/cli.js.map +1 -0
- package/dist/apps/management/routes/faucet.d.ts +8 -8
- package/dist/apps/management/routes/invitations.d.ts +138 -138
- package/dist/apps/management/routes/invite-links.d.ts +172 -172
- package/dist/apps/management/routes/members.d.ts +16 -16
- package/dist/apps/management/routes/orgs.d.ts +104 -104
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +8 -2
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts +125 -125
- package/dist/apps/management/routes/usage.d.ts +8 -8
- package/dist/apps/management/routes/verified-token-requests.d.ts +86 -86
- package/dist/apps/management/routes/webhooks.d.ts +485 -485
- package/dist/apps/mpp/App.d.ts +44 -44
- package/dist/apps/mpp/internal/Policy.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Policy.js +4 -2
- package/dist/apps/mpp/internal/Policy.js.map +1 -1
- package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Sponsorship.js +6 -4
- package/dist/apps/mpp/internal/Sponsorship.js.map +1 -1
- package/dist/apps/relay/App.d.ts +8 -0
- package/dist/apps/relay/App.d.ts.map +1 -1
- package/dist/apps/relay/App.js +16 -4
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/apps/relay/Sponsorships.d.ts +4 -6
- package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
- package/dist/apps/relay/Sponsorships.js +71 -60
- package/dist/apps/relay/Sponsorships.js.map +1 -1
- package/dist/apps/routes/Access.d.ts +53 -0
- package/dist/apps/routes/Access.d.ts.map +1 -1
- package/dist/apps/routes/Access.js +121 -0
- package/dist/apps/routes/Access.js.map +1 -1
- package/dist/apps/routes/App.d.ts +397 -158
- package/dist/apps/routes/App.d.ts.map +1 -1
- package/dist/apps/routes/App.js.map +1 -1
- package/dist/apps/routes/routes/chains.d.ts +25 -25
- package/dist/apps/routes/routes/deposit-addresses.d.ts +169 -61
- package/dist/apps/routes/routes/deposit-addresses.d.ts.map +1 -1
- package/dist/apps/routes/routes/deposit-addresses.js +217 -32
- package/dist/apps/routes/routes/deposit-addresses.js.map +1 -1
- package/dist/apps/routes/routes/quotes.d.ts +171 -81
- package/dist/apps/routes/routes/quotes.d.ts.map +1 -1
- package/dist/apps/routes/routes/quotes.js +101 -27
- package/dist/apps/routes/routes/quotes.js.map +1 -1
- package/dist/apps/routes/routes/transfers.d.ts +532 -480
- package/dist/apps/routes/routes/transfers.d.ts.map +1 -1
- package/dist/apps/routes/routes/transfers.js +182 -37
- package/dist/apps/routes/routes/transfers.js.map +1 -1
- package/dist/cli/ApiKeys.d.ts +120 -0
- package/dist/cli/ApiKeys.d.ts.map +1 -0
- package/dist/cli/ApiKeys.js +138 -0
- package/dist/cli/ApiKeys.js.map +1 -0
- package/dist/cli/Cli.d.ts +92 -34
- package/dist/cli/Cli.d.ts.map +1 -1
- package/dist/cli/Cli.js +188 -11
- package/dist/cli/Cli.js.map +1 -1
- package/dist/cli/Credentials.d.ts +95 -0
- package/dist/cli/Credentials.d.ts.map +1 -0
- package/dist/cli/Credentials.js +110 -0
- package/dist/cli/Credentials.js.map +1 -0
- package/dist/cli/Spec.d.ts +59 -0
- package/dist/cli/Spec.d.ts.map +1 -0
- package/dist/cli/Spec.js +77 -0
- package/dist/cli/Spec.js.map +1 -0
- package/dist/cli/bin.js +5 -1
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/admin.d.ts +5 -5
- package/dist/cli/commands/admin.d.ts.map +1 -1
- package/dist/cli/commands/auth.d.ts +73 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +204 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/keys.d.ts +17 -0
- package/dist/cli/commands/keys.d.ts.map +1 -0
- package/dist/cli/commands/keys.js +109 -0
- package/dist/cli/commands/keys.js.map +1 -0
- package/dist/cloudflare.d.ts +27 -2
- package/dist/cloudflare.d.ts.map +1 -1
- package/dist/cloudflare.js +144 -17
- package/dist/cloudflare.js.map +1 -1
- package/dist/db/Db.d.ts +23 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +413 -1
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +44 -1
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +56 -3
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/apiKeyAdmissions.d.ts +28 -2
- package/dist/db/tables/apiKeyAdmissions.d.ts.map +1 -1
- package/dist/db/tables/apiKeyAdmissions.js +65 -9
- package/dist/db/tables/apiKeyAdmissions.js.map +1 -1
- package/dist/db/tables/apiKeyOwnerTombstones.d.ts +2 -0
- package/dist/db/tables/apiKeyOwnerTombstones.d.ts.map +1 -1
- package/dist/db/tables/apiKeyOwnerTombstones.js +2 -2
- package/dist/db/tables/apiKeyOwnerTombstones.js.map +1 -1
- package/dist/db/tables/authAccounts.d.ts.map +1 -1
- package/dist/db/tables/authAccounts.js +1 -0
- package/dist/db/tables/authAccounts.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 +12 -1
- package/dist/db/tables/earnVaults.js.map +1 -1
- package/dist/db/tables/organizations.d.ts +4 -2
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +8 -34
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/requestUsage.d.ts +5 -1
- package/dist/db/tables/requestUsage.d.ts.map +1 -1
- package/dist/db/tables/requestUsage.js +10 -2
- package/dist/db/tables/requestUsage.js.map +1 -1
- package/dist/db/tables/rewardEligibilityAssociations.d.ts +13 -0
- package/dist/db/tables/rewardEligibilityAssociations.d.ts.map +1 -1
- package/dist/db/tables/rewardEligibilityAssociations.js +11 -0
- package/dist/db/tables/rewardEligibilityAssociations.js.map +1 -1
- package/dist/db/tables/rewardRuns.d.ts.map +1 -1
- package/dist/db/tables/rewardRuns.js +4 -0
- package/dist/db/tables/rewardRuns.js.map +1 -1
- package/dist/db/tables/rewardTransactionAttempts.d.ts.map +1 -1
- package/dist/db/tables/rewardTransactionAttempts.js +2 -0
- package/dist/db/tables/rewardTransactionAttempts.js.map +1 -1
- package/dist/db/tables/routesDepositAddresses.d.ts +29 -0
- package/dist/db/tables/routesDepositAddresses.d.ts.map +1 -1
- package/dist/db/tables/routesDepositAddresses.js +84 -31
- package/dist/db/tables/routesDepositAddresses.js.map +1 -1
- package/dist/db/tables/routesDeposits.d.ts.map +1 -1
- package/dist/db/tables/routesDeposits.js +31 -21
- package/dist/db/tables/routesDeposits.js.map +1 -1
- package/dist/db/tables/routesIdempotency.d.ts.map +1 -1
- package/dist/db/tables/routesIdempotency.js +2 -0
- package/dist/db/tables/routesIdempotency.js.map +1 -1
- package/dist/db/tables/routesPolicies.d.ts +58 -0
- package/dist/db/tables/routesPolicies.d.ts.map +1 -0
- package/dist/db/tables/routesPolicies.js +198 -0
- package/dist/db/tables/routesPolicies.js.map +1 -0
- package/dist/db/tables/routesTransferSubsidies.d.ts +22 -0
- package/dist/db/tables/routesTransferSubsidies.d.ts.map +1 -1
- package/dist/db/tables/routesTransferSubsidies.js +41 -0
- package/dist/db/tables/routesTransferSubsidies.js.map +1 -1
- package/dist/db/tables/routesTransferSubsidyNonces.d.ts.map +1 -1
- package/dist/db/tables/routesTransferSubsidyNonces.js +12 -4
- package/dist/db/tables/routesTransferSubsidyNonces.js.map +1 -1
- package/dist/db/tables/routesTransfers.d.ts +26 -1
- package/dist/db/tables/routesTransfers.d.ts.map +1 -1
- package/dist/db/tables/routesTransfers.js +36 -6
- package/dist/db/tables/routesTransfers.js.map +1 -1
- package/dist/db/tables/sponsoredTransactions.d.ts +45 -13
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +144 -33
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/db/tables/users.d.ts +2 -0
- package/dist/db/tables/users.d.ts.map +1 -1
- package/dist/db/tables/users.js +11 -0
- package/dist/db/tables/users.js.map +1 -1
- package/dist/handlers/internal/multisig.d.ts +1 -1
- package/dist/handlers/internal/multisig.d.ts.map +1 -1
- package/dist/handlers/internal/multisig.js +3 -2
- package/dist/handlers/internal/multisig.js.map +1 -1
- package/dist/handlers/relay.d.ts +2 -0
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +34 -15
- package/dist/handlers/relay.js.map +1 -1
- package/dist/internal/Auth.d.ts +57 -6
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +182 -78
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/BetterAuth.d.ts +22 -0
- package/dist/internal/BetterAuth.d.ts.map +1 -1
- package/dist/internal/BetterAuth.js +17 -14
- package/dist/internal/BetterAuth.js.map +1 -1
- package/dist/internal/Cache.d.ts +5 -0
- package/dist/internal/Cache.d.ts.map +1 -1
- package/dist/internal/Cache.js +3 -1
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/CliAuth.d.ts +243 -0
- package/dist/internal/CliAuth.d.ts.map +1 -0
- package/dist/internal/CliAuth.js +249 -0
- package/dist/internal/CliAuth.js.map +1 -0
- package/dist/internal/EarnSharePrices.d.ts +1 -1
- package/dist/internal/EarnSharePrices.d.ts.map +1 -1
- package/dist/internal/EarnSharePrices.js +7 -3
- package/dist/internal/EarnSharePrices.js.map +1 -1
- package/dist/internal/EarnVaults.d.ts +47 -0
- package/dist/internal/EarnVaults.d.ts.map +1 -1
- package/dist/internal/EarnVaults.js +188 -4
- package/dist/internal/EarnVaults.js.map +1 -1
- package/dist/internal/EarnWithdrawals.d.ts +73 -0
- package/dist/internal/EarnWithdrawals.d.ts.map +1 -0
- package/dist/internal/EarnWithdrawals.js +189 -0
- package/dist/internal/EarnWithdrawals.js.map +1 -0
- package/dist/internal/ExecutionContext.d.ts +21 -0
- package/dist/internal/ExecutionContext.d.ts.map +1 -0
- package/dist/internal/ExecutionContext.js +16 -0
- package/dist/internal/ExecutionContext.js.map +1 -0
- package/dist/internal/Log.d.ts +7 -3
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +163 -130
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +26 -1
- package/dist/internal/MetricSink.js.map +1 -1
- package/dist/internal/RateLimit.d.ts +67 -0
- package/dist/internal/RateLimit.d.ts.map +1 -1
- package/dist/internal/RateLimit.js +174 -27
- package/dist/internal/RateLimit.js.map +1 -1
- package/dist/internal/Store.d.ts +6 -0
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +4 -5
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Timing.d.ts +6 -0
- package/dist/internal/Timing.d.ts.map +1 -1
- package/dist/internal/Timing.js +21 -1
- package/dist/internal/Timing.js.map +1 -1
- package/dist/internal/Tron.d.ts +124 -0
- package/dist/internal/Tron.d.ts.map +1 -0
- package/dist/internal/Tron.js +202 -0
- package/dist/internal/Tron.js.map +1 -0
- package/dist/internal/Viem.d.ts +4 -0
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +25 -6
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/rewards/Read.d.ts +2 -2
- package/dist/internal/rewards/Read.d.ts.map +1 -1
- package/dist/internal/rewards/Read.js +11 -2
- package/dist/internal/rewards/Read.js.map +1 -1
- package/dist/internal/routes/Action.d.ts +27 -0
- package/dist/internal/routes/Action.d.ts.map +1 -1
- package/dist/internal/routes/Action.js +49 -1
- package/dist/internal/routes/Action.js.map +1 -1
- package/dist/internal/routes/Chain.d.ts +2 -0
- package/dist/internal/routes/Chain.d.ts.map +1 -1
- package/dist/internal/routes/Chain.js +4 -0
- package/dist/internal/routes/Chain.js.map +1 -1
- package/dist/internal/routes/DepositAddress.d.ts.map +1 -1
- package/dist/internal/routes/DepositAddress.js +5 -0
- package/dist/internal/routes/DepositAddress.js.map +1 -1
- package/dist/internal/routes/Evidence.d.ts.map +1 -1
- package/dist/internal/routes/Evidence.js +6 -2
- package/dist/internal/routes/Evidence.js.map +1 -1
- package/dist/internal/routes/EvmTransferSettlement.d.ts +17 -0
- package/dist/internal/routes/EvmTransferSettlement.d.ts.map +1 -0
- package/dist/internal/routes/EvmTransferSettlement.js +196 -0
- package/dist/internal/routes/EvmTransferSettlement.js.map +1 -0
- package/dist/internal/routes/Policy.d.ts +161 -0
- package/dist/internal/routes/Policy.d.ts.map +1 -0
- package/dist/internal/routes/Policy.js +249 -0
- package/dist/internal/routes/Policy.js.map +1 -0
- package/dist/internal/routes/Provider.d.ts +66 -3
- package/dist/internal/routes/Provider.d.ts.map +1 -1
- package/dist/internal/routes/Provider.js +106 -27
- package/dist/internal/routes/Provider.js.map +1 -1
- package/dist/internal/routes/Quota.d.ts +57 -0
- package/dist/internal/routes/Quota.d.ts.map +1 -0
- package/dist/internal/routes/Quota.js +144 -0
- package/dist/internal/routes/Quota.js.map +1 -0
- package/dist/internal/routes/Reconciliation.d.ts.map +1 -1
- package/dist/internal/routes/Reconciliation.js +163 -47
- package/dist/internal/routes/Reconciliation.js.map +1 -1
- package/dist/internal/routes/SolanaTransferSettlement.d.ts +12 -0
- package/dist/internal/routes/SolanaTransferSettlement.d.ts.map +1 -0
- package/dist/internal/routes/SolanaTransferSettlement.js +220 -0
- package/dist/internal/routes/SolanaTransferSettlement.js.map +1 -0
- package/dist/internal/routes/SolanaTransferSubsidy.d.ts +172 -0
- package/dist/internal/routes/SolanaTransferSubsidy.d.ts.map +1 -0
- package/dist/internal/routes/SolanaTransferSubsidy.js +564 -0
- package/dist/internal/routes/SolanaTransferSubsidy.js.map +1 -0
- package/dist/internal/routes/Subsidy.d.ts +2 -2
- package/dist/internal/routes/Subsidy.d.ts.map +1 -1
- package/dist/internal/routes/Subsidy.js +2 -2
- package/dist/internal/routes/Subsidy.js.map +1 -1
- package/dist/internal/routes/Transfer.d.ts +229 -1
- package/dist/internal/routes/Transfer.d.ts.map +1 -1
- package/dist/internal/routes/Transfer.js +644 -7
- package/dist/internal/routes/Transfer.js.map +1 -1
- package/dist/internal/routes/TransferReconciliation.d.ts +6 -0
- package/dist/internal/routes/TransferReconciliation.d.ts.map +1 -1
- package/dist/internal/routes/TransferReconciliation.js +83 -159
- package/dist/internal/routes/TransferReconciliation.js.map +1 -1
- package/dist/internal/routes/TransferSettlement.d.ts +45 -0
- package/dist/internal/routes/TransferSettlement.d.ts.map +1 -0
- package/dist/internal/routes/TransferSettlement.js +2 -0
- package/dist/internal/routes/TransferSettlement.js.map +1 -0
- package/dist/internal/routes/TransferSubsidy.d.ts +23 -8
- package/dist/internal/routes/TransferSubsidy.d.ts.map +1 -1
- package/dist/internal/routes/TransferSubsidy.js +64 -5
- package/dist/internal/routes/TransferSubsidy.js.map +1 -1
- package/dist/internal/routes/TronTransferSettlement.d.ts +12 -0
- package/dist/internal/routes/TronTransferSettlement.d.ts.map +1 -0
- package/dist/internal/routes/TronTransferSettlement.js +201 -0
- package/dist/internal/routes/TronTransferSettlement.js.map +1 -0
- package/dist/internal/routes/TronTransferSubsidy.d.ts +174 -0
- package/dist/internal/routes/TronTransferSubsidy.d.ts.map +1 -0
- package/dist/internal/routes/TronTransferSubsidy.js +439 -0
- package/dist/internal/routes/TronTransferSubsidy.js.map +1 -0
- package/dist/internal/routes/index.d.ts +3 -0
- package/dist/internal/routes/index.d.ts.map +1 -1
- package/dist/internal/routes/index.js +3 -0
- package/dist/internal/routes/index.js.map +1 -1
- package/dist/internal/routes/providers/across.d.ts +3 -0
- package/dist/internal/routes/providers/across.d.ts.map +1 -1
- package/dist/internal/routes/providers/across.js +11 -0
- package/dist/internal/routes/providers/across.js.map +1 -1
- package/dist/internal/routes/providers/legacyMesh.d.ts +29 -0
- package/dist/internal/routes/providers/legacyMesh.d.ts.map +1 -0
- package/dist/internal/routes/providers/legacyMesh.js +474 -0
- package/dist/internal/routes/providers/legacyMesh.js.map +1 -0
- package/dist/internal/routes/providers/legacyMeshDelivery.d.ts +59 -0
- package/dist/internal/routes/providers/legacyMeshDelivery.d.ts.map +1 -0
- package/dist/internal/routes/providers/legacyMeshDelivery.js +229 -0
- package/dist/internal/routes/providers/legacyMeshDelivery.js.map +1 -0
- package/dist/internal/routes/providers/legacyMeshOutbound.d.ts +19 -0
- package/dist/internal/routes/providers/legacyMeshOutbound.d.ts.map +1 -0
- package/dist/internal/routes/providers/legacyMeshOutbound.js +457 -0
- package/dist/internal/routes/providers/legacyMeshOutbound.js.map +1 -0
- package/dist/internal/routes/providers/legacyMeshOutboundDelivery.d.ts +123 -0
- package/dist/internal/routes/providers/legacyMeshOutboundDelivery.d.ts.map +1 -0
- package/dist/internal/routes/providers/legacyMeshOutboundDelivery.js +333 -0
- package/dist/internal/routes/providers/legacyMeshOutboundDelivery.js.map +1 -0
- package/dist/internal/routes/providers/propAmm.d.ts +70 -0
- package/dist/internal/routes/providers/propAmm.d.ts.map +1 -0
- package/dist/internal/routes/providers/propAmm.js +524 -0
- package/dist/internal/routes/providers/propAmm.js.map +1 -0
- package/dist/internal/routes/providers/relay.d.ts +3 -0
- package/dist/internal/routes/providers/relay.d.ts.map +1 -1
- package/dist/internal/routes/providers/relay.js +352 -41
- package/dist/internal/routes/providers/relay.js.map +1 -1
- package/dist/internal/routes/providers/rhino.d.ts +3 -0
- package/dist/internal/routes/providers/rhino.d.ts.map +1 -1
- package/dist/internal/routes/providers/rhino.js +11 -0
- package/dist/internal/routes/providers/rhino.js.map +1 -1
- package/dist/internal/routes/providers/squid.d.ts +3 -0
- package/dist/internal/routes/providers/squid.d.ts.map +1 -1
- package/dist/internal/routes/providers/squid.js +19 -0
- package/dist/internal/routes/providers/squid.js.map +1 -1
- package/dist/internal/routes/providers/stargate.d.ts.map +1 -1
- package/dist/internal/routes/providers/stargate.js +22 -9
- package/dist/internal/routes/providers/stargate.js.map +1 -1
- package/dist/internal/routes/providers/symbiosis.d.ts.map +1 -1
- package/dist/internal/routes/providers/symbiosis.js +13 -17
- package/dist/internal/routes/providers/symbiosis.js.map +1 -1
- package/dist/providers/index.d.ts +14 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +6 -0
- package/dist/providers/index.js.map +1 -1
- package/package.json +16 -6
- package/src/ApiKey.ts +7 -3
- package/src/ApiKeys.test.ts +652 -8
- package/src/ApiKeys.ts +191 -11
- package/src/App.test.ts +405 -18
- package/src/App.ts +51 -15
- package/src/Client.test-d.ts +157 -10
- package/src/Client.test.ts +4 -4
- package/src/Scope.ts +1 -2
- package/src/admin/App.ts +11 -5
- package/src/admin/apps/api-keys.test.ts +98 -2
- package/src/admin/apps/api-keys.ts +21 -12
- package/src/admin/apps/earn-vaults.test.ts +76 -7
- package/src/admin/apps/earn-vaults.ts +47 -8
- package/src/admin/apps/organizations.test.ts +147 -31
- package/src/admin/apps/organizations.ts +5 -4
- package/src/admin/apps/route-policies.test.ts +220 -0
- package/src/admin/apps/route-policies.ts +322 -0
- package/src/admin/apps/routes-support.test.ts +107 -2
- package/src/admin/apps/routes-support.ts +43 -3
- package/src/admin/apps/routes.test.ts +150 -0
- package/src/admin/apps/routes.ts +97 -1
- package/src/admin/apps/scopes.test.ts +1 -1
- package/src/admin/index.ts +0 -1
- package/src/admin/ui/plugin.ts +2 -1
- package/src/admin/ui/src/components/Dialog.tsx +1 -1
- package/src/admin/ui/src/components/EarnVaultForm.tsx +45 -0
- package/src/admin/ui/src/components/RoutePicker.tsx +79 -0
- package/src/admin/ui/src/lib/earn-vaults.ts +1 -1
- package/src/admin/ui/src/lib/route-policies.test.ts +452 -0
- package/src/admin/ui/src/lib/route-policies.ts +232 -0
- package/src/admin/ui/src/lib/routes.ts +34 -0
- package/src/admin/ui/src/routeTree.gen.ts +21 -0
- package/src/admin/ui/src/routes/api-keys.tsx +21 -2
- package/src/admin/ui/src/routes/early-access.tsx +1 -0
- package/src/admin/ui/src/routes/earn.vaults.tsx +1 -0
- package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +22 -0
- package/src/admin/ui/src/routes/earn.vaults_.new.tsx +10 -0
- package/src/admin/ui/src/routes/organizations.tsx +1 -0
- package/src/admin/ui/src/routes/organizations_.$orgId.tsx +18 -3
- package/src/admin/ui/src/routes/route-policies.tsx +678 -0
- package/src/admin/ui/src/routes/routes.tsx +85 -2
- package/src/admin/ui/src/routes/routes_.support.tsx +6 -22
- package/src/admin/ui/src/routes/subsidies.tsx +1 -0
- package/src/admin/ui/src/routes/verified-tokens.tsx +1 -0
- package/src/admin/ui.generated.ts +258 -202
- package/src/analytics/Analytics.test.ts +4 -50
- package/src/analytics/Analytics.ts +65 -1
- package/src/analytics/tables/requestEvents.test.ts +402 -1
- package/src/analytics/tables/requestEvents.ts +62 -93
- package/src/apps/data/routes/activities.test.ts +315 -14
- package/src/apps/data/routes/activities.ts +60 -0
- package/src/apps/data/routes/balances.ts +5 -7
- package/src/apps/data/routes/earn.bench.ts +18 -0
- package/src/apps/data/routes/earn.local.test.ts +507 -0
- package/src/apps/data/routes/earn.test.ts +405 -127
- package/src/apps/data/routes/earn.ts +660 -270
- package/src/apps/data/routes/exchanges.bench.ts +3 -1
- package/src/apps/data/routes/exchanges.test.ts +166 -2
- package/src/apps/data/routes/exchanges.ts +308 -101
- package/src/apps/data/routes/fee-amm.test.ts +134 -2
- package/src/apps/data/routes/fee-amm.ts +122 -60
- package/src/apps/data/routes/receipts.test.ts +21 -18
- package/src/apps/data/routes/rpc.test.ts +2 -2
- package/src/apps/data/routes/tokens.provenance.test.ts +157 -0
- package/src/apps/data/routes/tokens.test.ts +4 -4
- package/src/apps/data/routes/tokens.ts +11 -6
- package/src/apps/data/routes/transactions.test.ts +29 -0
- package/src/apps/data/routes/transactions.ts +33 -22
- package/src/apps/data/routes/webhooks.test.ts +12 -3
- package/src/apps/data/routes/webhooks.ts +2 -1
- package/src/apps/management/App.ts +4 -0
- package/src/apps/management/Billing.test.ts +253 -27
- package/src/apps/management/Billing.ts +83 -29
- package/src/apps/management/routes/api-keys.test.ts +1 -1
- package/src/apps/management/routes/api-keys.ts +85 -11
- package/src/apps/management/routes/billing.test.ts +68 -4
- package/src/apps/management/routes/cli.test.ts +370 -0
- package/src/apps/management/routes/cli.ts +533 -0
- package/src/apps/management/routes/me.test.ts +14 -4
- package/src/apps/management/routes/orgs.test.ts +108 -4
- package/src/apps/management/routes/orgs.ts +7 -2
- package/src/apps/management/routes/usage.test.ts +2 -2
- package/src/apps/mpp/internal/Policy.test.ts +51 -4
- package/src/apps/mpp/internal/Policy.ts +6 -2
- package/src/apps/mpp/internal/Sponsorship.test.ts +29 -0
- package/src/apps/mpp/internal/Sponsorship.ts +6 -4
- package/src/apps/relay/App.test.ts +85 -1
- package/src/apps/relay/App.ts +28 -4
- package/src/apps/relay/Sponsorships.test.ts +97 -1
- package/src/apps/relay/Sponsorships.ts +77 -66
- package/src/apps/routes/Access.test.ts +319 -0
- package/src/apps/routes/Access.ts +147 -0
- package/src/apps/routes/App.ts +10 -0
- package/src/apps/routes/routes/chains.test.ts +3 -2
- package/src/apps/routes/routes/deposit-addresses.test.ts +978 -34
- package/src/apps/routes/routes/deposit-addresses.ts +243 -32
- package/src/apps/routes/routes/propAmm.local.test.ts +951 -0
- package/src/apps/routes/routes/quotes.test.ts +81 -8
- package/src/apps/routes/routes/quotes.ts +109 -27
- package/src/apps/routes/routes/transfers.test.ts +252 -15
- package/src/apps/routes/routes/transfers.ts +212 -33
- package/src/cli/ApiKeys.test.ts +265 -0
- package/src/cli/ApiKeys.ts +189 -0
- package/src/cli/Cli.test.ts +188 -0
- package/src/cli/Cli.ts +280 -12
- package/src/cli/Credentials.test.ts +122 -0
- package/src/cli/Credentials.ts +144 -0
- package/src/cli/Spec.test.ts +133 -0
- package/src/cli/Spec.ts +132 -0
- package/src/cli/bin.test.ts +49 -0
- package/src/cli/bin.ts +5 -1
- package/src/cli/commands/auth.test.ts +138 -0
- package/src/cli/commands/auth.ts +254 -0
- package/src/cli/commands/keys.test.ts +68 -0
- package/src/cli/commands/keys.ts +125 -0
- package/src/cloudflare.ts +184 -36
- package/src/db/Db.test.ts +877 -0
- package/src/db/Db.ts +498 -1
- package/src/db/Schema.ts +75 -4
- package/src/db/tables/apiKeyAdmissions.test.ts +57 -0
- package/src/db/tables/apiKeyAdmissions.ts +101 -11
- package/src/db/tables/apiKeyOwnerTombstones.ts +2 -2
- package/src/db/tables/authAccounts.test.ts +30 -0
- package/src/db/tables/authAccounts.ts +1 -0
- package/src/db/tables/earnVaults.test.ts +34 -0
- package/src/db/tables/earnVaults.ts +17 -2
- package/src/db/tables/organizations.test.ts +148 -84
- package/src/db/tables/organizations.ts +11 -43
- package/src/db/tables/requestUsage.ts +13 -2
- package/src/db/tables/rewardCampaigns.test.ts +163 -0
- package/src/db/tables/rewardEligibilityAssociations.test.ts +14 -0
- package/src/db/tables/rewardEligibilityAssociations.ts +24 -0
- package/src/db/tables/rewardRuns.ts +4 -0
- package/src/db/tables/rewardTransactionAttempts.ts +2 -0
- package/src/db/tables/routesDepositAddresses.test.ts +263 -0
- package/src/db/tables/routesDepositAddresses.ts +118 -34
- package/src/db/tables/routesDeposits.test.ts +66 -5
- package/src/db/tables/routesDeposits.ts +47 -26
- package/src/db/tables/routesIdempotency.test.ts +80 -0
- package/src/db/tables/routesIdempotency.ts +2 -0
- package/src/db/tables/routesPolicies.test.ts +143 -0
- package/src/db/tables/routesPolicies.ts +251 -0
- package/src/db/tables/routesTransferSubsidies.ts +64 -0
- package/src/db/tables/routesTransferSubsidyNonces.ts +16 -4
- package/src/db/tables/routesTransfers.test.ts +183 -2
- package/src/db/tables/routesTransfers.ts +65 -6
- package/src/db/tables/sponsoredTransactions.test.ts +351 -76
- package/src/db/tables/sponsoredTransactions.ts +182 -38
- package/src/db/tables/users.test.ts +51 -0
- package/src/db/tables/users.ts +12 -0
- package/src/handlers/internal/multisig.store.test.ts +120 -0
- package/src/handlers/internal/multisig.test.ts +389 -0
- package/src/handlers/internal/multisig.ts +13 -10
- package/src/handlers/relay.test.ts +259 -83
- package/src/handlers/relay.ts +36 -17
- package/src/internal/Auth.test.ts +291 -8
- package/src/internal/Auth.ts +227 -96
- package/src/internal/BetterAuth.test.ts +58 -22
- package/src/internal/BetterAuth.ts +23 -17
- package/src/internal/Cache.ts +3 -1
- package/src/internal/CliAuth.test.ts +279 -0
- package/src/internal/CliAuth.ts +319 -0
- package/src/internal/EarnSharePrices.test.ts +25 -0
- package/src/internal/EarnSharePrices.ts +9 -2
- package/src/internal/EarnVaults.test.ts +67 -0
- package/src/internal/EarnVaults.ts +299 -4
- package/src/internal/EarnWithdrawals.test.ts +315 -0
- package/src/internal/EarnWithdrawals.ts +266 -0
- package/src/internal/ExecutionContext.ts +31 -0
- package/src/internal/Log.test.ts +210 -0
- package/src/internal/Log.ts +180 -142
- package/src/internal/MetricSink.test.ts +190 -15
- package/src/internal/MetricSink.ts +27 -1
- package/src/internal/RateLimit.test.ts +144 -8
- package/src/internal/RateLimit.ts +254 -26
- package/src/internal/Store.ts +4 -5
- package/src/internal/Timing.test.ts +32 -0
- package/src/internal/Timing.ts +20 -1
- package/src/internal/Tron.test-d.ts +22 -0
- package/src/internal/Tron.test.ts +94 -0
- package/src/internal/Tron.ts +289 -0
- package/src/internal/Viem.rpc.test.ts +76 -0
- package/src/internal/Viem.ts +29 -6
- package/src/internal/rewards/Read.test.ts +109 -0
- package/src/internal/rewards/Read.ts +13 -4
- package/src/internal/routes/Action.test.ts +70 -0
- package/src/internal/routes/Action.ts +76 -1
- package/src/internal/routes/Chain.test.ts +18 -0
- package/src/internal/routes/Chain.ts +5 -0
- package/src/internal/routes/DepositAddress.ts +5 -0
- package/src/internal/routes/Evidence.test.ts +64 -0
- package/src/internal/routes/Evidence.ts +11 -3
- package/src/internal/routes/EvmTransferSettlement.ts +213 -0
- package/src/internal/routes/Policy.test.ts +316 -0
- package/src/internal/routes/Policy.ts +399 -0
- package/src/internal/routes/Provider.test.ts +103 -2
- package/src/internal/routes/Provider.ts +149 -27
- package/src/internal/routes/Quota.test.ts +149 -0
- package/src/internal/routes/Quota.ts +226 -0
- package/src/internal/routes/Reconciliation.local.test.ts +285 -0
- package/src/internal/routes/Reconciliation.test.ts +371 -13
- package/src/internal/routes/Reconciliation.ts +193 -42
- package/src/internal/routes/SolanaTransferSettlement.ts +232 -0
- package/src/internal/routes/SolanaTransferSubsidy.test.ts +471 -0
- package/src/internal/routes/SolanaTransferSubsidy.ts +760 -0
- package/src/internal/routes/SourceObservation.test.ts +221 -0
- package/src/internal/routes/Subsidy.local.test.ts +46 -0
- package/src/internal/routes/Subsidy.ts +4 -2
- package/src/internal/routes/Transfer.solana.test.ts +389 -0
- package/src/internal/routes/Transfer.test.ts +130 -1
- package/src/internal/routes/Transfer.tron.test.ts +172 -0
- package/src/internal/routes/Transfer.ts +866 -8
- package/src/internal/routes/TransferReconciliation.test.ts +26 -4
- package/src/internal/routes/TransferReconciliation.ts +91 -166
- package/src/internal/routes/TransferSettlement.ts +48 -0
- package/src/internal/routes/TransferSubsidy.local.test.ts +143 -0
- package/src/internal/routes/TransferSubsidy.test.ts +34 -4
- package/src/internal/routes/TransferSubsidy.ts +87 -12
- package/src/internal/routes/TronTransferSettlement.ts +212 -0
- package/src/internal/routes/TronTransferSubsidy.test.ts +109 -0
- package/src/internal/routes/TronTransferSubsidy.ts +619 -0
- package/src/internal/routes/index.test.ts +3 -0
- package/src/internal/routes/index.ts +3 -0
- package/src/internal/routes/providers/across.ts +13 -0
- package/src/internal/routes/providers/index.test.ts +141 -0
- package/src/internal/routes/providers/legacyMesh.test.ts +484 -0
- package/src/internal/routes/providers/legacyMesh.ts +633 -0
- package/src/internal/routes/providers/legacyMeshDelivery.test.ts +306 -0
- package/src/internal/routes/providers/legacyMeshDelivery.ts +320 -0
- package/src/internal/routes/providers/legacyMeshOutbound.test.ts +401 -0
- package/src/internal/routes/providers/legacyMeshOutbound.ts +566 -0
- package/src/internal/routes/providers/legacyMeshOutboundDelivery.ts +473 -0
- package/src/internal/routes/providers/propAmm.ts +640 -0
- package/src/internal/routes/providers/relay.test.ts +1263 -41
- package/src/internal/routes/providers/relay.ts +423 -38
- package/src/internal/routes/providers/rhino.ts +14 -1
- package/src/internal/routes/providers/squid.ts +21 -0
- package/src/internal/routes/providers/stargate.test.ts +105 -1
- package/src/internal/routes/providers/stargate.ts +22 -9
- package/src/internal/routes/providers/symbiosis.ts +13 -47
- package/src/providers/index.test.ts +2 -0
- package/src/providers/index.ts +18 -0
- package/dist/admin/Access.d.ts +0 -39
- package/dist/admin/Access.d.ts.map +0 -1
- package/dist/admin/Access.js +0 -73
- package/dist/admin/Access.js.map +0 -1
- package/src/admin/Access.test.ts +0 -130
- package/src/admin/Access.ts +0 -85
|
@@ -8,6 +8,8 @@ import * as RoutesIdempotencyTable from '../../../db/tables/routesIdempotency.js
|
|
|
8
8
|
|
|
9
9
|
import * as Organizations from '../../../db/tables/organizations.js'
|
|
10
10
|
|
|
11
|
+
import * as RoutesPolicies from '../../../db/tables/routesPolicies.js'
|
|
12
|
+
|
|
11
13
|
import * as RoutesSubsidies from '../../../db/tables/routesSubsidies.js'
|
|
12
14
|
|
|
13
15
|
import * as StripeCustomers from '../../../db/tables/stripeCustomers.js'
|
|
@@ -20,6 +22,8 @@ import * as RoutesDeposit from '../../../internal/routes/Deposit.js'
|
|
|
20
22
|
|
|
21
23
|
import * as RoutesDepositAddress from '../../../internal/routes/DepositAddress.js'
|
|
22
24
|
|
|
25
|
+
import * as RoutesCatalog from '../../../internal/routes/Catalog.js'
|
|
26
|
+
|
|
23
27
|
import * as RoutesProvider from '../../../internal/routes/Provider.js'
|
|
24
28
|
|
|
25
29
|
import * as Idempotency from '../../../internal/Idempotency.js'
|
|
@@ -40,6 +44,8 @@ import * as Metrics from '../../../Metrics.js'
|
|
|
40
44
|
|
|
41
45
|
import * as TestApp from '../../../../test/App.js'
|
|
42
46
|
|
|
47
|
+
import * as TestRelay from '../../../../test/Relay.js'
|
|
48
|
+
|
|
43
49
|
import * as TestRoutes from '../../../../test/Routes.js'
|
|
44
50
|
|
|
45
51
|
import * as Routes from './deposit-addresses.js'
|
|
@@ -178,19 +184,21 @@ test('publishes generator-ready route deposit contracts', async () => {
|
|
|
178
184
|
spec.components.schemas.RoutesDepositAddressNotActiveError.properties.error.properties.code
|
|
179
185
|
.enum,
|
|
180
186
|
addressNotAvailable:
|
|
181
|
-
spec.components.schemas.
|
|
182
|
-
.enum,
|
|
187
|
+
spec.components.schemas.DepositAddressNotAvailableOrQuoteNotAvailableError.properties.error
|
|
188
|
+
.properties.code.enum,
|
|
183
189
|
addressNotFound:
|
|
184
190
|
spec.components.schemas.RoutesDepositAddressNotFoundError.properties.error.properties.code
|
|
185
191
|
.enum,
|
|
186
192
|
createInvalid:
|
|
187
|
-
spec.components.schemas
|
|
188
|
-
.
|
|
193
|
+
spec.components.schemas
|
|
194
|
+
.AmountTooLowOrApiKeyMalformedOrBodyInvalidOrIdempotencyKeyRequiredError.properties.error
|
|
195
|
+
.properties.code.enum,
|
|
189
196
|
depositNotFound:
|
|
190
197
|
spec.components.schemas.RoutesDepositNotFoundError.properties.error.properties.code.enum,
|
|
191
198
|
idempotencyConflict:
|
|
192
|
-
spec.components.schemas
|
|
193
|
-
.
|
|
199
|
+
spec.components.schemas
|
|
200
|
+
.IdempotencyConflictOrIdempotencyInProgressOrRoutePolicyChangedOrRoutePolicyIdentityConflictError
|
|
201
|
+
.properties.error.properties.code.enum,
|
|
194
202
|
notImplemented:
|
|
195
203
|
spec.components.schemas.NotImplemented501Error.properties.error.properties.code.enum,
|
|
196
204
|
parametersInvalid:
|
|
@@ -287,7 +295,7 @@ test('publishes generator-ready route deposit contracts', async () => {
|
|
|
287
295
|
"createAddress": {
|
|
288
296
|
"errors": {
|
|
289
297
|
"400": {
|
|
290
|
-
"$ref": "#/components/schemas/
|
|
298
|
+
"$ref": "#/components/schemas/AmountTooLowOrApiKeyMalformedOrBodyInvalidOrIdempotencyKeyRequiredError",
|
|
291
299
|
},
|
|
292
300
|
"401": {
|
|
293
301
|
"$ref": "#/components/schemas/AuthenticationError",
|
|
@@ -296,10 +304,10 @@ test('publishes generator-ready route deposit contracts', async () => {
|
|
|
296
304
|
"$ref": "#/components/schemas/CreateRoutesDepositAddressForbiddenError",
|
|
297
305
|
},
|
|
298
306
|
"404": {
|
|
299
|
-
"$ref": "#/components/schemas/
|
|
307
|
+
"$ref": "#/components/schemas/DepositAddressNotAvailableOrQuoteNotAvailableError",
|
|
300
308
|
},
|
|
301
309
|
"409": {
|
|
302
|
-
"$ref": "#/components/schemas/
|
|
310
|
+
"$ref": "#/components/schemas/IdempotencyConflictOrIdempotencyInProgressOrRoutePolicyChangedOrRoutePolicyIdentityConflictError",
|
|
303
311
|
},
|
|
304
312
|
"429": "#/components/responses/RateLimited",
|
|
305
313
|
"500": "#/components/responses/InternalError",
|
|
@@ -456,11 +464,13 @@ test('publishes generator-ready route deposit contracts', async () => {
|
|
|
456
464
|
],
|
|
457
465
|
"addressNotAvailable": [
|
|
458
466
|
"deposit_address_not_available",
|
|
467
|
+
"quote_not_available",
|
|
459
468
|
],
|
|
460
469
|
"addressNotFound": [
|
|
461
470
|
"routes_deposit_address_not_found",
|
|
462
471
|
],
|
|
463
472
|
"createInvalid": [
|
|
473
|
+
"amount_too_low",
|
|
464
474
|
"api_key_malformed",
|
|
465
475
|
"body_invalid",
|
|
466
476
|
"idempotency_key_required",
|
|
@@ -471,6 +481,8 @@ test('publishes generator-ready route deposit contracts', async () => {
|
|
|
471
481
|
"idempotencyConflict": [
|
|
472
482
|
"idempotency_conflict",
|
|
473
483
|
"idempotency_in_progress",
|
|
484
|
+
"route_policy_changed",
|
|
485
|
+
"route_policy_identity_conflict",
|
|
474
486
|
],
|
|
475
487
|
"notImplemented": [
|
|
476
488
|
"not_implemented",
|
|
@@ -490,7 +502,10 @@ test('publishes generator-ready route deposit contracts', async () => {
|
|
|
490
502
|
"billing_required",
|
|
491
503
|
"forbidden",
|
|
492
504
|
"routes_deposit_address_limit_exceeded",
|
|
505
|
+
"routes_not_allowed",
|
|
506
|
+
"routes_subsidy_not_allowed",
|
|
493
507
|
"routes_subsidy_not_enabled",
|
|
508
|
+
"routes_suspended",
|
|
494
509
|
],
|
|
495
510
|
},
|
|
496
511
|
"statusReasons": [
|
|
@@ -574,6 +589,356 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
574
589
|
})
|
|
575
590
|
})
|
|
576
591
|
|
|
592
|
+
test.skipIf(!relayApiKey)(
|
|
593
|
+
'creates a fresh Relay address after a cap change without changing wallets (live)',
|
|
594
|
+
async () => {
|
|
595
|
+
const key = {
|
|
596
|
+
id: 'key_relay_cap_replacement',
|
|
597
|
+
orgId: 'org_relay_cap_replacement',
|
|
598
|
+
scopes: ['routes:read', 'routes:write'],
|
|
599
|
+
token: 'secret_relay_cap_replacement',
|
|
600
|
+
}
|
|
601
|
+
await grantRoutes(key)
|
|
602
|
+
const scope = { environment: 'production', orgId: key.orgId } as const
|
|
603
|
+
// Leave room for live Relay fees while testing changes to pinned caps.
|
|
604
|
+
const policy = {
|
|
605
|
+
access: null,
|
|
606
|
+
maxAmount: '1',
|
|
607
|
+
subsidyEnabled: true,
|
|
608
|
+
subsidyPayer: 'tempo',
|
|
609
|
+
} as const
|
|
610
|
+
await RoutesPolicies.save(db, { policy, scope, version: 1 })
|
|
611
|
+
const app = create({
|
|
612
|
+
auth: { keys: [key] },
|
|
613
|
+
defaultChainId: tempoMainnet.id,
|
|
614
|
+
providers: [Relay.relay({ apiKey: relayApiKey, subsidies: { maxAmount: '100' } })],
|
|
615
|
+
routes: { depositSubsidyMaxAmount: '3' },
|
|
616
|
+
})
|
|
617
|
+
const body = {
|
|
618
|
+
...depositAddressBody,
|
|
619
|
+
amount: '10000000',
|
|
620
|
+
refundAddress: `0x${'93'.repeat(20)}`,
|
|
621
|
+
sourceChain: 'base',
|
|
622
|
+
sourceToken: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
|
|
623
|
+
subsidize: true,
|
|
624
|
+
}
|
|
625
|
+
const firstKey = nextDepositAddressKey()
|
|
626
|
+
const first = await TestApp.json(
|
|
627
|
+
await postDepositAddress(app, { body, idempotencyKey: firstKey, key }),
|
|
628
|
+
Routes.schema.createRoutesDepositAddress.Response,
|
|
629
|
+
)
|
|
630
|
+
const stored = await RoutesDepositAddresses.get(db, first.id)
|
|
631
|
+
expect(stored?.providerState).toMatchObject({
|
|
632
|
+
routePolicy: { maxAmount: '1', subsidyPayer: 'tempo' },
|
|
633
|
+
})
|
|
634
|
+
await RoutesPolicies.save(db, {
|
|
635
|
+
policy: { ...policy, maxAmount: '2' },
|
|
636
|
+
scope,
|
|
637
|
+
version: 2,
|
|
638
|
+
})
|
|
639
|
+
const replacement = await postDepositAddress(app, { body, key })
|
|
640
|
+
expect(replacement.status).toBe(200)
|
|
641
|
+
const second = await TestApp.json(
|
|
642
|
+
replacement,
|
|
643
|
+
Routes.schema.createRoutesDepositAddress.Response,
|
|
644
|
+
)
|
|
645
|
+
expect(second.address).not.toBe(first.address)
|
|
646
|
+
expect(second.recipient).toBe(first.recipient)
|
|
647
|
+
expect(second.refundAddress).toBe(first.refundAddress)
|
|
648
|
+
expect((await RoutesDepositAddresses.get(db, second.id))?.providerState).toMatchObject({
|
|
649
|
+
routePolicy: { maxAmount: '2', subsidyPayer: 'tempo' },
|
|
650
|
+
})
|
|
651
|
+
const reused = await TestApp.json(
|
|
652
|
+
await postDepositAddress(app, { body, key }),
|
|
653
|
+
Routes.schema.createRoutesDepositAddress.Response,
|
|
654
|
+
)
|
|
655
|
+
expect(reused.id).toBe(second.id)
|
|
656
|
+
const replay = await TestApp.json(
|
|
657
|
+
await postDepositAddress(app, { body, idempotencyKey: firstKey, key }),
|
|
658
|
+
Routes.schema.createRoutesDepositAddress.Response,
|
|
659
|
+
)
|
|
660
|
+
expect(replay.id).toBe(first.id)
|
|
661
|
+
expect(await RoutesDepositAddresses.get(db, first.id)).toEqual(stored)
|
|
662
|
+
await RoutesPolicies.save(db, { policy, scope, version: 3 })
|
|
663
|
+
const restored = await TestApp.json(
|
|
664
|
+
await postDepositAddress(app, { body, key }),
|
|
665
|
+
Routes.schema.createRoutesDepositAddress.Response,
|
|
666
|
+
)
|
|
667
|
+
expect(restored.id).toBe(first.id)
|
|
668
|
+
expect(await RoutesDepositAddresses.countByOrganization(db, key.orgId)).toBe(2)
|
|
669
|
+
},
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
test('keeps fixed-rate legacy Relay addresses separate from previous delivery terms', async () => {
|
|
673
|
+
const key = {
|
|
674
|
+
id: 'key_legacy_upgrade',
|
|
675
|
+
orgId: 'org_legacy_upgrade',
|
|
676
|
+
token: 'secret_legacy_upgrade',
|
|
677
|
+
scopes: ['routes:read', 'routes:write'],
|
|
678
|
+
}
|
|
679
|
+
const sibling = {
|
|
680
|
+
...key,
|
|
681
|
+
id: 'key_legacy_upgrade_sibling',
|
|
682
|
+
token: 'secret_legacy_upgrade_sibling',
|
|
683
|
+
}
|
|
684
|
+
await enableRoutesSubsidy(key, 'active')
|
|
685
|
+
const scope = { orgId: key.orgId, environment: 'production' } as const
|
|
686
|
+
const inputs: RoutesProvider.createDepositAddress.Parameters[] = []
|
|
687
|
+
const counts: Parameters<Metrics.Metrics['count']>[] = []
|
|
688
|
+
const app = create({
|
|
689
|
+
auth: { keys: [key, sibling] },
|
|
690
|
+
defaultChainId: tempoMainnet.id,
|
|
691
|
+
metrics: Metrics.from({
|
|
692
|
+
count: (...args) => {
|
|
693
|
+
counts.push(args)
|
|
694
|
+
},
|
|
695
|
+
flush() {},
|
|
696
|
+
gauge() {},
|
|
697
|
+
histogram() {},
|
|
698
|
+
}),
|
|
699
|
+
providers: [
|
|
700
|
+
depositAddressProvider(`0x${'81'.repeat(20)}`, (input) => {
|
|
701
|
+
inputs.push(input)
|
|
702
|
+
}),
|
|
703
|
+
],
|
|
704
|
+
routes: { depositSubsidyMaxAmount: '0.05' },
|
|
705
|
+
})
|
|
706
|
+
const body = {
|
|
707
|
+
...depositAddressBody,
|
|
708
|
+
sourceChain: 'base',
|
|
709
|
+
sourceToken: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
|
|
710
|
+
refundAddress: `0x${'82'.repeat(20)}`,
|
|
711
|
+
subsidize: true,
|
|
712
|
+
}
|
|
713
|
+
const idempotencyKey = nextDepositAddressKey()
|
|
714
|
+
const candidate = RoutesProvider.getDepositAddressCandidates({
|
|
715
|
+
catalog: TestRoutes.snapshot,
|
|
716
|
+
destinationChainId: tempoMainnet.id,
|
|
717
|
+
destinationToken: body.destinationToken,
|
|
718
|
+
providers,
|
|
719
|
+
sourceChain: body.sourceChain,
|
|
720
|
+
sourceToken: body.sourceToken,
|
|
721
|
+
})[0]?.candidate
|
|
722
|
+
if (!candidate) throw new Error('Expected a deposit-address candidate.')
|
|
723
|
+
const previous = await RoutesDepositAddress.create(db, {
|
|
724
|
+
deliveryStrategy: 'provider',
|
|
725
|
+
environment: 'production',
|
|
726
|
+
orgId: key.orgId,
|
|
727
|
+
providerOutputToken: candidate.destinationToken,
|
|
728
|
+
snapshot: {
|
|
729
|
+
address: `0x${'d4'.repeat(20)}`,
|
|
730
|
+
destinationChain: candidate.destinationChain,
|
|
731
|
+
destinationToken: candidate.destinationToken,
|
|
732
|
+
provider: { id: 'relay', name: 'Relay' },
|
|
733
|
+
recipient: body.recipient,
|
|
734
|
+
refundAddress: body.refundAddress,
|
|
735
|
+
sourceChain: candidate.sourceChain,
|
|
736
|
+
sourceToken: candidate.sourceToken,
|
|
737
|
+
subsidize: true,
|
|
738
|
+
},
|
|
739
|
+
})
|
|
740
|
+
const first = await TestApp.json(
|
|
741
|
+
await postDepositAddress(app, { body, key, idempotencyKey }),
|
|
742
|
+
Routes.schema.createRoutesDepositAddress.Response,
|
|
743
|
+
)
|
|
744
|
+
const claim = await RoutesDepositAddresses.claim(db, {
|
|
745
|
+
id: first.id,
|
|
746
|
+
leaseUntil: '2027-01-01T00:01:00.000Z',
|
|
747
|
+
now: '2027-01-01T00:00:00.000Z',
|
|
748
|
+
})
|
|
749
|
+
if (!claim) throw new Error('Expected a claimed address.')
|
|
750
|
+
await RoutesDepositAddresses.completePoll(db, {
|
|
751
|
+
id: first.id,
|
|
752
|
+
lastPolledAt: '2027-01-01T00:00:10.000Z',
|
|
753
|
+
nextPollAt: '2027-01-01T00:01:10.000Z',
|
|
754
|
+
pollLeaseVersion: claim.pollLeaseVersion,
|
|
755
|
+
providerRequestIds: claim.providerRequestIds,
|
|
756
|
+
providerState: claim.providerState,
|
|
757
|
+
})
|
|
758
|
+
const reused = await TestApp.json(
|
|
759
|
+
await postDepositAddress(app, { body, key: sibling }),
|
|
760
|
+
Routes.schema.createRoutesDepositAddress.Response,
|
|
761
|
+
)
|
|
762
|
+
expect(reused.id).toBe(first.id)
|
|
763
|
+
expect(first.id).not.toBe(previous.id)
|
|
764
|
+
expect(await RoutesDepositAddresses.get(db, previous.id)).toEqual(previous)
|
|
765
|
+
expect(inputs.map((input) => input.subsidize)).toEqual([true])
|
|
766
|
+
expect(await RoutesPolicies.get(db, scope)).toBeUndefined()
|
|
767
|
+
const stored = await RoutesDepositAddresses.get(db, first.id)
|
|
768
|
+
expect(stored?.providerState).toMatchObject({
|
|
769
|
+
fixedRate: '1:1',
|
|
770
|
+
routePolicy: {
|
|
771
|
+
maxAmount: '0.05',
|
|
772
|
+
scope: { ...scope, apiKeyId: key.id },
|
|
773
|
+
subsidyPayer: 'enterprise',
|
|
774
|
+
versions: [0, 0],
|
|
775
|
+
},
|
|
776
|
+
routePolicyKey: '["[\\"enterprise\\",\\"0.050000\\"]","1:1"]',
|
|
777
|
+
})
|
|
778
|
+
const reservation = await db.kysely
|
|
779
|
+
.selectFrom('routes_idempotency_requests')
|
|
780
|
+
.select('matchHash')
|
|
781
|
+
.where('apiKeyId', '=', key.id)
|
|
782
|
+
.where('keyHash', '=', Idempotency.inputHash(idempotencyKey))
|
|
783
|
+
.executeTakeFirstOrThrow()
|
|
784
|
+
expect(reservation.matchHash).toBe(
|
|
785
|
+
Idempotency.inputHash({
|
|
786
|
+
destinationTokenKey: 'eip155:4217/erc20:0x20c00000000000000000000014f22ca97301eb73',
|
|
787
|
+
environment: 'production',
|
|
788
|
+
orgId: key.orgId,
|
|
789
|
+
policyKey: '["[\\"enterprise\\",\\"0.050000\\"]","1:1"]',
|
|
790
|
+
providerId: 'relay',
|
|
791
|
+
recipient: body.recipient,
|
|
792
|
+
refundAddress: body.refundAddress,
|
|
793
|
+
sourceChainId: 'eip155:8453',
|
|
794
|
+
sourceTokenKey: 'eip155:8453/erc20:0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
|
|
795
|
+
subsidize: true,
|
|
796
|
+
}),
|
|
797
|
+
)
|
|
798
|
+
|
|
799
|
+
// A new commitment may provision, but cannot adopt the same physical address returned by this provider.
|
|
800
|
+
await RoutesPolicies.save(db, {
|
|
801
|
+
scope,
|
|
802
|
+
version: 0,
|
|
803
|
+
policy: { access: null, subsidyEnabled: true, subsidyPayer: 'tempo', maxAmount: '0.05' },
|
|
804
|
+
})
|
|
805
|
+
const conflict = await postDepositAddress(app, { body, key })
|
|
806
|
+
expect(conflict.status).toBe(409)
|
|
807
|
+
await expect(conflict.json()).resolves.toMatchObject({
|
|
808
|
+
error: { code: 'route_policy_identity_conflict' },
|
|
809
|
+
})
|
|
810
|
+
expect(counts).toContainEqual([
|
|
811
|
+
'routes_policy_denial_count',
|
|
812
|
+
1,
|
|
813
|
+
{ reason: 'route_policy_identity_conflict' },
|
|
814
|
+
])
|
|
815
|
+
expect(inputs.map((input) => input.subsidize)).toEqual([true, true])
|
|
816
|
+
|
|
817
|
+
await RoutesPolicies.save(db, { scope, version: 1, policy: { access: [], suspended: true } })
|
|
818
|
+
const replay = await postDepositAddress(app, { body, key, idempotencyKey })
|
|
819
|
+
expect(replay.status).toBe(200)
|
|
820
|
+
await expect(replay.json()).resolves.toMatchObject({ id: first.id })
|
|
821
|
+
expect(
|
|
822
|
+
(
|
|
823
|
+
await app.request(`/v1/routes/deposit-addresses/${first.id}`, {
|
|
824
|
+
headers: { authorization: `Bearer ${key.token}` },
|
|
825
|
+
})
|
|
826
|
+
).status,
|
|
827
|
+
).toBe(200)
|
|
828
|
+
// The accepted provider checkpoint still conflicts; suspension blocks genuinely fresh instructions.
|
|
829
|
+
const retry = await postDepositAddress(app, { body, key })
|
|
830
|
+
expect(retry.status).toBe(409)
|
|
831
|
+
await expect(retry.json()).resolves.toMatchObject({
|
|
832
|
+
error: { code: 'route_policy_identity_conflict' },
|
|
833
|
+
})
|
|
834
|
+
expect(
|
|
835
|
+
(
|
|
836
|
+
await postDepositAddress(app, {
|
|
837
|
+
body: { ...body, refundAddress: `0x${'83'.repeat(20)}` },
|
|
838
|
+
key,
|
|
839
|
+
})
|
|
840
|
+
).status,
|
|
841
|
+
).toBe(403)
|
|
842
|
+
expect((await RoutesDepositAddresses.get(db, first.id))?.providerState).toEqual(
|
|
843
|
+
stored?.providerState,
|
|
844
|
+
)
|
|
845
|
+
})
|
|
846
|
+
|
|
847
|
+
test('selects a financially eligible deposit provider and reuses its pinned terms', async () => {
|
|
848
|
+
const key = {
|
|
849
|
+
id: 'key_deposit_financial_fallback',
|
|
850
|
+
orgId: 'org_deposit_financial_fallback',
|
|
851
|
+
scopes: ['routes:read', 'routes:write'],
|
|
852
|
+
token: 'secret_deposit_financial_fallback',
|
|
853
|
+
}
|
|
854
|
+
await Organizations.create(db, { id: key.orgId, name: 'Financial fallback' })
|
|
855
|
+
const route = {
|
|
856
|
+
destinationChainId: 'eip155:4217',
|
|
857
|
+
destinationTokenId: 'usdce',
|
|
858
|
+
sourceChainId: 'eip155:8453',
|
|
859
|
+
sourceTokenId: 'usdc',
|
|
860
|
+
}
|
|
861
|
+
await RoutesCatalog.publish(db, {
|
|
862
|
+
...TestRoutes.data,
|
|
863
|
+
routes: TestRoutes.data.routes.map((entry) =>
|
|
864
|
+
entry.providerId === 'rhino' &&
|
|
865
|
+
entry.sourceChainId === route.sourceChainId &&
|
|
866
|
+
entry.sourceTokenId === route.sourceTokenId &&
|
|
867
|
+
entry.destinationTokenId === route.destinationTokenId
|
|
868
|
+
? { ...entry, capabilities: { depositAddress: true } }
|
|
869
|
+
: entry,
|
|
870
|
+
),
|
|
871
|
+
})
|
|
872
|
+
try {
|
|
873
|
+
await RoutesPolicies.save(db, {
|
|
874
|
+
policy: {
|
|
875
|
+
access: null,
|
|
876
|
+
maxAmount: '0.04',
|
|
877
|
+
subsidyEnabled: true,
|
|
878
|
+
subsidyPayer: 'enterprise',
|
|
879
|
+
terms: [{ ...route, providerId: 'rhino', subsidyPayer: 'tempo' }],
|
|
880
|
+
},
|
|
881
|
+
scope: { environment: 'production', orgId: key.orgId },
|
|
882
|
+
version: 0,
|
|
883
|
+
})
|
|
884
|
+
const provisions: string[] = []
|
|
885
|
+
const app = create({
|
|
886
|
+
auth: { keys: [key] },
|
|
887
|
+
defaultChainId: tempoMainnet.id,
|
|
888
|
+
providers: ['relay', 'rhino'].map((id) =>
|
|
889
|
+
RoutesProvider.from({
|
|
890
|
+
...depositAddressProvider(`0x${'86'.repeat(20)}`, () => {
|
|
891
|
+
provisions.push(id)
|
|
892
|
+
}),
|
|
893
|
+
id,
|
|
894
|
+
name: id,
|
|
895
|
+
}),
|
|
896
|
+
),
|
|
897
|
+
routes: {
|
|
898
|
+
depositSubsidyAccount: `0x${'aa'.repeat(20)}`,
|
|
899
|
+
depositSubsidyMaxAmount: '0.05',
|
|
900
|
+
},
|
|
901
|
+
})
|
|
902
|
+
const body = {
|
|
903
|
+
...depositAddressBody,
|
|
904
|
+
destinationToken: 'usdce',
|
|
905
|
+
refundAddress: `0x${'87'.repeat(20)}`,
|
|
906
|
+
sourceChain: 'base',
|
|
907
|
+
sourceToken: 'usdc',
|
|
908
|
+
subsidize: true,
|
|
909
|
+
}
|
|
910
|
+
const withoutSigner = create({
|
|
911
|
+
auth: { keys: [key] },
|
|
912
|
+
defaultChainId: tempoMainnet.id,
|
|
913
|
+
providers: ['relay', 'rhino'].map((id) =>
|
|
914
|
+
RoutesProvider.from({
|
|
915
|
+
...depositAddressProvider(`0x${'86'.repeat(20)}`, () => {
|
|
916
|
+
provisions.push(id)
|
|
917
|
+
}),
|
|
918
|
+
id,
|
|
919
|
+
name: id,
|
|
920
|
+
}),
|
|
921
|
+
),
|
|
922
|
+
routes: { depositSubsidyMaxAmount: '0.05' },
|
|
923
|
+
})
|
|
924
|
+
expect((await postDepositAddress(withoutSigner, { body, key })).status).toBe(403)
|
|
925
|
+
expect(provisions).toEqual([])
|
|
926
|
+
const response = await postDepositAddress(app, { body, key })
|
|
927
|
+
expect(response.status).toBe(200)
|
|
928
|
+
const first = await TestApp.json(response, Routes.schema.createRoutesDepositAddress.Response)
|
|
929
|
+
expect(first.provider.id).toBe('rhino')
|
|
930
|
+
const repeated = await postDepositAddress(app, { body, key })
|
|
931
|
+
expect(repeated.status).toBe(200)
|
|
932
|
+
await expect(repeated.json()).resolves.toMatchObject({ id: first.id })
|
|
933
|
+
expect(provisions).toEqual(['rhino'])
|
|
934
|
+
expect((await RoutesDepositAddresses.get(db, first.id))?.providerState).toMatchObject({
|
|
935
|
+
routePolicy: { maxAmount: '0.04', subsidyPayer: 'tempo' },
|
|
936
|
+
})
|
|
937
|
+
} finally {
|
|
938
|
+
await TestRoutes.publish(db)
|
|
939
|
+
}
|
|
940
|
+
})
|
|
941
|
+
|
|
577
942
|
test('rejects a missing policy, records its bounded reason, and releases the claim', async () => {
|
|
578
943
|
await Organizations.upsert(db, {
|
|
579
944
|
id: routesSubsidyDisabledWriter.orgId,
|
|
@@ -598,6 +963,7 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
598
963
|
body,
|
|
599
964
|
idempotencyKey,
|
|
600
965
|
key: routesSubsidyDisabledWriter,
|
|
966
|
+
policy: false,
|
|
601
967
|
})
|
|
602
968
|
|
|
603
969
|
expect({ body: await rejected.json(), status: rejected.status }).toMatchInlineSnapshot(
|
|
@@ -609,7 +975,7 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
609
975
|
"body": {
|
|
610
976
|
"error": {
|
|
611
977
|
"code": "routes_subsidy_not_enabled",
|
|
612
|
-
"message": "
|
|
978
|
+
"message": "Route subsidies are not enabled for this request. Contact Tempo to enable them, or use an API key from an organization with subsidies enabled.",
|
|
613
979
|
},
|
|
614
980
|
"requestId": Any<String>,
|
|
615
981
|
},
|
|
@@ -620,10 +986,7 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
620
986
|
expect(counts.find(([name]) => name === 'routes_subsidy_request_rejection_count')).toEqual([
|
|
621
987
|
'routes_subsidy_request_rejection_count',
|
|
622
988
|
1,
|
|
623
|
-
{
|
|
624
|
-
reason: 'routes_subsidy_not_enabled',
|
|
625
|
-
routes_environment: 'production',
|
|
626
|
-
},
|
|
989
|
+
{ reason: 'routes_subsidy_not_enabled', routes_environment: 'production' },
|
|
627
990
|
])
|
|
628
991
|
|
|
629
992
|
await enableRoutesSubsidy(routesSubsidyDisabledWriter, 'active')
|
|
@@ -663,7 +1026,7 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
663
1026
|
"body": {
|
|
664
1027
|
"error": {
|
|
665
1028
|
"code": "billing_required",
|
|
666
|
-
"message": "
|
|
1029
|
+
"message": "Active billing is required for enterprise-paid route subsidies. Set up billing for this organization, then retry.",
|
|
667
1030
|
},
|
|
668
1031
|
"requestId": Any<String>,
|
|
669
1032
|
},
|
|
@@ -708,6 +1071,365 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
708
1071
|
expect({ body: await replay.json(), status: replay.status }).toEqual({ body: firstBody, status: 200 }) // prettier-ignore
|
|
709
1072
|
})
|
|
710
1073
|
|
|
1074
|
+
test.each(['99000000', '100000001'])(
|
|
1075
|
+
'preserves the claim after Relay provisions invalid output %s',
|
|
1076
|
+
async (amount) => {
|
|
1077
|
+
const key = {
|
|
1078
|
+
id: `key_rejected_relay_${amount}`,
|
|
1079
|
+
orgId: `org_rejected_relay_${amount}`,
|
|
1080
|
+
scopes: ['routes:write'],
|
|
1081
|
+
token: `secret_rejected_relay_${amount}`,
|
|
1082
|
+
}
|
|
1083
|
+
await enableRoutesSubsidy(key, 'active')
|
|
1084
|
+
let provisions = 0
|
|
1085
|
+
const server = await TestRelay.createServer((_request, response) => {
|
|
1086
|
+
provisions++
|
|
1087
|
+
response.writeHead(200, { 'content-type': 'application/json' })
|
|
1088
|
+
response.end(
|
|
1089
|
+
JSON.stringify({
|
|
1090
|
+
details: {
|
|
1091
|
+
currencyIn: { amount: '100000000' },
|
|
1092
|
+
currencyOut: { amount, minimumAmount: '98000000' },
|
|
1093
|
+
isFixedRate: true,
|
|
1094
|
+
},
|
|
1095
|
+
steps: [
|
|
1096
|
+
{ depositAddress: depositAddressBody.refundAddress, requestId: 'rejected-rate' },
|
|
1097
|
+
],
|
|
1098
|
+
}),
|
|
1099
|
+
)
|
|
1100
|
+
})
|
|
1101
|
+
try {
|
|
1102
|
+
const app = create({
|
|
1103
|
+
auth: { keys: [key] },
|
|
1104
|
+
defaultChainId: tempoMainnet.id,
|
|
1105
|
+
providers: [Relay.relay({ baseUrl: server.url, subsidies: { maxAmount: '100' } })],
|
|
1106
|
+
routes: { depositSubsidyMaxAmount: '5' },
|
|
1107
|
+
})
|
|
1108
|
+
const body = { ...depositAddressBody, subsidize: true }
|
|
1109
|
+
const idempotencyKey = nextDepositAddressKey()
|
|
1110
|
+
const first = await postDepositAddress(app, { body, idempotencyKey, key })
|
|
1111
|
+
const retry = await postDepositAddress(app, { body, idempotencyKey, key })
|
|
1112
|
+
const sibling = await postDepositAddress(app, { body, key })
|
|
1113
|
+
expect({ provisions, statuses: [first.status, retry.status, sibling.status] })
|
|
1114
|
+
.toMatchInlineSnapshot(`
|
|
1115
|
+
{
|
|
1116
|
+
"provisions": 1,
|
|
1117
|
+
"statuses": [
|
|
1118
|
+
502,
|
|
1119
|
+
409,
|
|
1120
|
+
409,
|
|
1121
|
+
],
|
|
1122
|
+
}
|
|
1123
|
+
`)
|
|
1124
|
+
await expect(
|
|
1125
|
+
db.kysely
|
|
1126
|
+
.selectFrom('routes_idempotency_requests')
|
|
1127
|
+
.select(['irrevocable', 'status'])
|
|
1128
|
+
.where('apiKeyId', '=', key.id)
|
|
1129
|
+
.where('keyHash', '=', Idempotency.inputHash(idempotencyKey))
|
|
1130
|
+
.executeTakeFirstOrThrow(),
|
|
1131
|
+
).resolves.toEqual({ irrevocable: true, status: 'pending' })
|
|
1132
|
+
} finally {
|
|
1133
|
+
await server.closeAsync()
|
|
1134
|
+
}
|
|
1135
|
+
},
|
|
1136
|
+
)
|
|
1137
|
+
|
|
1138
|
+
test('provisions subsidized Solana USDC to Tempo USDC.e at a fixed rate', async () => {
|
|
1139
|
+
const key = {
|
|
1140
|
+
id: 'key_solana_usdc_fixed_rate',
|
|
1141
|
+
orgId: 'org_solana_usdc_fixed_rate',
|
|
1142
|
+
scopes: ['routes:write'],
|
|
1143
|
+
token: 'secret_solana_usdc_fixed_rate',
|
|
1144
|
+
}
|
|
1145
|
+
await grantRoutes(key)
|
|
1146
|
+
await RoutesPolicies.save(db, {
|
|
1147
|
+
policy: {
|
|
1148
|
+
access: null,
|
|
1149
|
+
maxAmount: '0.05',
|
|
1150
|
+
subsidyEnabled: true,
|
|
1151
|
+
subsidyPayer: 'tempo',
|
|
1152
|
+
},
|
|
1153
|
+
scope: { environment: 'production', orgId: key.orgId },
|
|
1154
|
+
version: 1,
|
|
1155
|
+
})
|
|
1156
|
+
const requests: Record<string, unknown>[] = []
|
|
1157
|
+
const server = await TestRelay.createServer(async (request, response) => {
|
|
1158
|
+
request.setEncoding('utf8')
|
|
1159
|
+
let payload = ''
|
|
1160
|
+
for await (const chunk of request) payload += chunk
|
|
1161
|
+
requests.push(JSON.parse(payload))
|
|
1162
|
+
response.writeHead(200, { 'content-type': 'application/json' })
|
|
1163
|
+
response.end(
|
|
1164
|
+
JSON.stringify({
|
|
1165
|
+
details: {
|
|
1166
|
+
currencyIn: { amount: '100000000' },
|
|
1167
|
+
currencyOut: { amount: '100000000', minimumAmount: '98000000' },
|
|
1168
|
+
isFixedRate: false,
|
|
1169
|
+
},
|
|
1170
|
+
steps: [
|
|
1171
|
+
{
|
|
1172
|
+
depositAddress: 'So11111111111111111111111111111111111111112',
|
|
1173
|
+
requestId: 'solana-usdc',
|
|
1174
|
+
},
|
|
1175
|
+
],
|
|
1176
|
+
}),
|
|
1177
|
+
)
|
|
1178
|
+
})
|
|
1179
|
+
try {
|
|
1180
|
+
const app = create({
|
|
1181
|
+
auth: { keys: [key] },
|
|
1182
|
+
defaultChainId: tempoMainnet.id,
|
|
1183
|
+
providers: [Relay.relay({ baseUrl: server.url, subsidies: { maxAmount: '100' } })],
|
|
1184
|
+
routes: { depositSubsidyMaxAmount: '5' },
|
|
1185
|
+
})
|
|
1186
|
+
const body = {
|
|
1187
|
+
...depositAddressBody,
|
|
1188
|
+
destinationToken: 'USDC.e',
|
|
1189
|
+
refundAddress: 'So11111111111111111111111111111111111111112',
|
|
1190
|
+
sourceChain: 'solana',
|
|
1191
|
+
sourceToken: 'USDC',
|
|
1192
|
+
subsidize: true,
|
|
1193
|
+
}
|
|
1194
|
+
const first = await TestApp.json(
|
|
1195
|
+
await postDepositAddress(app, { body, key }),
|
|
1196
|
+
Routes.schema.createRoutesDepositAddress.Response,
|
|
1197
|
+
)
|
|
1198
|
+
const reused = await TestApp.json(
|
|
1199
|
+
await postDepositAddress(app, { body, key }),
|
|
1200
|
+
Routes.schema.createRoutesDepositAddress.Response,
|
|
1201
|
+
)
|
|
1202
|
+
expect(reused.id).toBe(first.id)
|
|
1203
|
+
expect(requests).toEqual([
|
|
1204
|
+
expect.objectContaining({
|
|
1205
|
+
amount: '100000000',
|
|
1206
|
+
destinationChainId: 4217,
|
|
1207
|
+
destinationCurrency: '0x20c000000000000000000000b9537d11c60e8b50',
|
|
1208
|
+
fixedRate: '1:1',
|
|
1209
|
+
maxSubsidizationAmount: '50000',
|
|
1210
|
+
originChainId: 792703809,
|
|
1211
|
+
originCurrency: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
|
1212
|
+
refundTo: body.refundAddress,
|
|
1213
|
+
subsidizeFees: true,
|
|
1214
|
+
tradeType: 'EXACT_INPUT',
|
|
1215
|
+
useDepositAddress: true,
|
|
1216
|
+
user: body.refundAddress,
|
|
1217
|
+
}),
|
|
1218
|
+
])
|
|
1219
|
+
expect(first).toMatchObject({
|
|
1220
|
+
address: 'So11111111111111111111111111111111111111112',
|
|
1221
|
+
destinationAmount: { baseUnits: '100000000' },
|
|
1222
|
+
provider: { id: 'relay' },
|
|
1223
|
+
sourceChain: { kind: 'solana' },
|
|
1224
|
+
subsidize: true,
|
|
1225
|
+
})
|
|
1226
|
+
expect((await RoutesDepositAddresses.get(db, first.id))?.providerState).toMatchObject({
|
|
1227
|
+
fixedRate: '1:1',
|
|
1228
|
+
routePolicy: { maxAmount: '0.05', subsidyPayer: 'tempo' },
|
|
1229
|
+
})
|
|
1230
|
+
} finally {
|
|
1231
|
+
await server.closeAsync()
|
|
1232
|
+
}
|
|
1233
|
+
})
|
|
1234
|
+
|
|
1235
|
+
test.each([
|
|
1236
|
+
{ name: 'organization', organization: '0.05', platform: '1' },
|
|
1237
|
+
{ name: 'platform', organization: '1', platform: '0.05' },
|
|
1238
|
+
{ name: 'missing_platform', organization: '1', platform: undefined },
|
|
1239
|
+
])('caps legacy Relay sponsorship by $name', async (scenario) => {
|
|
1240
|
+
const key = {
|
|
1241
|
+
id: `key_legacy_cap_${scenario.name}`,
|
|
1242
|
+
orgId: `org_legacy_cap_${scenario.name}`,
|
|
1243
|
+
scopes: ['routes:write'],
|
|
1244
|
+
token: `secret_legacy_cap_${scenario.name}`,
|
|
1245
|
+
}
|
|
1246
|
+
await enableRoutesSubsidy(key, 'active')
|
|
1247
|
+
await RoutesSubsidies.upsertOrganization(db, {
|
|
1248
|
+
frequency: 'tx',
|
|
1249
|
+
maxAmount: scenario.organization,
|
|
1250
|
+
orgId: key.orgId,
|
|
1251
|
+
})
|
|
1252
|
+
const requests: Record<string, unknown>[] = []
|
|
1253
|
+
const server = await TestRelay.createServer(async (request, response) => {
|
|
1254
|
+
request.setEncoding('utf8')
|
|
1255
|
+
let payload = ''
|
|
1256
|
+
for await (const chunk of request) payload += chunk
|
|
1257
|
+
requests.push(JSON.parse(payload))
|
|
1258
|
+
response.writeHead(200, { 'content-type': 'application/json' })
|
|
1259
|
+
response.end(
|
|
1260
|
+
JSON.stringify({
|
|
1261
|
+
details: {
|
|
1262
|
+
currencyIn: { amount: '100000000' },
|
|
1263
|
+
currencyOut: { amount: '100000000', minimumAmount: '98000000' },
|
|
1264
|
+
isFixedRate: true,
|
|
1265
|
+
},
|
|
1266
|
+
steps: [
|
|
1267
|
+
{
|
|
1268
|
+
depositAddress: Hash.keccak256(Hex.fromString(`${key.id}_${requests.length}`)).slice(
|
|
1269
|
+
0,
|
|
1270
|
+
42,
|
|
1271
|
+
),
|
|
1272
|
+
requestId: `capped-rate-${requests.length}`,
|
|
1273
|
+
},
|
|
1274
|
+
],
|
|
1275
|
+
}),
|
|
1276
|
+
)
|
|
1277
|
+
})
|
|
1278
|
+
try {
|
|
1279
|
+
const options = {
|
|
1280
|
+
auth: { keys: [key] },
|
|
1281
|
+
defaultChainId: tempoMainnet.id,
|
|
1282
|
+
providers: [Relay.relay({ baseUrl: server.url, subsidies: { maxAmount: '100' } })],
|
|
1283
|
+
}
|
|
1284
|
+
const app = create({
|
|
1285
|
+
...options,
|
|
1286
|
+
routes: { depositSubsidyMaxAmount: scenario.platform },
|
|
1287
|
+
})
|
|
1288
|
+
const body = {
|
|
1289
|
+
...depositAddressBody,
|
|
1290
|
+
refundAddress: `0x${'93'.repeat(20)}`,
|
|
1291
|
+
sourceChain: 'base',
|
|
1292
|
+
sourceToken: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
|
|
1293
|
+
subsidize: true,
|
|
1294
|
+
}
|
|
1295
|
+
const response = await postDepositAddress(app, { body, key })
|
|
1296
|
+
if (scenario.platform === undefined) {
|
|
1297
|
+
expect(response.status).toBe(404)
|
|
1298
|
+
expect(requests).toEqual([])
|
|
1299
|
+
return
|
|
1300
|
+
}
|
|
1301
|
+
expect(response.status).toBe(200)
|
|
1302
|
+
const first = await TestApp.json(response, Routes.schema.createRoutesDepositAddress.Response)
|
|
1303
|
+
const stored = await RoutesDepositAddresses.get(db, first.id)
|
|
1304
|
+
const reused = await TestApp.json(
|
|
1305
|
+
await postDepositAddress(app, { body, key }),
|
|
1306
|
+
Routes.schema.createRoutesDepositAddress.Response,
|
|
1307
|
+
)
|
|
1308
|
+
expect(reused.id).toBe(first.id)
|
|
1309
|
+
expect(requests).toHaveLength(1)
|
|
1310
|
+
expect(requests[0]).toMatchObject({
|
|
1311
|
+
fixedRate: '1:1',
|
|
1312
|
+
maxSubsidizationAmount: '50000',
|
|
1313
|
+
subsidizeFees: true,
|
|
1314
|
+
})
|
|
1315
|
+
|
|
1316
|
+
if (scenario.name === 'organization')
|
|
1317
|
+
await RoutesSubsidies.upsertOrganization(db, {
|
|
1318
|
+
frequency: 'tx',
|
|
1319
|
+
maxAmount: '0.025',
|
|
1320
|
+
orgId: key.orgId,
|
|
1321
|
+
})
|
|
1322
|
+
const capped = create({
|
|
1323
|
+
...options,
|
|
1324
|
+
routes: {
|
|
1325
|
+
depositSubsidyMaxAmount: scenario.name === 'platform' ? '0.025' : scenario.platform,
|
|
1326
|
+
},
|
|
1327
|
+
})
|
|
1328
|
+
const replacement = await TestApp.json(
|
|
1329
|
+
await postDepositAddress(capped, { body, key }),
|
|
1330
|
+
Routes.schema.createRoutesDepositAddress.Response,
|
|
1331
|
+
)
|
|
1332
|
+
expect(replacement.id).not.toBe(first.id)
|
|
1333
|
+
expect(requests).toHaveLength(2)
|
|
1334
|
+
expect(requests[1]).toMatchObject({
|
|
1335
|
+
fixedRate: '1:1',
|
|
1336
|
+
maxSubsidizationAmount: '25000',
|
|
1337
|
+
subsidizeFees: true,
|
|
1338
|
+
})
|
|
1339
|
+
expect(await RoutesDepositAddresses.get(db, first.id)).toEqual(stored)
|
|
1340
|
+
if (scenario.name === 'organization')
|
|
1341
|
+
await RoutesSubsidies.upsertOrganization(db, {
|
|
1342
|
+
frequency: 'tx',
|
|
1343
|
+
maxAmount: scenario.organization,
|
|
1344
|
+
orgId: key.orgId,
|
|
1345
|
+
})
|
|
1346
|
+
const restored = await TestApp.json(
|
|
1347
|
+
await postDepositAddress(app, { body, key }),
|
|
1348
|
+
Routes.schema.createRoutesDepositAddress.Response,
|
|
1349
|
+
)
|
|
1350
|
+
expect(restored.id).toBe(first.id)
|
|
1351
|
+
expect(requests).toHaveLength(2)
|
|
1352
|
+
} finally {
|
|
1353
|
+
await server.closeAsync()
|
|
1354
|
+
}
|
|
1355
|
+
})
|
|
1356
|
+
|
|
1357
|
+
test.each(['AMOUNT_TOO_LOW', 'NO_ROUTE'])(
|
|
1358
|
+
'releases subsidized claims after a definitive Relay rejection %s',
|
|
1359
|
+
async (errorCode) => {
|
|
1360
|
+
const key = {
|
|
1361
|
+
id: `key_relay_rejection_${errorCode}`,
|
|
1362
|
+
orgId: `org_relay_rejection_${errorCode}`,
|
|
1363
|
+
scopes: ['routes:write'],
|
|
1364
|
+
token: `secret_relay_rejection_${errorCode}`,
|
|
1365
|
+
}
|
|
1366
|
+
await enableRoutesSubsidy(key, 'active')
|
|
1367
|
+
let requests = 0
|
|
1368
|
+
const server = await TestRelay.createServer((request, response) => {
|
|
1369
|
+
request.resume()
|
|
1370
|
+
requests += 1
|
|
1371
|
+
response.writeHead(requests < 4 ? 400 : 200, { 'content-type': 'application/json' })
|
|
1372
|
+
response.end(
|
|
1373
|
+
JSON.stringify(
|
|
1374
|
+
requests < 4
|
|
1375
|
+
? { errorCode }
|
|
1376
|
+
: {
|
|
1377
|
+
details: {
|
|
1378
|
+
currencyIn: { amount: '100000000' },
|
|
1379
|
+
currencyOut: { amount: '100000000', minimumAmount: '98000000' },
|
|
1380
|
+
isFixedRate: true,
|
|
1381
|
+
},
|
|
1382
|
+
steps: [
|
|
1383
|
+
{
|
|
1384
|
+
depositAddress: Hash.keccak256(Hex.fromString(key.id)).slice(0, 42),
|
|
1385
|
+
requestId: `recovered-${errorCode}`,
|
|
1386
|
+
},
|
|
1387
|
+
],
|
|
1388
|
+
},
|
|
1389
|
+
),
|
|
1390
|
+
)
|
|
1391
|
+
})
|
|
1392
|
+
try {
|
|
1393
|
+
const app = create({
|
|
1394
|
+
auth: { keys: [key] },
|
|
1395
|
+
defaultChainId: tempoMainnet.id,
|
|
1396
|
+
providers: [Relay.relay({ baseUrl: server.url, subsidies: { maxAmount: '5' } })],
|
|
1397
|
+
routes: { depositSubsidyMaxAmount: '5' },
|
|
1398
|
+
})
|
|
1399
|
+
const body = {
|
|
1400
|
+
...depositAddressBody,
|
|
1401
|
+
refundAddress: `0x${'95'.repeat(20)}`,
|
|
1402
|
+
sourceChain: 'base',
|
|
1403
|
+
sourceToken: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
|
|
1404
|
+
subsidize: true,
|
|
1405
|
+
}
|
|
1406
|
+
const idempotencyKey = nextDepositAddressKey()
|
|
1407
|
+
for (const requestKey of [idempotencyKey, idempotencyKey, nextDepositAddressKey()]) {
|
|
1408
|
+
const response = await postDepositAddress(app, { body, idempotencyKey: requestKey, key })
|
|
1409
|
+
expect(response.status).toBe(errorCode === 'AMOUNT_TOO_LOW' ? 400 : 404)
|
|
1410
|
+
expect((await response.json()).error.code).toBe(
|
|
1411
|
+
errorCode === 'AMOUNT_TOO_LOW' ? 'amount_too_low' : 'quote_not_available',
|
|
1412
|
+
)
|
|
1413
|
+
expect(
|
|
1414
|
+
await db.kysely
|
|
1415
|
+
.selectFrom('routes_idempotency_requests')
|
|
1416
|
+
.select(['irrevocable', 'status'])
|
|
1417
|
+
.where('apiKeyId', '=', key.id)
|
|
1418
|
+
.execute(),
|
|
1419
|
+
).toMatchInlineSnapshot(`[]`)
|
|
1420
|
+
}
|
|
1421
|
+
const recovered = await postDepositAddress(app, { body, idempotencyKey, key })
|
|
1422
|
+
expect(recovered.status).toMatchInlineSnapshot(`200`)
|
|
1423
|
+
expect(requests).toMatchInlineSnapshot(`4`)
|
|
1424
|
+
expect(
|
|
1425
|
+
await TestApp.json(recovered, Routes.schema.createRoutesDepositAddress.Response),
|
|
1426
|
+
).toMatchObject({ provider: { id: 'relay' }, subsidize: true })
|
|
1427
|
+
} finally {
|
|
1428
|
+
await server.closeAsync()
|
|
1429
|
+
}
|
|
1430
|
+
},
|
|
1431
|
+
)
|
|
1432
|
+
|
|
711
1433
|
test('preserves a subsidized claim after an ambiguous provider failure', async () => {
|
|
712
1434
|
await enableRoutesSubsidy(routesSubsidyReplayWriter, 'active')
|
|
713
1435
|
const app = transfersApp({
|
|
@@ -745,7 +1467,7 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
745
1467
|
).resolves.toEqual({ irrevocable: true, status: 'pending' })
|
|
746
1468
|
})
|
|
747
1469
|
|
|
748
|
-
test('resumes
|
|
1470
|
+
test('resumes a legacy provisioned subsidy after policy adoption without replaying stale quote terms', async () => {
|
|
749
1471
|
await Organizations.upsert(db, {
|
|
750
1472
|
id: routesSubsidyResumeWriter.orgId,
|
|
751
1473
|
name: 'Routes subsidy resume',
|
|
@@ -813,11 +1535,19 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
813
1535
|
.where('keyHash', '=', identity.keyHash)
|
|
814
1536
|
.execute()
|
|
815
1537
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1538
|
+
await RoutesPolicies.save(db, {
|
|
1539
|
+
scope: { orgId: routesSubsidyResumeWriter.orgId, environment: 'production' },
|
|
1540
|
+
version: 0,
|
|
1541
|
+
policy: { access: [], suspended: true, subsidyEnabled: false },
|
|
820
1542
|
})
|
|
1543
|
+
const response = await postDepositAddress(
|
|
1544
|
+
transfersApp({ metrics, providers: [], routes: { depositSubsidyAccount: undefined } }),
|
|
1545
|
+
{
|
|
1546
|
+
body,
|
|
1547
|
+
idempotencyKey,
|
|
1548
|
+
key: routesSubsidyResumeWriter,
|
|
1549
|
+
},
|
|
1550
|
+
)
|
|
821
1551
|
|
|
822
1552
|
expect(response.status).toBe(200)
|
|
823
1553
|
const responseBody = await TestApp.json(
|
|
@@ -826,6 +1556,9 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
826
1556
|
)
|
|
827
1557
|
expect(responseBody.subsidize).toBe(true)
|
|
828
1558
|
expect(responseBody).not.toHaveProperty('quote')
|
|
1559
|
+
const recovered = await RoutesDepositAddresses.get(db, responseBody.id)
|
|
1560
|
+
expect(recovered).toBeDefined()
|
|
1561
|
+
expect(recovered!.providerState).toBeNull()
|
|
829
1562
|
expect(
|
|
830
1563
|
histograms.filter(
|
|
831
1564
|
([name]) =>
|
|
@@ -836,6 +1569,95 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
836
1569
|
).toEqual([])
|
|
837
1570
|
})
|
|
838
1571
|
|
|
1572
|
+
test('waits for a settler before persisting a resumed non-Relay subsidy', async () => {
|
|
1573
|
+
await Organizations.upsert(db, {
|
|
1574
|
+
id: routesSubsidyResumeWriter.orgId,
|
|
1575
|
+
name: 'Routes subsidy resume',
|
|
1576
|
+
})
|
|
1577
|
+
const body = {
|
|
1578
|
+
...depositAddressBody,
|
|
1579
|
+
recipient: `0x${'7b'.repeat(20)}`,
|
|
1580
|
+
subsidize: true,
|
|
1581
|
+
}
|
|
1582
|
+
const idempotencyKey = nextDepositAddressKey()
|
|
1583
|
+
const identity = {
|
|
1584
|
+
apiKeyId: routesSubsidyResumeWriter.id,
|
|
1585
|
+
keyHash: Idempotency.inputHash(idempotencyKey),
|
|
1586
|
+
requestHash: Idempotency.inputHash({
|
|
1587
|
+
body,
|
|
1588
|
+
endpoint: 'POST /v1/routes/deposit-addresses',
|
|
1589
|
+
}),
|
|
1590
|
+
}
|
|
1591
|
+
const claim = await RoutesIdempotencyTable.claim(db, {
|
|
1592
|
+
...identity,
|
|
1593
|
+
irrevocable: true,
|
|
1594
|
+
operation: RoutesIdempotencyTable.operations.depositAddress,
|
|
1595
|
+
orgId: routesSubsidyResumeWriter.orgId,
|
|
1596
|
+
ttlMs: 60_000,
|
|
1597
|
+
})
|
|
1598
|
+
if (claim.type !== 'claimed') throw new Error('Expected a fresh idempotency claim.')
|
|
1599
|
+
const snapshot = TestRoutes.depositAddressSnapshot({
|
|
1600
|
+
address: 'TG6rrQgTkf3e85cYzqtD2q8fYuJDdM99Rx',
|
|
1601
|
+
provider: { id: 'rhino', name: 'Rhino' },
|
|
1602
|
+
recipient: body.recipient,
|
|
1603
|
+
subsidize: true,
|
|
1604
|
+
})
|
|
1605
|
+
await RoutesIdempotencyTable.checkpoint(db, {
|
|
1606
|
+
apiKeyId: identity.apiKeyId,
|
|
1607
|
+
createdAt: claim.createdAt,
|
|
1608
|
+
keyHash: identity.keyHash,
|
|
1609
|
+
replayTtlMs: 86_400_000,
|
|
1610
|
+
response: JSON.stringify({
|
|
1611
|
+
candidate: {
|
|
1612
|
+
destinationChain: snapshot.destinationChain,
|
|
1613
|
+
destinationToken: snapshot.destinationToken,
|
|
1614
|
+
sourceChain: snapshot.sourceChain,
|
|
1615
|
+
sourceToken: snapshot.sourceToken,
|
|
1616
|
+
},
|
|
1617
|
+
provider: snapshot.provider,
|
|
1618
|
+
recipient: snapshot.recipient,
|
|
1619
|
+
refundAddress: snapshot.refundAddress,
|
|
1620
|
+
result: {
|
|
1621
|
+
address: snapshot.address,
|
|
1622
|
+
correlation: { requestId: 'rhino_resume_request' },
|
|
1623
|
+
destinationAmount: '1000000',
|
|
1624
|
+
destinationAmountMin: '1000000',
|
|
1625
|
+
fees: [],
|
|
1626
|
+
sampledAt: '2026-08-04T00:00:00.000Z',
|
|
1627
|
+
},
|
|
1628
|
+
}),
|
|
1629
|
+
})
|
|
1630
|
+
|
|
1631
|
+
const count = await RoutesDepositAddresses.countByOrganization(
|
|
1632
|
+
db,
|
|
1633
|
+
routesSubsidyResumeWriter.orgId,
|
|
1634
|
+
)
|
|
1635
|
+
const unavailable = await postDepositAddress(
|
|
1636
|
+
transfersApp({ providers: [], routes: { depositSubsidyAccount: undefined } }),
|
|
1637
|
+
{ body, idempotencyKey, key: routesSubsidyResumeWriter },
|
|
1638
|
+
)
|
|
1639
|
+
expect(await unavailable.json()).toMatchObject({
|
|
1640
|
+
error: { code: 'deposit_address_not_available' },
|
|
1641
|
+
})
|
|
1642
|
+
expect(unavailable.status).toBe(404)
|
|
1643
|
+
expect(
|
|
1644
|
+
await RoutesDepositAddresses.countByOrganization(db, routesSubsidyResumeWriter.orgId),
|
|
1645
|
+
).toBe(count)
|
|
1646
|
+
|
|
1647
|
+
const resumed = await postDepositAddress(transfersApp({ providers: [] }), {
|
|
1648
|
+
body,
|
|
1649
|
+
idempotencyKey,
|
|
1650
|
+
key: routesSubsidyResumeWriter,
|
|
1651
|
+
})
|
|
1652
|
+
expect(resumed.status).toBe(200)
|
|
1653
|
+
expect(
|
|
1654
|
+
await TestApp.json(resumed, Routes.schema.createRoutesDepositAddress.Response),
|
|
1655
|
+
).toMatchObject({
|
|
1656
|
+
provider: { id: 'rhino' },
|
|
1657
|
+
subsidize: true,
|
|
1658
|
+
})
|
|
1659
|
+
})
|
|
1660
|
+
|
|
839
1661
|
test('creates a reusable address and persists private provider correlation', async () => {
|
|
840
1662
|
await Organizations.upsert(db, { id: routesWriter.orgId, name: 'Routes' })
|
|
841
1663
|
let seen: RoutesProvider.createDepositAddress.Parameters | undefined
|
|
@@ -1052,6 +1874,7 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
1052
1874
|
|
|
1053
1875
|
test('stores subsidized addresses as a separate reusable identity', async () => {
|
|
1054
1876
|
await enableRoutesSubsidy(routesSubsidyWriter, 'active')
|
|
1877
|
+
await grantRoutes(routesSubsidyWriter)
|
|
1055
1878
|
const inputs: RoutesProvider.createDepositAddress.Parameters[] = []
|
|
1056
1879
|
const app = transfersApp({
|
|
1057
1880
|
providers: [
|
|
@@ -1059,7 +1882,10 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
1059
1882
|
inputs.push(input)
|
|
1060
1883
|
return {
|
|
1061
1884
|
address: inputs.length === 1 ? `0x${'31'.repeat(20)}` : `0x${'32'.repeat(20)}`,
|
|
1062
|
-
correlation: {
|
|
1885
|
+
correlation: {
|
|
1886
|
+
...(input.subsidize ? { fixedRate: '1:1' as const } : {}),
|
|
1887
|
+
requestId: `relay_request_${inputs.length}`,
|
|
1888
|
+
},
|
|
1063
1889
|
destinationAmount: '1000000',
|
|
1064
1890
|
destinationAmountMin: '1000000',
|
|
1065
1891
|
fees: [],
|
|
@@ -1085,26 +1911,45 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
1085
1911
|
}),
|
|
1086
1912
|
Routes.schema.createRoutesDepositAddress.Response,
|
|
1087
1913
|
)
|
|
1914
|
+
const subsidizedRecord = await RoutesDepositAddresses.get(db, subsidized.id)
|
|
1088
1915
|
|
|
1089
1916
|
expect({
|
|
1090
1917
|
distinct: standard.id !== subsidized.id,
|
|
1091
|
-
inputs: inputs.map(({ subsidize }) => subsidize),
|
|
1918
|
+
inputs: inputs.map(({ subsidize, subsidyMaxAmount }) => ({ subsidize, subsidyMaxAmount })),
|
|
1092
1919
|
standard: standard.subsidize,
|
|
1093
1920
|
subsidized: subsidized.subsidize,
|
|
1094
1921
|
}).toMatchInlineSnapshot(`
|
|
1095
1922
|
{
|
|
1096
1923
|
"distinct": true,
|
|
1097
1924
|
"inputs": [
|
|
1098
|
-
|
|
1099
|
-
|
|
1925
|
+
{
|
|
1926
|
+
"subsidize": false,
|
|
1927
|
+
"subsidyMaxAmount": undefined,
|
|
1928
|
+
},
|
|
1929
|
+
{
|
|
1930
|
+
"subsidize": true,
|
|
1931
|
+
"subsidyMaxAmount": "5",
|
|
1932
|
+
},
|
|
1100
1933
|
],
|
|
1101
1934
|
"standard": false,
|
|
1102
1935
|
"subsidized": true,
|
|
1103
1936
|
}
|
|
1104
1937
|
`)
|
|
1938
|
+
expect(subsidizedRecord?.providerState).toHaveProperty('routePolicy')
|
|
1939
|
+
expect(subsidizedRecord?.providerState).toHaveProperty('routePolicyKey')
|
|
1940
|
+
expect(subsidizedRecord?.providerState).toHaveProperty('fixedRate', '1:1')
|
|
1941
|
+
const reused = await TestApp.json(
|
|
1942
|
+
await postDepositAddress(app, {
|
|
1943
|
+
body: { ...body, subsidize: true },
|
|
1944
|
+
key: routesSubsidyWriter,
|
|
1945
|
+
}),
|
|
1946
|
+
Routes.schema.createRoutesDepositAddress.Response,
|
|
1947
|
+
)
|
|
1948
|
+
expect(reused.id).toBe(subsidized.id)
|
|
1949
|
+
expect(inputs).toHaveLength(2)
|
|
1105
1950
|
})
|
|
1106
1951
|
|
|
1107
|
-
test('
|
|
1952
|
+
test('checks policy before reusing a subsidized address under a fresh API key', async () => {
|
|
1108
1953
|
await enableRoutesSubsidy(routesSubsidyWriter, 'active')
|
|
1109
1954
|
let calls = 0
|
|
1110
1955
|
const app = transfersApp({
|
|
@@ -1125,12 +1970,15 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
1125
1970
|
await postDepositAddress(app, { body, key: routesSubsidyWriter }),
|
|
1126
1971
|
Routes.schema.createRoutesDepositAddress.Response,
|
|
1127
1972
|
)
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
)
|
|
1132
|
-
|
|
1133
|
-
|
|
1973
|
+
await RoutesSubsidies.removeOrganization(db, routesSubsidyWriter.orgId)
|
|
1974
|
+
const second = await postDepositAddress(app, { body, key: routesSubsidyWriterSecond })
|
|
1975
|
+
|
|
1976
|
+
expect(first.id).toBeDefined()
|
|
1977
|
+
expect({ body: await second.json(), calls, status: second.status }).toMatchObject({
|
|
1978
|
+
body: { error: { code: 'routes_subsidy_not_enabled' } },
|
|
1979
|
+
calls: 1,
|
|
1980
|
+
status: 403,
|
|
1981
|
+
})
|
|
1134
1982
|
})
|
|
1135
1983
|
|
|
1136
1984
|
test('normalizes EVM accounts before provisioning and matching', async () => {
|
|
@@ -1253,6 +2101,79 @@ describe('POST /routes/deposit-addresses', () => {
|
|
|
1253
2101
|
expect(calls).toBe(0)
|
|
1254
2102
|
})
|
|
1255
2103
|
|
|
2104
|
+
test('returns 409 on initial completion and retry when different terms win during provisioning', async () => {
|
|
2105
|
+
const key: TestApp.kvStore.Key = {
|
|
2106
|
+
id: 'key_policy_insert_race',
|
|
2107
|
+
orgId: 'org_policy_insert_race',
|
|
2108
|
+
scopes: ['routes:read', 'routes:write'],
|
|
2109
|
+
token: 'secret_policy_insert_race',
|
|
2110
|
+
}
|
|
2111
|
+
await grantRoutes(key)
|
|
2112
|
+
const scope = { environment: 'production', orgId: key.orgId } as const
|
|
2113
|
+
const policy = {
|
|
2114
|
+
access: null,
|
|
2115
|
+
maxAmount: '0.04',
|
|
2116
|
+
subsidyEnabled: true,
|
|
2117
|
+
subsidyPayer: 'tempo',
|
|
2118
|
+
} as const
|
|
2119
|
+
await RoutesPolicies.save(db, { policy, scope, version: 1 })
|
|
2120
|
+
const entered = Promise.withResolvers<void>()
|
|
2121
|
+
const release = Promise.withResolvers<void>()
|
|
2122
|
+
let provisions = 0
|
|
2123
|
+
const app = create({
|
|
2124
|
+
auth: { keys: [key] },
|
|
2125
|
+
defaultChainId: tempoMainnet.id,
|
|
2126
|
+
providers: [
|
|
2127
|
+
depositAddressProvider(`0x${'84'.repeat(20)}`, async () => {
|
|
2128
|
+
provisions++
|
|
2129
|
+
if (provisions === 1) {
|
|
2130
|
+
entered.resolve()
|
|
2131
|
+
await release.promise
|
|
2132
|
+
}
|
|
2133
|
+
}),
|
|
2134
|
+
],
|
|
2135
|
+
routes: { depositSubsidyMaxAmount: '0.05' },
|
|
2136
|
+
})
|
|
2137
|
+
const body = {
|
|
2138
|
+
...depositAddressBody,
|
|
2139
|
+
refundAddress: `0x${'85'.repeat(20)}`,
|
|
2140
|
+
sourceChain: 'base',
|
|
2141
|
+
sourceToken: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
|
|
2142
|
+
subsidize: true,
|
|
2143
|
+
}
|
|
2144
|
+
const idempotencyKey = nextDepositAddressKey()
|
|
2145
|
+
const pending = postDepositAddress(app, { body, idempotencyKey, key })
|
|
2146
|
+
await entered.promise
|
|
2147
|
+
try {
|
|
2148
|
+
await RoutesPolicies.save(db, {
|
|
2149
|
+
policy: { ...policy, maxAmount: '0.05' },
|
|
2150
|
+
scope,
|
|
2151
|
+
version: 2,
|
|
2152
|
+
})
|
|
2153
|
+
expect((await postDepositAddress(app, { body, key })).status).toBe(200)
|
|
2154
|
+
} finally {
|
|
2155
|
+
release.resolve()
|
|
2156
|
+
}
|
|
2157
|
+
for (const response of [
|
|
2158
|
+
await pending,
|
|
2159
|
+
await postDepositAddress(app, { body, idempotencyKey, key }),
|
|
2160
|
+
]) {
|
|
2161
|
+
expect(response.status).toBe(409)
|
|
2162
|
+
await expect(response.json()).resolves.toMatchObject({
|
|
2163
|
+
error: { code: 'route_policy_identity_conflict' },
|
|
2164
|
+
})
|
|
2165
|
+
}
|
|
2166
|
+
expect(provisions).toBe(2)
|
|
2167
|
+
expect(await RoutesDepositAddresses.countByOrganization(db, key.orgId)).toBe(1)
|
|
2168
|
+
expect(
|
|
2169
|
+
await db.kysely
|
|
2170
|
+
.selectFrom('routes_deposit_addresses')
|
|
2171
|
+
.select('providerState')
|
|
2172
|
+
.where('orgId', '=', key.orgId)
|
|
2173
|
+
.executeTakeFirstOrThrow(),
|
|
2174
|
+
).toMatchObject({ providerState: { routePolicy: { maxAmount: '0.05' } } })
|
|
2175
|
+
})
|
|
2176
|
+
|
|
1256
2177
|
test('retains the provider checkpoint when address persistence fails', async () => {
|
|
1257
2178
|
const address = 'TKJebW6SdoJhgkKU13BeKCbrMzT6HeQhJ6'
|
|
1258
2179
|
const body = { ...depositAddressBody, recipient: `0x${'66'.repeat(20)}` }
|
|
@@ -2829,19 +3750,22 @@ function nextDepositAddressKey() {
|
|
|
2829
3750
|
return `routes_deposit_address_${depositAddressIdempotencySequence++}`
|
|
2830
3751
|
}
|
|
2831
3752
|
|
|
2832
|
-
function postDepositAddress(
|
|
3753
|
+
async function postDepositAddress(
|
|
2833
3754
|
app: ReturnType<typeof create>,
|
|
2834
3755
|
options: {
|
|
2835
3756
|
body?: unknown
|
|
2836
3757
|
headers?: Record<string, string> | undefined
|
|
2837
3758
|
idempotencyKey?: string | undefined
|
|
2838
|
-
key?:
|
|
3759
|
+
key?: TestApp.kvStore.Key | undefined
|
|
3760
|
+
policy?: boolean | undefined
|
|
2839
3761
|
} = {},
|
|
2840
3762
|
) {
|
|
3763
|
+
const key = options.key ?? routesWriter
|
|
3764
|
+
if (options.policy === true && options.headers === undefined) await grantRoutes(key)
|
|
2841
3765
|
return app.request('/v1/routes/deposit-addresses', {
|
|
2842
3766
|
body: JSON.stringify(options.body ?? depositAddressBody),
|
|
2843
3767
|
headers: options.headers ?? {
|
|
2844
|
-
authorization: `Bearer ${
|
|
3768
|
+
authorization: `Bearer ${key.token}`,
|
|
2845
3769
|
'content-type': 'application/json',
|
|
2846
3770
|
'idempotency-key': options.idempotencyKey ?? nextDepositAddressKey(),
|
|
2847
3771
|
},
|
|
@@ -2870,6 +3794,9 @@ function depositAddressProvider(
|
|
|
2870
3794
|
address,
|
|
2871
3795
|
correlation: {
|
|
2872
3796
|
checkEndpoint: '/intents/status/v3?requestId=relay_request_1',
|
|
3797
|
+
...(input.subsidize && input.candidate.provider.id === 'relay'
|
|
3798
|
+
? { fixedRate: '1:1' as const }
|
|
3799
|
+
: {}),
|
|
2873
3800
|
requestId: 'relay_request_1',
|
|
2874
3801
|
},
|
|
2875
3802
|
destinationAmount: '1000000',
|
|
@@ -3078,6 +4005,11 @@ const routesSubsidyRaceWriter = {
|
|
|
3078
4005
|
function transfersApp(options: TestApp.create.Options = {}) {
|
|
3079
4006
|
return create({
|
|
3080
4007
|
...options,
|
|
4008
|
+
routes: {
|
|
4009
|
+
depositSubsidyAccount: `0x${'aa'.repeat(20)}`,
|
|
4010
|
+
depositSubsidyMaxAmount: '5',
|
|
4011
|
+
...options.routes,
|
|
4012
|
+
},
|
|
3081
4013
|
auth: {
|
|
3082
4014
|
keys: [
|
|
3083
4015
|
TestApp.key,
|
|
@@ -3136,6 +4068,18 @@ function as(key: { token: string }) {
|
|
|
3136
4068
|
return { headers: { authorization: `Bearer ${key.token}` } } as const
|
|
3137
4069
|
}
|
|
3138
4070
|
|
|
4071
|
+
async function grantRoutes(key: TestApp.kvStore.Key) {
|
|
4072
|
+
await Organizations.upsert(db, { id: key.orgId, name: key.orgId })
|
|
4073
|
+
const scope = { environment: 'production' as const, orgId: key.orgId }
|
|
4074
|
+
const current = await RoutesPolicies.get(db, scope)
|
|
4075
|
+
if (current?.policy.access === null) return
|
|
4076
|
+
await RoutesPolicies.save(db, {
|
|
4077
|
+
policy: { ...current?.policy, access: null },
|
|
4078
|
+
scope,
|
|
4079
|
+
version: current?.version ?? 0,
|
|
4080
|
+
})
|
|
4081
|
+
}
|
|
4082
|
+
|
|
3139
4083
|
let seedDepositAddressSecond = 0
|
|
3140
4084
|
|
|
3141
4085
|
function seedDepositAddress(input: Partial<RoutesDepositAddress.create.Input> = {}) {
|