Skip to content

Commit

Permalink
Merge pull request #430 from FastLane-Labs/escrow-test-refactor
Browse files Browse the repository at this point in the history
Improve `Escrow.t.sol` hook input and revert tests
  • Loading branch information
BenSparksCode authored Sep 18, 2024
2 parents 5605e76 + 1c476d9 commit cca606d
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 50 deletions.
61 changes: 38 additions & 23 deletions test/Escrow.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,10 @@ contract EscrowTest is BaseTest {
.withAllowAllocateValueFailure(true) // Allow the value allocation to fail
.build()
);
executeHookCase(false, block.timestamp * 2, noError);

(UserOperation memory userOp,,) = executeHookCase(block.timestamp * 2, noError);
bytes memory expectedInput = abi.encode(userOp);
assertEq(expectedInput, dAppControl.preOpsInputData(), "preOpsInputData should match expectedInput");
}

// Ensure metacall reverts with the proper error when the preOps hook reverts.
Expand All @@ -154,7 +157,8 @@ contract EscrowTest is BaseTest {
.withReuseUserOp(true) // Allow metacall to revert
.build()
);
executeHookCase(true, 0, AtlasErrors.PreOpsFail.selector);
dAppControl.setPreOpsShouldRevert(true);
executeHookCase(0, AtlasErrors.PreOpsFail.selector);
}

// Ensure the user operation executes successfully. To ensure the operation's returned data is as expected, we
Expand All @@ -168,7 +172,9 @@ contract EscrowTest is BaseTest {
.withAllowAllocateValueFailure(true) // Allow the value allocation to fail
.build()
);
executeHookCase(false, block.timestamp * 3, noError);
executeHookCase(block.timestamp * 3, noError);
bytes memory expectedInput = abi.encode(block.timestamp * 3);
assertEq(expectedInput, dAppControl.userOpInputData(), "userOpInputData should match expectedInput");
}

// Ensure metacall reverts with the proper error when the user operation reverts.
Expand All @@ -178,7 +184,8 @@ contract EscrowTest is BaseTest {
.withReuseUserOp(true) // Allow metacall to revert
.build()
);
executeHookCase(true, 0, AtlasErrors.UserOpFail.selector);
dAppControl.setUserOpShouldRevert(true);
executeHookCase(0, AtlasErrors.UserOpFail.selector);
}

// Ensure metacall reverts with the proper error when the allocateValue hook reverts.
Expand All @@ -193,8 +200,7 @@ contract EscrowTest is BaseTest {
);

dAppControl.setAllocateValueShouldRevert(true);

executeHookCase(false, 1, AtlasErrors.AllocateValueFail.selector);
executeHookCase(1, AtlasErrors.AllocateValueFail.selector);
}

// Ensure the postOps hook is successfully called. No return data is expected from the postOps hook, so we do not
Expand All @@ -205,7 +211,9 @@ contract EscrowTest is BaseTest {
.withRequirePostOps(true) // Execute the postOps hook
.build()
);
executeHookCase(false, 0, noError);
executeHookCase(0, noError);
bytes memory expectedInput = abi.encode(true, new bytes(0));
assertEq(expectedInput, dAppControl.postOpsInputData(), "postOpsInputData should match expectedInput");
}

// Ensure metacall reverts with the proper error when the postOps hook reverts.
Expand All @@ -219,7 +227,8 @@ contract EscrowTest is BaseTest {
.withAllowAllocateValueFailure(true) // Allow the value allocation to fail
.build()
);
executeHookCase(false, 1, AtlasErrors.PostOpsFail.selector);
dAppControl.setPostOpsShouldRevert(true);
executeHookCase(1, AtlasErrors.PostOpsFail.selector);
}

// Ensure the allocateValue hook is successfully called. No return data is expected from the allocateValue hook, so
Expand All @@ -231,35 +240,37 @@ contract EscrowTest is BaseTest {
.withTrackUserReturnData(true) // Track the user operation's return data
.build()
);
uint256 userOpArg = 321;

vm.prank(userEOA);
address executionEnvironment = atlas.createExecutionEnvironment(userEOA, address(dAppControl));
executeHookCase(userOpArg, noError);

vm.expectEmit(false, false, false, true, executionEnvironment);
emit MEVPaymentSuccess(address(0), defaultBidAmount);
this.executeHookCase(false, 0, noError);
bytes memory expectedInput = abi.encode(address(0), defaultBidAmount, abi.encode(userOpArg));
assertEq(expectedInput, dAppControl.allocateValueInputData(), "allocateValueInputData should match expectedInput");
}

