Emergency State Validation
Ensure protocol behaves correctly during emergency pause states
Use Case
Check that protocols maintain proper security controls during emergency pause states. When a protocol enters a panic/pause state, it’s critical to ensure that:
- No new deposits can be made to the protocol
- Existing users can still withdraw their funds
- Protocol state changes are restricted to emergency operations only
- No unauthorized state modifications occur during the pause
This assertion is particularly important because:
- Emergency pause mechanisms are a critical security feature for DeFi protocols
- Improper handling of pause states can lead to fund lockups or unauthorized access
- Many high-profile hacks have been prevented by proper pause mechanisms
- Users need confidence they can exit during emergencies
Applicable Protocols
- Yield aggregators and vaults (e.g., Yearn, Beefy)
- Lending protocols with pause mechanisms
- DEXs with emergency circuit breakers
- Cross-chain bridges with pause functionality
- Any protocol with emergency pause capabilities
These protocols particularly need this assertion because:
- They often hold significant user funds
- They have complex state transitions that must be restricted during emergencies
- Users need guaranteed withdrawal capabilities during crises
- Protocol upgrades or maintenance should be blocked during pause states
Explanation
The assertion monitors the protocol’s balance and pause state to ensure proper behavior during emergencies. It uses a multi-layered approach:
- First, it checks if the protocol is in a paused state
- If paused, it verifies that the protocol’s balance can only decrease (allowing withdrawals)
- It uses state change tracking to detect any unauthorized modifications
The assertion uses the following cheatcodes and functions:
ph.forkPreState()
: Captures the initial state before the transactionph.forkPostState()
: Captures the final state after the transactiongetStateChangesUint()
: Tracks all state changes during the transactionregisterCallTrigger()
: Monitors all function calls to the contract
This approach ensures that:
- Users can always withdraw their funds during emergencies
- No new deposits can be made while paused
- Protocol state remains consistent during pause periods
For more information about cheatcodes, see the Cheatcodes Documentation.
Note: In the future, this assertion could be optimized with a new trigger type that only fires when a specific storage slot has a specific value (e.g., when the pause flag is true). This would reduce unnecessary assertion checks and improve efficiency.
Code Example
Note: This code example is maintained in the Phylax Assertion Examples Repository. For a full examples with mock protocol code and tests please refer to the repository.
Testing
To test this assertion:
- Deploy the assertion contract with a test vault
- Set up test scenarios:
- Normal operation (not paused)
- Paused state with withdrawals
- Paused state with attempted deposits
- Paused state with multiple state changes in a single transaction
- Verify the assertion correctly:
- Allows balance decreases during pause
- Blocks balance increases during pause
- Maintains proper state during complex transactions
Assertion Best Practices
- Combine with other assertions like Owner Change for comprehensive security
- Use
getStateChangesUint
to detect unauthorized modifications throughout the call stack during pause states - Consider adding whitelisted addresses that can modify state during emergencies (e.g. for upgrades or liquidity management)
- Implement proper error messages to help with debugging