-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Updated Yield Averaging to correctly match the yield distributed #6292
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?
Changes from all commits
668625b
3dbc93d
d774a82
408452e
498c1ee
f1b6203
d210ad5
59a408e
0dc380e
cc512dc
d50b569
51e766d
4964698
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -24,10 +24,43 @@ const CONTRACTS = { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| V2_MANAGER: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| [CHAIN.ETHEREUM]: "0x03ACc35286bAAE6D73d99a9f14Ef13752208C8dC", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| FROM_BLOCK: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| [CHAIN.ETHEREUM]: 24241384, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| [CHAIN.BSC]: 76733207, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const YIELD_SEGMENTS = 10 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const PROTOCOL_START_TS: Record<string, number> = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| [CHAIN.ETHEREUM]: Math.floor(new Date("2024-11-11T00:00:00Z").getTime() / 1000), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| [CHAIN.BSC]: Math.floor(new Date("2025-07-27T00:00:00Z").getTime() / 1000), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function fetchVaultDeployedLogs(options: FetchOptions, vaultDeployer: string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const startTs = PROTOCOL_START_TS[options.chain] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (startTs === undefined) return [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const deployFromBlock = await options.getBlock(startTs, options.chain, {}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const toBlock = await options.getToBlock() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (deployFromBlock > toBlock) return [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const span = toBlock - deployFromBlock | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const seen = new Set<string>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const merged: any[] = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (let i = 0; i < YIELD_SEGMENTS; i++) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const chunkFrom = deployFromBlock + Math.floor((i * span) / YIELD_SEGMENTS) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const chunkTo = i === YIELD_SEGMENTS - 1 ? toBlock : deployFromBlock + Math.floor(((i + 1) * span) / YIELD_SEGMENTS) - 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (chunkFrom > chunkTo) continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const chunkLogs = await options.getLogs({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| target: vaultDeployer, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| eventAbi: ABI.DEPLOY_VAULT, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| cacheInCloud: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fromBlock: chunkFrom, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| toBlock: chunkTo, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const log of chunkLogs) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const key = String(log.vault).toLowerCase() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (seen.has(key)) continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| seen.add(key) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| merged.push(log) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return merged | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function fetch(options: FetchOptions): Promise<FetchResultV2> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -38,7 +71,6 @@ async function fetch(options: FetchOptions): Promise<FetchResultV2> { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| const manager = CONTRACTS.V2_MANAGER[options.chain]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const navManager = CONTRACTS.NAV_MANAGER[options.chain]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const vaultDeployer = CONTRACTS.VAULT_DEPLOYER[options.chain]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const fromBlock = CONTRACTS.FROM_BLOCK[options.chain]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (yieldProxy) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const yieldDistributedLogs = await options.getLogs({ target: yieldProxy, eventAbi: ABI.YIELD_PROXY }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -57,29 +89,47 @@ async function fetch(options: FetchOptions): Promise<FetchResultV2> { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| target: navManager, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| eventAbi: ABI.NAV, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const vaultDeployedLogs = await options.getLogs({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| target: vaultDeployer, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| eventAbi: ABI.DEPLOY_VAULT, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| cacheInCloud: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fromBlock, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const vaultDeployedLogs = await fetchVaultDeployedLogs(options, vaultDeployer); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. vauluDeployer was deployed 75 days ago, what do you intend to do by using fromBlock which is a year and half old |
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| navManagerLogs.forEach((log: any) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| dailyFees.add(log.vault, log.managementFee + log.performanceFee); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| dailyRevenue.add(log.vault, log.managementFee + log.performanceFee); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const vaults = vaultDeployedLogs.map((log: any) => log.vault); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const totalSupplies = await options.api.multiCall({ abi: 'uint256:totalSupply', calls: vaults, permitFailure: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const assets = await options.api.multiCall({ abi: 'address:asset', calls: vaults, permitFailure: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const decimals = await options.api.multiCall({ abi: 'uint8:decimals', calls: vaults, permitFailure: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const assetDecimals = await options.api.multiCall({ abi: 'uint8:decimals', calls: assets, permitFailure: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const vaultRateBefore = await options.fromApi.multiCall({ abi: 'uint256:getRate', calls: vaults, permitFailure: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const vaultRateAfter = await options.toApi.multiCall({ abi: 'uint256:getRate', calls: vaults, permitFailure: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const fromFeeBlock = await options.getFromBlock(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const toFeeBlock = await options.getToBlock(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const feeSpan = toFeeBlock - fromFeeBlock; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const boundaryBlocks: number[] = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (let j = 0; j <= YIELD_SEGMENTS; j++) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pull hourly fetches blocks every hour, so i dont think you need to split again to calculate every 6 minutes |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| boundaryBlocks.push(j === YIELD_SEGMENTS ? toFeeBlock : fromFeeBlock + Math.floor((j * feeSpan) / YIELD_SEGMENTS)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const suppliesAt: any[][] = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ratesAt: any[][] = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const block of boundaryBlocks) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| suppliesAt.push(await options.api.multiCall({ abi: 'uint256:totalSupply', calls: vaults, permitFailure: true, block })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ratesAt.push(await options.api.multiCall({ abi: 'uint256:getRate', calls: vaults, permitFailure: true, block })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (let i = 0; i < vaults.length; i++) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (assets[i] && totalSupplies[i] && decimals[i] && assetDecimals[i] && vaultRateAfter[i] && vaultRateBefore[i]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| dailyFees.add(assets[i], (totalSupplies[i] / 10 ** decimals[i]) * (vaultRateAfter[i] - vaultRateBefore[i]) / 10 ** (decimals[i] - assetDecimals[i])); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!assets[i] || decimals[i] == null || assetDecimals[i] == null) continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| let acc = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (let k = 0; k < YIELD_SEGMENTS; k++) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const s0 = suppliesAt[k][i]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const s1 = suppliesAt[k + 1][i]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const r0 = ratesAt[k][i]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const r1 = ratesAt[k + 1][i]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (s0 == null || s1 == null || r0 == null || r1 == null) continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const avgSupply = (Number(s0) + Number(s1)) / 2; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const rateDelta = Number(r1) - Number(r0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| acc += (avgSupply / 10 ** decimals[i]) * rateDelta / 10 ** (decimals[i] - assetDecimals[i]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (acc !== 0) dailyFees.add(assets[i], acc); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+121
to
+132
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Python float uses the same IEEE-754 double-precision model as JS Number.
python - <<'PY'
max_safe = 2**53 - 1
print("max safe integer:", max_safe)
for raw in (10**18, 10**18 + 1, 10**23, 10**23 + 1):
print(f"{raw}: float(raw) == float(raw + 1) -> {float(raw) == float(raw + 1)}")
PYRepository: DefiLlama/dimension-adapters Length of output: 349 🏁 Script executed: # Find the yieldfi index file and examine the code around lines 121-132
find . -path "*fees/yieldfi/index.ts" -type fRepository: DefiLlama/dimension-adapters Length of output: 95 🏁 Script executed: # Check the code at the specified lines
if [ -f "fees/yieldfi/index.ts" ]; then
wc -l fees/yieldfi/index.ts
echo "=== Lines 115-140 ==="
sed -n '115,140p' fees/yieldfi/index.ts
fiRepository: DefiLlama/dimension-adapters Length of output: 1405 🏁 Script executed: # Also check what dailyFees is and what its add method expects
rg -A 5 "dailyFees\s*=" fees/yieldfi/index.ts | head -20Repository: DefiLlama/dimension-adapters Length of output: 350 🏁 Script executed: # Check the file structure and imports to understand data types
head -50 fees/yieldfi/index.tsRepository: DefiLlama/dimension-adapters Length of output: 2564 🏁 Script executed: # Find the Balances type definition
rg -r "class Balances" --type ts | head -5Repository: DefiLlama/dimension-adapters Length of output: 468 🏁 Script executed: # Also check the adapters/types file
if [ -f "adapters/types.ts" ]; then
rg "class Balances|interface.*Balances|add\(" adapters/types.ts | head -20
fiRepository: DefiLlama/dimension-adapters Length of output: 54 🏁 Script executed: # Search more broadly for Balances definition
find . -name "*.ts" -type f -exec grep -l "class Balances" {} \; 2>/dev/null | head -5Repository: DefiLlama/dimension-adapters Length of output: 54 🏁 Script executed: # Check helper files for Balances
ls -la helpers/Repository: DefiLlama/dimension-adapters Length of output: 3931 🏁 Script executed: # Search for Balances class in adapters directory
find . -name "*.ts" -type f | xargs grep -l "class Balances" 2>/dev/nullRepository: DefiLlama/dimension-adapters Length of output: 56 🏁 Script executed: # Look in the main adapters directory
find adapters -name "*.ts" -type f | xargs grep -l "class Balances" 2>/dev/nullRepository: DefiLlama/dimension-adapters Length of output: 56 🏁 Script executed: # Try a different approach - search for the add method definition
rg "add\s*\(" adapters/types.tsRepository: DefiLlama/dimension-adapters Length of output: 54 🏁 Script executed: # Check adapters/types.ts directly
if [ -f "adapters/types.ts" ]; then
wc -l adapters/types.ts
fiRepository: DefiLlama/dimension-adapters Length of output: 93 🏁 Script executed: # Read adapters/types.ts to find Balances
head -200 adapters/types.ts | tail -100Repository: DefiLlama/dimension-adapters Length of output: 3325 🏁 Script executed: # Check the beginning of adapters/types.ts for imports
head -50 adapters/types.tsRepository: DefiLlama/dimension-adapters Length of output: 1666 🏁 Script executed: # Search for where Balances is imported from in adapters/types.ts
grep "import.*Balances\|from.*Balances" adapters/types.tsRepository: DefiLlama/dimension-adapters Length of output: 130 🏁 Script executed: # Try searching in the entire codebase for Balances class definition
find . -type f -name "*.ts" | head -50 | xargs grep -l "export.*class Balances" 2>/dev/nullRepository: DefiLlama/dimension-adapters Length of output: 56 🏁 Script executed: # Check if it's in a sdk or external package - search package.json
cat package.json | grep -A 50 '"dependencies"'Repository: DefiLlama/dimension-adapters Length of output: 772 🏁 Script executed: # Search for usages of .add() on Balances-like objects in the codebase
rg "\.add\(" fees/ | head -30Repository: DefiLlama/dimension-adapters Length of output: 2716 🏁 Script executed: # Look specifically at how dailyFees.add is used in yieldfi
grep -A 2 -B 2 "dailyFees.add" fees/yieldfi/index.tsRepository: DefiLlama/dimension-adapters Length of output: 866 🏁 Script executed: # Check if there are any other files that use .add() with stringified values
rg "\.add\([^,]+,\s*['\"].*\.toString\(\)" fees/ | head -10Repository: DefiLlama/dimension-adapters Length of output: 54 🏁 Script executed: # Look at other fee adapters to understand the pattern
rg "dailyFees\.add\(" fees/ | head -20Repository: DefiLlama/dimension-adapters Length of output: 1739 🏁 Script executed: # Check if yieldfi adapter has allowNegativeValue
grep -n "allowNegativeValue" fees/yieldfi/index.tsRepository: DefiLlama/dimension-adapters Length of output: 123 🏁 Script executed: # Check what types suppliesAt and ratesAt actually contain
grep -B 5 -A 5 "suppliesAt\|ratesAt" fees/yieldfi/index.ts | head -40Repository: DefiLlama/dimension-adapters Length of output: 1432 🏁 Script executed: # Look for the export/return statement to see the adapter config
tail -20 fees/yieldfi/index.tsRepository: DefiLlama/dimension-adapters Length of output: 412 🏁 Script executed: # Check if Balances.add accepts strings or numbers - search for documentation or method signature
rg "interface.*Balances|\.add.*:" adapters/ --type ts | head -20Repository: DefiLlama/dimension-adapters Length of output: 445 🏁 Script executed: # Look at similar fee calculations to understand the pattern better
grep -B 5 -A 5 "avgSupply\|rateDelta" fees/ --include="*.ts" | head -50Repository: DefiLlama/dimension-adapters Length of output: 99 🏁 Script executed: # Check what multiCall returns by looking at examples
grep -B 2 -A 5 "\.multiCall" fees/yieldfi/index.ts | head -30Repository: DefiLlama/dimension-adapters Length of output: 1227 🏁 Script executed: # Look at the external SDK - check package and understand multiCall return type
rg "multiCall.*uint256" fees/ --type ts | head -5Repository: DefiLlama/dimension-adapters Length of output: 891 🏁 Script executed: # Check actual usage in other files to see what type is returned
rg "multiCall.*uint256" . --type ts -B 2 -A 2 | head -50Repository: DefiLlama/dimension-adapters Length of output: 5018 🏁 Script executed: # Search for how other adapters handle bigint/number conversions for fees
rg "BigInt.*add|acc.*=.*0n" fees/ --type ts | head -20Repository: DefiLlama/dimension-adapters Length of output: 644 🏁 Script executed: # Look at how other adapters with large decimals handle precision
grep -r "toFixed\|toString\|BigInt" fees/ --include="*.ts" | grep -i "fees\|revenue" | head -20Repository: DefiLlama/dimension-adapters Length of output: 1615 🏁 Script executed: # Let's understand what Balances.add() accepts by checking if there are any TypeScript definitions
rg "\.add\(" --type ts -A 2 | grep -E "string|number|bigint" | head -20Repository: DefiLlama/dimension-adapters Length of output: 747 🏁 Script executed: # Check if the proposed fix's BigInt math is mathematically sound
python3 <<'EOF'
# Test the proposed fix logic for accuracy
# Original: (avgSupply / 10^decimals) * rateDelta / 10^(decimals - assetDecimals)
# Proposed: (supplySum * rateDelta) / (2 * 10^exponent) where exponent = 2*decimals - assetDecimals
# Example: decimals = 18, assetDecimals = 6
decimals = 18
assetDecimals = 6
exponent = 2 * decimals - assetDecimals # 30
# Test with sample values
s0 = 10**18
s1 = 10**18
r0 = 10**9
r1 = 10**9 + 1
# Original calculation (in float)
avgSupply = (s0 + s1) / 2
rateDelta = r1 - r0
original_result = (avgSupply / 10 ** decimals) * rateDelta / 10 ** (decimals - assetDecimals)
# Proposed calculation (in bigint)
supplySum = s0 + s1
rateDelta_bi = r1 - r0
exponent = 2 * decimals - assetDecimals
if exponent >= 0:
segmentAcc = (supplySum * rateDelta_bi) // (2 * 10 ** exponent)
else:
segmentAcc = (supplySum * rateDelta_bi * 10 ** (-exponent)) // 2
print(f"Original (float): {original_result}")
print(f"Proposed (bigint, stringified): {str(segmentAcc)}")
print(f"Match: {original_result == float(segmentAcc)}")
print(f"\nPrecision difference: {abs(original_result - float(segmentAcc))}")
EOFRepository: DefiLlama/dimension-adapters Length of output: 171 🏁 Script executed: # Check what Balances is and what methods it accepts
# Search for documentation or examples that pass strings to add()
rg "dailyFees\.add.*toString\|add.*\.toString" fees/ --type tsRepository: DefiLlama/dimension-adapters Length of output: 54 🏁 Script executed: # Look more carefully at Balances usage - does it accept bigint or strings?
rg "\.add\(" fees/ --type ts -B 2 -A 1 | grep -E "\.add\(.*0n|\.add\(.*BigInt" | head -20Repository: DefiLlama/dimension-adapters Length of output: 2009 🏁 Script executed: # Check if there are examples of passing bigint values to .add()
rg "\.add\([^,]+,\s*(0n|BigInt|[0-9]+n)" . --type ts | head -15Repository: DefiLlama/dimension-adapters Length of output: 1920 🏁 Script executed: # Look at what types multiCall returns - check SDK usage more carefully
rg "multiCall.*uint256" fees/yieldfi/ --type ts -A 5 -B 2Repository: DefiLlama/dimension-adapters Length of output: 786 🏁 Script executed: # Test the precision issue more comprehensively for various decimal combinations
python3 <<'EOF'
# Test precision for different decimal combinations that would occur in practice
test_cases = [
# (decimals, assetDecimals, s0, s1, r0, r1, description)
(18, 6, 10**18, 10**18 + 1000, 10**9, 10**9 + 1, "18-decimal vault"),
(8, 6, 10**8, 10**8 + 100, 10**5, 10**5 + 1, "8-decimal vault"),
(6, 6, 10**6, 10**6 + 10, 10**3, 10**3 + 1, "6-decimal vault"),
]
for decimals, assetDecimals, s0, s1, r0, r1, desc in test_cases:
# Original (float)
avgSupply = (s0 + s1) / 2
rateDelta = r1 - r0
original = (avgSupply / 10 ** decimals) * rateDelta / 10 ** (decimals - assetDecimals)
# Proposed (bigint with integer division)
supplySum = s0 + s1
rateDelta_bi = r1 - r0
exponent = 2 * decimals - assetDecimals
if exponent >= 0:
proposed = (supplySum * rateDelta_bi) // (2 * (10 ** exponent))
else:
proposed = ((supplySum * rateDelta_bi) * (10 ** (-exponent))) // 2
print(f"\n{desc}:")
print(f" Original (float): {original}")
print(f" Proposed (bigint): {proposed}")
print(f" Precision loss: {original - proposed}")
print(f" Match: {original == proposed}")
EOFRepository: DefiLlama/dimension-adapters Length of output: 473 🏁 Script executed: # Check if there's a type definition for Balances.add() signature in the SDK
# or if we can infer from documentation
rg "add.*\(.*\)" . --type ts -B 2 -A 2 | grep -E "add\s*\([^)]*:\s*(number|string|bigint)" | head -10Repository: DefiLlama/dimension-adapters Length of output: 54 🏁 Script executed: # Check the fragment/index.ts mentioned in learnings to see if it uses a similar pattern
if [ -f "fees/fragment/index.ts" ]; then
sed -n '95,110p' fees/fragment/index.ts
fiRepository: DefiLlama/dimension-adapters Length of output: 1176 🏁 Script executed: # Verify whether stringification is actually needed - test if bigint is passed directly elsewhere
rg "\.add\([^,]+,\s*[a-zA-Z_][a-zA-Z0-9_]*n\s*\)" . --type ts | head -10Repository: DefiLlama/dimension-adapters Length of output: 426 🏁 Script executed: # Look at the exact math one more time - check if there's a better approach
# Let's see if the calculation can be kept in bigint without stringification
python3 <<'EOF'
# Test if we can keep bigint arithmetic without stringification
# and what the actual impact is
# Case: 18-decimal vault with assetDecimals = 6
decimals = 18
assetDecimals = 6
s0_raw = 10**18
s1_raw = 10**18
r0_raw = 10**9
r1_raw = 10**9 + 1
# Original code path (problematic precision)
avgSupply_float = (float(s0_raw) + float(s1_raw)) / 2
rateDelta_float = float(r1_raw) - float(r0_raw)
original = (avgSupply_float / 10 ** decimals) * rateDelta_float / 10 ** (decimals - assetDecimals)
print(f"Original calculation (float): {original}")
print(f"Original (scientific): {original:.20e}")
# Better approach: keep in bigint but use different division strategy
# Instead of: (sum * delta) / (2 * 10^exponent)
# We could do: (sum * delta) / (2) / 10^exponent
# But this still loses precision with integer division
# Or even better: accumulate in base units and scale at the end
supplySum = s0_raw + s1_raw
rateDelta = r1_raw - r0_raw
# This is raw: (supply * rate_change) - but we need to divide by 2 and apply decimals
# If we keep as bigint: supplySum * rateDelta = 2e18 * 1 = 2e18
result_raw = (supplySum * rateDelta) // 2 # = 1e18
# Now we need to scale: divide by 10^decimals and by 10^(decimals - assetDecimals)
# Total scale: 10^(decimals + decimals - assetDecimals) = 10^(2*18 - 6) = 10^30
final_scaled = result_raw / (10 ** 30)
print(f"\nBetter bigint approach: {final_scaled}")
print(f"Better (scientific): {final_scaled:.20e}")
# The real question: is this precision loss acceptable?
# For yield farming, typically dealing with raw amounts anyway
EOFRepository: DefiLlama/dimension-adapters Length of output: 236 🏁 Script executed: # Final check: verify the adapter's allowNegativeValue setting one more time
grep -B 5 -A 5 "allowNegativeValue" fees/yieldfi/index.tsRepository: DefiLlama/dimension-adapters Length of output: 328 🏁 Script executed: # Check if there are any tests or comments about expected precision
grep -r "precision\|BigInt\|Number.*conversion" fees/yieldfi/ --include="*.ts"Repository: DefiLlama/dimension-adapters Length of output: 54 Keep raw token math in Lines 128–130 convert raw supply/rate values to Possible fix- let acc = 0;
+ let acc = 0n;
for (let k = 0; k < YIELD_SEGMENTS; k++) {
const s0 = suppliesAt[k][i];
const s1 = suppliesAt[k + 1][i];
const r0 = ratesAt[k][i];
const r1 = ratesAt[k + 1][i];
if (s0 == null || s1 == null || r0 == null || r1 == null) continue;
- const avgSupply = (Number(s0) + Number(s1)) / 2;
- const rateDelta = Number(r1) - Number(r0);
- acc += (avgSupply / 10 ** decimals[i]) * rateDelta / 10 ** (decimals[i] - assetDecimals[i]);
+ const supplySum = BigInt(s0) + BigInt(s1);
+ const rateDelta = BigInt(r1) - BigInt(r0);
+ const exponent = 2n * BigInt(decimals[i]) - BigInt(assetDecimals[i]);
+ acc += (supplySum * rateDelta) / (2n * 10n ** exponent);
}
- if (acc !== 0) dailyFees.add(assets[i], acc);
+ if (acc !== 0n) dailyFees.add(assets[i], acc);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const dailySupplySideRevenue = dailyFees.clone(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
Handle failed block resolution and cap the discovery scan.
helpers/getBlock.tscan returnnullon Line 39. If that happens, the range math below degenerates into a genesis-sized scan, and even the non-null path still splits the full protocol lifetime into only 10getLogswindows. CI is already failing on BSC with pruned-history / limit errors, so this needs anullguard plus block-span-based chunking.Possible fix
const startTs = PROTOCOL_START_TS[options.chain] if (startTs === undefined) return [] const deployFromBlock = await options.getBlock(startTs, options.chain, {}) const toBlock = await options.getToBlock() - if (deployFromBlock > toBlock) return [] - const span = toBlock - deployFromBlock + if (deployFromBlock == null || deployFromBlock > toBlock) return [] + const maxBlockSpan = 50_000 const seen = new Set<string>() const merged: any[] = [] - for (let i = 0; i < YIELD_SEGMENTS; i++) { - const chunkFrom = deployFromBlock + Math.floor((i * span) / YIELD_SEGMENTS) - const chunkTo = i === YIELD_SEGMENTS - 1 ? toBlock : deployFromBlock + Math.floor(((i + 1) * span) / YIELD_SEGMENTS) - 1 + for (let chunkFrom = deployFromBlock; chunkFrom <= toBlock; chunkFrom += maxBlockSpan) { + const chunkTo = Math.min(chunkFrom + maxBlockSpan - 1, toBlock) if (chunkFrom > chunkTo) continue const chunkLogs = await options.getLogs({🤖 Prompt for AI Agents