Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add relay handler #239

Open
wants to merge 7 commits into
base: sc-feat/interop-testing-campaign
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 100 additions & 59 deletions packages/contracts-bedrock/test/invariants/interop/helpers/Handler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { OptimismPortalInterop } from "src/L1/OptimismPortalInterop.sol";
import { ProxyAdmin } from "src/universal/ProxyAdmin.sol";

contract Handler is Setup {
error TargetCallFailed();

struct Message {
address from;
uint256 amount;
Expand Down Expand Up @@ -53,11 +55,10 @@ contract Handler is Setup {
Actors actor = randomActor(_actorIndex);
_amount = clampLte(_amount, SUPER_TOKEN.balanceOf(address(actor)));

try actor.directCall(
(bool success,) = actor.directCall(
address(SUPER_TOKEN), _ZERO_VALUE, abi.encodeWithSelector(SUPER_TOKEN.transfer.selector, _to, _amount)
) { } catch {
assert(false);
}
);
assert(success);
}

function handler_transferFromSuperchainERC20(
Expand All @@ -74,22 +75,20 @@ contract Handler is Setup {
_amount = clampLte(_amount, SUPER_TOKEN.balanceOf(address(fromActor)));

// Approve the spender to transfer the tokens
try fromActor.directCall(
(bool success,) = fromActor.directCall(
address(SUPER_TOKEN),
_ZERO_VALUE,
abi.encodeWithSelector(SUPER_TOKEN.approve.selector, address(callerActor), _amount)
) { } catch {
assert(false);
}
);
assert(success);

// Transfer the tokens
try callerActor.directCall(
(success,) = callerActor.directCall(
address(SUPER_TOKEN),
_ZERO_VALUE,
abi.encodeWithSelector(SUPER_TOKEN.transferFrom.selector, address(fromActor), _to, _amount)
) { } catch {
assert(false);
}
);
assert(success);
}

function handler_permitSuperchainERC20(
Expand Down Expand Up @@ -124,13 +123,14 @@ contract Handler is Setup {
uint256 callerActorBalanceBefore = SUPER_TOKEN.balanceOf(address(callerActor));

// Call transferFrom
try callerActor.directCall(
(bool success,) = callerActor.directCall(
address(SUPER_TOKEN),
_ZERO_VALUE,
abi.encodeWithSelector(SUPER_TOKEN.transferFrom.selector, fromEOA, address(callerActor), _amount)
) {
);
if (success) {
assert(SUPER_TOKEN.balanceOf(address(callerActor)) == callerActorBalanceBefore + _amount);
} catch {
} else {
assert(false);
}
}
Expand Down Expand Up @@ -171,35 +171,32 @@ contract Handler is Setup {
Actors actor = randomActor(_actorIndex);
vm.deal(address(actor), _value);

try actor.directCall(address(SUPER_WETH), _value, abi.encodeWithSelector(SUPER_WETH.deposit.selector)) {
_ghost_superWethBalancesSum += _value;
} catch {
assert(false);
}
(bool success,) =
actor.directCall(address(SUPER_WETH), _value, abi.encodeWithSelector(SUPER_WETH.deposit.selector));
assert(success);

_ghost_superWethBalancesSum += _value;
}

function handler_withdrawSuperchainWETH(uint256 _value, uint256 _actorIndex) public initialize {
Actors actor = randomActor(_actorIndex);
_value = clampLte(_value, SUPER_WETH.balanceOf(address(actor)));

try actor.directCall(
(bool success,) = actor.directCall(
address(SUPER_WETH), _ZERO_VALUE, abi.encodeWithSelector(SUPER_WETH.withdraw.selector, _value)
) {
_ghost_superWethBalancesSum -= _value;
} catch {
assert(false);
}
);
assert(success);
_ghost_superWethBalancesSum -= _value;
}

function handler_transferSuperchainWETH(address _to, uint256 _amount, uint256 _actorIndex) public initialize {
Actors actor = randomActor(_actorIndex);
_amount = clampLte(_amount, SUPER_WETH.balanceOf(address(actor)));

try actor.directCall(
(bool success,) = actor.directCall(
address(SUPER_WETH), _ZERO_VALUE, abi.encodeWithSelector(SUPER_WETH.transfer.selector, _to, _amount)
) { } catch {
assert(false);
}
);
assert(success);
}

function handler_transferFromSuperchainWETH(
Expand All @@ -225,13 +222,12 @@ contract Handler is Setup {
}

// Transfer the tokens
try callerActor.directCall(
(bool success,) = callerActor.directCall(
address(SUPER_WETH),
_ZERO_VALUE,
abi.encodeWithSelector(SUPER_WETH.transferFrom.selector, address(fromActor), _to, _amount)
) { } catch {
assert(false);
}
);
assert(success);
}

function handler_permit2SuperchainWETH(
Expand Down Expand Up @@ -277,17 +273,16 @@ contract Handler is Setup {
// Clamp the value to prevent an overflow or a revert due to insufficient balance
_value = clampLte(_value, Utils.min(type(uint256).max - sWethBalanceBefore, actorBalanceBefore));

try actor.directCall(
(bool success,) = actor.directCall(
address(SUPER_WETH), _value, abi.encodeCall(SUPER_WETH.sendETH, (_to, DESTINATION_CHAIN_ID))
) {
// Check the Ether balances and that the superchain WETH total supply was not modified
assert(actor.ethBalance() == actorBalanceBefore - _value);
assert(address(ETH_LIQUIDITY).balance == _ethLiquidityBefore + _value);
// The total supply of superchain WETH should not change
assert(SUPER_WETH.totalSupply() == _sWETHTotalSupplyBefore);
} catch {
assert(false);
}
);
assert(success);

// Check the Ether balances and that the superchain WETH total supply was not modified
assert(actor.ethBalance() == actorBalanceBefore - _value);
Comment on lines +281 to +282

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a blank space here between the call with its assert success, and the rest of assertions

assert(address(ETH_LIQUIDITY).balance == _ethLiquidityBefore + _value);
// The total supply of superchain WETH should not change
assert(SUPER_WETH.totalSupply() == _sWETHTotalSupplyBefore);
}

function handler_superchainWETHRelayETH(
Expand Down Expand Up @@ -328,15 +323,13 @@ contract Handler is Setup {
require(!L2_TO_L2_MESSENGER.successfulMessages(messageHash));

Actors actor = randomActor(_toActorIndex);
try actor.callL2ToL2MessengerRelayMessage(_id, sentMessage) {
// Check the Ether balances
assert(address(ETH_LIQUIDITY).balance == _ethLiquidityBefore - _message.amount);
assert(targetActor.balance == _tagretActorBalanceBefore + _message.amount);
// The total supply of superchain WETH should not change
assert(SUPER_WETH.totalSupply() == _sWETHTotalSupplyBefore);
} catch {
assert(false);
}
bool success = actor.callL2ToL2MessengerRelayMessage(_id, sentMessage);
assert(success);
// Check the Ether balances
assert(address(ETH_LIQUIDITY).balance == _ethLiquidityBefore - _message.amount);
assert(targetActor.balance == _tagretActorBalanceBefore + _message.amount);
// The total supply of superchain WETH should not change
assert(SUPER_WETH.totalSupply() == _sWETHTotalSupplyBefore);
}

function handler_migrateAndAddL1Dependency() public initialize {
Expand Down Expand Up @@ -412,11 +405,59 @@ contract Handler is Setup {
// Get balances before
uint256 actorBalanceBefore = actor.ethBalance();
uint256 portalBalanceBefore = address(PORTAL).balance;
try actor.directCall(address(PORTAL), _amount, abi.encodeWithSelector(PORTAL.donateETH.selector)) {
assert(actor.ethBalance() == actorBalanceBefore - _amount);
assert(address(PORTAL).balance == portalBalanceBefore + _amount);
} catch {
assert(false);
}

(bool success,) = actor.directCall(address(PORTAL), _amount, abi.encodeWithSelector(PORTAL.donateETH.selector));
assert(success);
assert(actor.ethBalance() == actorBalanceBefore - _amount);
assert(address(PORTAL).balance == portalBalanceBefore + _amount);
}

function handler_L2ToL2MessengerRelayMessage(
uint256 _relayerActorIndex,
uint256 _value,
Identifier memory _id,
address _target,
uint256 _nonce,
address _sender,
bytes memory _message
)
public
initialize
{
require(_target != address(CROSS_L2_INBOX));
require(_target != address(L2_TO_L2_MESSENGER));
require(!_isL1Contract(_target));
require(!_isL1Contract(_sender));

// Ensure the id inputs are valid
_id.origin = address(L2_TO_L2_MESSENGER);
_id.chainId == block.chainid;

// Build the message
bytes memory sentMessage = abi.encodePacked(
abi.encode(_SENT_MESSAGE_EVENT_SELECTOR, block.chainid, _target, _nonce), // topics
abi.encode(_sender, _message) // data
);

// Ensure the message is not already relayed
bytes32 messageHash = Hashing.hashL2toL2CrossDomainMessage({
_destination: block.chainid,
_source: _id.chainId,
_nonce: _nonce,
_sender: _sender,
_target: _target,
_message: _message
});
require(!L2_TO_L2_MESSENGER.successfulMessages(messageHash));

Actors actor = randomActor(_relayerActorIndex);
_value = clampLte(_value, actor.ethBalance());
(bool success, bytes memory returnData) = actor.directCall(
address(L2_TO_L2_MESSENGER),
_value,
abi.encodeWithSelector(L2_TO_L2_MESSENGER.relayMessage.selector, _id, sentMessage)
);
if (success) assert(L2_TO_L2_MESSENGER.successfulMessages(messageHash));
else assert(bytes4(returnData) == TargetCallFailed.selector);
}
}