Conversation
📝 WalkthroughWalkthroughA new ForgeYields APY adaptor module has been added that fetches strategy data from an external API, processes token gateway information across supported chains, and returns standardized pool records with APY and TVL data. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 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 forgeyields adapter exports pools: Test Suites: 1 passed, 1 total |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/adaptors/forgeyields/index.js (1)
24-26: Consider case-insensitive chain matching.If the API returns chain names with different casing (e.g.,
"Ethereum"vs"ethereum"), the lookup will silently fail and skip the gateway. This is defensive but worth noting.Optional: normalize chain to lowercase
const chain = gw.domain; - const tokens = underlyingTokens[chain]?.[s.underlyingSymbol]; + const tokens = underlyingTokens[chain.toLowerCase()]?.[s.underlyingSymbol];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/adaptors/forgeyields/index.js` around lines 24 - 26, The lookup for tokens uses gw.domain as-is so differing case (e.g., "Ethereum" vs "ethereum") causes misses; normalize the chain string before lookup (e.g., const chain = gw.domain.toLowerCase()) and ensure the keys in underlyingTokens are normalized to the same case (or perform a case-insensitive lookup) so the tokens = underlyingTokens[chain]?.[s.underlyingSymbol] check succeeds; update the code around gw.domain, underlyingTokens, and the tokens lookup to use the normalized key.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/adaptors/forgeyields/index.js`:
- Line 34: The assignment apyBase: Number(s.integrationInfo.overallApy) can
throw when s.integrationInfo is null/undefined; update the apyBase calculation
in the adaptor (where apyBase is set) to use optional chaining on
s.integrationInfo (e.g., s.integrationInfo?.overallApy) so Number() receives
undefined instead of causing a TypeError, letting keepFinite handle the
resulting NaN as intended.
- Line 33: The pool creation currently sets tvlUsd to the strategy-level
s.tvlUSD (causing double-counting across gateways) and accesses
s.integrationInfo.overallApy without a null guard; update the pool TVL
assignment to use per-gateway gw.tvlUSD if the API supplies it, otherwise divide
s.tvlUSD by s.gateways.length (e.g., tvlUsd = gw.tvlUSD ?? s.tvlUSD /
s.gateways.length), and wrap reads of s.integrationInfo.overallApy with a
null-safe check (e.g., compute overallApy = s.integrationInfo ?
s.integrationInfo.overallApy : undefined) before using it in the pool object so
no TypeError occurs.
---
Nitpick comments:
In `@src/adaptors/forgeyields/index.js`:
- Around line 24-26: The lookup for tokens uses gw.domain as-is so differing
case (e.g., "Ethereum" vs "ethereum") causes misses; normalize the chain string
before lookup (e.g., const chain = gw.domain.toLowerCase()) and ensure the keys
in underlyingTokens are normalized to the same case (or perform a
case-insensitive lookup) so the tokens =
underlyingTokens[chain]?.[s.underlyingSymbol] check succeeds; update the code
around gw.domain, underlyingTokens, and the tokens lookup to use the normalized
key.
🪄 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: c0f2686e-00c0-44fd-85db-0021f9c5cfe7
📒 Files selected for processing (1)
src/adaptors/forgeyields/index.js
| chain: utils.formatChain(chain), | ||
| project: 'forgeyields', | ||
| symbol: s.underlyingSymbol, | ||
| tvlUsd: Number(s.tvlUSD), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n src/adaptors/forgeyields/index.jsRepository: DefiLlama/yield-server
Length of output: 1967
🏁 Script executed:
# Search for forgeyields-related files
fd -i "forgeyields" -type f
# Search for documentation or examples
fd -i "test" -type f | grep -i forgeyields || echo "No test file found with forgeyields in name"
# Look for API documentation or comments about forgeyields
rg -i "forgeyields" --type=json --type=md -A 5 -B 5 | head -100Repository: DefiLlama/yield-server
Length of output: 468
🏁 Script executed:
# Find all forgeyields-related files
fd -i "forgeyields"
# Search for example API responses or documentation
rg "tvlUSD\|token_gateway_per_domain" -A 3 -B 3
# Check if there are any comments or docs about the API structure
rg "forgeyields" -i -A 5 -B 5 | head -150Repository: DefiLlama/yield-server
Length of output: 2029
🏁 Script executed:
# Search for other adaptors with similar nested loop patterns (multi-chain/gateway)
rg "for.*strategies\|for.*domains\|for.*chains" -A 10 src/adaptors/ | head -200
# Check git history for this file
git log --oneline src/adaptors/forgeyields/index.js 2>/dev/null | head -20
# Look for similar patterns in other adaptors where TVL comes from outer loop
rg "tvlUsd.*Number\(s\." src/adaptors/ | head -20Repository: DefiLlama/yield-server
Length of output: 206
🏁 Script executed:
# Check the recent commit to understand the changes
git show b6a6b95d --stat 2>/dev/null || echo "Cannot show commit"
# Search for adaptors with nested loops and nested pool creation
rg "for.*of.*strategies\|for.*of.*gateways\|for.*of.*domains" -A 15 src/adaptors/ | grep -A 15 "tvlUsd\|tvl" | head -150
# Look for PR or issue documentation
fd README -type f src/adaptors/forgeyields/
# Check if there are any similar multi-gateway or chain-specific TVL patterns
rg "tvlUsd.*Number" -B 5 -A 5 src/adaptors/ | head -200Repository: DefiLlama/yield-server
Length of output: 50567
🏁 Script executed:
# Look for other adaptors with nested loops where TVL might be duplicated
rg "for.*const.*of" -A 20 src/adaptors/ --max-count=5 | grep -A 20 "pools.push\|tvlUsd" | head -100
# Search for similar multi-chain/multi-gateway patterns
rg "\.length.*||.*1" src/adaptors/ | grep -i "gateway\|domain\|chain" | head -20Repository: DefiLlama/yield-server
Length of output: 8091
🏁 Script executed:
# Search for any documentation about ForgeYields API or data structure
rg "integrationInfo\|token_gateway_per_domain" -B 5 -A 5 src/adaptors/ | head -50
# Look for any tests or examples that might show API response structure
fd -i "test\|spec\|example" -type f src/adaptors/forgeyields/Repository: DefiLlama/yield-server
Length of output: 236
🏁 Script executed:
# Search for integrationInfo and token_gateway_per_domain without type filter
rg "integrationInfo\|token_gateway_per_domain" -B 3 -A 3
# List all files in forgeyields directory
ls -la src/adaptors/forgeyields/Repository: DefiLlama/yield-server
Length of output: 243
🏁 Script executed:
# Search for adaptors with similar nested loops (multiple pools from single data point)
rg "for.*of.*\[" -A 15 src/adaptors/ | grep -B 5 -A 10 "pools.push" | head -150
# Check if there are any comments or documentation about multi-gateway TVL handling
rg "gateway\|domain\|chain.*loop\|tvl.*strategy\|tvl.*per" -i src/adaptors/ | grep -v node_modules | head -30Repository: DefiLlama/yield-server
Length of output: 1337
🏁 Script executed:
# Search for any README or documentation that might have API examples
find . -name "README*" -o -name "*.md" | xargs grep -l "forgeyields\|tvlUSD\|token_gateway" 2>/dev/null || echo "No docs found"
# Look for any API response examples or constants that might show data structure
rg "API_URL\|https://api.forgeyields" -A 10 -B 5 src/adaptors/
# Check if integrationInfo is checked elsewhere in the codebase
rg "integrationInfo" src/adaptors/Repository: DefiLlama/yield-server
Length of output: 168
Divide TVL across gateways and add null check for integrationInfo.
The strategy-level s.tvlUSD is assigned to every pool created per gateway, causing TVL to be counted multiple times. Additionally, s.integrationInfo.overallApy is accessed without a null guard, which will throw a TypeError if integrationInfo is undefined.
Proposed fixes
+ const gatewayCount = s.token_gateway_per_domain.length || 1;
pools.push({
pool: `${gw.token_gateway}-${chain}`.toLowerCase(),
chain: utils.formatChain(chain),
project: 'forgeyields',
symbol: s.underlyingSymbol,
- tvlUsd: Number(s.tvlUSD),
+ tvlUsd: Number(s.tvlUSD) / gatewayCount,
- apyBase: Number(s.integrationInfo.overallApy),
+ apyBase: s.integrationInfo ? Number(s.integrationInfo.overallApy) : undefined,Consider whether the API provides per-gateway TVL; if so, use gw.tvlUSD instead of dividing.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/adaptors/forgeyields/index.js` at line 33, The pool creation currently
sets tvlUsd to the strategy-level s.tvlUSD (causing double-counting across
gateways) and accesses s.integrationInfo.overallApy without a null guard; update
the pool TVL assignment to use per-gateway gw.tvlUSD if the API supplies it,
otherwise divide s.tvlUSD by s.gateways.length (e.g., tvlUsd = gw.tvlUSD ??
s.tvlUSD / s.gateways.length), and wrap reads of s.integrationInfo.overallApy
with a null-safe check (e.g., compute overallApy = s.integrationInfo ?
s.integrationInfo.overallApy : undefined) before using it in the pool object so
no TypeError occurs.
| project: 'forgeyields', | ||
| symbol: s.underlyingSymbol, | ||
| tvlUsd: Number(s.tvlUSD), | ||
| apyBase: Number(s.integrationInfo.overallApy), |
There was a problem hiding this comment.
Missing null check on integrationInfo will crash the adaptor.
If s.integrationInfo is null or undefined for any strategy, accessing .overallApy throws a TypeError before keepFinite can filter it out.
Proposed fix using optional chaining
- apyBase: Number(s.integrationInfo.overallApy),
+ apyBase: Number(s.integrationInfo?.overallApy),This will produce NaN for missing data, which keepFinite will then filter out.
📝 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.
| apyBase: Number(s.integrationInfo.overallApy), | |
| apyBase: Number(s.integrationInfo?.overallApy), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/adaptors/forgeyields/index.js` at line 34, The assignment apyBase:
Number(s.integrationInfo.overallApy) can throw when s.integrationInfo is
null/undefined; update the apyBase calculation in the adaptor (where apyBase is
set) to use optional chaining on s.integrationInfo (e.g.,
s.integrationInfo?.overallApy) so Number() receives undefined instead of causing
a TypeError, letting keepFinite handle the resulting NaN as intended.
Add ForgeYields yield adaptor
ForgeYields is a cross-chain yield
allocator that deploys and rotates liquidity into optimized DeFi
strategies (Curve, Convex, lending markets, LST/LRT issuers).
Strategies
Architecture
redemptions
allocated to yield strategies on Ethereum L1
Adaptor details
https://api.forgeyields.com/strategiesapyBase: projected APY (integrationInfo.overallApy)token: token gateway contract address (receipt token / depositentry point)
underlyingTokens: per-chain underlying asset addressessearchTokenOverride: token gateway address for search matchingTVL
Already listed on DefiLlama:
forgeyields(~$2M TVL)Summary by CodeRabbit