Add Neverland fees & revenue adapter (Monad)#6355
Add Neverland fees & revenue adapter (Monad)#63550xCatalyst wants to merge 1 commit intoDefiLlama:masterfrom
Conversation
WalkthroughA 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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labels
🚥 Pre-merge checks | ✅ 3 | ❌ 3❌ Failed checks (3 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches✨ Simplify 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. Comment |
|
The neverland.ts adapter exports: |
There was a problem hiding this comment.
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
📒 Files selected for processing (1)
fees/neverland.ts
| 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; | ||
| } |
There was a problem hiding this comment.
🧹 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.
| 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.
| } catch (error: any) { | ||
| console.error("neverland: failed to fetch royalty receipts", error?.message || error); | ||
| } |
There was a problem hiding this comment.
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:
- Re-throwing the error to fail fast and surface the issue
- Adding a flag/metric to indicate partial data
- At minimum, log at
warnlevel instead oferrorif 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.
| } 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.
|
@0xCatalyst thanks for the PR. |
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:Revenue and ProtocolRevenue are identical, all protocol-collected fees flow to a single treasury with no DAO split.
Data sources
getPoolFeesgetETHReceivedoraddTokensReceived/v4/opportunitiesand/v4/campaigns, filtered to Neverland-controlled campaignsgetEVMTokenTransfers