Skip to content

Updated Yield Averaging to correctly match the yield distributed#6292

Open
neelra8n wants to merge 13 commits intoDefiLlama:masterfrom
neelra8n:master
Open

Updated Yield Averaging to correctly match the yield distributed#6292
neelra8n wants to merge 13 commits intoDefiLlama:masterfrom
neelra8n:master

Conversation

@neelra8n
Copy link
Copy Markdown
Contributor

@neelra8n neelra8n commented Mar 30, 2026

Summary by CodeRabbit

Release Notes

Refactor

  • Refactored vault discovery mechanism to enhance reliability and ensure more comprehensive identification of available vaults across the entire protocol
  • Optimized fee calculation system through implementation of enhanced multi-block historical sampling techniques, providing more accurate fee assessments and improving overall calculation precision and reliability

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

📝 Walkthrough

Walkthrough

The fee calculation mechanism for YieldFi vaults has been redesigned to use multi-block sampling instead of single-point snapshots. Vault discovery now operates through segmented block ranges with deduplication, and fee accumulation computes values across sub-intervals using averaged supply and rate deltas.

Changes

Cohort / File(s) Summary
YieldFi Fee Calculation Refactor
fees/yieldfi/index.ts
Replaced single-point vault discovery and rate/supply snapshots with segmented block-range queries. Introduced fetchVaultDeployedLogs() using protocol start timestamps and chunked YIELD_SEGMENTS. Updated fee accumulation to sample totalSupply and getRate at sub-interval boundaries, computing fees via averaged supply (s0+s1)/2 and rate deltas (r1-r0) per vault per segment, with null-safe skipping and zero-value filtering.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Worker as Fee Processor
    participant Discover as Vault Discovery
    participant Chain as Blockchain
    participant Sample as Multi-Block Sampler
    participant Fee as Fee Accumulator
    
    Worker->>Discover: fetchVaultDeployedLogs(options, vaultDeployer)
    Discover->>Discover: Compute scan window from PROTOCOL_START_TS
    Discover->>Chain: Query ABI.DEPLOY_VAULT logs in YIELD_SEGMENTS chunks
    Chain-->>Discover: Return deployment logs
    Discover->>Discover: Deduplicate vaults by address
    Discover-->>Worker: Merged, deduplicated vault list
    
    Worker->>Sample: Split [getFromBlock, getToBlock] into YIELD_SEGMENTS
    Sample->>Sample: Build boundaryBlocks array
    loop For each boundary
        Sample->>Chain: multiCall(totalSupply, getRate, block)
        Chain-->>Sample: Values at boundary
        Sample->>Sample: Collect snapshot
    end
    Sample-->>Worker: Block-boundary snapshots
    
    Worker->>Fee: Accumulate fees per vault/segment
    loop For each vault and sub-interval
        Fee->>Fee: avgSupply = (s0+s1)/2
        Fee->>Fee: rateDelta = r1-r0
        Fee->>Fee: Compute fee with decimals scaling
        Fee->>Fee: Skip if null, add if non-zero
    end
    Fee-->>Worker: Final accumulated fees
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 Hops with glee

Block by block, we now sample true,
Segmented queries through and through!
Vaults discovered, deduplicated fair,
Averaged rates float through the air,
Multi-point fees—no single guess,
YieldFi calculations, now at their best! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is empty, with no explanation of the changes, rationale, or context provided despite the template requiring detailed information for protocol listing or updates. Add a detailed description explaining the yield averaging fix, why it was needed, what changed, and how it affects the protocol's fee calculations.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: updating yield averaging logic to correctly match distributed yields, which aligns with the multi-block sampling and fee accumulation adjustments in the changeset.

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

@llamabutler
Copy link
Copy Markdown

The yieldfi adapter exports:

> adapters@1.0.0 test
> ts-node --transpile-only cli/testAdapter.ts fees yieldfi

🦙 Running YIELDFI adapter 🦙
---------------------------------------------------
Start Date:	Sun, 29 Mar 2026 03:00:00 GMT
End Date:	Mon, 30 Mar 2026 03:00:00 GMT
---------------------------------------------------

