Skip to content

Add BTCD sBTCD yield adapter#2705

Open
z-j-lin wants to merge 13 commits into
DefiLlama:masterfrom
BTCDlabs:master
Open

Add BTCD sBTCD yield adapter#2705
z-j-lin wants to merge 13 commits into
DefiLlama:masterfrom
BTCDlabs:master

Conversation

@z-j-lin
Copy link
Copy Markdown

@z-j-lin z-j-lin commented May 26, 2026

Adds the BTCD yield adapter for the Ethereum sBTCD staking vault only.

Exposes the sBTCD ERC-4626 vault
Computes TVL from on-chain totalAssets()
Computes base APY from 30-day previewRedeem() growth
Uses the BTCD underlying token and vault address metadata required by Yield Server

Summary by CodeRabbit

  • New Features
    • Added BTCD adapter providing TVL and a 30-day APY estimate for the sBTCD staking vault and linking to https://btcd.fi.
    • Derives an algorithmic BTC peg from on-chain price feeds to normalize TVL and APY calculations.
    • Robust behavior: skips pools or reports zero APY when required price, TVL, or timing inputs are missing or invalid.

Review Change Stack

z-j-lin added 9 commits April 29, 2026 12:17
Pure on-chain ERC4626 share-price-growth adapter for the BTCD staking
vault (sBTCD). Reads sBTCD.totalSupply for size, the on-chain
SBTCDOracle (Chainlink-compatible, 8 decimals USD) for tvlUsd, and 7d
share-price growth via convertToAssets(1e18) at current and historical
blocks for apyBase.

Single pool, no rewards (yield is auto-compounded into the share price
via 8h linear vesting from the YieldDistributor). underlyingTokens =
BTCD; pool id lowercased per yield-server convention.

Note: the upstream PR is gated on BTCD being listed on DefiLlama's TVL
page (DefiLlama-Adapters). Holding the merge here until that lands.
feat(btcd): remove cooldown mention from poolMeta in BTCD staking vault
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2dd6998f-fb8b-4c27-af9d-933e87e5b858

📥 Commits

Reviewing files that changed from the base of the PR and between 4bb1c66 and f9f96bd.

📒 Files selected for processing (1)
  • src/adaptors/btcd/index.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/adaptors/btcd/index.js

📝 Walkthrough

Walkthrough

Adds a new BTCD adapter exporting apy() and url; computes a 30-day sBTCD base APR from ERC‑4626 previewRedeem, derives an algorithmic BTC peg from Chainlink (sqrt(btcUsd / P0_USD)), computes TVL from totalAssets, and returns a single pool entry or [] if data is missing.

Changes

BTCD Adapter

Layer / File(s) Summary
Adapter setup and configuration
src/adaptors/btcd/index.js
Defines chain/address and timing/scaling constants, ERC-4626 and Chainlink ABI fragments, and helper formatDuration plus utilities for historical block resolution.
Base APY calculation
src/adaptors/btcd/index.js
computeApyBase() estimates a 30-day APR via previewRedeem at current and timestamp-derived historical block, enforces SBTCD_YIELD_TURNED_ON_BLOCK, uses BigInt math, and returns 0 on errors or non-positive growth.
BTC price peg derivation
src/adaptors/btcd/index.js
btcdPriceUsd() reads Chainlink latestRoundData, validates the BTC/USD feed, and computes the peg as sqrt(btcUsd / P0_USD), returning null on failures or invalid values.
APY entrypoint and TVL assembly
src/adaptors/btcd/index.js
apy() concurrently fetches totalAssets, peg, and apyBase, computes tvlUsd (assets normalized * peg), formats optional vesting/cooldown labels, and returns a single Ethereum/sBTCD pool object or [] when required data is missing.
Module exports
src/adaptors/btcd/index.js
Exports apy and url = https://btcd.fi.

Sequence Diagram

sequenceDiagram
  participant apy as apy()
  participant sBTCD as sBTCD contract
  participant Chainlink as Chainlink BTC/USD
  participant RPC as Ethereum RPC / Block history

  apy->>sBTCD: call totalAssets()
  apy->>Chainlink: call latestRoundData()
  apy->>RPC: computeApyBase() -> RPC: getBlockByTimestamp(lookback)
  RPC->>sBTCD: call previewRedeem(1e18) at lookback block
  RPC->>sBTCD: call previewRedeem(1e18) at latest block
  apy->>apy: compute peg, apyBase, tvlUsd -> return [pool]
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • 0xkr3p

Poem

🐰 I hopped through blocks and Chainlink light,
I nudged the math to make the peg just right,
Measured thirty days of gentle yield,
Packed assets, TVL, and APY in a tiny field,
A rabbit's patch of code — take flight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: adding a new yield adapter for BTCD's sBTCD vault, which matches the core purpose of this 213-line addition.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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 btcd adapter exports pools:

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

