fix(swap): cap treasury surplus via Barter-anchored floor on divergent quotes#117
Open
passandscore wants to merge 3 commits intomainfrom
Open
fix(swap): cap treasury surplus via Barter-anchored floor on divergent quotes#117passandscore wants to merge 3 commits intomainfrom
passandscore wants to merge 3 commits intomainfrom
Conversation
…t quotes When Uniswap's single-hop quote diverges from Barter's routed output by more than a configurable threshold (default 25%), derive `userAmtOut` from Barter's output minus a configurable treasury margin (default 1.5%) instead of the Uniswap-anchored slippageLimit. Prevents the surplus leak seen on thin direct pools (e.g. 67 DAI → WBTC tx 0x2f19...f507: user received ~$23 while treasury swept ~$47). Both thresholds live in Edge Config under: - quote_guard_divergence_threshold_pct - quote_guard_treasury_margin_pct Pure guard math split into src/lib/quote-guard.ts with 15 unit tests covering the reproduction case, boundary/misconfiguration behavior, and treasury-margin precision.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Prior behaviour silently swallowed /route errors, leaving the swap button active. Without Barter data the quote-guard cannot anchor minAmountOut on Barter, so thin-pool pairs would fall back to the Uniswap-only floor — the exact regression the guard was added to prevent. useBarterValidation now retries a single transient failure after 800ms (absorbs brief blips with no UI flicker) and flips a new barterUnavailable flag when two consecutive attempts fail. ActionButton renders a disabled "Route unavailable — retrying" state while the flag is set. The flag clears automatically on the next successful validation, re-triggered by the 15s quote refresh tick.
Contributor
Author
The cron that populates `miles_estimate_surplus_rate` in Edge Config had two problems producing misleading miles estimates. 1. Swap coverage. The query divided `user_amt_out / 1e18`, which only makes sense for ETH/WETH outputs. To avoid garbage rates on erc20 outputs (USDC has 6 decimals, WBTC has 8), it filtered `swap_type = 'eth_weth'` and silently excluded ~58% of production volume (1,645 of 2,865 recent processed rows are erc20→erc20). Fixed by dividing `surplus / user_amt_out` — both in the same output token units, so the ratio is decimal-agnostic and works for every swap type. 2. Statistic. The realized distribution is strongly bimodal: a tight cluster at 0.5–0.6% for larger swaps (~$130–230 out) and another at 2–2.1% for smaller swaps (~$25–35 out). The median sits in the gap at ~0.9% — mathematically the middle, but very few users experience anything close to it. Switched to p25: aligns with the large-swap cluster, under-promises for small-swap users (they earn more than shown, the desired direction), and keeps the estimator from over-promising on either end. Also updates the four hardcoded fallbacks from 0.0068 → 0.0056 to match the new p25-of-all-swaps methodology, and refreshes the stale inline comments that described the old semantics.
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
Three related fixes to the surplus path. Two address observed bugs on the quote-guard (PR-original scope), and one fixes a miles-estimator methodology issue discovered while investigating inflated miles displays.
1. Quote guard (surplus leak on thin pools)
When Barter's routed output exceeds the Uniswap quote by more than a configurable divergence threshold, the protective floor (
userAmtOutin the signed Permit2 intent) is derived from Barter's output minus a configurable treasury margin instead of the Uniswap-anchored slippageLimit.Reproduction case — tx 0x2f19...f507 (67 DAI → WBTC):
2. Barter-failure policy
Previously, errors from
/routewere silently swallowed and swaps proceeded on the Uniswap-anchored floor. On thin pools during a Barter outage this re-introduced the original leak.Now: one silent retry after 800ms absorbs transient blips; two consecutive failures flip
barterUnavailable, which disables the swap button with a "Route unavailable — retrying" state. Clears automatically on the next successful validation (re-triggered by the 15s quote refresh tick).3. Miles estimator surplus rate — sampling fix (new)
Investigating inflated miles estimates surfaced that the cron populating
miles_estimate_surplus_ratein Edge Config had two independent problems:user_amt_out / 1e18, assuming 18-decimal ETH/WETH output. To avoid garbage on erc20 outputs, it filteredswap_type = 'eth_weth'— silently excluding the 1,645 erc20→erc20 swaps out of 2,865 recent processed rows.Fixed by:
surplus / user_amt_out(both in same output token units → ratio is dimensionless, works for every swap type).swap_type = 'eth_weth'filter.0.0068→0.0056to match.Configuration
Two new Edge Config keys for the quote guard (with built-in defaults if unset):
quote_guard_divergence_threshold_pctquote_guard_treasury_margin_pctExisting
miles_estimate_surplus_rateis unchanged at the key level — the cron now writes p25 instead of p50 into the same key.What's in the diff
Quote guard
src/lib/quote-guard.ts— pure helpers (isQuoteGuardTriggered,computeQuoteGuardFloor), 15 unit testssrc/app/api/config/quote-guard/route.ts— Edge Config reader endpointsrc/hooks/use-quote-guard-config.ts— client hook with defaults fallbacksrc/hooks/use-barter-validation.ts— exposesbarterAmountOut; retries a single transient/routefailure after 800ms and surfacesbarterUnavailableafter two consecutive failuressrc/hooks/use-swap-form.ts— wires the guard intocomputedMinAmountOutand exposesdisplayedAmountOutFormattedto prevent Min > Expected inversion when the guard firessrc/components/swap/SwapForm.tsx,SwapInterface.tsx,ActionButton.tsx— four display sites swap todisplayedAmountOutFormatted; new "Route unavailable — retrying" disabled state whenbarterUnavailableis setMiles estimator surplus rate
src/lib/analytics/queries.ts— decimal-agnostic query, no swap-type filtersrc/app/api/cron/update-edge-config/miles-estimate-gas/route.ts—computeMedianSurplusRate→computeSurplusRateEstimate, picks p25, updated docstring with rationalesrc/app/api/config/gas-estimate/route.ts,src/hooks/use-estimated-miles.ts,src/hooks/use-surplus-rate.ts,src/components/dashboard/user-swaps-parts.tsx— fallback constants 0.0068 → 0.0056, inline comments refreshedTest plan
Automated —
bunx vitest run(50 tests, all passing). Quote-guard math has 15 dedicated tests covering the reproduction case, routine majors (no false trigger), boundary math, misconfigured-threshold fail-closed behavior.Manual (before merge)
quote_guard_divergence_threshold_pct = 1in Edge Config or local default — swap ETH → long-tail token, confirm modal shows Barter-derived "Expected" and "Minimum received" (no inversion)/api/barter/routelocally (throw in the handler) — confirm the swap button flips to "Route unavailable — retrying" after ~1.3s and clears on restorep25: X.XX% (chosen)and Edge Config value written matchesAction items for the team
quote_guard_divergence_threshold_pct= 25 andquote_guard_treasury_margin_pct= 1.5 in the production Edge Config store (or tune after running historical fastswap data against candidate thresholds)Follow-up ticketed