Skip to content

Add Neverland fees & revenue adapter (Monad)#6355

Open
0xCatalyst wants to merge 1 commit intoDefiLlama:masterfrom
0xCatalyst:add-neverland-specialized-fees-adapter
Open

Add Neverland fees & revenue adapter (Monad)#6355
0xCatalyst wants to merge 1 commit intoDefiLlama:masterfrom
0xCatalyst:add-neverland-specialized-fees-adapter

Conversation

@0xCatalyst
Copy link
Copy Markdown
Contributor

@0xCatalyst 0xCatalyst commented Apr 8, 2026

Website : https://neverland.money/

Summary

Fees and revenue adapter for Neverland on Monad. Lending pool fees, borrow interest, flash loans, and liquidations are handled by the shared Aave V3 helper via getPoolFees. On top of that, the adapter tracks Neverland-specific revenue:

  • veDUST NFT royalties: MON or WMON received by the dedicated royalty wallet, filtered to exclude internal contract transfers
  • veDUST holder distributions: RevenueReward top-ups, pro-rated over 7-day epochs, via Allium
  • DUST LP incentives: Merkl campaign rewards for DUST pools, pro-rated over each campaign's active period
  • DUST buybacks: DUST arriving at the revenue wallet, recognized on receipt

Revenue and ProtocolRevenue are identical, all protocol-collected fees flow to a single treasury with no DAO split.

Data sources

  • Lending fees: on-chain Aave V3 pool events, getPoolFees
  • Royalties: on-chain native and WMON transfers, getETHReceived or addTokensReceived
  • veDUST rewards: Allium SQL query with a 7-day lookback
  • LP incentives: Merkl API, /v4/opportunities and /v4/campaigns, filtered to Neverland-controlled campaigns
  • Buybacks: on-chain ERC-20 transfers, getEVMTokenTransfers

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

Walkthrough

A new Neverland lending and revenue adapter for Monad chain has been added. The adapter computes Aave v3 lending fees and augments them with four Neverland-specific revenue streams: veDUST holder rewards, DUST LP campaign revenue, DUST buyback receipts, and veDUST sale royalties.

Changes

Cohort / File(s) Summary
Neverland Adapter
fees/neverland.ts
New adapter for Monad Neverland protocol fetching Aave v3 lending fees (borrow interest, liquidation, flashloan) and four custom revenue streams (veDUST rewards, DUST LP campaigns, buyback receipts, royalty receipts). Includes contract addresses, constants, and detailed methodology documentation.

Sequence Diagram(s)

sequenceDiagram
    participant Fetch as Fetch Handler
    participant Aave as Aave v3 Pool
    participant Merkl as Merkl Campaigns
    participant ERC20 as ERC-20 Contracts
    participant Output as Revenue & Fees Object

    Fetch->>Aave: Get lending fees (borrow, liquidation, flashloan)
    Aave-->>Fetch: dailyFees, dailyProtocolRevenue, dailySupplySideRevenue
    
    Fetch->>Merkl: Query DUST LP campaigns (MONAD chain, filtered)
    Merkl-->>Fetch: Campaign data for prorating
    
    Fetch->>ERC20: Fetch DUST transfers to REVENUE_WALLET
    ERC20-->>Fetch: DUST buyback revenue
    
    Fetch->>ERC20: Fetch MON/WMON transfers to ROYALTY_RECEIVER
    ERC20-->>Fetch: Royalty receipts
    
    Fetch->>Output: Combine all streams into revenue/fees objects
    Output-->>Fetch: Complete adapter response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

fees, new-adapter

🚥 Pre-merge checks | ✅ 3 | ❌ 3

❌ Failed checks (3 inconclusive)

