Skip to content

Commit

Permalink
zevm natspecs
Browse files Browse the repository at this point in the history
  • Loading branch information
skosito committed Jul 23, 2024
1 parent 63bfb60 commit 0837639
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 21 deletions.
80 changes: 66 additions & 14 deletions contracts/prototypes/zevm/GatewayZEVM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,19 @@ import "../../zevm/interfaces/zContract.sol";
import "./IGatewayZEVM.sol";
import "../../zevm/interfaces/IWZETA.sol";

// The GatewayZEVM contract is the endpoint to call smart contracts on omnichain
// The contract doesn't hold any funds and should never have active allowances
/// @title GatewayZEVM
/// @notice The GatewayZEVM contract is the endpoint to call smart contracts on omnichain.
/// @dev The contract doesn't hold any funds and should never have active allowances.
contract GatewayZEVM is IGatewayZEVMEvents, IGatewayZEVMErrors, Initializable, OwnableUpgradeable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
/// @notice Error indicating a zero address was provided.
error ZeroAddress();

/// @notice The constant address of the Fungible module.
address public constant FUNGIBLE_MODULE_ADDRESS = 0x735b14BB79463307AAcBED86DAf3322B1e6226aB;
/// @notice The address of the Zeta token.
address public zetaToken;

// @dev Only Fungible module address allowed modifier.
/// @dev Only Fungible module address allowed modifier.
modifier onlyFungible() {
if (msg.sender != FUNGIBLE_MODULE_ADDRESS) {
revert CallerIsNotFungibleModule();
Expand All @@ -43,13 +47,19 @@ contract GatewayZEVM is IGatewayZEVMEvents, IGatewayZEVMErrors, Initializable, O
zetaToken = _zetaToken;
}

/// @dev Authorizes the upgrade of the contract.
/// @param newImplementation The address of the new implementation.
function _authorizeUpgrade(address newImplementation) internal override onlyOwner() {}

/// @dev Receive function to receive ZETA from WETH9.withdraw().
receive() external payable {
if (msg.sender != zetaToken && msg.sender != FUNGIBLE_MODULE_ADDRESS) revert OnlyWZETAOrFungible();
}

/// @dev Internal function to withdraw ZRC20 tokens.
/// @param amount The amount of tokens to withdraw.
/// @param zrc20 The address of the ZRC20 token.
/// @return The gas fee for the withdrawal.
function _withdrawZRC20(uint256 amount, address zrc20) internal returns (uint256) {
(address gasZRC20, uint256 gasFee) = IZRC20(zrc20).withdrawGasFee();
if (!IZRC20(gasZRC20).transferFrom(msg.sender, FUNGIBLE_MODULE_ADDRESS, gasFee)) {
Expand All @@ -65,43 +75,61 @@ contract GatewayZEVM is IGatewayZEVMEvents, IGatewayZEVMErrors, Initializable, O
return gasFee;
}

/// @dev Internal function to transfer ZETA tokens.
/// @param amount The amount of tokens to transfer.
/// @param to The address to transfer the tokens to.
function _transferZETA(uint256 amount, address to) internal {
if (!IWETH9(zetaToken).transferFrom(msg.sender, address(this), amount)) revert FailedZetaSent();
IWETH9(zetaToken).withdraw(amount);
(bool sent, ) = to.call{value: amount}("");
if (!sent) revert FailedZetaSent();
}

// Withdraw ZRC20 tokens to external chain
/// @notice Withdraw ZRC20 tokens to 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.
function withdraw(bytes memory receiver, uint256 amount, address zrc20) external nonReentrant {
uint256 gasFee = _withdrawZRC20(amount, zrc20);
emit Withdrawal(msg.sender, zrc20, receiver, amount, gasFee, IZRC20(zrc20).PROTOCOL_FLAT_FEE(), "");
}

// Withdraw ZRC20 tokens and call smart contract on external chain
/// @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 nonReentrant {
uint256 gasFee = _withdrawZRC20(amount, zrc20);
emit Withdrawal(msg.sender, zrc20, receiver, amount, gasFee, IZRC20(zrc20).PROTOCOL_FLAT_FEE(), message);
}

// Withdraw ZETA to external chain
/// @notice Withdraw ZETA tokens to an external chain.
/// @param amount The amount of tokens to withdraw.
function withdraw(uint256 amount) external nonReentrant {
_transferZETA(amount, FUNGIBLE_MODULE_ADDRESS);
emit Withdrawal(msg.sender, address(zetaToken), abi.encodePacked(FUNGIBLE_MODULE_ADDRESS), amount, 0, 0, "");
}

// Withdraw ZETA and call smart contract on external chain
/// @notice Withdraw ZETA tokens and call a smart contract on an external chain.
/// @param amount The amount of tokens to withdraw.
/// @param message The calldata to pass to the contract call.
function withdrawAndCall(uint256 amount, bytes calldata message) external nonReentrant {
_transferZETA(amount, FUNGIBLE_MODULE_ADDRESS);
emit Withdrawal(msg.sender, address(zetaToken), abi.encodePacked(FUNGIBLE_MODULE_ADDRESS), amount, 0, 0, message);
}

// Call smart contract on external chain without asset transfer
/// @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, bytes calldata message) external nonReentrant {
emit Call(msg.sender, receiver, message);
}

