Skip to content

Add VIRTUS Protocol DEX adapters for basic AMM and CL pools on Base#6248

Closed
cto-virtus wants to merge 1 commit intoDefiLlama:masterfrom
cto-virtus:feat/virtus-dex-adapter
Closed

Add VIRTUS Protocol DEX adapters for basic AMM and CL pools on Base#6248
cto-virtus wants to merge 1 commit intoDefiLlama:masterfrom
cto-virtus:feat/virtus-dex-adapter

Conversation

@cto-virtus
Copy link
Copy Markdown
Contributor

@cto-virtus cto-virtus commented Mar 23, 2026

Adds on-chain DEX volume/fees adapters for VIRTUS Protocol (Aerodrome fork) on Base mainnet.

Website: https://virtus-protocol.com
Twitter: https://x.com/VirtusCEO

  • dexs/virtus-protocol/ — Basic AMM pools (PoolFactory: 0x7F03ae4452192b0E280fB0d4f9c225DDa88C7623)
  • dexs/virtus-protocol-cl/ — Concentrated Liquidity pools (CLFactory: 0x0e5Ab24beBdA7e5Bb3961f7E9b3532a83aE86B48)

Both read swap events directly from on-chain contracts. No backend API dependency.

Tested with ts-node cli/testAdapter.ts dexs virtus-protocol and virtus-protocol-cl.

Summary by CodeRabbit

  • New Features
    • Added Virtus Protocol adapter for Base chain — collects daily swap volumes and fees, exposing daily volume and revenue metrics.
    • Added Virtus Protocol Concentrated Liquidity adapter for Base chain — collects per-pool swap volumes and fees, exposing daily volume and revenue metrics (including holders revenue).

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 23, 2026

📝 Walkthrough

Walkthrough

Adds two new Virtus Protocol adapter modules for Base that enumerate pools, fetch pool metadata (tokens, fees), scan Swap events, and compute per-day volumes and fee-based revenues.

Changes

Cohort / File(s) Summary
Virtus Protocol - constant-liquidity adapter
dexs/virtus-protocol-cl/index.ts
New deprecated adapter-style module exporting a SimpleAdapter (v2) for CHAIN.BASE. Discovers pools via factory PoolCreated logs, multicalls pool fee, filters invalid pools, fetches Swap logs, accumulates dailyVolume and fee-adjusted dailyFees. Returns dailyRevenue and dailyHoldersRevenue = dailyFees.
Virtus Protocol - standard adapter
dexs/virtus-protocol/index.ts
New SimpleAdapter (v2) for CHAIN.BASE. Reads allPoolsLength/allPools, multicalls per-pool token0/token1/stable, requests getFee (permitting failures), fetches Swap logs, sums amounts per token, computes dailyFees (uses default fee 30/1e4 when missing), returns volumes and revenues.

Sequence Diagram

sequenceDiagram
    participant Adapter as Adapter
    participant Factory as Factory / Pool Contracts
    participant Multicall as Multicall
    participant Logs as Event Indexer

    Adapter->>Factory: Query pool list (factory logs or allPools)
    Factory-->>Adapter: Pool addresses
    Adapter->>Multicall: Batch fetch token0, token1, fee/getFee per pool
    Multicall-->>Adapter: Pool metadata (tokens, fee or null)
    Adapter->>Logs: Query Swap events for each pool
    Logs-->>Adapter: Swap events (amount0, amount1)
    Adapter->>Adapter: Aggregate dailyVolume (amounts)
    Adapter->>Adapter: Calculate dailyFees (amount * fee)
    Adapter-->>Adapter: Return {dailyVolume, dailyFees, dailyRevenue, dailyHoldersRevenue}
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped through logs at break of day,
Found pools and swaps along the way.
I counted tokens, fees so neat,
Tallied volumes for every tweet.
Virtus on Base — a carrot treat! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description provides context about the protocol, implementation details, and testing, but does not follow the repository's template structure for new protocol listings. Clarify whether this PR adds a new protocol listing to DefiLlama or only updates adapters. If it's a new listing, complete the template with protocol details like name, category, description, and other required fields.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding DEX adapters for VIRTUS Protocol's basic AMM and CL pools on Base.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@llamabutler
Copy link
Copy Markdown

