From e881d7c3a60c689d3f0b0c3c490705f97e536a56 Mon Sep 17 00:00:00 2001 From: skosito Date: Mon, 8 Jul 2024 20:46:40 +0200 Subject: [PATCH] more foundry tests --- .../prototypes/{evm => test}/GatewayEVM.t.sol | 2 +- .../prototypes/test/GatewayIntegration.t.sol | 103 ++++++++++++++++++ contracts/prototypes/zevm/GatewayZEVM.sol | 6 +- contracts/prototypes/zevm/interfaces.sol | 5 + contracts/zevm/Interfaces.sol | 62 ----------- contracts/zevm/ZRC20.sol | 8 +- contracts/{prototypes => }/zevm/ZRC20New.sol | 8 +- contracts/zevm/interfaces/ISystem.sol | 19 ++++ contracts/zevm/interfaces/IZRC20.sol | 38 ++++++- 9 files changed, 172 insertions(+), 79 deletions(-) rename contracts/prototypes/{evm => test}/GatewayEVM.t.sol (98%) create mode 100644 contracts/prototypes/test/GatewayIntegration.t.sol delete mode 100644 contracts/zevm/Interfaces.sol rename contracts/{prototypes => }/zevm/ZRC20New.sol (97%) create mode 100644 contracts/zevm/interfaces/ISystem.sol diff --git a/contracts/prototypes/evm/GatewayEVM.t.sol b/contracts/prototypes/test/GatewayEVM.t.sol similarity index 98% rename from contracts/prototypes/evm/GatewayEVM.t.sol rename to contracts/prototypes/test/GatewayEVM.t.sol index dea62917..6d5430de 100644 --- a/contracts/prototypes/evm/GatewayEVM.t.sol +++ b/contracts/prototypes/test/GatewayEVM.t.sol @@ -10,7 +10,7 @@ import "contracts/prototypes/evm/ERC20CustodyNew.sol"; import "contracts/prototypes/evm/TestERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import "./interfaces.sol"; +import "../evm/interfaces.sol"; import "forge-std/console.sol"; contract GatewayEVMTest is Test, IGatewayEVMErrors, IGatewayEVMEvents, IReceiverEVMEvents { diff --git a/contracts/prototypes/test/GatewayIntegration.t.sol b/contracts/prototypes/test/GatewayIntegration.t.sol new file mode 100644 index 00000000..a69278f8 --- /dev/null +++ b/contracts/prototypes/test/GatewayIntegration.t.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.7; + +import "forge-std/Test.sol"; +import "forge-std/Vm.sol"; + +import "contracts/prototypes/evm/GatewayEVM.sol"; +import "contracts/prototypes/evm/ReceiverEVM.sol"; +import "contracts/prototypes/evm/ERC20CustodyNew.sol"; +import "contracts/prototypes/evm/TestERC20.sol"; +import "contracts/prototypes/evm/ReceiverEVM.sol"; + +import "contracts/prototypes/zevm/GatewayZEVM.sol"; +import "contracts/prototypes/zevm/SenderZEVM.sol"; +import "contracts/zevm/ZRC20New.sol"; +import "contracts/zevm/testing/SystemContractMock.sol"; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "../evm/interfaces.sol"; +import "../zevm/interfaces.sol"; +import "forge-std/console.sol"; + +contract GatewayIntegrationTest is Test, IGatewayEVMErrors, IGatewayEVMEvents, IGatewayZEVMEvents, IReceiverEVMEvents { + // evm + using SafeERC20 for IERC20; + + GatewayEVM gatewayEVM; + ERC20CustodyNew custody; + TestERC20 token; + ReceiverEVM receiverEVM; + address ownerEVM; + address destination; + address tssAddress; + + // zevm + GatewayZEVM gatewayZEVM; + SenderZEVM senderZEVM; + SystemContractMock systemContract; + ZRC20New zrc20; + address ownerZEVM; + + function setUp() public { + // evm + ownerEVM = address(this); + destination = address(0x1234); + tssAddress = address(0x5678); + ownerZEVM = address(0x4321); + + token = new TestERC20("test", "TTK"); + gatewayEVM = new GatewayEVM(); + custody = new ERC20CustodyNew(address(gatewayEVM)); + + gatewayEVM.initialize(tssAddress); + gatewayEVM.setCustody(address(custody)); + + // Mint initial supply to the ownerEVM + token.mint(ownerEVM, 1000000); + + // Transfer some tokens to the custody contract + token.transfer(address(custody), 500000); + + receiverEVM = new ReceiverEVM(); + + // zevm + // Impersonate the fungible module account + gatewayZEVM = new GatewayZEVM(); + + senderZEVM = new SenderZEVM(address(gatewayZEVM)); + address fungibleModuleAddress = address(0x735b14BB79463307AAcBED86DAf3322B1e6226aB); + vm.startPrank(fungibleModuleAddress); + systemContract = new SystemContractMock(address(0), address(0), address(0)); + zrc20 = new ZRC20New("TOKEN", "TKN", 18, 1, CoinType.Zeta, 0, address(systemContract), address(gatewayZEVM)); + systemContract.setGasCoinZRC20(1, address(zrc20)); + systemContract.setGasPrice(1, 1); + zrc20.deposit(ownerZEVM, 1000000); + zrc20.deposit(address(senderZEVM), 1000000); + vm.stopPrank(); + + vm.prank(ownerZEVM); + zrc20.approve(address(gatewayZEVM), 1000000); + } + + function testCallReceiverEVMFromZEVM() public { + string memory str = "Hello, Hardhat!"; + uint256 num = 42; + bool flag = true; + uint256 value = 1 ether; + + // Encode the function call data and call on zevm + 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), abi.encodePacked(receiverEVM), message); + gatewayZEVM.call(abi.encodePacked(receiverEVM), message); + + // Call execute on evm + vm.deal(address(gatewayEVM), value); + vm.expectEmit(true, true, true, true, address(gatewayEVM)); + emit Executed(address(receiverEVM), value, message); + gatewayEVM.execute{value: value}(address(receiverEVM), message); + } +} \ No newline at end of file diff --git a/contracts/prototypes/zevm/GatewayZEVM.sol b/contracts/prototypes/zevm/GatewayZEVM.sol index bb0c3eb3..0730a864 100644 --- a/contracts/prototypes/zevm/GatewayZEVM.sol +++ b/contracts/prototypes/zevm/GatewayZEVM.sol @@ -6,10 +6,11 @@ import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "../../zevm/interfaces/IZRC20.sol"; import "../../zevm/interfaces/zContract.sol"; +import "./interfaces.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 -contract GatewayZEVM is Initializable, OwnableUpgradeable, UUPSUpgradeable { +contract GatewayZEVM is IGatewayZEVMEvents, Initializable, OwnableUpgradeable, UUPSUpgradeable { address public constant FUNGIBLE_MODULE_ADDRESS = 0x735b14BB79463307AAcBED86DAf3322B1e6226aB; error WithdrawalFailed(); @@ -20,9 +21,6 @@ contract GatewayZEVM is Initializable, OwnableUpgradeable, UUPSUpgradeable { error CallerIsNotFungibleModule(); error InvalidTarget(); - event Call(address indexed sender, bytes receiver, bytes message); - event Withdrawal(address indexed from, bytes to, uint256 value, uint256 gasfee, uint256 protocolFlatFee, bytes message); - /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); diff --git a/contracts/prototypes/zevm/interfaces.sol b/contracts/prototypes/zevm/interfaces.sol index 0ac42801..60088a5c 100644 --- a/contracts/prototypes/zevm/interfaces.sol +++ b/contracts/prototypes/zevm/interfaces.sol @@ -31,4 +31,9 @@ interface IGatewayZEVM { address target, bytes calldata message ) external; +} + +interface IGatewayZEVMEvents { + event Call(address indexed sender, bytes receiver, bytes message); + event Withdrawal(address indexed from, bytes to, uint256 value, uint256 gasfee, uint256 protocolFlatFee, bytes message); } \ No newline at end of file diff --git a/contracts/zevm/Interfaces.sol b/contracts/zevm/Interfaces.sol deleted file mode 100644 index f9bba9b2..00000000 --- a/contracts/zevm/Interfaces.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.7; - -/** - * @dev Interfaces of SystemContract and ZRC20 to make easier to import. - */ -interface ISystem { - function FUNGIBLE_MODULE_ADDRESS() external view returns (address); - - function wZetaContractAddress() external view returns (address); - - function uniswapv2FactoryAddress() external view returns (address); - - function gasPriceByChainId(uint256 chainID) external view returns (uint256); - - function gasCoinZRC20ByChainId(uint256 chainID) external view returns (address); - - function gasZetaPoolByChainId(uint256 chainID) external view returns (address); -} - -interface IZRC20 { - function totalSupply() external view returns (uint256); - - function balanceOf(address account) external view returns (uint256); - - function transfer(address recipient, uint256 amount) external returns (bool); - - function allowance(address owner, address spender) external view returns (uint256); - - function approve(address spender, uint256 amount) external returns (bool); - - function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); - - function deposit(address to, uint256 amount) external returns (bool); - - function withdraw(bytes memory to, uint256 amount) external returns (bool); - - function withdrawGasFee() external view returns (address, uint256); - - event Transfer(address indexed from, address indexed to, uint256 value); - event Approval(address indexed owner, address indexed spender, uint256 value); - event Deposit(bytes from, address indexed to, uint256 value); - event Withdrawal(address indexed from, bytes to, uint256 value, uint256 gasfee, uint256 protocolFlatFee); - event UpdatedSystemContract(address systemContract); - event UpdatedGasLimit(uint256 gasLimit); - event UpdatedProtocolFlatFee(uint256 protocolFlatFee); -} - -interface IZRC20Metadata is IZRC20 { - function name() external view returns (string memory); - - function symbol() external view returns (string memory); - - function decimals() external view returns (uint8); -} - -/// @dev Coin types for ZRC20. Zeta value should not be used. -enum CoinType { - Zeta, - Gas, - ERC20 -} diff --git a/contracts/zevm/ZRC20.sol b/contracts/zevm/ZRC20.sol index 5d758c89..21a6e5f5 100644 --- a/contracts/zevm/ZRC20.sol +++ b/contracts/zevm/ZRC20.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.7; -import "./Interfaces.sol"; +import "./interfaces/IZRC20.sol"; /** * @dev Custom errors for ZRC20 @@ -17,7 +17,7 @@ interface ZRC20Errors { error ZeroAddress(); } -contract ZRC20 is IZRC20, IZRC20Metadata, ZRC20Errors { +contract ZRC20 is IZRC20Metadata, ZRC20Events, ZRC20Errors { /// @notice Fungible address is always the same, maintained at the protocol level address public constant FUNGIBLE_MODULE_ADDRESS = 0x735b14BB79463307AAcBED86DAf3322B1e6226aB; /// @notice Chain id.abi @@ -29,7 +29,7 @@ contract ZRC20 is IZRC20, IZRC20Metadata, ZRC20Errors { /// @notice Gas limit. uint256 public GAS_LIMIT; /// @notice Protocol flat fee. - uint256 public PROTOCOL_FLAT_FEE; + uint256 public override PROTOCOL_FLAT_FEE; mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; @@ -170,7 +170,7 @@ contract ZRC20 is IZRC20, IZRC20Metadata, ZRC20Errors { * @param amount, amount to burn. * @return true/false if succeeded/failed. */ - function burn(uint256 amount) external returns (bool) { + function burn(uint256 amount) external override returns (bool) { _burn(msg.sender, amount); return true; } diff --git a/contracts/prototypes/zevm/ZRC20New.sol b/contracts/zevm/ZRC20New.sol similarity index 97% rename from contracts/prototypes/zevm/ZRC20New.sol rename to contracts/zevm/ZRC20New.sol index 62f55ce5..adc8fbde 100644 --- a/contracts/prototypes/zevm/ZRC20New.sol +++ b/contracts/zevm/ZRC20New.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.7; -import "../../zevm/Interfaces.sol"; +import "./interfaces/IZRC20.sol"; /** * @dev Custom errors for ZRC20 @@ -19,7 +19,7 @@ interface ZRC20Errors { // NOTE: this is exactly the same as ZRC20, except gateway contract address is set at deployment // and used to allow deposit. This is first version, it might change in the future. -contract ZRC20New is IZRC20, IZRC20Metadata, ZRC20Errors { +contract ZRC20New is IZRC20Metadata, ZRC20Errors, ZRC20Events { /// @notice Fungible address is always the same, maintained at the protocol level address public constant FUNGIBLE_MODULE_ADDRESS = 0x735b14BB79463307AAcBED86DAf3322B1e6226aB; /// @notice Chain id.abi @@ -33,7 +33,7 @@ contract ZRC20New is IZRC20, IZRC20Metadata, ZRC20Errors { /// @notice Gas limit. uint256 public GAS_LIMIT; /// @notice Protocol flat fee. - uint256 public PROTOCOL_FLAT_FEE; + uint256 public override PROTOCOL_FLAT_FEE; mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; @@ -176,7 +176,7 @@ contract ZRC20New is IZRC20, IZRC20Metadata, ZRC20Errors { * @param amount, amount to burn. * @return true/false if succeeded/failed. */ - function burn(uint256 amount) external returns (bool) { + function burn(uint256 amount) external override returns (bool) { _burn(msg.sender, amount); return true; } diff --git a/contracts/zevm/interfaces/ISystem.sol b/contracts/zevm/interfaces/ISystem.sol new file mode 100644 index 00000000..f8fb70bd --- /dev/null +++ b/contracts/zevm/interfaces/ISystem.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.7; + +/** + * @dev Interfaces of SystemContract and ZRC20 to make easier to import. + */ +interface ISystem { + function FUNGIBLE_MODULE_ADDRESS() external view returns (address); + + function wZetaContractAddress() external view returns (address); + + function uniswapv2FactoryAddress() external view returns (address); + + function gasPriceByChainId(uint256 chainID) external view returns (uint256); + + function gasCoinZRC20ByChainId(uint256 chainID) external view returns (address); + + function gasZetaPoolByChainId(uint256 chainID) external view returns (address); +} diff --git a/contracts/zevm/interfaces/IZRC20.sol b/contracts/zevm/interfaces/IZRC20.sol index eab06e7f..6b811cf5 100644 --- a/contracts/zevm/interfaces/IZRC20.sol +++ b/contracts/zevm/interfaces/IZRC20.sol @@ -1,6 +1,23 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.7; +/** + * @dev Interfaces of SystemContract and ZRC20 to make easier to import. + */ +interface ISystem { + function FUNGIBLE_MODULE_ADDRESS() external view returns (address); + + function wZetaContractAddress() external view returns (address); + + function uniswapv2FactoryAddress() external view returns (address); + + function gasPriceByChainId(uint256 chainID) external view returns (uint256); + + function gasCoinZRC20ByChainId(uint256 chainID) external view returns (address); + + function gasZetaPoolByChainId(uint256 chainID) external view returns (address); +} + interface IZRC20 { function totalSupply() external view returns (uint256); @@ -12,10 +29,6 @@ interface IZRC20 { function approve(address spender, uint256 amount) external returns (bool); - function decreaseAllowance(address spender, uint256 amount) external returns (bool); - - function increaseAllowance(address spender, uint256 amount) external returns (bool); - function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); function deposit(address to, uint256 amount) external returns (bool); @@ -27,7 +40,17 @@ interface IZRC20 { function withdrawGasFee() external view returns (address, uint256); function PROTOCOL_FLAT_FEE() external view returns (uint256); +} + +interface IZRC20Metadata is IZRC20 { + function name() external view returns (string memory); + + function symbol() external view returns (string memory); + function decimals() external view returns (uint8); +} + +interface ZRC20Events { event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); event Deposit(bytes from, address indexed to, uint256 value); @@ -36,3 +59,10 @@ interface IZRC20 { event UpdatedGasLimit(uint256 gasLimit); event UpdatedProtocolFlatFee(uint256 protocolFlatFee); } + +/// @dev Coin types for ZRC20. Zeta value should not be used. +enum CoinType { + Zeta, + Gas, + ERC20 +}