Skip to content

fix(gmx-sol): exclude points-farming volume from GMTrade#8085

Open
mkzung wants to merge 2 commits into
DefiLlama:masterfrom
mkzung:fix/gmx-sol-points-farming
Open

fix(gmx-sol): exclude points-farming volume from GMTrade#8085
mkzung wants to merge 2 commits into
DefiLlama:masterfrom
mkzung:fix/gmx-sol-points-farming

Conversation

@mkzung

@mkzung mkzung commented Jul 12, 2026

Copy link
Copy Markdown

Closes #7120.

Problem

@treeoflife2 you were right to close #7235. totalFees mixes price impact, liquidation, borrowing and funding fees, which this repo already documents in fees/gmx-sol.ts ("Fees from opening/closing perpetual positions + borrowing fees + liquidation fees"), so fees/volume is not a trading fee rate and a threshold on it is fitted to noise.

I also tried the rule you suggested, at least 90% of closes held under a minute. It does not work either. On 2026-05-20 it flags 2 wallets and 0.095% of the volume, because only 1.9% of that day's closes were held under a minute. The variable is right, the scale is off: median hold that day is about 1h against about 10h on a quiet day.

So I went and looked at who actually produces the spike. On 2026-05-20 it is 15 wallets, and 13 of them carry 99.8% of it. They are close to identical:

capital deployed $2.8M to $11.0M
volume $60M to $288M
turnover 20x to 33x
markets open at once 15 to 26
VIP tier 4 to 5
GT rank 220 to 998

Thirteen wallets with the same capital band, the same market count, the same turnover, a book that is flat overall, and points ranks in one contiguous block. That reads as one operation rather than a population of traders.

One correction to the premise, before you decide what to do about it. They are not paying fees to do this. On that day they are net +0.63 bps profitable after fees and price impact, while everyone else on the platform is -0.60 bps. They are farming the GT points programme and they are good enough at it to trade for free. And since GMX Solana is pool-based rather than an orderbook, none of this is self-matched wash trading. It is real trading against the pool that only exists because of the points programme.

Whether that volume should count is your call. What follows filters it if you want it filtered.

Fix

Per wallet, per day, from tradeEvents:

  • turnover, volume divided by peak concurrent capital, meaning the sum of the wallet's open positions tracked through the day. One full round trip of the book is 2x.
  • imbalance, |long volume - short volume| / volume.
  • a floor of 20 trades.

Flagged when turnover >= 20 and imbalance <= 0.15.

Turnover has to be measured against total concurrent capital, not the largest single position. These wallets run 15 to 26 markets at once, so keying off the biggest position understates their capital by about that factor and sweeps in ordinary multi-market traders. I had that wrong at first and it roughly doubled the amount removed.

Quiet days stay at 0.0% removed anywhere in turnover 10-30 and imbalance 0.05-0.25, so the exact thresholds are not load bearing.

Fees are untouched. Those fees were really paid.

Verified

Recomputing volume from tradeEvents reproduces volumeRecordDailies.tradeVolume exactly on all 62 days from 2026-05-10 to 2026-07-10, so with nothing flagged this returns exactly what the adapter returns today.

day now filtered removed wallets
2026-05-14 $3.94B $2.17B 45.0% 14
2026-05-20 $5.25B $3.22B 38.7% 15
2026-05-18 $4.90B $3.57B 27.2% 21
2026-06-01 $4.43B $3.62B 18.2% 19
2026-03-25 $0.50B $0.50B 0.0% 0
2026-04-08 $0.67B $0.67B 0.0% 0
2026-06-19 $0.65B $0.65B 0.0% 0
2026-07-10 $0.62B $0.62B 0.0% 0

Over the 62 days $78.4B becomes $71.2B, and 52 of the 62 days move by less than 2%. The 2026-03-25 and 2026-04-08 rows are the ones I would check first: real $500-675M days with nothing flagged, so this is not just firing whenever volume is high.

It adds about 4s to the adapter on a normal day and 6s on the heaviest one, since it reads the day's trade events (three requests at 50k rows).

Happy to move the thresholds, or to drop this if you would rather not treat incentive volume differently.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Summary by CodeRabbit

  • Improvements
    • Daily volume reporting now derives from aggregated trade activity across the day, instead of relying on precomputed daily records.
    • Volume reflects position changes and separates long vs. short activity.
    • Reported totals exclude wallets showing suspicious “farmer-like” behavior (based on trade frequency and turnover/imbalance).
  • Documentation
    • Added a clear “Volume” methodology description to the GMX Solana adapter.

