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: task to stop localnet #34

Merged
merged 20 commits into from
Sep 12, 2024
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
32 changes: 32 additions & 0 deletions .github/workflows/run.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Run Localnet

on:
pull_request:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
start:
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "21"
registry-url: "https://registry.npmjs.org"

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1

- name: Install Dependencies
run: yarn

- name: Start Localnet
run: yarn hardhat localnet --stop-after-init
1 change: 1 addition & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import "./packages/tasks/src/localnet";
import "./packages/tasks/src/stop";

import { HardhatUserConfig } from "hardhat/config";

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"typescript": "^5.5.4"
},
"dependencies": {
"@inquirer/prompts": "^5.5.0",
"@uniswap/v2-core": "^1.0.1",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@zetachain/protocol-contracts": "10.0.0-rc10",
Expand Down
3 changes: 3 additions & 0 deletions packages/localnet/src/createToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,16 @@ export const createToken = async ({
deployOpts
);

await zrc20.waitForDeployment();

if (!isGasToken) {
const erc20Factory = new ethers.ContractFactory(
TestERC20.abi,
TestERC20.bytecode,
deployer
);
erc20 = await erc20Factory.deploy(symbol, symbol, deployOpts);
await erc20.waitForDeployment();
const erc20Decimals = await (erc20 as any).connect(deployer).decimals();

await (erc20 as any)
Expand Down
1 change: 1 addition & 0 deletions packages/tasks/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { localnetTask } from "./localnet";
export { localnetStopTask } from "./stop";
165 changes: 129 additions & 36 deletions packages/tasks/src/localnet.ts
Original file line number Diff line number Diff line change
@@ -1,72 +1,165 @@
import { task, types } from "hardhat/config";
import { initLocalnet } from "../../localnet/src";
import { exec } from "child_process";
import { exec, execSync } from "child_process";
import waitOn from "wait-on";
import ansis from "ansis";
import fs from "fs";
import { confirm } from "@inquirer/prompts";

const main = async (args: any) => {
const port = args.port || 8545;
const anvilArgs = args.anvil ? `${args.anvil}` : "";
const LOCALNET_PID_FILE = "./localnet.pid";

console.log(`Starting anvil on port ${port} with args: ${anvilArgs}`);
const killProcessOnPort = async (port: number, forceKill: boolean) => {
try {
const output = execSync(`lsof -ti tcp:${port}`).toString().trim();
if (output) {
console.log(
ansis.yellow(`Port ${port} is already in use by process ${output}.`)
);

if (forceKill) {
execSync(`kill -9 ${output}`);
console.log(
ansis.green(`Successfully killed process ${output} on port ${port}.`)
);
} else {
const answer = await confirm({
message: `Do you want to kill the process running on port ${port}?`,
default: true,
});

if (answer) {
execSync(`kill -9 ${output}`);
console.log(
ansis.green(
`Successfully killed process ${output} on port ${port}.`
)
);
} else {
console.log(ansis.red("Process not killed. Exiting..."));
process.exit(1);
}
}
}
} catch (error) {
// Silently continue if no process is found or killing fails
}
};

const localnet = async (args: any) => {
try {
execSync("which anvil");
} catch (error) {
console.error(
ansis.red(
"Error: 'anvil' not found. Please install Foundry: https://getfoundry.sh"
)
);
process.exit(1);
}

await killProcessOnPort(args.port, args.forceKill);

if (args.anvil !== "")
console.log(`Starting anvil on port ${args.port} with args: ${args.anvil}`);

const anvilProcess = exec(
`anvil --auto-impersonate --port ${port} ${anvilArgs}`
`anvil --auto-impersonate --port ${args.port} ${args.anvil}`
);

if (anvilProcess.stdout && anvilProcess.stderr) {
anvilProcess.stdout.pipe(process.stdout);
anvilProcess.stderr.pipe(process.stderr);
}

await waitOn({ resources: [`tcp:127.0.0.1:${port}`] });
await waitOn({ resources: [`tcp:127.0.0.1:${args.port}`] });

const addr = await initLocalnet(port);
const cleanup = () => {
console.log("\nShutting down anvil and cleaning up...");
if (anvilProcess) {
anvilProcess.kill();
}
if (fs.existsSync(LOCALNET_PID_FILE)) {
fs.unlinkSync(LOCALNET_PID_FILE);
}
};

console.log(ansis.cyan`
EVM Contract Addresses
======================
try {
const addr = await initLocalnet(args.port);

Gateway EVM: ${addr.gatewayEVM}
ERC-20 custody: ${addr.custodyEVM}
TSS: ${addr.tssEVM}
ZETA: ${addr.zetaEVM}`);
// EVM Contract Addresses
const evmHeader = "\nEVM Contract Addresses";
console.log(ansis.cyan(`${evmHeader}\n${"=".repeat(evmHeader.length)}`));

addr.foreignCoins
.filter((coin: any) => coin.asset !== "")
.forEach((coin: any) => {
console.log(ansis.cyan`ERC-20 ${coin.symbol}: ${coin.asset}`);
});
const evmAddresses = {
"Gateway EVM": addr.gatewayEVM,
"ERC-20 Custody": addr.custodyEVM,
TSS: addr.tssEVM,
ZETA: addr.zetaEVM,
...addr.foreignCoins
.filter((coin: any) => coin.asset !== "")
.reduce((acc: any, coin: any) => {
acc[`ERC-20 ${coin.symbol}`] = coin.asset;
return acc;
}, {}),
};

console.log(ansis.green`
ZetaChain Contract Addresses
============================
console.table(evmAddresses);

Gateway ZetaChain: ${addr.gatewayZetaChain}
ZETA: ${addr.zetaZetaChain}
Fungible module: ${addr.fungibleModuleZetaChain}
System contract: ${addr.sytemContractZetaChain}`);
const zetaHeader = "\nZetaChain Contract Addresses";
console.log(ansis.green(`${zetaHeader}\n${"=".repeat(zetaHeader.length)}`));

addr.foreignCoins.forEach((coin: any) => {
console.log(
ansis.green`ZRC-20 ${coin.symbol}: ${coin.zrc20_contract_address}`
);
});
const zetaAddresses = {
"Gateway ZetaChain": addr.gatewayZetaChain,
ZETA: addr.zetaZetaChain,
"Fungible Module": addr.fungibleModuleZetaChain,
"System Contract": addr.sytemContractZetaChain,
...addr.foreignCoins.reduce((acc: any, coin: any) => {
acc[`ZRC-20 ${coin.symbol}`] = coin.zrc20_contract_address;
return acc;
}, {}),
};

console.table(zetaAddresses);

fs.writeFileSync(LOCALNET_PID_FILE, process.pid.toString(), "utf-8");
} catch (error: any) {
console.error(ansis.red`Error initializing localnet: ${error}`);
cleanup();
process.exit(1);
}

process.on("SIGINT", () => {
console.log("\nReceived Ctrl-C, shutting down anvil...");
anvilProcess.kill();
const handleExit = (signal: string) => {
console.log(`\nReceived ${signal}, shutting down...`);
cleanup();
process.exit();
};

process.on("SIGINT", () => handleExit("SIGINT"));
process.on("SIGTERM", () => handleExit("SIGTERM"));

process.on("exit", () => {
console.log("Process exiting...");
});

if (args.stopAfterInit) {
console.log(ansis.green("Localnet successfully initialized. Stopping..."));
cleanup();
process.exit(0);
}

await new Promise(() => {});
};

export const localnetTask = task("localnet", "Start localnet", main)
export const localnetTask = task("localnet", "Start localnet", localnet)
.addOptionalParam("port", "Port to run anvil on", 8545, types.int)
.addOptionalParam(
"anvil",
"Additional arguments to pass to anvil",
"",
types.string
)
.addFlag("forceKill", "Force kill any process on the port without prompting")
.addFlag(
"stopAfterInit",
"Stop the localnet after successful initialization"
);
28 changes: 28 additions & 0 deletions packages/tasks/src/stop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { task } from "hardhat/config";
import fs from "fs";
import ansis from "ansis";

const LOCALNET_PID_FILE = "./localnet.pid";

const localnetStop = async (args: any) => {
if (!fs.existsSync(LOCALNET_PID_FILE)) {
console.log(ansis.red("Localnet is not running or PID file is missing."));
return;
}

const pid = fs.readFileSync(LOCALNET_PID_FILE, "utf-8").trim();
try {
process.kill(Number(pid));
console.log(ansis.green(`Successfully stopped localnet (PID: ${pid})`));
fs.unlinkSync(LOCALNET_PID_FILE);
console.log(ansis.green(`PID file ${LOCALNET_PID_FILE} removed.`));
} catch (err) {
console.error(ansis.red(`Failed to stop localnet: ${err}`));
}
};

export const localnetStopTask = task(
"localnet-stop",
"Stop localnet",
localnetStop
);
Loading
Loading