> ## Documentation Index
> Fetch the complete documentation index at: https://docs.phylax.systems/llms.txt
> Use this file to discover all available pages before exploring further.

# Read Logs

> Capture and verify logs as a signal for changes in the state of the contract.

Read log mappings describe how assertions can inspect emitted events and use them as signals for state changes during a transaction.

## Status

* [x] Supported
* [ ] Partially Supported
* [ ] Not Supported Yet

## Implementation Details

### Required Cheatcodes

* `getLogs()`

### Example Implementation

Protocol:

```solidity theme={null}
import {AssertionSpec} from "credible-std/SpecRecorder.sol";

contract Protocol {
    event Transfer(address from, address to, uint256 amount);

    mapping(address => uint256) public balances;

    function transfer(address to, uint256 amount) public {
        balances[msg.sender] -= amount;
        balances[to] += amount;
        emit Transfer(msg.sender, to, amount);
    }

    function mint(address to, uint256 amount) public {
        balances[to] += amount;
        emit Transfer(address(0), to, amount);
    }
}
```

Assertion:

```solidity theme={null}
contract ReadLogsAssertion is Assertion {
    constructor() {
        registerAssertionSpec(AssertionSpec.Reshiram);
    }

    mapping(address => int256) public balanceChanges;
    address[] changedAddresses;

    function triggers() public view override {
        registerTxEndTrigger(this.assertionReadLogs.selector);
    }

    function assertionReadLogs() public view {
        Protocol protocol = Protocol(ph.getAssertionAdopter());
        PhEvm.Log[] memory logs = ph.getLogs();

        for (uint256 i = 0; i < logs.length; i++) {
            (address from, address to, uint256 amount) = abi.decode(logs[i].data, (address, address, uint256));
            // There is an edge case where the changedBalance has gone back to 0
            // In this case, it would be duplicated in the changedAddresses array
            // Additional flags can remove duplicates
            if (balanceChanges[from] == 0) {
                changedAddresses.push(from);
            }
            if (balanceChanges[to] == 0) {
                changedAddresses.push(to);
            }
            balanceChanges[from] -= int256(amount);
            balanceChanges[to] += int256(amount);
        }

        uint256 preBalance;
        uint256 absDiff;

        for (uint256 i = 0; i < changedAddresses.length; i++) {
            bytes32 balanceSlot = keccak256(abi.encode(changedAddresses[i], uint256(0)));
            PhEvm.ForkId memory preTx = _preTx();
            PhEvm.ForkId memory postTx = _postTx();

            preBalance = uint256(ph.loadStateAt(address(protocol), balanceSlot, preTx));
            uint256 postBalance = uint256(ph.loadStateAt(address(protocol), balanceSlot, postTx));

            if (balanceChanges[changedAddresses[i]] > 0) {
                absDiff = uint256(balanceChanges[changedAddresses[i]]);
                require(postBalance == preBalance + absDiff, "Balance change mismatch");
            } else {
                absDiff = uint256(balanceChanges[changedAddresses[i]] * -1);
                require(postBalance == preBalance - absDiff, "Balance change mismatch");
            }
        }
    }
}
```

### Implementation Notes

* **Fetching logs:**
  * Use `getLogs()` to retrieve logs
  * Filter for specific events
  * Decode parameters using `abi.decode()` to reconstruct event arguments

* **State verification:**
  * Use explicit `ForkId` values with `loadStateAt()` to compare states
  * Validate state changes match expected behavior based on inputs

* **State tracking considerations:**
  * Manually track modified storage slots/keys
  * Consider all functions that modify relevant state variables
  * Account for complex state dependencies between function calls

* **State reconstruction considerations:**
  * Manual tracking required for parameter-dependent storage slots (i.e. keys of mappings)
  * No direct access to intermediate call frame states
  * Order guarantees only within same function calls, not across different functions

## Example Use Cases

* Transfer events of ERC20 tokens
* Ownership changes
* Whenever a log is emitted, which represents a state change, it can be used to reconstruct the expected behavior

## Implementation Considerations

1. **Parameter-dependent Storage Access**
   * Storage slots that depend on event parameters lack automated tracking
   * Example: Mapping keys derived from event parameters
   * Workaround: Manual tracking required, but introduces overhead

2. **Requirement of events to be emitted**
   * If it is not certain that the event is emitted upon state change, the state change is not tracked
   * Might result in false negatives

## Future Improvements
