Skip to content

Conversation

@sirmoremoney
Copy link

@sirmoremoney sirmoremoney commented Jan 13, 2026

Summary

Test output

Nb of pools: 1

Sample pools: [
  {
    pool: '0xd53b68fb4eb907c3c1e348cd7d7bede34f763805-ethereum',
    chain: 'Ethereum',
    project: 'lazyusd',
    symbol: 'USDC',
    tvlUsd: 500661.494622,
    apyBase: 8.048184567666667,
    underlyingTokens: [ '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' ],
    url: 'https://getlazy.xyz'
  }
]

Test plan

  • Ran npm run test --adapter=lazyusd - all relevant tests pass
  • Pending: TVL adapter merge (PR #17673) for protocol slug validation

Summary by CodeRabbit

  • New Features
    • Added LazyUSD adapter that fetches on-chain vault data to report TVL and APY for a USDC pool.
    • Computes APY using a 7-day historical comparison with a 1-day fallback when history is limited, and a fallback method for very young vaults.
    • Returns normalized pool data: symbol, TVL, APY, underlying token, and project URL.

✏️ Tip: You can customize this high-level summary in your review settings.

LazyUSD is a delta-neutral yield vault on Ethereum that deploys USDC
across lending protocols, DEXs, and derivatives platforms to generate
sustainable yields while maintaining full USDC backing.

The adapter calculates APY based on share price changes over a 7-day
window, falling back to accumulated yield for new vaults.
@coderabbitai
Copy link

coderabbitai bot commented Jan 13, 2026

📝 Walkthrough

Walkthrough

Adds a new LazyUSD adapter that reads a Vault contract on Ethereum to compute TVL and APY (7-day comparison with 1-day fallback) and returns a standardized pool object with fields like pool, chain, project, symbol, tvlUsd, apyBase, underlyingTokens, and url.

Changes

Cohort / File(s) Change Summary
New LazyUSD Adapter Module
src/adaptors/lazyusd/index.js
Adds an async apy export that queries vault sharePrice and totalAssets via RPC, attempts historical lookup (7d, fallback to 1d or observed-change method), computes apyBase, returns a normalized pool object, and exports timetravel: false and url.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Module as LazyUSD Module
    participant RPC as Ethereum RPC
    participant SDK as DefiLlama SDK

    Caller->>Module: apy()
    Module->>RPC: read current sharePrice & totalAssets
    RPC-->>Module: current data
    Module->>RPC: read historical block (7d ago)
    alt historical data present
        RPC-->>Module: historical sharePrice
        Module->>Module: compute APY from price change (annualize)
    else historical data missing
        RPC-->>Module: no historical price
        Module->>RPC: fetch recent accumulatedYield / deposits
        RPC-->>Module: accumulated data
        Module->>Module: compute fallback APY from observed change
    end
    Module->>Caller: return array with pool object (tvlUsd, apyBase, etc.)
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 I hop through blocks where vault numbers play,

Counting shares and TVL along the way.
If old price hides, I find a fallback trail,
Returning one pool with an APY tale.

🚥 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 'Add LazyUSD yield adapter' clearly and directly describes the main change: adding a new yield adapter module for LazyUSD.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
src/adaptors/lazyusd/index.js (2)

1-1: Remove unused import.

sdk is imported but never used in this file.

🧹 Suggested fix
-const sdk = require('@defillama/sdk');
 const { ethers } = require('ethers');

13-36: Consider adding error handling for RPC calls.

The function makes several async RPC calls (getBlockNumber, sharePrice, totalAssets) without try-catch. If the RPC provider fails or returns an error, this will throw an unhandled exception.

🛡️ Suggested approach
 const apy = async () => {
+  try {
     const provider = new ethers.providers.JsonRpcProvider('https://eth.llamarpc.com');
     const vault = new ethers.Contract(VAULT, ABI, provider);

     const latestBlock = await provider.getBlockNumber();
     // ... rest of the function ...
     
     return [{
       // pool data
     }];
+  } catch (error) {
+    console.error('LazyUSD adapter error:', error.message);
+    throw error; // or return [] depending on adapter conventions
+  }
 };
📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67a354e and 5156c75.

📒 Files selected for processing (1)
  • src/adaptors/lazyusd/index.js
🧰 Additional context used
🧬 Code graph analysis (1)
src/adaptors/lazyusd/index.js (1)
src/adaptors/beforeTests.js (1)
  • module (55-55)
🔇 Additional comments (4)
src/adaptors/lazyusd/index.js (4)

4-6: LGTM!

Constants are well-defined. The USDC address matches the Ethereum mainnet contract.


8-11: LGTM!

Human-readable ABI is correctly formatted for ethers.js v5.


38-47: LGTM!

Pool object structure follows DefiLlama adapter conventions with all required fields properly populated.


50-54: LGTM!

Module exports are correctly structured with timetravel: false, the apy function, and the project URL.


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

@llamatester
Copy link

Error while running lazyusd adapter:

Test Suites: 1 failed, 1 total
Tests: 1 failed, 9 passed, 10 total
Snapshots: 0 total
Time: 0.32 s
Ran all test suites.

Nb of pools: 1
 

Sample pools:
┌─────────┬───────────────────────────────────────────────────────┬────────────┬───────────┬────────┬───────────────┬───────────────────┬──────────────────────────────────────────────────┬───────────────────────┐
│ (index) │ pool                                                  │ chain      │ project   │ symbol │ tvlUsd        │ apyBase           │ underlyingTokens                                 │ url                   │
├─────────┼───────────────────────────────────────────────────────┼────────────┼───────────┼────────┼───────────────┼───────────────────┼──────────────────────────────────────────────────┼───────────────────────┤
│ 0       │ '0xd53b68fb4eb907c3c1e348cd7d7bede34f763805-ethereum' │ 'Ethereum' │ 'lazyusd' │ 'USDC' │ 500661.494622 │ 8.048184567666667 │ [ '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' ] │ 'https://getlazy.xyz' │
└─────────┴───────────────────────────────────────────────────────┴────────────┴───────────┴────────┴───────────────┴───────────────────┴──────────────────────────────────────────────────┴───────────────────────┘
}

