Skip to content

feat: provide split routing recommendations minimizing slippage acros… - #320

Open
p70436464-prog wants to merge 2 commits into
Divinity-Labs:mainfrom
p70436464-prog:feat/split-route-proportions
Open

p70436464-prog wants to merge 2 commits into
Divinity-Labs:mainfrom
p70436464-prog:feat/split-route-proportions

Conversation

@p70436464-prog

Copy link
Copy Markdown

Closes #296

Summary

Best route previously picked a single venue. For orders large enough to experience price impact, splitting volume across venues (SDEX and AMM) beats routing the entirety to a single venue.

This PR adds split routing recommendations that calculate optimal proportions per venue to minimize total slippage for a given swap size:

  • Conditional Proportions: Venue proportions are returned only when splitting strictly outperforms the best single-venue route.
  • Comparison Included: Full comparison details (singleVenueRoute, singleVenueOutput, outputImprovement, outputImprovementPct) are included in the response.
  • Honest Answers: Handles zero liquidity and single-venue markets honestly with appropriate status and messaging rather than fabricated 0s or 500 errors.
  • Provenance: Includes venue breakdown, sample count, and freshness timestamps for verification.
  • Dual-Network Aware: Reads req.network ?? activeNetwork, scopes pool queries, and echoes network in the response.
  • API Contract: Declares GET /price/:assetA/:assetB/split with ?network= query parameter in openapi.yaml and synchronizes openapi.json.

Testing

  • Added unit tests in src/__tests__/splitRoute.test.ts covering:
    • Order size where splitting beats single-venue route (returns split proportions and comparison).
    • Order size where single-venue beats splitting (split proportions are omitted).
    • Single-venue availability (AMM-only, SDEX-only).
    • Zero liquidity honest reporting.
    • Network scoping and parameter echoing.
  • Added schema validation tests in src/__tests__/schemaValidation.test.ts.

…s venues

- Calculate proportions per venue for a given size that minimise total slippage
- Return split proportions only when splitting beats the single-venue route
- Include single-venue alternative comparison in response
- Add GET /price/:assetA/:assetB/split route filtering on and echoing network
- Fully declare /price/{assetA}/{assetB}/split and schemas in openapi.yaml and openapi.json
- Add vitest tests covering size where splitting wins and where it does not
@drips-wave

drips-wave Bot commented Oct 16, 2026

Copy link
Copy Markdown

@p70436464-prog 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! 🚀

Learn more about application limits

@Miracle656

Copy link
Copy Markdown
Collaborator

CI is red, and both of your open PRs fail on the same file for the same reason — src/__tests__/splitRoute.test.ts:

Error: [vitest] There was an error when mocking a module. If you are using "vi.mock" factory,
make sure there are no top level variables inside, since this call is hoisted to top of the file.
 ❯ src/aggregator/splitRoute.ts:1:1
   1| import { Asset } from '@stellar/stellar-sdk'

vi.mock is hoisted above the imports, so a factory that closes over a top-level variable runs before that variable exists. The usual fix in this repo is vi.hoisted:

const { mockThing } = vi.hoisted(() => ({ mockThing: vi.fn() }))
vi.mock('../network/clients', () => ({ getHorizonServer: mockThing }))

src/__tests__/priceHistoryNetwork.test.ts, merged earlier today in #308, is a working example in this repo.

There is a second trap behind this one, specific to mocking @stellar/stellar-sdk: spreading the module namespace drops non-enumerable bindings, so Networks disappears and the failure looks unrelated to your mock. PR #158 fixes exactly that and is in my queue — if your mock still misbehaves after the vi.hoisted change, read #158 before spending time on it.

Separately: #320 and #322 both carry splitRoute. If #322 is meant to build on #320, say so in its description and base it on that branch, otherwise whichever merges second will conflict with itself.

Miracle656 added a commit that referenced this pull request Oct 22, 2026
  tsc --noEmit   clean
  vitest         97 files, 1135 passing
  dropped-tests  none

  #311  adaptive slippage impact endpoint
  #323  trade size and volume concentration metrics

#320 and #322 screened clean individually but conflict once #311 and #323 are
in -- all four touch the same routing and pricing surface. They need a rebase
rather than a fix; noted on both.

Claude-Session: https://claude.ai/code/session_01USgemLt4Rnz4SGB1Srf3GB

@Miracle656 Miracle656 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a rebase. This screened clean on its own — tsc --noEmit passes against main as it was — but conflicts once #311 (adaptive slippage) and #323 (trade size and volume concentration) are in, and those landed as b906b47.

All four touch the same routing and pricing surface, which is why they collided rather than merged. Nothing is wrong with the code.

Rebase on current main and re-run npx vitest run before re-requesting. Two things worth checking while you are there, because both have caught PRs in this repo today:

  • the logging-hygiene test forbids console.* in non-test source — use createLogger (three PRs tripped this in two batches)
  • tests/openapi.test.ts fails if you document a route without registering it in src/app.ts, or the reverse

Happy to take it as soon as it is green.

@Miracle656 Miracle656 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scanned clean. Rebased cleanly onto current main — no conflicts at all now, which is what the last round was about. But it no longer compiles, and the cause is different from last time.

npx tsc --noEmit gives 5 errors, all of them the same thing: src/aggregator/splitRoute.ts and src/api/rest.ts import five symbols that do not exist anywhere.

src/aggregator/splitRoute.ts(6): '"../types"' has no exported member 'SplitRecommendation'
src/aggregator/splitRoute.ts(7): '"../types"' has no exported member 'SplitVenueAllocation'
src/aggregator/splitRoute.ts(8): '"../types"' has no exported member 'SplitComparison'
src/aggregator/splitRoute.ts(9): '"../types"' has no exported member 'SplitProvenance'
src/api/rest.ts(20):           '"./schemas"' has no exported member 'splitResponseSchema'

The branch does not touch src/types.ts or src/api/schemas.ts:

$ git diff --stat origin/main L320 -- src/types.ts src/api/schemas.ts
(no output)

and they are not on main either:

$ git grep -ln "SplitRecommendation\|splitResponseSchema" origin/main -- src
(no output)

So the four types and the response schema were never committed. My guess is they were dropped during the rebase — a rebase that resolves a conflict in src/types.ts by taking main's side wholesale would do exactly this, silently, because the file then has no marker left to notice. src/types.ts and src/api/schemas.ts are two of the most contended files in this repo right now, so they are the easiest place for it to happen.

Worth checking your reflog or the pre-rebase commit for them before rewriting from scratch; they almost certainly still exist.

Two notes for when you add them back:

  • splitResponseSchema is the wire contract for /split, so whatever shape it declares is what clients get. It needs to match what the route actually returns, and tests/openapi.test.ts will hold you to documenting the route in openapi.yaml too — every documented path must be registered and every registered non-internal route must be documented.
  • Paths in openapi.yaml now carry the /v1 prefix (97c50bf). A new route is registered at its bare path and documented at /v1/<path>; the version segment is resolved by rewriteUrl, so you do not register it twice.

The split-routing logic itself I have no complaint about — minimising slippage across venues is worth having, and 351 lines of it with tests is real work. It just cannot be reviewed properly until it builds.

Get npx tsc --noEmit clean and re-request, and I will take it quickly.

https://claude.ai/code/session_01USgemLt4Rnz4SGB1Srf3GB

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Route recommendations are not split across venues

2 participants