Feat: add orrai quant terminal#2624
Conversation
📝 WalkthroughWalkthroughAdds a new ORAI “quant terminal” adaptor that queries ORAI's CosmWasm LCD smart-query endpoint for hardcoded vaults, parses returned Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Adaptor
participant LCD as ORAI LCD Endpoint
participant Contract as CosmWasm Contract
participant Parser as Parser
Client->>Client: build encoded get_vault_stats query (per vault)
Client->>LCD: POST smart-query with encoded message
LCD->>Contract: forward smart query to contract
Contract-->>LCD: return get_vault_stats response
LCD-->>Client: return response payload
Client->>Parser: extract numeric fields (tvl, apy)
Parser->>Parser: deep numeric parsing, fallback strategies
Parser->>Parser: scale tvl (×1e6), normalize/validate apy
Parser-->>Client: parsed metrics or null
Client->>Client: filter failures, format pool objects, export results
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
|
Error while running orai-quant-terminal adapter: Test Suites: 1 failed, 1 total |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/adaptors/orai-quant-terminal/index.js (2)
122-133: NormalizevaultAddresscasing in the pool id.
config.vaultAddressis preserved in mixed case here (e.g.0xd3A1C2Bd...), while the LCD query uses.toLowerCase()(Line 60) and yoururlfield is already lowercased. DefiLlama pool ids for EVM addresses are conventionally lowercased so they remain stable and deduped — worth aligning here.♻️ Proposed tweak
- pool: `${config.vaultAddress}-${CHAIN}`, + pool: `${config.vaultAddress.toLowerCase()}-${CHAIN}`, ... - token: config.vaultAddress, + token: config.vaultAddress.toLowerCase(),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/adaptors/orai-quant-terminal/index.js` around lines 122 - 133, The pool id and token fields currently use config.vaultAddress in mixed case; normalize the EVM address to lowercase to match the LCD query and url. Update the return object where pool is constructed (`pool: `${config.vaultAddress}-${CHAIN}``) and the token field (`token: config.vaultAddress`) to use config.vaultAddress.toLowerCase() (or a single lowercase variable) so both pool and token are consistently lowercased while leaving other fields (e.g., chain via utils.formatChain(CHAIN), url) unchanged.
113-114: Hardcoded1e6assumes every vault is 6-decimal (USDC).All four current pools are USDC so this works today, but the scaling is not tied to the pool config. Adding any non-USDC vault later (e.g. USDT on a chain where it's 6 decimals is fine; ETH/WBTC/DAI are not) will silently miscount TVL by orders of magnitude. Consider moving the divisor into
POOLS_CONFIG(e.g.decimals: 6→10 ** decimals) so future vaults are safe by construction.♻️ Proposed refactor
const POOLS_CONFIG = [ { statsContract: 'orai1rzfk6fd6d5zhm77cshdtr0vsuyu0qe0dg36evysklx8n6q8h38psxywppw', vaultAddress: '0xd3A1C2Bd6E1d163A7380D701c946aDCd82DD95b1', symbol: 'USDC', + decimals: 6, poolMeta: 'Golden Rhythm Vault', ... }, ... ]; @@ - const tvlRaw = extractNumericByKeys(tvlSource, ['tvl']) ?? extractFirstNumber(tvlSource); - const tvlUsd = tvlRaw / 1e6; + const tvlRaw = extractNumericByKeys(tvlSource, ['tvl']) ?? extractFirstNumber(tvlSource); + const tvlUsd = tvlRaw / 10 ** (config.decimals ?? 6);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/adaptors/orai-quant-terminal/index.js` around lines 113 - 114, The code currently divides tvlRaw by a hardcoded 1e6 producing tvlUsd which assumes 6-decimal tokens; change this to read the token decimals from POOLS_CONFIG (e.g. use POOLS_CONFIG[poolId].decimals) and compute the divisor as 10 ** decimals, then set tvlUsd = tvlRaw / (10 ** decimals); update the call site that computes tvlRaw/tvlUsd (references: tvlRaw, tvlUsd) to accept or look up the pool's config so non-USDC pools use the correct scaling.
🤖 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/orai-quant-terminal/index.js`:
- Around line 97-102: The normalizeApyPercent function misclassifies legitimate
sub-1% APYs by treating any value <= 1 as a ratio; update normalizeApyPercent to
only convert ratio-to-percent for values that are almost certainly ratios (e.g.
value < 0.01) or remove the heuristic and rely on the documented contract
format; specifically change the condition in normalizeApyPercent (the function
name) from "value <= 1" to a tighter check like "value < 0.01" (or remove
conversion entirely if the contract is fixed) and add a short comment
documenting the chosen assumption so future readers know whether inputs are
expected as ratios or percentages.
- Around line 136-139: The apy() function currently uses Promise.all on
POOLS_CONFIG.map(buildPoolData) so a single thrown error from utils.getData
(called inside buildPoolData / statsContract) rejects the whole batch; change
apy to use Promise.allSettled (or wrap each buildPoolData call with a per-pool
try/catch) and only include fulfilled results in the returned array (filter by
status === "fulfilled" and non-null value or filter out rejected entries) so one
failed LCD query won't drop all pools.
- Around line 64-85: The current extractor (extractFirstNumber) can pick any
numeric field by DFS and causes wrong tvl/apy; modify the extraction flow so
that extractNumericByKeys (or the caller that uses extractFirstNumber) first
checks only an explicit whitelist of allowed keys (e.g.,
'tvl','apy','apr','total_value_locked','total_value'), searching those keys on
the response object (and shallow/nested predictable shapes) and returning null
if none present; remove or disable the generic Object.values DFS fallback in
extractFirstNumber (or stop calling it when no whitelisted keys matched) so
arbitrary numeric fields like 'decimals' or 'block_height' are not returned.
Ensure the functions to change are extractNumericByKeys and/or
extractFirstNumber and keep the return contract (null or finite number) so
downstream checks still work.
---
Nitpick comments:
In `@src/adaptors/orai-quant-terminal/index.js`:
- Around line 122-133: The pool id and token fields currently use
config.vaultAddress in mixed case; normalize the EVM address to lowercase to
match the LCD query and url. Update the return object where pool is constructed
(`pool: `${config.vaultAddress}-${CHAIN}``) and the token field (`token:
config.vaultAddress`) to use config.vaultAddress.toLowerCase() (or a single
lowercase variable) so both pool and token are consistently lowercased while
leaving other fields (e.g., chain via utils.formatChain(CHAIN), url) unchanged.
- Around line 113-114: The code currently divides tvlRaw by a hardcoded 1e6
producing tvlUsd which assumes 6-decimal tokens; change this to read the token
decimals from POOLS_CONFIG (e.g. use POOLS_CONFIG[poolId].decimals) and compute
the divisor as 10 ** decimals, then set tvlUsd = tvlRaw / (10 ** decimals);
update the call site that computes tvlRaw/tvlUsd (references: tvlRaw, tvlUsd) to
accept or look up the pool's config so non-USDC pools use the correct scaling.
🪄 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: ab1002fc-e916-48cd-b506-ec24b7c6ccec
📒 Files selected for processing (1)
src/adaptors/orai-quant-terminal/index.js
| function extractFirstNumber(payload, depth = 0) { | ||
| if (depth > 8 || payload === null || payload === undefined) return null; | ||
| if (typeof payload === 'number' && Number.isFinite(payload)) return payload; | ||
| if (typeof payload === 'string') { | ||
| const value = Number(payload); | ||
| return Number.isFinite(value) ? value : null; | ||
| } | ||
| if (Array.isArray(payload)) { | ||
| for (const item of payload) { | ||
| const value = extractFirstNumber(item, depth + 1); | ||
| if (value !== null) return value; | ||
| } | ||
| return null; | ||
| } | ||
| if (typeof payload !== 'object') return null; | ||
|
|
||
| for (const value of Object.values(payload)) { | ||
| const number = extractFirstNumber(value, depth + 1); | ||
| if (number !== null) return number; | ||
| } | ||
| return null; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
wc -l src/adaptors/orai-quant-terminal/index.jsRepository: DefiLlama/yield-server
Length of output: 112
🏁 Script executed:
cat -n src/adaptors/orai-quant-terminal/index.js | sed -n '50,130p'Repository: DefiLlama/yield-server
Length of output: 3214
🏁 Script executed:
fd -e js -e json -e md -e txt | xargs grep -l "get_vault_stats" 2>/dev/null | head -20Repository: DefiLlama/yield-server
Length of output: 108
🏁 Script executed:
grep -r "get_vault_stats" src/adaptors/orai-quant-terminal/ -A 5 -B 5Repository: DefiLlama/yield-server
Length of output: 892
🏁 Script executed:
head -50 src/adaptors/orai-quant-terminal/index.jsRepository: DefiLlama/yield-server
Length of output: 1847
🏁 Script executed:
find src/adaptors/orai-quant-terminal -type fRepository: DefiLlama/yield-server
Length of output: 108
🏁 Script executed:
tail -60 src/adaptors/orai-quant-terminal/index.jsRepository: DefiLlama/yield-server
Length of output: 1692
Lock extraction to known response schema keys instead of relying on generic fallback.
When vaultStatsData lacks top-level tvl or apy keys, the code falls back to searching the entire response object with extractFirstNumber(). This recursively returns the first finite number found (via Object.values() iteration), which could silently pick any numeric field like decimals, block_height, or fee_bps, producing valid-looking but completely wrong tvlUsd or apy values. Downstream validation at line 120 only checks isFinite() and > 0, insufficient to catch this.
Instead, restrict extractNumericByKeys to an explicit whitelist of valid keys (e.g. ['tvl', 'apy', 'apr', 'total_value_locked']) and return null if none match, rather than falling back to the generic DFS search.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/adaptors/orai-quant-terminal/index.js` around lines 64 - 85, The current
extractor (extractFirstNumber) can pick any numeric field by DFS and causes
wrong tvl/apy; modify the extraction flow so that extractNumericByKeys (or the
caller that uses extractFirstNumber) first checks only an explicit whitelist of
allowed keys (e.g., 'tvl','apy','apr','total_value_locked','total_value'),
searching those keys on the response object (and shallow/nested predictable
shapes) and returning null if none present; remove or disable the generic
Object.values DFS fallback in extractFirstNumber (or stop calling it when no
whitelisted keys matched) so arbitrary numeric fields like 'decimals' or
'block_height' are not returned. Ensure the functions to change are
extractNumericByKeys and/or extractFirstNumber and keep the return contract
(null or finite number) so downstream checks still work.
|
Error while running orai-quant-terminal adapter: Test Suites: 1 failed, 1 total |
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/adaptors/orai-quant-terminal/index.js (1)
105-108:⚠️ Potential issue | 🟠 MajorRemove generic numeric fallback for TVL/APY extraction.
Line 105 and Line 108 still allow
extractFirstNumber(...)fallback, so unrelated numeric fields can be misread astvl/apywhen expected keys are missing. This remains a silent data-corruption risk.🔧 Proposed fix
-function extractNumericByKeys(payload, keys) { - if (!payload || typeof payload !== 'object') return null; - for (const key of keys) { - if (!(key in payload)) continue; - const value = extractFirstNumber(payload[key]); - if (value !== null) return value; - } - return null; -} +function extractNumericByKeys(payload, keys, depth = 0) { + if (depth > 8 || payload === null || payload === undefined) return null; + if (Array.isArray(payload)) { + for (const item of payload) { + const value = extractNumericByKeys(item, keys, depth + 1); + if (value !== null) return value; + } + return null; + } + if (typeof payload !== 'object') return null; + + for (const key of keys) { + if (Object.prototype.hasOwnProperty.call(payload, key)) { + const value = extractFirstNumber(payload[key]); + if (value !== null) return value; + } + } + + for (const value of Object.values(payload)) { + const nested = extractNumericByKeys(value, keys, depth + 1); + if (nested !== null) return nested; + } + return null; +} @@ - const tvlSource = vaultStatsData?.tvl ?? vaultStatsData; - const apySource = vaultStatsData?.apy ?? vaultStatsData; - - const tvlRaw = extractNumericByKeys(tvlSource, ['tvl']) ?? extractFirstNumber(tvlSource); + const tvlRaw = extractNumericByKeys(vaultStatsData, ['tvl', 'total_value_locked', 'total_value']); const tvlUsd = tvlRaw / 1e6; - const apyRaw = extractNumericByKeys(apySource, ['apy']) ?? extractFirstNumber(apySource); + const apyRaw = extractNumericByKeys(vaultStatsData, ['apy', 'apr']);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/adaptors/orai-quant-terminal/index.js` around lines 105 - 108, The code currently falls back to extractFirstNumber(...) for TVL and APY which can misread unrelated numbers; remove the generic fallback and only use extractNumericByKeys(...) for both tvl and apy (i.e., set tvlRaw = extractNumericByKeys(tvlSource, ['tvl']) and apyRaw = extractNumericByKeys(apySource, ['apy'])), then handle missing values explicitly (e.g., leave tvlRaw/apyRaw undefined/null and add a clear validation/logging or error path before computing tvlUsd or using apyRaw) so unrelated numeric fields cannot be silently interpreted as TVL/APY.
🧹 Nitpick comments (1)
src/adaptors/orai-quant-terminal/index.js (1)
115-125: Normalize pool/token address casing in identifiers.Line 115 and Line 124 use raw
vaultAddresscasing. Normalizing viautils.formatAddress(...)prevents case-variant identity drift in future config edits.♻️ Proposed refactor
- pool: `${config.vaultAddress}-${CHAIN}`, + pool: `${utils.formatAddress(config.vaultAddress)}-${CHAIN}`, @@ - token: config.vaultAddress, + token: utils.formatAddress(config.vaultAddress),Based on learnings, pool keys in this codebase are intentionally chain-qualified and lowercased to reduce cross-chain/symbol collision risks.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/adaptors/orai-quant-terminal/index.js` around lines 115 - 125, The pool identifier and token fields use raw config.vaultAddress casing which can cause identity drift; update both occurrences (the pool template string that builds pool and the token assignment) to pass config.vaultAddress through utils.formatAddress (consistent with utils.formatChain/utils.formatSymbol usage) so the chain-qualified pool key and token are normalized/lowercased; ensure you reference utils.formatAddress(config.vaultAddress) when building pool and when setting token.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/adaptors/orai-quant-terminal/index.js`:
- Around line 105-108: The code currently falls back to extractFirstNumber(...)
for TVL and APY which can misread unrelated numbers; remove the generic fallback
and only use extractNumericByKeys(...) for both tvl and apy (i.e., set tvlRaw =
extractNumericByKeys(tvlSource, ['tvl']) and apyRaw =
extractNumericByKeys(apySource, ['apy'])), then handle missing values explicitly
(e.g., leave tvlRaw/apyRaw undefined/null and add a clear validation/logging or
error path before computing tvlUsd or using apyRaw) so unrelated numeric fields
cannot be silently interpreted as TVL/APY.
---
Nitpick comments:
In `@src/adaptors/orai-quant-terminal/index.js`:
- Around line 115-125: The pool identifier and token fields use raw
config.vaultAddress casing which can cause identity drift; update both
occurrences (the pool template string that builds pool and the token assignment)
to pass config.vaultAddress through utils.formatAddress (consistent with
utils.formatChain/utils.formatSymbol usage) so the chain-qualified pool key and
token are normalized/lowercased; ensure you reference
utils.formatAddress(config.vaultAddress) when building pool and when setting
token.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 806d4f59-8579-4b84-8738-00aafa3375a8
📒 Files selected for processing (1)
src/adaptors/orai-quant-terminal/index.js
Summary by CodeRabbit