From cc57caa123dc1b379ed1aef8a4a0cbaa56afb68c Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Thu, 3 Oct 2024 12:13:07 +0300 Subject: [PATCH] Call from EVM --- examples/hello/README.md | 46 ++++++-- .../hello/contracts/{Revert.sol => Echo.sol} | 17 ++- examples/hello/hardhat.config.ts | 4 +- examples/hello/package.json | 4 +- examples/hello/tasks/callFromEVM.ts | 103 ++++++++++++++++++ .../{gatewayCall.ts => callFromZetachain.ts} | 7 +- examples/hello/tasks/deploy.ts | 6 +- examples/hello/tasks/deployRevert.ts | 31 ------ examples/swap/package.json | 2 +- 9 files changed, 169 insertions(+), 51 deletions(-) rename examples/hello/contracts/{Revert.sol => Echo.sol} (55%) create mode 100644 examples/hello/tasks/callFromEVM.ts rename examples/hello/tasks/{gatewayCall.ts => callFromZetachain.ts} (93%) delete mode 100644 examples/hello/tasks/deployRevert.ts diff --git a/examples/hello/README.md b/examples/hello/README.md index 62730f9d..5cadb39d 100644 --- a/examples/hello/README.md +++ b/examples/hello/README.md @@ -1,15 +1,45 @@ -# ZetaChain Contracts Template +# Hello Example -## Getting Started +``` +yarn deploy +``` + +## EVM + +Successful call: + +``` +npx hardhat call-from-evm --contract 0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E --receiver 0x67d269191c92Caf3cD7723F116c85e6E9bf55933 --network localhost --types '["string"]' hello +``` + +Failed call: + +``` +npx hardhat call-from-evm --contract 0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E --receiver 0x67d269191c92Caf3cD7723F116c85e6E9bf55933 --network localhost --types '["uint256"]' 42 +``` -Install dependencies: +Failed call with handled revert: ``` -yarn +npx hardhat call-from-evm --contract 0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E --receiver 0x67d269191c92Caf3cD7723F116c85e6E9bf55933 --network localhost --revert-address 0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E --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 call-from-zetachain --contract 0x67d269191c92Caf3cD7723F116c85e6E9bf55933 --receiver 0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E --zrc20 0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe --function "hello(string)" --network localhost --types '["string"]' hello +``` + +Failed call: + +``` +npx hardhat call-from-zetachain --contract 0x67d269191c92Caf3cD7723F116c85e6E9bf55933 --receiver 0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E --zrc20 0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe --function "hello(string)" --network localhost --types '["uint256"]' 42 +``` + +Failed call with handled revert: + +``` +npx hardhat call-from-zetachain --contract 0x67d269191c92Caf3cD7723F116c85e6E9bf55933 --receiver 0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E --zrc20 0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe --function "hello(string)" --network localhost --revert-address 0x67d269191c92Caf3cD7723F116c85e6E9bf55933 --revert-message 0x --call-on-revert --types '["uint256"]' 42 +``` diff --git a/examples/hello/contracts/Revert.sol b/examples/hello/contracts/Echo.sol similarity index 55% rename from examples/hello/contracts/Revert.sol rename to examples/hello/contracts/Echo.sol index aa0b821e..8cbe185a 100644 --- a/examples/hello/contracts/Revert.sol +++ b/examples/hello/contracts/Echo.sol @@ -2,11 +2,18 @@ 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 gateway; -contract Revert { event RevertEvent(string, RevertContext); event HelloEvent(string, string); + constructor(address payable gatewayAddress) { + gateway = GatewayEVM(gatewayAddress); + } + function hello(string memory message) external { emit HelloEvent("Hello on EVM", message); } @@ -15,6 +22,14 @@ contract Revert { emit RevertEvent("Revert on EVM", revertContext); } + function gatewayCall( + address receiver, + bytes calldata message, + RevertOptions memory revertOptions + ) external { + gateway.call(receiver, message, revertOptions); + } + receive() external payable {} fallback() external payable {} diff --git a/examples/hello/hardhat.config.ts b/examples/hello/hardhat.config.ts index 7b9bd47f..b4454dba 100644 --- a/examples/hello/hardhat.config.ts +++ b/examples/hello/hardhat.config.ts @@ -1,6 +1,6 @@ import "./tasks/deploy"; -import "./tasks/deployRevert"; -import "./tasks/gatewayCall"; +import "./tasks/callFromZetachain"; +import "./tasks/callFromEVM"; import "@zetachain/localnet/tasks"; import "@nomicfoundation/hardhat-toolbox"; import "@zetachain/toolkit/tasks"; diff --git a/examples/hello/package.json b/examples/hello/package.json index 2a8151fd..bb47d44b 100644 --- a/examples/hello/package.json +++ b/examples/hello/package.json @@ -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": "", @@ -59,4 +59,4 @@ "@solana/web3.js": "^1.95.2", "@zetachain/protocol-contracts": "10.0.0-rc10" } -} +} \ No newline at end of file diff --git a/examples/hello/tasks/callFromEVM.ts b/examples/hello/tasks/callFromEVM.ts new file mode 100644 index 00000000..19977041 --- /dev/null +++ b/examples/hello/tasks/callFromEVM.ts @@ -0,0 +1,103 @@ +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); + + 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 gasLimit = hre.ethers.BigNumber.from(args.gasLimit); + + const factory = (await hre.ethers.getContractFactory(args.name)) as any; + const contract = factory.attach(args.contract); + + const tx = await contract.gatewayCall( + args.receiver, + encodedParameters, + revertOptions, + txOptions + ); + + console.log(`Transaction hash: ${tx.hash}`); + await tx.wait(); + console.log("gatewayCall executed successfully"); +}; + +task("call-from-evm", "Calls the gateway on a contract on EVM", main) + .addParam("contract", "The address of the deployed contract") + .addOptionalParam( + "gasLimit", + "Gas limit for for a cross-chain call", + 7000000, + types.int + ) + .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"); + +module.exports = {}; diff --git a/examples/hello/tasks/gatewayCall.ts b/examples/hello/tasks/callFromZetachain.ts similarity index 93% rename from examples/hello/tasks/gatewayCall.ts rename to examples/hello/tasks/callFromZetachain.ts index 35a34acd..ccf2325d 100644 --- a/examples/hello/tasks/gatewayCall.ts +++ b/examples/hello/tasks/callFromZetachain.ts @@ -60,7 +60,7 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { await zrc20TransferTx.wait(); - const factory = await hre.ethers.getContractFactory("Hello"); + const factory = (await hre.ethers.getContractFactory(args.name)) as any; const contract = factory.attach(args.contract); const tx = await contract.gatewayCall( @@ -78,8 +78,8 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { }; task( - "gateway-call", - "Calls the gatewayCall function on the Hello contract", + "call-from-zetachain", + "Calls the gatewayCall function on a contract on ZetaChain", main ) .addParam("contract", "The address of the deployed Hello contract") @@ -120,6 +120,7 @@ task( types.int ) .addParam("function", `Function to call (example: "hello(string)")`) + .addParam("name", "The name of the contract", "Hello") .addParam("types", `The types of the parameters (example: '["string"]')`) .addVariadicPositionalParam("values", "The values of the parameters"); diff --git a/examples/hello/tasks/deploy.ts b/examples/hello/tasks/deploy.ts index 647ea6bf..877d2b34 100644 --- a/examples/hello/tasks/deploy.ts +++ b/examples/hello/tasks/deploy.ts @@ -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) { @@ -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" ); diff --git a/examples/hello/tasks/deployRevert.ts b/examples/hello/tasks/deployRevert.ts deleted file mode 100644 index 080808c7..00000000 --- a/examples/hello/tasks/deployRevert.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { task, types } from "hardhat/config"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; - -const main = async (args: any, hre: HardhatRuntimeEnvironment) => { - const network = hre.network.name; - - 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(args.name); - const contract = await (factory as any).deploy(); - await contract.deployed(); - - if (args.json) { - console.log(JSON.stringify(contract)); - } else { - console.log(`🔑 Using account: ${signer.address} - -🚀 Successfully deployed "${args.name}" contract on ${network}. -📜 Contract address: ${contract.address} -`); - } -}; - -task("deploy-revert", "Deploy the contract", main) - .addFlag("json", "Output in JSON") - .addOptionalParam("name", "Contract to deploy", "Revert"); diff --git a/examples/swap/package.json b/examples/swap/package.json index 2d5c7eae..4c95cb25 100644 --- a/examples/swap/package.json +++ b/examples/swap/package.json @@ -59,4 +59,4 @@ "@zetachain/protocol-contracts": "10.0.0-rc10", "@zetachain/toolkit": "13.0.0-rc4" } -} +} \ No newline at end of file