Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
fadeev committed Jun 18, 2024
1 parent 6a70653 commit 74ce3ff
Show file tree
Hide file tree
Showing 5 changed files with 361 additions and 2 deletions.
7 changes: 5 additions & 2 deletions omnichain/example/contracts/Caller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ contract Caller {
error DepositFailed();

function deposit(address universalAppAddress) public payable {
bytes memory data = abi.encodePacked();
bytes memory data = abi.encodePacked(universalAppAddress);

(bool success, ) = tssAddress.call{value: msg.value, gas: 100000}(data);
(bool success, ) = payable(tssAddress).call{
value: msg.value,
gas: 100000
}(data);

if (success) {
emit DepositSuccess(msg.sender, universalAppAddress);
Expand Down
311 changes: 311 additions & 0 deletions omnichain/example/flat
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();
}
}
}
2 changes: 2 additions & 0 deletions omnichain/example/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import "./tasks/interact";
import "./tasks/deploy";
import "./tasks/deposit-caller";
import "./tasks/deploy-caller";
import "@nomicfoundation/hardhat-toolbox";
import "@zetachain/toolkit/tasks";

Expand Down
23 changes: 23 additions & 0 deletions omnichain/example/tasks/deploy-caller.ts
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"
);
20 changes: 20 additions & 0 deletions omnichain/example/tasks/deposit-caller.ts
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);

0 comments on commit 74ce3ff

Please sign in to comment.