The virtus-protocol-cl adapter exports:

> adapters@1.0.0 test
> ts-node --transpile-only cli/testAdapter.ts dexs virtus-protocol-cl

🦙 Running VIRTUS-PROTOCOL-CL adapter 🦙
---------------------------------------------------
Start Date:	Sun, 22 Mar 2026 11:46:25 GMT
End Date:	Mon, 23 Mar 2026 11:46:25 GMT
---------------------------------------------------

BASE 👇
Backfill start time: 5/3/2026
Daily volume: 0.00
Daily fees: 0.00
Daily revenue: 0.00
Daily holders revenue: 0.00
End timestamp: 1774266384 (2026-03-23T11:46:24.000Z)

@llamabutler
Copy link
Copy Markdown

The virtus-protocol adapter exports:

> adapters@1.0.0 test
> ts-node --transpile-only cli/testAdapter.ts dexs virtus-protocol

🦙 Running VIRTUS-PROTOCOL adapter 🦙
---------------------------------------------------
Start Date:	Sun, 22 Mar 2026 11:47:26 GMT
End Date:	Mon, 23 Mar 2026 11:47:26 GMT
---------------------------------------------------

BASE 👇
Backfill start time: 5/3/2026
Daily volume: 8.00
Daily fees: 0.00
Daily revenue: 0.00
Daily holders revenue: 0.00
End timestamp: 1774266445 (2026-03-23T11:47:25.000Z)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
dexs/virtus-protocol/index.ts (1)

34-46: Consider logging or filtering pools with failed fee lookups.

When permitFailure: true causes a fee lookup to return null/undefined, the code silently falls back to 30 basis points. While this maintains resilience, it may produce inaccurate fee calculations for pools with non-standard fees.

For comparison, the Aerodrome adapter (referenced in context snippet 1) doesn't use permitFailure on the getFee call, ensuring fee data is always accurate or the call fails explicitly.

💡 Optional: Filter out pools with failed fee lookups instead of using fallback
   pools.forEach((pool, index) => {
     const p = pool.toLowerCase()
+    const rawFee = fees[index]
+    if (rawFee === null || rawFee === undefined) return; // Skip pools with failed fee lookup
     poolTokens[p] = [token0s[index], token1s[index]]
-    poolFees[p] = (fees[index] ?? 30) / 1e4
+    poolFees[p] = rawFee / 1e4
   })
+
+  const targets = Object.keys(poolTokens)
+  if (!targets.length) return { dailyVolume, dailyFees }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dexs/virtus-protocol/index.ts` around lines 34 - 46, The multiCall to
api.multiCall with abi factoryAbis.getFee and permitFailure: true can return
null/undefined fees which are silently converted to a 30 bps fallback; update
the handling after receiving fees to detect failed lookups (check fees[index]
=== null || fees[index] === undefined) and either (a) log a warning including
the pool address from pools[index] and index so operators can investigate, or
(b) filter those pools out entirely by skipping adding entries to poolTokens and
poolFees (or remove them from pools/token arrays) instead of unconditionally
using 30/1e4; apply this change in the loop that sets poolTokens and poolFees to
ensure inaccurate default fees are not silently used.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@dexs/virtus-protocol/index.ts`:
- Around line 34-46: The multiCall to api.multiCall with abi factoryAbis.getFee
and permitFailure: true can return null/undefined fees which are silently
converted to a 30 bps fallback; update the handling after receiving fees to
detect failed lookups (check fees[index] === null || fees[index] === undefined)
and either (a) log a warning including the pool address from pools[index] and
index so operators can investigate, or (b) filter those pools out entirely by
skipping adding entries to poolTokens and poolFees (or remove them from
pools/token arrays) instead of unconditionally using 30/1e4; apply this change
in the loop that sets poolTokens and poolFees to ensure inaccurate default fees
are not silently used.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 43bf0bf8-df12-402a-9393-cce615110075

