Reconcile direct-hybrid swap quotes to wallet simulation (#471) #1005

Merged
PlasticDigits merged 1 commit from fix/471-direct-hybrid-wallet-reconcile into main 2026-07-02 02:00:54 +00:00
PlasticDigits commented 2026-07-01 14:09:44 +00:00 (Migrated from gitlab.com)

Summary

Fixes #471: the direct-hybrid swap path (quoteDirectHybridSwap indexer branch) previously displayed raw indexer estimated_amount_out and slippage_percent without wallet cross-check, while the multi-hop path already reconciled via simulateMultiHopSwap.

After POST /route/solve, the indexer branch now runs wallet hybrid_simulation with the same hybrid params, shows the wallet receive amount, derives slippage via resolveRouteSlippagePercent, and surfaces swap-direct-hybrid-amount-reconciled / trade-market-amount-reconciled when indexer and wallet disagree by ≥0.1%.

Acceptance checklist

Criterion Verification Result
Direct-hybrid indexer branch reconciles receive to wallet hybrid_simulation npm test -- src/utils/directHybridQuote.test.ts PASS
Slippage derived from wallet amount vs indexer spot (not raw indexer slippage) npm test -- src/utils/swapRouteSlippage.test.ts PASS
Mismatch warning shown in Swap UI npm test -- src/pages/SwapPage.test.tsx -t "reconciles direct-hybrid" PASS
Trade market panel shows reconcile copy on mismatch Code review: TradeMarketOrderPanel.tsx trade-market-amount-reconciled PASS
Disclosure copy "checked against your wallet" is now accurate Wallet sim always runs on indexer success branch PASS
Docs/skills updated skills/AGENTS_HYBRID_QUOTING.md, skills/AGENTS_FRONTEND_SWAP_ROUTE_DISPLAY.md, docs/security-model.md PASS
Frontend lint make lint-frontend PASS
On-chain E2E hybrid swap Not run (unit tests cover reconcile logic; no contract changes) SKIP

Third-party verification

cd frontend-dapp
npm test -- src/utils/directHybridQuote.test.ts src/utils/swapRouteSlippage.test.ts
npm test -- src/pages/SwapPage.test.tsx -t "reconciles direct-hybrid"
make lint-frontend

Manual (optional, needs LocalTerra + indexer): direct CW20↔CW20 swap with Settings → Advanced → book leg > 0; confirm receive line matches wallet sim and reconcile warning appears if indexer estimate is tampered.

Blockers

None.

## Summary Fixes #471: the direct-hybrid swap path (`quoteDirectHybridSwap` indexer branch) previously displayed raw indexer `estimated_amount_out` and `slippage_percent` without wallet cross-check, while the multi-hop path already reconciled via `simulateMultiHopSwap`. After `POST /route/solve`, the indexer branch now runs wallet `hybrid_simulation` with the same hybrid params, shows the wallet receive amount, derives slippage via `resolveRouteSlippagePercent`, and surfaces `swap-direct-hybrid-amount-reconciled` / `trade-market-amount-reconciled` when indexer and wallet disagree by ≥0.1%. ## Acceptance checklist | Criterion | Verification | Result | |-----------|--------------|--------| | Direct-hybrid indexer branch reconciles receive to wallet `hybrid_simulation` | `npm test -- src/utils/directHybridQuote.test.ts` | PASS | | Slippage derived from wallet amount vs indexer spot (not raw indexer slippage) | `npm test -- src/utils/swapRouteSlippage.test.ts` | PASS | | Mismatch warning shown in Swap UI | `npm test -- src/pages/SwapPage.test.tsx -t "reconciles direct-hybrid"` | PASS | | Trade market panel shows reconcile copy on mismatch | Code review: `TradeMarketOrderPanel.tsx` `trade-market-amount-reconciled` | PASS | | Disclosure copy "checked against your wallet" is now accurate | Wallet sim always runs on indexer success branch | PASS | | Docs/skills updated | `skills/AGENTS_HYBRID_QUOTING.md`, `skills/AGENTS_FRONTEND_SWAP_ROUTE_DISPLAY.md`, `docs/security-model.md` | PASS | | Frontend lint | `make lint-frontend` | PASS | | On-chain E2E hybrid swap | Not run (unit tests cover reconcile logic; no contract changes) | SKIP | ## Third-party verification ```bash cd frontend-dapp npm test -- src/utils/directHybridQuote.test.ts src/utils/swapRouteSlippage.test.ts npm test -- src/pages/SwapPage.test.tsx -t "reconciles direct-hybrid" make lint-frontend ``` Manual (optional, needs LocalTerra + indexer): direct CW20↔CW20 swap with Settings → Advanced → book leg > 0; confirm receive line matches wallet sim and reconcile warning appears if indexer estimate is tampered. ## Blockers None.
PlasticDigits commented 2026-07-01 14:12:57 +00:00 (Migrated from gitlab.com)

Security review — MR !1005

Commit reviewed: 8750c78583fc3995ab774ea92ed11b2d224c35f1

Scope: Direct-hybrid swap quote reconciliation (#471) — quoteDirectHybridSwap wallet hybrid_simulation cross-check, indexerWalletAmountMismatch / resolveRouteSlippagePercent wiring, Swap + Trade market reconcile UI, tests, docs/skills.

Outcome: FINDINGS: 0 medium+

Inline threads: None (no medium+ issues on this diff).


Summary

This MR closes the direct-hybrid display/submit gap documented as SEC-I02 / #471: the indexer branch previously surfaced raw estimated_amount_out and slippage_percent without wallet reconciliation, while multi-hop already used simulateMultiHopSwap.

After the change, the indexer success path always runs LCD hybrid_simulation with the same HybridSwapParams passed to postRouteSolve, uses the wallet return_amount for display/minReceived (via useSubmitAlignedSimQuote), derives slippage from wallet output vs indexer spot_amount_out (same pattern as multi-hop), and surfaces a user-visible reconcile warning when amounts diverge ≥0.1%.

Attack-path review (no exploitable medium+ issues)

Candidate Trace Verdict
Malicious indexer inflated estimated_amount_out Attacker input → former display/minReceived sink Mitigated — display and submit floor now use wallet sim, not indexer estimate
Display/submit hybrid param split hybridFromSingleHopIndexerOps(idxOps) vs user book leg Unchanged / pre-existing — honest indexer merges POST hybrid_by_hop into router_operations; compromised-indexer trust is documented in docs/security-model.md, not introduced here
Indexer spot_amount_out slippage deception resolveRouteSlippagePercent(wallet, spot, …) Same trust model as multi-hop — affects UX/expert-mode gating only; on-chain protection remains computeDirectHybridMinReturn + pair assert_max_spread
Injection / XSS / SSRF Static DIRECT_HYBRID_AMOUNT_RECONCILED_COPY; LCD query via existing queryContract No new sinks
Secret leakage console.warn omits addresses/amounts No issue
parseFloat on raw amounts in mismatch helper Warning threshold only Inherited pattern; does not control fund flow

No block:security label applied.

## Security review — MR !1005 **Commit reviewed:** `8750c78583fc3995ab774ea92ed11b2d224c35f1` **Scope:** Direct-hybrid swap quote reconciliation (#471) — `quoteDirectHybridSwap` wallet `hybrid_simulation` cross-check, `indexerWalletAmountMismatch` / `resolveRouteSlippagePercent` wiring, Swap + Trade market reconcile UI, tests, docs/skills. **Outcome:** `FINDINGS: 0` medium+ **Inline threads:** None (no medium+ issues on this diff). --- ### Summary This MR **closes** the direct-hybrid display/submit gap documented as SEC-I02 / #471: the indexer branch previously surfaced raw `estimated_amount_out` and `slippage_percent` without wallet reconciliation, while multi-hop already used `simulateMultiHopSwap`. After the change, the indexer success path always runs LCD `hybrid_simulation` with the **same** `HybridSwapParams` passed to `postRouteSolve`, uses the wallet `return_amount` for display/`minReceived` (via `useSubmitAlignedSimQuote`), derives slippage from wallet output vs indexer `spot_amount_out` (same pattern as multi-hop), and surfaces a user-visible reconcile warning when amounts diverge ≥0.1%. ### Attack-path review (no exploitable medium+ issues) | Candidate | Trace | Verdict | |-----------|-------|---------| | Malicious indexer inflated `estimated_amount_out` | Attacker input → former display/`minReceived` sink | **Mitigated** — display and submit floor now use wallet sim, not indexer estimate | | Display/submit hybrid param split | `hybridFromSingleHopIndexerOps(idxOps)` vs user book leg | **Unchanged / pre-existing** — honest indexer merges POST `hybrid_by_hop` into `router_operations`; compromised-indexer trust is documented in `docs/security-model.md`, not introduced here | | Indexer `spot_amount_out` slippage deception | `resolveRouteSlippagePercent(wallet, spot, …)` | **Same trust model as multi-hop** — affects UX/expert-mode gating only; on-chain protection remains `computeDirectHybridMinReturn` + pair `assert_max_spread` | | Injection / XSS / SSRF | Static `DIRECT_HYBRID_AMOUNT_RECONCILED_COPY`; LCD query via existing `queryContract` | No new sinks | | Secret leakage | `console.warn` omits addresses/amounts | No issue | | `parseFloat` on raw amounts in mismatch helper | Warning threshold only | Inherited pattern; does not control fund flow | No `block:security` label applied.
PlasticDigits (Migrated from gitlab.com) merged commit a793eb6b84 into main 2026-07-02 02:00:54 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic!1005
No description provided.