Skip to content

fix: replace tiered second-deposit panic with typed EscrowError in fu…#593

Open
edrizxabdulganiyu-blip wants to merge 1 commit into
Liquifact:mainfrom
edrizxabdulganiyu-blip:security/contracts-tiered-second-deposit-typed-error
Open

fix: replace tiered second-deposit panic with typed EscrowError in fu…#593
edrizxabdulganiyu-blip wants to merge 1 commit into
Liquifact:mainfrom
edrizxabdulganiyu-blip:security/contracts-tiered-second-deposit-typed-error

Conversation

@edrizxabdulganiyu-blip

Copy link
Copy Markdown

fix: replace tiered second-deposit panic with typed EscrowError in fund_with_commitment

Summary

fund_impl (reached via fund_with_commitment) previously guarded the tiered re-entry path with a
raw assert! and a panic string. Every other funding guard in the contract uses the append-only
EscrowError enum, and the SDK contract requires callers to branch on ContractError(code) — not
panic text. This PR brings the tiered second-deposit guard into line with that discipline.

Changes

escrow/src/lib.rs

  • The TieredSecondDeposit = 108 variant's doc comment is expanded to a full NatSpec-style
    block: invariant rationale, affected DataKey, recommended client action (use fund() for
    follow-on principal), and explicit code-stability note.
  • The ensure(&env, prev == 0, EscrowError::TieredSecondDeposit) call in fund_impl is the single
    enforcement point — guard ordering and exact trigger condition (prev != 0) are unchanged.

escrow/src/tests/funding.rs

  • Upgraded two #[should_panic] tests to assert_contract_error(...,
    EscrowError::TieredSecondDeposit) so they assert the numeric code, not just any panic:

    • test_fund_with_commitment_twice_panics
    • test_fund_then_fund_with_commitment_panics
  • Added 4 new edge-case tests:

┌───────────────────────────────────┬──────────────────────────────────────────────────────┐
│ Test │ What it covers │
├───────────────────────────────────┼──────────────────────────────────────────────────────┤
│ test_tiered_second_deposit_d │ Re-entry with a different lock (attempted tier │
│ ifferent_lock_rejected │ upgrade) is rejected; effective yield unchanged │
├───────────────────────────────────┼──────────────────────────────────────────────────────┤
│ test_tiered_second_deposit_z │ Re-entry with lock=0 (base-yield fallback) is also │
│ ero_lock_also_rejected │ rejected — all re-entries are blocked │
├───────────────────────────────────┼──────────────────────────────────────────────────────┤
│ test_tiered_second_deposit_g │ Guard is per-investor: Investor B's first call │
│ uard_is_per_investor │ succeeds; Investor A's second is rejected │
├───────────────────────────────────┼──────────────────────────────────────────────────────┤
│ test_follow_on_fund_after_ti │ Three consecutive fund() follow-ons preserve both │
│ ered_commitment_preserves_all_sta │ InvestorEffectiveYield and InvestorClaimNotBefore │
│ te │ │
└───────────────────────────────────┴──────────────────────────────────────────────────────┘

docs/escrow-error-messages.md

  • Enriched code 108 canonical table row with full trigger condition and client action.
  • Legacy panic string row updated to note the assert! origin.

docs/adr/ADR-005-tiered-yield.md

  • Replaced "Panics if the investor already has a contribution" with the explicit typed-error
    reference and SDK branching note.
  • Test coverage table extended from 15 to 19 entries.

Security notes

  • Identical revert condition: guard fires on prev != 0, unchanged from before.
  • Stable numeric code: 108 is append-only; no existing code is renumbered or removed.
  • fund() follow-on unaffected: the simple_fund = true path has no ensure on prev; returning
    investors using fund() continue to work as before.
  • No migration required: this is a code-only change; no stored XDR layout or DataKey variant is
    modified.

What was tested

  • All existing tiered-deposit tests pass with the upgraded assertions.
  • New tests cover: re-entry with different lock, re-entry with zero lock, per-investor
    isolation, and multi-leg fund() follow-on invariant preservation.

close #355

…nd_with_commitment with tests

- TieredSecondDeposit = 108 guard was using a raw assert! panic string;
  replaced with ensure(&env, prev == 0, EscrowError::TieredSecondDeposit)
  so SDK clients can branch on ContractError(108) instead of panic text.

- Enhanced TieredSecondDeposit variant doc comment with NatSpec-style
  documentation: invariant rationale, affected DataKey, recommended client
  action, and explicit code-stability note.

- Upgraded test_fund_with_commitment_twice_panics and
  test_fund_then_fund_with_commitment_panics from #[should_panic] to
  assert_contract_error with EscrowError::TieredSecondDeposit so the
  tests assert the numeric code, not just any panic.

- Added 4 new edge-case tests:
    test_tiered_second_deposit_different_lock_rejected
    test_tiered_second_deposit_zero_lock_also_rejected
    test_tiered_second_deposit_guard_is_per_investor
    test_follow_on_fund_after_tiered_commitment_preserves_all_state

- Updated docs/escrow-error-messages.md: enriched code 108 canonical
  table row; legacy panic string row now notes the assert! origin.

- Updated docs/adr/ADR-005-tiered-yield.md: replaced 'panics' language
  with explicit EscrowError::TieredSecondDeposit (code 108) reference;
  extended test coverage table to 19 entries.
@drips-wave

drips-wave Bot commented Jul 1, 2026

Copy link
Copy Markdown

@edrizxabdulganiyu-blip Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

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.

Convert the tiered second-deposit panic in fund_with_commitment to a typed error

1 participant