Skip to content

feat: add ScrubVault yield pools to scrub project (Kava USDt + Arbitrum USDC)#2564

Closed
gaspare100 wants to merge 3202 commits into
DefiLlama:masterfrom
gaspare100:feat/scrubvault-yield
Closed

feat: add ScrubVault yield pools to scrub project (Kava USDt + Arbitrum USDC)#2564
gaspare100 wants to merge 3202 commits into
DefiLlama:masterfrom
gaspare100:feat/scrubvault-yield

Conversation

@gaspare100
Copy link
Copy Markdown
Contributor

@gaspare100 gaspare100 commented Apr 7, 2026

Summary

Adds a yield adapter under src/adaptors/scrub/ tracking the two ScrubVault DepositVault pools, reusing the existing scrub protocol slug.

What is ScrubVault?

ScrubVault is a delta-neutral managed vault where users deposit USDt (Kava) or USDC (Arbitrum) and receive share tokens. Capital is deployed off-chain across CEXs and DEXs running a delta-neutral funding-rate and market-making strategy.

Why funds are not sitting in the contract

The vault contract acts as an on-chain accounting and settlement layer. Once deposits are processed, stablecoins move to the strategy wallet and are actively deployed on exchanges. The on-chain totalVaultValue is the authoritative AUM figure, updated via distributeRewards() each time PnL is settled. TVL and APY here reflect assets under management, not tokens in the contract.

APY methodology

  • Kava pool: rolling 30-day APR read from the live Kava subgraph (apr field, basis-points ×100), with on-chain RewardDistributed event fallback.
  • Arbitrum pool: APR computed from RewardDistributed events via eth_getLogs, annualised over a 30-day window. Returns 0 until reward history accumulates (vault is newly deployed).

Pools

Pool ID Chain Token Live TVL Live APY
0x7bff6c730da681df03364c955b165576186370bc-kava Kava USDt ~$42k ~12.5%
0x439a923517c4dfd3f3d0abb0c36e356d39cf3f9d-arbitrum Arbitrum USDC ~$2.8k 0% (new)

Note: the Arbitrum pool is below the $10k display threshold today — it will appear automatically once TVL grows.

Test results

All 16 adapter tests pass (npm run test --adapter=scrub).

Summary by CodeRabbit

  • New Features
    • Added ScrubVault yield adapter surfacing real-time 30-day annualized APY and TVL for Kava USDt and Arbitrum USDC vaults. Pools display APY, TVL, underlying tokens, pool metadata, and a direct invest.scrub.money link. APY now falls back to on‑chain reward data when external APR sources are unavailable to keep values current.

byyyyyyyyy and others added 30 commits February 13, 2024 17:03
* feat(MorphoBlue): adding morpho blue pools yields

* feat(emtamorpho): adding mm + fixing Merlin comments

* feat(emtamorpho): last change

* feat(emtamorpho): typing

* feat(metamorpho): using wadLibs

* feat(metamorpho): tvl mm error fixed
* Sommelier: Add Turbo GHO

* Edge case for apys

* Sommelier: Add ETH Trend Growth vault

* Add Turbo Steth

* Add Turbo SOMM

* Sommelier: Add Turbo EETH Vault

* Sommelier: Add turbo steth 2 vault

* Sommelier: Morpho Maxmimizer

* Modify next minor version of vaults to handle new holding position logic

* Add Turbo divETH & ETHx vaults

* Sommelier Add Turbo eETH V2

* Stash changes for partial chain abstraction

* Fix chain config issues

* Holding postion chain abstraction

* Finish abstracting away chains

* Add real yield eth on arbitrum
* feat: add cream-lending stables yield adaptor

* update cream-leanding adaptor
* Sommelier: Add Turbo GHO

* Edge case for apys

* Sommelier: Add ETH Trend Growth vault

* Add Turbo Steth

* Add Turbo SOMM

* Sommelier: Add Turbo EETH Vault

* Sommelier: Add turbo steth 2 vault

* Sommelier: Morpho Maxmimizer

* Modify next minor version of vaults to handle new holding position logic

* Add Turbo divETH & ETHx vaults

* Sommelier Add Turbo eETH V2

* Stash changes for partial chain abstraction

* Fix chain config issues

* Holding postion chain abstraction

* Finish abstracting away chains

* Add real yield eth on arbitrum

* RYUSD-ARB

---------

