Skip to content

Commit

Permalink
Add example to hardhat-zksync-node
Browse files Browse the repository at this point in the history
  • Loading branch information
Mimi TxFusion committed Oct 13, 2023
1 parent 07ff539 commit 066b11e
Show file tree
Hide file tree
Showing 12 changed files with 288 additions and 2 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ jobs:
yarn hardhat compile
yarn hardhat deploy-zksync
- name: Test node example
run: |
cd examples/node-example
yarn hardhat compile
yarn hardhat test
- name: Test zkvyper example
run: |
cd examples/vyper-example
Expand Down
7 changes: 7 additions & 0 deletions examples/node-example/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
extends: [`${__dirname}/../../config/eslint/eslintrc.js`],
parserOptions: {
project: `${__dirname}/tsconfig.json`,
sourceType: "module",
},
};
3 changes: 3 additions & 0 deletions examples/node-example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cache
artifacts
contracts/tmp
61 changes: 61 additions & 0 deletions examples/node-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# zkSync 2.0 deploy environment example

This project demonstrates how to compile and deploy your contracts in zkSync 2.0 using the Hardhat plugins.

## Prerequisites

- node.js 14.x or later.
- yarn.

## Configuration

Plugin configuration is located in [`hardhat.config.ts`](./hardhat.config.ts).
You should only change the zkSync network configuration.

`hardhat.config.ts` example with zkSync network configured with the name `zkTestnet` and `goerli` used as the underlying layer 1 network:
```ts
import "@matterlabs/hardhat-zksync-deploy";
import { HardhatUserConfig } from 'hardhat/types';

const config: HardhatUserConfig = {
networks: {
goerli: {
url: 'https://goerli.infura.io/v3/<API_KEY>' // you can use either the URL of the Ethereum Web3 RPC, or the identifier of the network (e.g. `mainnet` or `rinkeby`)
},
zkTestnet: {
url: 'https://zksync2-testnet.zksync.dev', // you should use the URL of the zkSync network RPC
ethNetwork: 'goerli',
zksync: true
},
}
};

export default config;
```

## Usage

Before using plugins, you need to build them first

```sh
# Run the following in the *root* of the repo.
yarn
yarn build
```

After that you should be able to run plugins:

```sh
# Run the following in `examples/basic-example` folder.
yarn
yarn hardhat compile
yarn hardhat deploy-zksync
```

- `yarn hardhat compile`: compiles all the contracts in the `contracts` folder.
- `yarn hardhat deploy-zksync`: runs all the deploy scripts in the `deploy` folder.
- To run a specific script, add the `--script` argument, e.g. `--script 001_deploy.ts`.
- To run on a specific zkSync network, use standard hardhat `--network` argument, e.g. `--network zkTestnet`
(with `zkTestnet` network specified in the `hardhat.config` networks section, with the `zksync` flag set to `true` and `ethNetwork` specified).

If you don't specify zkSync network (`--network`), `local-setup` with <http://localhost:8545> (Ethereum RPC URL) and <http://localhost:3050> (zkSync RPC URL) will be used.
19 changes: 19 additions & 0 deletions examples/node-example/contracts/Greeter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
pragma abicoder v2;

contract Greeter {
string greeting;
constructor(string memory _greeting) {
greeting = _greeting;
}

function greet() public view returns (string memory) {
return greeting;
}

function setGreeting(string memory _greeting) public {
greeting = _greeting;
}
}
44 changes: 44 additions & 0 deletions examples/node-example/deploy/001_deploy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const ethers = require('ethers');
const zk = require('zksync-web3');
const { Deployer } = require('@matterlabs/hardhat-zksync-deploy');
const chalk = require('chalk');

// An example of a deploy script which will deploy and call a simple contract.
module.exports = async function (hre) {
console.info(chalk.yellow(`Running deploy script for the Greeter contract`));

// Initialize an Ethereum wallet.
const testMnemonic = 'stuff slice staff easily soup parent arm payment cotton trade scatter struggle';
const zkWallet = zk.Wallet.fromMnemonic(testMnemonic, "m/44'/60'/0'/0/0");

// Create deployer object and load desired artifact.
const deployer = new Deployer(hre, zkWallet);

// Deposit some funds to L2 in order to be able to perform deposits.
const depositHandle = await deployer.zkWallet.deposit({
to: deployer.zkWallet.address,
token: zk.utils.ETH_ADDRESS,
amount: ethers.utils.parseEther('0.001'),
});
await depositHandle.wait();

// Load the artifact we want to deploy.
const artifact = await deployer.loadArtifact('Greeter');

// Deploy this contract. The returned object will be of a `Contract` type, similarly to ones in `ethers`.
// `greeting` is an argument for contract constructor.
const greeting = 'Hi there!';
const greeterContract = await deployer.deploy(artifact, [greeting]);

// Show the contract info.
const contractAddress = greeterContract.address;
console.info(chalk.green(`${artifact.contractName} was deployed to ${contractAddress}!`));

// Call the deployed contract.
const greetingFromContract = await greeterContract.greet();
if (greetingFromContract == greeting) {
console.info(chalk.green(`Successful greeting from the contract`));
} else {
throw new Error(`Contract returned unexpected greeting: ${greetingFromContract}`);
}
};
37 changes: 37 additions & 0 deletions examples/node-example/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import '@matterlabs/hardhat-zksync-deploy';
import '@matterlabs/hardhat-zksync-solc';
import '@matterlabs/hardhat-zksync-node';

import { HardhatUserConfig } from 'hardhat/config';

