feat: add Slush DeFi Quickstart Provider API for margin lending#875
feat: add Slush DeFi Quickstart Provider API for margin lending#875
Conversation
Implement the Slush DeFi Quickstart Provider API (OpenAPI v1.1.0) as a new /slush/v1/ route module in the deepbook-server, exposing DeepBook margin lending pools as LENDING strategies. Key design decisions: - Reuses existing margin_metrics infrastructure (margin_pool_snapshots) for pool state data (TVL, utilization, depositor counts) rather than making duplicate RPC calls - Fetches APY from Abyss Protocol vault indexer with 5-minute caching - Correctly constructs deposit and withdraw PTBs with proper arguments and TransferObjects commands for SupplierCap and returned Coin objects - Returns 501 Not Implemented for withdraw/cancel since DeepBook margin withdrawals are instant (no hold period) New files: - slush/types.rs: Request/response types matching OpenAPI spec exactly - slush/config.rs: Pool configuration and vault mapping (env-driven) - slush/apy.rs: Abyss Protocol APY client with TTL cache - slush/ptb.rs: PTB construction for deposit and withdraw transactions - slush/handlers.rs: Axum handlers for all 9 endpoints - slush/routes.rs: Route definitions nested under /slush/v1/ Configuration via env vars: - MARGIN_REGISTRY_ID: Object ID of the MarginRegistry shared object - SLUSH_VAULT_MAPPING: JSON mapping of pool IDs to Abyss vault addresses Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@claude review this |
|
test comment to verify gh works |
Code Review: feat/slush-defi-quickstart-apiThanks for implementing the Slush DeFi Quickstart Provider API. The overall structure is clean and the PTB fixes look correct. Here are my findings organized by severity. Critical Issues1. Silent amount parse failure in withdraw handler - potential fund-loss footgun In handlers.rs, if principal.amount fails to parse (e.g. a decimal like "1.5", a negative, or out-of-range value), this silently becomes None, meaning withdraw everything. Should return a 422 error on failure, not silently promote to a full withdrawal. 2. Integer overflow risk in build_positions Both the summation and subtraction of i64 amounts can overflow silently in release builds. Use saturating_add/saturating_sub or checked_* variants instead of .sum() and -. Major Issues3. N+1 queries in build_strategies count_active_depositors is called inside the pool loop - one DB round-trip per pool. This should be rewritten as a single aggregated query returning counts for all pools at once, similar to how get_latest_margin_pool_snapshots handles multi-pool state. 4. APY cache stampede In apy.rs, the read lock is dropped before the write lock is acquired. During the gap, N concurrent requests can all observe a cache miss and all make outbound calls to the Abyss API in parallel. Consider a tokio::sync::Mutex-protected in-flight set to deduplicate concurrent fetches for the same vault address. 5. tvl_usd is not in USD - it is token units Dividing the raw on-chain amount by decimals gives a token quantity, not a USD value. This is correct only for stablecoins with a 1:1 USD peg. For other assets the value will be wrong. Either rename the field to tvl_native or integrate a price oracle. 6. OR-based join in get_all_margin_pools prevents index usage The three-way OR on asset_type prevents the query planner from using an index. Consider normalizing asset types at ingest time so a simple equality join works. 7. Missing input validation on public endpoints
Moderate Issues8. Silent config parsing failure for SLUSH_VAULT_MAPPING Invalid JSON silently becomes an empty map - all APY values will be 0.0 with no log output. Add a tracing::warn! (or return a startup error) when the env var is set but unparseable. 9. get_position makes redundant DB work The handler fetches the supplier cap to find the owner address, then calls build_positions which loads all positions for that owner, then filters to the requested one. A direct lookup by cap ID would be significantly more efficient for owners with many positions. 10. Slush routes and rate limiting Verify that the Slush sub-router is covered by the existing governor-based rate limiter. The /strategies and /positions endpoints trigger outbound HTTP calls to the Abyss API and should be protected. 11. reqwest pulls in native-tls unnecessarily Without default-features = false, this brings in native-tls and core-foundation (visible in the Cargo.lock diff). If the workspace already uses rustls, prefer adding default-features = false and specifying rustls-tls explicitly. Minor / Nits
Missing TestsThere are no unit tests for the PTB building logic (ptb.rs) or the handler helpers. The PTB builders are pure functions and would be straightforward to test - verifying the serialized bytes deserialize to the expected commands/arguments would prevent regressions like the PR#870 bugs this PR fixes. SummaryThe module structure is clean, the PTB fixes are correct, and the API shape follows the OpenAPI spec well. Top items to address before merging:
|
- Return 422 error on invalid withdrawal amount instead of silently defaulting to full withdrawal (empty string still means "withdraw all") - Use saturating_add/saturating_sub in build_positions to prevent integer overflow in release builds - Log a warning when SLUSH_VAULT_MAPPING env var is set but contains invalid JSON, instead of silently falling back to an empty map - Add TODO comments for TVL USD accuracy (currently token quantity, correct only for stablecoins), volume_24h_usd placeholder, and APY rolling average placeholders Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Acknowledged — this was a test comment. |
|
Thanks for the thorough review! I've pushed a commit addressing the items that can be fixed directly. Here's a summary of what was done and what's deferred: Fixed in this commit1. Silent amount parse failure in withdraw handler (Critical) ✅ 2. Integer overflow in build_positions (Critical) ✅ 8. Silent config parsing failure (Moderate) ✅ 5. TVL not in USD (Major) ✅ (documented) APY averages / volume_24h_usd (Nits) ✅ Acknowledged — will address in follow-up PRs3. N+1 queries in build_strategies — Agreed this should be a single aggregated query. Will batch the 4. APY cache stampede — Good catch on the read→write lock gap. Will add a 6. OR-based join in get_all_margin_pools — The three-way OR does prevent index usage. Will normalize asset types at ingest time. 7. Missing input validation — Will add Sui address format validation on 9. get_position redundant DB work — Agree a direct lookup by cap ID would be more efficient. Will refactor. 10. Rate limiting coverage — Will verify the Slush sub-router is covered by the governor rate limiter. 11. reqwest native-tls — Will add Missing tests — Agree the PTB builders are pure functions and straightforward to test. Will add unit tests in a follow-up. |
|
Summary
/slush/v1/route module indeepbook-server, exposing DeepBook margin lending pools as LENDING strategiesmargin_metricsinfrastructure (margin_pool_snapshotstable) for pool state data (TVL, utilization, depositor counts) instead of making duplicate RPC callsTransferObjectscommands for newly-createdSupplierCapand returnedCoinobjectswithdraw/cancelsince DeepBook margin withdrawals are instant (no hold period)MARGIN_REGISTRY_IDfor the registry shared object,SLUSH_VAULT_MAPPING(JSON) for pool-to-Abyss vault address mappingEndpoints
/slush/v1/version1.1.0/slush/v1/provider/slush/v1/strategies/slush/v1/strategies/{id}/slush/v1/positions?address=/slush/v1/positions/{id}/slush/v1/deposit/slush/v1/withdraw/slush/v1/withdraw/cancelPTB Fixes (vs previous attempt)
mint_supplier_capnow correctly passesregistrythenclockargumentsTransferObjectsadded to transfer the mintedSupplierCapto senderTransferObjectsadded to transfer the returnedCoin<Asset>to senderTest plan
cargo build -p deepbook-servercompiles cleanlycargo fmt --checkpasses/slush/v1/versionreturns{"version":"1.1.0"}/slush/v1/providerreturns provider metadata with aggregated TVL/slush/v1/strategiesreturns strategies with correct APY from Abyss/slush/v1/depositreturns valid base64-encoded transaction-kind bytes/slush/v1/withdrawreturns valid base64-encoded transaction-kind bytes/slush/v1/withdraw/cancelreturns 501 with{"_tag":"NotImplementedError"}oasdiff breakingagainst the OpenAPI spec to confirm zero breaking changes🤖 Generated with Claude Code