-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
361 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,311 @@ | ||
// Sources flattened with hardhat v2.22.3 https://hardhat.org | ||
|
||
// SPDX-License-Identifier: MIT | ||
|
||
// File @zetachain/protocol-contracts/contracts/zevm/interfaces/[email protected] | ||
|
||
// Original license: SPDX_License_Identifier: MIT | ||
pragma solidity 0.8.7; | ||
|
||
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 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); | ||
|
||
function burn(address account, uint256 amount) external returns (bool); | ||
|
||
function withdraw(bytes memory to, uint256 amount) external returns (bool); | ||
|
||
function withdrawGasFee() external view returns (address, uint256); | ||
|
||
function PROTOCOL_FEE() external view returns (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); | ||
} | ||
|
||
|
||
// File @zetachain/protocol-contracts/contracts/zevm/interfaces/[email protected] | ||
|
||
// Original license: SPDX_License_Identifier: MIT | ||
pragma solidity 0.8.7; | ||
|
||
struct zContext { | ||
bytes origin; | ||
address sender; | ||
uint256 chainID; | ||
} | ||
|
||
interface zContract { | ||
function onCrossChainCall( | ||
zContext calldata context, | ||
address zrc20, | ||
uint256 amount, | ||
bytes calldata message | ||
) external; | ||
} | ||
|
||
|
||
// File @zetachain/protocol-contracts/contracts/zevm/[email protected] | ||
|
||
// Original license: SPDX_License_Identifier: MIT | ||
pragma solidity 0.8.7; | ||
|
||
|
||
/** | ||
* @dev Custom errors for SystemContract | ||
*/ | ||
interface SystemContractErrors { | ||
error CallerIsNotFungibleModule(); | ||
error InvalidTarget(); | ||
error CantBeIdenticalAddresses(); | ||
error CantBeZeroAddress(); | ||
error ZeroAddress(); | ||
} | ||
|
||
/** | ||
* @dev The system contract it's called by the protocol to interact with the blockchain. | ||
* Also includes a lot of tools to make easier to interact with ZetaChain. | ||
*/ | ||
contract SystemContract is SystemContractErrors { | ||
/// @notice Map to know the gas price of each chain given a chain id. | ||
mapping(uint256 => uint256) public gasPriceByChainId; | ||
/// @notice Map to know the ZRC20 address of a token given a chain id, ex zETH, zBNB etc. | ||
mapping(uint256 => address) public gasCoinZRC20ByChainId; | ||
// @dev: Map to know uniswap V2 pool of ZETA/ZRC20 given a chain id. This refer to the build in uniswap deployed at genesis. | ||
mapping(uint256 => address) public gasZetaPoolByChainId; | ||
|
||
/// @notice Fungible address is always the same, it's on protocol level. | ||
address public constant FUNGIBLE_MODULE_ADDRESS = 0x735b14BB79463307AAcBED86DAf3322B1e6226aB; | ||
/// @notice Uniswap V2 addresses. | ||
address public immutable uniswapv2FactoryAddress; | ||
address public immutable uniswapv2Router02Address; | ||
/// @notice Address of the wrapped ZETA to interact with Uniswap V2. | ||
address public wZetaContractAddress; | ||
/// @notice Address of ZEVM Zeta Connector. | ||
address public zetaConnectorZEVMAddress; | ||
|
||
/// @notice Custom SystemContract errors. | ||
event SystemContractDeployed(); | ||
event SetGasPrice(uint256, uint256); | ||
event SetGasCoin(uint256, address); | ||
event SetGasZetaPool(uint256, address); | ||
event SetWZeta(address); | ||
event SetConnectorZEVM(address); | ||
|
||
/** | ||
* @dev Only fungible module can deploy a system contract. | ||
*/ | ||
constructor(address wzeta_, address uniswapv2Factory_, address uniswapv2Router02_) { | ||
if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); | ||
wZetaContractAddress = wzeta_; | ||
uniswapv2FactoryAddress = uniswapv2Factory_; | ||
uniswapv2Router02Address = uniswapv2Router02_; | ||
emit SystemContractDeployed(); | ||
} | ||
|
||
/** | ||
* @dev Deposit foreign coins into ZRC20 and call user specified contract on zEVM. | ||
* @param context, context data for deposit. | ||
* @param zrc20, zrc20 address for deposit. | ||
* @param amount, amount to deposit. | ||
* @param target, contract address to make a call after deposit. | ||
* @param message, calldata for a call. | ||
*/ | ||
function depositAndCall( | ||
zContext calldata context, | ||
address zrc20, | ||
uint256 amount, | ||
address target, | ||
bytes calldata message | ||
) external { | ||
if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); | ||
if (target == FUNGIBLE_MODULE_ADDRESS || target == address(this)) revert InvalidTarget(); | ||
|
||
IZRC20(zrc20).deposit(target, amount); | ||
zContract(target).onCrossChainCall(context, zrc20, amount, message); | ||
} | ||
|
||
/** | ||
* @dev Sort token addresses lexicographically. Used to handle return values from pairs sorted in the order. | ||
* @param tokenA, tokenA address. | ||
* @param tokenB, tokenB address. | ||
* @return token0 token1, returns sorted token addresses,. | ||
*/ | ||
function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { | ||
if (tokenA == tokenB) revert CantBeIdenticalAddresses(); | ||
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); | ||
if (token0 == address(0)) revert CantBeZeroAddress(); | ||
} | ||
|
||
/** | ||
* @dev Calculates the CREATE2 address for a pair without making any external calls. | ||
* @param factory, factory address. | ||
* @param tokenA, tokenA address. | ||
* @param tokenB, tokenB address. | ||
* @return pair tokens pair address. | ||
*/ | ||
function uniswapv2PairFor(address factory, address tokenA, address tokenB) public pure returns (address pair) { | ||
(address token0, address token1) = sortTokens(tokenA, tokenB); | ||
pair = address( | ||
uint160( | ||
uint256( | ||
keccak256( | ||
abi.encodePacked( | ||
hex"ff", | ||
factory, | ||
keccak256(abi.encodePacked(token0, token1)), | ||
hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" // init code hash | ||
) | ||
) | ||
) | ||
) | ||
); | ||
} | ||
|
||
/** | ||
* @dev Fungible module updates the gas price oracle periodically. | ||
* @param chainID, chain id. | ||
* @param price, new gas price. | ||
*/ | ||
function setGasPrice(uint256 chainID, uint256 price) external { | ||
if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); | ||
gasPriceByChainId[chainID] = price; | ||
emit SetGasPrice(chainID, price); | ||
} | ||
|
||
/** | ||
* @dev Setter for gasCoinZRC20ByChainId map. | ||
* @param chainID, chain id. | ||
* @param zrc20, ZRC20 address. | ||
*/ | ||
function setGasCoinZRC20(uint256 chainID, address zrc20) external { | ||
if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); | ||
gasCoinZRC20ByChainId[chainID] = zrc20; | ||
emit SetGasCoin(chainID, zrc20); | ||
} | ||
|
||
/** | ||
* @dev Set the pool wzeta/erc20 address. | ||
* @param chainID, chain id. | ||
* @param erc20, pair for uniswap wzeta/erc20. | ||
*/ | ||
function setGasZetaPool(uint256 chainID, address erc20) external { | ||
if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); | ||
address pool = uniswapv2PairFor(uniswapv2FactoryAddress, wZetaContractAddress, erc20); | ||
gasZetaPoolByChainId[chainID] = pool; | ||
emit SetGasZetaPool(chainID, pool); | ||
} | ||
|
||
/** | ||
* @dev Setter for wrapped ZETA address. | ||
* @param addr, wzeta new address. | ||
*/ | ||
function setWZETAContractAddress(address addr) external { | ||
if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); | ||
if (addr == address(0)) revert ZeroAddress(); | ||
wZetaContractAddress = addr; | ||
emit SetWZeta(wZetaContractAddress); | ||
} | ||
|
||
/** | ||
* @dev Setter for zetaConnector ZEVM Address | ||
* @param addr, zeta connector new address. | ||
*/ | ||
function setConnectorZEVMAddress(address addr) external { | ||
if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); | ||
if (addr == address(0)) revert ZeroAddress(); | ||
zetaConnectorZEVMAddress = addr; | ||
emit SetConnectorZEVM(zetaConnectorZEVMAddress); | ||
} | ||
} | ||
|
||
|
||
// File @zetachain/toolkit/contracts/[email protected] | ||
|
||
// Original license: SPDX_License_Identifier: MIT | ||
pragma solidity ^0.8.7; | ||
|
||
contract OnlySystem { | ||
error OnlySystemContract(string); | ||
|
||
modifier onlySystem(SystemContract systemContract) { | ||
if (msg.sender != address(systemContract)) { | ||
revert OnlySystemContract( | ||
"Only system contract can call this function" | ||
); | ||
} | ||
_; | ||
} | ||
} | ||
|
||
|
||
// File contracts/UniversalApp.sol | ||
|
||
// Original license: SPDX_License_Identifier: MIT | ||
pragma solidity 0.8.7; | ||
|
||
|
||
|
||
contract UniversalApp is zContract, OnlySystem { | ||
SystemContract public systemContract; | ||
|
||
constructor(address systemContractAddress) { | ||
systemContract = SystemContract(systemContractAddress); | ||
} | ||
|
||
function onCrossChainCall( | ||
zContext calldata context, | ||
address zrc20, | ||
uint256 amount, | ||
bytes calldata message | ||
) external virtual override onlySystem(systemContract) { | ||
// TODO: implement the logic | ||
} | ||
} | ||
|
||
|
||
// File contracts/Caller.sol | ||
|
||
// Original license: SPDX_License_Identifier: MIT | ||
pragma solidity 0.8.7; | ||
|
||
contract Caller { | ||
address constant tssAddress = 0x8531a5aB847ff5B22D855633C25ED1DA3255247e; | ||
event DepositSuccess(address, address); | ||
error DepositFailed(); | ||
|
||
function deposit(address universalAppAddress) public payable { | ||
bytes memory data = abi.encodePacked(); | ||
|
||
(bool success, ) = payable(tssAddress).call{ | ||
value: msg.value, | ||
gas: 100000 | ||
}(data); | ||
|
||
if (success) { | ||
emit DepositSuccess(msg.sender, universalAppAddress); | ||
} else { | ||
revert DepositFailed(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { getAddress, ParamChainName } from "@zetachain/protocol-contracts"; | ||
import { task } from "hardhat/config"; | ||
import { HardhatRuntimeEnvironment } from "hardhat/types"; | ||
|
||
const main = async (args: any, hre: HardhatRuntimeEnvironment) => { | ||
const [signer] = await hre.ethers.getSigners(); | ||
if (signer === undefined) { | ||
throw new Error( | ||
`Wallet not found. Please, run "npx hardhat account --save" or set PRIVATE_KEY env variable (for example, in a .env file)` | ||
); | ||
} | ||
|
||
const factory = await hre.ethers.getContractFactory("Caller"); | ||
const contract = await factory.deploy(); | ||
await contract.deployed(); | ||
|
||
console.log(contract.address); | ||
}; | ||
|
||
task("deploy-caller", "Deploy the contract", main).addFlag( | ||
"json", | ||
"Output in JSON" | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { task } from "hardhat/config"; | ||
import { HardhatRuntimeEnvironment } from "hardhat/types"; | ||
|
||
const main = async (args: any, hre: HardhatRuntimeEnvironment) => { | ||
const factory = await hre.ethers.getContractFactory("Caller"); | ||
const contract = factory.attach(args.caller); | ||
|
||
const tx = await contract.deposit(args.contract, { | ||
value: hre.ethers.utils.parseUnits(args.amount, 18), | ||
}); | ||
await tx.wait(); | ||
|
||
console.log(tx.hash); | ||
}; | ||
|
||
task("deposit-caller", "") | ||
.addParam("contract", "Universal app contract address") | ||
.addParam("caller", "Contract address") | ||
.addParam("amount", "The amount of ETH to send") | ||
.setAction(main); |