Walkthrough

GMX Solana daily volume now comes from paginated tradeEvents, aggregated per wallet and position, with volume-farmer wallets excluded. The adapter also documents its volume methodology.

Changes

GMX Solana volume calculation

Layer / File(s) Summary
Trade aggregation models and farmer rules
dexs/gmx-sol.ts
Defines trade and wallet state, scaling and filtering thresholds, pagination settings, and volume-farmer classification.
Paginated trade processing
dexs/gmx-sol.ts
Fetches daily trade events in pages and aggregates exposure, position-size changes, trade counts, and long/short volume per wallet.
Daily volume output and methodology
dexs/gmx-sol.ts
Excludes classified farmer wallets from dailyVolume and adds the adapter’s volume methodology description.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Adapter
  participant GMXGraphQL
  participant WalletAggregation
  participant DailyVolume
  Adapter->>GMXGraphQL: Request paginated tradeEvents
  GMXGraphQL-->>Adapter: Return daily trade events
  Adapter->>WalletAggregation: Apply position and volume changes
  WalletAggregation-->>DailyVolume: Return non-farmer wallet volumes
  DailyVolume-->>Adapter: Return dailyVolume
Loading

Suggested labels: dexs, bug-fix, methodology

🚥 Pre-merge checks | ✅ 13 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title is related to the change, but it does not follow the required '[type] protocol-name - description' format. Rename it to the repository's required format, e.g. '[fix] gmx-sol - exclude points-farming volume from GMTrade'.
Linked Issues check ⚠️ Warning The PR addresses the core GMX Solana wash-trading filter goal, but it does not implement #7235's fee-rate threshold method. Either implement the fee-rate cap from #7235 or remove that issue from the linked scope if this alternative approach is intentional.
✅ Passed checks (13 passed)
Check name Status Explanation
Description check ✅ Passed The description is detailed and covers the problem, fix, verification, and impact, which is sufficient for this PR.
Out of Scope Changes check ✅ Passed The added pagination cap, error handling, and methodology metadata are all tied to the adapter change and are not unrelated scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Breakdown Methodology Check ✅ Passed Not applicable here: gmx-sol.ts has no .add() breakdown labels and exports only methodology, not a fees breakdownMethodology object.
Income Statement Compliance ✅ Passed PR only changes dexs/gmx-sol.ts; fees/gmx-sol.ts already maps dailyFees to total fees and splits revenue/supply-side 25/75 or 70/30, so this check is unaffected.
Version 2 Required ✅ Passed PASS: dexs/gmx-sol.ts is an existing SimpleAdapter v1 adapter being updated, which is exempt from the version-2 requirement.
Pullhourly Required For Version 2 ✅ Passed gmx-sol.ts is version 1, so the version-2 pullHourly requirement does not apply.
Dune Adapters Are Version 1 ✅ Passed gmx-sol.ts has no Dune queries, and it already exports version: 1; no duplicate TIME_RANGE filters appear.
Income Statement Balance ✅ Passed PR only changes dexs/gmx-sol.ts volume logic; fees/gmx-sol.ts already keeps dailyFees split as dailyRevenue + dailySupplySideRevenue.
Fetchoptions Usage ✅ Passed dexs/gmx-sol.ts uses the single FetchOptions arg, no legacy 3-arg signature, no timestamp return, and it keys the day window from options.startOfDay/endTimestamp in a v1 adapter.
Adapter Shape ✅ Passed gmx-sol.ts uses a single-chain SimpleAdapter with one global start date, so it matches the simplest adapter shape.
Efficiency And Error Handling ✅ Passed PASS: the adapter uses a direct paginated GraphQL request, with no getLogs/api.call/try-catch/allowNegativeValue patterns; numeric constants are documented in comments.
Methodology Keys ✅ Passed The adapter’s methodology object uses the display-name key Volume, not a code field name like dailyVolume.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

The gmx-sol.ts adapter exports:

> adapters@1.0.0 test
> ts-node --transpile-only cli/testAdapter.ts dexs gmx-sol.ts