📥 Commits

Reviewing files that changed from the base of the PR and between bbc1b02 and cdf4158.

📒 Files selected for processing (2)
  • dexs/virtus-protocol-cl/index.ts
  • dexs/virtus-protocol/index.ts

@bheluga bheluga self-assigned this Mar 24, 2026
@bheluga
Copy link
Copy Markdown
Member

bheluga commented Mar 24, 2026

@cto-virtus thanks for the PR.
Can you add website/twitter links in PR description
Also please raise TVL PR in https://github.com/DefiLlama/DefiLlama-Adapters, you can use uniswapV2 and uniswapV3 helpers

- dexs/virtus-protocol: basic AMM pools via PoolFactory
- dexs/virtus-protocol-cl: concentrated liquidity pools via CLFactory
@cto-virtus cto-virtus force-pushed the feat/virtus-dex-adapter branch from cdf4158 to ffa90c2 Compare March 24, 2026 14:11
@llamabutler
Copy link
Copy Markdown

The virtus-protocol-cl adapter exports:

> adapters@1.0.0 test
> ts-node --transpile-only cli/testAdapter.ts dexs virtus-protocol-cl

🦙 Running VIRTUS-PROTOCOL-CL adapter 🦙
---------------------------------------------------
Start Date:	Mon, 23 Mar 2026 11:41:40 GMT
End Date:	Tue, 24 Mar 2026 11:41:40 GMT
---------------------------------------------------

BASE 👇
Backfill start time: 5/3/2026
Daily volume: 0.00
Daily fees: 0.00
Daily revenue: 0.00
Daily holders revenue: 0.00
End timestamp: 1774352499 (2026-03-24T11:41:39.000Z)

@cto-virtus
Copy link
Copy Markdown
Contributor Author

@cto-virtus thanks for the PR. Can you add website/twitter links in PR description Also please raise TVL PR in https://github.com/DefiLlama/DefiLlama-Adapters, you can use uniswapV2 and uniswapV3 helpers

Thanks! Updated the description with links.

Website: https://virtus-protocol.com
Twitter: https://x.com/VirtusCEO

TVL adapter PR raised here: DefiLlama/DefiLlama-Adapters#18516

@llamabutler
Copy link
Copy Markdown

The virtus-protocol adapter exports:

> adapters@1.0.0 test
> ts-node --transpile-only cli/testAdapter.ts dexs virtus-protocol

🦙 Running VIRTUS-PROTOCOL adapter 🦙
---------------------------------------------------
Start Date:	Mon, 23 Mar 2026 11:42:17 GMT
End Date:	Tue, 24 Mar 2026 11:42:17 GMT
---------------------------------------------------

BASE 👇
Backfill start time: 5/3/2026
Daily volume: 1.02 k
Daily fees: 1.00
Daily revenue: 1.00
Daily holders revenue: 1.00
End timestamp: 1774352536 (2026-03-24T11:42:16.000Z)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
dexs/virtus-protocol-cl/index.ts (1)

68-68: Unnecessary type cast.

The as any cast may hide type mismatches. If the fetch function's signature doesn't match expected types, consider adjusting the function signature rather than silencing the type checker.

♻️ Suggested fix
     [CHAIN.BASE]: {
-      fetch: fetch as any,
+      fetch,
       start: '2026-03-05',
     }

If this causes type errors, ensure fetch returns the correct FetchResult type.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dexs/virtus-protocol-cl/index.ts` at line 68, Remove the unnecessary "as any"
cast on the fetch assignment and fix the fetch function's signature/return type
so it matches the expected FetchResult type instead of silencing the type
checker; locate the fetch implementation referenced by the fetch property
(symbol: fetch) and adjust its parameter/return typings (or convert its return
value) to conform to the expected FetchResult interface used by the surrounding
code so the cast is no longer needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@dexs/virtus-protocol-cl/index.ts`:
- Line 61: The return object currently sets both dailyRevenue and
dailyHoldersRevenue to dailyFees, double-counting revenue; update the return to
set dailyRevenue: 0 and dailyHoldersRevenue: dailyFees so protocol revenue is
zero and holders get 100% of fees (adjust the same pattern wherever you find the
same return with dailyVolume, dailyFees, dailyRevenue, dailyHoldersRevenue).

