feat: add DorkFi yield adapter (Algorand + Voi Network)#2599
feat: add DorkFi yield adapter (Algorand + Voi Network)#2599mikepappalardo wants to merge 1 commit into
Conversation
Reports supply and borrow APY for all 47 DorkFi lending markets across two chains and four pools. Pools: Algorand: 3333688282 (Pool A), 3345940978 (Pool B) Voi: 47139778 (Pool A), 47139781 (Pool B) APY computed from the protocol's kinked utilization curve: borrow APY = borrowRate + utilization × slope supply APY = borrow APY × utilization × (1 − reserveFactor) Markets include ALGO, USDC, goBTC, goETH, WBTC, WETH, LINK, SOL, USDT, VOI, UNIT, WAD, aUSDC, POW, tALGO, FINITE, FOLKS, COOP, GOLD$ and more. Data sourced from DorkFi API (dorkfi-api.nautilus.sh).
📝 WalkthroughWalkthroughAdds a new DorkFi DefiLlama yield server adapter that fetches market data and TVL analytics for Algorand and Voi Network, deduplicates markets, filters low-TVL entries, calculates APY metrics from rate parameters, and exports standardized pool output objects. Changes
Sequence Diagram(s)sequenceDiagram
participant DefiLlama as DefiLlama System
participant Adapter as DorkFi Adapter
participant API as DorkFi API
participant Processor as Data Processor
participant Output as Pool Output
DefiLlama->>Adapter: call apy()
activate Adapter
Adapter->>API: fetch Algorand markets
activate API
API-->>Adapter: market data + success flag
deactivate API
Adapter->>API: fetch Algorand TVL analytics
activate API
API-->>Adapter: TVL data (optional)
deactivate API
Adapter->>API: fetch Voi Network markets
activate API
API-->>Adapter: market data + success flag
deactivate API
Adapter->>API: fetch Voi Network TVL analytics
activate API
API-->>Adapter: TVL data (optional)
deactivate API
Adapter->>Processor: deduplicate by appId:marketId
Processor->>Processor: filter zero deposits & low TVL
Processor->>Processor: calculate utilization & APY
Processor-->>Adapter: processed markets
Adapter->>Output: emit pool objects<br/>(symbol, tvlUsd, apyBase, apyBaseBorrow)
Output-->>DefiLlama: standardized pool array
deactivate Adapter
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 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 dorkfi adapter exports pools: Test Suites: 1 passed, 1 total |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/adaptors/dorkfi/index.js (3)
162-162:underlyingTokens: []limits DefiLlama features.For lending adapters,
underlyingTokensis typically populated with the on-chain token address (ASA ID for Algorand/Voi). Leaving it empty disables token-based search/filtering and price attribution on the DefiLlama yields UI. Sincemarket.marketIdalready appears to be the ASA ID, consider populating it (as a string) or omitting the field entirely rather than sending[].♻️ Proposed fix
- underlyingTokens: [], + underlyingTokens: [String(market.marketId)],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/adaptors/dorkfi/index.js` at line 162, The adapter is sending underlyingTokens: [] which prevents token-based search/price attribution; update the object construction that sets underlyingTokens (or where markets are assembled) to either omit the underlyingTokens field or set it to the on-chain token address string using the existing market.marketId (e.g., underlyingTokens: [String(market.marketId)]), ensuring the ASA ID is passed as a string so DefiLlama can index and price the token.
114-116: Minor: redundant zero-deposit guard.
computeRatesguardstotalDep === 0, but the caller at line 145 already filters the same condition before invoking it. Not harmful, but one of the two checks is dead code. Prefer keeping the guard incomputeRates(defense in depth) and dropping the outer one, or vice versa.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/adaptors/dorkfi/index.js` around lines 114 - 116, Remove the redundant zero-deposit check in the caller that filters markets before calling computeRates and keep the defensive guard inside computeRates itself; specifically, ensure computeRates() still checks totalDep === 0 (using totalDep/totalBor logic and returns { borrowApy: 0, supplyApy: 0, utilization: 0 } when zero) and delete the outer pre-filter that skips markets with zero deposits so the single authoritative guard lives in computeRates.
83-95: Add request timeouts and per-chain resilience.Two reliability concerns:
- Neither
fetchMarketDatanorfetchAnalyticsTVLsets an axios timeout. Requests will wait indefinitely (axios 1.7.2 defaults totimeout: 0), risking adapter hangs if the DorkFi API becomes unresponsive.fetchMarketDatathrows on!data.success, whilefetchAnalyticsTVLgracefully returns{}. ThePromise.all()at line 139 means a single chain's market data failure crashes the entire adapter, even if the other chain succeeds.Wrap each request with a timeout (e.g., 30 seconds) and consider using
Promise.allSettled()instead ofPromise.all()to degrade gracefully per chain.♻️ Proposed fix
async function fetchMarketData(chain) { - const { data } = await axios.get(`${API_BASE}/market-data/${NETWORK[chain]}`); + const { data } = await axios.get(`${API_BASE}/market-data/${NETWORK[chain]}`, { timeout: 30_000 }); if (!data.success) throw new Error(`DorkFi API error: ${chain}`); return data.data; } async function fetchAnalyticsTVL(chain) { - const { data } = await axios.get(`${API_BASE}/analytics/tvl/${NETWORK[chain]}`); + const { data } = await axios.get(`${API_BASE}/analytics/tvl/${NETWORK[chain]}`, { timeout: 30_000 }); if (!data.success) return {}; const map = {}; for (const m of data.data.markets || []) map[`${m.appId}:${m.marketId}`] = m.tvl; return map; }And in
apy(), swapPromise.all(line 139) forPromise.allSettledso a single chain failure doesn't block results from healthy chains.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/adaptors/dorkfi/index.js` around lines 83 - 95, fetchMarketData and fetchAnalyticsTVL currently make axios calls without timeouts and the former throws on API errors which can abort all chains; update both functions to pass a timeout option (e.g., timeout: 30000) to their axios.get calls and handle request/response failures by returning safe fallbacks (e.g., null for fetchMarketData and {} or empty map for fetchAnalyticsTVL) instead of throwing; then in apy replace Promise.all(...) that aggregates per-chain promises with Promise.allSettled(...) and merge only the fulfilled results so one chain failure doesn’t crash the whole adapter.
🤖 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/dorkfi/index.js`:
- Around line 144-149: The loop over dedup(markets) silently treats missing TVL
entries as dust because tvlUsd is set via tvlMap[key] || 0 and then filtered
with if (tvlUsd < 1) continue; change this to detect absence explicitly (e.g.,
check Object.prototype.hasOwnProperty.call(tvlMap, key) or key in tvlMap), and
when a TVL entry is missing either log a warning (using the existing logger)
that the market key is absent from the analytics/tvl feed or include the market
with an explicit tvlUsd = 0 and a flag/note so downstream consumers can see it
rather than quietly dropping it; update the conditional that currently uses if
(tvlUsd < 1) continue to only skip true dust markets while preserving/annotating
markets missing from tvlMap.
---
Nitpick comments:
In `@src/adaptors/dorkfi/index.js`:
- Line 162: The adapter is sending underlyingTokens: [] which prevents
token-based search/price attribution; update the object construction that sets
underlyingTokens (or where markets are assembled) to either omit the
underlyingTokens field or set it to the on-chain token address string using the
existing market.marketId (e.g., underlyingTokens: [String(market.marketId)]),
ensuring the ASA ID is passed as a string so DefiLlama can index and price the
token.
- Around line 114-116: Remove the redundant zero-deposit check in the caller
that filters markets before calling computeRates and keep the defensive guard
inside computeRates itself; specifically, ensure computeRates() still checks
totalDep === 0 (using totalDep/totalBor logic and returns { borrowApy: 0,
supplyApy: 0, utilization: 0 } when zero) and delete the outer pre-filter that
skips markets with zero deposits so the single authoritative guard lives in
computeRates.
- Around line 83-95: fetchMarketData and fetchAnalyticsTVL currently make axios
calls without timeouts and the former throws on API errors which can abort all
chains; update both functions to pass a timeout option (e.g., timeout: 30000) to
their axios.get calls and handle request/response failures by returning safe
fallbacks (e.g., null for fetchMarketData and {} or empty map for
fetchAnalyticsTVL) instead of throwing; then in apy replace Promise.all(...)
that aggregates per-chain promises with Promise.allSettled(...) and merge only
the fulfilled results so one chain failure doesn’t crash the whole adapter.
🪄 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: 8aac67e1-a9ea-4c9b-8c60-55279b1ff111
📒 Files selected for processing (1)
src/adaptors/dorkfi/index.js
| for (const market of dedup(markets)) { | ||
| if (Number(market.totalScaledDeposits || 0) === 0) continue; | ||
|
|
||
| const key = `${market.appId}:${market.marketId}`; | ||
| const tvlUsd = tvlMap[key] || 0; | ||
| if (tvlUsd < 1) continue; |
There was a problem hiding this comment.
Silent filtering of markets missing from the TVL analytics endpoint.
Markets present in /market-data but absent from /analytics/tvl get tvlUsd = 0 and are dropped by the < 1 guard — indistinguishable from genuinely dust/empty markets. If the two endpoints can legitimately get out of sync (e.g., a newly-added market appears in market-data first), those pools silently disappear from the feed. Consider logging a warning for missing TVL entries, or exposing them with an explicit tvlUsd = 0 and letting DefiLlama filter downstream.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/adaptors/dorkfi/index.js` around lines 144 - 149, The loop over
dedup(markets) silently treats missing TVL entries as dust because tvlUsd is set
via tvlMap[key] || 0 and then filtered with if (tvlUsd < 1) continue; change
this to detect absence explicitly (e.g., check
Object.prototype.hasOwnProperty.call(tvlMap, key) or key in tvlMap), and when a
TVL entry is missing either log a warning (using the existing logger) that the
market key is absent from the analytics/tvl feed or include the market with an
explicit tvlUsd = 0 and a flag/note so downstream consumers can see it rather
than quietly dropping it; update the conditional that currently uses if (tvlUsd
< 1) continue to only skip true dust markets while preserving/annotating markets
missing from tvlMap.
| tvlUsd, | ||
| apyBase: supplyApy, | ||
| apyBaseBorrow: borrowApy, | ||
| underlyingTokens: [], |
There was a problem hiding this comment.
can you add underlying tokens pls
if you cannot add an address, pls add the coingeckoid in this format coingecko:id
DorkFi Lending Markets — APY Adapter
Adds supply and borrow APY data for all 47 DorkFi lending markets across Algorand and Voi Network.
Pools
Interest Rate Model
DorkFi uses a kinked utilization curve:
Sample Markets (current snapshot)
Data Source
All data sourced from DorkFi API (
https://dorkfi-api.nautilus.sh), which indexes on-chain protocol state.Links
Summary by CodeRabbit