contract CallFrameContextAssertion is Assertion {
function triggers() public view override {
registerCallTrigger(this.assertCallFrameContext.selector, Protocol.batchTransfer.selector);
}
function assertCallFrameContext() public {
Protocol protocol = Protocol(ph.getAssertionAdopter());
// Get all transfer calls within the batchTransfer transaction
PhEvm.CallInputs[] memory transferCalls = ph.getCallInputs(
address(protocol),
protocol.transfer.selector
);
// Validate each transfer call individually
for (uint256 i = 0; i < transferCalls.length; i++) {
// Fork to state before this specific call
ph.forkPreCall(transferCalls[i].id);
address from = transferCalls[i].caller;
(address to, uint256 amount) = abi.decode(transferCalls[i].input, (address, uint256));
uint256 preFromBalance = protocol.balances(from);
uint256 preToBalance = protocol.balances(to);
// Fork to state after this specific call
ph.forkPostCall(transferCalls[i].id);
uint256 postFromBalance = protocol.balances(from);
uint256 postToBalance = protocol.balances(to);
// Verify the transfer was executed correctly for this call frame
require(
postFromBalance == preFromBalance - amount,
"From balance mismatch in call frame"
);
require(
postToBalance == preToBalance + amount,
"To balance mismatch in call frame"
);
}
}
}