diff --git a/v2/src/evm/ERC20Custody.sol b/v2/src/evm/ERC20Custody.sol index b7e948e2..387754fb 100644 --- a/v2/src/evm/ERC20Custody.sol +++ b/v2/src/evm/ERC20Custody.sol @@ -17,10 +17,7 @@ contract ERC20Custody is IERC20CustodyEvents, IERC20CustodyErrors, ReentrancyGua using SafeERC20 for IERC20; /// @notice Gateway contract. - IGatewayEVM public gateway; - /// @notice TSS address. - address public tssAddress; - + IGatewayEVM public immutable gateway; /// @notice New role identifier for pauser role. bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); /// @notice New role identifier for withdrawer role. @@ -29,11 +26,10 @@ contract ERC20Custody is IERC20CustodyEvents, IERC20CustodyErrors, ReentrancyGua /// @notice Constructor for ERC20Custody. /// @dev Set admin as default admin and pauser, and tssAddress as tss role. constructor(address _gateway, address _tssAddress, address _admin) { - if (_gateway == address(0) || _tssAddress == address(0)) { + if (_gateway == address(0) || _tssAddress == address(0) || _admin == address(0)) { revert ZeroAddress(); } gateway = IGatewayEVM(_gateway); - tssAddress = _tssAddress; _grantRole(DEFAULT_ADMIN_ROLE, _admin); _grantRole(PAUSER_ROLE, _admin); _grantRole(WITHDRAWER_ROLE, _tssAddress); diff --git a/v2/src/evm/GatewayEVM.sol b/v2/src/evm/GatewayEVM.sol index 3959fae7..b110cd23 100644 --- a/v2/src/evm/GatewayEVM.sol +++ b/v2/src/evm/GatewayEVM.sol @@ -19,6 +19,7 @@ contract GatewayEVM is Initializable, AccessControlUpgradeable, UUPSUpgradeable, + IGatewayEVM, IGatewayEVMErrors, IGatewayEVMEvents, ReentrancyGuardUpgradeable, @@ -96,7 +97,8 @@ contract GatewayEVM is /// @param destination Address to call. /// @param data Calldata to pass to the call. function executeRevert(address destination, bytes calldata data) public payable onlyRole(TSS_ROLE) whenNotPaused { - (bool success, bytes memory result) = destination.call{ value: msg.value }(""); + if (destination == address(0)) revert ZeroAddress(); + (bool success,) = destination.call{ value: msg.value }(""); if (!success) revert ExecutionFailed(); Revertable(destination).onRevert(data); @@ -193,7 +195,7 @@ contract GatewayEVM is if (msg.value == 0) revert InsufficientETHAmount(); (bool deposited,) = tssAddress.call{ value: msg.value }(""); - if (deposited == false) revert DepositFailed(); + if (!deposited) revert DepositFailed(); emit Deposit(msg.sender, receiver, msg.value, address(0), ""); } @@ -217,7 +219,7 @@ contract GatewayEVM is if (msg.value == 0) revert InsufficientETHAmount(); (bool deposited,) = tssAddress.call{ value: msg.value }(""); - if (deposited == false) revert DepositFailed(); + if (!deposited) revert DepositFailed(); emit Deposit(msg.sender, receiver, msg.value, address(0), payload); } @@ -291,7 +293,7 @@ contract GatewayEVM is // transfer amount to gateway IERC20(token).safeTransferFrom(from, address(this), amount); // approve connector to handle tokens depending on connector version (eg. lock or burn) - IERC20(token).approve(zetaConnector, amount); + if (!IERC20(token).approve(zetaConnector, amount)) revert ApprovalFailed(); // send tokens to connector ZetaConnectorBase(zetaConnector).receiveTokens(amount); } else { @@ -309,7 +311,7 @@ contract GatewayEVM is if (token == zetaToken) { // transfer to connector // approve connector to handle tokens depending on connector version (eg. lock or burn) - IERC20(token).approve(zetaConnector, amount); + if (!IERC20(token).approve(zetaConnector, amount)) revert ApprovalFailed(); // send tokens to connector ZetaConnectorBase(zetaConnector).receiveTokens(amount); } else { diff --git a/v2/src/evm/ZetaConnectorBase.sol b/v2/src/evm/ZetaConnectorBase.sol index 5aa31bf6..d9adeb35 100644 --- a/v2/src/evm/ZetaConnectorBase.sol +++ b/v2/src/evm/ZetaConnectorBase.sol @@ -23,8 +23,6 @@ abstract contract ZetaConnectorBase is IZetaConnectorEvents, ReentrancyGuard, Pa IGatewayEVM public immutable gateway; /// @notice The address of the Zeta token. address public immutable zetaToken; - /// @notice The address of the TSS (Threshold Signature Scheme) contract. - address public tssAddress; /// @notice New role identifier for withdrawer role. bytes32 public constant WITHDRAWER_ROLE = keccak256("WITHDRAWER_ROLE"); @@ -39,7 +37,6 @@ abstract contract ZetaConnectorBase is IZetaConnectorEvents, ReentrancyGuard, Pa } gateway = IGatewayEVM(_gateway); zetaToken = _zetaToken; - tssAddress = _tssAddress; _grantRole(DEFAULT_ADMIN_ROLE, _admin); _grantRole(WITHDRAWER_ROLE, _tssAddress); diff --git a/v2/src/evm/ZetaConnectorNonNative.sol b/v2/src/evm/ZetaConnectorNonNative.sol index 65605f79..8785667d 100644 --- a/v2/src/evm/ZetaConnectorNonNative.sol +++ b/v2/src/evm/ZetaConnectorNonNative.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.26; import "./ZetaConnectorBase.sol"; import "./interfaces/IZetaNonEthNew.sol"; -import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; /// @title ZetaConnectorNonNative /// @notice Implementation of ZetaConnectorBase for non-native token handling. diff --git a/v2/src/evm/interfaces/IGatewayEVM.sol b/v2/src/evm/interfaces/IGatewayEVM.sol index c752d054..640584da 100644 --- a/v2/src/evm/interfaces/IGatewayEVM.sol +++ b/v2/src/evm/interfaces/IGatewayEVM.sol @@ -80,6 +80,12 @@ interface IGatewayEVM { /// @param data The calldata to pass to the contract call. function executeWithERC20(address token, address to, uint256 amount, bytes calldata data) external; + /// @notice Transfers msg.value to destination contract and executes it's onRevert function. + /// @dev This function can only be called by the TSS address and it is payable. + /// @param destination Address to call. + /// @param data Calldata to pass to the call. + function executeRevert(address destination, bytes calldata data) external payable; + /// @notice Executes a call to a contract. /// @param destination The address of the contract to call. /// @param data The calldata to pass to the contract call. @@ -92,6 +98,38 @@ interface IGatewayEVM { /// @param amount The amount of tokens to transfer. /// @param data The calldata to pass to the contract call. function revertWithERC20(address token, address to, uint256 amount, bytes calldata data) external; + + /// @notice Deposits ETH to the TSS address. + /// @param receiver Address of the receiver. + function deposit(address receiver) external payable; + + /// @notice Deposits ERC20 tokens to the custody or connector contract. + /// @param receiver Address of the receiver. + /// @param amount Amount of tokens to deposit. + /// @param asset Address of the ERC20 token. + function deposit(address receiver, uint256 amount, address asset) external; + + /// @notice Deposits ETH to the TSS address and calls an omnichain smart contract. + /// @param receiver Address of the receiver. + /// @param payload Calldata to pass to the call. + function depositAndCall(address receiver, bytes calldata payload) external payable; + + /// @notice Deposits ERC20 tokens to the custody or connector contract and calls an omnichain smart contract. + /// @param receiver Address of the receiver. + /// @param amount Amount of tokens to deposit. + /// @param asset Address of the ERC20 token. + /// @param payload Calldata to pass to the call. + function depositAndCall( + address receiver, + uint256 amount, + address asset, + bytes calldata payload + ) external; + + /// @notice Calls an omnichain smart contract without asset transfer. + /// @param receiver Address of the receiver. + /// @param payload Calldata to pass to the call. + function call(address receiver, bytes calldata payload) external; } /// @title Revertable diff --git a/v2/src/zevm/GatewayZEVM.sol b/v2/src/zevm/GatewayZEVM.sol index 61ecec1a..444727cd 100644 --- a/v2/src/zevm/GatewayZEVM.sol +++ b/v2/src/zevm/GatewayZEVM.sol @@ -16,6 +16,7 @@ import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol /// @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 + IGatewayZEVM, IGatewayZEVMEvents, IGatewayZEVMErrors, Initializable, @@ -182,7 +183,7 @@ contract GatewayZEVM is function deposit(address zrc20, uint256 amount, address target) external onlyFungible whenNotPaused { if (target == FUNGIBLE_MODULE_ADDRESS || target == address(this)) revert InvalidTarget(); - IZRC20(zrc20).deposit(target, amount); + if (!IZRC20(zrc20).deposit(target, amount)) revert ZRC20DepositFailed(); } /// @notice Execute a user-specified contract on ZEVM. @@ -224,7 +225,7 @@ contract GatewayZEVM is { if (target == FUNGIBLE_MODULE_ADDRESS || target == address(this)) revert InvalidTarget(); - IZRC20(zrc20).deposit(target, amount); + if (!IZRC20(zrc20).deposit(target, amount)) revert ZRC20DepositFailed(); UniversalContract(target).onCrossChainCall(context, zrc20, amount, message); } @@ -288,7 +289,7 @@ contract GatewayZEVM is { if (target == FUNGIBLE_MODULE_ADDRESS || target == address(this)) revert InvalidTarget(); - IZRC20(zrc20).deposit(target, amount); + if (!IZRC20(zrc20).deposit(target, amount)) revert ZRC20DepositFailed(); UniversalContract(target).onRevert(context, zrc20, amount, message); } } diff --git a/v2/src/zevm/ZRC20.sol b/v2/src/zevm/ZRC20.sol index bfdd67ca..fa0a0149 100644 --- a/v2/src/zevm/ZRC20.sol +++ b/v2/src/zevm/ZRC20.sol @@ -42,16 +42,12 @@ contract ZRC20 is IZRC20Metadata, ZRC20Errors, ZRC20Events { uint256 private _totalSupply; string private _name; string private _symbol; - uint8 private _decimals; + uint8 private immutable _decimals; function _msgSender() internal view virtual returns (address) { return msg.sender; } - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } - /** * @dev Only fungible module modifier. */ @@ -74,6 +70,7 @@ contract ZRC20 is IZRC20Metadata, ZRC20Errors, ZRC20Events { address gatewayAddress_ ) { if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); + if (systemContractAddress_ == address(0) || gatewayAddress_ == address(0)) revert ZeroAddress(); _name = name_; _symbol = symbol_; _decimals = decimals_; @@ -279,6 +276,7 @@ contract ZRC20 is IZRC20Metadata, ZRC20Errors, ZRC20Events { * @param addr, new system contract address. */ function updateSystemContractAddress(address addr) external onlyFungible { + if (addr == address(0)) revert ZeroAddress(); systemContractAddress = addr; emit UpdatedSystemContract(addr); } @@ -288,6 +286,7 @@ contract ZRC20 is IZRC20Metadata, ZRC20Errors, ZRC20Events { * @param addr, new gateway contract address. */ function updateGatewayAddress(address addr) external onlyFungible { + if (addr == address(0)) revert ZeroAddress(); gatewayAddress = addr; emit UpdatedGateway(addr); } diff --git a/v2/src/zevm/interfaces/IGatewayZEVM.sol b/v2/src/zevm/interfaces/IGatewayZEVM.sol index 6d7c2a19..aef95977 100644 --- a/v2/src/zevm/interfaces/IGatewayZEVM.sol +++ b/v2/src/zevm/interfaces/IGatewayZEVM.sol @@ -71,6 +71,49 @@ interface IGatewayZEVM { bytes calldata message ) external; + + /// @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, + address target, + bytes calldata message + ) + external; + + /// @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, + uint256 amount, + address target, + bytes calldata message + ) + external; + + /// @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, + uint256 amount, + address target, + bytes calldata message + ) + external; } /// @title IGatewayZEVMEvents @@ -119,6 +162,9 @@ interface IGatewayZEVMErrors { /// @notice Error indicating a failure to transfer ZRC20 tokens. error ZRC20TransferFailed(); + /// @notice Error indicating a failure to deposit ZRC20 tokens. + error ZRC20DepositFailed(); + /// @notice Error indicating a failure to transfer gas fee. error GasFeeTransferFailed();