Add Hyperlynx V3 yield adapter (HyperEVM)#2763
Conversation
📝 WalkthroughWalkthroughAdds a new Hyperlynx v3 Adapter
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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 |
|
Error while running hyperlynx adapter: Test Suites: 1 failed, 1 total |
|
Error while running hyperlynx adapter: Test Suites: 1 failed, 1 total |
Match merged TVL/dimension structure (Hyperlynx V3 = id 8107). V2 yields deferred until a pair crosses the $10k display threshold.
|
The hyperlynx-v3 adapter exports pools: Test Suites: 1 passed, 1 total |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/adaptors/hyperlynx-v3/index.js`:
- Around line 64-89: The adaptor in hyperlynx-v3 is dropping entire pools when
`poolDayData[0]` is missing because `apyBase` and `apyBase7d` become non-finite
and then `utils.keepFinite()` filters them out. Update the pool mapping logic in
the main return path so pools are still returned even when the daily bucket is
absent, and leave APY fields as non-finite/null-safe values for downstream
handling by `triggerAdaptor` instead of filtering the whole object out.
- Around line 67-71: The trailing APY calculation in the Hyperlynx v3 adaptor is
using the number of available day buckets instead of a fixed 7-day window, so
`apyBase7d` is inflated for newer pools. Update the `apyBase7d` computation in
`src/adaptors/hyperlynx-v3/index.js` to either divide by 7 regardless of
`days.length` or return no value until the `days` array contains seven buckets,
and keep the logic localized near the existing `fees7d`, `volume7d`, and
`apyBase7d` calculations.
- Around line 41-45: The Goldsky GraphQL call in the hyperlynx-v3 adaptor is
missing a timeout/abort path, so a stalled response can block the run. Update
the request in the adaptor’s main fetch logic around the request(SUBGRAPH_URL,
poolsQuery, ...) call to pass a signal from AbortSignal.timeout(...) or an
AbortController with a bounded timeout, and handle the abort path cleanly so the
request fails fast instead of hanging.
🪄 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: 442fd152-32e6-435a-8c21-990ed435ec05
📒 Files selected for processing (1)
src/adaptors/hyperlynx-v3/index.js
| const { pools } = await request(SUBGRAPH_URL, poolsQuery, { | ||
| first, | ||
| skip, | ||
| minTvl: String(MIN_TVL_USD), | ||
| }); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the adaptor and request implementation
git ls-files | rg '^src/adaptors/hyperlynx-v3/index\.js$|^src/.*request.*|^src/.*goldsky.*|^src/.*subgraph.*|^src/.*fetch.*'
echo '--- hyperlynx-v3 index.js ---'
cat -n src/adaptors/hyperlynx-v3/index.js | sed -n '1,180p'
echo '--- likely request implementations ---'
rg -n "function request|const request =|export .*request|AbortController|timeout|fetch\(" src --glob '!**/*.map' --glob '!**/dist/**' --glob '!**/build/**'Repository: DefiLlama/yield-server
Length of output: 17449
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- triggerAdaptor / adaptor runner ---'
rg -n "triggerAdaptor|await apy\(|apy\(\)" src test . --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**'
echo '--- graphql-request dependency/version ---'
cat package.json | sed -n '1,220p'
echo '--- lockfile references to graphql-request ---'
rg -n '"graphql-request"|graphql-request@|graphql-request' package-lock.json yarn.lock pnpm-lock.yaml
echo '--- local node_modules check (if present) ---'
if [ -d node_modules/graphql-request ]; then
find node_modules/graphql-request -maxdepth 2 -type f | sed -n '1,80p'
fiRepository: DefiLlama/yield-server
Length of output: 14751
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- src/handlers/triggerAdaptor.js ---'
cat -n src/handlers/triggerAdaptor.js | sed -n '1,180p'
echo '--- package.json ---'
cat -n package.json | sed -n '1,220p'
echo '--- graphql-request references in lockfiles ---'
if [ -f package-lock.json ]; then rg -n '"graphql-request"|graphql-request' package-lock.json | sed -n '1,40p'; fi
if [ -f yarn.lock ]; then rg -n '^graphql-request@|graphql-request@' yarn.lock | sed -n '1,40p'; fi
if [ -f pnpm-lock.yaml ]; then rg -n 'graphql-request' pnpm-lock.yaml | sed -n '1,40p'; fi
echo '--- local graphql-request package files (if installed) ---'
if [ -d node_modules/graphql-request ]; then
find node_modules/graphql-request -maxdepth 2 -type f | sed -n '1,80p'
fiRepository: DefiLlama/yield-server
Length of output: 10768
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- graphql-request package.json ---'
cat -n node_modules/graphql-request/package.json | sed -n '1,220p'
echo '--- graphql-request README snippets for request/options/signal ---'
rg -n "signal|timeout|request\(|GraphQLClient|fetch" node_modules/graphql-request/README.md | sed -n '1,120p'
echo '--- graphql-request type surface ---'
find node_modules/graphql-request -maxdepth 2 \( -name '*.d.ts' -o -name '*.ts' \) | sed -n '1,120p'Repository: DefiLlama/yield-server
Length of output: 5581
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- README around request/fetch options ---'
sed -n '60,140p' node_modules/graphql-request/README.md
echo '--- type exports and request signatures ---'
rg -n "export declare function request|type RequestOptions|interface RequestOptions|signal|fetchOptions|RequestDocument" node_modules/graphql-request/build -g '*.d.ts' | sed -n '1,200p'
echo '--- main entrypoint d.ts excerpt ---'
sed -n '1,220p' node_modules/graphql-request/build/entrypoints/main.d.tsRepository: DefiLlama/yield-server
Length of output: 10306
Add a timeout/abort path to the Goldsky request. graphql-request supports signal, so a bounded AbortSignal.timeout(...) or AbortController here would keep a stalled response from blocking the adaptor run.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/adaptors/hyperlynx-v3/index.js` around lines 41 - 45, The Goldsky GraphQL
call in the hyperlynx-v3 adaptor is missing a timeout/abort path, so a stalled
response can block the run. Update the request in the adaptor’s main fetch logic
around the request(SUBGRAPH_URL, poolsQuery, ...) call to pass a signal from
AbortSignal.timeout(...) or an AbortController with a bounded timeout, and
handle the abort path cleanly so the request fails fast instead of hanging.
| const feesUsd = Number(day?.feesUSD); | ||
| const apyBase = Number.isFinite(feesUsd) ? ((feesUsd * 365) / tvlUsd) * 100 : NaN; | ||
|
|
||
| const fees7d = days.reduce((s, d) => s + (Number(d.feesUSD) || 0), 0); | ||
| const volume7d = days.reduce((s, d) => s + (Number(d.volumeUSD) || 0), 0); | ||
| const apyBase7d = days.length | ||
| ? (((fees7d / days.length) * 365) / tvlUsd) * 100 | ||
| : NaN; | ||
|
|
||
| return { | ||
| pool: p.id, | ||
| chain: utils.formatChain(CHAIN), | ||
| project: PROJECT, | ||
| symbol: `${p.token0.symbol}-${p.token1.symbol}`, | ||
| tvlUsd, | ||
| apyBase, | ||
| apyBase7d, | ||
| underlyingTokens: [p.token0.id, p.token1.id], | ||
| poolMeta: `${Number(p.feeTier) / 10000}%`, | ||
| url: 'https://hyperlynx.fi/liquidity', | ||
| volumeUsd1d: Number(day?.volumeUSD) || 0, | ||
| volumeUsd7d: volume7d, | ||
| }; | ||
| }) | ||
| .filter(Boolean) | ||
| .filter((p) => utils.keepFinite(p)); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Don't drop pools just because the daily bucket is missing.
When poolDayData[0] is absent, apyBase/apyBase7d become non-finite and utils.keepFinite() removes the entire pool. src/handlers/triggerAdaptor.js already nulls non-finite APY fields downstream, so this turns subgraph lag or brand-new pools into missing pool rows instead of incomplete APY data.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/adaptors/hyperlynx-v3/index.js` around lines 64 - 89, The adaptor in
hyperlynx-v3 is dropping entire pools when `poolDayData[0]` is missing because
`apyBase` and `apyBase7d` become non-finite and then `utils.keepFinite()`
filters them out. Update the pool mapping logic in the main return path so pools
are still returned even when the daily bucket is absent, and leave APY fields as
non-finite/null-safe values for downstream handling by `triggerAdaptor` instead
of filtering the whole object out.
| const fees7d = days.reduce((s, d) => s + (Number(d.feesUSD) || 0), 0); | ||
| const volume7d = days.reduce((s, d) => s + (Number(d.volumeUSD) || 0), 0); | ||
| const apyBase7d = days.length | ||
| ? (((fees7d / days.length) * 365) / tvlUsd) * 100 | ||
| : NaN; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
apyBase7d is overstated for pools with fewer than 7 day buckets.
The PR objective says this field is a trailing 7-day average, but dividing by days.length makes a 1-day-old pool report its 1-day APY as apyBase7d. Divide by a fixed 7 days, or suppress the metric until seven buckets exist.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/adaptors/hyperlynx-v3/index.js` around lines 67 - 71, The trailing APY
calculation in the Hyperlynx v3 adaptor is using the number of available day
buckets instead of a fixed 7-day window, so `apyBase7d` is inflated for newer
pools. Update the `apyBase7d` computation in
`src/adaptors/hyperlynx-v3/index.js` to either divide by 7 regardless of
`days.length` or return no value until the `days` array contains seven buckets,
and keep the logic localized near the existing `fees7d`, `volume7d`, and
`apyBase7d` calculations.
There was a problem hiding this comment.
@isagieth pls implement this fix to only compute if > 7 days
There was a problem hiding this comment.
@0xkr3p, sounds good — tagging @isagieth for visibility. Here's a suggested implementation for the fix (suppressing apyBase7d until 7 full day buckets are available):
const fees7d = days.reduce((s, d) => s + (Number(d.feesUSD) || 0), 0);
const volume7d = days.reduce((s, d) => s + (Number(d.volumeUSD) || 0), 0);
const apyBase7d = days.length === 7
? ((fees7d / 7) * 365 / tvlUsd) * 100
: NaN;This way, pools with fewer than 7 day buckets simply won't report apyBase7d (it'll be filtered out later by utils.keepFinite), avoiding the overstatement for newer pools. Let me know once it's implemented and I'll take another look.
| return pools | ||
| .map((p) => { | ||
| const days = p.poolDayData || []; | ||
| const day = days[0]; |
There was a problem hiding this comment.
i would opt to use the last full day as I believe days[0] could be a partial day. So opt for days[1] which would be the last full day
| apyBase7d, | ||
| underlyingTokens: [p.token0.id, p.token1.id], | ||
| poolMeta: `${Number(p.feeTier) / 10000}%`, | ||
| url: 'https://hyperlynx.fi/liquidity', |
There was a problem hiding this comment.
Adds a yields adapter for Hyperlynx, a Uniswap V3 fork DEX on HyperEVM. Per-pool base APY is computed from swap fees (
feesUSD × 365 / tvlUsd) using the project subgraph, modeled on the existinghyperswap-v3adapter. V3 concentrated-liquidity pools only (V2 pools are below the $10k display threshold).This is intentionally a draft. yield-server requires
protocolIdto match Hyperlynx's id in https://api.llama.fi/protocols, which is only assigned once the TVL adapter merges.protocolIdis currently aTODOplaceholder and will be filled in (and this PR marked ready) as soon as the TVL PR lands.Related PRs (same protocol)
Details
hyperlynxuniswap-v3-hyperevm)apyBase7dfrom the trailing 7-day averageWebsite: https://hyperlynx.fi · Docs: https://hyperlynx.gitbook.io/hyperlynx-docs/
Summary by CodeRabbit