contract FunctionCallInputsAssertion is Assertion {
Protocol public protocol;
mapping(address => int256) public balanceChanges;
address[] changedAddresses;
constructor(Protocol protocol_) {
protocol = protocol_;
}
function triggers() public view override {
registerCallTrigger(this.assertionFunctionCallInputs.selector);
}
function assertionFunctionCallInputs() public {
PhEvm.CallInputs[] memory callInputs = ph.getCallInputs(address(protocol), protocol.transfer.selector);
for (uint256 i = 0; i < callInputs.length; i++) {
address from = callInputs[i].caller;
(address to, uint256 amount) = abi.decode(callInputs[i].input, (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++) {
ph.forkPreState();
preBalance = protocol.balances(changedAddresses[i]);
ph.forkPostState();
if (balanceChanges[changedAddresses[i]] > 0) {
absDiff = uint256(balanceChanges[changedAddresses[i]]);
require(protocol.balances(changedAddresses[i]) == preBalance + absDiff, "Balance change mismatch");
} else {
absDiff = uint256(balanceChanges[changedAddresses[i]] * -1);
require(protocol.balances(changedAddresses[i]) == preBalance - absDiff, "Balance change mismatch");
}
}
}
}