Skip to content

Commit

Permalink
add revertoptions to zevm
Browse files Browse the repository at this point in the history
  • Loading branch information
skosito committed Aug 8, 2024
1 parent ce1e637 commit 003162a
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 60 deletions.
2 changes: 1 addition & 1 deletion v2/src/evm/ERC20Custody.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "./interfaces//IGatewayEVM.sol";
import "./interfaces/IERC20Custody.sol";
import { IGatewayEVM } from "./interfaces/IGatewayEVM.sol";

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
Expand Down
2 changes: 1 addition & 1 deletion v2/src/evm/ZetaConnectorBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

import "src/evm/interfaces/IGatewayEVM.sol";
import { IGatewayEVM, IGatewayEVMErrors, IGatewayEVMEvents } from "src/evm/interfaces/IGatewayEVM.sol";
import "src/evm/interfaces/IZetaConnector.sol";

/// @title ZetaConnectorBase
Expand Down
9 changes: 8 additions & 1 deletion v2/src/evm/interfaces/IGatewayEVM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,14 @@ interface IGatewayEVMEvents {
/// @param asset The address of the ERC20 token (zero address if ETH).
/// @param payload The calldata passed with the deposit.
/// @param revertOptions Revert options.
event Deposit(address indexed sender, address indexed receiver, uint256 amount, address asset, bytes payload, RevertOptions revertOptions);
event Deposit(
address indexed sender,
address indexed receiver,
uint256 amount,
address asset,
bytes payload,
RevertOptions revertOptions
);

/// @notice Emitted when an omnichain smart contract call is made without asset transfer.
/// @param sender The address of the sender.
Expand Down
73 changes: 63 additions & 10 deletions v2/src/zevm/GatewayZEVM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -117,24 +117,38 @@ contract GatewayZEVM is
/// @param receiver The receiver address on the external chain.
/// @param amount The amount of tokens to withdraw.
/// @param zrc20 The address of the ZRC20 token.
function withdraw(bytes memory receiver, uint256 amount, address zrc20) external nonReentrant whenNotPaused {
/// @param revertOptions Revert options.
function withdraw(
bytes memory receiver,
uint256 amount,
address zrc20,
RevertOptions calldata revertOptions
)
external
nonReentrant
whenNotPaused
{
if (receiver.length == 0) revert ZeroAddress();
if (amount == 0) revert InsufficientZRC20Amount();

uint256 gasFee = _withdrawZRC20(amount, zrc20);
emit Withdrawal(msg.sender, 0, receiver, zrc20, amount, gasFee, IZRC20(zrc20).PROTOCOL_FLAT_FEE(), "");
emit Withdrawal(
msg.sender, 0, receiver, zrc20, amount, gasFee, IZRC20(zrc20).PROTOCOL_FLAT_FEE(), "", revertOptions
);
}

/// @notice Withdraw ZRC20 tokens and call a smart contract on an external chain.
/// @param receiver The receiver address on the external chain.
/// @param amount The amount of tokens to withdraw.
/// @param zrc20 The address of the ZRC20 token.
/// @param message The calldata to pass to the contract call.
/// @param revertOptions Revert options.
function withdrawAndCall(
bytes memory receiver,
uint256 amount,
address zrc20,
bytes calldata message
bytes calldata message,
RevertOptions calldata revertOptions
)
external
nonReentrant
Expand All @@ -144,28 +158,49 @@ contract GatewayZEVM is
if (amount == 0) revert InsufficientZRC20Amount();

uint256 gasFee = _withdrawZRC20(amount, zrc20);
emit Withdrawal(msg.sender, 0, receiver, zrc20, amount, gasFee, IZRC20(zrc20).PROTOCOL_FLAT_FEE(), message);
emit Withdrawal(
msg.sender, 0, receiver, zrc20, amount, gasFee, IZRC20(zrc20).PROTOCOL_FLAT_FEE(), message, revertOptions
);
}

/// @notice Withdraw ZETA tokens to an external chain.
/// @param amount The amount of tokens to withdraw.
function withdraw(uint256 amount, uint256 chainId) external nonReentrant whenNotPaused {
/// @param revertOptions Revert options.
function withdraw(
uint256 amount,
uint256 chainId,
RevertOptions calldata revertOptions
)
external
nonReentrant
whenNotPaused
{
if (amount == 0) revert InsufficientZetaAmount();

_transferZETA(amount, FUNGIBLE_MODULE_ADDRESS);
emit Withdrawal(
msg.sender, chainId, abi.encodePacked(FUNGIBLE_MODULE_ADDRESS), address(zetaToken), amount, 0, 0, ""
msg.sender,
chainId,
abi.encodePacked(FUNGIBLE_MODULE_ADDRESS),
address(zetaToken),
amount,
0,
0,
"",
revertOptions
);
}

/// @notice Withdraw ZETA tokens and call a smart contract on an external chain.
/// @param amount The amount of tokens to withdraw.
/// @param chainId Chain id of the external chain.
/// @param message The calldata to pass to the contract call.
/// @param revertOptions Revert options.
function withdrawAndCall(
uint256 amount,
uint256 chainId,
bytes calldata message
bytes calldata message,
RevertOptions calldata revertOptions
)
external
nonReentrant
Expand All @@ -175,17 +210,35 @@ contract GatewayZEVM is

_transferZETA(amount, FUNGIBLE_MODULE_ADDRESS);
emit Withdrawal(
msg.sender, chainId, abi.encodePacked(FUNGIBLE_MODULE_ADDRESS), address(zetaToken), amount, 0, 0, message
msg.sender,
chainId,
abi.encodePacked(FUNGIBLE_MODULE_ADDRESS),
address(zetaToken),
amount,
0,
0,
message,
revertOptions
);
}

/// @notice Call a smart contract on an external chain without asset transfer.
/// @param receiver The receiver address on the external chain.
/// @param message The calldata to pass to the contract call.
function call(bytes memory receiver, uint256 chainId, bytes calldata message) external nonReentrant whenNotPaused {
/// @param revertOptions Revert options.
function call(
bytes memory receiver,
uint256 chainId,
bytes calldata message,
RevertOptions calldata revertOptions
)
external
nonReentrant
whenNotPaused
{
if (receiver.length == 0) revert ZeroAddress();

emit Call(msg.sender, chainId, receiver, message);
emit Call(msg.sender, chainId, receiver, message, revertOptions);
}

/// @notice Deposit foreign coins into ZRC20.
Expand Down
59 changes: 52 additions & 7 deletions v2/src/zevm/interfaces/IGatewayZEVM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ pragma solidity 0.8.26;

import "./zContract.sol";

/// @notice Struct containing revert options
/// @param revertAddress Address to receive revert.
/// @param callOnRevert Flag if onRevert hook should be called.
/// @pararm abortAddress Address to receive funds if aborted.
struct RevertOptions {
address revertAddress;
bool callOnRevert;
address abortAddress;
}

/// @title IGatewayZEVM
/// @notice Interface for the GatewayZEVM contract.
/// @dev Defines functions for cross-chain interactions and token handling.
Expand All @@ -11,30 +21,60 @@ interface IGatewayZEVM {
/// @param receiver The receiver address on the external chain.
/// @param amount The amount of tokens to withdraw.
/// @param zrc20 The address of the ZRC20 token.
function withdraw(bytes memory receiver, uint256 amount, address zrc20) external;
/// @param revertOptions Revert options.
function withdraw(
bytes memory receiver,
uint256 amount,
address zrc20,
RevertOptions calldata revertOptions
)
external;

/// @notice Withdraw ZETA tokens to an external chain.
/// @param amount The amount of tokens to withdraw.
function withdraw(uint256 amount, uint256 chainId) external;
/// @param revertOptions Revert options.
function withdraw(uint256 amount, uint256 chainId, RevertOptions calldata revertOptions) external;

/// @notice Withdraw ZRC20 tokens and call a smart contract on an external chain.
/// @param receiver The receiver address on the external chain.
/// @param amount The amount of tokens to withdraw.
/// @param zrc20 The address of the ZRC20 token.
/// @param message The calldata to pass to the contract call.
function withdrawAndCall(bytes memory receiver, uint256 amount, address zrc20, bytes calldata message) external;
/// @param revertOptions Revert options.
function withdrawAndCall(
bytes memory receiver,
uint256 amount,
address zrc20,
bytes calldata message,
RevertOptions calldata revertOptions
)
external;

/// @notice Withdraw ZETA tokens and call a smart contract on an external chain.
/// @param amount The amount of tokens to withdraw.
/// @param chainId Chain id of the external chain.
/// @param message The calldata to pass to the contract call.
function withdrawAndCall(uint256 amount, uint256 chainId, bytes calldata message) external;
/// @param revertOptions Revert options.
function withdrawAndCall(
uint256 amount,
uint256 chainId,
bytes calldata message,
RevertOptions calldata revertOptions
)
external;

/// @notice Call a smart contract on an external chain without asset transfer.
/// @param receiver The receiver address on the external chain.
/// @param chainId Chain id of the external chain.
/// @param message The calldata to pass to the contract call.
function call(bytes memory receiver, uint256 chainId, bytes calldata message) external;
/// @param revertOptions Revert options.
function call(
bytes memory receiver,
uint256 chainId,
bytes calldata message,
RevertOptions calldata revertOptions
)
external;

/// @notice Deposit foreign coins into ZRC20.
/// @param zrc20 The address of the ZRC20 token.
Expand Down Expand Up @@ -124,7 +164,10 @@ interface IGatewayZEVMEvents {
/// @param chainId Chain id of external chain.
/// @param receiver The receiver address on the external chain.
/// @param message The calldata passed to the contract call.
event Call(address indexed sender, uint256 indexed chainId, bytes receiver, bytes message);
/// @param revertOptions Revert options.
event Call(
address indexed sender, uint256 indexed chainId, bytes receiver, bytes message, RevertOptions revertOptions
);

/// @notice Emitted when a withdrawal is made.
/// @param sender The address from which the tokens are withdrawn.
Expand All @@ -135,6 +178,7 @@ interface IGatewayZEVMEvents {
/// @param gasfee The gas fee for the withdrawal.
/// @param protocolFlatFee The protocol flat fee for the withdrawal.
/// @param message The calldata passed to the contract call.
/// @param revertOptions Revert options.
event Withdrawal(
address indexed sender,
uint256 indexed chainId,
Expand All @@ -143,7 +187,8 @@ interface IGatewayZEVMEvents {
uint256 value,
uint256 gasfee,
uint256 protocolFlatFee,
bytes message
bytes message,
RevertOptions revertOptions
);
}

Expand Down
41 changes: 29 additions & 12 deletions v2/test/GatewayEVMZEVM.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,24 @@ import "./utils/ReceiverEVM.sol";

import "./utils/TestERC20.sol";
import "src/evm/ERC20Custody.sol";
import "src/evm/GatewayEVM.sol";
import { GatewayEVM } from "src/evm/GatewayEVM.sol";
import "src/evm/ZetaConnectorNonNative.sol";

import "./utils/SenderZEVM.sol";

import "./utils/SystemContractMock.sol";

import "src/zevm/GatewayZEVM.sol";
import { GatewayZEVM } from "src/zevm/GatewayZEVM.sol";
import { IGatewayZEVM } from "src/zevm/GatewayZEVM.sol";
import { IGatewayZEVMErrors } from "src/zevm/GatewayZEVM.sol";
import { IGatewayZEVMEvents } from "src/zevm/GatewayZEVM.sol";

import { IGatewayEVMErrors } from "src/evm/GatewayEVM.sol";
import { IGatewayEVMEvents } from "src/evm/GatewayEVM.sol";

import "src/zevm/ZRC20.sol";

import "./utils/IReceiverEVM.sol";
import "src/evm/interfaces/IGatewayEVM.sol";
import "src/zevm/interfaces/IGatewayZEVM.sol";

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
Expand Down Expand Up @@ -56,6 +61,8 @@ contract GatewayEVMZEVMTest is
ZRC20 zrc20;
address ownerZEVM;

RevertOptions revertOptions;

function setUp() public {
// evm
ownerEVM = address(this);
Expand Down Expand Up @@ -108,6 +115,9 @@ contract GatewayEVMZEVMTest is
zrc20.approve(address(gatewayZEVM), 1_000_000);

vm.deal(tssAddress, 1 ether);

revertOptions =
RevertOptions({ revertAddress: address(0x321), callOnRevert: true, abortAddress: address(0x321) });
}

function testCallReceiverEVMFromZEVM() public {
Expand All @@ -121,8 +131,8 @@ contract GatewayEVMZEVMTest is
bytes memory message = abi.encodeWithSelector(receiverEVM.receivePayable.selector, str, num, flag);
vm.prank(ownerZEVM);
vm.expectEmit(true, true, true, true, address(gatewayZEVM));
emit Call(address(ownerZEVM), chainId, abi.encodePacked(receiverEVM), message);
gatewayZEVM.call(abi.encodePacked(receiverEVM), chainId, message);
emit Call(address(ownerZEVM), chainId, abi.encodePacked(receiverEVM), message, revertOptions);
gatewayZEVM.call(abi.encodePacked(receiverEVM), chainId, message, revertOptions);

// Call execute on evm
vm.deal(address(gatewayEVM), value);
Expand All @@ -141,8 +151,13 @@ contract GatewayEVMZEVMTest is

// Encode the function call data and call on zevm
bytes memory message = abi.encodeWithSelector(receiverEVM.receivePayable.selector, str, num, flag);
bytes memory data =
abi.encodeWithSignature("call(bytes,uint256,bytes)", abi.encodePacked(receiverEVM), chainId, message);
bytes memory data = abi.encodeWithSignature(
"call(bytes,uint256,bytes,(address,bool,address))",
abi.encodePacked(receiverEVM),
chainId,
message,
revertOptions
);
vm.expectCall(address(gatewayZEVM), 0, data);
vm.prank(ownerZEVM);
senderZEVM.callReceiver(abi.encodePacked(receiverEVM), chainId, str, num, flag);
Expand Down Expand Up @@ -174,10 +189,11 @@ contract GatewayEVMZEVMTest is
1_000_000,
0,
zrc20.PROTOCOL_FLAT_FEE(),
message
message,
revertOptions
);
vm.prank(ownerZEVM);
gatewayZEVM.withdrawAndCall(abi.encodePacked(receiverEVM), 1_000_000, address(zrc20), message);
gatewayZEVM.withdrawAndCall(abi.encodePacked(receiverEVM), 1_000_000, address(zrc20), message, revertOptions);

// Check the balance after withdrawal
uint256 balanceOfAfterWithdrawal = zrc20.balanceOf(ownerZEVM);
Expand All @@ -203,11 +219,12 @@ contract GatewayEVMZEVMTest is
uint256 senderBalanceBeforeWithdrawal = IZRC20(zrc20).balanceOf(address(senderZEVM));
bytes memory message = abi.encodeWithSelector(receiverEVM.receivePayable.selector, str, num, flag);
bytes memory data = abi.encodeWithSignature(
"withdrawAndCall(bytes,uint256,address,bytes)",
"withdrawAndCall(bytes,uint256,address,bytes,(address,bool,address))",
abi.encodePacked(receiverEVM),
1_000_000,
address(zrc20),
message
message,
revertOptions
);
vm.expectCall(address(gatewayZEVM), 0, data);
vm.prank(ownerZEVM);
Expand Down
Loading

0 comments on commit 003162a

Please sign in to comment.