add adapter for raydium vault#18521
Conversation
📝 WalkthroughWalkthroughAdds a new Solana TVL module that derives vault PDAs, fetches vault and pool accounts in parallel, decodes vault/position/pool/stake-pool state, computes concentrated-liquidity token amounts and truSOL price, and returns total TVL denominated in SOL. Changes
Sequence DiagramsequenceDiagram
participant TVL as TVL Calculator
participant RPC as Solana RPC
participant Vault as Vault Accounts
participant Pool as Raydium CLMM Pool
participant StakePool as StakePool
TVL->>TVL: Derive vault PDAs (state, ATA, position NFT)
par Parallel Fetches
TVL->>RPC: Fetch vault SOL balance
TVL->>RPC: Fetch WSOL/truSOL token accounts
TVL->>RPC: Fetch vault position account
TVL->>RPC: Fetch Raydium pool account
TVL->>RPC: Fetch stake pool account
end
RPC-->>TVL: Return account data
TVL->>TVL: Decode vault state (tick bounds, pool address)
TVL->>TVL: Decode position account details
TVL->>TVL: Decode Raydium pool data
TVL->>StakePool: Read stake pool totals/supply
StakePool-->>TVL: Return totals for truSOL price calc
TVL->>TVL: Calculate truSOL price
TVL->>TVL: Compute position token amounts using tickToPrice
TVL->>TVL: Sum vault SOL, direct token balances, position amounts, accrued fees
TVL->>TVL: Convert to SOL (divide by 1e9)
TVL-->>TVL: Return { solana: totalSol }
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 adapter at projects/trufin-vaults exports TVL: |
|
The adapter at projects/trufin-vaults exports TVL: |
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 `@projects/trufin-vaults/index.js`:
- Around line 121-123: The adapter exports live-only Solana TVL via
module.exports with solana.tvl set to raydiumVaultTvl but does not mark it
latest-only; add the timetravel flag to the export by setting timetravel: false
alongside tvl (i.e., update the exported solana object that references
raydiumVaultTvl to include timetravel: false) so historical runs won't replay
current RPC state.
- Around line 104-116: The code assumes pool side A is WSOL and B is truSOL when
computing positionWsol/positionTrusol; instead, inspect the pool's token
ordering (e.g., pool.tokenMintA and pool.tokenMintB) after
decodeAccount("raydiumCLMM", poolAccount) and map amount0/amount1 and
tokenFeesOwedA/tokenFeesOwedB to the correct buckets accordingly before valuing:
if tokenMintA equals WSOL_MINT then treat amount0+tokenFeesOwedA as positionWsol
and amount1+tokenFeesOwedB as positionTrusol, otherwise swap them; then compute
positionWsol/positionTrusol and totalSol using trusolPrice as before so TVL uses
the correct side mapping for getPositionAmounts outputs and tokenFeesOwedA/B.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7ed7623e-c7ae-4042-885f-6a56b897f25f
📒 Files selected for processing (1)
projects/trufin-vaults/index.js
| const position = decodeAccount("raydiumPositionInfo", positionAccount); | ||
| const pool = decodeAccount("raydiumCLMM", poolAccount); | ||
|
|
||
| const { amount0, amount1 } = getPositionAmounts( | ||
| position.tickLower, position.tickUpper, pool.tickCurrent, | ||
| Number(position.liquidity.toString()), | ||
| ); | ||
|
|
||
| const positionWsol = amount0 + Number(position.tokenFeesOwedA.toString()); | ||
| const positionTrusol = amount1 + Number(position.tokenFeesOwedB.toString()); | ||
|
|
||
| const totalSol = solBalance + wsolBalance + positionWsol | ||
| + (trusolBalance + positionTrusol) * trusolPrice; |
There was a problem hiding this comment.
Map CLMM side A/B to WSOL/truSOL before valuing the position.
amount0/amount1 and tokenFeesOwedA/B are pool-side values, but this code hard-codes side A as WSOL and side B as truSOL. If the Raydium pool uses the opposite mint ordering, the position portion of TVL is mispriced. Read the pool’s token ordering first, then route each side into the WSOL/truSOL buckets before applying trusolPrice.
🧭 Minimal shape of the fix
- const positionWsol = amount0 + Number(position.tokenFeesOwedA.toString());
- const positionTrusol = amount1 + Number(position.tokenFeesOwedB.toString());
+ const isWsolTokenA = /* compare the decoded pool token ordering against WSOL_MINT/TRUSOL_MINT */;
+
+ const positionWsol = isWsolTokenA
+ ? amount0 + Number(position.tokenFeesOwedA.toString())
+ : amount1 + Number(position.tokenFeesOwedB.toString());
+ const positionTrusol = isWsolTokenA
+ ? amount1 + Number(position.tokenFeesOwedB.toString())
+ : amount0 + Number(position.tokenFeesOwedA.toString());🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@projects/trufin-vaults/index.js` around lines 104 - 116, The code assumes
pool side A is WSOL and B is truSOL when computing positionWsol/positionTrusol;
instead, inspect the pool's token ordering (e.g., pool.tokenMintA and
pool.tokenMintB) after decodeAccount("raydiumCLMM", poolAccount) and map
amount0/amount1 and tokenFeesOwedA/tokenFeesOwedB to the correct buckets
accordingly before valuing: if tokenMintA equals WSOL_MINT then treat
amount0+tokenFeesOwedA as positionWsol and amount1+tokenFeesOwedB as
positionTrusol, otherwise swap them; then compute positionWsol/positionTrusol
and totalSol using trusolPrice as before so TVL uses the correct side mapping
for getPositionAmounts outputs and tokenFeesOwedA/B.
| module.exports = { | ||
| methodology: "Counts the TVL of the TruFin Raydium Vault.", | ||
| solana: { tvl: raydiumVaultTvl }, |
There was a problem hiding this comment.
Mark this adapter as latest-only.
raydiumVaultTvl always reads live Solana RPC state and never consumes historical inputs. Without timetravel: false, historical runs can replay current TVL as if it were past data.
🗓️ Minimal export fix
module.exports = {
+ timetravel: false,
methodology: "Counts the TVL of the TruFin Raydium Vault.",
solana: { tvl: raydiumVaultTvl },
};Based on learnings: In DefiLlama adapters, timetravel is a valid export key.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@projects/trufin-vaults/index.js` around lines 121 - 123, The adapter exports
live-only Solana TVL via module.exports with solana.tvl set to raydiumVaultTvl
but does not mark it latest-only; add the timetravel flag to the export by
setting timetravel: false alongside tvl (i.e., update the exported solana object
that references raydiumVaultTvl to include timetravel: false) so historical runs
won't replay current RPC state.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
projects/trufin-vaults/index.js (2)
119-122:⚠️ Potential issue | 🟠 MajorMark this adapter as latest-only.
raydiumVaultTvlonly reads live Solana RPC state and never consumes historical inputs, so historical runs will replay current TVL unlesstimetravel: falseis exported.🗓️ Minimal export fix
module.exports = { + timetravel: false, methodology: "Counts the TVL of the TruFin Raydium Vault.", solana: { tvl: raydiumVaultTvl }, };Based on learnings: In DefiLlama adapters,
timetravelis a valid export key.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@projects/trufin-vaults/index.js` around lines 119 - 122, The adapter is currently replaying live RPC state for historical runs; update the module.exports object to mark this adapter as latest-only by adding timetravel: false to the exported object (the same object that contains methodology and solana), e.g., add the key timetravel: false alongside methodology and solana so raydiumVaultTvl is treated as non-timetravelable.
102-111:⚠️ Potential issue | 🟠 MajorUse the decoded pool ordering when splitting A/B balances.
You decode
poolon Line 103, but Lines 110-111 still hard-code side A as WSOL and side B as truSOL. If Raydium stores the pair in the opposite order, both the position liquidity and accrued fees are assigned to the wrong asset.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@projects/trufin-vaults/index.js` around lines 102 - 111, The code assumes amount0/amount1 and tokenFeesOwedA/tokenFeesOwedB map to WSOL/truSOL (positionWsol/positionTrusol) regardless of pool ordering; update the logic that sets positionWsol and positionTrusol (and any uses of position amounts) to consult the decoded pool ordering from pool (e.g., pool.tokenA/tokenB or pool.tokenOrder) and assign amount0 + tokenFeesOwedA to whichever asset is the first token and amount1 + tokenFeesOwedB to the second token, swapping assignments when the pair is stored reversed so balances and fees match the actual asset mints returned by decodeAccount("raydiumCLMM", poolAccount).
🤖 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/trufin-vaults/index.js`:
- Around line 119-122: The module export for this project currently exposes
methodology and solana: { tvl: raydiumVaultTvl } but does not mark that the TVL
is actually counted inside Raydium CLMM pools; update the exported object
(module.exports) to include doublecounted: true so the overlap with
projects/raydium/index.js is surfaced (i.e., add doublecounted: true at the
top-level export alongside methodology and solana).
- Around line 116-121: raydiumVaultTvl is already exported under module.exports
as solana.tvl, so its return should be the native/token balance payload itself
rather than a chain-scoped wrapper; update the return in raydiumVaultTvl to
return the balances object (e.g., the mapping of token identifiers to amounts or
the native token balance) directly instead of returning { solana: ... }, leaving
module.exports = { methodology: ..., solana: { tvl: raydiumVaultTvl } }
unchanged.
---
Duplicate comments:
In `@projects/trufin-vaults/index.js`:
- Around line 119-122: The adapter is currently replaying live RPC state for
historical runs; update the module.exports object to mark this adapter as
latest-only by adding timetravel: false to the exported object (the same object
that contains methodology and solana), e.g., add the key timetravel: false
alongside methodology and solana so raydiumVaultTvl is treated as
non-timetravelable.
- Around line 102-111: The code assumes amount0/amount1 and
tokenFeesOwedA/tokenFeesOwedB map to WSOL/truSOL (positionWsol/positionTrusol)
regardless of pool ordering; update the logic that sets positionWsol and
positionTrusol (and any uses of position amounts) to consult the decoded pool
ordering from pool (e.g., pool.tokenA/tokenB or pool.tokenOrder) and assign
amount0 + tokenFeesOwedA to whichever asset is the first token and amount1 +
tokenFeesOwedB to the second token, swapping assignments when the pair is stored
reversed so balances and fees match the actual asset mints returned by
decodeAccount("raydiumCLMM", poolAccount).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9cede83c-2451-4388-96b6-8c1cad21bffb
📒 Files selected for processing (1)
projects/trufin-vaults/index.js
| return { solana: totalSol / 1e9 }; | ||
| } | ||
|
|
||
| module.exports = { | ||
| methodology: "Counts the TVL of the TruFin Raydium Vault.", | ||
| solana: { tvl: raydiumVaultTvl }, |
There was a problem hiding this comment.
Match the solana.tvl return contract.
On Line 121 this function is already mounted under the Solana chain. Returning { solana: ... } again on Line 116 is what llamabutler is flagging as “exports TVL values twice”; return the native/token balance payload directly instead of another chain-scoped wrapper.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@projects/trufin-vaults/index.js` around lines 116 - 121, raydiumVaultTvl is
already exported under module.exports as solana.tvl, so its return should be the
native/token balance payload itself rather than a chain-scoped wrapper; update
the return in raydiumVaultTvl to return the balances object (e.g., the mapping
of token identifiers to amounts or the native token balance) directly instead of
returning { solana: ... }, leaving module.exports = { methodology: ..., solana:
{ tvl: raydiumVaultTvl } } unchanged.
| module.exports = { | ||
| methodology: "Counts the TVL of the TruFin Raydium Vault.", | ||
| solana: { tvl: raydiumVaultTvl }, | ||
| }; |
There was a problem hiding this comment.
Flag the Raydium overlap as external double counting.
This vault TVL is capital parked inside a Raydium CLMM position, and projects/raydium/index.js:1-35 already sums every CLMM pool’s vaultA/vaultB balances. Add doublecounted: true so the overlap is surfaced correctly on the protocol page.
🔁 Minimal export fix
module.exports = {
+ doublecounted: true,
methodology: "Counts the TVL of the TruFin Raydium Vault.",
solana: { tvl: raydiumVaultTvl },
};Based on learnings: doublecounted: true remains appropriate when a vault deploys assets into an external protocol that DefiLlama tracks independently.
📝 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.
| module.exports = { | |
| methodology: "Counts the TVL of the TruFin Raydium Vault.", | |
| solana: { tvl: raydiumVaultTvl }, | |
| }; | |
| module.exports = { | |
| doublecounted: true, | |
| methodology: "Counts the TVL of the TruFin Raydium Vault.", | |
| solana: { tvl: raydiumVaultTvl }, | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@projects/trufin-vaults/index.js` around lines 119 - 122, The module export
for this project currently exposes methodology and solana: { tvl:
raydiumVaultTvl } but does not mark that the TVL is actually counted inside
Raydium CLMM pools; update the exported object (module.exports) to include
doublecounted: true so the overlap with projects/raydium/index.js is surfaced
(i.e., add doublecounted: true at the top-level export alongside methodology and
solana).
NOTE
Please enable "Allow edits by maintainers" while putting up the PR.
If you would like to add a
volume/fees/revenueadapter please submit the PR here.Once your adapter has been merged, it takes time to show on the UI. If more than 24 hours have passed, please let us know in Discord.
Sorry, We no longer accept fetch adapter for new projects, we prefer the tvl to computed from blockchain data, if you have trouble with creating a the adapter, please hop onto our discord, we are happy to assist you.
For updating listing info It is a different repo, you can find your listing in this file, you can edit it there and put up a PR
Please do not add new npm dependencies, do not edit/push
pnpm-lock.yamlfile as part of your changes(Needs to be filled only for new listings)
Name (to be shown on DefiLlama): TruVault
Twitter Link: https://x.com/TruYields
List of audit links if any:
Website Link: https://app.truyields.com
Logo (High resolution, will be shown with rounded borders): https://truyields.com/brand/mark-primary.png
Current TVL:
Treasury Addresses (if the protocol has treasury)
Chain: Solana
Coingecko ID (so your TVL can appear on Coingecko, leave empty if not listed): (https://api.coingecko.com/api/v3/coins/list)
Coinmarketcap ID (so your TVL can appear on Coinmarketcap, leave empty if not listed): (https://api.coinmarketcap.com/data-api/v3/map/all?listing_status=active,inactive,untracked&start=1&limit=10000)
Short Description (to be shown on DefiLlama):
Token address and ticker if any:
Category (full list at https://defillama.com/categories) *Please choose only one: Yield
Oracle Provider(s): Specify the oracle(s) used (e.g., Chainlink, Band, API3, TWAP, etc.):
Implementation Details: Briefly describe how the oracle is integrated into your project:
Documentation/Proof: Provide links to documentation or any other resources that verify the oracle's usage:
forkedFrom (Does your project originate from another project):
methodology (what is being counted as tvl, how is tvl being calculated):
Github org/user (Optional, if your code is open source, we can track activity):
Does this project have a referral program?
TruVaults is a new listing under the parent https://defillama.com/protocol/trufin-protocol
TruFin has undergone a rebrand and we are now called TruYields. I need help with updating the website url, twitter handle, and logo. I have included the new details in the PR description above.
TruYields (update logo and links, rename from TruFin Protocol to TruYields)
├── TruStake (update logo and links)
├── TruVault (this PR)
└── TruFin Legacy Vaults (update logo and links)
Summary by CodeRabbit