Nb of pools: 1
 

Sample pools:
┌─────────┬───────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬────────────────────┬────────────────────┬──────────────────────────────────────────────────┬───────────────────────────────────┬──────────────────────────────────┬──────────────────────────────────────────────┐
│ (index) │ pool                                                  │ chain      │ project │ symbol  │ tvlUsd             │ apyBase            │ underlyingTokens                                 │ poolMeta                          │ url                              │ token                                        │
├─────────┼───────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼────────────────────┼────────────────────┼──────────────────────────────────────────────────┼───────────────────────────────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
│ 0       │ '0x3bc801419479865b24b4d32fab0bf64638abbd5f-ethereum' │ 'Ethereum' │ 'btcd'  │ 'sBTCD' │ 346260.12944765744 │ 11.815592916901423 │ [ '0xC6694e05B750015f54Ac646544a4a9D33cbe4086' ] │ 'BTCD staking vault (8h vesting)' │ 'https://btcd.fi/app/stake/btcd' │ '0x3BC801419479865B24b4d32faB0Bf64638Abbd5f' │
└─────────┴───────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴────────────────────┴────────────────────┴──────────────────────────────────────────────────┴───────────────────────────────────┴──────────────────────────────────┴──────────────────────────────────────────────┘

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: 2

🤖 Prompt for all review comments with 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.

Inline comments:
In `@src/adaptors/btcd/index.js`:
- Around line 34-36: The getBlockAtTimestamp function calls axios.get without a
timeout and assumes response.data.height exists; modify getBlockAtTimestamp to
pass a sensible timeout option to axios.get (e.g., { timeout: 5000 }) and
validate the response before returning: ensure response and response.data exist
and that response.data.height is a finite number, otherwise throw or return a
clear error; reference the getBlockAtTimestamp function and the axios.get call
when making the change.
- Around line 96-103: The apy function's call to sdk.api.abi.call(..., abi:
totalAssetsAbi) can reject and cause Promise.all to fail; guard this by handling
failures for that specific call (either wrap the sdk.api.abi.call in a try/catch
before awaiting the other promises or use Promise.allSettled and treat a
rejected totalAssets result as zero), set a sensible default for
sBtcdAssetsRes.output (e.g. "0") so sBtcdTvlUsd calculation can continue, and
log the error; locate apy, sdk.api.abi.call with totalAssetsAbi, sBtcdAssetsRes
and sBtcdTvlUsd to apply the change.
🪄 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: 3daefc63-1cab-450d-841e-5175faaf693f

📥 Commits

Reviewing files that changed from the base of the PR and between cfec1b6 and c3f83ea.

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

Comment thread src/adaptors/btcd/index.js Outdated
Comment thread src/adaptors/btcd/index.js
z-j-lin added 2 commits May 27, 2026 12:47
feat(btcd): enhance sBTCD adapter with vesting and cooldown duration …
@github-actions
Copy link
Copy Markdown

The btcd adapter exports pools:

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

Nb of pools: 1
 

Sample pools:
┌─────────┬───────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬────────────────────┬────────────────────┬──────────────────────────────────────────────────┬───────────────────────────────────┬──────────────────────────────────┬──────────────────────────────────────────────┐
│ (index) │ pool                                                  │ chain      │ project │ symbol  │ tvlUsd             │ apyBase            │ underlyingTokens                                 │ poolMeta                          │ url                              │ token                                        │
├─────────┼───────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼────────────────────┼────────────────────┼──────────────────────────────────────────────────┼───────────────────────────────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
│ 0       │ '0x3bc801419479865b24b4d32fab0bf64638abbd5f-ethereum' │ 'Ethereum' │ 'btcd'  │ 'sBTCD' │ 343626.66856197134 │ 11.896495489813086 │ [ '0xC6694e05B750015f54Ac646544a4a9D33cbe4086' ] │ 'BTCD staking vault (1d vesting)' │ 'https://btcd.fi/app/stake/btcd' │ '0x3BC801419479865B24b4d32faB0Bf64638Abbd5f' │
└─────────┴───────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴────────────────────┴────────────────────┴──────────────────────────────────────────────────┴───────────────────────────────────┴──────────────────────────────────┴──────────────────────────────────────────────┘

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

🤖 Prompt for all review comments with 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.

Inline comments:
In `@src/adaptors/btcd/index.js`:
- Line 50: The code dereferences properties on the result of lookupBlock (the
variable `old`) inside `apy()` without verifying the return shape; update calls
that use `old.block` and `old.timestamp` to first guard that `old` is an object
with numeric `block`/`timestamp` (e.g., `if (!old || typeof old.block !==
'number') return 0` or similarly validate and bail) before using
`SBTCD_YIELD_TURNED_ON_BLOCK` or computing time deltas; apply the same guard
where `old.timestamp` is read (and for any `new`/other lookupBlock results) so
unexpected shapes won't throw and will cause a safe early return or fallback.
🪄 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: 76454f66-8d75-4e19-ba8d-7bbb0db38ede

