Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(messaging): add --fees optional param to set ZETA/native fees #63

Merged
merged 4 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading