Make cross-chain development on top of LayerZero easier.
LZ Kit was designed to be used for a hardhat
project so if you didn't setup hardhat, follow
this tutorial first.
You also need to install hardhat-deploy
plugin to run lz-kit test
. Follow
this link for details.
npm install -g @lz-kit/cli
or
yarn global add @lz-kit/cli
You need to import @lz-kit/cli/hardhat
in your hardhat.config.js
or hardhat.config.ts
:
import "@lz-kit/cli/hardhat";
or
require("@lz-kit/cli/hardhat");
Set environment variables for reuse:
MNEMONIC=<your seed phrase>
Bootstrap ethereum, optimism and arbitrum with one command (fork networks and spin up relayers between them):
lz-kit bootstrap --mnemonic "$MNEMONIC" --balance 10000 --accounts 3 ethereum optimism arbitrum
Write a cross chain contract that extends LzApp
:
// contracts/MyLzApp.sol
pragma solidity ^0.8.0;
import "@layerzerolabs/solidity-examples/contracts/lzApp/LzApp.sol";
contract MyLzApp is LzApp {
constructor(address _lzEndpoint) LzApp(_lzEndpoint) {}
// ... send and receive functions
}
Write a deployment script to deploy MyLzApp
:
// deploy/00_MyLzApp.ts
import { constants } from "ethers";
import { endpoint } from "../constants/layerzero.json";
export default async ({ getNamedAccounts, deployments, network }) => {
const { deploy } = deployments;
const { deployer } = await getNamedAccounts();
await deploy("MyLzApp", {
from: deployer,
args: [endpoint[network.name]],
log: true,
});
};
Deploy contracts, configure trusted remotes with one command
(--config only works if the contract is a subclass of LzApp
):
```shell
lz-kit deploy --networks ethereum-fork arbitrum-fork optimism-fork --mnemonic "$MNEMONIC" --config MyLzApp
Write a sample test script:
// test/MyLzApp.test.ts
import { utils } from "ethers";
import { getChain } from "hardhat";
import { getChainId } from "./ethers";
describe("MyLzApp", function () {
it("should test", async function () {
const ethereum = await getChain("ethereum-fork");
const arbitrum = await getChain("arbitrum-fork");
const optimism = await getChain("optimism-fork");
// your testing code
});
});
Run tests:
lz-kit test --networks ethereum-fork -d arbitrum-fork optimism-fork --mnemonic "$MNEMONIC" --deploy --config MyLzApp
In your test script, you can access getChain()
in the HRE object. For example:
import { utils } from "ethers";
import { getChain } from "hardhat";
describe("MyLzApp", function () {
it("should test", async function () {
const ethereum = await getChain("ethereum-fork");
const [deployer, alice, bob, carol] = await ethereum.getSigners();
});
});
Chain
object is defined as below:
export interface SignerWithAddress extends Signer {
address: string;
}
export interface Snapshot {
restore: () => Promise<void>;
id: string;
}
export interface ForkedNetwork {
chainId: number;
forkBlockNumber: number;
forkBlockHash: string;
}
export interface Chain {
name: string;
config: HttpNetworkConfig;
provider: providers.JsonRpcProvider;
forkedNetwork?: ForkedNetwork;
lzChainId: number;
snapshot: Snapshot;
takeSnapshot: () => Promise<Snapshot>;
getSigners: () => Promise<Array<SignerWithAddress>>;
getSigner: (address: string) => Promise<SignerWithAddress>;
getImpersonatedSigner: (
address: string,
balance?: BigNumberish
) => Promise<SignerWithAddress>;
getContract: <T extends Contract>(
name: string,
signer?: Signer
) => Promise<T>;
getContractAt: <T extends Contract>(
nameOrAbi: string | unknown[],
address: string,
signer?: Signer
) => Promise<T>;
setBalance: (address: string, balance: BigNumberish) => Promise<void>;
}
Distributed under the MIT License. See LICENSE
for more information.