Skip to content

dexfinance refactor#18705

Open
RohanNero wants to merge 4 commits intoDefiLlama:mainfrom
RohanNero:dexfinance-refactor
Open

dexfinance refactor#18705
RohanNero wants to merge 4 commits intoDefiLlama:mainfrom
RohanNero:dexfinance-refactor

Conversation

@RohanNero
Copy link
Copy Markdown
Contributor

@RohanNero RohanNero commented Apr 9, 2026

resolves #18625

Summary by CodeRabbit

  • New Features

    • Added DexFinance Vault V2 TVL tracking on Sonic, Avalanche, BSC, Ethereum, and Arbitrum.
    • Added TVL and staking aggregation for DexFinance Vault V2 on Base with Base-specific balance adjustments.
    • Support for NFT-backed vault positions with automatic flavor detection (Shadow, Slipstream, Algebra) and grouped token handling.
    • Enhanced LP/NFT unwrapping to better handle Algebra and shadow-style NFTs and accurately attribute liquidity.
  • Chores

    • Exported updated NFT unwrapping helper for reuse.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

📝 Walkthrough

Walkthrough

Adds a new DexFi adapter module implementing per-chain TVL logic and Base-specific staking adjustments; extends helper unwrapping utilities to support Algebra and Shadow (RamsesV3) NFT position variants and exports a new NFT unwrap helper.

Changes

Cohort / File(s) Summary
DexFi Adapter Core
projects/dexfinance-vault-v2/index.js
New module exporting tvl(api) for sonic, avax, bsc, ethereum, arbitrum and tvl(api) + staking(api) for base. Enumerates vaults/farms, classifies NFT vs ERC20/LP farms, detects NFT flavor (RamsesV3/Slipstream/Algebra/UNI), unwraps NFT positions or sums connector liquidity, and applies Base-specific gDEX pool/staking balance adjustments.
NFT / LP Unwrapping Helpers
projects/helper/unwrapLPs.js
Added export unwrapUniswapV3NFT. Extended unwrapUniswapV3NFT(..., isAlgebra = false) to support Algebra-specific ABIs and pool discovery; extended unwrapSlipstreamNFT(..., isShadow = false) to support Shadow/RamsesV3 positions and alternate factory/deployer resolution. Adjusted pool/tick discovery and UNI extras handling.

Sequence Diagram(s)

sequenceDiagram
    participant API as API Instance
    participant Factory as Vault Factory
    participant Vault as Vault Contract
    participant Farm as Farm Connector
    participant NFTDetect as NFT/Token Detector
    participant AMMUnwrap as AMM Unwrapper (Slipstream/V3/Algebra)
    participant Aggregator as Token Aggregator

    API->>Factory: fetchVaults()
    Factory-->>API: vault list
    loop per vault
      API->>Vault: getFarms()
      Vault-->>API: farm addresses
      loop per farm
        API->>Farm: stakingToken(), tokenId?()
        Farm-->>API: token / tokenId
        alt NFT position
          API->>NFTDetect: probeNFT(contract)
          NFTDetect-->>API: flavor (Ramses/Slipstream/Algebra/UNI)
          API->>AMMUnwrap: unwrap(flavor, tokenIds)
          AMMUnwrap-->>API: underlying tokens & amounts
        else ERC20/LP
          API->>Farm: liquidity()
          Farm-->>API: balances
        end
        API->>Aggregator: addBalances(...)
      end
    end
    Aggregator->>Aggregator: resolveLPs & aggregate
    Aggregator-->>API: total TVL
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through vaults on six-chain nights,
gDEX glints, NFT moons and algebraic lights,
Slipstream, Shadow, positions untied,
Connectors whisper, balances glide,
A rabbit counts tokens with joy and delight.

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description only contains 'resolves #18625' with no additional context, failing to follow the repository template or explain the changes being made. Provide a detailed description following the template, including project information, methodology, and rationale for removing legacy adapters and adding the new DexFi adapter.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'dexfinance refactor' is vague and generic, failing to convey the primary change of adding a new DexFi adapter and removing legacy adapters. Consider a more specific title such as 'Add DexFi vault v2 adapter and refactor dexfinance modules' that clearly describes the main change.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The code changes implement the core requirements from issue #18625: new DexFi adapter for multiple chains with TVL computation, support for staking on Base, and no new npm dependencies added.
Out of Scope Changes check ✅ Passed All changes are scoped to the DexFi adapter implementation and related helper functions. The refactoring of unwrapLPs.js to support Algebra and Shadow NFT protocols is necessary for the adapter's NFT position handling.

✏️ 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 adapter at projects/dexfinance-vault-v2 exports TVL:

base-staking              2.08 M
staking                   2.08 M
base                      1.72 M
ethereum                  301.09 k
arbitrum                  62.86 k
bsc                       42.47 k
avax                      13.38 k
sonic                     7.18 k

total                    2.15 M 

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 (2)
projects/dexfinance-vault-v2/index.js (2)

89-123: Well-structured flavor detection for NFT positions.

The detection logic correctly identifies four NFT position flavors:

  1. Shadow: Detected via deployerRamsesV3Factory chain
  2. Slipstream: Detected via poolImplementation on factory
  3. Algebra: Detected via poolByPair function availability
  4. Standard UniV3: Fallback via sumTokens2 with uniV3ExtraConfig

Minor simplification possible:

♻️ Simplify null check
- else if (poolByPairResults[j] !== null && poolByPairResults[j] !== undefined) algebraSet.add(f.idx);
+ else if (poolByPairResults[j] != null) algebraSet.add(f.idx);

The != null check covers both null and undefined.

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

In `@projects/dexfinance-vault-v2/index.js` around lines 89 - 123, The
null/undefined check in the factory detection loop is verbose; in
factoriesWithIndex.forEach where you currently do "if (poolImplResults[j])
slipstreamSet.add(f.idx); else if (poolByPairResults[j] !== null &&
poolByPairResults[j] !== undefined) algebraSet.add(f.idx);", replace the latter
condition with the simpler "poolByPairResults[j] != null" to cover both null and
undefined; update the condition referencing poolByPairResults[j] accordingly to
keep the same behavior.

48-48: Avoid mutating input objects.

delete item.farm.data mutates the original farm object. While this likely works since vaultFarms is freshly created, it's safer to avoid mutation:

♻️ Suggested change to avoid mutation
- delete item.farm.data;
- return { ...item, connector };
+ const { data, ...farmWithoutData } = item.farm;
+ return { ...item, farm: farmWithoutData, connector };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@projects/dexfinance-vault-v2/index.js` at line 48, The code currently mutates
the farm object with "delete item.farm.data"; instead, create a non-mutating
copy and assign that to item.farm (for example by shallow-cloning item or
item.farm and omitting the data property) so vaultFarms remains derived without
altering the original objects; locate the transformation where vaultFarms is
built (the loop/map that references item and item.farm) and replace the delete
with creation of a new farm object that excludes data, then set item.farm to
that new object or build a new item object for the returned array.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@projects/helper/unwrapLPs.js`:
- Around line 342-343: The ABI string stored in algebraPositionsABI incorrectly
types the nonce as uint96 causing decoding errors; update the ABI declaration
for algebraPositionsABI so the positions(uint256) return tuple uses uint88 nonce
(matching the contract) instead of uint96, ensuring the rest of the return types
remain unchanged and references to algebraPositionsABI or the positions()
decoding logic will now match the on-chain contract.

---

Nitpick comments:
In `@projects/dexfinance-vault-v2/index.js`:
- Around line 89-123: The null/undefined check in the factory detection loop is
verbose; in factoriesWithIndex.forEach where you currently do "if
(poolImplResults[j]) slipstreamSet.add(f.idx); else if (poolByPairResults[j] !==
null && poolByPairResults[j] !== undefined) algebraSet.add(f.idx);", replace the
latter condition with the simpler "poolByPairResults[j] != null" to cover both
null and undefined; update the condition referencing poolByPairResults[j]
accordingly to keep the same behavior.
- Line 48: The code currently mutates the farm object with "delete
item.farm.data"; instead, create a non-mutating copy and assign that to
item.farm (for example by shallow-cloning item or item.farm and omitting the
data property) so vaultFarms remains derived without altering the original
objects; locate the transformation where vaultFarms is built (the loop/map that
references item and item.farm) and replace the delete with creation of a new
farm object that excludes data, then set item.farm to that new object or build a
new item object for the returned array.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2dacaa8a-5667-43f3-9dc7-710bbf3eacf1

📥 Commits

Reviewing files that changed from the base of the PR and between 3d42b21 and bb96118.

📒 Files selected for processing (2)
  • projects/dexfinance-vault-v2/index.js
  • projects/helper/unwrapLPs.js

@llamabutler
Copy link
Copy Markdown

The adapter at projects/dexfinance-vault-v2 exports TVL:

base-staking              2.12 M
staking                   2.12 M
base                      1.86 M
ethereum                  330.03 k
arbitrum                  64.32 k
bsc                       44.84 k
avax                      13.15 k
sonic                     7.29 k

total                    2.32 M 

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

🧹 Nitpick comments (1)
projects/dexfinance-vault-v2/index.js (1)

44-51: Avoid mutating input object.

delete item.farm.data mutates the original array elements passed to this function. While harmless in current usage, this is a side effect that could cause unexpected behavior if vaultFarms is reused.

♻️ Suggested fix: Create new object instead of mutating
   return vaultFarms
     .map((item, i) => {
       const connector = connectors[i];
       if (!connector) return null;
-      delete item.farm.data;
-      return { ...item, connector };
+      const { data, ...farmWithoutData } = item.farm;
+      return { ...item, farm: farmWithoutData, connector };
     }).filter(item => item !== null);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@projects/dexfinance-vault-v2/index.js` around lines 44 - 51, The code mutates
input objects by calling delete on item.farm.data; instead, return a new object
with connector and a copied farm that omits data. Locate the map that iterates
vaultFarms and uses connectors (variables: vaultFarms, connectors, item,
connector) and replace the mutation with creation of a newItem that clones item
and its farm (or destructures farm to exclude data) and then return {
...newItem, connector } so the original vaultFarms elements are not modified.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@projects/dexfinance-vault-v2/index.js`:
- Around line 111-114: The current classification loop using factoriesWithIndex
incorrectly treats a zero address from poolByPairResults[j] as a valid Algebra
pool; update the condition inside the forEach (which references
factoriesWithIndex, poolImplResults, poolByPairResults, slipstreamSet,
algebraSet, and f.idx) to also ensure poolByPairResults[j] is not the zero
address (e.g. compare against '0x0000000000000000000000000000000000000000' or a
shared ADDRESS_ZERO constant) before adding f.idx to algebraSet, keeping the
existing slipstreamSet check intact.
- Around line 131-137: The liquidity() multicall is currently using
abi.vault.liquidity with the connector address as target (see liquidityCalls and
abi.vault.liquidity), which may be wrong: either connectors must implement
liquidity(address) or the call should target the vault (as in v1 where
calculationConnector was used). Update the multicall to target the correct
contract (vault or calculationConnector) and/or move the liquidity ABI to the
correct namespace to reflect its real target, and add a short code comment
documenting which contract (connector vs vault) is expected to expose
liquidity(address); keep permitFailure: true only if silent failures are
intentional.

---

