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");
}
}
}
}