> ## Documentation Index
> Fetch the complete documentation index at: https://phylaxsystems-docs-project-incidents.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Owner Change

> Assert that the owner of a contract has not changed unexpectedly

## Use Case & Applications

Prevents unauthorized ownership transfers, which could allow attackers to take complete control of the protocol. Critical for DeFi protocols with owner-controlled administrative functions, lending protocols, yield aggregators, cross-chain bridges, and governance systems.

For example, in the [Radiant Capital hack](/assertions-book/previous-hacks/hack1-radiant-capital), the attacker gained control over 3 signers of the multisig, which allowed them to change ownership of the lending pools and ultimately drain the protocol. This assertion would have prevented such an attack.

## Explanation

Monitors changes to the owner address storage slot in contracts using:

* `ph.forkPreTx()` / `ph.forkPostTx()`: Compare owner address before and after transaction
* `getStateChangesAddress()`: Track all changes to the owner address slot during transaction execution
* `registerStorageChangeTrigger()`: Trigger when owner address changes

The assertion performs both direct pre/post comparison and verification of all intermediate state changes to detect unauthorized ownership modifications.

For more information about cheatcodes, see the [Cheatcodes Documentation](/credible/cheatcodes-reference).

## Code Example

```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {Assertion} from "credible-std/Assertion.sol";
import {PhEvm} from "credible-std/PhEvm.sol";

contract OwnerChangeAssertion is Assertion {
    function triggers() external view override {
        // Register triggers for changes to both owner and admin storage slots
        registerStorageChangeTrigger(this.assertionOwnerChange.selector, bytes32(uint256(0)));
        registerStorageChangeTrigger(this.assertionAdminChange.selector, bytes32(uint256(1)));
    }

    // Assert that the owner address doesn't change during the state transition
    function assertionOwnerChange() external {
        // Get the assertion adopter address
        IOwnership adopter = IOwnership(ph.getAssertionAdopter());

        // Get pre-state owner
        ph.forkPreTx();
        address preOwner = adopter.owner();

        // Get post-state owner
        ph.forkPostTx();
        address postOwner = adopter.owner();

        // Verify owner hasn't changed after the transaction
        // Fail early if the owner has changed
        require(preOwner == postOwner, "Owner changed");

        // Get all state changes for the owner slot
        // This checks if the owner address has changed throughout the callstack
        address[] memory changes = getStateChangesAddress(
            address(adopter),
            bytes32(uint256(0)) // First storage slot for owner address
        );

        // Additional check: verify no changes take place in the owner slot throughout the callstack
        for (uint256 i = 0; i < changes.length; i++) {
            require(changes[i] == preOwner, "Unauthorized owner change detected");
        }
    }

    // Assert that the admin address doesn't change during the state transition
    function assertionAdminChange() external {
        // Get the assertion adopter address
        IOwnership adopter = IOwnership(ph.getAssertionAdopter());

        // Get pre-state admin
        ph.forkPreTx();
        address preAdmin = adopter.admin();

        // Get post-state admin
        ph.forkPostTx();
        address postAdmin = adopter.admin();

        // Get all state changes for the admin slot
        address[] memory changes = getStateChangesAddress(
            address(adopter),
            bytes32(uint256(1)) // Second storage slot for admin address
        );

        // Verify admin hasn't changed after the transaction
        require(preAdmin == postAdmin, "Admin changed");

        // Additional check: verify no changes take place in the admin slot throughout the callstack
        for (uint256 i = 0; i < changes.length; i++) {
            require(changes[i] == preAdmin, "Unauthorized admin change detected");
        }
    }
}

interface IOwnership {
    function owner() external view returns (address);
    function admin() external view returns (address);
}
```

<Note>Full examples with tests available in the [Phylax Assertion Examples Repository](https://github.com/phylaxsystems/assertion-examples/blob/main/assertions/).</Note>

## Related Assertions

* [Implementation Address Change](/assertions-book/assertions/ass01-impl-addr-change)
* [Timelock Verification](/assertions-book/assertions/ass09-timelock-verification)
