Implementation Address Change
Assert that the address of an implementation has not changed unexpectedly
Use Case
Check that the implementation address of a proxy contract does not change unexpectedly. In proxy-based upgradeable contracts, the implementation address is a critical security parameter that determines which contract’s logic is executed. An unauthorized change to this address could allow an attacker to replace the entire contract logic with malicious code.
This assertion is particularly important for:
- Preventing unauthorized upgrades to proxy contracts
- Ensuring implementation changes only occur through proper governance channels
- Detecting potential proxy hijacking attempts
- Maintaining protocol security by enforcing implementation address invariants
For example, it would be possible to define a list of allowed implementations that would be whitelisted and any other implementation would be considered an invalid state.
Applicable Protocols
- Proxy-based upgradeable contracts that need to enforce strict upgrade controls
- DeFi protocols using proxy patterns for their core functionality (e.g., lending pools, yield aggregators)
- Governance systems that control implementation upgrades through timelocks or multi-sig
- Cross-chain bridges and bridges that use proxy patterns for their core logic
Explanation
The assertion monitors changes to the implementation address storage slot in proxy contracts. In proxy patterns, the implementation address is typically stored in a specific storage slot (often slot 0) and determines which contract’s logic is executed when calls are delegated.
The assertion uses the following cheatcodes and functions:
ph.forkPreState()
: Creates a fork of the state before the transaction to capture the original implementation addressph.forkPostState()
: Creates a fork of the state after the transaction to detect any changesgetStateChangesAddress()
: Gets all state changes for the implementation address storage slot to detect unauthorized modifications throughout the callstack of the transactionregisterStorageChangeTrigger()
: Triggers the assertion when a change is detected in the implementation address storage slot
The implementation performs two checks:
- A direct comparison between pre-state and post-state implementation addresses
- A thorough verification of all state changes to the implementation slot during the transaction’s execution
This multi-layered approach ensures that:
- The implementation address remains constant unless explicitly changed through proper channels
- Any unauthorized attempts to modify the implementation address are detected
- The proxy contract’s logic cannot be replaced with malicious code
For more information about cheatcodes, see the Cheatcodes Documentation.
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.
Assertion Best Practices
- Consider combining this assertion with other assertions like Owner Change for comprehensive security
- Use the
getStateChangesAddress
cheatcode to detect changes to the implementation address throughout the callstack of the transaction - Consider whitelisting specific implementation addresses if you know the set of allowed future implementations
- Ensure proper error messages in the assertion to help with debugging
- Be aware that the example assumes the implementation address is stored in slot 0, which may vary across different proxy implementations