-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add LazyUSD yield adapter #2267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
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.
- 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
- 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)
📝 WalkthroughWalkthroughA new adaptor module for LazyUSD is introduced that fetches on-chain vault data from Ethereum, calculates annual percentage yield (APY) using share price mechanics with historical fallback logic, and returns structured pool information including TVL, APY, and underlying tokens. Changes
Sequence DiagramsequenceDiagram
participant Caller
participant Adaptor as LazyUSD Adaptor
participant Provider as JSON-RPC Provider
participant Vault as Vault Contract
Caller->>Adaptor: apy()
Adaptor->>Provider: Connect to Ethereum
Adaptor->>Vault: Read sharePrice & totalAssets
Vault-->>Adaptor: Contract data
Adaptor->>Adaptor: Calculate APY (with historical fallback)
Adaptor->>Adaptor: Format pool metadata
Adaptor-->>Caller: Return pool array [pool, tvlUsd, apyBase, ...]
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
|
Error while running lazyusd adapter: Test Suites: 1 failed, 1 total |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@src/adaptors/lazyusd/index.js`:
- Around line 33-35: The calculation for apyBase uses historicalSharePrice (from
await vault.sharePrice({ blockTag: historicalBlock })) without guarding against
zero, which can yield Infinity/NaN; before computing priceChange and apyBase,
check that historicalSharePrice is a positive non-zero number and if it's zero
or falsy either set apyBase to 0 (or skip the APY calculation) and optionally
log/mark the vault as uninitialized — update the block that assigns
historicalSharePrice, computes priceChange, and sets apyBase to perform this
validation and handle the zero-case safely.
- Around line 4-6: The VAULT_DEPLOY_BLOCK constant is incorrect (24181000 is
non-existent); locate the actual deployment block for the VAULT address
(0xd53B68fB4eb907c3c1E348CD7d7bEDE34f763805) by checking the contract creation
transaction on Etherscan or by querying the creation tx receipt via web3/ethers
to get its blockNumber, then update VAULT_DEPLOY_BLOCK to that integer value in
the file (ensure you modify the VAULT_DEPLOY_BLOCK constant alongside the
existing VAULT and USDC constants).
🧹 Nitpick comments (2)
src/adaptors/lazyusd/index.js (2)
1-1: Unused import:sdkis never used.The
@defillama/sdkimport is declared but not referenced anywhere in this file. Remove it to avoid confusion.Suggested fix
-const sdk = require('@defillama/sdk'); const { ethers } = require('ethers');
18-18:blocksPerDayis an approximation.The value 7200 assumes exactly 12-second block times. Post-merge Ethereum averages ~12.05s, which introduces minor drift over 7 days (~0.4%). This is acceptable for APY estimates but worth noting.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/adaptors/lazyusd/index.js
🔇 Additional comments (3)
src/adaptors/lazyusd/index.js (3)
8-11: LGTM!The human-readable ABI format is appropriate for ethers.js, and the function signatures match standard vault interfaces.
27-36: Edge case: vault less than 1 day old still returnsapyBase = 0.If the vault is less than 1 day old, the fallback on line 28 sets
historicalBlock = latestBlock - blocksPerDay, which may still be less thanVAULT_DEPLOY_BLOCK. The condition on line 32 will then be false, leavingapyBase = 0. This is likely acceptable behavior for new vaults, but worth confirming this is intentional.
50-54: LGTM!The export structure follows the standard DefiLlama yield adapter pattern with
timetravel,apy, andurlproperties.
| const VAULT = '0xd53B68fB4eb907c3c1E348CD7d7bEDE34f763805'; | ||
| const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; | ||
| const VAULT_DEPLOY_BLOCK = 24181000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if vault contract exists
echo "=== Checking vault contract ==="
curl -s -X POST https://eth.llamarpc.com \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_getCode","params":["0xd53B68fB4eb907c3c1E348CD7d7bEDE34f763805","latest"],"id":1}' | jq '.'
echo ""
echo "=== Checking block 24181000 ==="
curl -s -X POST https://eth.llamarpc.com \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x170f658","false"],"id":1}' | jq '.result | {number, timestamp, hash}'Repository: DefiLlama/yield-server
Length of output: 31926
Correct the VAULT_DEPLOY_BLOCK value.
The VAULT contract at 0xd53B68fB4eb907c3c1E348CD7d7bEDE34f763805 exists on-chain, and the USDC address is correct for Ethereum mainnet. However, block 24181000 does not exist. Verify and update VAULT_DEPLOY_BLOCK to the actual deployment block of the LazyUSD vault contract.
🤖 Prompt for AI Agents
In `@src/adaptors/lazyusd/index.js` around lines 4 - 6, The VAULT_DEPLOY_BLOCK
constant is incorrect (24181000 is non-existent); locate the actual deployment
block for the VAULT address (0xd53B68fB4eb907c3c1E348CD7d7bEDE34f763805) by
checking the contract creation transaction on Etherscan or by querying the
creation tx receipt via web3/ethers to get its blockNumber, then update
VAULT_DEPLOY_BLOCK to that integer value in the file (ensure you modify the
VAULT_DEPLOY_BLOCK constant alongside the existing VAULT and USDC constants).
| const apy = async () => { | ||
| const provider = new ethers.providers.JsonRpcProvider('https://eth.llamarpc.com'); | ||
| const vault = new ethers.Contract(VAULT, ABI, provider); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for RPC and contract calls.
The function has no try-catch or error handling. If the RPC endpoint is unavailable or the contract calls revert, the adapter 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);
+ // ... rest of function
+ } catch (error) {
+ console.error('LazyUSD adapter error:', error.message);
+ return [];
+ }
};| const historicalSharePrice = (await vault.sharePrice({ blockTag: historicalBlock })) / 1e6; | ||
| const priceChange = (currentSharePrice - historicalSharePrice) / historicalSharePrice; | ||
| apyBase = (priceChange / daysForCalc) * 365 * 100; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential division by zero if historicalSharePrice is zero.
If the vault's share price was zero at the historical block (e.g., before initialization), line 34 will produce Infinity or NaN, corrupting the APY result.
Suggested fix
if (historicalBlock >= VAULT_DEPLOY_BLOCK) {
const historicalSharePrice = (await vault.sharePrice({ blockTag: historicalBlock })) / 1e6;
+ if (historicalSharePrice > 0) {
const priceChange = (currentSharePrice - historicalSharePrice) / historicalSharePrice;
apyBase = (priceChange / daysForCalc) * 365 * 100;
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const historicalSharePrice = (await vault.sharePrice({ blockTag: historicalBlock })) / 1e6; | |
| const priceChange = (currentSharePrice - historicalSharePrice) / historicalSharePrice; | |
| apyBase = (priceChange / daysForCalc) * 365 * 100; | |
| if (historicalBlock >= VAULT_DEPLOY_BLOCK) { | |
| const historicalSharePrice = (await vault.sharePrice({ blockTag: historicalBlock })) / 1e6; | |
| if (historicalSharePrice > 0) { | |
| const priceChange = (currentSharePrice - historicalSharePrice) / historicalSharePrice; | |
| apyBase = (priceChange / daysForCalc) * 365 * 100; | |
| } | |
| } |
🤖 Prompt for AI Agents
In `@src/adaptors/lazyusd/index.js` around lines 33 - 35, The calculation for
apyBase uses historicalSharePrice (from await vault.sharePrice({ blockTag:
historicalBlock })) without guarding against zero, which can yield Infinity/NaN;
before computing priceChange and apyBase, check that historicalSharePrice is a
positive non-zero number and if it's zero or falsy either set apyBase to 0 (or
skip the APY calculation) and optionally log/mark the vault as uninitialized —
update the block that assigns historicalSharePrice, computes priceChange, and
sets apyBase to perform this validation and handle the zero-case safely.
Summary
Test output
Test plan
npm run test --adapter=lazyusd- all relevant tests passSummary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.