Co-authored-by: Phil <pbal@fordham.edu>
Co-authored-by: Phil <30321052+philipjames44@users.noreply.github.com>
slasher125 and others added 9 commits May 8, 2024 10:51
* IndigoProtocol/ Add Yields for Indigo Stability Pools

- create appropriate functionality to get APY for each Stability Pool
- fetch Prices/APR/AssetAnalytics/ADA price to USD
- calculate TVL

* IndigoProtocol/ Add Indigo Stability Pools

- update response of TVL
- update APY rewards

* IndigoProtocol/ Add Indigo Stability Pools

- update response of TVL
- update APY rewards
…um USDC)

Adds a yield adapter under src/adaptors/scrub/ tracking the two ScrubVault
DepositVault pools, reusing the existing scrub protocol slug.

ScrubVault is a delta-neutral managed vault. Users deposit USDt (Kava) or
USDC (Arbitrum) and receive share tokens. Capital is deployed off-chain
across CEXs and DEXs running a delta-neutral funding-rate and market-making
strategy.

Because funds are actively managed off-chain the vault contract holds only
unprocessed pending deposits. The on-chain totalVaultValue variable is the
authoritative AUM figure, updated via distributeRewards() each time PnL is
settled back to the vault. TVL and APY here reflect assets under management,
not tokens sitting in the contract.

APY methodology:
- Kava pool: rolling 30-day APR read from the live Kava subgraph (apr field
  in basis-points x100), falling back to on-chain RewardDistributed event
  computation if subgraph is unavailable.
- Arbitrum pool: APR computed from RewardDistributed events via eth_getLogs,
  annualised over a 30-day window. Returns 0 until reward history accumulates.

Pools:
- 0x7bff6c730da681df03364c955b165576186370bc-kava     (USDt, Kava)
- 0x439a923517c4dfd3f3d0abb0c36e356d39cf3f9d-arbitrum (USDC, Arbitrum)
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 7, 2026

Warning

Rate limit exceeded

@gaspare100 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 34 minutes and 8 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 34 minutes and 8 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8f46dfb1-e72e-4c16-9342-c51783a7e41a

📥 Commits

Reviewing files that changed from the base of the PR and between 154c56e and 6304e2c.

📒 Files selected for processing (1)
  • src/adaptors/scrub/index.js
📝 Walkthrough

Walkthrough

Adds a new DefiLlama yield adapter for ScrubVault that exposes timetravel: false, url, and apy(). It reports TVL from vault contracts and computes 30-day rolling base APY per vault via a subgraph APR or by parsing on-chain RewardDistributed events when the subgraph is unavailable.

Changes

Cohort / File(s) Summary
New ScrubVault Yield Adapter
src/adaptors/scrub/index.js
New adapter module exporting timetravel: false, url, and apy(); defines two vault configs (Kava USDT, Arbitrum USDC); reads totalVaultValue for tvlUsd; obtains apyBase from vault subgraph APR if present, otherwise parses RewardDistributed logs over ~30 days, sums positive rewards, annualizes and rounds; returns pool objects with metadata and per-vault URL.

Sequence Diagram(s)

sequenceDiagram
    participant Caller as External Caller
    participant Adapter as Scrub Adapter
    participant Vault as Vault Contract
    participant Subgraph as Vault Subgraph
    participant RPC as RPC Node

    Caller->>Adapter: apy()
    
    par Fetch TVL
        Adapter->>Vault: totalVaultValue() (ABI)
        Vault-->>Adapter: totalVaultValue
        Adapter->>Adapter: convert to tvlUsd (stablecoin decimals)
    and Fetch APY
        Adapter->>Subgraph: query APR (if subgraphUrl)
        alt Subgraph returns APR
            Subgraph-->>Adapter: apr (scaled)
            Adapter->>Adapter: convert to apyBase
        else Subgraph missing/failed
            Adapter->>RPC: eth_getLogs RewardDistributed (last ~30 days)
            RPC-->>Adapter: logs
            Adapter->>Adapter: decode rewards, filter positive, sum, annualize -> apyBase
        end
    end

    Adapter->>Adapter: clamp & round apyBase
    Adapter-->>Caller: return pools [{pool, chain, project, symbol, tvlUsd, apyBase, underlyingTokens, poolMeta, url}]
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hop through vaults where stablecoins hide,