// Deposit foreign coins into ZRC20
/// @notice Deposit foreign coins into ZRC20.
/// @param zrc20 The address of the ZRC20 token.
/// @param amount The amount of tokens to deposit.
/// @param target The target address to receive the deposited tokens.
function deposit(
address zrc20,
uint256 amount,
Expand All @@ -112,7 +140,12 @@ contract GatewayZEVM is IGatewayZEVMEvents, IGatewayZEVMErrors, Initializable, O
IZRC20(zrc20).deposit(target, amount);
}

// Execute user specified contract on ZEVM
/// @notice Execute a user-specified contract on ZEVM.
/// @param context The context of the cross-chain call.
/// @param zrc20 The address of the ZRC20 token.
/// @param amount The amount of tokens to transfer.
/// @param target The target contract to call.
/// @param message The calldata to pass to the contract call.
function execute(
zContext calldata context,
address zrc20,
Expand All @@ -123,7 +156,12 @@ contract GatewayZEVM is IGatewayZEVMEvents, IGatewayZEVMErrors, Initializable, O
UniversalContract(target).onCrossChainCall(context, zrc20, amount, message);
}

// Deposit foreign coins into ZRC20 and call user specified contract on ZEVM
/// @notice Deposit foreign coins into ZRC20 and call a user-specified contract on ZEVM.
/// @param context The context of the cross-chain call.
/// @param zrc20 The address of the ZRC20 token.
/// @param amount The amount of tokens to transfer.
/// @param target The target contract to call.
/// @param message The calldata to pass to the contract call.
function depositAndCall(
zContext calldata context,
address zrc20,
Expand All @@ -137,7 +175,11 @@ contract GatewayZEVM is IGatewayZEVMEvents, IGatewayZEVMErrors, Initializable, O
UniversalContract(target).onCrossChainCall(context, zrc20, amount, message);
}

// Deposit zeta and call user specified contract on ZEVM
/// @notice Deposit ZETA and call a user-specified contract on ZEVM.
/// @param context The context of the cross-chain call.
/// @param amount The amount of tokens to transfer.
/// @param target The target contract to call.
/// @param message The calldata to pass to the contract call.
function depositAndCall(
zContext calldata context,
uint256 amount,
Expand All @@ -150,7 +192,12 @@ contract GatewayZEVM is IGatewayZEVMEvents, IGatewayZEVMErrors, Initializable, O
UniversalContract(target).onCrossChainCall(context, zetaToken, amount, message);
}

// Revert user specified contract on ZEVM
/// @notice Revert a user-specified contract on ZEVM.
/// @param context The context of the revert call.
/// @param zrc20 The address of the ZRC20 token.
/// @param amount The amount of tokens to revert.
/// @param target The target contract to call.
/// @param message The calldata to pass to the contract call.
function executeRevert(
revertContext calldata context,
address zrc20,
Expand All @@ -161,7 +208,12 @@ contract GatewayZEVM is IGatewayZEVMEvents, IGatewayZEVMErrors, Initializable, O
UniversalContract(target).onRevert(context, zrc20, amount, message);
}

// Deposit foreign coins into ZRC20 and revert user specified contract on ZEVM
/// @notice Deposit foreign coins into ZRC20 and revert a user-specified contract on ZEVM.
/// @param context The context of the revert call.
/// @param zrc20 The address of the ZRC20 token.
/// @param amount The amount of tokens to revert.
/// @param target The target contract to call.
/// @param message The calldata to pass to the contract call.
function depositAndRevert(
revertContext calldata context,
address zrc20,
Expand Down
65 changes: 65 additions & 0 deletions contracts/prototypes/zevm/IGatewayZEVM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,44 @@ pragma solidity 0.8.7;

import "../../zevm/interfaces/zContract.sol";

/// @title IGatewayZEVM
/// @notice Interface for the GatewayZEVM contract.
/// @dev Defines functions for cross-chain interactions and token handling.
interface IGatewayZEVM {
/// @notice Withdraw ZRC20 tokens to 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.
function withdraw(bytes memory receiver, uint256 amount, address zrc20) 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;

/// @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, bytes calldata message) external;

/// @notice Deposit foreign coins into ZRC20.
/// @param zrc20 The address of the ZRC20 token.
/// @param amount The amount of tokens to deposit.
/// @param target The target address to receive the deposited tokens.
function deposit(
address zrc20,
uint256 amount,
address target
) external;

/// @notice Execute a user-specified contract on ZEVM.
/// @param context The context of the cross-chain call.
/// @param zrc20 The address of the ZRC20 token.
/// @param amount The amount of tokens to transfer.
/// @param target The target contract to call.
/// @param message The calldata to pass to the contract call.
function execute(
zContext calldata context,
address zrc20,
Expand All @@ -24,6 +49,12 @@ interface IGatewayZEVM {
bytes calldata message
) external;

/// @notice Deposit foreign coins into ZRC20 and call a user-specified contract on ZEVM.
/// @param context The context of the cross-chain call.
/// @param zrc20 The address of the ZRC20 token.
/// @param amount The amount of tokens to transfer.
/// @param target The target contract to call.
/// @param message The calldata to pass to the contract call.
function depositAndCall(
zContext calldata context,
address zrc20,
Expand All @@ -33,19 +64,53 @@ interface IGatewayZEVM {
) external;
}

/// @title IGatewayZEVMEvents
/// @notice Interface for the events emitted by the GatewayZEVM contract.
interface IGatewayZEVMEvents {
/// @notice Emitted when a cross-chain call is made.
/// @param sender The address of the sender.
/// @param receiver The receiver address on the external chain.
/// @param message The calldata passed to the contract call.
event Call(address indexed sender, bytes receiver, bytes message);

/// @notice Emitted when a withdrawal is made.
/// @param from The address from which the tokens are withdrawn.
/// @param zrc20 The address of the ZRC20 token.
/// @param to The receiver address on the external chain.
/// @param value The amount of tokens withdrawn.
/// @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.
event Withdrawal(address indexed from, address zrc20, bytes to, uint256 value, uint256 gasfee, uint256 protocolFlatFee, bytes message);
}

/// @title IGatewayZEVMErrors
/// @notice Interface for the errors used in the GatewayZEVM contract.
interface IGatewayZEVMErrors {
/// @notice Error indicating a withdrawal failure.
error WithdrawalFailed();

/// @notice Error indicating an insufficient ZRC20 token amount.
error InsufficientZRC20Amount();

/// @notice Error indicating a failure to burn ZRC20 tokens.
error ZRC20BurnFailed();

/// @notice Error indicating a failure to transfer ZRC20 tokens.
error ZRC20TransferFailed();

/// @notice Error indicating a failure to transfer gas fee.
error GasFeeTransferFailed();

/// @notice Error indicating that the caller is not the Fungible module.
error CallerIsNotFungibleModule();

/// @notice Error indicating an invalid target address.
error InvalidTarget();

/// @notice Error indicating a failure to send ZETA tokens.
error FailedZetaSent();

/// @notice Error indicating that only WZETA or the Fungible module can call the function.
error OnlyWZETAOrFungible();
}
27 changes: 22 additions & 5 deletions contracts/prototypes/zevm/SenderZEVM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,26 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IGatewayZEVM.sol";
import "../../zevm/interfaces/IZRC20.sol";

// @notice This contract is used just for testing
/// @title SenderZEVM
/// @notice This contract is used just for testing.
/// @dev Provides functions to call a receiver on EVM and to withdraw and call a receiver on EVM.
contract SenderZEVM {
/// @notice The address of the gateway contract.
address public gateway;

/// @notice Error indicating that the approval of tokens failed.
error ApprovalFailed();

constructor(address _gateway) {
gateway = _gateway;
}

// Call receiver on EVM
/// @notice Call a receiver on EVM.
/// @param receiver The address of the receiver on the external chain.
/// @param str A string parameter to pass to the receiver's function.
/// @param num A numeric parameter to pass to the receiver's function.
/// @param flag A boolean parameter to pass to the receiver's function.
/// @dev Encodes the function call and passes it to the gateway.
function callReceiver(bytes memory receiver, string memory str, uint256 num, bool flag) external {
// Encode the function call to the receiver's receivePayable method
bytes memory message = abi.encodeWithSignature("receivePayable(string,uint256,bool)", str, num, flag);
Expand All @@ -23,15 +33,22 @@ contract SenderZEVM {
IGatewayZEVM(gateway).call(receiver, message);
}

// Withdraw and call receiver on EVM
/// @notice Withdraw and call a receiver on EVM.
/// @param receiver The address of the receiver on the external chain.
/// @param amount The amount of tokens to withdraw.
/// @param zrc20 The address of the ZRC20 token.
/// @param str A string parameter to pass to the receiver's function.
/// @param num A numeric parameter to pass to the receiver's function.
/// @param flag A boolean parameter to pass to the receiver's function.
/// @dev Approves the gateway to withdraw tokens and encodes the function call to pass to the gateway.
function withdrawAndCallReceiver(bytes memory receiver, uint256 amount, address zrc20, string memory str, uint256 num, bool flag) external {
// Encode the function call to the receiver's receivePayable method
bytes memory message = abi.encodeWithSignature("receivePayable(string,uint256,bool)", str, num, flag);

// Approve gateway to withdraw
if(!IZRC20(zrc20).approve(gateway, amount)) revert ApprovalFailed();
if (!IZRC20(zrc20).approve(gateway, amount)) revert ApprovalFailed();

// Pass encoded call to gateway
IGatewayZEVM(gateway).withdrawAndCall(receiver, amount, zrc20, message);
}
}
}
Loading

0 comments on commit 0837639

Please sign in to comment.