Skip to content

feat(pricing): liquidity-depth & slippage estimation endpoint (#84) - #531

Merged
Miracle656 merged 1 commit into
Divinity-Labs:mainfrom
sparkysolutions10-pixel:feat/liquidity-depth-slippage-84
Oct 19, 2026
Merged

Miracle656 merged 1 commit into
Divinity-Labs:mainfrom
sparkysolutions10-pixel:feat/liquidity-depth-slippage-84

Conversation

@sparkysolutions10-pixel

Copy link
Copy Markdown
Contributor

Closes #84

Summary of Changes

  • Implements depth and slippage estimation logic in src/pricing/depth.ts backed by constant-product AMM curves and order-book snapshots.
  • Adds calculateSlippage(spotPrice, executionPrice) with mathematical verification against known constant-product formulas:
    $$dy = \frac{y \cdot dx \cdot (1 - \text{fee})}{x + dx \cdot (1 - \text{fee})}$$
  • Updates getDepth and getAMMDepth to accept network parameter and filter pool snapshots per network.
  • Passes active network from /price/:assetA/:assetB/depth route in src/api/rest.ts.
  • Added unit tests in src/__tests__/depth.test.ts verifying monotonic slippage increase, zero/negative size handling, and database mocking.

Miracle656 added a commit that referenced this pull request Oct 19, 2026
`getDepth` carried a `NOTE: getDepth reads order-book data with no network
column yet — see L048`, and that note was the bug: `pool_snapshots` and
`price_points` both hold rows for every network the process indexes, so an
unfiltered read could answer a mainnet depth query with testnet reserves. Both
queries are now scoped, and `network` threads through from the route.

Also adds `calculateSlippage`, guards `calculateAMMPrice` against a
non-positive amount, and documents each formula.

Worth noting separately: it deletes a block of thinking-out-loud comments in
`generateAMMDepthLevels` ("Wait no: actually for pair A/B...") that talked
itself into and out of inverting the bid price. The code was already right —
`price = amountB / amountA` is unchanged — and the comment was the only thing
suggesting otherwise.

One change while merging: the new `getAMMDepth` branched on `network` and kept
an unscoped copy of the query for the falsy case. That branch is unreachable,
because `network` defaults to `activeNetwork` and passing `undefined` takes
the default — so it was a second copy of the SQL that could only drift from
the first. Collapsed to the single scoped query, with a comment saying why
there is deliberately no unscoped path.

Verified on the merge result: `tsc --noEmit` silent, 885 tests pass across 82
suites, nothing red.

Closes #84

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

Copy link
Copy Markdown
Collaborator

Merged. #84 is unassigned so this is yours, and the core of it is a real correctness fix rather than a feature.

That NOTE: getDepth reads order-book data with no network column yet — see L048 was the bug. pool_snapshots and price_points both hold rows for every network the process indexes, so before this an unfiltered read could answer a mainnet depth query using testnet reserves — and nothing in the response would have said so. Threading network through from the route closes it.

The thing I liked most is one you might not have thought of as a contribution: you deleted the block of thinking-out-loud comments in generateAMMDepthLevels that argued itself into and out of inverting the bid price —

// Wait no: actually for pair A/B, bids are orders to buy A with B...

The code was already correct (price = amountB / amountA, unchanged by this PR) and that comment was the only thing suggesting it might not be. A comment that contradicts working code is worse than no comment, because the next person trusts it and "fixes" the code. Removing it is a genuine improvement.

Also good: guarding calculateAMMPrice against a non-positive amount rather than returning NaN or Infinity into a price field, and writing each formula above its function so the constant-product arithmetic is checkable without deriving it.

One change while merging

The new getAMMDepth branched on network and kept a second, unscoped copy of the query for the falsy case:

const query = network ? `...WHERE ps.network = $2...` : `...` // no network filter
const params = network ? [pairKey, network] : [pairKey]

That branch can't be reached — network: NetworkName = activeNetwork means passing undefined takes the default, and there's no other falsy NetworkName. So it was a duplicate of the SQL that could only ever drift from the one actually in use, and it's exactly the unscoped read this PR exists to remove. I collapsed it to the single scoped query with a comment stating that the absence of an unscoped path is deliberate.

Worth keeping in mind generally: a defensive fallback for a case the type system already rules out tends to become the stale copy.

Verified on the merge result rather than your branch: tsc --noEmit silent, 885 tests across 82 suites, nothing red.

Closes #84.

@Miracle656
Miracle656 merged commit cb4b0eb into Divinity-Labs:main Oct 19, 2026
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.

Liquidity-depth & slippage estimation endpoint

2 participants