feat(napier): add dexs volume adapter + rewrite fees adapter to API-based#6065
feat(napier): add dexs volume adapter + rewrite fees adapter to API-based#6065amrrobb wants to merge 6 commits intoDefiLlama:masterfrom
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughRemoved the legacy Napier fees adapter and added new primary and staging Napier fees and volume adapters that fetch market data from Napier APIs, read on‑chain TokenExchange/HookSwap events, convert token amounts using decimals, and return aggregated daily fees, revenues, and volumes. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Adapter as Napier Adapter
participant API as Napier API
participant Chain as Blockchain RPC
participant Conv as TokenConversion
participant Acc as Aggregator
Adapter->>API: GET /v1/market?chainIds=<chain>
API-->>Adapter: markets + metrics (dailyFeeInUsd, curator/protocol, underlyingRewards)
Adapter->>Chain: getLogs(TokenExchange) for Curve pools
Adapter->>Chain: getLogs(HookSwap) for TokiHook contract
Chain-->>Adapter: event logs (amounts, poolId, token indexes)
Adapter->>Conv: convert raw amounts using token decimals (assetDecimals/targetDecimals)
Conv-->>Adapter: normalized asset amounts (USD via market metrics)
Adapter->>Acc: increment dailyFees, dailySupplySideRevenue, dailyRevenue, dailyVolume
Adapter-->>Client: return { timestamp: options.startOfDay, dailyFees/dailyRevenue/dailySupplySideRevenue/dailyVolume }
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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 napier 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/napier/index.ts`:
- Around line 43-48: The current try/catch around the axios.get call swallows
failures and only logs a warning, which can allow invalid/partial metrics to be
emitted; in the catch block for the axios.get<Market[]>(url) call (the code that
assigns markets), rethrow the error (or throw a new Error with context) after
logging so the function fails closed—include options.api.chainId and the
original error message in the thrown error to preserve context; this ensures any
caller of the markets fetch path receives the failure instead of proceeding with
an empty/partial markets array.
- Around line 63-72: The code calls addTokensReceived even when rewardTokens is
empty which can trigger unnecessary indexer queries; update the block around
rewardTokens (computed from markets.flatMap(...)) to check if
rewardTokens.length === 0 and skip calling addTokensReceived (e.g., set
dailyRevenue to an empty/default value) instead of invoking addTokensReceived
with an empty tokens array; ensure you still reference
chainConfig[chain].treasury when needed and only call addTokensReceived when
rewardTokens.length > 0.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 064bf9f4-249d-4aef-8392-78b6e0d82213
📒 Files selected for processing (2)
fees/napier.tsfees/napier/index.ts
💤 Files with no reviewable changes (1)
- fees/napier.ts
|
Addressed CodeRabbit review feedback:
Also added: |
|
The napier adapter exports: |
|
The napier adapter exports: |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
dexs/napier/index.ts (1)
123-125: Consider using10n ** BigInt(decimals)instead ofBigInt(10 ** decimals)for clearer intent and to avoid unnecessary number coercion.While testing shows
BigInt(10 ** decimals)produces correct results for typical token decimals (6, 8, 18), the alternative pattern10n ** BigInt(decimals)is more explicit about working directly with bigints and is a clearer code style. This pattern is used at lines 124 and 156.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@dexs/napier/index.ts` around lines 123 - 125, Replace the expressions that compute 10^decimals via BigInt(10 ** decimals) with the clearer bigint-native form 10n ** BigInt(decimals); specifically update the computation of assetAmount (which uses targetAmount, assetDecimals and targetDecimals) and the similar expression used elsewhere around the dailyVolume.add call so they use 10n ** BigInt(assetDecimals) and 10n ** BigInt(targetDecimals) respectively to avoid intermediate Number coercion and express intent plainly.
🤖 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/napier/index.ts`:
- Around line 28-40: The code currently uses TOKI_HOOK_ADDRESSES zero-address
placeholders and silently skips processing when a hook is unset; instead, in the
block that examines TOKI_HOOK_ADDRESSES (where poolIdToMarket is consulted), add
a validation: for the current chainId look up TOKI_HOOK_ADDRESSES[chainId] and
if poolIdToMarket.size > 0 and the address is missing or the zero-address (e.g.
"0x0000000000000000000000000000000000000000"), throw or exit with an explicit
error (do not continue silently). Update the logic that previously
returned/continued on unset hook (the code around the current skip at the
poolIdToMarket check) to perform this fail-closed validation so volume is not
dropped silently.
- Around line 71-75: The current branch treats any non-"TOKI_HOOK" pool as a
Curve pool, causing unknown/new pool types to be misclassified; change the
conditional around market.pool.poolType so you only push poolAddress into
tokiPools when poolType === "TOKI_HOOK" and into curvePools only when poolType
matches the explicit Curve type/constant (e.g., "CURVE" or the codebase's Curve
enum value), and otherwise skip the pool (do not push) and emit a warning/log
that includes market.pool.poolType and poolAddress so unknown types are
discoverable; update the block referencing market.pool.poolType, tokiPools,
curvePools, and poolAddress accordingly.
In `@fees/napier/index.ts`:
- Line 45: The code currently masks malformed API responses by setting markets =
Array.isArray(res.data) ? res.data : [] which can emit incorrect zero metrics;
change this to validate that res.data is an array and if not, throw or return an
error (or log and exit) instead of defaulting to [] — update the assignment
around markets and the API response handling for res.data so schema mismatches
produce an explicit error path (e.g., throw new Error or return a failed result)
to fail closed when the payload is not an array.
---
Nitpick comments:
In `@dexs/napier/index.ts`:
- Around line 123-125: Replace the expressions that compute 10^decimals via
BigInt(10 ** decimals) with the clearer bigint-native form 10n **
BigInt(decimals); specifically update the computation of assetAmount (which uses
targetAmount, assetDecimals and targetDecimals) and the similar expression used
elsewhere around the dailyVolume.add call so they use 10n **
BigInt(assetDecimals) and 10n ** BigInt(targetDecimals) respectively to avoid
intermediate Number coercion and express intent plainly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 87779093-3060-4d99-baa9-ae3d68f96d56
📒 Files selected for processing (2)
dexs/napier/index.tsfees/napier/index.ts
dexs/napier/index.ts
Outdated
| const TOKI_HOOK_ADDRESSES: Record<number, string> = { | ||
| 1: "0x0000000000000000000000000000000000000000", // Ethereum - TODO: populate | ||
| 8453: "0x0000000000000000000000000000000000000000", // Base - TODO: populate | ||
| 146: "0x0000000000000000000000000000000000000000", // Sonic - TODO: populate | ||
| 42161: "0xd4234A3D471159E03501fcC2fFD61aAf43FE1888", // Arbitrum | ||
| 10: "0x0000000000000000000000000000000000000000", // Optimism - TODO: populate | ||
| 252: "0x0000000000000000000000000000000000000000", // Fraxtal - TODO: populate | ||
| 5000: "0x0000000000000000000000000000000000000000", // Mantle - TODO: populate | ||
| 56: "0x0000000000000000000000000000000000000000", // BSC - TODO: populate | ||
| 137: "0x0000000000000000000000000000000000000000", // Polygon - TODO: populate | ||
| 43114: "0x0000000000000000000000000000000000000000", // Avalanche - TODO: populate | ||
| 999: "0x0000000000000000000000000000000000000000", // HyperEVM - TODO: populate | ||
| }; |
There was a problem hiding this comment.
Do not silently skip TOKI volume when hook address is unset.
Lines 28-40 contain zero-address placeholders, and Lines 140-143 skip processing in that case. If TOKI pools exist, volume is dropped without signal. Fail closed when poolIdToMarket.size > 0 but hook address is missing/zero.
Suggested fix
+ const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
const tokiHookAddress = TOKI_HOOK_ADDRESSES[options.api.chainId!];
- if (tokiHookAddress && tokiHookAddress !== "0x0000000000000000000000000000000000000000" && poolIdToMarket.size > 0) {
+ if (poolIdToMarket.size > 0 && (!tokiHookAddress || tokiHookAddress === ZERO_ADDRESS)) {
+ throw new Error(`Missing TOKI_HOOK address for chainId ${options.api.chainId} with active TOKI pools`);
+ }
+
+ if (tokiHookAddress && tokiHookAddress !== ZERO_ADDRESS && poolIdToMarket.size > 0) {Also applies to: 140-143
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@dexs/napier/index.ts` around lines 28 - 40, The code currently uses
TOKI_HOOK_ADDRESSES zero-address placeholders and silently skips processing when
a hook is unset; instead, in the block that examines TOKI_HOOK_ADDRESSES (where
poolIdToMarket is consulted), add a validation: for the current chainId look up
TOKI_HOOK_ADDRESSES[chainId] and if poolIdToMarket.size > 0 and the address is
missing or the zero-address (e.g. "0x0000000000000000000000000000000000000000"),
throw or exit with an explicit error (do not continue silently). Update the
logic that previously returned/continued on unset hook (the code around the
current skip at the poolIdToMarket check) to perform this fail-closed validation
so volume is not dropped silently.
|
The napier adapter exports: |
|
The napier adapter exports: |
|
Linear: NAP-2375 — DefiLlama Dashboard Enhancement Related PRs:
This PR covers two adapters:
Draft until napier-api deploys |
|
The napier adapter exports: |
|
The napier adapter exports: |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (4)
fees/napier/index.ts (1)
42-45:⚠️ Potential issue | 🟠 MajorReject malformed market payloads.
Line 45 still treats a non-array
/v1/marketresponse as “no markets”, which will publish zero fees instead of failing. Please throw on schema mismatch the same way thecatchpath does.Suggested fix
- let markets: Market[]; + let markets: Market[]; try { - const res = await axios.get<Market[]>(url); - markets = Array.isArray(res.data) ? res.data : []; + const res = await axios.get<unknown>(url); + if (!Array.isArray(res.data)) { + throw new Error( + `Napier API returned non-array payload for chain ${chain} (chainId ${options.api.chainId})` + ); + } + markets = res.data as Market[]; } catch (e: any) { throw new Error(`Napier API fetch failed for chain ${chain} (chainId ${options.api.chainId}): ${e?.message ?? e}`); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@fees/napier/index.ts` around lines 42 - 45, The try block that fetches markets using axios.get(url) assigns an empty array when res.data is not an array, which masks schema errors and results in publishing zero fees; instead validate the response and throw on mismatch. In the try block where you call axios.get<Market[]>(url) and set markets, check Array.isArray(res.data) and if false throw an Error (include context like the url and typeof res.data) so the catch path handles it the same way as network/errors; update the logic around the markets variable (and any callers expecting Market[]) to rely on the thrown error rather than silently using an empty array.fees/napier/index.staging.ts (2)
54-63:⚠️ Potential issue | 🟠 MajorSkip the treasury transfer scan when
rewardTokensis empty.
addTokensReceivedstill runs withtokens: [], which can trigger an unnecessary indexer lookup before it bails out. The production adapter already guards this case.Suggested fix
const rewardTokens = [...new Set( markets.flatMap((m) => (m.metrics?.underlyingRewards ?? []).map((r: any) => r.rewardToken.address) ) )]; - const dailyRevenue = await addTokensReceived({ - options, - target: chainConfig[chain].treasury, - tokens: rewardTokens, - }); + let dailyRevenue = createBalances(); + if (rewardTokens.length > 0) { + dailyRevenue = await addTokensReceived({ + options, + target: chainConfig[chain].treasury, + tokens: rewardTokens, + }); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@fees/napier/index.staging.ts` around lines 54 - 63, The code calls addTokensReceived with an empty tokens array causing an unnecessary indexer lookup; before invoking addTokensReceived in the block that computes dailyRevenue, check that rewardTokens (derived from markets.flatMap(...) and used as tokens) is non-empty and only call addTokensReceived when rewardTokens.length > 0, otherwise skip the treasury transfer scan or set dailyRevenue to a default value; update the logic around rewardTokens / addTokensReceived (referencing rewardTokens, addTokensReceived, markets, chainConfig, chain) so the production guard for an empty token list is mirrored here.
34-40:⚠️ Potential issue | 🟠 MajorFail closed in staging too.
Lines 37-40 still convert broken staging responses into a valid-looking zero day. That hides API regressions and can leave this adapter emitting partial data after the treasury scan.
Suggested fix
- let markets: Market[] = []; + let markets: Market[]; try { - const res = await axios.get<Market[]>(url); - markets = Array.isArray(res.data) ? res.data : []; + const res = await axios.get<unknown>(url); + if (!Array.isArray(res.data)) { + throw new Error( + `Napier staging API returned non-array payload for chain ${chain} (chainId ${options.api.chainId})` + ); + } + markets = res.data as Market[]; } catch (e: any) { - console.warn(`Failed to fetch markets for chain ${options.api.chainId}: ${e.message}`); + throw new Error( + `Napier staging API fetch failed for chain ${chain} (chainId ${options.api.chainId}): ${e?.message ?? e}` + ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@fees/napier/index.staging.ts` around lines 34 - 40, The current staging adapter swallows errors and converts bad/empty responses into an empty markets array (variables: markets, axios.get<Market[]>(url), options.api.chainId), which masks API regressions; change the behavior so that on any non-array response or axios error you fail closed by throwing a descriptive error (including the chainId and the raw response/error) instead of assigning an empty array — locate the axios.get call and the markets assignment in index.staging.ts and replace the silent warn+empty-array path with a thrown error so upstream/CI can detect the failure.dexs/napier/index.ts (1)
53-63:⚠️ Potential issue | 🟠 MajorHandle unsupported or incomplete pool metadata explicitly.
This
elsesends every non-TOKI_HOOKmarket down the Curve path, including unknownpoolTypes andTOKI_HOOKentries missingpoolId. Those pools then get parsed with the wrong ABI. The staging adapter duplicates the same branch.Suggested fix
- if (market.pool.poolType === "TOKI_HOOK" && market.pool.poolId) { - tokiHookAddress = poolAddress; - poolIdToMarket.set(market.pool.poolId.toLowerCase(), market); - } else { - curvePools.push(poolAddress); - } + if (market.pool.poolType === "TOKI_HOOK") { + if (!market.pool.poolId) { + throw new Error(`Missing poolId for TOKI_HOOK market ${market.metadata.address} on chainId ${api.chainId}`); + } + tokiHookAddress = poolAddress; + poolIdToMarket.set(market.pool.poolId.toLowerCase(), market); + } else if (market.pool.poolType === "CURVE_TWO_CRYPTO") { + curvePools.push(poolAddress); + } else { + continue; + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@dexs/napier/index.ts` around lines 53 - 63, The code is incorrectly treating any non-TOKI_HOOK market as a Curve pool, which misroutes unknown poolType values and TOKI_HOOK entries missing poolId into curvePools; update the logic in the loop that uses poolToMarket, curvePools, poolIdToMarket and tokiHookAddress to explicitly handle supported poolType values (e.g., check for "CURVE" before pushing to curvePools), separately handle TOKI_HOOK cases that lack poolId (skip and/or log an error) and add a fallback path for unknown poolType that does not assume Curve ABI (e.g., skip or record as unsupported), so only truly Curve markets are parsed with the Curve ABI and TOKI_HOOK mappings only get set when poolId exists.
🤖 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/napier/index.ts`:
- Around line 42-45: In fetchMarkets(async function), validate the API response
before iterating: check Array.isArray(res.data) (the value used by the for
(const market of res.data) loop) and fail-closed if it’s not an array — e.g.,
log an error or throw so malformed responses don’t silently iterate as
strings/iterables and produce zero volume; update the code around the axios.get
call and the loop to perform this guard and handle the invalid payload
deterministically.
In `@fees/napier/index.ts`:
- Around line 95-96: Update the methodology text in the fees/napier adapter by
removing the phrase about "rehypothecation yield" from the Fees description;
edit the export/object property named Fees (and ensure UserFees remains
unchanged) so the Fees string only lists AMM trading fees and PT/YT fees
(issuance, redemption, performance) to match the adapter's actual calculations
and avoid mentioning rehypothecation yield.
---
Duplicate comments:
In `@dexs/napier/index.ts`:
- Around line 53-63: The code is incorrectly treating any non-TOKI_HOOK market
as a Curve pool, which misroutes unknown poolType values and TOKI_HOOK entries
missing poolId into curvePools; update the logic in the loop that uses
poolToMarket, curvePools, poolIdToMarket and tokiHookAddress to explicitly
handle supported poolType values (e.g., check for "CURVE" before pushing to
curvePools), separately handle TOKI_HOOK cases that lack poolId (skip and/or log
an error) and add a fallback path for unknown poolType that does not assume
Curve ABI (e.g., skip or record as unsupported), so only truly Curve markets are
parsed with the Curve ABI and TOKI_HOOK mappings only get set when poolId
exists.
In `@fees/napier/index.staging.ts`:
- Around line 54-63: The code calls addTokensReceived with an empty tokens array
causing an unnecessary indexer lookup; before invoking addTokensReceived in the
block that computes dailyRevenue, check that rewardTokens (derived from
markets.flatMap(...) and used as tokens) is non-empty and only call
addTokensReceived when rewardTokens.length > 0, otherwise skip the treasury
transfer scan or set dailyRevenue to a default value; update the logic around
rewardTokens / addTokensReceived (referencing rewardTokens, addTokensReceived,
markets, chainConfig, chain) so the production guard for an empty token list is
mirrored here.
- Around line 34-40: The current staging adapter swallows errors and converts
bad/empty responses into an empty markets array (variables: markets,
axios.get<Market[]>(url), options.api.chainId), which masks API regressions;
change the behavior so that on any non-array response or axios error you fail
closed by throwing a descriptive error (including the chainId and the raw
response/error) instead of assigning an empty array — locate the axios.get call
and the markets assignment in index.staging.ts and replace the silent
warn+empty-array path with a thrown error so upstream/CI can detect the failure.
In `@fees/napier/index.ts`:
- Around line 42-45: The try block that fetches markets using axios.get(url)
assigns an empty array when res.data is not an array, which masks schema errors
and results in publishing zero fees; instead validate the response and throw on
mismatch. In the try block where you call axios.get<Market[]>(url) and set
markets, check Array.isArray(res.data) and if false throw an Error (include
context like the url and typeof res.data) so the catch path handles it the same
way as network/errors; update the logic around the markets variable (and any
callers expecting Market[]) to rely on the thrown error rather than silently
using an empty array.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f97e4bed-1886-4d69-a948-a20819becd57
📒 Files selected for processing (4)
dexs/napier/index.staging.tsdexs/napier/index.tsfees/napier/index.staging.tsfees/napier/index.ts
Replace on-chain event tracking with pre-computed dailyFeeInUsd from napier-api market list. Removes all ABI definitions, BigNumber deps, and complex on-chain fee parsing in favor of a single API call per chain. - Sum dailyFeeInUsd across all markets for daily fees - Split into curator (supply side) and protocol revenue - Keep addTokensReceived for reward token protocol revenue - Support all 11 chains (ETH, Base, Sonic, ARB, OP, Frax, Mantle, BSC, Polygon, Avax, HyperEVM)
- Add production dexs/napier/index.ts for on-chain volume tracking (Curve TokenExchange + TokiHook HookSwap events, all 11 chains) - Fix fees adapter: throw on API failure instead of silently returning zeros - Fix fees adapter: skip addTokensReceived when no reward tokens exist
fcb347b to
b7356cb
Compare
|
The napier adapter exports: |
|
The napier adapter exports: |
There was a problem hiding this comment.
♻️ Duplicate comments (3)
dexs/napier/index.ts (3)
120-137:⚠️ Potential issue | 🟠 MajorFail closed when TOKI pools were discovered but no hook address was resolved.
If
poolIdToMarket.size > 0andtokiHookAddressis null, this branch silently drops all TOKI volume for the chain and returns zeroes. That should be an explicit error, not a quiet skip.Suggested fix
- if (tokiHookAddress && poolIdToMarket.size > 0) { + if (poolIdToMarket.size > 0 && !tokiHookAddress) { + throw new Error(`Missing TOKI hook address for chainId ${options.api.chainId}`); + } + + if (tokiHookAddress && poolIdToMarket.size > 0) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@dexs/napier/index.ts` around lines 120 - 137, The code silently skips processing when poolIdToMarket.size > 0 but tokiHookAddress is falsy; change this to fail loudly by checking if (poolIdToMarket.size > 0 && !tokiHookAddress) and then throw or log and throw an explicit error (e.g., include chain/context) instead of continuing; update the logic around tokiHookAddress, getLogs, and the TOKI_HOOK_ABI.hookSwapEvent handling so TOKI volume is not dropped silently and ensure callers of this function can catch/handle the thrown error rather than returning zeroes from dailyVolume.add.
58-63:⚠️ Potential issue | 🟠 MajorDon't route every non-TOKI market through the Curve decoder.
This
elsealso catchesTOKI_HOOKentries missingpoolIdand any future/unknownpoolType, then parses them with the Curve ABI. Only enqueue explicitCURVE_TWO_CRYPTOpools; skip or fail on anything else.Suggested fix
- if (market.pool.poolType === "TOKI_HOOK" && market.pool.poolId) { + if (market.pool.poolType === "TOKI_HOOK") { + if (!market.pool.poolId) { + throw new Error(`Missing poolId for TOKI_HOOK market ${market.metadata.address} on chainId ${api.chainId}`); + } tokiHookAddress = poolAddress; poolIdToMarket.set(market.pool.poolId.toLowerCase(), market); - } else { + } else if (market.pool.poolType === "CURVE_TWO_CRYPTO") { curvePools.push(poolAddress); + } else { + continue; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@dexs/napier/index.ts` around lines 58 - 63, The current branch routes any non-TOKI_HOOK market (including TOKI_HOOK without poolId and unknown types) into curvePools; change the logic so only explicit CURVE_TWO_CRYPTO pools are pushed to curvePools: keep the TOKI_HOOK branch as is (market.pool.poolType === "TOKI_HOOK" && market.pool.poolId) to set tokiHookAddress and poolIdToMarket, add an else if that checks market.pool.poolType === "CURVE_TWO_CRYPTO" to push poolAddress onto curvePools, and for any other poolType (including TOKI_HOOK missing poolId or unknown types) skip or log a warning instead of enqueuing for Curve decoding.
42-45:⚠️ Potential issue | 🟠 MajorGuard the market API payload before iterating it.
axios.get<Market[]>is only a compile-time hint. If the API returns a non-array payload on an error path, this loop can either iterate garbage or fail mid-scan and publish misleading volume data. Fail closed before entering the loop.Suggested fix
- const res = await axios.get<Market[]>(url); + const res = await axios.get<unknown>(url); + if (!Array.isArray(res.data)) { + throw new Error(`Napier API returned a non-array market payload for chainId ${api.chainId}`); + } @@ - for (const market of res.data) { + for (const market of res.data as Market[]) {Also applies to: 53-53
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@dexs/napier/index.ts` around lines 42 - 45, fetchMarkets currently assumes axios.get<Market[]> returns an array; validate the payload before iterating by checking Array.isArray(res.data) (and optionally that each item has required Market fields) inside fetchMarkets (and the similar call at the other site), and if the check fails log the error/context and return an empty array or throw to "fail closed" instead of proceeding; update code around the axios.get<Market[]> call in fetchMarkets to perform this guard and handle the bad-response path consistently.
🧹 Nitpick comments (1)
dexs/napier/index.ts (1)
147-160: Consider sharing Napier chain start dates with the fees adapter.This table is duplicated in
fees/napier/index.ts:102-151. Extracting a common Napier chain config would reduce drift the next time a chain or backfill date changes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@dexs/napier/index.ts` around lines 147 - 160, The chainConfig object is duplicated between dexs' Napier and fees' Napier; extract this map into a single exported constant (e.g., NAPIER_CHAIN_CONFIG) in a small shared module (e.g., napierConfig) and replace the local chainConfig declarations in both dexs' and fees' Napier code with imports of that exported constant; update references to use the shared symbol (chainConfig or NAPIER_CHAIN_CONFIG) so future chain/start-date changes are made in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@dexs/napier/index.ts`:
- Around line 120-137: The code silently skips processing when
poolIdToMarket.size > 0 but tokiHookAddress is falsy; change this to fail loudly
by checking if (poolIdToMarket.size > 0 && !tokiHookAddress) and then throw or
log and throw an explicit error (e.g., include chain/context) instead of
continuing; update the logic around tokiHookAddress, getLogs, and the
TOKI_HOOK_ABI.hookSwapEvent handling so TOKI volume is not dropped silently and
ensure callers of this function can catch/handle the thrown error rather than
returning zeroes from dailyVolume.add.
- Around line 58-63: The current branch routes any non-TOKI_HOOK market
(including TOKI_HOOK without poolId and unknown types) into curvePools; change
the logic so only explicit CURVE_TWO_CRYPTO pools are pushed to curvePools: keep
the TOKI_HOOK branch as is (market.pool.poolType === "TOKI_HOOK" &&
market.pool.poolId) to set tokiHookAddress and poolIdToMarket, add an else if
that checks market.pool.poolType === "CURVE_TWO_CRYPTO" to push poolAddress onto
curvePools, and for any other poolType (including TOKI_HOOK missing poolId or
unknown types) skip or log a warning instead of enqueuing for Curve decoding.
- Around line 42-45: fetchMarkets currently assumes axios.get<Market[]> returns
an array; validate the payload before iterating by checking
Array.isArray(res.data) (and optionally that each item has required Market
fields) inside fetchMarkets (and the similar call at the other site), and if the
check fails log the error/context and return an empty array or throw to "fail
closed" instead of proceeding; update code around the axios.get<Market[]> call
in fetchMarkets to perform this guard and handle the bad-response path
consistently.
---
Nitpick comments:
In `@dexs/napier/index.ts`:
- Around line 147-160: The chainConfig object is duplicated between dexs' Napier
and fees' Napier; extract this map into a single exported constant (e.g.,
NAPIER_CHAIN_CONFIG) in a small shared module (e.g., napierConfig) and replace
the local chainConfig declarations in both dexs' and fees' Napier code with
imports of that exported constant; update references to use the shared symbol
(chainConfig or NAPIER_CHAIN_CONFIG) so future chain/start-date changes are made
in one place.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 029610cb-d2ee-4bcb-86e7-fc520a2fe004
📒 Files selected for processing (2)
dexs/napier/index.staging.tsdexs/napier/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- dexs/napier/index.staging.ts
…type check, fix methodology
|
The napier adapter exports: |
|
The napier adapter exports: |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
fees/napier/index.ts (2)
105-159: MoveConfigtype definition before its usage.The
Configtype is defined at lines 156-159 but referenced at line 105. While TypeScript hoisting makes this work, placing type definitions before their usage improves readability.Suggested reordering
+type Config = { + treasury: string; + start: string; +}; + const chainConfig: Record<Chain, Config> = { [CHAIN.ETHEREUM]: { // ... }; - -type Config = { - treasury: string; - start: string; -};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@fees/napier/index.ts` around lines 105 - 159, The Config type is declared after it's used in the chainConfig declaration; move the type declaration for Config above the chainConfig constant so the type is defined before being referenced (ensure the type name remains Config and update only ordering, leaving chainConfig and CHAIN entries unchanged).
56-63:dailySupplySideRevenueshould checkcuratorFee > 0for consistency.The PR summary states "sum of
dailyCuratorFeeInUsdonly when positive", but the code addscuratorFeewheneverfee > 0without checkingcuratorFee's sign. This could add zero or negative values todailySupplySideRevenue.Suggested fix
for (const market of markets) { const fee = Number(market.metrics?.dailyFeeInUsd ?? 0); const curatorFee = Number(market.metrics?.dailyCuratorFeeInUsd ?? 0); if (fee > 0) { dailyFees.addUSDValue(fee); - dailySupplySideRevenue.addUSDValue(curatorFee); + } + if (curatorFee > 0) { + dailySupplySideRevenue.addUSDValue(curatorFee); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@fees/napier/index.ts` around lines 56 - 63, The loop currently adds curatorFee to dailySupplySideRevenue whenever fee > 0, but the requirement is to sum dailyCuratorFeeInUsd only when it's positive; update the logic in the loop that iterates over markets (where fee = Number(market.metrics?.dailyFeeInUsd ?? 0) and curatorFee = Number(market.metrics?.dailyCuratorFeeInUsd ?? 0)) to only call dailySupplySideRevenue.addUSDValue(curatorFee) when curatorFee > 0 (in addition to the existing fee > 0 check), ensuring both dailyFees.addUSDValue(fee) and dailySupplySideRevenue.addUSDValue(curatorFee) respect their own positivity checks.dexs/napier/index.ts (1)
103-116: Consider edge case: bothsold_idandbought_idcould theoretically be1n.The logic assumes exactly one of
sold_idorbought_idequals1n(the target token index). If the Curve pool configuration allows swapping target token for itself (unlikely but worth confirming), both conditions could match andtargetAmountwould usetokensSold.The current logic is likely correct for Napier's Curve pool configuration. If you want to be defensive:
let targetAmount: bigint; if (soldId === 1n) { targetAmount = tokensSold; - } else if (boughtId === 1n) { + } else if (boughtId === 1n && soldId !== 1n) { targetAmount = tokensBought; } else { continue; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@dexs/napier/index.ts` around lines 103 - 116, The code assumes exactly one of soldId or boughtId equals 1n; make this defensive by handling the case where both equal 1n explicitly: update the targetAmount assignment in the block that uses soldId, boughtId, tokensSold, and tokensBought so that if both soldId === 1n && boughtId === 1n you choose a deterministic behavior (e.g., set targetAmount = tokensSold or skip the entry), otherwise keep the existing branches; then continue converting targetAmount to assetAmount and calling dailyVolume.add as before.
🤖 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/napier/index.ts`:
- Around line 103-116: The code assumes exactly one of soldId or boughtId equals
1n; make this defensive by handling the case where both equal 1n explicitly:
update the targetAmount assignment in the block that uses soldId, boughtId,
tokensSold, and tokensBought so that if both soldId === 1n && boughtId === 1n
you choose a deterministic behavior (e.g., set targetAmount = tokensSold or skip
the entry), otherwise keep the existing branches; then continue converting
targetAmount to assetAmount and calling dailyVolume.add as before.
In `@fees/napier/index.ts`:
- Around line 105-159: The Config type is declared after it's used in the
chainConfig declaration; move the type declaration for Config above the
chainConfig constant so the type is defined before being referenced (ensure the
type name remains Config and update only ordering, leaving chainConfig and CHAIN
entries unchanged).
- Around line 56-63: The loop currently adds curatorFee to
dailySupplySideRevenue whenever fee > 0, but the requirement is to sum
dailyCuratorFeeInUsd only when it's positive; update the logic in the loop that
iterates over markets (where fee = Number(market.metrics?.dailyFeeInUsd ?? 0)
and curatorFee = Number(market.metrics?.dailyCuratorFeeInUsd ?? 0)) to only call
dailySupplySideRevenue.addUSDValue(curatorFee) when curatorFee > 0 (in addition
to the existing fee > 0 check), ensuring both dailyFees.addUSDValue(fee) and
dailySupplySideRevenue.addUSDValue(curatorFee) respect their own positivity
checks.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e385c25d-2895-4268-b606-e2c556d499af
📒 Files selected for processing (2)
dexs/napier/index.tsfees/napier/index.ts
Summary
Linear: NAP-2375 — DefiLlama Dashboard Enhancement
Fees adapter (
fees/napier/index.ts) — Rewrite to API-baseddailyFeeInUsdfrom napier-apiaddTokensReceivedDexs adapter (
dexs/napier/index.ts) — New production adapterTokenExchangeeventsHookSwapevents (filtered by poolId)Methodology
Fees:
Revenue:
Volume:
Supported chains (both adapters)
Ethereum, Base, Sonic, Arbitrum, Optimism, Fraxtal, Mantle, BSC, Polygon, Avalanche, HyperEVM
Test plan
Summary by CodeRabbit
New Features
Chores
Refactor