Skip to content

fix(stake): deposit-cap principal basis (#154) + clear deposit record on full withdraw (#155) - #172

Merged
dcccrypto merged 1 commit into
mainfrom
fix/stake-154-155-deposit-cap-and-tranche-flag
Jun 19, 2026
Merged

fix(stake): deposit-cap principal basis (#154) + clear deposit record on full withdraw (#155)#172
dcccrypto merged 1 commit into
mainfrom
fix/stake-154-155-deposit-cap-and-tranche-flag

Conversation

@dcccrypto

@dcccrypto dcccrypto commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Fixes #154 (deposit cap counted accrued fees → spurious lockout) and #155 (deposit-PDA tranche flag persisted after full withdrawal → wallet permanently blocked from the other tranche). See commit for the mechanism + safety analysis. #157 deliberately NOT fixed — on my own analysis it is not a reachable bug (a fee-enriched junior always covers any loss ≤ deposited, so the gross_senior saturation never affects the result); I reverted the speculative change and will note it on #157. Build-sbf clean; suite green except the 4 pre-existing v17_stake_insurance_e2e LiteSVM failures.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Fixed deposit cap enforcement in mode-1 trading pools to exclude accrued fees, preventing legitimate deposits from being incorrectly rejected.
    • Deposit records can now be reused for either tranche on subsequent deposits.
  • Tests

    • Added regression test to validate deposit cap enforcement behavior with accrued fees.

… on full withdraw (#155)

#154 (MEDIUM): process_deposit / process_deposit_junior enforced the deposit cap against
total_pool_value(), which is fee-inclusive for mode-1 trading pools. As fees accrued, the
cap was hit and NEW deposits were locked out even though contributed principal was under
the cap. Add StakePool::principal_tvl() (deposited − withdrawn − flushed + returned, NO
fees) and enforce the cap on it. LP pricing still uses total_pool_value() (fee-inclusive).
For mode-0 principal_tvl == total_pool_value. Unit test: test_154_*.

#155 (HIGH): process_withdraw decremented deposit.lp_amount but never cleared is_initialized
or the _reserved[8] tranche flag when the position reached zero. PERC-303's anti-mixing guard
keys off those persisted fields, so a wallet that fully withdrew was permanently blocked from
depositing into the OTHER tranche (junior↔senior). Reset is_initialized + _reserved[8] when
lp_amount hits 0 so the (pool,user) PDA is reusable for either tranche. Safe vs PERC-303 (no
residual position to mix when fully withdrawn); metadata-only. The deposit re-init paths
correctly re-initialize a zeroed record (verified). Runtime e2e regression deferred to the
OpenSSL-blocked LiteSVM suite (follow-up).

NOT included: #157 (senior-stall) — on analysis it is NOT a reachable bug. A fee-enriched
junior (jb > principal) always has jb > net_loss (net_loss ≤ flushed ≤ deposited ≤ principal),
so junior (first-loss) always covers the loss and the gross_senior saturation never changes
the outcome. Reverted the speculative change; flagged on the issue.

Build-sbf clean; full suite green except the 4 pre-existing v17_stake_insurance_e2e failures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dcccrypto
dcccrypto merged commit c51e4fc into main Jun 19, 2026
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aaefc837-95aa-4520-890c-0fcdc0be3f57

📥 Commits

Reviewing files that changed from the base of the PR and between 3b5679d and f027b15.

📒 Files selected for processing (3)
  • src/processor.rs
  • src/state.rs
  • tests/unit.rs

📝 Walkthrough

Walkthrough

Adds StakePool::principal_tvl() that computes TVL excluding accrued trading fees, then replaces total_pool_value() with principal_tvl() in both deposit-cap checks. On full withdrawal (lp_amount == 0), the deposit PDA's is_initialized and tranche flag are cleared to allow reuse. A regression test validates all three behaviors.

Changes

Principal TVL deposit-cap enforcement and deposit PDA reuse

Layer / File(s) Summary
StakePool::principal_tvl() method
src/state.rs
New method computes total_deposited - total_withdrawn - total_flushed + total_returned via checked arithmetic, explicitly omitting total_fees_earned that total_pool_value() adds in mode-1 pools.
Deposit cap checks switched to principal_tvl()
src/processor.rs
Both process_deposit (line 496–498) and process_deposit_junior (line 2201–2203) now call principal_tvl() instead of total_pool_value() when evaluating the deposit cap, so fee appreciation in mode-1 pools no longer contributes to cap consumption.
Deposit PDA reset on full withdrawal
src/processor.rs
After burning LP in process_withdraw, if deposit_mut.lp_amount == 0, is_initialized is set to false and _reserved[8] is cleared, permitting the same deposit PDA to be reused for either tranche on a subsequent deposit.
Regression test #154
tests/unit.rs
test_154_deposit_cap_enforced_on_principal_not_fees asserts principal_tvl() excludes fees in mode-1, that a deposit within the principal cap passes despite fee-inclusive value exceeding the cap, and that principal_tvl() == total_pool_value() in mode-0.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • dcccrypto/percolator-stake#98: Modifies the same tranche-mixing anti-bypass logic in process_deposit/process_withdraw that this PR's deposit PDA reset directly relaxes on full withdrawal.
  • dcccrypto/percolator-stake#122: Modifies process_deposit_junior's TVL computation in the same function block where this PR switches the deposit-cap basis from total_pool_value() to principal_tvl().

Poem

🐇 Hoppity-hop through the ledger I go,
Fees piled high? They won't block the flow!
Principal only — the cap holds true,
And empty PDAs get a fresh start too.
No silent lockout, no admin surprise —
The burrow stays open under honest skies! 🌿

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/stake-154-155-deposit-cap-and-tranche-flag

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.

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.

1 participant