Skip to content

fix: junior withdrawal balance drift causes permanent fund lockup - #103

Merged
dcccrypto merged 1 commit into
dcccrypto:masterfrom
0x-SquidSol:fix/junior-withdrawal-balance-drift
Apr 7, 2026
Merged

fix: junior withdrawal balance drift causes permanent fund lockup#103
dcccrypto merged 1 commit into
dcccrypto:masterfrom
0x-SquidSol:fix/junior-withdrawal-balance-drift

Conversation

@0x-SquidSol

@0x-SquidSol 0x-SquidSol commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Junior withdrawal subtracts withdrawal_amount (derived from loss-adjusted effective_junior_balance) from the raw junior_balance, causing a permanent residual when insurance losses exist
  • After all junior LP holders exit and insurance is later returned, the orphaned junior_balance > 0 with junior_total_lp == 0 triggers the supply=0 && value>0 guard in calc_junior_lp_for_deposit, permanently blocking new junior deposits and locking returned insurance tokens in the vault

Severity

HIGH — permanent token lockup. Returned insurance tokens become inaccessible to both senior and junior holders.

Reproduction

  1. Junior deposits 1000, Senior deposits 4000
  2. Admin flushes 500 to insurance (total_flushed=500)
  3. Junior withdraws all LP → gets 500 (correct), but junior_balance = 1000 - 500 = 500 (should be 0)
  4. Admin returns insurance → total_returned=500, vault has 4500
  5. Senior can only claim 4000 (senior_balance = 4500 - 500). 500 tokens locked forever.

Fix

  • When all junior LP is burned (new_junior_lp == 0), zero out junior_balance
  • For partial withdrawals, decrease junior_balance proportionally by LP share of the raw balance (lp_amount * junior_balance / junior_lp_before), not by the loss-adjusted withdrawal_amount

Test plan

  • cargo build — compiles cleanly
  • cargo clippy — zero warnings
  • CI tests

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Refined withdrawal amount calculations for liquidity pools to provide more accurate balance updates and prevent calculation errors.

process_withdraw subtracted withdrawal_amount (derived from the
loss-adjusted effective_junior_balance) from the raw junior_balance.
When insurance losses exist, effective < raw, so the raw balance
accumulated a residual with each withdrawal.  After all junior LP
holders exit and insurance is returned, the orphaned junior_balance
blocks new junior deposits (supply=0, value>0 guard) and locks
tokens permanently in the vault — inaccessible to senior holders
because senior_balance excludes effective_junior_balance.

Fix: when all junior LP is burned, zero out junior_balance.  For
partial withdrawals, decrease junior_balance proportionally by the
LP share of the raw balance (lp_amount * junior_balance / junior_lp)
rather than by the loss-adjusted withdrawal_amount.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 6, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 26a8a9e7-0558-494b-844f-28c44f0f2c77

📥 Commits

Reviewing files that changed from the base of the PR and between b1f14d2 and 6071fd5.

📒 Files selected for processing (1)
  • src/processor.rs

📝 Walkthrough

Walkthrough

The process_withdraw function's junior-tranche withdrawal logic was modified to change how junior_balance is reduced. Instead of a fixed subtraction, the new approach calculates a proportional reduction based on the withdrawn LP's share of total junior LP, with a special case that zeroes the balance when all LP is withdrawn.

Changes

Cohort / File(s) Summary
Junior Tranche Withdrawal Logic
src/processor.rs
Modified process_withdraw to compute junior balance reduction proportionally (raw_decrease = lp_amount * jb / junior_lp_before) instead of fixed amount subtraction, with special-case zeroing when all LP is withdrawn.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 A rabbit hops through balance sheets with care,
Proportional withdrawals, fair and square,
No longer fixed, but share-based and true,
When all LP burns, the balance zeroes too! 🔥

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
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.
Description check ❓ Inconclusive The description covers the required sections: Summary (root cause and impact), How to test (build and clippy passed, CI tests pending), and partially addresses the Checklist. However, it is missing explicit confirmation that the full Checklist items were completed before submission, and does not mention the Kani proof verification requirement for math/invariant code. Confirm completion of all Checklist items (Kani verification is critical for math/invariant changes) and provide explicit status for each test before marking ready for merge.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: a fix for junior withdrawal balance drift that causes permanent fund lockup, which is the core issue addressed in the PR.

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

@dcccrypto

Copy link
Copy Markdown
Owner

Sentinel security review — MERGE

Fix is correct and the bug is real. Current master subtracts the loss-adjusted withdrawal_amount from the raw junior_balance, creating a permanent residual when insurance losses exist. When all junior LP burns to zero and insurance is later returned, the orphaned junior_balance > 0 with junior_total_lp == 0 hits the supply=0/value>0 guard in calc_junior_lp_for_deposit, permanently blocking new junior deposits and locking returned insurance tokens in the vault.

The fix is correct on both paths: zero-out when all LP is burned, proportional raw-balance reduction on partial withdrawal. The u128 intermediate prevents overflow. Logic matches the proportional LP math used elsewhere in the codebase.

No conflicts with any other open PR. No new issues introduced.

Severity confirmed: HIGH (CVSS ~7.5 — permanent fund lockup of returned insurance tokens, no admin recovery path).

Security APPROVED PR #103 — findings: none | junior_balance accounting fix prevents permanent vault lockup on insurance return

@dcccrypto
dcccrypto merged commit c0957e2 into dcccrypto:master Apr 7, 2026
1 check passed
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