const config: HardhatUserConfig = {
zksolc: {
compilerSource: 'binary',
settings: {
isSystem: true,
optimizer: {
enabled: true,
},
}
},
networks: {
hardhat: {
zksync: true,
},
ethNetwork: {
url: 'http://0.0.0.0:8545',
},
zkSyncNetwork: {
url: 'http://0.0.0.0:3050',
ethNetwork: 'ethNetwork',
zksync: true,
},
},
// Docker image only works for solidity ^0.8.0.
// For earlier versions you need to use binary releases of zksolc.
solidity: {
version: '0.8.17',
},
};

export default config;
53 changes: 53 additions & 0 deletions examples/node-example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "harhat-zksync-example-node",
"version": "0.1.0",
"author": "Matter Labs",
"license": "MIT",
"scripts": {
"lint": "yarn prettier --check && yarn eslint",
"lint:fix": "yarn eslint --fix",
"fmt": "yarn prettier --write",
"eslint": "eslint deploy/*.ts",
"prettier": "prettier deploy/*.ts",
"test": "mocha test/tests.ts --exit",
"build": "tsc --build .",
"clean": "rimraf dist"
},
"devDependencies": {
"@types/node": "^18.11.17",
"@types/chai": "^4.2.0",
"@types/mocha": "^9.1.0",
"@typescript-eslint/eslint-plugin": "4.29.2",
"@typescript-eslint/parser": "5.13.0",
"eslint": "^8.10.0",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-import": "2.25.4",
"eslint-plugin-prettier": "3.4.0",
"chai": "^4.3.7",
"mocha": "^10.1.0",
"prettier": "2.3.2",
"rimraf": "^3.0.2",
"ts-node": "^10.6.0",
"typescript": "^4.6.2"
},
"dependencies": {
"@matterlabs/hardhat-zksync-deploy": "link:../../packages/hardhat-zksync-deploy",
"@matterlabs/hardhat-zksync-solc": "link:../../packages/hardhat-zksync-solc",
"@matterlabs/hardhat-zksync-node": "link:../../packages/hardhat-zksync-node",
"@matterlabs/hardhat-zksync-chai-matchers": "link:../../packages/hardhat-zksync-chai-matchers",
"chalk": "4.1.2",
"hardhat": "^2.14.0",
"ethers": "~5.7.2",
"zksync-web3": "^0.14.3",
"@matterlabs/zksync-contracts": "^0.6.1",
"@openzeppelin/contracts": "^4.9.2",
"@openzeppelin/contracts-upgradeable": "^4.9.2"
},
"prettier": {
"tabWidth": 4,
"printWidth": 120,
"parser": "typescript",
"singleQuote": true,
"bracketSpacing": true
}
}
35 changes: 35 additions & 0 deletions examples/node-example/test/tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { expect } from "chai";
import { Contract, Wallet } from "zksync-web3";
import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types';
import { ZkSyncProviderAdapter } from "@matterlabs/hardhat-zksync-node";
import * as hre from "hardhat";

describe("Greeter", function () {
let deployer: Deployer;
let artifact: ZkSyncArtifact;
let contract: Contract;

beforeEach(async function () {
// Deploy the contract before each test
deployer = new Deployer(hre, new Wallet('0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110', (hre.network.provider as ZkSyncProviderAdapter)._zkSyncProvider));
artifact = await deployer.loadArtifact('Greeter');
contract = await deployer.deploy(artifact, ['Hello, world!']);
});

// Test the constructor
it("Should set the greeting to the constructor argument", async function () {
expect(await contract.greet()).to.equal("Hello, world!");
});

// Test the greet() function
it("Should return the current greeting", async function () {
expect(await contract.greet()).to.equal("Hello, world!");
});

// Test the setGreeting() function
it("Should set a new greeting", async function () {
await contract.setGreeting("Hello, Ethereum!");
expect(await contract.greet()).to.equal("Hello, Ethereum!");
});
});
18 changes: 18 additions & 0 deletions examples/node-example/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"moduleResolution": "node",
"forceConsistentCasingInFileNames": true,
"outDir": "dist"
},
"include": [
"./hardhat.config.ts",
"./scripts",
"./deploy",
"./test",
"typechain/**/*"
]
}
3 changes: 3 additions & 0 deletions packages/hardhat-zksync-node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,9 @@ task(

const files = await run(TASK_TEST_GET_TEST_FILES, { testFiles });

// Download the binary, if necessary
const binaryPath: string = await run(TASK_NODE_ZKSYNC_DOWNLOAD_BINARY, { force: false });

// Start the zkSync node using TASK_RUN_NODE_ZKSYNC_IN_SEPARATE_PROCESS
const taskArgs: any[] = [
/* Add necessary arguments here */
Expand Down
4 changes: 2 additions & 2 deletions packages/hardhat-zksync-node/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ export async function isPortAvailable(port: number): Promise<boolean> {
return availableIPv4 && availableIPv6;
}

export async function waitForNodeToBeReady(port: number, maxAttempts: number = 10): Promise<void> {
export async function waitForNodeToBeReady(port: number, maxAttempts: number = 20): Promise<void> {
const rpcEndpoint = `http://localhost:${port}`;

const payload = {
Expand All @@ -339,7 +339,7 @@ export async function waitForNodeToBeReady(port: number, maxAttempts: number = 1
}

attempts++;
await new Promise((r) => setTimeout(r, 500)); // Wait for 500ms before the next attempt.
await new Promise((r) => setTimeout(r, 1000)); // Wait for 1000ms before the next attempt.
}

throw new ZkSyncNodePluginError("Server didn't respond after multiple attempts");
Expand Down

0 comments on commit 066b11e

Please sign in to comment.