Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions src/adaptors/hyperlynx-v3/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
const { request, gql } = require('graphql-request');
const utils = require('../utils');

// Hyperlynx V3 — Uniswap V3 concentrated-liquidity pools on HyperEVM.
const PROJECT = 'hyperlynx-v3';
const CHAIN = 'hyperevm';
const MIN_TVL_USD = 10000;

const SUBGRAPH_URL =
'https://api.goldsky.com/api/public/project_cmg87miatabz301usdo94h2v3/subgraphs/uniswap-v3-hyperevm/prod/gn';

const poolsQuery = gql`
query getPools($first: Int!, $skip: Int!, $minTvl: BigDecimal!) {
pools(
first: $first
skip: $skip
orderBy: totalValueLockedUSD
orderDirection: desc
where: { totalValueLockedUSD_gt: $minTvl }
) {
id
token0 { id symbol decimals }
token1 { id symbol decimals }
feeTier
totalValueLockedUSD
poolDayData(first: 7, orderBy: date, orderDirection: desc) {
date
volumeUSD
feesUSD
tvlUSD
}
}
}
`;

async function fetchAllPools() {
const all = [];
const first = 1000;
let skip = 0;
while (true) {
const { pools } = await request(SUBGRAPH_URL, poolsQuery, {
first,
skip,
minTvl: String(MIN_TVL_USD),
});
Comment on lines +41 to +45

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.

🩺 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'
fi

Repository: 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'
fi

Repository: 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.ts

Repository: 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.

if (!pools?.length) break;
all.push(...pools);
if (pools.length < first) break;
skip += first;
}
return all;
}

async function apy() {
const pools = await fetchAllPools();

return pools
.map((p) => {
const days = p.poolDayData || [];
const day = days[0];

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.

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

const tvlUsd = Number(day?.tvlUSD) || Number(p.totalValueLockedUSD);
if (!Number.isFinite(tvlUsd) || tvlUsd < MIN_TVL_USD) return null;

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;
Comment on lines +67 to +71

@coderabbitai coderabbitai Bot Jun 30, 2026

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.

🎯 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.

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.

@isagieth pls implement this fix to only compute if > 7 days

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.

@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 {
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',

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.

volumeUsd1d: Number(day?.volumeUSD) || 0,
volumeUsd7d: volume7d,
};
})
.filter(Boolean)
.filter((p) => utils.keepFinite(p));
Comment on lines +64 to +89

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.

🎯 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.

}

module.exports = {
protocolId: '8107',
timetravel: false,
apy,
url: 'https://hyperlynx.fi',
};
Loading