function executeHookCase(bool hookShouldRevert, uint256 expectedHookReturnValue, bytes4 expectedError) public {
function executeHookCase(uint256 expectedHookReturnValue, bytes4 expectedError) public returns(
UserOperation memory userOp,
SolverOperation[] memory solverOps,
DAppOperation memory dappOp
) {
bool revertExpected = expectedError != noError;

UserOperation memory userOp = validUserOperation(address(dAppControl))
userOp = validUserOperation(address(dAppControl))
.withData(
abi.encodeWithSelector(
dAppControl.userOperationCall.selector,
hookShouldRevert,
expectedHookReturnValue
)
)
.signAndBuild(address(atlasVerification), userPK);

SolverOperation[] memory solverOps = new SolverOperation[](1);
solverOps = new SolverOperation[](1);
solverOps[0] = validSolverOperation(userOp)
.withBidAmount(defaultBidAmount)
.withData(abi.encode(expectedHookReturnValue))
.signAndBuild(address(atlasVerification), solverOnePK);

DAppOperation memory dappOp = validDAppOperation(userOp, solverOps).build();
dappOp = validDAppOperation(userOp, solverOps).build();

if (revertExpected) {
vm.expectRevert(expectedError);
Expand Down Expand Up @@ -385,7 +396,7 @@ contract EscrowTest is BaseTest {
);

UserOperation memory userOp = validUserOperation(address(dAppControl))
.withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, false, 1))
.withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, 1))
.signAndBuild(address(atlasVerification), userPK);

SolverOperation[] memory solverOps = new SolverOperation[](1);
Expand All @@ -394,6 +405,8 @@ contract EscrowTest is BaseTest {
.signAndBuild(address(atlasVerification), solverOnePK);

uint256 result = (1 << uint256(SolverOutcome.PreSolverFailed));
dAppControl.setPreSolverShouldRevert(true);

executeSolverOperationCase(userOp, solverOps, false, false, result, true);
}

Expand All @@ -409,7 +422,7 @@ contract EscrowTest is BaseTest {
);

UserOperation memory userOp = validUserOperation(address(dAppControl))
.withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, false, 1))
.withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, 1))
.signAndBuild(address(atlasVerification), userPK);

SolverOperation[] memory solverOps = new SolverOperation[](1);
Expand All @@ -418,6 +431,8 @@ contract EscrowTest is BaseTest {
.signAndBuild(address(atlasVerification), solverOnePK);

uint256 result = (1 << uint256(SolverOutcome.PostSolverFailed));
dAppControl.setPostSolverShouldRevert(true);

executeSolverOperationCase(userOp, solverOps, true, false, result, true);
}

Expand Down Expand Up @@ -508,7 +523,7 @@ contract EscrowTest is BaseTest {
);

userOp = validUserOperation(address(dAppControl))
.withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, false, expectedDataValue))
.withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, expectedDataValue))
.signAndBuild(address(atlasVerification), userPK);

solverOps[0] = validSolverOperation(userOp)
Expand Down Expand Up @@ -539,7 +554,7 @@ contract EscrowTest is BaseTest {
);

userOp = validUserOperation(address(dAppControl))
.withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, false, dataValue))
.withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, dataValue))
.signAndBuild(address(atlasVerification), userPK);

solverOps[0] = validSolverOperation(userOp)
Expand All @@ -565,7 +580,7 @@ contract EscrowTest is BaseTest {
defaultAtlasWithCallConfig(callConfig);

userOp = validUserOperation(address(dAppControl))
.withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, false, 0))
.withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, 0))
.signAndBuild(address(atlasVerification), userPK);