Nitpick comments:
In `@projects/dexfinance-vault-v2/index.js`:
- Around line 44-51: The code mutates input objects by calling delete on
item.farm.data; instead, return a new object with connector and a copied farm
that omits data. Locate the map that iterates vaultFarms and uses connectors
(variables: vaultFarms, connectors, item, connector) and replace the mutation
with creation of a newItem that clones item and its farm (or destructures farm
to exclude data) and then return { ...newItem, connector } so the original
vaultFarms elements are not modified.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: f4a39aa8-6b3c-4c69-a29a-6a026e09e97b

📥 Commits

Reviewing files that changed from the base of the PR and between bb96118 and e109a01.

📒 Files selected for processing (1)
  • projects/dexfinance-vault-v2/index.js

Comment on lines +111 to +114
factoriesWithIndex.forEach((f, j) => {
if (poolImplResults[j]) slipstreamSet.add(f.idx);
else if (poolByPairResults[j] !== null && poolByPairResults[j] !== undefined) algebraSet.add(f.idx);
});
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 | 🟡 Minor

Algebra detection may have false positives with zero address.

The check poolByPairResults[j] !== null && poolByPairResults[j] !== undefined would pass if poolByPair returns the zero address for non-existent pairs. This could incorrectly classify non-Algebra factories as Algebra.

