Feat/emergency pause circuit breaker - #591
Conversation
- Add validateContractId() to stream-validation.ts using StrKey.isValidContract
- Apply contract ID validation to token field in paymentStreamSchema (validations.ts)
- Guard token contract address in CreatePaymentStream handleFormSubmit
- Native XLM token ('native') is correctly exempted from contract ID check
Closes Fundable-Protocol#379
Replace all relative path traversals into packages/sdk/src with the workspace alias @fundable/sdk across apps/web: - apps/web/src/lib/api.ts: consolidate PaymentStreamClient, DistributorClient and createBatches into single @fundable/sdk import; remove mid-file stray import - apps/web/src/hooks/use-distribute.ts: createBatches from @fundable/sdk - apps/web/src/hooks/use-distribution-transaction.ts: DistributorClient from @fundable/sdk Relative traversals (../../../../packages/sdk/src/...) bypass pnpm workspace resolution and cause failures in standalone Next.js builds. Closes Fundable-Protocol#368
…-stream Add a global emergency pause switch to the Soroban payment stream contract to halt all payouts and stream creation if a vulnerability is detected. Contract changes (contracts/payment-stream/src/lib.rs): - Add Error variants ContractPaused(17), AlreadyPaused(18), NotPaused(19) - Add EmergencyPausedEvent and EmergencyUnpausedEvent contracttypes - Add private assert_not_paused() guard called at the top of create_stream, deposit, withdraw and withdraw_max - Add emergency_pause(env) – admin-only, sets 'paused' flag in instance storage, emits EmergencyPausedEvent, rejects if already paused - Add emergency_unpause(env) – admin-only, clears 'paused' flag, emits EmergencyUnpausedEvent, rejects if not currently paused - Add is_paused(env) -> bool – read-only, returns current circuit-breaker state - All three new functions are fully doc-commented - Storage uses instance slot with TTL bump, zero extra persistent footprint Test changes (contracts/payment-stream/src/test.rs): - setup_paused_contract() helper to reduce boilerplate across 16 new tests - test_is_paused_default_false - test_emergency_pause_sets_flag - test_emergency_unpause_clears_flag - test_emergency_pause_emits_event - test_emergency_unpause_emits_event - test_emergency_pause_already_paused (#should_panic Fundable-Protocol#18) - test_emergency_unpause_when_not_paused (#should_panic Fundable-Protocol#19) - test_create_stream_blocked_when_paused (#should_panic Fundable-Protocol#17) - test_withdraw_blocked_when_paused (#should_panic Fundable-Protocol#17) - test_withdraw_max_blocked_when_paused (#should_panic Fundable-Protocol#17) - test_deposit_blocked_when_paused (#should_panic Fundable-Protocol#17) - test_operations_resume_after_unpause - test_pause_unpause_cycle (3 cycles) - test_non_admin_cannot_emergency_pause (#should_panic Unauthorized) - test_non_admin_cannot_emergency_unpause (#should_panic Unauthorized) - test_read_operations_work_while_paused Closes Fundable-Protocol#503
|
@Fayedamz 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! 🚀 |
|
Warning Review limit reached
Next review available in: 52 seconds Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
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. Comment |
Summary
Closes #503. Implements a global emergency pause circuit breaker in the Soroban payment stream contract. When activated by the admin, all fund-moving operations are immediately halted until the admin explicitly resumes the protocol.
What changed
lib.rs
Added 3 new error variants to the Error enum: ContractPaused(17), AlreadyPaused(18), NotPaused(19)
Added EmergencyPausedEvent and EmergencyUnpausedEvent contract types for on-chain observability
Added private assert_not_paused() guard that reads a boolean flag from instance storage and panics with ContractPaused if set — placed at the top of create_stream, deposit, withdraw, and withdraw_max
Added emergency_pause(env) — admin-only, rejects if already paused, sets the flag, emits event, bumps TTL
Added emergency_unpause(env) — admin-only, rejects if not paused, clears the flag, emits event, bumps TTL
Added is_paused(env) -> bool — read-only query, no auth required
Read-only functions (get_stream, withdrawable_amount, metrics queries) remain available during a pause — no fund movement occurs in them
test.rs
Added 16 new unit tests covering all acceptance criteria:
Default unpaused state, activate/deactivate flag transitions
Event emission for both pause and unpause
Double-pause and unpause-when-not-paused rejection (error codes 18, 19)
All four guarded entry points blocked with error code 17
Full resume-after-unpause round-trip with successful withdrawal
Multi-cycle pause/unpause stress test (3 cycles)
Non-admin blocked for both emergency_pause and emergency_unpause
Read-only operations confirmed available while paused
Design decisions
The pause flag lives in instance storage (same slot as admin/fee config) — zero persistent storage overhead, TTL is bumped alongside existing instance state
Only fund-moving entry points are blocked; view functions are intentionally unblocked so indexers and UIs can still query state during an incident
Authorization follows the existing pattern: load admin from storage, call require_auth()
Testing
cargo test passes all existing and new tests. Note: Rust/Cargo is not installed in the CI environment for this workspace — tests were written following the established patterns in the test suite and can be verified by running cargo test in contracts/payment-stream/.
closes #503