I count the logs and query graphs beside,
Rewards and TVL I tally with delight,
Thirty days of yields turned into light,
A tiny scrub rabbit cheers the APY tonight!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding a new ScrubVault yield adapter with two configured pools (Kava USDt and Arbitrum USDC), which matches the changeset of introducing src/adaptors/scrub/index.js.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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.

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 the current code and only fix it if needed.

Inline comments:
In `@src/adaptors/scrub/index.js`:
- Around line 91-107: In fetchAprFromSubgraph, guard against
malformed/non-finite apr values: after extracting aprRaw (from
data?.data?.vault?.apr) validate it using a finite-number check before returning
Number(aprRaw) / 100; if the converted value is NaN or not finite, return null
so fetchAprFromSubgraph always yields either a finite number or null (instead of
leaking NaN into apyBase).
🪄 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: 22c9b9e4-4884-4a16-96e4-5c7d47698542

📥 Commits

Reviewing files that changed from the base of the PR and between 6cd3862 and b7517e8.

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

Comment thread src/adaptors/scrub/index.js Outdated
Comment thread src/adaptors/scrub/index.js Outdated
const blocksWindow = Math.ceil(windowSec / avgBlockTime);

// Fetch latest block number
const latestResp = await axios.post(rpc, {
Copy link
Copy Markdown
Contributor

@0xkr3p 0xkr3p Apr 15, 2026

Choose a reason for hiding this comment

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

can you refactor to use defillama SDK?

maybe something similar to this:

  Get latest block:
  // Instead of:
  const latestResp = await axios.post(rpc, {
    jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [],
  });
  const latestBlock = parseInt(latestResp.data.result, 16);

  // Use:
  const currentBlock = await sdk.api.util.getLatestBlock(chain);
  const latestBlock = currentBlock.number;

  Fetch event logs:
  // Instead of raw eth_getLogs via axios:
  const logsResp = await axios.post(rpc, {
    jsonrpc: '2.0', id: 2, method: 'eth_getLogs',
    params: [{ fromBlock, toBlock, address, topics }],
  });

  // Use:
  const logs = await sdk.getEventLogs({
    target: vaultAddress,
    eventAbi: 'event RewardDistributed(int256 rewardAmount, uint256 newTotalVaultValue, uint256 timestamp)',
    fromBlock,
    toBlock: latestBlock,
    chain,
  });

Comment thread src/adaptors/scrub/index.js Outdated
const fromBlock = Math.max(0, latestBlock - blocksWindow);

// Fetch logs
const logsResp = await axios.post(rpc, {
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.

same as above

Comment thread src/adaptors/scrub/index.js Outdated
apyBase: Math.round(apyBase * 100) / 100, // round to 2 dp
underlyingTokens: [vault.stablecoin],
poolMeta: vault.poolMeta,
url: 'https://invest.scrub.money/',
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.

Copy link
Copy Markdown
Contributor

@0xkr3p 0xkr3p left a comment

Choose a reason for hiding this comment

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

hey @gaspare100 thanks for the PR, few changes needed and TVL adapter must be merged before approval

@github-actions
Copy link
Copy Markdown

The aave-v3 adapter exports pools:

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

Nb of pools: 233
 

Sample pools:
┌─────────┬───────────────────────────────────────────────────────┬────────────┬───────────┬─────────────┬────────────────────┬────────────────────────┬──────────────────────────────────────────────────┬────────────────────┬────────────────────┬────────────────┬───────────────────────┬───────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬────────────┬────────────┬──────────┐
│ (index) │ pool                                                  │ chain      │ project   │ symbol      │ tvlUsd             │ apyBase                │ underlyingTokens                                 │ totalSupplyUsd     │ totalBorrowUsd     │ debtCeilingUsd │ apyBaseBorrow         │ ltv   │ url                                                                                                                             │ borrowable │ mintedCoin │ poolMeta │
├─────────┼───────────────────────────────────────────────────────┼────────────┼───────────┼─────────────┼────────────────────┼────────────────────────┼──────────────────────────────────────────────────┼────────────────────┼────────────────────┼────────────────┼───────────────────────┼───────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────────┼────────────┼──────────┤
│ 0       │ '0xbdfa7b7893081b35fb54027489e2bc7a38275129-ethereum' │ 'ethereum' │ 'aave-v3' │ 'weETH'     │ 4599031745.00551   │ 0.00010857884170300257 │ [ '0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee' ] │ 4599935775.515074  │ 904030.509563446   │ null           │ 1.0045853496709467    │ 0.775 │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee&marketName=proto_mainnet_v3' │ false      │ null       │ null     │
│ 1       │ '0x0b925ed163218f6662a35e0f0371ac234f9e9371-ethereum' │ 'ethereum' │ 'aave-v3' │ 'wstETH'    │ 4007711452.9986267 │ 0.00029575843761996454 │ [ '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0' ] │ 4085661728.9514613 │ 77950275.9528346   │ null           │ 0.02384882028458665   │ 0.785 │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0&marketName=proto_mainnet_v3' │ false      │ null       │ null     │
│ 2       │ '0x5ee5bf7ae06d1be5997a1a72006fe6c607ec6de8-ethereum' │ 'ethereum' │ 'aave-v3' │ 'WBTC'      │ 2955440468.3159914 │ 0.004744594216754388   │ [ '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599' ] │ 3040860971.433608  │ 85420503.11761665  │ null           │ 0.33778795763055525   │ 0.73  │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0x2260fac5e5542a773aa44fbcfedf7c193bc2c599&marketName=proto_mainnet_v3' │ true       │ null       │ null     │
│ 3       │ '0x5c647ce0ae10658ec44fa4e11a51c96e94efd1dd-ethereum' │ 'ethereum' │ 'aave-v3' │ 'cbBTC'     │ 1751187360.6729228 │ 0.002187251685429113   │ [ '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf' ] │ 1775562935.5865464 │ 24375574.91362357  │ null           │ 0.3186427557496026    │ 0.73  │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf&marketName=proto_mainnet_v3' │ true       │ null       │ null     │
│ 4       │ '0x2d62109243b87c4ba3ee7ba1d91b0dd0a074d7b1-ethereum' │ 'ethereum' │ 'aave-v3' │ 'rsETH'     │ 1192643724.0024607 │ 1.019687960477034e-7   │ [ '0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7' ] │ 1192748468.2733428 │ 104744.27088212967 │ null           │ 0.0013660511022450437 │ 0     │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0xa1290d69c65a6fe4df752f95823fae25cb99e5a7&marketName=proto_mainnet_v3' │ false      │ null       │ null     │
│ 5       │ '0x23878914efe38d27c4d67ab83ed1b93a74d4086a-ethereum' │ 'ethereum' │ 'aave-v3' │ 'USDT'      │ 1057173587.5582298 │ 2.314386677667458      │ [ '0xdAC17F958D2ee523a2206206994597C13D831ec7' ] │ 4577600331.630116  │ 3520426744.0718865 │ null           │ 3.3437422103696943    │ 0.75  │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0xdac17f958d2ee523a2206206994597c13d831ec7&marketName=proto_mainnet_v3' │ true       │ null       │ null     │
│ 6       │ '0x4579a27af00a62c0eb156349f31b345c08386419-ethereum' │ 'ethereum' │ 'aave-v3' │ 'sUSDe'     │ 903942514.5479499  │ 0                      │ [ '0x9D39A5DE30e57443BfF2A8307A4256c8797A3497' ] │ 903942481.3030113  │ -33.24493861198425 │ null           │ 0                     │ 0     │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0x9d39a5de30e57443bff2a8307a4256c8797a3497&marketName=proto_mainnet_v3' │ false      │ null       │ null     │
│ 7       │ '0x98c23e9d8f34fefb1b7bd6a91b7ff122f4e16f5c-ethereum' │ 'ethereum' │ 'aave-v3' │ 'USDC'      │ 740169900.7537959  │ 2.3431000281214875     │ [ '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' ] │ 3272330079.2726903 │ 2532160178.518894  │ null           │ 3.3644202780520565    │ 0.75  │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&marketName=proto_mainnet_v3' │ true       │ null       │ null     │
│ 8       │ '0x4d5f47fa6a74757f35c14fd3a6ef8e3c9bc514e8-ethereum' │ 'ethereum' │ 'aave-v3' │ 'WETH'      │ 658834493.2678087  │ 1.7747695409335362     │ [ '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' ] │ 6870578844.7069435 │ 6211744351.439135  │ null           │ 2.3094126369857166    │ 0.805 │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2&marketName=proto_mainnet_v3' │ true       │ null       │ null     │
│ 9       │ '0xd4ee376c40edc83832aaafc18fc0272660f5e90b-plasma'   │ 'plasma'   │ 'aave-v3' │ 'syrupUSDT' │ 609339974.395721   │ 0                      │ [ '0xC4374775489CB9C56003BF2C9b12495fC64F0771' ] │ 609339974.395721   │ 0                  │ null           │ 0                     │ 0     │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0xc4374775489cb9c56003bf2c9b12495fc64f0771&marketName=proto_plasma_v3'  │ false      │ null       │ null     │
└─────────┴───────────────────────────────────────────────────────┴────────────┴───────────┴─────────────┴────────────────────┴────────────────────────┴──────────────────────────────────────────────────┴────────────────────┴────────────────────┴────────────────┴───────────────────────┴───────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴────────────┴────────────┴──────────┘
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.

🧹 Nitpick comments (2)
src/adaptors/scrub/index.js (2)

19-24: Keep APY methodology docs consistent with implemented denominator.

Lines 20-24 say APR uses pre-reward TVL at window start, but Line 167 divides by current tvlUsd. Please align comment text (or adjust calculation) to prevent semantic drift.

Also applies to: 167-167

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/adaptors/scrub/index.js` around lines 19 - 24, Comment describes using
the "pre-reward TVL at the start of that window" as the denominator for rolling
30-day APY but the implementation divides by the current tvlUsd; update the code
or comment to match: either change the calculation to divide summed rewards by
the stored preRewardTvl (or a captured tvlUsd at window start) before
annualising, referencing the RewardDistributed handling and the variables
rewardAmount and tvlUsd, or adjust the documentation text that mentions
"pre-reward TVL" to state that the current tvlUsd is used as the denominator
(and ensure the Kava `apr` note remains accurate).

83-90: Prefer formatUnits with finite checks for numeric conversions in reward/TVL calculations.

Line 89 (Number(output)) and Line 157 (rewardRaw.toNumber()) can lose precision on large values or throw exceptions. While Scrub's reward amounts are unlikely to exceed JavaScript's safe integer bounds (2^53) in practice, using ethers.utils.formatUnits with Number.isFinite() guards is more defensive and consistent with other adapters in the codebase. Note: Line 157 is already wrapped in try-catch, so exceptions silently return 0 APR; Line 89 has no such protection.

Suggested refactor
 async function fetchTvlUsd(chain, vaultAddress, decimals) {
   const { output } = await sdk.api.abi.call({
     abi:    TOTAL_VAULT_VALUE_ABI,
     target: vaultAddress,
     chain,
   });
-  return Number(output) / 10 ** decimals;
+  const tvlUsd = Number(ethers.utils.formatUnits(output, decimals));
+  return Number.isFinite(tvlUsd) ? tvlUsd : 0;
 }
@@
-      totalRewardUsd += rewardRaw.toNumber() / 10 ** decimals;
+      const rewardUsd = Number(ethers.utils.formatUnits(rewardRaw, decimals));
+      if (!Number.isFinite(rewardUsd)) continue;
+      totalRewardUsd += rewardUsd;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/adaptors/scrub/index.js` around lines 83 - 90, Replace unsafe Number(...)
and .toNumber() conversions with ethers.utils.formatUnits and finite checks: in
fetchTvlUsd (function fetchTvlUsd) use formatUnits(output, decimals) then
parseFloat and guard with Number.isFinite — return 0 if not finite; similarly
replace rewardRaw.toNumber() usage (variable rewardRaw) with
parseFloat(formatUnits(rewardRaw, rewardDecimals)) and check Number.isFinite
before using the value to avoid precision loss or exceptions. Ensure
ethers.utils.formatUnits is imported/available where these conversions occur.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/adaptors/scrub/index.js`:
- Around line 19-24: Comment describes using the "pre-reward TVL at the start of
that window" as the denominator for rolling 30-day APY but the implementation
divides by the current tvlUsd; update the code or comment to match: either
change the calculation to divide summed rewards by the stored preRewardTvl (or a
captured tvlUsd at window start) before annualising, referencing the
RewardDistributed handling and the variables rewardAmount and tvlUsd, or adjust
the documentation text that mentions "pre-reward TVL" to state that the current
tvlUsd is used as the denominator (and ensure the Kava `apr` note remains
accurate).
- Around line 83-90: Replace unsafe Number(...) and .toNumber() conversions with
ethers.utils.formatUnits and finite checks: in fetchTvlUsd (function
fetchTvlUsd) use formatUnits(output, decimals) then parseFloat and guard with
Number.isFinite — return 0 if not finite; similarly replace rewardRaw.toNumber()
usage (variable rewardRaw) with parseFloat(formatUnits(rewardRaw,
rewardDecimals)) and check Number.isFinite before using the value to avoid
precision loss or exceptions. Ensure ethers.utils.formatUnits is
imported/available where these conversions occur.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a3949c2-861c-482f-a534-1480342f1a8e

📥 Commits

Reviewing files that changed from the base of the PR and between 42b1254 and 154c56e.

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

- Drop 30-day rolling APY approach
- Compute APR from single most-recent daily RewardDistributed event
  formula: rewardAmount / prevTVL * 365 * 100
- Use sdk.api.util.getLogs for both chains (no axios)
- Kava: 3-day window split into 5 parallel 10k-block SDK calls
  (Kava RPC caps eth_getLogs at 10k blocks per request)
- Kava latest block via ethers.providers.JsonRpcProvider
  (sdk.api.util.getLatestBlock does not support Kava)
- Arbitrum: single getLogs call; sdk.api.util.getLatestBlock works fine
- Verified live: Kava TVL=$55,590 APY=15%, Arb TVL=$6,770 APY=15%
@github-actions
Copy link
Copy Markdown

The aave-v3 adapter exports pools:

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

Nb of pools: 233
 

Sample pools:
┌─────────┬───────────────────────────────────────────────────────┬────────────┬───────────┬─────────────┬────────────────────┬────────────────────────┬──────────────────────────────────────────────────┬────────────────────┬────────────────────┬────────────────┬───────────────────────┬───────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬────────────┬────────────┬──────────┐
│ (index) │ pool                                                  │ chain      │ project   │ symbol      │ tvlUsd             │ apyBase                │ underlyingTokens                                 │ totalSupplyUsd     │ totalBorrowUsd     │ debtCeilingUsd │ apyBaseBorrow         │ ltv   │ url                                                                                                                             │ borrowable │ mintedCoin │ poolMeta │
├─────────┼───────────────────────────────────────────────────────┼────────────┼───────────┼─────────────┼────────────────────┼────────────────────────┼──────────────────────────────────────────────────┼────────────────────┼────────────────────┼────────────────┼───────────────────────┼───────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────────┼────────────┼──────────┤
│ 0       │ '0xbdfa7b7893081b35fb54027489e2bc7a38275129-ethereum' │ 'ethereum' │ 'aave-v3' │ 'weETH'     │ 4599293693.416918  │ 0.00010857884170300257 │ [ '0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee' ] │ 4600197775.712212  │ 904082.295293808   │ null           │ 1.0045853496709467    │ 0.775 │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee&marketName=proto_mainnet_v3' │ false      │ null       │ null     │
│ 1       │ '0x0b925ed163218f6662a35e0f0371ac234f9e9371-ethereum' │ 'ethereum' │ 'aave-v3' │ 'wstETH'    │ 4012574334.6487255 │ 0.0002957543415003866  │ [ '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0' ] │ 4090618642.283357  │ 78044307.63463163  │ null           │ 0.023848655136368156  │ 0.785 │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0&marketName=proto_mainnet_v3' │ false      │ null       │ null     │
│ 2       │ '0x5ee5bf7ae06d1be5997a1a72006fe6c607ec6de8-ethereum' │ 'ethereum' │ 'aave-v3' │ 'WBTC'      │ 2960919999.985433  │ 0.004744477913432535   │ [ '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599' ] │ 3046497156.4042883 │ 85577156.41885519  │ null           │ 0.3377862495951657    │ 0.73  │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0x2260fac5e5542a773aa44fbcfedf7c193bc2c599&marketName=proto_mainnet_v3' │ true       │ null       │ null     │
│ 3       │ '0x5c647ce0ae10658ec44fa4e11a51c96e94efd1dd-ethereum' │ 'ethereum' │ 'aave-v3' │ 'cbBTC'     │ 1753884690.045111  │ 0.0021872184966972657  │ [ '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf' ] │ 1778297497.601905  │ 24412807.556794167 │ null           │ 0.31864189878996174   │ 0.73  │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf&marketName=proto_mainnet_v3' │ true       │ null       │ null     │
│ 4       │ '0x2d62109243b87c4ba3ee7ba1d91b0dd0a074d7b1-ethereum' │ 'ethereum' │ 'aave-v3' │ 'rsETH'     │ 1197098997.565099  │ 1.019687960477034e-7   │ [ '0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7' ] │ 1197204133.1217082 │ 105135.55660915375 │ null           │ 0.0013660511022450437 │ 0     │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0xa1290d69c65a6fe4df752f95823fae25cb99e5a7&marketName=proto_mainnet_v3' │ false      │ null       │ null     │
│ 5       │ '0x23878914efe38d27c4d67ab83ed1b93a74d4086a-ethereum' │ 'ethereum' │ 'aave-v3' │ 'USDT'      │ 1050118675.8892505 │ 2.324412461649219      │ [ '0xdAC17F958D2ee523a2206206994597C13D831ec7' ] │ 4580051575.776786  │ 3529932899.887535  │ null           │ 3.3509768289933506    │ 0.75  │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0xdac17f958d2ee523a2206206994597c13d831ec7&marketName=proto_mainnet_v3' │ true       │ null       │ null     │
│ 6       │ '0x4579a27af00a62c0eb156349f31b345c08386419-ethereum' │ 'ethereum' │ 'aave-v3' │ 'sUSDe'     │ 903965914.5888523  │ 0                      │ [ '0x9D39A5DE30e57443BfF2A8307A4256c8797A3497' ] │ 903965881.3439136  │ -33.24493873119354 │ null           │ 0                     │ 0     │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0x9d39a5de30e57443bff2a8307a4256c8797a3497&marketName=proto_mainnet_v3' │ false      │ null       │ null     │
│ 7       │ '0x98c23e9d8f34fefb1b7bd6a91b7ff122f4e16f5c-ethereum' │ 'ethereum' │ 'aave-v3' │ 'USDC'      │ 740642063.8443227  │ 2.3424339211500516     │ [ '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' ] │ 3272825338.0147634 │ 2532183274.1704407 │ null           │ 3.3639420178257082    │ 0.75  │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&marketName=proto_mainnet_v3' │ true       │ null       │ null     │
│ 8       │ '0x4d5f47fa6a74757f35c14fd3a6ef8e3c9bc514e8-ethereum' │ 'ethereum' │ 'aave-v3' │ 'WETH'      │ 662836639.4259014  │ 1.772989113410155      │ [ '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' ] │ 6879768707.078143  │ 6216932067.652242  │ null           │ 2.308253958786386     │ 0.805 │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2&marketName=proto_mainnet_v3' │ true       │ null       │ null     │
│ 9       │ '0xd4ee376c40edc83832aaafc18fc0272660f5e90b-plasma'   │ 'plasma'   │ 'aave-v3' │ 'syrupUSDT' │ 609409735.5349813  │ 0                      │ [ '0xC4374775489CB9C56003BF2C9b12495fC64F0771' ] │ 609409735.5349813  │ 0                  │ null           │ 0                     │ 0     │ 'https://app.aave.com/reserve-overview/?underlyingAsset=0xc4374775489cb9c56003bf2c9b12495fc64f0771&marketName=proto_plasma_v3'  │ false      │ null       │ null     │
└─────────┴───────────────────────────────────────────────────────┴────────────┴───────────┴─────────────┴────────────────────┴────────────────────────┴──────────────────────────────────────────────────┴────────────────────┴────────────────────┴────────────────┴───────────────────────┴───────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴────────────┴────────────┴──────────┘
This adapter contains some pools with <10k TVL, these pools won't be shown in DefiLlama

@gaspare100
Copy link
Copy Markdown
Contributor Author

please check now

@gaspare100 gaspare100 requested a review from 0xkr3p April 15, 2026 13:24
const reward = parsed.args.rewardAmount; // int256, may be negative on a loss day
const newTvl = parsed.args.newTotalVaultValue;

if (reward.lte(0)) 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.

this could be misleading if we aren't including negative days right?

@gaspare100
Copy link
Copy Markdown
Contributor Author

gaspare100 commented Apr 29, 2026 via email

@gaspare100
Copy link
Copy Markdown
Contributor Author

Recreated as #2678 — same feature and same diff. The original branch was force-pushed during a local-history cleanup, which made this PR unreopenable per GitHub's force-push policy.

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.