Repository navigation
add per-token net buy/sell flow endpoint - #237
Merged
moodysalem merged 2 commits intoOct 26, 2026
Merged
moodysalem merged 2 commits into
moodysalem merged 2 commits into
Conversation
moodysalem
deleted the
EKU-2155-api-per-token-net-buy-sell-flow-across-all-ekubo-pools-for-swap-price-chart
branch
October 26, 2026 13:51
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.
Problem
The API only exposes gross, unsigned volume (
/price/.../ohlc,/overview/volume). A price chart can't show whether traders were net buying or net selling a token, and a pair-level figure ignores the token's other pools.Solution
New
GET /tokens/{chainId}/{tokenAddress}/net-flow?interval=<s>&duration=<s>:interval(60–86400, default 900) andduration(3600–2592000, default 86400) as/tokens/{chainId}/{tokenAddress}/price-history; 400 whenduration / interval > 1000.{ timestamp, start, end, interval, data }. The envelope times are unix seconds. Buckets usedate_binwith the price history's origin, so they line up with its points.{ start, bought, sold, swap_count }.boughtis the raw amount that left Ekubo pools to traders (the sum of-deltawhere the token's delta is negative), andsoldis the raw amount traders paid in (the sum of positive deltas). Swap deltas are the pool's balance change: the indexer adds them to TVL and charges fees on the positive side.data.boughtandsold, which leaves net unchanged. This is documented in the route description.Query
The query collects the token's pool ids into two arrays, one for pools where the token is token0 and one for token1. Each side is then a single index-only scan of
swaps_pool_key_id_block_time_ohlc_idx(pool_key_id = ANY(...)plus theblock_timerange), which already carriesdelta0/delta1. No new index or migration.A first version used a lateral join per pool. The planner flattened it, misestimated, and nested-looped every swap on the chain against the pool list: 12.4 s for Starknet ETH over 1 day, which has about 1,070 pools. With the array form the same token over 30 days (507k swaps) runs in about 1.0 s.
Verification
bun run format:check,bun run lint,bun run typecheck,bun testall pass.TEST_PG_CONNECTION_STRING.openapi.jsonregenerated.Against the production database with
wrangler dev --remote --env prod(uncached requests):/tokens/1/0x4c46…d0f/net-flow?interval=1800&duration=86400(EKUBO, 23 pools)/tokens/1/0x4c46…d0f/net-flow?interval=43200&duration=2592000/tokens/1/0x0/net-flow?interval=43200&duration=2592000(ETH)/tokens/1/0xa0b8…eb48/net-flow?interval=43200&duration=2592000(USDC)/tokens/23448594291968334/0x49d3…dc7/net-flow?interval=43200&duration=2592000(Starknet ETH)/tokens/23448594291968334/0x4718…38d/net-flow?interval=43200&duration=2592000(STRK)/tokens/1/0xdead/net-flow/tokens/1/0x0/net-flow?interval=60&duration=86400Spot check: summing the swaps from
/tokens/1/{EKUBO}/{other}/eventsacross every EKUBO pair matches net-flow exactly in all 18 complete 30-minute buckets from 2026-10-25 14:00 to 2026-10-26 13:00. That window covered 92 swaps; bought, sold, swap count and net all match.