---

Nitpick comments:
In `@dexs/virtus-protocol-cl/index.ts`:
- Line 68: Remove the unnecessary "as any" cast on the fetch assignment and fix
the fetch function's signature/return type so it matches the expected
FetchResult type instead of silencing the type checker; locate the fetch
implementation referenced by the fetch property (symbol: fetch) and adjust its
parameter/return typings (or convert its return value) to conform to the
expected FetchResult interface used by the surrounding code so the cast is no
longer needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4197266c-9037-49df-bfad-1ead0572d2d9

📥 Commits

Reviewing files that changed from the base of the PR and between cdf4158 and ffa90c2.

📒 Files selected for processing (2)
  • dexs/virtus-protocol-cl/index.ts
  • dexs/virtus-protocol/index.ts
✅ Files skipped from review due to trivial changes (1)
  • dexs/virtus-protocol/index.ts

})
})

return { dailyVolume, dailyFees, dailyRevenue: dailyFees, dailyHoldersRevenue: dailyFees }
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how fees are distributed in similar Aerodrome-fork adapters
rg -n -A5 "dailyRevenue.*dailyFees|dailyHoldersRevenue.*dailyFees" --type=ts dexs/ -g '!node_modules' | head -60

Repository: DefiLlama/dimension-adapters

Length of output: 3189


🌐 Web query:

What is the fee distribution mechanism for Aerodrome concentrated liquidity pools between LPs, veNFT holders, and protocol?

💡 Result:

The fee distribution mechanism for Aerodrome concentrated liquidity pools (Slipstream) follows the same model as standard pools, with a split between Liquidity Providers (LPs), veNFT (veAERO) holders, and protocol (minimal or none). Trading fees from swaps are generated based on pool fee tiers (e.g., 1bps to 0.3%) plus dynamic adjustments. - LPs earn fees directly proportional to their liquidity contribution if they do not stake their position NFT in the gauge. Staked LPs forgo swap fees to earn AERO emissions instead. - A portion of fees accrues to staked liquidity (gauged positions). These fees, along with an "unstaked liquidity fee" (default 10%, max 50%) skimmed from unstaked LP fees, are collected by the gauge via collectProtocol at the end of each epoch. These fees are then distributed to veAERO holders who voted for the pool in the previous epoch, proportional to their voting power. - Protocol receives no direct cut; 100% of collected fees flow to veAERO voters. No explicit protocol treasury retention is mentioned. This creates incentives: LPs choose fees (unstaked) or emissions (staked); voters earn fees by directing emissions to high-fee pools; emissions are separate AERO rewards to staked LPs.

Citations:


Fix double-counted revenue fields for Aerodrome-style fee distribution.

Setting both dailyRevenue and dailyHoldersRevenue to the full dailyFees value misrepresents the fee distribution. In Aerodrome concentrated liquidity (and VIRTUS if following the same model):

  • Protocol receives 0% of fees
  • veAERO holders receive 100% of collected fees

This same issue appears in dexs/velodrome-slipstream/index.ts:119 and dexs/virtus-protocol/index.ts:65.

Change to: dailyRevenue: 0, dailyHoldersRevenue: dailyFees (or verify if VIRTUS deviates from Aerodrome's model with an explicit protocol treasury cut).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dexs/virtus-protocol-cl/index.ts` at line 61, The return object currently
sets both dailyRevenue and dailyHoldersRevenue to dailyFees, double-counting
revenue; update the return to set dailyRevenue: 0 and dailyHoldersRevenue:
dailyFees so protocol revenue is zero and holders get 100% of fees (adjust the
same pattern wherever you find the same return with dailyVolume, dailyFees,
dailyRevenue, dailyHoldersRevenue).

@bheluga
Copy link
Copy Markdown
Member

bheluga commented Mar 26, 2026

added here #6273

@bheluga bheluga closed this Mar 26, 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.

3 participants