Skip to content

Commit

Permalink
refactor and events
Browse files Browse the repository at this point in the history
  • Loading branch information
fadeev committed Nov 8, 2024
1 parent 5de7d5f commit 0adfb61
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 40 deletions.
32 changes: 22 additions & 10 deletions examples/token/contracts/Connected.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,33 @@ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@zetachain/protocol-contracts/contracts/evm/GatewayEVM.sol";
import {RevertContext} from "@zetachain/protocol-contracts/contracts/Revert.sol";
import "./shared/Events.sol";

contract Connected is ERC20, Ownable {
contract Connected is ERC20, Ownable, Events {
GatewayEVM public immutable gateway;
address public counterparty;

error InvalidAddress();
error Unauthorized();

modifier onlyGateway() {
require(msg.sender == address(gateway), "Caller is not the gateway");
_;
}

function setCounterparty(address contractAddress) external onlyOwner {
counterparty = contractAddress;
emit CounterpartySet(contractAddress);
}

constructor(
address payable gatewayAddress,
address initialOwner
) ERC20("MyToken", "MTK") Ownable(initialOwner) {
address owner,
string memory name,
string memory symbol
) ERC20(name, symbol) Ownable(owner) {
if (gatewayAddress == address(0) || owner == address(0))
revert InvalidAddress();
gateway = GatewayEVM(gatewayAddress);
}

Expand All @@ -31,42 +40,45 @@ contract Connected is ERC20, Ownable {
}

function transferCrossChain(
address receiver,
address destination,
address receiver,
uint256 amount
) external payable {
_burn(msg.sender, amount);
bytes memory encodedData = abi.encode(receiver, amount, destination);
bytes memory message = abi.encode(destination, receiver, amount);

RevertOptions memory revertOptions = RevertOptions(
address(this),
true,
address(0),
encodedData,
message,
0
);

if (destination == address(0)) {
gateway.call(counterparty, encodedData, revertOptions);
gateway.call(counterparty, message, revertOptions);
} else {
gateway.depositAndCall{value: msg.value}(
counterparty,
encodedData,
message,
revertOptions
);
}
emit TokenTransfer(destination, receiver, amount);
}

function onCall(
MessageContext calldata messageContext,
MessageContext calldata context,
bytes calldata message
) external payable onlyGateway returns (bytes4) {
if (messageContext.sender != counterparty) revert("Unauthorized");
if (context.sender != counterparty) revert Unauthorized();
(address receiver, uint256 amount) = abi.decode(
message,
(address, uint256)
);
_mint(receiver, amount);
emit TokenTransferReceived(receiver, amount);
return "";
}

function onRevert(RevertContext calldata context) external onlyGateway {}
Expand Down
73 changes: 47 additions & 26 deletions examples/token/contracts/Universal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,93 +9,106 @@ import "@zetachain/protocol-contracts/contracts/zevm/interfaces/IGatewayZEVM.sol
import "@zetachain/protocol-contracts/contracts/zevm/GatewayZEVM.sol";
import {SwapHelperLib} from "@zetachain/toolkit/contracts/SwapHelperLib.sol";
import {SystemContract} from "@zetachain/toolkit/contracts/SystemContract.sol";
import "./shared/Events.sol";

contract Universal is ERC20, Ownable, UniversalContract {
contract Universal is ERC20, Ownable, UniversalContract, Events {
GatewayZEVM public immutable gateway;
SystemContract public immutable systemContract =
SystemContract(0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9);
uint256 private _nextTokenId;
bool public isUniversal = true;
uint256 public gasLimit = 700000;
uint256 public gasLimit;

error TransferFailed();
error Unauthorized();
error InvalidAddress();
error InvalidGasLimit();

mapping(address => bytes) public counterparty;

event CounterpartySet(address indexed zrc20, bytes indexed contractAddress);

modifier onlyGateway() {
require(msg.sender == address(gateway), "Caller is not the gateway");
_;
}

constructor(
address payable gatewayAddress,
address initialOwner
) ERC20("MyToken", "MTK") Ownable(initialOwner) {
address owner,
string memory name,
string memory symbol,
uint256 gas
) ERC20(name, symbol) Ownable(owner) {
if (gatewayAddress == address(0) || owner == address(0))
revert InvalidAddress();
if (gas == 0) revert InvalidGasLimit();
gateway = GatewayZEVM(gatewayAddress);
gasLimit = gas;
}

function setCounterparty(
address zrc20,
bytes memory contractAddress
) external onlyOwner {
counterparty[zrc20] = contractAddress;
emit CounterpartySet(zrc20, contractAddress);
emit CounterpartyMappingSet(zrc20, contractAddress);
}

function transferCrossChain(
address destination,
address receiver,
address zrc20,
uint256 amount
) public {
if (receiver == address(0)) revert InvalidAddress();
_burn(msg.sender, amount);

(, uint256 gasFee) = IZRC20(zrc20).withdrawGasFeeWithGasLimit(gasLimit);
if (!IZRC20(zrc20).transferFrom(msg.sender, address(this), gasFee))
revert TransferFailed();
IZRC20(zrc20).approve(address(gateway), gasFee);

bytes memory encodedData = abi.encode(receiver, amount);
(, uint256 gasFee) = IZRC20(destination).withdrawGasFeeWithGasLimit(
gasLimit
);
if (
!IZRC20(destination).transferFrom(msg.sender, address(this), gasFee)
) revert TransferFailed();
IZRC20(destination).approve(address(gateway), gasFee);
bytes memory message = abi.encode(receiver, amount);

CallOptions memory callOptions = CallOptions(gasLimit, false);

RevertOptions memory revertOptions = RevertOptions(
address(this),
true,
address(0),
encodedData,
message,
gasLimit
);

gateway.call(
counterparty[zrc20],
zrc20,
encodedData,
counterparty[destination],
destination,
message,
callOptions,
revertOptions
);
emit TokenTransfer(destination, receiver, amount);
}

function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}

function onCall(
MessageContext calldata messageContext,
MessageContext calldata context,
address zrc20,
uint256 amount,
bytes calldata message
) external override onlyGateway {
if (keccak256(messageContext.origin) != keccak256(counterparty[zrc20]))
revert("Unauthorized");
(address receiver, uint256 tokenAmount, address destination) = abi
.decode(message, (address, uint256, address));
if (keccak256(context.origin) != keccak256(counterparty[zrc20]))
revert Unauthorized();
(address destination, address receiver, uint256 tokenAmount) = abi
.decode(message, (address, address, uint256));
if (destination == address(0)) {
_mint(receiver, tokenAmount);
} else {
(, uint256 gasFee) = IZRC20(destination).withdrawGasFeeWithGasLimit(
700000
gasLimit
);
SwapHelperLib.swapExactTokensForTokens(
systemContract,
Expand All @@ -109,11 +122,19 @@ contract Universal is ERC20, Ownable, UniversalContract {
counterparty[destination],
destination,
abi.encode(receiver, tokenAmount),
CallOptions(700000, false),
CallOptions(gasLimit, false),
RevertOptions(address(0), false, address(0), "", 0)
);
}
emit TokenTransferToDestination(destination, receiver, amount);
}

function onRevert(RevertContext calldata context) external onlyGateway {}
function onRevert(RevertContext calldata context) external onlyGateway {
(address sender, uint256 amount) = abi.decode(
context.revertMessage,
(address, uint256)
);
_mint(sender, amount);
emit TokenTransferReverted(sender, amount);
}
}
24 changes: 24 additions & 0 deletions examples/token/contracts/shared/Events.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract Events {
event CounterpartyMappingSet(
address indexed zrc20,
bytes indexed contractAddress
);
event CounterpartySet(address indexed contractAddress);

event TokenMinted(address indexed to, uint256 amount);
event TokenTransfer(
address indexed destination,
address indexed receiver,
uint256 amount
);
event TokenTransferReceived(address indexed receiver, uint256 amount);
event TokenTransferReverted(address indexed sender, uint256 amount);
event TokenTransferToDestination(
address indexed destination,
address indexed sender,
uint256 amount
);
}
3 changes: 2 additions & 1 deletion examples/token/scripts/test.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/bin/bash

set -e
set -x

if [ "$1" = "localnet" ]; then
npx hardhat localnet --exit-on-error & sleep 10
Expand Down Expand Up @@ -28,7 +29,7 @@ GATEWAY_BNB=$(jq -r '.addresses[] | select(.type=="gatewayEVM" and .chain=="bnb"
SENDER=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266


CONTRACT_ZETACHAIN=$(npx hardhat deploy --network localhost --json | jq -r '.contractAddress')
CONTRACT_ZETACHAIN=$(npx hardhat deploy --network localhost --gas-limit 700000 --json | jq -r '.contractAddress')
echo -e "\n🚀 Deployed contract on ZetaChain: $CONTRACT_ZETACHAIN"

CONTRACT_ETHEREUM=$(npx hardhat deploy --name Connected --json --network localhost --gateway "$GATEWAY_ETHEREUM" | jq -r '.contractAddress')
Expand Down
13 changes: 11 additions & 2 deletions examples/token/tasks/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => {
);
}

const factory = await hre.ethers.getContractFactory(args.name);
const contract = await factory.deploy(args.gateway, signer.address);
const factory: any = await hre.ethers.getContractFactory(args.name);
const contract = await factory.deploy(
args.gateway,
signer.address,
args.tokenName,
args.tokenSymbol,
...(args.gasLimit ? [args.gasLimit] : [])
);
await contract.deployed();

if (args.json) {
Expand All @@ -33,7 +39,10 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => {

task("deploy", "Deploy the NFT contract", main)
.addFlag("json", "Output the result in JSON format")
.addOptionalParam("tokenName", "Token name", "Universal Token")
.addOptionalParam("tokenSymbol", "Token symbol", "UFT")
.addOptionalParam("name", "The contract name to deploy", "Universal")
.addOptionalParam("gasLimit", "Gas limit for the transaction")
.addOptionalParam(
"gateway",
"Gateway address (default: ZetaChain Gateway)",
Expand Down
6 changes: 5 additions & 1 deletion examples/token/tasks/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => {

const receiver = args.receiver || signer.address;

console.log(args.to, receiver, args.amount, {
...txOptions,
value: gasAmount,
});
tx = await (contract as any).transferCrossChain(
receiver,
args.to,
receiver,
args.amount,
{ ...txOptions, value: gasAmount }
);
Expand Down

0 comments on commit 0adfb61

Please sign in to comment.