🐛 Suggested fix: Also check for zero address
   factoriesWithIndex.forEach((f, j) => {
     if (poolImplResults[j]) slipstreamSet.add(f.idx);
-    else if (poolByPairResults[j] !== null && poolByPairResults[j] !== undefined) algebraSet.add(f.idx);
+    else if (poolByPairResults[j] && poolByPairResults[j] !== ADDRESSES.null) algebraSet.add(f.idx);
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@projects/dexfinance-vault-v2/index.js` around lines 111 - 114, The current
classification loop using factoriesWithIndex incorrectly treats a zero address
from poolByPairResults[j] as a valid Algebra pool; update the condition inside
the forEach (which references factoriesWithIndex, poolImplResults,
poolByPairResults, slipstreamSet, algebraSet, and f.idx) to also ensure
poolByPairResults[j] is not the zero address (e.g. compare against
'0x0000000000000000000000000000000000000000' or a shared ADDRESS_ZERO constant)
before adding f.idx to algebraSet, keeping the existing slipstreamSet check
intact.

Comment on lines +131 to +137
// ERC20 tokens and LPs
const liquidityCalls = tokenFarms.map(({ connector }) => ({ target: connector, params: [connector] }));
const liquidities = await api.multiCall({ calls: liquidityCalls, abi: abi.vault.liquidity, permitFailure: true });
tokenFarms.forEach(({ stakingToken }, i) => {
if (liquidities[i]) api.add(stakingToken, liquidities[i]);
});
await sumTokens2({ api, resolveLP: true });
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify if liquidity() is called on vault or connector in existing dexfinance adapters
echo "=== Checking existing dexfinance adapter patterns ==="
rg -n "abi\.vault\.liquidity" projects/dexfinance*/

echo ""
echo "=== Checking how liquidity calls are structured ==="
rg -nB2 -A2 "liquidityCalls" projects/dexfinance*/

Repository: DefiLlama/DefiLlama-Adapters

Length of output: 2996


🏁 Script executed:

#!/bin/bash
# Check what properties are available in tokenFarms
echo "=== Checking tokenFarms structure in getChainFarms ==="
rg -nB5 -A10 "function getChainFarms" projects/dexfinance-vault-v2/

echo ""
echo "=== Checking farm/vault structure ==="
rg -n "connector\|vault\|farm" projects/dexfinance-vault-v2/index.js | head -30

echo ""
echo "=== Looking for ABI definitions ==="
rg -n "abi" projects/dexfinance-vault-v2/index.js | head -20

Repository: DefiLlama/DefiLlama-Adapters

Length of output: 1860


🏁 Script executed:

#!/bin/bash
# Check the beginning of dexfinance-vault-v2/index.js to find getChainFarms
echo "=== First 150 lines of dexfinance-vault-v2/index.js ==="
head -150 projects/dexfinance-vault-v2/index.js | cat -n

echo ""
echo "=== Check the ABI definitions ==="
cat projects/dexfinance-vault/abi.js

Repository: DefiLlama/DefiLlama-Adapters

Length of output: 9338


Clarify ABI usage for liquidity() calls.

The code calls abi.vault.liquidity on the connector address (line 132), but the ABI is defined under vault: in the ABI file. This differs from v1 dexfinance adapter which calls the same method on calculationConnector instead. If v2 connectors expose their own liquidity(address) method, this architectural change should be documented. If not, the method should be called on the vault address with the connector as a parameter (as in v1), and the ABI should be repositioned accordingly to reflect its actual target.

The same pattern appears at line 163-164. The permitFailure: true flag masks call failures, which is acceptable if intentional, but the target mismatch should be clarified.

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

In `@projects/dexfinance-vault-v2/index.js` around lines 131 - 137, The
liquidity() multicall is currently using abi.vault.liquidity with the connector
address as target (see liquidityCalls and abi.vault.liquidity), which may be
wrong: either connectors must implement liquidity(address) or the call should
target the vault (as in v1 where calculationConnector was used). Update the
multicall to target the correct contract (vault or calculationConnector) and/or
move the liquidity ABI to the correct namespace to reflect its real target, and
add a short code comment documenting which contract (connector vs vault) is
expected to expose liquidity(address); keep permitFailure: true only if silent
failures are intentional.

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.

Caution

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

⚠️ Outside diff range comments (1)
projects/helper/unwrapLPs.js (1)

431-451: ⚠️ Potential issue | 🟠 Major

Algebra path bypasses blacklistedPools filtering.

Line 448 currently skips pool blacklist enforcement when isAlgebra is true. That creates a behavior gap and can include pools callers explicitly intended to exclude.

Proposed fix
-    if (!isAlgebra) {
-      const poolKey = `${token0.toLowerCase()}-${token1.toLowerCase()}-${position.fee}`.toLowerCase()
-      if (blacklistedPools.includes(poolKey)) return
-    }
+    const poolKey = isAlgebra
+      ? `${token0.toLowerCase()}-${token1.toLowerCase()}`.toLowerCase()
+      : `${token0.toLowerCase()}-${token1.toLowerCase()}-${position.fee}`.toLowerCase()
+    if (blacklistedPools.includes(poolKey)) return
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@projects/helper/unwrapLPs.js` around lines 431 - 451, The Algebra branch in
addV3PositionBalances bypasses the pool blacklist check; ensure the same
blacklistedPools filtering runs for Algebra positions by constructing the pool
key from token0, token1 and position.fee (use the same normalization as the
non-Algebra path, e.g., token0.toLowerCase()-token1.toLowerCase()-position.fee)
and returning early if blacklistedPools.includes(poolKey) before proceeding;
keep the check centralized in addV3PositionBalances and still preserve the
existing positionId/blacklistedPositionIds logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@projects/helper/unwrapLPs.js`:
- Around line 431-451: The Algebra branch in addV3PositionBalances bypasses the
pool blacklist check; ensure the same blacklistedPools filtering runs for
Algebra positions by constructing the pool key from token0, token1 and
position.fee (use the same normalization as the non-Algebra path, e.g.,
token0.toLowerCase()-token1.toLowerCase()-position.fee) and returning early if
blacklistedPools.includes(poolKey) before proceeding; keep the check centralized
in addV3PositionBalances and still preserve the existing
positionId/blacklistedPositionIds logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cffbd546-1382-4748-9011-68e29563d77e

📥 Commits

Reviewing files that changed from the base of the PR and between e109a01 and 23fdd12.

📒 Files selected for processing (1)
  • projects/helper/unwrapLPs.js

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.

2 participants