Check name Status Explanation Resolution
Breakdown Methodology Check ❓ Inconclusive The fees/neverland.ts file is not accessible in the current environment, making it impossible to verify breakdown methodology consistency. Provide access to fees/neverland.ts or verify all .add() breakdown labels have matching breakdownMethodology entries and are included in the adapter's return statement.
Income Statement Compliance ❓ Inconclusive Income statement compliance verification for Neverland fees adapter requires examination of actual code implementation for dailyFees, dailyRevenue, and dailySupplySideRevenue relationships. Unable to execute shell commands to retrieve and analyze the neverland.ts file; manual code inspection of the adapter implementation is required to verify fee accounting relationships.
Description check ❓ Inconclusive The PR description addresses protocol details but does not follow the repository's template for new protocol listings. If this is a new protocol listing, use the provided template to include: protocol name, Twitter/website links, chain, category, methodology, oracle providers, and other required fields. If updating an existing adapter, clarify the submission type.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is descriptive and clearly summarizes the main change (adding a Neverland fees & revenue adapter for Monad), though it does not follow the specified [type] protocol-name - description format.
Docstring Coverage ✅ Passed Docstring coverage is 84.62% which is sufficient. The required threshold is 50.00%.
Version 2 Required ✅ Passed The new fees/neverland.ts adapter correctly exports a SimpleAdapter instance with version: 2 properly configured at line 464. The adapter definition includes all required properties such as pullHourly: true, chains: [CHAIN.MONAD], dependencies: [Dependencies.ALLIUM], and the fetch handler, fully meeting the version 2 format requirements for new adapters.

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

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

@llamabutler
Copy link
Copy Markdown

The neverland.ts adapter exports:

> adapters@1.0.0 test
> ts-node --transpile-only cli/testAdapter.ts fees neverland.ts

🦙 Running NEVERLAND.TS adapter 🦙
---------------------------------------------------
Start Date:	Tue, 07 Apr 2026 05:00:00 GMT
End Date:	Wed, 08 Apr 2026 05:00:00 GMT
---------------------------------------------------

neverland: failed to fetch royalty receipts Allium API Key is required[Ignore this error for github bot]
------ ERROR ------
Error: Allium API Key is required[Ignore this error for github bot]
    at _queryAllium (/home/runner/work/dimension-adapters/dimension-adapters/helpers/allium.ts:78:11)
    at /home/runner/work/dimension-adapters/dimension-adapters/helpers/allium.ts:10:71
Allium API Key is required[Ignore this error for github bot]

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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@fees/neverland.ts`:
- Around line 230-241: The pagination in getMerklPages currently stops after 20
pages (page < 20) which silently caps results at 2,000 items; update
getMerklPages to document this limit and emit a warning (via your logger) if the
loop exits because the page limit was reached (e.g., when page === 19 and last
pageItems.length === 100) so callers can detect truncation, or refactor the
hardcoded 20 into a named constant (e.g., MAX_MERKL_PAGES) and add an inline
comment explaining the rationale; use the existing getMerklPage and items=100
semantics when constructing the message so the warning clearly states the page
limit and page size.
- Around line 378-380: The catch block that handles errors from fetching royalty
receipts currently swallows the failure (console.error) which causes royalty
data to be omitted from dailyFees and dailyProtocolRevenue unnoticed; update the
error handling in the catch in fees/neverland.ts around the "failed to fetch
royalty receipts" area: either re-throw the error to fail fast (propagate so
calling code can surface the failure), or set a boolean flag/metric (e.g.,
royaltiesFetchFailed = true) that downstream consumers can check and include in
outputs, or at minimum change the logging to use a warning-level log and include
full error details (error/stack) so the system can detect partial data; ensure
references to dailyFees and dailyProtocolRevenue are not updated when
royaltiesFetchFailed is true so consumers can detect incomplete metrics.
🪄 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: 723cec32-2d79-4796-99f2-5596b9a67a27

📥 Commits

Reviewing files that changed from the base of the PR and between af24524 and 9be6ae1.

📒 Files selected for processing (1)
  • fees/neverland.ts

Comment on lines +230 to +241
async function getMerklPages<T>(pathPrefix: string) {
const items: T[] = [];

for (let page = 0; page < 20; page++) {
const pageItems = await getMerklPage<T>(`${pathPrefix}&items=100&page=${page}`);
if (!pageItems.length) break;
items.push(...pageItems);
if (pageItems.length < 100) break;
}

return items;
}
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.

🧹 Nitpick | 🔵 Trivial

Consider documenting the pagination limit.

The loop caps at 20 pages (2,000 campaigns max). While likely sufficient for Neverland's current scale, consider adding a comment or logging a warning if the limit is reached to help future debugging.

Suggested improvement
 async function getMerklPages<T>(pathPrefix: string) {
   const items: T[] = [];
+  const MAX_PAGES = 20;
 
-  for (let page = 0; page < 20; page++) {
+  for (let page = 0; page < MAX_PAGES; page++) {
     const pageItems = await getMerklPage<T>(`${pathPrefix}&items=100&page=${page}`);
     if (!pageItems.length) break;
     items.push(...pageItems);
     if (pageItems.length < 100) break;
   }
 
+  if (items.length === MAX_PAGES * 100) {
+    console.warn(`neverland: Merkl pagination limit reached (${MAX_PAGES} pages), results may be incomplete`);
+  }
+
   return items;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function getMerklPages<T>(pathPrefix: string) {
const items: T[] = [];
for (let page = 0; page < 20; page++) {
const pageItems = await getMerklPage<T>(`${pathPrefix}&items=100&page=${page}`);
if (!pageItems.length) break;
items.push(...pageItems);
if (pageItems.length < 100) break;
}
return items;
}
async function getMerklPages<T>(pathPrefix: string) {
const items: T[] = [];
const MAX_PAGES = 20;
for (let page = 0; page < MAX_PAGES; page++) {
const pageItems = await getMerklPage<T>(`${pathPrefix}&items=100&page=${page}`);
if (!pageItems.length) break;
items.push(...pageItems);
if (pageItems.length < 100) break;
}
if (items.length === MAX_PAGES * 100) {
console.warn(`neverland: Merkl pagination limit reached (${MAX_PAGES} pages), results may be incomplete`);
}
return items;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fees/neverland.ts` around lines 230 - 241, The pagination in getMerklPages
currently stops after 20 pages (page < 20) which silently caps results at 2,000
items; update getMerklPages to document this limit and emit a warning (via your
logger) if the loop exits because the page limit was reached (e.g., when page
=== 19 and last pageItems.length === 100) so callers can detect truncation, or
refactor the hardcoded 20 into a named constant (e.g., MAX_MERKL_PAGES) and add
an inline comment explaining the rationale; use the existing getMerklPage and
items=100 semantics when constructing the message so the warning clearly states
the page limit and page size.

