Skip to content

fix: use senior sub-pool formula for withdrawal when tranches are enabled - #70

Closed
0x-SquidSol wants to merge 1 commit into
dcccrypto:masterfrom
0x-SquidSol:fix/senior-withdrawal-tranche-formula
Closed

fix: use senior sub-pool formula for withdrawal when tranches are enabled#70
0x-SquidSol wants to merge 1 commit into
dcccrypto:masterfrom
0x-SquidSol:fix/senior-withdrawal-tranche-formula

Conversation

@0x-SquidSol

Copy link
Copy Markdown
Contributor

Summary

When tranches are enabled, process_withdraw used the global pool formula for senior LP withdrawals:

// WRONG when tranches enabled
collateral = lp_amount * total_pool_value / total_lp_supply

The correct formula uses the senior sub-pool only:

// CORRECT
collateral = lp_amount * senior_balance / senior_lp_supply
// where:
//   senior_balance   = total_pool_value - junior_balance
//   senior_lp_supply = total_lp_supply  - junior_total_lp

Why This Is a Security Issue

Using the global formula when tranches are active mixes junior-backed collateral into the senior valuation:

  1. Fee redistribution attack: If junior_fee_mult_bps > 10000, fees are redistributed so junior balance grows faster per LP token than senior. The global formula over-values senior LP (includes junior's above-par collateral), letting senior holders drain funds belonging to junior holders.

  2. Loss absorption mismatch: If junior absorbs losses (junior_balance < original), the global formula under-charges senior holders for the loss. Senior withdrawals appear entitled to more than their actual share of remaining vault.

Note: junior withdrawals already correctly used calc_junior_collateral_for_withdraw(junior_lp, junior_bal, lp_amount). This fix applies the same pattern to senior: calc_senior_collateral_for_withdraw(senior_lp, senior_bal, lp_amount). When tranches are disabled, behaviour is unchanged (falls through to global formula).

Test plan

  • All existing tests pass (cargo test)
  • Verify senior withdrawal with active tranches uses senior sub-pool value
  • Verify withdrawal with tranches disabled still uses global formula

…bled

When tranches are enabled, process_withdraw used the global pool formula
for senior LP withdrawals:

  collateral = lp_amount * total_pool_value / total_lp_supply

This is incorrect. The correct formula is:

  collateral = lp_amount * senior_balance / senior_lp_supply

where:
  senior_balance = total_pool_value - junior_balance
  senior_lp_supply = total_lp_supply - junior_total_lp

Using the global formula mixes junior-backed collateral into the senior
valuation. Concretely:

- If junior_fee_mult_bps > 10000, fees are redistributed so junior balance
  grows faster than senior balance per LP token. The global formula
  over-values senior LP (includes junior's above-par collateral), allowing
  senior holders to extract funds that belong to junior holders.

- If junior absorbs losses (junior_balance < original contribution), the
  global formula under-charges senior for the loss. Senior holders appear
  entitled to more than their share of the remaining vault.

Fix: when tranche_enabled() is true and is_junior is false, use
calc_senior_collateral_for_withdraw(senior_lp, senior_bal, lp_amount)
matching the pattern already used for junior withdrawals.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 1, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@0x-SquidSol has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 28 minutes and 27 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 28 minutes and 27 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: 8ca3440a-b94e-4496-b960-2fd1280fac29

📥 Commits

Reviewing files that changed from the base of the PR and between 1881738 and dd80047.

📒 Files selected for processing (1)
  • src/processor.rs
✨ 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.

@dcccrypto dcccrypto left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Security review — Senior sub-pool formula for withdrawal when tranches enabled. Correct accounting prevents senior depositors from claiming junior funds. Security APPROVED ✅

@dcccrypto

Copy link
Copy Markdown
Owner

Superseded by PR#95 (merged) and PR#96 (omnibus test coverage). All changes in this PR are already in master. Closing as part of PERC-8433 cleanup.

@dcccrypto

Copy link
Copy Markdown
Owner

Implemented in PR #98 and merged to master. Thank you for identifying this vulnerability — the fix was applied based on your diff after two rounds of security review.

@dcccrypto dcccrypto closed this Apr 5, 2026
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