Skip to content

Commit

Permalink
feat(messaging): add --fees optional param to set ZETA/native fees (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
fadeev authored Sep 28, 2023
1 parent bb3884a commit 3b21660
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 56 deletions.
11 changes: 10 additions & 1 deletion lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,18 @@ const prepareData = (args: any) => {
return [n, `args.${p[0]}`];
});

const feesNative = args.fees === "native";

return {
args,
arguments: { casts, names, pairs, pairsWithDataLocation, types },
arguments: {
casts,
feesNative,
names,
pairs,
pairsWithDataLocation,
types,
},
contractName,
contractNameUnderscore: camelToUnderscoreUpper(contractName),
};
Expand Down
8 changes: 7 additions & 1 deletion tasks/messaging.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as fs from "fs";
import { task } from "hardhat/config";
import { task, types } from "hardhat/config";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import * as path from "path";

Expand Down Expand Up @@ -28,6 +28,12 @@ export const messagingTask = task(
main
)
.addPositionalParam("name", "Name of the contract")
.addOptionalParam(
"fees",
"Use ZETA or native gas tokens for cross-chain fees",
"native",
types.string
)
.addOptionalVariadicPositionalParam(
"arguments",
"Arguments for a crosschain call (e.g. dest:address to:bytes32 output:uint256)"
Expand Down
37 changes: 21 additions & 16 deletions templates/messaging/contracts/{{contractName}}.sol.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,48 @@ import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@zetachain/protocol-contracts/contracts/evm/tools/ZetaInteractor.sol";
import "@zetachain/protocol-contracts/contracts/evm/interfaces/ZetaInterfaces.sol";
{{#unless arguments.feesNative}}
import "@zetachain/protocol-contracts/contracts/evm/Zeta.eth.sol";
{{/unless}}

interface {{contractName}}Errors {
contract {{contractName}} is ZetaInteractor, ZetaReceiver {
error InvalidMessageType();
}

contract {{contractName}} is
ZetaInteractor,
ZetaReceiver,
{{contractName}}Errors
{
bytes32 public constant {{contractNameUnderscore}}_MESSAGE_TYPE =
keccak256("CROSS_CHAIN_{{contractNameUnderscore}}");
{{#unless arguments.feesNative}}
error ErrorTransferringZeta();
{{/unless}}

event {{contractName}}Event({{#each arguments.pairs}}{{#if @index}}, {{/if}}{{this.[1]}}{{/each}});
event {{contractName}}RevertedEvent({{#each arguments.pairs}}{{#if @index}}, {{/if}}{{this.[1]}}{{/each}});

bytes32 public constant {{contractNameUnderscore}}_MESSAGE_TYPE =
keccak256("CROSS_CHAIN_{{contractNameUnderscore}}");
{{#if arguments.feesNative}}
ZetaTokenConsumer private immutable _zetaConsumer;
{{/if}}
IERC20 internal immutable _zetaToken;

constructor(
address connectorAddress,
address zetaTokenAddress,
address zetaConsumerAddress
) ZetaInteractor(connectorAddress) {
constructor(address connectorAddress, address zetaTokenAddress{{#if arguments.feesNative}}, address zetaConsumerAddress{{/if}}) ZetaInteractor(connectorAddress) {
_zetaToken = IERC20(zetaTokenAddress);
{{#if arguments.feesNative}}
_zetaConsumer = ZetaTokenConsumer(zetaConsumerAddress);
{{/if}}
}

function sendMessage(uint256 destinationChainId{{#if arguments.pairsWithDataLocation}}, {{#each arguments.pairsWithDataLocation}}{{#if @index}}, {{/if}}{{this.[1]}} {{this.[0]}}{{/each}}{{/if}}) external payable {
function sendMessage(uint256 destinationChainId{{#if arguments.pairsWithDataLocation}}, {{#each arguments.pairsWithDataLocation}}{{#if @index}}, {{/if}}{{this.[1]}} {{this.[0]}}{{/each}}{{/if}}{{#unless arguments.feesNative}}, uint256 zetaValueAndGas{{/unless}}) external payable {
if (!_isValidChainId(destinationChainId))
revert InvalidDestinationChainId();

{{#if arguments.feesNative}}
uint256 crossChainGas = 2 * (10 ** 18);
uint256 zetaValueAndGas = _zetaConsumer.getZetaFromEth{
value: msg.value
}(address(this), crossChainGas);
_zetaToken.approve(address(connector), zetaValueAndGas);
{{else}}
bool success1 = _zetaToken.approve(address(connector), zetaValueAndGas);
bool success2 = _zetaToken.transferFrom(msg.sender, address(this), zetaValueAndGas);
if (!(success1 && success2)) revert ErrorTransferringZeta();
{{/if}}

connector.send(
ZetaInterfaces.SendInput({
Expand Down
64 changes: 32 additions & 32 deletions templates/messaging/tasks/deploy.ts.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,22 @@ const contractName = "{{contractName}}";

const main = async (args: any, hre: HardhatRuntimeEnvironment) => {
const networks = args.networks.split(",");
// A mapping between network names and deployed contract addresses.
const contracts: { [key: string]: string } = {};
await Promise.all(
networks.map(async (networkName: string) => {
contracts[networkName] = await deployContract(hre, networkName);
contracts[networkName] = await deployContract(hre, networkName, args.json);
})
);

for (const source in contracts) {
await setInteractors(hre, source, contracts);
await setInteractors(hre, source, contracts, args.json);
}

if (args.json) {
console.log(JSON.stringify(contracts, null, 2));
}
};

// Initialize a wallet using a network configuration and a private key from
// environment variables.
const initWallet = (hre: HardhatRuntimeEnvironment, networkName: string) => {
const { url } = hre.config.networks[networkName] as any;
const provider = new ethers.providers.JsonRpcProvider(url);
Expand All @@ -31,17 +32,16 @@ const initWallet = (hre: HardhatRuntimeEnvironment, networkName: string) => {
return wallet;
};

// Deploy the contract on the specified network. deployContract reads the
// contract artifact, creates a contract factory, and deploys the contract using
// that factory.
const deployContract = async (
hre: HardhatRuntimeEnvironment,
networkName: string
networkName: string,
json: boolean = false
) => {
const wallet = initWallet(hre, networkName);

const connector = getAddress("connector", networkName as any);
const zetaToken = getAddress("zetaToken", networkName as any);
{{#if arguments.feesNative}}
const zetaTokenConsumerUniV2 = getAddress(
"zetaTokenConsumerUniV2",
networkName as any
Expand All @@ -50,42 +50,38 @@ const deployContract = async (
"zetaTokenConsumerUniV3",
networkName as any
);
{{/if}}

const { abi, bytecode } = await hre.artifacts.readArtifact(contractName);
const factory = new ethers.ContractFactory(abi, bytecode, wallet);
const contract = await factory.deploy(
connector,
zetaToken,
zetaTokenConsumerUniV2 || zetaTokenConsumerUniV3
);
const contract = await factory.deploy(connector, zetaToken{{#if arguments.feesNative}}, zetaTokenConsumerUniV2 || zetaTokenConsumerUniV3{{/if}});

await contract.deployed();
console.log(`
if (!json) {
console.log(`
🚀 Successfully deployed contract on ${networkName}.
📜 Contract address: ${contract.address}`);
}
return contract.address;
};

// Set interactors for a contract. setInteractors attaches to the contract
// deployed at the specified address, and for every other network, sets the
// deployed contract's address as an interactor.
const setInteractors = async (
hre: HardhatRuntimeEnvironment,
source: string,
contracts: { [key: string]: string }
contracts: { [key: string]: string },
json: boolean = false
) => {
console.log(`
if (!json) {
console.log(`
🔗 Setting interactors for a contract on ${source}`);
}
const wallet = initWallet(hre, source);

const { abi, bytecode } = await hre.artifacts.readArtifact(contractName);
const factory = new ethers.ContractFactory(abi, bytecode, wallet);
const contract = factory.attach(contracts[source]);

for (const counterparty in contracts) {
// Skip the destination network if it's the same as the source network.
// For example, we don't need to set an interactor for a contract on
// Goerli if the destination network is also Goerli.
if (counterparty === source) continue;

const counterpartyContract = hre.ethers.utils.solidityPack(
Expand All @@ -96,15 +92,19 @@ const setInteractors = async (
await (
await contract.setInteractorByChainId(chainId, counterpartyContract)
).wait();
console.log(
`✅ Interactor address for ${chainId} (${counterparty}) is set to ${counterpartyContract}`
);
if (!json) {
console.log(
`✅ Interactor address for ${chainId} (${counterparty}) is set to ${counterpartyContract}`
);
}
}
};

task("deploy", "Deploy the contract", main).addParam(
"networks",
`Comma separated list of networks to deploy to (e.g. ${getSupportedNetworks(
"ccm"
)})`
);
task("deploy", "Deploy the contract", main)
.addParam(
"networks",
`Comma separated list of networks to deploy to (e.g. ${getSupportedNetworks(
"ccm"
)})`
)
.addFlag("json", "Output JSON");
6 changes: 3 additions & 3 deletions templates/messaging/tasks/interact.ts.hbs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { task } from "hardhat/config";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { parseEther } from "@ethersproject/units";
import { trackCCTX } from "@zetachain/toolkit/helpers";

const contractName = "{{contractName}}";

Expand All @@ -20,15 +19,16 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => {
{{#each arguments.casts}}const param{{this.[0]}} = {{this.[1]}};
{{/each}}

const value = parseEther(args.amount);

const tx = await contract
.connect(signer)
.sendMessage(destination, {{#each arguments.casts}} param{{this.[0]}}, {{/each}} { value: parseEther(args.amount) });
.sendMessage(destination{{#each arguments.casts}}, param{{this.[0]}}{{/each}}{{#if arguments.feesNative}}, { value }{{else}}, value{{/if}});

const receipt = await tx.wait();
console.log(`✅ The transaction has been broadcasted to ${hre.network.name}
📝 Transaction hash: ${receipt.transactionHash}
`);
await trackCCTX(tx.hash);
};

task("interact", "Sends a message from one chain to another.", main)
Expand Down
3 changes: 1 addition & 2 deletions templates/omnichain/tasks/interact.ts.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { task } from "hardhat/config";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { parseEther } from "@ethersproject/units";
import { getAddress } from "@zetachain/protocol-contracts";
import { prepareData, trackCCTX } from "@zetachain/toolkit/helpers";
import { prepareData } from "@zetachain/toolkit/helpers";

const main = async (args: any, hre: HardhatRuntimeEnvironment) => {
const [signer] = await hre.ethers.getSigners();
Expand All @@ -22,7 +22,6 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => {
🚀 Successfully broadcasted a token transfer transaction on ${hre.network.name} network.
📝 Transaction hash: ${tx.hash}
`);
await trackCCTX(tx.hash);
};

task("interact", "Interact with the contract", main)
Expand Down
Loading

0 comments on commit 3b21660

Please sign in to comment.