🦙 Running GMX-SOL.TS adapter 🦙
---------------------------------------------------
Start Date:	Sat, 11 Jul 2026 00:00:00 GMT
End Date:	Sun, 12 Jul 2026 00:00:00 GMT
---------------------------------------------------

SOLANA 👇
Backfill start time: 12/2/2025
Daily volume: 97.01 M

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@dexs/gmx-sol.ts`:
- Line 109: Update the error message in the events-length check to remove the
stray period after the exclamation mark, preserving the existing error condition
and behavior.
- Around line 101-109: Cap the pagination loop in the trade-event retrieval flow
around the events accumulator and request call by enforcing a maximum number of
pages and a timeout for each GraphQL request. Stop or fail clearly when either
limit is reached, while preserving the existing short-page termination and “Not
found daily data!” behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 33888a54-c3ba-488f-866a-fdf1ff2c3e4a

📥 Commits

Reviewing files that changed from the base of the PR and between 0dd1a21 and 1fc9494.

📒 Files selected for processing (1)
  • dexs/gmx-sol.ts

Comment thread dexs/gmx-sol.ts Outdated
Comment thread dexs/gmx-sol.ts Outdated
@mkzung

mkzung commented Jul 12, 2026

Copy link
Copy Markdown
Author

Both fair. Added a cap on the pagination loop so it cannot spin if the subgraph keeps handing back full pages, and reworded the error. The daily numbers are unchanged.

@github-actions

Copy link
Copy Markdown

The gmx-sol.ts adapter exports:

> adapters@1.0.0 test
> ts-node --transpile-only cli/testAdapter.ts dexs gmx-sol.ts

🦙 Running GMX-SOL.TS adapter 🦙
---------------------------------------------------
Start Date:	Sat, 11 Jul 2026 00:00:00 GMT
End Date:	Sun, 12 Jul 2026 00:00:00 GMT
---------------------------------------------------

SOLANA 👇
Backfill start time: 12/2/2025
Daily volume: 97.01 M

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
dexs/gmx-sol.ts (1)

99-151: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Adapter should be version: 2 — the subgraph supports timestamp filtering.

The SQD subgraph accepts from/to timestamp parameters (line 105), confirming it supports granular time filtering. Per coding guidelines, subgraphs should use version: 2 with pullHourly explicitly set. The current version: 1 means the adapter runs once daily and uses options.startOfDay (a v1-only time anchor), which the guidelines say v2 adapters should not use.

Migrating to v2 would require:

  • Replacing options.startOfDay with options.startTimestamp/options.fromTimestamp
  • Adding pullHourly: true (or false with a comment if hourly pulling is impractical for farmer detection)
  • Adapting the farmer classification logic to work on hourly windows (a wallet with 20 trades/day may only have 1–2 trades/hour, falling below the trades >= 20 threshold)

If hourly farmer detection is genuinely impractical, version: 2 with pullHourly: false and an inline comment explaining why would still be more guideline-compliant than version: 1.

Based on learnings, version selection should be based on data source capabilities (timestamp filtering), not on query expense. Subgraphs explicitly fall into the v2 category. As per coding guidelines, "Use Version 2 when the source supports granular time filtering, such as on-chain logs, contract calls, subgraphs."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dexs/gmx-sol.ts` around lines 99 - 151, Update the adapter definition that
uses fetch to version 2 because the subgraph supports timestamp filtering.
Replace the v1 options.startOfDay anchor in fetch with the v2 start/from
timestamp field, and explicitly configure pullHourly; if disabling hourly pulls,
add the required rationale comment. Adjust isVolumeFarmer-based classification
as needed so farmer detection remains correct for the selected window size.

Sources: Coding guidelines, Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@dexs/gmx-sol.ts`:
- Around line 99-151: Update the adapter definition that uses fetch to version 2
because the subgraph supports timestamp filtering. Replace the v1
options.startOfDay anchor in fetch with the v2 start/from timestamp field, and
explicitly configure pullHourly; if disabling hourly pulls, add the required
rationale comment. Adjust isVolumeFarmer-based classification as needed so
farmer detection remains correct for the selected window size.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: af8d10ea-c548-4dc6-8176-0d3a8dbdeecb

📥 Commits

Reviewing files that changed from the base of the PR and between 1fc9494 and 1f9e780.

📒 Files selected for processing (1)
  • dexs/gmx-sol.ts

@bheluga bheluga self-assigned this Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

filter wash trading volume from GMTrade

2 participants