------ ERROR ------
undefined
{
  llamaRPCError: true,
  errors: [
    {
      host: 'https://bsc-dataseed.binance.org',
      error: 'limit exceeded'
    },
    { host: 'https://rpc-bsc.48.club', error: 'header not found' },
    {
      host: 'https://bsc-dataseed3.ninicoin.io',
      error: 'limit exceeded'
    },
    { host: 'https://bsc.rpc.blxrbdn.com', error: 'header not found' },
    {
      host: 'https://bsc-dataseed.bnbchain.org',
      error: 'limit exceeded'
    },
    {
      host: 'https://bsc-rpc.publicnode.com',
      error: 'History has been pruned for this block. To remove restrictions, order a dedicated full node here: https://www.allnodes.com/bnb/host'
    },
    {
      host: 'https://bsc-dataseed3.bnbchain.org',
      error: 'limit exceeded'
    }
  ],
  method: 'getLogs',
  chain: 'bsc'
}

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@fees/yieldfi/index.ts`:
- Around line 39-55: The code assumes deployFromBlock (from options.getBlock) is
non-null and then computes a huge span; add a null guard and a scan-cap before
chunking: if deployFromBlock is null (or options.getToBlock() is null) return []
or compute a safe fallback window instead of using genesis (do not let
deployFromBlock be treated as 0), and introduce a MAX_SCAN_BLOCKS cap so span =
Math.min(toBlock - deployFromBlock, MAX_SCAN_BLOCKS) before calculating
chunkFrom/chunkTo; keep using YIELD_SEGMENTS to split that capped span and pass
those chunkFrom/chunkTo into options.getLogs (symbols: deployFromBlock,
options.getBlock, options.getToBlock, YIELD_SEGMENTS, chunkFrom, chunkTo,
options.getLogs).
- Around line 121-132: The loop currently converts supplies/rates to Number and
uses floating math (loss of precision for 1e18); change the accumulation to use
bigint math: convert s0,s1,r0,r1 from suppliesAt and ratesAt into BigInt,
compute avgSupply = (s0 + s1) / 2n and rateDelta = r1 - r0 as BigInt, then
perform all scaling with BigInt power expressions (e.g., 10n **
BigInt(decimals[i]) and 10n ** BigInt(decimals[i] - assetDecimals[i])) to
compute acc as a BigInt; only convert to the expected type when calling
dailyFees.add (or adjust dailyFees.add to accept BigInt) and use acc !== 0n for
the nonzero check. Ensure you reference YIELD_SEGMENTS, suppliesAt, ratesAt,
decimals, assetDecimals, acc, and dailyFees.add when applying the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6179c8b4-a1f4-427a-99df-8872a98c29a1

📥 Commits

Reviewing files that changed from the base of the PR and between defb1f4 and 4964698.

📒 Files selected for processing (1)
  • fees/yieldfi/index.ts

Comment on lines +39 to +55
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,
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Handle failed block resolution and cap the discovery scan.

helpers/getBlock.ts can return null on 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 10 getLogs windows. CI is already failing on BSC with pruned-history / limit errors, so this needs a null guard 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
Verify each finding against the current code and only fix it if needed.

In `@fees/yieldfi/index.ts` around lines 39 - 55, The code assumes deployFromBlock
(from options.getBlock) is non-null and then computes a huge span; add a null
guard and a scan-cap before chunking: if deployFromBlock is null (or
options.getToBlock() is null) return [] or compute a safe fallback window
instead of using genesis (do not let deployFromBlock be treated as 0), and
introduce a MAX_SCAN_BLOCKS cap so span = Math.min(toBlock - deployFromBlock,
MAX_SCAN_BLOCKS) before calculating chunkFrom/chunkTo; keep using YIELD_SEGMENTS
to split that capped span and pass those chunkFrom/chunkTo into options.getLogs
(symbols: deployFromBlock, options.getBlock, options.getToBlock, YIELD_SEGMENTS,
chunkFrom, chunkTo, options.getLogs).

Comment on lines +121 to +132
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);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 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)}")
PY

Repository: 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 f

Repository: 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
fi

Repository: 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 -20

Repository: DefiLlama/dimension-adapters

Length of output: 350


🏁 Script executed:

# Check the file structure and imports to understand data types
head -50 fees/yieldfi/index.ts

Repository: DefiLlama/dimension-adapters

Length of output: 2564


🏁 Script executed:

# Find the Balances type definition
rg -r "class Balances" --type ts | head -5

Repository: 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
fi

Repository: 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 -5

Repository: 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/null

Repository: 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/null

Repository: DefiLlama/dimension-adapters

Length of output: 56


🏁 Script executed:

# Try a different approach - search for the add method definition
rg "add\s*\(" adapters/types.ts

Repository: 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
fi

Repository: DefiLlama/dimension-adapters

Length of output: 93


🏁 Script executed:

# Read adapters/types.ts to find Balances
head -200 adapters/types.ts | tail -100

Repository: DefiLlama/dimension-adapters

Length of output: 3325


🏁 Script executed:

# Check the beginning of adapters/types.ts for imports
head -50 adapters/types.ts

Repository: 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.ts

Repository: 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/null

Repository: 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 -30

Repository: 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.ts

Repository: 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 -10

Repository: DefiLlama/dimension-adapters

Length of output: 54


🏁 Script executed:

# Look at other fee adapters to understand the pattern
rg "dailyFees\.add\(" fees/ | head -20

Repository: DefiLlama/dimension-adapters

Length of output: 1739


🏁 Script executed:

# Check if yieldfi adapter has allowNegativeValue
grep -n "allowNegativeValue" fees/yieldfi/index.ts

Repository: 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 -40

Repository: 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.ts

Repository: 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 -20

Repository: 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 -50

Repository: 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 -30

Repository: 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 -5

Repository: 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 -50

Repository: 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 -20

Repository: 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 -20

Repository: 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 -20

Repository: 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))}")
EOF

Repository: 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 ts

Repository: 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 -20

Repository: 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 -15

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

Repository: 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}")
EOF

Repository: 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 -10

Repository: 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
fi

Repository: 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 -10

Repository: 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
EOF

Repository: 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.ts

Repository: 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 bigint.

Lines 128–130 convert raw supply/rate values to Number and then scale them with floating-point math. For 18-decimal vaults, 1e18 exceeds the safe-integer range (2^53 - 1), so this loses precision and can feed fractional raw amounts into dailyFees.add. Keep the accumulation in integer space.

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

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

Suggested change
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);
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 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 !== 0n) dailyFees.add(assets[i], acc);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fees/yieldfi/index.ts` around lines 121 - 132, The loop currently converts
supplies/rates to Number and uses floating math (loss of precision for 1e18);
change the accumulation to use bigint math: convert s0,s1,r0,r1 from suppliesAt
and ratesAt into BigInt, compute avgSupply = (s0 + s1) / 2n and rateDelta = r1 -
r0 as BigInt, then perform all scaling with BigInt power expressions (e.g., 10n
** BigInt(decimals[i]) and 10n ** BigInt(decimals[i] - assetDecimals[i])) to
compute acc as a BigInt; only convert to the expected type when calling
dailyFees.add (or adjust dailyFees.add to accept BigInt) and use acc !== 0n for
the nonzero check. Ensure you reference YIELD_SEGMENTS, suppliesAt, ratesAt,
decimals, assetDecimals, acc, and dailyFees.add when applying the change.

@bheluga bheluga self-assigned this Mar 30, 2026
cacheInCloud: true,
fromBlock,
});
const vaultDeployedLogs = await fetchVaultDeployedLogs(options, vaultDeployer);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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

const toFeeBlock = await options.getToBlock();
const feeSpan = toFeeBlock - fromFeeBlock;
const boundaryBlocks: number[] = [];
for (let j = 0; j <= YIELD_SEGMENTS; j++) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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

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.

3 participants