solverOps = new SolverOperation[](1);
Expand Down
78 changes: 51 additions & 27 deletions test/base/DummyDAppControl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ contract DummyDAppControl is DAppControl {
bool public allocateValueShouldRevert;
bool public postOpsShouldRevert;

bytes public preOpsInputData;
bytes public userOpInputData;
bytes public preSolverInputData;
bytes public postSolverInputData;
bytes public allocateValueInputData;
bytes public postOpsInputData;

event MEVPaymentSuccess(address bidToken, uint256 bidAmount);

constructor(
Expand All @@ -38,38 +45,37 @@ contract DummyDAppControl is DAppControl {
function _checkUserOperation(UserOperation memory) internal pure virtual override { }

function _preOpsCall(UserOperation calldata userOp) internal virtual override returns (bytes memory) {
if (userOp.data.length == 0) {
return new bytes(0);
}
bool shouldRevert = DummyDAppControl(CONTROL).preOpsShouldRevert();
require(!shouldRevert, "_preOpsCall revert requested");

DummyDAppControl(CONTROL).setInputData(abi.encode(userOp), 0);
console.logBytes(abi.encode(userOp));

if (userOp.data.length == 0) return new bytes(0);

(bool success, bytes memory data) = address(userOp.dapp).call(userOp.data);
require(success, "_preOpsCall reverted");
(, bytes memory data) = address(userOp.dapp).call(userOp.data);
return data;
}

function _postOpsCall(bool, bytes calldata data) internal pure virtual override {
if (data.length == 0) return;

(bool shouldRevert) = abi.decode(data, (bool));
function _postOpsCall(bool solved, bytes calldata data) internal virtual override {
bool shouldRevert = DummyDAppControl(CONTROL).postOpsShouldRevert();
require(!shouldRevert, "_postOpsCall revert requested");
}

function _preSolverCall(SolverOperation calldata, bytes calldata returnData) internal view virtual override {
if (returnData.length == 0) {
return;
}
DummyDAppControl(CONTROL).setInputData(abi.encode(solved, data), 5);
}

(bool shouldRevert) = abi.decode(returnData, (bool));
function _preSolverCall(SolverOperation calldata solverOp, bytes calldata returnData) internal virtual override {
bool shouldRevert = DummyDAppControl(CONTROL).preSolverShouldRevert();
require(!shouldRevert, "_preSolverCall revert requested");
}

function _postSolverCall(SolverOperation calldata, bytes calldata returnData) internal pure virtual override {
if (returnData.length == 0) {
return;
}
DummyDAppControl(CONTROL).setInputData(abi.encode(solverOp, returnData), 2);
}

(bool shouldRevert) = abi.decode(returnData, (bool));
function _postSolverCall(SolverOperation calldata solverOp, bytes calldata returnData) internal virtual override {
bool shouldRevert = DummyDAppControl(CONTROL).postSolverShouldRevert();
require(!shouldRevert, "_postSolverCall revert requested");

DummyDAppControl(CONTROL).setInputData(abi.encode(solverOp, returnData), 3);
}

function _allocateValueCall(
Expand All @@ -81,13 +87,12 @@ contract DummyDAppControl is DAppControl {
virtual
override
{
if (data.length == 0) {
return;
}

bool shouldRevert = DummyDAppControl(CONTROL).allocateValueShouldRevert();
require(!shouldRevert, "_allocateValueCall revert requested");
emit MEVPaymentSuccess(bidToken, winningAmount);

DummyDAppControl(CONTROL).setInputData(abi.encode(bidToken, winningAmount, data), 4);

// emit MEVPaymentSuccess(bidToken, winningAmount);
}

function getBidValue(SolverOperation calldata solverOp) public view virtual override returns (uint256) {
Expand All @@ -100,8 +105,12 @@ contract DummyDAppControl is DAppControl {
// Custom functions
// ****************************************

function userOperationCall(bool shouldRevert, uint256 returnValue) public pure returns (uint256) {
function userOperationCall(uint256 returnValue) public returns (uint256) {
bool shouldRevert = DummyDAppControl(CONTROL).userOpShouldRevert();
require(!shouldRevert, "userOperationCall revert requested");

DummyDAppControl(CONTROL).setInputData(abi.encode(returnValue), 1);

return returnValue;
}

Expand Down Expand Up @@ -130,4 +139,19 @@ contract DummyDAppControl is DAppControl {
function setPostOpsShouldRevert(bool _postOpsShouldRevert) public {
postOpsShouldRevert = _postOpsShouldRevert;
}

// Called by the EE to save input data for testing after the metacall ends
function setInputData(
bytes memory inputData,
uint256 hook // 0: preOps, 1: userOp, 2: preSolver, 3: postSolver, 4: allocateValue, 5: postOps
)
public
{
if (hook == 0) preOpsInputData = inputData;
if (hook == 1) userOpInputData = inputData;
if (hook == 2) preSolverInputData = inputData;
if (hook == 3) postSolverInputData = inputData;
if (hook == 4) allocateValueInputData = inputData;
if (hook == 5) postOpsInputData = inputData;
}
}

0 comments on commit cca606d

Please sign in to comment.