📥 Commits

Reviewing files that changed from the base of the PR and between c3f83ea and b44d6e5.

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

} catch (e) {
return 0;
}
if (old.block < SBTCD_YIELD_TURNED_ON_BLOCK) return 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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard lookupBlock output before dereferencing fields.

Line 50 and Line 78 use old.block / old.timestamp outside the lookup guard. If lookupBlock returns an unexpected shape (instead of throwing), this can throw here and cause apy() to fail via Promise.all.

Suggested fix
   } catch (e) {
     return 0;
   }
+  if (
+    !Number.isFinite(latest?.timestamp) ||
+    !Number.isFinite(old?.timestamp) ||
+    !Number.isInteger(old?.block)
+  ) {
+    return 0;
+  }
   if (old.block < SBTCD_YIELD_TURNED_ON_BLOCK) return 0;
@@
   const rate = Number(rateScaled) / 1e18;
   if (!isFinite(rate) || rate <= 0) return 0;
   const secondsElapsed = latest.timestamp - old.timestamp;
   if (secondsElapsed <= 0) return 0;

Also applies to: 78-80

🤖 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/btcd/index.js` at line 50, The code dereferences properties on
the result of lookupBlock (the variable `old`) inside `apy()` without verifying
the return shape; update calls that use `old.block` and `old.timestamp` to first
guard that `old` is an object with numeric `block`/`timestamp` (e.g., `if (!old
|| typeof old.block !== 'number') return 0` or similarly validate and bail)
before using `SBTCD_YIELD_TURNED_ON_BLOCK` or computing time deltas; apply the
same guard where `old.timestamp` is read (and for any `new`/other lookupBlock
results) so unexpected shapes won't throw and will cause a safe early return or
fallback.

@github-actions
Copy link
Copy Markdown

The btcd adapter exports pools:

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

Nb of pools: 1
 

Sample pools:
┌─────────┬───────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬────────────────────┬────────────────────┬──────────────────────────────────────────────────┬───────────────────────────────────┬──────────────────────────────────┬──────────────────────────────────────────────┐
│ (index) │ pool                                                  │ chain      │ project │ symbol  │ tvlUsd             │ apyBase            │ underlyingTokens                                 │ poolMeta                          │ url                              │ token                                        │
├─────────┼───────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼────────────────────┼────────────────────┼──────────────────────────────────────────────────┼───────────────────────────────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
│ 0       │ '0x3bc801419479865b24b4d32fab0bf64638abbd5f-ethereum' │ 'Ethereum' │ 'btcd'  │ 'sBTCD' │ 343627.79696603393 │ 11.896962265462806 │ [ '0xC6694e05B750015f54Ac646544a4a9D33cbe4086' ] │ 'BTCD staking vault (1d vesting)' │ 'https://btcd.fi/app/stake/btcd' │ '0x3BC801419479865B24b4d32faB0Bf64638Abbd5f' │
└─────────┴───────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴────────────────────┴────────────────────┴──────────────────────────────────────────────────┴───────────────────────────────────┴──────────────────────────────────┴──────────────────────────────────────────────┘

@github-actions
Copy link
Copy Markdown

The btcd adapter exports pools:

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

Nb of pools: 1
 

Sample pools:
┌─────────┬───────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬────────────────────┬────────────────────┬──────────────────────────────────────────────────┬───────────────────────────────────┬──────────────────────────────────┬──────────────────────────────────────────────┐
│ (index) │ pool                                                  │ chain      │ project │ symbol  │ tvlUsd             │ apyBase            │ underlyingTokens                                 │ poolMeta                          │ url                              │ token                                        │
├─────────┼───────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼────────────────────┼────────────────────┼──────────────────────────────────────────────────┼───────────────────────────────────┼──────────────────────────────────┼──────────────────────────────────────────────┤
│ 0       │ '0x3bc801419479865b24b4d32fab0bf64638abbd5f-ethereum' │ 'Ethereum' │ 'btcd'  │ 'sBTCD' │ 335221.60489903495 │ 11.957720219520072 │ [ '0xC6694e05B750015f54Ac646544a4a9D33cbe4086' ] │ 'BTCD staking vault (1d vesting)' │ 'https://btcd.fi/app/stake/btcd' │ '0x3BC801419479865B24b4d32faB0Bf64638Abbd5f' │
└─────────┴───────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴────────────────────┴────────────────────┴──────────────────────────────────────────────────┴───────────────────────────────────┴──────────────────────────────────┴──────────────────────────────────────────────┘

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

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.

1 participant