From 066b11e710613dd60d11bf964157218c02fe2e95 Mon Sep 17 00:00:00 2001 From: Mimi TxFusion Date: Fri, 13 Oct 2023 16:16:39 +0200 Subject: [PATCH] Add example to hardhat-zksync-node --- .github/workflows/ci.yml | 6 ++ examples/node-example/.eslintrc.js | 7 +++ examples/node-example/.gitignore | 3 + examples/node-example/README.md | 61 +++++++++++++++++++++ examples/node-example/contracts/Greeter.sol | 19 +++++++ examples/node-example/deploy/001_deploy.js | 44 +++++++++++++++ examples/node-example/hardhat.config.ts | 37 +++++++++++++ examples/node-example/package.json | 53 ++++++++++++++++++ examples/node-example/test/tests.ts | 35 ++++++++++++ examples/node-example/tsconfig.json | 18 ++++++ packages/hardhat-zksync-node/src/index.ts | 3 + packages/hardhat-zksync-node/src/utils.ts | 4 +- 12 files changed, 288 insertions(+), 2 deletions(-) create mode 100644 examples/node-example/.eslintrc.js create mode 100644 examples/node-example/.gitignore create mode 100644 examples/node-example/README.md create mode 100644 examples/node-example/contracts/Greeter.sol create mode 100644 examples/node-example/deploy/001_deploy.js create mode 100644 examples/node-example/hardhat.config.ts create mode 100644 examples/node-example/package.json create mode 100644 examples/node-example/test/tests.ts create mode 100644 examples/node-example/tsconfig.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad7636299..4c6433976 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/examples/node-example/.eslintrc.js b/examples/node-example/.eslintrc.js new file mode 100644 index 000000000..889740f22 --- /dev/null +++ b/examples/node-example/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: [`${__dirname}/../../config/eslint/eslintrc.js`], + parserOptions: { + project: `${__dirname}/tsconfig.json`, + sourceType: "module", + }, +}; diff --git a/examples/node-example/.gitignore b/examples/node-example/.gitignore new file mode 100644 index 000000000..183b3751a --- /dev/null +++ b/examples/node-example/.gitignore @@ -0,0 +1,3 @@ +cache +artifacts +contracts/tmp diff --git a/examples/node-example/README.md b/examples/node-example/README.md new file mode 100644 index 000000000..a21a70247 --- /dev/null +++ b/examples/node-example/README.md @@ -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/' // 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 (Ethereum RPC URL) and (zkSync RPC URL) will be used. diff --git a/examples/node-example/contracts/Greeter.sol b/examples/node-example/contracts/Greeter.sol new file mode 100644 index 000000000..f502c1ad1 --- /dev/null +++ b/examples/node-example/contracts/Greeter.sol @@ -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; + } +} diff --git a/examples/node-example/deploy/001_deploy.js b/examples/node-example/deploy/001_deploy.js new file mode 100644 index 000000000..406936339 --- /dev/null +++ b/examples/node-example/deploy/001_deploy.js @@ -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}`); + } +}; diff --git a/examples/node-example/hardhat.config.ts b/examples/node-example/hardhat.config.ts new file mode 100644 index 000000000..f18b97607 --- /dev/null +++ b/examples/node-example/hardhat.config.ts @@ -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; diff --git a/examples/node-example/package.json b/examples/node-example/package.json new file mode 100644 index 000000000..5d5d57491 --- /dev/null +++ b/examples/node-example/package.json @@ -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 + } +} diff --git a/examples/node-example/test/tests.ts b/examples/node-example/test/tests.ts new file mode 100644 index 000000000..1d986eefc --- /dev/null +++ b/examples/node-example/test/tests.ts @@ -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!"); + }); +}); diff --git a/examples/node-example/tsconfig.json b/examples/node-example/tsconfig.json new file mode 100644 index 000000000..c6570050d --- /dev/null +++ b/examples/node-example/tsconfig.json @@ -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/**/*" + ] +} diff --git a/packages/hardhat-zksync-node/src/index.ts b/packages/hardhat-zksync-node/src/index.ts index 1fa2aaddb..63bb13ced 100644 --- a/packages/hardhat-zksync-node/src/index.ts +++ b/packages/hardhat-zksync-node/src/index.ts @@ -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 */ diff --git a/packages/hardhat-zksync-node/src/utils.ts b/packages/hardhat-zksync-node/src/utils.ts index 783f745fa..e8258e44c 100644 --- a/packages/hardhat-zksync-node/src/utils.ts +++ b/packages/hardhat-zksync-node/src/utils.ts @@ -316,7 +316,7 @@ export async function isPortAvailable(port: number): Promise { return availableIPv4 && availableIPv6; } -export async function waitForNodeToBeReady(port: number, maxAttempts: number = 10): Promise { +export async function waitForNodeToBeReady(port: number, maxAttempts: number = 20): Promise { const rpcEndpoint = `http://localhost:${port}`; const payload = { @@ -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");