Skip to content

Commit

Permalink
Call from EVM (#201)
Browse files Browse the repository at this point in the history
  • Loading branch information
fadeev authored Oct 16, 2024
1 parent 2f1b130 commit 893d2bf
Show file tree
Hide file tree
Showing 15 changed files with 391 additions and 110 deletions.
46 changes: 38 additions & 8 deletions examples/hello/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,45 @@
# ZetaChain Contracts Template
# Hello Example

## Getting Started
```
yarn deploy
```

## EVM

Successful call:

```
npx hardhat echo-call --contract 0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690 --receiver 0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E --network localhost --types '["string"]' hello
```

Failed call:

```
npx hardhat echo-call --contract 0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690 --receiver 0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E --network localhost --types '["uint256"]' 42
```

Install dependencies:
Failed call with handled revert:

```
yarn
npx hardhat echo-call --contract 0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690 --receiver 0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E --network localhost --revert-address 0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690 --revert-message 0x --call-on-revert --types '["uint256"]' 42
```

## Next Steps
## ZetaChain

Ready to dive in? Follow our [**🚀 smart contract
tutorials**](https://www.zetachain.com/docs/developers/tutorials/intro/) to
start building universal app contracts.
Successful call:

```
npx hardhat hello-call --contract 0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E --receiver 0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690 --zrc20 0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe --function "hello(string)" --network localhost --types '["string"]' hello
```

Failed call:

```
npx hardhat hello-call --contract 0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E --receiver 0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690 --zrc20 0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe --function "hello(string)" --network localhost --types '["uint256"]' 42
```

Failed call with handled revert:

```
npx hardhat hello-call --contract 0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E --receiver 0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690 --zrc20 0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe --function "hello(string)" --network localhost --revert-address 0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E --revert-message 0x --call-on-revert --types '["uint256"]' 42
```
36 changes: 36 additions & 0 deletions examples/hello/contracts/Echo.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {RevertContext} from "@zetachain/protocol-contracts/contracts/Revert.sol";
import "@zetachain/protocol-contracts/contracts/evm/GatewayEVM.sol";

contract Echo {
GatewayEVM public immutable gateway;

event RevertEvent(string, RevertContext);
event HelloEvent(string, string);

constructor(address payable gatewayAddress) {
gateway = GatewayEVM(gatewayAddress);
}

function hello(string memory message) external payable {
emit HelloEvent("Hello on EVM", message);
}

function onRevert(RevertContext calldata revertContext) external {
emit RevertEvent("Revert on EVM", revertContext);
}

function call(
address receiver,
bytes calldata message,
RevertOptions memory revertOptions
) external {
gateway.call(receiver, message, revertOptions);
}

receive() external payable {}

fallback() external payable {}
}
41 changes: 39 additions & 2 deletions examples/hello/contracts/Hello.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import "@zetachain/protocol-contracts/contracts/zevm/interfaces/IGatewayZEVM.sol
import "@zetachain/protocol-contracts/contracts/zevm/GatewayZEVM.sol";

contract Hello is UniversalContract {
GatewayZEVM public gateway;
GatewayZEVM public immutable gateway;

event HelloEvent(string, string);
event RevertEvent(string, RevertContext);
error TransferFailed();

constructor(address payable gatewayAddress) {
gateway = GatewayZEVM(gatewayAddress);
Expand All @@ -30,15 +31,51 @@ contract Hello is UniversalContract {
emit RevertEvent("Revert on ZetaChain", revertContext);
}

function gatewayCall(
function call(
bytes memory receiver,
address zrc20,
bytes calldata message,
uint256 gasLimit,
RevertOptions memory revertOptions
) external {
(, uint256 gasFee) = IZRC20(zrc20).withdrawGasFeeWithGasLimit(gasLimit);
if (!IZRC20(zrc20).transferFrom(msg.sender, address(this), gasFee))
revert TransferFailed();
IZRC20(zrc20).approve(address(gateway), gasFee);
gateway.call(receiver, zrc20, message, gasLimit, revertOptions);
}

function withdrawAndCall(
bytes memory receiver,
uint256 amount,
address zrc20,
bytes calldata message,
uint256 gasLimit,
RevertOptions memory revertOptions
) external {
(address gasZRC20, uint256 gasFee) = IZRC20(zrc20)
.withdrawGasFeeWithGasLimit(gasLimit);
uint256 target = zrc20 == gasZRC20 ? amount + gasFee : amount;
if (!IZRC20(zrc20).transferFrom(msg.sender, address(this), target))
revert TransferFailed();
IZRC20(zrc20).approve(address(gateway), target);
if (zrc20 != gasZRC20) {
if (
!IZRC20(gasZRC20).transferFrom(
msg.sender,
address(this),
gasFee
)
) revert TransferFailed();
IZRC20(gasZRC20).approve(address(gateway), gasFee);
}
gateway.withdrawAndCall(
receiver,
amount,
zrc20,
message,
gasLimit,
revertOptions
);
}
}
21 changes: 0 additions & 21 deletions examples/hello/contracts/Revert.sol

This file was deleted.

5 changes: 3 additions & 2 deletions examples/hello/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "./tasks/deploy";
import "./tasks/deployRevert";
import "./tasks/gatewayCall";
import "./tasks/helloCall";
import "./tasks/echoCall";
import "./tasks/helloWithdrawAndCall";
import "@zetachain/localnet/tasks";
import "@nomicfoundation/hardhat-toolbox";
import "@zetachain/toolkit/tasks";
Expand Down
6 changes: 3 additions & 3 deletions examples/hello/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"test": "echo \"Error: no test specified\" && exit 1",
"lint:fix": "npx eslint . --ext .js,.ts --fix",
"lint": "npx eslint . --ext .js,.ts",
"deploy": "npx hardhat compile --force && npx hardhat deploy --network localhost && npx hardhat deploy-revert --network localhost"
"deploy": "npx hardhat compile --force && npx hardhat deploy --network localhost && npx hardhat deploy --name Echo --network localhost --gateway 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"
},
"keywords": [],
"author": "",
Expand All @@ -28,8 +28,8 @@
"@types/node": ">=12.0.0",
"@typescript-eslint/eslint-plugin": "^5.59.9",
"@typescript-eslint/parser": "^5.59.9",
"@zetachain/localnet": "^3.0.4",
"@zetachain/toolkit": "13.0.0-rc2",
"@zetachain/localnet": "^3.3.0",
"@zetachain/toolkit": "13.0.0-rc4",
"axios": "^1.3.6",
"chai": "^4.2.0",
"dotenv": "^16.0.3",
Expand Down
6 changes: 3 additions & 3 deletions examples/hello/tasks/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => {
}

const factory = await hre.ethers.getContractFactory(args.name);
const contract = await (factory as any).deploy(args.gatewayZetaChain);
const contract = await (factory as any).deploy(args.gateway);
await contract.deployed();

if (args.json) {
Expand All @@ -30,7 +30,7 @@ task("deploy", "Deploy the contract", main)
.addFlag("json", "Output in JSON")
.addOptionalParam("name", "Contract to deploy", "Hello")
.addOptionalParam(
"gatewayZetaChain",
"Gateway address",
"gateway",
"Gateway address (default: ZetaChain Gateway)",
"0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0"
);
31 changes: 0 additions & 31 deletions examples/hello/tasks/deployRevert.ts

This file was deleted.

99 changes: 99 additions & 0 deletions examples/hello/tasks/echoCall.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { task, types } from "hardhat/config";
import type { HardhatRuntimeEnvironment } from "hardhat/types";

const main = async (args: any, hre: HardhatRuntimeEnvironment) => {
const { ethers } = hre;
const [signer] = await ethers.getSigners();

const txOptions = {
gasPrice: args.txOptionsGasPrice,
gasLimit: args.txOptionsGasLimit,
};

const revertOptions = {
abortAddress: "0x0000000000000000000000000000000000000000", // not used
callOnRevert: args.callOnRevert,
onRevertGasLimit: args.onRevertGasLimit,
revertAddress: args.revertAddress,
revertMessage: ethers.utils.hexlify(
ethers.utils.toUtf8Bytes(args.revertMessage)
),
};

const types = JSON.parse(args.types);

if (types.length !== args.values.length) {
throw new Error(
`The number of types (${types.length}) does not match the number of values (${args.values.length}).`
);
}

const valuesArray = args.values.map((value: any, index: number) => {
const type = types[index];

if (type === "bool") {
try {
return JSON.parse(value.toLowerCase());
} catch (e) {
throw new Error(`Invalid boolean value: ${value}`);
}
} else if (type.startsWith("uint") || type.startsWith("int")) {
return ethers.BigNumber.from(value);
} else {
return value;
}
});
const encodedParameters = ethers.utils.defaultAbiCoder.encode(
types,
valuesArray
);

const factory = (await hre.ethers.getContractFactory(args.name)) as any;
const contract = factory.attach(args.contract).connect(signer);

const tx = await contract.call(
args.receiver,
encodedParameters,
revertOptions,
txOptions
);

console.log(`Transaction hash: ${tx.hash}`);
await tx.wait();
console.log("gatewayCall executed successfully");
};

task("echo-call", "Calls the gateway on a contract on EVM", main)
.addParam("contract", "The address of the deployed contract")
.addOptionalParam(
"txOptionsGasPrice",
"The gas price for the transaction",
10000000000,
types.int
)
.addOptionalParam(
"txOptionsGasLimit",
"The gas limit for the transaction",
7000000,
types.int
)
.addFlag("callOnRevert", "Whether to call on revert")
.addOptionalParam(
"revertAddress",
"Revert address",
"0x0000000000000000000000000000000000000000"
)
.addOptionalParam("revertMessage", "Revert message", "0x")
.addParam(
"receiver",
"The address of the receiver contract on a connected chain"
)
.addOptionalParam(
"onRevertGasLimit",
"The gas limit for the revert transaction",
7000000,
types.int
)
.addParam("name", "The name of the contract", "Echo")
.addParam("types", `The types of the parameters (example: '["string"]')`)
.addVariadicPositionalParam("values", "The values of the parameters");
Loading

0 comments on commit 893d2bf

Please sign in to comment.