diff --git a/v2/contracts/evm/GatewayEVM.sol b/v2/contracts/evm/GatewayEVM.sol index 14a9954c..c690aaf2 100644 --- a/v2/contracts/evm/GatewayEVM.sol +++ b/v2/contracts/evm/GatewayEVM.sol @@ -42,6 +42,8 @@ contract GatewayEVM is bytes32 public constant ASSET_HANDLER_ROLE = keccak256("ASSET_HANDLER_ROLE"); /// @notice New role identifier for pauser role. bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + /// @notice Max payload size. + uint256 public constant MAX_PAYLOAD_SIZE = 512; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { @@ -294,6 +296,7 @@ contract GatewayEVM is { if (msg.value == 0) revert InsufficientETHAmount(); if (receiver == address(0)) revert ZeroAddress(); + if (payload.length + revertOptions.revertMessage.length >= MAX_PAYLOAD_SIZE) revert PayloadSizeExceeded(); (bool deposited,) = tssAddress.call{ value: msg.value }(""); @@ -321,6 +324,7 @@ contract GatewayEVM is { if (amount == 0) revert InsufficientERC20Amount(); if (receiver == address(0)) revert ZeroAddress(); + if (payload.length + revertOptions.revertMessage.length >= MAX_PAYLOAD_SIZE) revert PayloadSizeExceeded(); transferFromToAssetHandler(msg.sender, asset, amount); @@ -341,6 +345,8 @@ contract GatewayEVM is nonReentrant { if (receiver == address(0)) revert ZeroAddress(); + if (payload.length + revertOptions.revertMessage.length >= MAX_PAYLOAD_SIZE) revert PayloadSizeExceeded(); + emit Called(msg.sender, receiver, payload, revertOptions); } diff --git a/v2/contracts/evm/interfaces/IGatewayEVM.sol b/v2/contracts/evm/interfaces/IGatewayEVM.sol index a1f22a03..08ac2307 100644 --- a/v2/contracts/evm/interfaces/IGatewayEVM.sol +++ b/v2/contracts/evm/interfaces/IGatewayEVM.sol @@ -90,6 +90,9 @@ interface IGatewayEVMErrors { /// @notice Error when trying to call onRevert method using arbitrary call. error NotAllowedToCallOnRevert(); + + /// @notice Error indicating payload size exceeded in external functions. + error PayloadSizeExceeded(); } /// @title IGatewayEVM diff --git a/v2/contracts/zevm/GatewayZEVM.sol b/v2/contracts/zevm/GatewayZEVM.sol index 3343aac7..035c26e5 100644 --- a/v2/contracts/zevm/GatewayZEVM.sol +++ b/v2/contracts/zevm/GatewayZEVM.sol @@ -36,6 +36,9 @@ contract GatewayZEVM is /// @notice New role identifier for pauser role. bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + /// @notice Max message size. + uint256 public constant MAX_MESSAGE_SIZE = 512; + /// @dev Only protocol address allowed modifier. modifier onlyProtocol() { if (msg.sender != PROTOCOL_ADDRESS) { @@ -178,6 +181,7 @@ contract GatewayZEVM is if (receiver.length == 0) revert ZeroAddress(); if (amount == 0) revert InsufficientZRC20Amount(); if (gasLimit == 0) revert InsufficientGasLimit(); + if (message.length + revertOptions.revertMessage.length >= MAX_MESSAGE_SIZE) revert MessageSizeExceeded(); uint256 gasFee = _withdrawZRC20WithGasLimit(amount, zrc20, gasLimit); emit Withdrawn( @@ -216,6 +220,7 @@ contract GatewayZEVM is if (receiver.length == 0) revert ZeroAddress(); if (amount == 0) revert InsufficientZRC20Amount(); if (callOptions.gasLimit == 0) revert InsufficientGasLimit(); + if (message.length + revertOptions.revertMessage.length >= MAX_MESSAGE_SIZE) revert MessageSizeExceeded(); uint256 gasFee = _withdrawZRC20WithGasLimit(amount, zrc20, callOptions.gasLimit); emit Withdrawn( @@ -283,6 +288,7 @@ contract GatewayZEVM is { if (receiver.length == 0) revert ZeroAddress(); if (amount == 0) revert InsufficientZetaAmount(); + if (message.length + revertOptions.revertMessage.length >= MAX_MESSAGE_SIZE) revert MessageSizeExceeded(); _transferZETA(amount, PROTOCOL_ADDRESS); emit Withdrawn( @@ -321,6 +327,7 @@ contract GatewayZEVM is if (receiver.length == 0) revert ZeroAddress(); if (amount == 0) revert InsufficientZetaAmount(); if (callOptions.gasLimit == 0) revert InsufficientGasLimit(); + if (message.length + revertOptions.revertMessage.length >= MAX_MESSAGE_SIZE) revert MessageSizeExceeded(); _transferZETA(amount, PROTOCOL_ADDRESS); emit Withdrawn( @@ -346,6 +353,7 @@ contract GatewayZEVM is whenNotPaused { if (callOptions.gasLimit == 0) revert InsufficientGasLimit(); + if (message.length + revertOptions.revertMessage.length >= MAX_MESSAGE_SIZE) revert MessageSizeExceeded(); _call(receiver, zrc20, message, callOptions, revertOptions); } @@ -368,6 +376,7 @@ contract GatewayZEVM is whenNotPaused { if (gasLimit == 0) revert InsufficientGasLimit(); + if (message.length + revertOptions.revertMessage.length >= MAX_MESSAGE_SIZE) revert MessageSizeExceeded(); _call(receiver, zrc20, message, CallOptions({ gasLimit: gasLimit, isArbitraryCall: true }), revertOptions); } @@ -382,7 +391,6 @@ contract GatewayZEVM is internal { if (receiver.length == 0) revert ZeroAddress(); - if (message.length == 0) revert EmptyMessage(); (address gasZRC20, uint256 gasFee) = IZRC20(zrc20).withdrawGasFeeWithGasLimit(callOptions.gasLimit); if (!IZRC20(gasZRC20).transferFrom(msg.sender, PROTOCOL_ADDRESS, gasFee)) { diff --git a/v2/contracts/zevm/interfaces/IGatewayZEVM.sol b/v2/contracts/zevm/interfaces/IGatewayZEVM.sol index db7eeb58..11fc2010 100644 --- a/v2/contracts/zevm/interfaces/IGatewayZEVM.sol +++ b/v2/contracts/zevm/interfaces/IGatewayZEVM.sol @@ -84,11 +84,11 @@ interface IGatewayZEVMErrors { /// @notice Error indicating that only WZETA or the protocol address can call the function. error OnlyWZETAOrProtocol(); - /// @notice Error indicating call method received empty message as argument. - error EmptyMessage(); - /// @notice Error indicating an insufficient gas limit. error InsufficientGasLimit(); + + /// @notice Error indicating message size exceeded in external functions. + error MessageSizeExceeded(); } /// @title IGatewayZEVM diff --git a/v2/test/GatewayEVM.t.sol b/v2/test/GatewayEVM.t.sol index 6e0d1c29..60b02ffa 100644 --- a/v2/test/GatewayEVM.t.sol +++ b/v2/test/GatewayEVM.t.sol @@ -507,6 +507,17 @@ contract GatewayEVMInboundTest is Test, IGatewayEVMErrors, IGatewayEVMEvents, IR gateway.depositAndCall(destination, amount, address(token), payload, revertOptions); } + function testDepositERC20ToCustodyWithPayloadFailsIfPayloadSizeExceeded() public { + uint256 amount = 100_000; + bytes memory payload = new bytes(256); + revertOptions.revertMessage = new bytes(256); + + token.approve(address(gateway), amount); + + vm.expectRevert(PayloadSizeExceeded.selector); + gateway.depositAndCall(destination, amount, address(token), payload, revertOptions); + } + function testDepositERC20ToCustodyWithPayload() public { uint256 amount = 100_000; uint256 custodyBalanceBefore = token.balanceOf(address(custody)); @@ -558,6 +569,15 @@ contract GatewayEVMInboundTest is Test, IGatewayEVMErrors, IGatewayEVMEvents, IR assertEq(tssBalanceBefore + amount, tssBalanceAfter); } + function testDepositEthToTssWithPayloadFailsIfPayloadSizeExceeded() public { + uint256 amount = 100_000; + bytes memory payload = new bytes(256); + revertOptions.revertMessage = new bytes(256); + + vm.expectRevert(PayloadSizeExceeded.selector); + gateway.depositAndCall{ value: amount }(destination, payload, revertOptions); + } + function testFailDepositEthToTssWithPayloadIfAmountIs0() public { uint256 amount = 0; bytes memory payload = abi.encodeWithSignature("hello(address)", destination); @@ -582,6 +602,14 @@ contract GatewayEVMInboundTest is Test, IGatewayEVMErrors, IGatewayEVMEvents, IR gateway.call(destination, payload, revertOptions); } + function testCallWithPayloadFailsIfPayloadSizeExceeded() public { + bytes memory payload = new bytes(256); + revertOptions.revertMessage = new bytes(256); + + vm.expectRevert(PayloadSizeExceeded.selector); + gateway.call(destination, payload, revertOptions); + } + function testCallWithPayloadFailsIfDestinationIsZeroAddress() public { bytes memory payload = abi.encodeWithSignature("hello(address)", destination); diff --git a/v2/test/GatewayZEVM.t.sol b/v2/test/GatewayZEVM.t.sol index 19d3f522..d2b153f1 100644 --- a/v2/test/GatewayZEVM.t.sol +++ b/v2/test/GatewayZEVM.t.sol @@ -152,6 +152,14 @@ contract GatewayZEVMInboundTest is Test, IGatewayZEVMEvents, IGatewayZEVMErrors gateway.withdrawAndCall(abi.encodePacked(""), 1, address(zrc20), message, 1, revertOptions); } + function testWithdrawAndCallZRC20FailsIfMessageSizeExceeded() public { + bytes memory message = new bytes(256); + revertOptions.revertMessage = new bytes(256); + + vm.expectRevert(MessageSizeExceeded.selector); + gateway.withdrawAndCall(abi.encodePacked(addr1), 1, address(zrc20), message, 1, revertOptions); + } + function testWithdrawAndCallZRC20FailsIfGasLimitIsZero() public { bytes memory message = abi.encodeWithSignature("hello(address)", addr1); vm.expectRevert(InsufficientGasLimit.selector); @@ -213,6 +221,13 @@ contract GatewayZEVMInboundTest is Test, IGatewayZEVMEvents, IGatewayZEVMErrors gateway.withdrawAndCall(abi.encodePacked(""), 1, address(zrc20), message, callOptions, revertOptions); } + function testWithdrawAndCallZRC20WithCallOptsFailsIfMessageSizeExceeded() public { + bytes memory message = new bytes(256); + revertOptions.revertMessage = new bytes(256); + vm.expectRevert(MessageSizeExceeded.selector); + gateway.withdrawAndCall(abi.encodePacked(addr1), 1, address(zrc20), message, callOptions, revertOptions); + } + function testWithdrawAndCallZRC20WithCallOptsFailsIfGasLimitIsZero() public { bytes memory message = abi.encodeWithSignature("hello(address)", addr1); callOptions.gasLimit = 0; @@ -285,6 +300,13 @@ contract GatewayZEVMInboundTest is Test, IGatewayZEVMEvents, IGatewayZEVMErrors gateway.withdrawAndCall(abi.encodePacked(addr1), 0, 1, message, revertOptions); } + function testWithdrawAndCallZETAFailsIfMessageSizeExceeded() public { + bytes memory message = new bytes(256); + revertOptions.revertMessage = new bytes(256); + vm.expectRevert(MessageSizeExceeded.selector); + gateway.withdrawAndCall(abi.encodePacked(addr1), 1, 1, message, revertOptions); + } + function testWithdrawAndCallZETAFailsIfAmountIsReceiverIsZeroAddress() public { bytes memory message = abi.encodeWithSignature("hello(address)", addr1); vm.expectRevert(ZeroAddress.selector); @@ -297,6 +319,13 @@ contract GatewayZEVMInboundTest is Test, IGatewayZEVMEvents, IGatewayZEVMErrors gateway.withdrawAndCall(abi.encodePacked(addr1), 0, 1, message, callOptions, revertOptions); } + function testWithdrawAndCallZETAWithCallOptsFailsIfMessageSizeExceeded() public { + bytes memory message = new bytes(256); + revertOptions.revertMessage = new bytes(256); + vm.expectRevert(MessageSizeExceeded.selector); + gateway.withdrawAndCall(abi.encodePacked(addr1), 1, 1, message, callOptions, revertOptions); + } + function testWithdrawAndCallZETAWithCallOptsFailsIfAmountIsReceiverIsZeroAddress() public { bytes memory message = abi.encodeWithSignature("hello(address)", addr1); vm.expectRevert(ZeroAddress.selector); @@ -508,6 +537,13 @@ contract GatewayZEVMInboundTest is Test, IGatewayZEVMEvents, IGatewayZEVMErrors gateway.call(abi.encodePacked(""), address(zrc20), message, 1, revertOptions); } + function testCallFailsIfMessageSizeExceeded() public { + bytes memory message = new bytes(256); + revertOptions.revertMessage = new bytes(256); + vm.expectRevert(MessageSizeExceeded.selector); + gateway.call(abi.encodePacked(addr1), address(zrc20), message, 1, revertOptions); + } + function testCallFailsIfGasLimitIsZero() public { bytes memory message = abi.encodeWithSignature("hello(address)", addr1); vm.expectRevert(InsufficientGasLimit.selector); @@ -528,6 +564,13 @@ contract GatewayZEVMInboundTest is Test, IGatewayZEVMEvents, IGatewayZEVMErrors gateway.call(abi.encodePacked(""), address(zrc20), message, callOptions, revertOptions); } + function testCallWithCallOptsFailsIfMessageSizeExceeded() public { + bytes memory message = new bytes(256); + revertOptions.revertMessage = new bytes(256); + vm.expectRevert(MessageSizeExceeded.selector); + gateway.call(abi.encodePacked(addr1), address(zrc20), message, callOptions, revertOptions); + } + function testCallWithCallOptsFailsIfGasLimitIsZero() public { bytes memory message = abi.encodeWithSignature("hello(address)", addr1); callOptions.gasLimit = 0;