Copy link

@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

🤖 Fix all issues with AI agents
In @src/adaptors/lazyusd/index.js:
- Around line 40-42: Prevent division by zero when computing APY: before
calculating priceChange and apyBase, check if historicalSharePrice is 0 or falsy
and handle that case (e.g., set apyBase = 0 or null or skip the computation)
instead of performing (currentSharePrice - historicalSharePrice) /
historicalSharePrice; update the logic around priceChange, historicalSharePrice,
currentSharePrice and apyBase to only compute priceChange and apyBase when
historicalSharePrice is nonzero, otherwise assign a safe default.
- Around line 61-64: The APY fallback uses a hardcoded daysLive = 6 and divides
by totalDeposited, which will become incorrect as the vault ages and can divide
by zero; replace the hardcoded value by computing daysLive from the vault
launch/deployment timestamp (e.g., fetch a deployment timestamp from the
contract or vault metadata and compute (now - launchTimestamp)/86400) and add
defensive checks: only compute yieldRate/apyBase when totalDeposited > 0, and
ensure daysLive is at least 1 (or use a minimum clamp) to avoid division by
zero; update the calculation around the variables daysLive, yieldRate,
accumulatedYield, totalDeposited, and apyBase accordingly and default apyBase to
0 when data is insufficient.
🧹 Nitpick comments (1)
src/adaptors/lazyusd/index.js (1)

43-51: Catch block swallows all errors, not just "vault didn't exist".

Network failures, RPC errors, or other transient issues would trigger the fallback calculation, potentially returning incorrect APY data. Consider checking the error type or logging for observability.

