Skip to content

feat: add DorkFi yield adapter (Algorand + Voi Network)#2599

Open
mikepappalardo wants to merge 1 commit into
DefiLlama:masterfrom
mikepappalardo:feat/dorkfi
Open

feat: add DorkFi yield adapter (Algorand + Voi Network)#2599
mikepappalardo wants to merge 1 commit into
DefiLlama:masterfrom
mikepappalardo:feat/dorkfi

Conversation

@mikepappalardo
Copy link
Copy Markdown

@mikepappalardo mikepappalardo commented Apr 17, 2026

DorkFi Lending Markets — APY Adapter

Adds supply and borrow APY data for all 47 DorkFi lending markets across Algorand and Voi Network.

Pools

Chain Pool App ID
Algorand Pool A 3333688282
Algorand Pool B 3345940978
Voi Network Pool A 47139778
Voi Network Pool B 47139781

Interest Rate Model

DorkFi uses a kinked utilization curve:

borrowAPR = borrowRate + utilization × slope      (all in basis points)
supplyAPR = borrowAPR × utilization × (1 − reserveFactor)
APY       = (1 + APR/365)^365 − 1

Sample Markets (current snapshot)

Symbol Chain TVL Supply APY Borrow APY
UNIT Voi Network $58K 0.00% 1.07%
goBTC Algorand $44K 0.01% 3.17%
USDC Algorand $38.5K 5.17% 16.09%
VOI Voi Network $37.8K 1.15% 9.49%
aUSDC Voi Network $18.5K 10.74% 22.45%

Data Source

All data sourced from DorkFi API (https://dorkfi-api.nautilus.sh), which indexes on-chain protocol state.

Links

Summary by CodeRabbit

  • New Features
    • Added DorkFi yield server integration providing market data, APY calculations, and TVL analytics for Algorand and Voi Network ecosystems.

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).
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 17, 2026

📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
DorkFi Adapter Implementation
src/adaptors/dorkfi/index.js
New yield server adapter (172 lines) fetching DorkFi markets, deduplicating by appId:marketId, filtering zero-deposit/low-TVL pools, computing utilization-based APY from borrow rates and slopes, converting to annualized percentages, mapping market symbols, and emitting standardized pool metrics with TVL and URLs.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

A rabbit hops through markets new,
Where DorkFi data sparkles true,
Borrowing rates and TVL blend,
APY calculations transcend—
Voi and Algo's yields aligned! 🐰✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a DorkFi yield adapter for Algorand and Voi Network, which matches the new adapter file and objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

The dorkfi adapter exports pools:

Test Suites: 1 passed, 1 total
Tests: 286 passed, 286 total
Snapshots: 0 total
Time: 0.313 s
Ran all test suites.

Nb of pools: 47
 

Sample pools:
┌─────────┬─────────────────────────────────────────┬───────────────┬──────────┬─────────┬──────────────┬─────────┬───────────────┬──────────────────┬───────────────────┬───────────────────┐
│ (index) │ pool                                    │ chain         │ project  │ symbol  │ tvlUsd       │ apyBase │ apyBaseBorrow │ underlyingTokens │ poolMeta          │ url               │
├─────────┼─────────────────────────────────────────┼───────────────┼──────────┼─────────┼──────────────┼─────────┼───────────────┼──────────────────┼───────────────────┼───────────────────┤
│ 0       │ 'dorkfi-voi-47139778-420069'            │ 'Voi Network' │ 'dorkfi' │ 'UNIT'  │ 58009.463574 │ 0.0013  │ 1.0657        │ []               │ 'Pool 47139778'   │ 'https://dork.fi' │
│ 1       │ 'dorkfi-algorand-3333688282-3211820549' │ 'Algorand'    │ 'dorkfi' │ 'goBTC' │ 44246.610088 │ 0.0105  │ 3.1683        │ []               │ 'Pool 3333688282' │ 'https://dork.fi' │
│ 2       │ 'dorkfi-algorand-3333688282-3210682240' │ 'Algorand'    │ 'dorkfi' │ 'USDC'  │ 38506.065333 │ 5.1716  │ 16.0926       │ []               │ 'Pool 3333688282' │ 'https://dork.fi' │
│ 3       │ 'dorkfi-voi-47139778-41877720'          │ 'Voi Network' │ 'dorkfi' │ 'VOI'   │ 37760.919801 │ 1.1543  │ 9.4869        │ []               │ 'Pool 47139778'   │ 'https://dork.fi' │
│ 4       │ 'dorkfi-voi-47139778-395614'            │ 'Voi Network' │ 'dorkfi' │ 'aUSDC' │ 18492.164707 │ 10.7352 │ 22.4507       │ []               │ 'Pool 47139778'   │ 'https://dork.fi' │
│ 5       │ 'dorkfi-algorand-3333688282-3220125024' │ 'Algorand'    │ 'dorkfi' │ 'UNIT'  │ 17014.621508 │ 0.0001  │ 2.0248        │ []               │ 'Pool 3333688282' │ 'https://dork.fi' │
│ 6       │ 'dorkfi-voi-47139781-410111'            │ 'Voi Network' │ 'dorkfi' │ 'USDT'  │ 10684.654951 │ 0       │ 1.0064        │ []               │ 'Pool 47139781'   │ 'https://dork.fi' │
│ 7       │ 'dorkfi-algorand-3345940978-3212524778' │ 'Algorand'    │ 'dorkfi' │ 'COOP'  │ 9001.219319  │ 0       │ 2.0201        │ []               │ 'Pool 3345940978' │ 'https://dork.fi' │
│ 8       │ 'dorkfi-algorand-3333688282-3207744109' │ 'Algorand'    │ 'dorkfi' │ 'ALGO'  │ 6953.878466  │ 0.33    │ 2.0236        │ []               │ 'Pool 3333688282' │ 'https://dork.fi' │
│ 9       │ 'dorkfi-algorand-3333688282-3211838479' │ 'Algorand'    │ 'dorkfi' │ 'LINK'  │ 5567.626429  │ 0       │ 2.0201        │ []               │ 'Pool 3333688282' │ 'https://dork.fi' │
└─────────┴─────────────────────────────────────────┴───────────────┴──────────┴─────────┴──────────────┴─────────┴───────────────┴──────────────────┴───────────────────┴───────────────────┘
This adapter contains some pools with <10k TVL, these pools won't be shown in DefiLlama

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/adaptors/dorkfi/index.js (3)

162-162: underlyingTokens: [] limits DefiLlama features.

For lending adapters, underlyingTokens is 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. Since market.marketId already 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.

computeRates guards totalDep === 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 in computeRates (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:

  1. Neither fetchMarketData nor fetchAnalyticsTVL sets an axios timeout. Requests will wait indefinitely (axios 1.7.2 defaults to timeout: 0), risking adapter hangs if the DorkFi API becomes unresponsive.
  2. fetchMarketData throws on !data.success, while fetchAnalyticsTVL gracefully returns {}. The Promise.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 of Promise.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(), swap Promise.all (line 139) for Promise.allSettled so 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

📥 Commits

Reviewing files that changed from the base of the PR and between 55f71b3 and 161b532.

📒 Files selected for processing (1)
  • src/adaptors/dorkfi/index.js

Comment on lines +144 to +149
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;
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.

⚠️ Potential issue | 🟡 Minor

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: [],
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.

can you add underlying tokens pls

if you cannot add an address, pls add the coingeckoid in this format coingecko:id

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants