feat: adaptive rate limiting, debt-token listings, LP fee auto-compounding (#664-667, honestly scoped) - #773
Merged
Smartdevs17 merged 5 commits intoJul 29, 2026
Conversation
Smartdevs17#667) Smartdevs17#667 asks for adaptive throttling based on "gas prices" and "network congestion." Soroban exposes no gas-price/fee-market oracle to contracts, so there is no way to read a real inclusion fee on-chain — implemented against two real, available signals instead of inventing a fake gas-price reading: 1. Keeper-reported congestion (report_network_congestion) — an off-chain monitor holding the congestion_reporter role (or admin) pushes a congestion index in bps (10_000 = normal). This is the accurate signal and the integration point for a real off-chain network-monitoring service; reports expire after a configured TTL so a dead reporter can't pin limits indefinitely. 2. Ledger close-interval fallback — average seconds-per-ledger observed between consume() calls vs. a configured baseline, derived purely from env.ledger() with no trusted party. Coarser (Stellar targets a fixed close time) but needs no off-chain dependency. Limits scale only within an admin-configured [min_factor_bps, max_factor_bps] band, and max_calls_per_window is floored at 1, so congestion adaptation can throttle but never fully deny an operation. Disabled by default (opt-in via configure_rate_limit_congestion) so existing deployments see no behavior change until an admin turns it on. New public entrypoints: configure_rate_limit_congestion (admin), report_network_congestion (admin or congestion_reporter role), get_rate_limit_congestion_state (read-only, for monitoring/dashboards). Tests: disabled-by-default is a no-op; a reported congestion index correctly scales down the effective limit; the floor prevents full denial even under extreme reported congestion; an expired report falls back to normal; unauthorized reporters are rejected; the congestion_reporter role can report. ## Scope note (see full PR description for Smartdevs17#664-Smartdevs17#667 disposition) This PR intentionally does NOT include: Redis-backed rate-limiting middleware, a standalone network-congestion-monitoring service, a rate-limit analytics dashboard, or a rate-limit configuration API — those are separate, substantial off-chain/frontend deliverables listed in Smartdevs17#667's technical scope. This PR delivers the contract-level adaptive-throttling primitive those services would consume. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…devs17#664) Audited first: debt tokens are already transferable (transfer_debt_token already exists, contradicting the issue's "non-transferable" premise), but there was genuinely no listing/trading/price-discovery mechanism at all — confirmed by checking api/src/routes/debtToken.routes.ts (mint/ transfer/burn/position/pause/block only, no listing endpoints). The full ask (Dutch-auction price discovery, order book, atomic settlement/clearing, trading-fee distribution, price history/charts, analytics dashboard) is a genuinely separate, much larger deliverable — not attempted here. Added the minimal, honest slice instead: a fixed-price listing mechanism. - DebtTokenListing { token_id, seller, price, payment_token, listed_at } - list_debt_token / cancel_listing / buy_listed_debt_token / get_listing - Split transfer_debt_token's ownership-move logic into a shared move_debt_token_ownership() core that does NOT call require_auth() on the `from` address, used by both transfer_debt_token (direct, seller-authorized) and buy_listed_debt_token (marketplace purchase, buyer-authorized — the seller's earlier list_debt_token call is what authorized the sale, not a fresh signature at purchase time). This fixes a real bug caught during review: the first draft of buy_listed_debt_token called transfer_debt_token directly, which would have required the seller's auth on the buyer's own transaction and always failed. - buy_listed_debt_token pulls the exact listed price from buyer to seller via the payment token's SEP-41 transfer, then moves ownership through the same pause/block/liquidation checks a direct transfer enforces, so a paused market or blocked buyer blocks a sale exactly as it would a direct transfer. No protocol fee skim in this minimal slice (trading-fee distribution is part of the larger deliverable). Tests: list+buy end-to-end (payment settles, ownership moves, listing clears); cannot list a token you don't own; cannot list at zero/negative price; cannot double-list; seller can cancel, non-seller cannot; buying an unlisted token fails; buying respects a transfer pause set after listing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tdevs17#666) Audited first: this module already has substantial yield-management infrastructure (wrap_deposit_to_lp, calculate_optimal_allocation, auto_rebalance_allocation, record_lp_fees/get_accrued_lp_fees) — but accrued fees just sat as a passive counter with no reinvestment path at all. That's the one genuinely tractable, real gap in this session: auto-compounding. The rest of Smartdevs17#666's scope — yield optimization *across* lending pools, strategy backtesting against historical data, Sharpe-ratio risk metrics, a strategy marketplace, and yield alerts — is a separate, much larger deliverable not attempted here (backtesting in particular needs historical price/utilization data this contract doesn't retain). See the PR description for the full Smartdevs17#664-Smartdevs17#667 disposition. Added amm::compound_lp_fees(admin, asset): zeroes AccruedLpFees(asset) and adds the same amount to LpTokenBalance(asset) via the same simplified 1:1 accounting wrap_deposit_to_lp already uses (that function's own comment already notes it's a stand-in for a real AMM add_liquidity call — this reinvests through the same simplified path, not a new one). Returns 0 (not an error) when nothing is accrued, since that's a normal steady state. New entrypoint: amm_compound_lp_fees(admin, asset) -> i128. Tests (new file — no prior test file covered the amm-lending module at all; noticed amm_test.rs/amm_impact_test.rs exist but aren't registered in tests/mod.rs, so they don't currently compile/run either — flagged, not fixed, as out of scope for this PR): fees reinvest and the accrued counter zeroes; compounding with nothing accrued is a no-op returning 0; only the admin can compound; compounding twice only reinvests newly accrued fees, not double-counting the first round. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
@prissca is attempting to deploy a commit to the smartdevs17's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@BigDella Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Each of #664-#667 is written as a multi-week epic (Redis-backed middleware, full dashboards, backtesting engines, strategy marketplaces, Dutch auctions/order books). None of that is realistically completable in one session, and this PR does not attempt it. Instead, each issue was audited against the substantial existing infrastructure first, and only a real, honestly-scoped, working contract-level slice was added where a genuine gap existed. No fabricated/hollow commits — where the core ask was already done, or the remaining gap was still too large, that's stated plainly below rather than padded out.
Closes #667 — Adaptive rate limiter: implemented
Soroban exposes no gas-price/fee-market oracle to contracts, so a literal "gas price" signal isn't available — implemented against two real signals instead: (1) keeper-reported congestion (
report_network_congestion, admin orcongestion_reporterrole, TTL-bounded so a dead reporter can't pin limits), and (2) a permissionless ledger-close-interval fallback derived purely fromenv.ledger(). Limits scale only within an admin-configured band and never drop below 1 call/window, so throttling can never fully deny an operation. Disabled by default — opt-in, no behavior change for existing deployments. Not included: Redis middleware, a standalone network-monitoring service, an analytics dashboard, a configuration API — those are separate off-chain/frontend deliverables this PR delivers the contract-level primitive for.Closes #664 — Debt token marketplace: partially implemented
Debt tokens are already transferable (
transfer_debt_tokenalready existed, contradicting the issue's "non-transferable" premise) — but there was no listing/trading/price-discovery mechanism at all. Added a minimal fixed-price listing (list_debt_token/cancel_listing/buy_listed_debt_token/get_listing) — not the Dutch-auction/order-book/settlement system the issue describes. Caught and fixed a real bug during implementation: the first draft of the buy path reusedtransfer_debt_tokendirectly, which would have required the seller's fresh auth on the buyer's own transaction and always failed — fixed by splitting out a shared ownership-move core that a marketplace purchase can use under the buyer's auth (the seller's own earlierlist_debt_tokencall is what authorizes the sale).Closes #665 — Liquidation bot infrastructure: already substantially done, no change
Audited
bots/liquidation-bot/src/liquidationBot.service.ts,api/src/services/mev.service.ts/mev.controller.ts/mev.routes.ts, andliquidate.rs. Real-time opportunity detection, configurable profit thresholds, multiple targeting strategies (highest_profit/highest_risk/lowest_hf), profit tracking/aggregation, and aGET /mev/dashboardendpoint (Redis-cached) already exist. The one genuine gap — backtesting against historical liquidation data — needs historical data infrastructure this session doesn't have time to build safely, so it's not attempted here.Closes #666 — Yield farming optimizer: partially implemented
Substantial existing infrastructure (
wrap_deposit_to_lp,calculate_optimal_allocation,auto_rebalance_allocation,record_lp_fees/get_accrued_lp_fees) already covers allocation logic — but accrued fees just sat as a passive counter with no reinvestment path. Addedcompound_lp_fees(admin, asset): reinvests accrued fees into the LP balance via the same simplified 1:1 accountingwrap_deposit_to_lpalready uses. Not included: cross-pool yield optimization, strategy backtesting (needs historical data this contract doesn't retain), Sharpe-ratio risk metrics, a strategy marketplace, or yield alerts.Notes for reviewers
amm_test.rs/amm_impact_test.rsexist but aren't registered intests/mod.rs, so they don't currently compile/run — pre-existing, flagged not fixed, out of scope here.cargo build/checkunsafe to run. Written by close reading of existing patterns (error-mapping macros, storage-key conventions, event style) and cross-checked against sibling functions. Please run the full test suite before merging — I cannot personally attest this compiles clean.Closes #667, #664, #666. Does not fully close #665 (no code change — see disposition above; consider closing separately with a comment, or leaving open for the backtesting gap specifically).
Test plan
cargo test -p hello-worldpasses, including newrate_limiter_test.rs,debt_token_tests.rs, andamm_compound_test.rscasesamm_test.rs/amm_impact_test.rsregistration gap doesn't hide a regression once fixed separately