♻️ Suggested improvement
   } catch (e) {
-    // Vault might not have existed 7 days ago, use accumulated yield instead
+    // Vault might not have existed 7 days ago, use accumulated yield instead
+    console.log(`Historical share price unavailable, using fallback: ${e.message}`);
     const accumulatedYield = (
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e95933c and 7e1685c.

📒 Files selected for processing (1)
  • src/adaptors/lazyusd/index.js
🧰 Additional context used
🧬 Code graph analysis (1)
src/adaptors/lazyusd/index.js (1)
src/adaptors/beforeTests.js (1)
  • module (55-55)
🔇 Additional comments (4)
src/adaptors/lazyusd/index.js (4)

1-4: LGTM!

Constants are well-defined with clear naming. The contract addresses match the expected format.


6-23: LGTM!

The share price and TVL fetching logic is clean and correctly handles USDC's 6 decimal places.


67-76: LGTM!

The pool structure follows the expected DefiLlama adapter format with proper lowercase pool ID construction.


79-83: The timetravel: false setting is correct.

The adapter does not accept a timestamp parameter for external historical queries. While it internally uses a fixed historical block (7 days ago) for APY calculation, the timetravel flag indicates whether the adapter function accepts an external timestamp parameter—which lazyusd does not. Adapters with timetravel: true (like smardex-usdn) accept a timestamp parameter and can be queried at arbitrary historical points.

Comment on lines 40 to 42
// Calculate 7-day APY
const priceChange = (currentSharePrice - historicalSharePrice) / historicalSharePrice;
apyBase = (priceChange / 7) * 365 * 100;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential division by zero if historicalSharePrice is 0.

If the vault's historical share price was 0 (edge case during initialization), this would produce Infinity or NaN for the APY.

🛡️ Suggested defensive check
     // Calculate 7-day APY
+    if (historicalSharePrice === 0) {
+      throw new Error('Historical share price is zero');
+    }
     const priceChange = (currentSharePrice - historicalSharePrice) / historicalSharePrice;
     apyBase = (priceChange / 7) * 365 * 100;
🤖 Prompt for AI Agents
In @src/adaptors/lazyusd/index.js around lines 40 - 42, Prevent division by zero
when computing APY: before calculating priceChange and apyBase, check if
historicalSharePrice is 0 or falsy and handle that case (e.g., set apyBase = 0
or null or skip the computation) instead of performing (currentSharePrice -
historicalSharePrice) / historicalSharePrice; update the logic around
priceChange, historicalSharePrice, currentSharePrice and apyBase to only compute
priceChange and apyBase when historicalSharePrice is nonzero, otherwise assign a
safe default.

- Use vault deploy block to calculate available history
- Try 7-day window first, fall back to 1-day if vault is newer
- Remove hardcoded daysLive that would become stale
Copy link

@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

🤖 Fix all issues with AI agents
In @src/adaptors/lazyusd/index.js:
- Around line 48-49: The calculation for apyBase uses historicalSharePrice
without guarding against zero, which will produce Infinity; update the logic in
the block that computes priceChange and apyBase to check historicalSharePrice
(and possibly very small epsilon) before dividing: if historicalSharePrice is
zero or nearly zero, set apyBase to 0 (or skip the calculation) instead of
performing the division; adjust references in this change to the variables
priceChange, historicalSharePrice, daysForCalc, and apyBase so callers receive a
safe numeric value.
🧹 Nitpick comments (2)
src/adaptors/lazyusd/index.js (2)

11-25: Consider parallelizing independent RPC calls.

The currentSharePrice and totalAssets calls are independent and could be fetched concurrently using Promise.all to reduce latency.

♻️ Suggested refactor
-  const currentSharePrice = (
-    await sdk.api.abi.call({
+  const [sharePriceResult, totalAssetsResult] = await Promise.all([
+    sdk.api.abi.call({
       target: VAULT,
       abi: 'function sharePrice() view returns (uint256)',
       chain: 'ethereum',
-    })
-  ).output / 1e6;
-
-  const totalAssets = (
-    await sdk.api.abi.call({
+    }),
+    sdk.api.abi.call({
       target: VAULT,
       abi: 'function totalAssets() view returns (uint256)',
       chain: 'ethereum',
-    })
-  ).output / 1e6;
+    }),
+  ]);
+
+  const currentSharePrice = sharePriceResult.output / 1e6;
+  const totalAssets = totalAssetsResult.output / 1e6;

32-36: Edge case: Vault less than 1 day old returns zero APY.

If the vault is less than 1 day old, the 1-day fallback will still be before VAULT_DEPLOY_BLOCK, causing apyBase to remain 0. Consider using the maximum available history from the deploy block instead:

♻️ Suggested improvement to use all available history
   // Fall back to 1-day if vault is less than 7 days old
   if (historicalBlock < VAULT_DEPLOY_BLOCK) {
-    historicalBlock = latestBlock.number - blocksPerDay;
-    daysForCalc = 1;
+    historicalBlock = VAULT_DEPLOY_BLOCK;
+    const blocksElapsed = latestBlock.number - VAULT_DEPLOY_BLOCK;
+    daysForCalc = blocksElapsed / blocksPerDay;
   }

This ensures the adapter uses all available history rather than defaulting to fixed windows, providing better APY estimates for vaults between 1-7 days old.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e1685c and 67a354e.

📒 Files selected for processing (1)
  • src/adaptors/lazyusd/index.js
🧰 Additional context used
🧬 Code graph analysis (1)
src/adaptors/lazyusd/index.js (1)
src/adaptors/beforeTests.js (1)
  • module (55-55)
🔇 Additional comments (3)
src/adaptors/lazyusd/index.js (3)

1-5: LGTM!

Constants are appropriately defined with checksummed addresses. The deploy block constant enables proper historical data bounds checking.


64-68: LGTM!

Module exports are correctly structured with timetravel: false (appropriate since this adapter doesn't support historical queries via the standard API), the apy function, and the project URL.


52-61: Pool structure is correct and ready; protocol slug registration pending TVL adapter PR.

The return object follows the DefiLlama adapter format correctly. The project: 'lazyusd' slug is not yet registered in the DefiLlama protocol registry, but this is expected and depends on PR #17673 being merged to add the TVL adapter first. No changes needed in this code.

Comment on lines +48 to +49
const priceChange = (currentSharePrice - historicalSharePrice) / historicalSharePrice;
apyBase = (priceChange / daysForCalc) * 365 * 100;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential division by zero if historical share price is zero.

If historicalSharePrice is 0 (unlikely but possible during vault initialization), line 48 will produce Infinity. Consider adding a guard:

🛡️ Suggested guard
+    if (historicalSharePrice === 0) {
+      return [{ /* ... pool object with apyBase: 0 ... */ }];
+    }
     const priceChange = (currentSharePrice - historicalSharePrice) / historicalSharePrice;
     apyBase = (priceChange / daysForCalc) * 365 * 100;
🤖 Prompt for AI Agents
In @src/adaptors/lazyusd/index.js around lines 48 - 49, The calculation for
apyBase uses historicalSharePrice without guarding against zero, which will
produce Infinity; update the logic in the block that computes priceChange and
apyBase to check historicalSharePrice (and possibly very small epsilon) before
dividing: if historicalSharePrice is zero or nearly zero, set apyBase to 0 (or
skip the calculation) instead of performing the division; adjust references in
this change to the variables priceChange, historicalSharePrice, daysForCalc, and
apyBase so callers receive a safe numeric value.

- Corrected VAULT_DEPLOY_BLOCK from 21763550 to 24181000 (Jan 7, 2026)
- Use ethers.js directly instead of SDK for more reliable ABI parsing
- Calculate APY using 7-day window (falls back to 1-day for new vaults)
@llamatester
Copy link

Error while running lazyusd adapter:

Test Suites: 1 failed, 1 total
Tests: 1 failed, 9 passed, 10 total
Snapshots: 0 total
Time: 0.27 s
Ran all test suites.

Nb of pools: 1
 

Sample pools:
┌─────────┬───────────────────────────────────────────────────────┬────────────┬───────────┬────────┬───────────────┬────────────────────┬──────────────────────────────────────────────────┬───────────────────────┐
│ (index) │ pool                                                  │ chain      │ project   │ symbol │ tvlUsd        │ apyBase            │ underlyingTokens                                 │ url                   │
├─────────┼───────────────────────────────────────────────────────┼────────────┼───────────┼────────┼───────────────┼────────────────────┼──────────────────────────────────────────────────┼───────────────────────┤
│ 0       │ '0xd53b68fb4eb907c3c1e348cd7d7bede34f763805-ethereum' │ 'Ethereum' │ 'lazyusd' │ 'USDC' │ 500661.494622 │ 19.552464046207987 │ [ '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' ] │ 'https://getlazy.xyz' │
└─────────┴───────────────────────────────────────────────────────┴────────────┴───────────┴────────┴───────────────┴────────────────────┴──────────────────────────────────────────────────┴───────────────────────┘
}

@sirmoremoney sirmoremoney deleted the add-lazyusd branch January 14, 2026 01:28
@sirmoremoney sirmoremoney restored the add-lazyusd branch January 14, 2026 01:28
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