updating subgraph endpoints (satsuma is deprecated) #2352
updating subgraph endpoints (satsuma is deprecated) #2352ethereumdegen wants to merge 2 commits intoDefiLlama:masterfrom
Conversation
… status keywords for more accurate accounting
📝 WalkthroughWalkthroughThe Teller adaptor was rewritten to use Ormi Labs subgraph endpoints and The Graph non-pool endpoints, adding token metadata fetching, price lookups, enrichment calculations (tvlUsd, apy, ltv, etc.), filtering guardrails, and outputting separate Lending and Collateral pool objects per source pool. Changes
Sequence Diagram(s)sequenceDiagram
participant Main as Main Process
participant Subgraph as Ormi/TheGraph Subgraphs
participant TokenInfo as Token Info Resolver
participant PriceSvc as Price Lookup Service
participant Enricher as Enrichment & Transform
participant Output as Filtered Output
Main->>Subgraph: Query pools (v2 & v1) and non-pool bids
Subgraph-->>Main: Pool and bid data (token addresses, amounts)
Main->>TokenInfo: Resolve symbols & decimals (per-token, timeout)
TokenInfo-->>Main: Token metadata (or UNKNOWN)
Main->>PriceSvc: Request prices for principal & collateral tokens
PriceSvc-->>Main: Prices in USD
Main->>Enricher: Enrich pools (calculate tvlUsd, totalSupplyUsd, totalBorrowUsd, ltv, apyBase, borrowApy)
Enricher->>Enricher: Apply guardrails (skip UNKNOWN, invalid LTV, low TVL)
Enricher->>Enricher: Produce Lending & Collateral objects per pool
Enricher-->>Output: Emit filtered pool objects
Output-->>Main: Aggregated result
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 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 |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@src/adaptors/teller/index.js`:
- Around line 220-230: The lending pool object is setting tvlUsd to
p.totalSupplyUsd which double-counts borrowed assets; replace that assignment
with the previously computed tvlUsd (the value computed as totalSupplyUsd −
totalBorrowUsd) so lendingPool.tvlUsd uses the net TVL, or if the intent was to
report gross supply, rename the property or add a clear comment; update the
tvlUsd field in the lendingPool construction (const lendingPool = { ... tvlUsd:
... }) to reference the computed tvlUsd variable instead of p.totalSupplyUsd.
- Line 217: The variable `chain` is computed but never used; either remove the
unused declaration `const chain = chainString === 'ethereum' ? 'mainnet' :
chainString;` or, if the intent was to normalize `ethereum` to `mainnet` for
downstream URL/resolve logic, replace the downstream uses of `chainString` with
`chain` (e.g., in any URL construction or resolution code that expects
"mainnet") so the normalized value is actually consumed and the unused-variable
lint error is resolved.
- Around line 280-316: The main function collects pool entries from
pools_v2_endpoints and pools_v1_endpoints causing duplicate pool IDs when the
same group_pool_address exists in both; add deduplication before returning
filteredData by iterating filteredData and using a Set of seen
group_pool_address values (or the composite pool id) to keep only the first
occurrence from v2 (ensure v2 is processed before v1 as in the current loops)
and skip duplicates from v1, then return the deduplicated array; reference main,
pools_v2_endpoints, pools_v1_endpoints, topLvl, filteredData, and
group_pool_address to locate where to apply this change.
🧹 Nitpick comments (4)
src/adaptors/teller/index.js (4)
21-45: Pagination:first: 1000silently truncates results.If any chain ever has more than 1000 pools, data will be silently lost. Consider either paginating (using
skip) or at least logging a warning when the result count equals 1000 so you know you've hit the cap.
48-79: Sequential token info fetching is a performance bottleneck; also remove commented-out code.Each token address is fetched one-by-one with a 1.5s timeout. With many unique tokens across pools, this serialization adds significant latency. Consider batching with
Promise.all(orPromise.allSettledto handle individual failures gracefully).Also, line 64 has commented-out dead code that should be removed.
♻️ Parallel token info fetching
const fetchTokenInfo = async (tokenAddresses, chainString) => { const tokens = {}; - for (const address of tokenAddresses) { - try { - const timeoutPromise = new Promise((_, reject) => - setTimeout(() => reject(new Error('Token fetch timeout')), 1500) - ); - const tokenInfoPromise = sdk.api.erc20.info(address, chainString); - const tokenInfo = await Promise.race([tokenInfoPromise, timeoutPromise]); - - // const tokenInfo = await sdk.api.erc20.info(address, chainString); - tokens[address.toLowerCase()] = { - symbol: tokenInfo.output.symbol, - decimals: Number(tokenInfo.output.decimals) || 18, - }; - console.log(`Successfully fetched ${tokenInfo.output.symbol} for ${address}`); - } catch (error) { - console.warn(`failure to fetch token info ${address}, ${chainString}`); - tokens[address.toLowerCase()] = { symbol: 'UNKNOWN', decimals: 18 }; - } - } + const results = await Promise.allSettled( + tokenAddresses.map(async (address) => { + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('Token fetch timeout')), 3000) + ); + const tokenInfo = await Promise.race([ + sdk.api.erc20.info(address, chainString), + timeoutPromise, + ]); + return { address, tokenInfo }; + }) + ); + for (const result of results) { + if (result.status === 'fulfilled') { + const { address, tokenInfo } = result.value; + tokens[address.toLowerCase()] = { + symbol: tokenInfo.output.symbol, + decimals: Number(tokenInfo.output.decimals) || 18, + }; + } else { + // Extract address from the rejected promise context isn't straightforward, + // so consider wrapping to always resolve with {address, error/data}. + } + } + // Fill in any missing addresses as UNKNOWN + for (const address of tokenAddresses) { + if (!tokens[address.toLowerCase()]) { + tokens[address.toLowerCase()] = { symbol: 'UNKNOWN', decimals: 18 }; + } + } return tokens; };
107-122: Per-pool price fetching insidePromise.allmay cause excessive API calls.
utils.getPricesis called once per pool, but many pools likely share the same principal/collateral tokens. Consider collecting all unique token addresses upfront and making a single batched price call, then distributing the results. This would reduce API load and improve performance.
252-278: Yield functions are correct but defined after their call sites.
calculateActiveLenderYieldandcalculateActiveBorrowerYieldare referenced on lines 169 and 171 but defined on lines 253 and 267. This works at runtime becausetopLvlis only called frommain()(after module initialization), but it harms readability. Consider moving these function definitions abovetopLvlfor clarity.
| } | ||
| } | ||
| const underlyingTokens = [p.principal_token_address, p.collateral_token_address]; | ||
| const chain = chainString === 'ethereum' ? 'mainnet' : chainString; |
There was a problem hiding this comment.
Unused variable chain.
chain is computed but never referenced — chainString is used everywhere downstream (lines 218, 222, 234). Either this is dead code to remove, or there was an intent to use chain somewhere (e.g., in the URL for mainnet resolution). If the URL is intentionally using chainString as-is, just remove the unused line.
Proposed fix
- const chain = chainString === 'ethereum' ? 'mainnet' : chainString;
const url = `https://app.teller.org/${chainString}/lend/pool/${p.group_pool_address}`;📝 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.
| const chain = chainString === 'ethereum' ? 'mainnet' : chainString; | |
| const url = `https://app.teller.org/${chainString}/lend/pool/${p.group_pool_address}`; |
🤖 Prompt for AI Agents
In `@src/adaptors/teller/index.js` at line 217, The variable `chain` is computed
but never used; either remove the unused declaration `const chain = chainString
=== 'ethereum' ? 'mainnet' : chainString;` or, if the intent was to normalize
`ethereum` to `mainnet` for downstream URL/resolve logic, replace the downstream
uses of `chainString` with `chain` (e.g., in any URL construction or resolution
code that expects "mainnet") so the normalized value is actually consumed and
the unused-variable lint error is resolved.
| const lendingPool = { | ||
| pool: p.group_pool_address + "-lending", | ||
| chain: utils.formatChain(chainString), | ||
| project: 'teller', | ||
| symbol: p.principalSymbol, | ||
| poolMeta: p.collateralSymbol, | ||
| tvlUsd: Number(p.totalSupplyUsd.toFixed(4)), | ||
| apyBase: p.apyBase, | ||
| underlyingTokens, | ||
| url, | ||
| }; |
There was a problem hiding this comment.
Lending pool tvlUsd uses totalSupplyUsd instead of the computed tvlUsd (supply − borrows).
Line 163 correctly computes tvlUsd = totalSupplyUsd - totalBorrowUsd, but line 226 sets the lending pool's tvlUsd to totalSupplyUsd. This means the lending pool's TVL double-counts tokens that are currently borrowed. If DefiLlama expects TVL to exclude outstanding borrows for lending pools, this would overstate TVL.
Was this intentional? If so, consider renaming or adding a comment. If not:
Proposed fix if TVL should exclude borrows
const lendingPool = {
pool: p.group_pool_address + "-lending",
chain: utils.formatChain(chainString),
project: 'teller',
symbol: p.principalSymbol,
poolMeta: p.collateralSymbol,
- tvlUsd: Number(p.totalSupplyUsd.toFixed(4)),
+ tvlUsd: Number(p.tvlUsd.toFixed(4)),
apyBase: p.apyBase,
underlyingTokens,
url,
};📝 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.
| const lendingPool = { | |
| pool: p.group_pool_address + "-lending", | |
| chain: utils.formatChain(chainString), | |
| project: 'teller', | |
| symbol: p.principalSymbol, | |
| poolMeta: p.collateralSymbol, | |
| tvlUsd: Number(p.totalSupplyUsd.toFixed(4)), | |
| apyBase: p.apyBase, | |
| underlyingTokens, | |
| url, | |
| }; | |
| const lendingPool = { | |
| pool: p.group_pool_address + "-lending", | |
| chain: utils.formatChain(chainString), | |
| project: 'teller', | |
| symbol: p.principalSymbol, | |
| poolMeta: p.collateralSymbol, | |
| tvlUsd: Number(p.tvlUsd.toFixed(4)), | |
| apyBase: p.apyBase, | |
| underlyingTokens, | |
| url, | |
| }; |
🤖 Prompt for AI Agents
In `@src/adaptors/teller/index.js` around lines 220 - 230, The lending pool object
is setting tvlUsd to p.totalSupplyUsd which double-counts borrowed assets;
replace that assignment with the previously computed tvlUsd (the value computed
as totalSupplyUsd − totalBorrowUsd) so lendingPool.tvlUsd uses the net TVL, or
if the intent was to report gross supply, rename the property or add a clear
comment; update the tvlUsd field in the lendingPool construction (const
lendingPool = { ... tvlUsd: ... }) to reference the computed tvlUsd variable
instead of p.totalSupplyUsd.
| const main = async (timestamp = null) => { | ||
| let data = []; | ||
|
|
||
|
|
||
| for (const [chain, url] of Object.entries(pools_v2_endpoints)) { | ||
| if (!url) { | ||
| console.log(`Skipping v2 data for ${chain} - no URL configured`); | ||
| continue; | ||
| } | ||
| try { | ||
| console.log(`Fetching v2 data for ${chain}...`); | ||
| const chainData = await topLvl(chain, url, query, timestamp); | ||
| data.push(...chainData); | ||
| } catch (err) { | ||
| console.log(chain, err); | ||
| } | ||
| } | ||
|
|
||
| for (const [chain, url] of Object.entries(pools_v1_endpoints)) { | ||
| if (!url) { | ||
| console.log(`Skipping v1 data for ${chain} - no URL configured`); | ||
| continue; | ||
| } | ||
| try { | ||
| console.log(`Fetching v1 data for ${chain}...`); | ||
| const chainData = await topLvl(chain, url, query, timestamp); | ||
| data.push(...chainData); | ||
| } catch (err) { | ||
| console.log(chain, err); | ||
| } | ||
| for (const [chain, url] of Object.entries(pools_v1_endpoints)) { | ||
| if (!url) { | ||
| console.log(`Skipping v1 data for ${chain} - no URL configured`); | ||
| continue; | ||
| } | ||
| try { | ||
| console.log(`Fetching v1 data for ${chain}...`); | ||
| const chainData = await topLvl(chain, url, query, timestamp); | ||
| data.push(...chainData); | ||
| } catch (err) { | ||
| console.log(chain, err); | ||
| } | ||
| } | ||
|
|
||
| console.log(`build filteredData ...`); | ||
| console.log(`build filteredData ...`); | ||
|
|
||
| const filteredData = data.filter((p) => utils.keepFinite(p)); | ||
| return filteredData; | ||
| }; | ||
| const filteredData = data.filter((p) => utils.keepFinite(p)); | ||
| return filteredData; | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check if DefiLlama's utils or framework enforces unique pool IDs
rg -n 'pool.*unique|duplicate.*pool|pool.*already' --type=js -C 3 -g '!node_modules/**'Repository: DefiLlama/yield-server
Length of output: 8722
🏁 Script executed:
# Read the specific file and lines mentioned in the review
sed -n '1,20p' src/adaptors/teller/index.js && echo "---" && sed -n '280,316p' src/adaptors/teller/index.jsRepository: DefiLlama/yield-server
Length of output: 2314
🏁 Script executed:
# Find the topLvl function to understand what data it returns
rg -n 'const topLvl|function topLvl' --type=js -A 30 -g '!node_modules/**'Repository: DefiLlama/yield-server
Length of output: 50378
🏁 Script executed:
# Search for group_pool_address usage to understand pool ID generation
rg -n 'group_pool_address' --type=js -C 2 -g '!node_modules/**'Repository: DefiLlama/yield-server
Length of output: 3348
🏁 Script executed:
# Find utils.keepFinite to understand what it filters
rg -n 'keepFinite' --type=js -C 5 -g '!node_modules/**'Repository: DefiLlama/yield-server
Length of output: 50378
Duplicate pool IDs if same group_pool_address appears in both v1 and v2 subgraphs.
If a pool exists in both v1 and v2 endpoints, the code will emit duplicate pool IDs (e.g., "0xabc-lending" and "0xabc-collateral" appear twice). This violates the database UNIQUE constraint on the pool field and will cause insertion errors or test failures. Add deduplication logic (e.g., using a Set to track seen group_pool_address values, preferring v2 over v1) before returning filtered data.
🤖 Prompt for AI Agents
In `@src/adaptors/teller/index.js` around lines 280 - 316, The main function
collects pool entries from pools_v2_endpoints and pools_v1_endpoints causing
duplicate pool IDs when the same group_pool_address exists in both; add
deduplication before returning filteredData by iterating filteredData and using
a Set of seen group_pool_address values (or the composite pool id) to keep only
the first occurrence from v2 (ensure v2 is processed before v1 as in the current
loops) and skip duplicates from v1, then return the deduplicated array;
reference main, pools_v2_endpoints, pools_v1_endpoints, topLvl, filteredData,
and group_pool_address to locate where to apply this change.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@src/adaptors/teller/index.js`:
- Around line 487-500: The non-pool loop can call malformed endpoints when
GRAPH_API_KEY is undefined; add a guard to validate the API key and/or
constructed endpoints before calling topLvlNonPool. Specifically, in the main
flow where nonpool_endpoints are built (or at the start of main), check
process.env.GRAPH_API_KEY (or validate each endpoint string) and skip/log any
entry that would contain "undefined" or is missing the API key; update the loop
that iterates nonpool_endpoints and the endpoint construction logic to ensure
endpoints are valid before calling topLvlNonPool and to emit a clear warning
when GRAPH_API_KEY is not set.
- Around line 434-448: The pushed non-pool collateral object is using the
lending token symbol (`symbol: info.symbol`) which is incorrect; change the
`symbol` field in the object created in the `results.push` call to use the
collateral token's symbol (e.g., lookup the symbol for `addr` such as
`collateral.symbol` or from your token info map for the `addr`), falling back to
`info.symbol` if no collateral symbol exists; keep `underlyingTokens: [addr]`
and optionally update `poolMeta` to clarify it's collateral if you want an
additional hint.
- Around line 262-288: The two arrow-function constants
calculateActiveLenderYield and calculateActiveBorrowerYield are used inside
topLvl before their const declarations (causing a TDZ ReferenceError); fix by
relocating their definitions above topLvl (move both functions to before
topLvl’s declaration) or convert them into hoisted function declarations
(function calculateActiveLenderYield(...) { ... } and function
calculateActiveBorrowerYield(...) { ... }) and remove the duplicate definitions
later in the file so each function is defined once.
- Around line 21-27: Replace the hardcoded GRAPH_API_KEY value with a lookup
from an env var (e.g., process.env.THEGRAPH_API_KEY) and construct
nonpool_endpoints dynamically (use the GRAPH_API_KEY variable for template
strings) instead of committing secrets; ensure you remove the literal key from
the file and rotate the exposed key immediately. In addition, add a guard in the
main flow so that if process.env.THEGRAPH_API_KEY is undefined or empty the code
skips non-pool fetching (the nonpool_endpoints creation and any fetch logic that
uses nonpool_endpoints) to avoid runtime errors and accidental calls when the
key is absent; reference GRAPH_API_KEY, nonpool_endpoints, and main to locate
where to implement these changes.
🧹 Nitpick comments (3)
src/adaptors/teller/index.js (3)
58-89: Token info fetched sequentially — consider batching.Each token is fetched one at a time with its own timeout. For chains with many unique tokens this becomes a serial bottleneck.
Promise.allSettledwith the same per-token timeout would parallelize the work. Not blocking, but worth noting for performance.
117-132: Per-pool price fetching insidePromise.allmay cause rate-limit issues.Every pool fires its own
utils.getPricescall concurrently. With many pools this could hit rate limits. Consider deduplicating token addresses across all pools and making a single batch price call before themap, then looking up prices from the result.
314-324: No request timeout onaxios.post— could hang indefinitely.Add a timeout to prevent the adaptor from stalling if The Graph endpoint is unresponsive:
const response = await axios.post(endpoint, { query: nonPoolQueryString, variables: { lastId }, - }); + }, { timeout: 30000 });
| const GRAPH_API_KEY = 'c2d64965ccdfcfed572cdb30b0369ab0'; | ||
| const nonpool_endpoints = { | ||
| ethereum: `https://gateway.thegraph.com/api/${GRAPH_API_KEY}/subgraphs/id/4JruhWH1ZdwvUuMg2xCmtnZQYYHvmEq6cmTcZkpM6pW`, | ||
| base: `https://gateway.thegraph.com/api/${GRAPH_API_KEY}/subgraphs/id/8jSq7mzq9HEiJEcAZfvrTT4wYk59oMxm82xUpcVBzryF`, | ||
| arbitrum: `https://gateway.thegraph.com/api/${GRAPH_API_KEY}/subgraphs/id/F2Cgx4q4ATiopuZ13nr1EMKmZXwfAdevF3EujqfayK7a`, | ||
| polygon: `https://gateway.thegraph.com/api/${GRAPH_API_KEY}/subgraphs/id/BBp2ZJTG8j4sx9gLoFYN6iLCpWQsNpoiYjXNwRcE3DQr`, | ||
| }; |
There was a problem hiding this comment.
Hardcoded API key committed to a public repository.
The Graph gateway API key is embedded directly in source code. This key is now exposed in git history and can be abused by anyone. Move it to an environment variable (e.g., process.env.THEGRAPH_API_KEY) and rotate the current key immediately.
Proposed fix
-const GRAPH_API_KEY = 'c2d64965ccdfcfed572cdb30b0369ab0';
+const GRAPH_API_KEY = process.env.THEGRAPH_API_KEY;
const nonpool_endpoints = {
- ethereum: `https://gateway.thegraph.com/api/${GRAPH_API_KEY}/subgraphs/id/4JruhWH1ZdwvUuMg2xCmtnZQYYHvmEq6cmTcZkpM6pW`,
- base: `https://gateway.thegraph.com/api/${GRAPH_API_KEY}/subgraphs/id/8jSq7mzq9HEiJEcAZfvrTT4wYk59oMxm82xUpcVBzryF`,
- arbitrum: `https://gateway.thegraph.com/api/${GRAPH_API_KEY}/subgraphs/id/F2Cgx4q4ATiopuZ13nr1EMKmZXwfAdevF3EujqfayK7a`,
- polygon: `https://gateway.thegraph.com/api/${GRAPH_API_KEY}/subgraphs/id/BBp2ZJTG8j4sx9gLoFYN6iLCpWQsNpoiYjXNwRcE3DQr`,
+ ethereum: `https://gateway.thegraph.com/api/${GRAPH_API_KEY}/subgraphs/id/4JruhWH1ZdwvUuMg2xCmtnZQYYHvmEq6cmTcZkpM6pW`,
+ base: `https://gateway.thegraph.com/api/${GRAPH_API_KEY}/subgraphs/id/8jSq7mzq9HEiJEcAZfvrTT4wYk59oMxm82xUpcVBzryF`,
+ arbitrum: `https://gateway.thegraph.com/api/${GRAPH_API_KEY}/subgraphs/id/F2Cgx4q4ATiopuZ13nr1EMKmZXwfAdevF3EujqfayK7a`,
+ polygon: `https://gateway.thegraph.com/api/${GRAPH_API_KEY}/subgraphs/id/BBp2ZJTG8j4sx9gLoFYN6iLCpWQsNpoiYjXNwRcE3DQr`,
};Also guard against undefined in main so non-pool fetching is skipped gracefully when the env var isn't set.
🧰 Tools
🪛 Gitleaks (8.30.0)
[high] 21-21: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🤖 Prompt for AI Agents
In `@src/adaptors/teller/index.js` around lines 21 - 27, Replace the hardcoded
GRAPH_API_KEY value with a lookup from an env var (e.g.,
process.env.THEGRAPH_API_KEY) and construct nonpool_endpoints dynamically (use
the GRAPH_API_KEY variable for template strings) instead of committing secrets;
ensure you remove the literal key from the file and rotate the exposed key
immediately. In addition, add a guard in the main flow so that if
process.env.THEGRAPH_API_KEY is undefined or empty the code skips non-pool
fetching (the nonpool_endpoints creation and any fetch logic that uses
nonpool_endpoints) to avoid runtime errors and accidental calls when the key is
absent; reference GRAPH_API_KEY, nonpool_endpoints, and main to locate where to
implement these changes.
| // Calculate active yield for lenders | ||
| const calculateActiveLenderYield = (poolBorrowedPercent, interestRateLowerBound, interestRateUpperBound) => { | ||
| let poolYieldRaw; | ||
| if (poolBorrowedPercent === 0) { | ||
| poolYieldRaw = interestRateLowerBound; | ||
| } else if (poolBorrowedPercent === 1) { | ||
| poolYieldRaw = interestRateUpperBound; | ||
| } else { | ||
| const range = interestRateUpperBound - interestRateLowerBound; | ||
| poolYieldRaw = interestRateLowerBound + (poolBorrowedPercent * range); | ||
| } | ||
| return (poolYieldRaw / 100) * poolBorrowedPercent; | ||
| }; | ||
|
|
||
| // Calculate active yield for borrowers | ||
| const calculateActiveBorrowerYield = (poolBorrowedPercent, interestRateLowerBound, interestRateUpperBound) => { | ||
| let poolYieldRaw; | ||
| if (poolBorrowedPercent === 0) { | ||
| poolYieldRaw = interestRateLowerBound; | ||
| } else if (poolBorrowedPercent === 1) { | ||
| poolYieldRaw = interestRateUpperBound; | ||
| } else { | ||
| const range = interestRateUpperBound - interestRateLowerBound; | ||
| poolYieldRaw = interestRateLowerBound + (poolBorrowedPercent * range); | ||
| } | ||
| return poolYieldRaw / 100; | ||
| }; |
There was a problem hiding this comment.
calculateActiveLenderYield and calculateActiveBorrowerYield are called before they are defined — ReferenceError at runtime.
These are const arrow-function expressions (lines 263, 277), which are not hoisted. They are first called on lines 179 and 181 inside topLvl, which executes before reaching their declarations. This will throw a ReferenceError due to the temporal dead zone (TDZ).
Move both functions above topLvl, or convert them to hoisted function declarations.
Proposed fix — move declarations before topLvl (before line 91)
+// Calculate active yield for lenders
+const calculateActiveLenderYield = (poolBorrowedPercent, interestRateLowerBound, interestRateUpperBound) => {
+ let poolYieldRaw;
+ if (poolBorrowedPercent === 0) {
+ poolYieldRaw = interestRateLowerBound;
+ } else if (poolBorrowedPercent === 1) {
+ poolYieldRaw = interestRateUpperBound;
+ } else {
+ const range = interestRateUpperBound - interestRateLowerBound;
+ poolYieldRaw = interestRateLowerBound + (poolBorrowedPercent * range);
+ }
+ return (poolYieldRaw / 100) * poolBorrowedPercent;
+};
+
+// Calculate active yield for borrowers
+const calculateActiveBorrowerYield = (poolBorrowedPercent, interestRateLowerBound, interestRateUpperBound) => {
+ let poolYieldRaw;
+ if (poolBorrowedPercent === 0) {
+ poolYieldRaw = interestRateLowerBound;
+ } else if (poolBorrowedPercent === 1) {
+ poolYieldRaw = interestRateUpperBound;
+ } else {
+ const range = interestRateUpperBound - interestRateLowerBound;
+ poolYieldRaw = interestRateLowerBound + (poolBorrowedPercent * range);
+ }
+ return poolYieldRaw / 100;
+};
+
const topLvl = async (chainString, url, query, timestamp) => {And remove the duplicate definitions at lines 262–288.
🤖 Prompt for AI Agents
In `@src/adaptors/teller/index.js` around lines 262 - 288, The two arrow-function
constants calculateActiveLenderYield and calculateActiveBorrowerYield are used
inside topLvl before their const declarations (causing a TDZ ReferenceError);
fix by relocating their definitions above topLvl (move both functions to before
topLvl’s declaration) or convert them into hoisted function declarations
(function calculateActiveLenderYield(...) { ... } and function
calculateActiveBorrowerYield(...) { ... }) and remove the duplicate definitions
later in the file so each function is defined once.
| results.push({ | ||
| pool: poolId + '-collateral', | ||
| chain: utils.formatChain(chainString), | ||
| project: 'teller', | ||
| symbol: p.collateralSymbol, | ||
| mintedCoin: p.principalSymbol, | ||
| tvlUsd: Number(p.totalCollateralUsd.toFixed(4)), | ||
| totalSupplyUsd: Number(p.totalCollateralUsd.toFixed(4)), | ||
| totalBorrowUsd: Number(p.totalBorrowUsd.toFixed(4)), | ||
| ltv: p.ltv, | ||
| apyBaseBorrow: p.borrowApy, | ||
| symbol: info.symbol, | ||
| poolMeta: 'Non-Pool Collateral', | ||
| tvlUsd: Number(totalCollateralUsd.toFixed(4)), | ||
| totalSupplyUsd: Number(totalCollateralUsd.toFixed(4)), | ||
| totalBorrowUsd: Number(totalBorrowUsd.toFixed(4)), | ||
| apyBaseBorrow: borrowApy, | ||
| apyBase: 0, | ||
| underlyingTokens, | ||
| url, | ||
| }; | ||
| underlyingTokens: [addr], | ||
| url: appUrl, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Non-pool collateral entry uses the lending token symbol, not the collateral token symbol.
Line 438 sets symbol: info.symbol which is the lending token (e.g., USDC), but this entry represents locked collateral (e.g., ETH). This is misleading for users viewing the pool. Consider using the collateral token symbol(s) instead, or at minimum clarify via poolMeta.
🤖 Prompt for AI Agents
In `@src/adaptors/teller/index.js` around lines 434 - 448, The pushed non-pool
collateral object is using the lending token symbol (`symbol: info.symbol`)
which is incorrect; change the `symbol` field in the object created in the
`results.push` call to use the collateral token's symbol (e.g., lookup the
symbol for `addr` such as `collateral.symbol` or from your token info map for
the `addr`), falling back to `info.symbol` if no collateral symbol exists; keep
`underlyingTokens: [addr]` and optionally update `poolMeta` to clarify it's
collateral if you want an additional hint.
| // Fetch non-pool (direct P2P) loans from The Graph Protocol | ||
| for (const [chain, endpoint] of Object.entries(nonpool_endpoints)) { | ||
| if (!endpoint) { | ||
| console.log(`Skipping non-pool data for ${chain} - no URL configured`); | ||
| continue; | ||
| } | ||
| try { | ||
| console.log(`Fetching non-pool data for ${chain}...`); | ||
| const chainData = await topLvlNonPool(chain, endpoint); | ||
| data.push(...chainData); | ||
| } catch (err) { | ||
| console.log(`Non-pool ${chain}:`, err.message || err); | ||
| } | ||
| } |
There was a problem hiding this comment.
Non-pool endpoints will silently fail if GRAPH_API_KEY env var is undefined.
If you move the API key to an environment variable (per the earlier comment), GRAPH_API_KEY being undefined will produce malformed URLs like https://gateway.thegraph.com/api/undefined/subgraphs/.... The !endpoint guard on line 489 won't catch this. Add a check for the API key at the top of main or when constructing endpoints.
🤖 Prompt for AI Agents
In `@src/adaptors/teller/index.js` around lines 487 - 500, The non-pool loop can
call malformed endpoints when GRAPH_API_KEY is undefined; add a guard to
validate the API key and/or constructed endpoints before calling topLvlNonPool.
Specifically, in the main flow where nonpool_endpoints are built (or at the
start of main), check process.env.GRAPH_API_KEY (or validate each endpoint
string) and skip/log any entry that would contain "undefined" or is missing the
API key; update the loop that iterates nonpool_endpoints and the endpoint
construction logic to ensure endpoints are valid before calling topLvlNonPool
and to emit a clear warning when GRAPH_API_KEY is not set.
|
hi @ethereumdegen pls ensure the test passes before i can review the pr - thanks! |
updating subgraph endpoints (satsuma is deprecated) and using updated status keywords for more accurate accounting
Summary by CodeRabbit
Refactor
New Features