Comment on lines +378 to +380
} catch (error: any) {
console.error("neverland: failed to fetch royalty receipts", error?.message || error);
}
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

Silent error handling may hide incomplete data.

The try/catch logs the error but continues silently, causing royalty revenue to be omitted from dailyFees and dailyProtocolRevenue without any indication to downstream consumers. This could lead to understated metrics that appear complete.

Consider either:

  1. Re-throwing the error to fail fast and surface the issue
  2. Adding a flag/metric to indicate partial data
  3. At minimum, log at warn level instead of error if this is expected behavior
Option 1: Re-throw to fail fast
   } catch (error: any) {
-    console.error("neverland: failed to fetch royalty receipts", error?.message || error);
+    console.error("neverland: failed to fetch royalty receipts", error?.message || error);
+    throw error; // Surface the failure rather than returning incomplete data
   }
Option 2: Log as warning if expected
   } catch (error: any) {
-    console.error("neverland: failed to fetch royalty receipts", error?.message || error);
+    console.warn("neverland: failed to fetch royalty receipts, royalties will be excluded", error?.message || error);
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (error: any) {
console.error("neverland: failed to fetch royalty receipts", error?.message || error);
}
} catch (error: any) {
console.error("neverland: failed to fetch royalty receipts", error?.message || error);
throw error; // Surface the failure rather than returning incomplete data
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fees/neverland.ts` around lines 378 - 380, The catch block that handles
errors from fetching royalty receipts currently swallows the failure
(console.error) which causes royalty data to be omitted from dailyFees and
dailyProtocolRevenue unnoticed; update the error handling in the catch in
fees/neverland.ts around the "failed to fetch royalty receipts" area: either
re-throw the error to fail fast (propagate so calling code can surface the
failure), or set a boolean flag/metric (e.g., royaltiesFetchFailed = true) that
downstream consumers can check and include in outputs, or at minimum change the
logging to use a warning-level log and include full error details (error/stack)
so the system can detect partial data; ensure references to dailyFees and
dailyProtocolRevenue are not updated when royaltiesFetchFailed is true so
consumers can detect incomplete metrics.

@bheluga bheluga self-assigned this Apr 9, 2026
@bheluga
Copy link
Copy Markdown
Member

bheluga commented Apr 11, 2026

@0xCatalyst thanks for the PR.
Is there a way we can get merkl distribution using onchain logs than depending on api?
The idea is when we backfill , merkl would return same data everyday (only current active campaigns), which we might not be expecting. Also it needs to be considered supplySideRevenue not holdersRevenue
It is also fine you if completely remove merkl part if those are temporary and wont impact a lot , as it is more of an incentive.

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.

3 participants