diff --git a/.changeset/README.md b/.changeset/README.md deleted file mode 100644 index e5b6d8d6a..000000000 --- a/.changeset/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Changesets - -Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works -with multi-package repos, or single-package repos to help you version and publish your code. You can -find the full documentation for it [in our repository](https://github.com/changesets/changesets) - -We have a quick list of common questions to get you started engaging with this project in -[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/config.json b/.changeset/config.json deleted file mode 100644 index a4c77f392..000000000 --- a/.changeset/config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json", - "changelog": "@changesets/cli/changelog", - "commit": false, - "fixed": [], - "linked": [], - "access": "public", - "baseBranch": "main", - "updateInternalDependencies": "minor", - "ignore": [] -} diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 63ec6d33a..56a5e12a3 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -61,7 +61,7 @@ This ensures consistent code style throughout the project and helps identify pot ## Need Assistance? -If you're unsure about something or have questions, don't hesitate to open an issue or initiate a discussion in our [zkSync Community Hub](https://github.com/zkSync-Community-Hub/zkSync-developers/discussions). We're here to assist! +If you're unsure about something or have questions, don't hesitate to open an issue or initiate a discussion in our [zkSync Community Hub](https://github.com/zkSync-Community-Hub/zksync-developers/discussions). We're here to assist! ## What's Next? diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml index 4b09aeb64..3b1415813 100644 --- a/.github/ISSUE_TEMPLATE/config.yaml +++ b/.github/ISSUE_TEMPLATE/config.yaml @@ -1,7 +1,7 @@ blank_issues_enabled: true contact_links: - name: zksync-developers Discussion - url: https://github.com/zkSync-Community-Hub/zkync-developers/discussions + url: https://github.com/zkSync-Community-Hub/zksync-developers/discussions about: Please provide feedback, and ask questions here. - name: hardhat-zksync plugins documentation page url: https://era.zksync.io/docs/tools/hardhat/plugins.html diff --git a/.github/release-please/config.json b/.github/release-please/config.json new file mode 100644 index 000000000..6c53c2c94 --- /dev/null +++ b/.github/release-please/config.json @@ -0,0 +1,41 @@ +{ + "separate-pull-requests": true, + "group-pull-request-title-pattern": "chore(${component}): release at branch ${branch}", + "bootstrap-sha": "4d2086931dcebd7e7c21cad9cbd6668c2abbebbf", + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "packages": { + "packages/hardhat-zksync-deploy": { + "release-type": "node", + "component": "@matterlabs/hardhat-zksync-deploy" + }, + "packages/hardhat-zksync-solc": { + "release-type": "node", + "component": "@matterlabs/hardhat-zksync-solc" + }, + "packages/hardhat-zksync-upgradable": { + "release-type": "node", + "component": "@matterlabs/hardhat-zksync-upgradable" + }, + "packages/hardhat-zksync-vyper": { + "release-type": "node", + "component": "@matterlabs/hardhat-zksync-vyper" + }, + "packages/hardhat-zksync-verify": { + "release-type": "node", + "component": "@matterlabs/hardhat-zksync-verify" + }, + "packages/hardhat-zksync-toolbox": { + "release-type": "node", + "component": "@matterlabs/hardhat-zksync-toolbox" + }, + "packages/hardhat-zksync-node": { + "release-type": "node", + "component": "@matterlabs/hardhat-zksync-node" + }, + "packages/hardhat-zksync-chai-matchers": { + "release-type": "node", + "component": "@matterlabs/hardhat-zksync-chai-matchers" + } + } +} \ No newline at end of file diff --git a/.github/release-please/manifest.json b/.github/release-please/manifest.json new file mode 100644 index 000000000..4cfe1371c --- /dev/null +++ b/.github/release-please/manifest.json @@ -0,0 +1,10 @@ +{ + "packages/hardhat-zksync-deploy": "1.1.2", + "packages/hardhat-zksync-solc": "1.0.6", + "packages/hardhat-zksync-upgradable": "1.2.1", + "packages/hardhat-zksync-vyper": "1.0.5", + "packages/hardhat-zksync-verify": "1.2.2", + "packages/hardhat-zksync-toolbox": "1.2.1", + "packages/hardhat-zksync-node": "1.0.1", + "packages/hardhat-zksync-chai-matchers": "1.2.1" +} \ No newline at end of file diff --git a/.github/workflows/check-pr-title.yml b/.github/workflows/check-pr-title.yml new file mode 100644 index 000000000..d905c31ff --- /dev/null +++ b/.github/workflows/check-pr-title.yml @@ -0,0 +1,18 @@ +name: Check PR title +on: + pull_request_target: + types: + - opened + - reopened + - edited + - synchronize + +jobs: + lint: + runs-on: ubuntu-latest + permissions: + statuses: write + steps: + - uses: aslafy-z/conventional-pr-title-action@v3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e77a3c5dd..48f7d4ddf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,9 +52,9 @@ jobs: cd packages/hardhat-zksync-vyper yarn test - deploy: + examples: runs-on: ubuntu-latest - name: deploy + name: examples steps: - uses: actions/checkout@v2 @@ -88,11 +88,11 @@ 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 node example + run: | + cd examples/node-example + yarn hardhat compile + yarn hardhat test - name: Test noninline libraries example run: | @@ -193,6 +193,7 @@ jobs: run: | cd packages/hardhat-zksync-upgradable yarn test + verify-vyper: runs-on: ubuntu-latest name: verify-vyper @@ -213,6 +214,7 @@ jobs: run: | cd packages/hardhat-zksync-verify-vyper yarn test + verify: runs-on: ubuntu-latest name: verify @@ -233,6 +235,47 @@ jobs: run: | cd packages/hardhat-zksync-verify yarn test + + deploy: + runs-on: ubuntu-latest + name: deploy + steps: + - uses: actions/checkout@v2 + + - uses: actions/checkout@v2 + with: + repository: matter-labs/local-setup + path: local-setup + + - name: Run server + run: | + cd local-setup + ./start.sh &>../server.log & + + - uses: actions/setup-node@v2 + with: + node-version: "16" + cache: yarn + + - name: Setup environment + run: | + yarn install + yarn build + + - name: Wait until server is up + run: | + while ! curl -s -X POST -d '{"jsonrpc":"2.0","method":"net_version","id":1}' -H 'Content-Type: application/json' 0.0.0.0:3050; do sleep 1; done + + - name: Test deploy package + run: | + cd packages/hardhat-zksync-deploy + yarn test + + - name: Show logs + if: always() + run: | + cat server.log + zksync-ethers: runs-on: ubuntu-latest name: zksync-ethers @@ -272,6 +315,7 @@ jobs: if: always() run: | cat server.log + node: runs-on: ubuntu-latest name: node diff --git a/.github/workflows/create-release-pull-request.yml b/.github/workflows/create-release-pull-request.yml deleted file mode 100644 index 42cd6b86e..000000000 --- a/.github/workflows/create-release-pull-request.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Create release PR - -on: - push: - branches: - - main - -jobs: - create-release-pull-request: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits - fetch-depth: 0 - - - uses: actions/setup-node@v3 - with: - node-version: '16' - registry-url: 'https://registry.npmjs.org' - cache: 'yarn' - - - name: Setup environment - run: yarn && yarn build - - - name: Create release pull request - uses: changesets/action@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml new file mode 100644 index 000000000..b9817dfcb --- /dev/null +++ b/.github/workflows/github-release.yml @@ -0,0 +1,27 @@ +name: Publish packages to github + +on: + push: + branches: + - main + - ethers-v5 + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + runs-on: ubuntu-latest + outputs: + release_please_output: ${{ toJSON(steps.release.outputs) }} + steps: + - name: Run release-please + id: release + uses: google-github-actions/release-please-action@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + config-file: .github/release-please/config.json + manifest-file: .github/release-please/manifest.json + target-branch: ${{ github.ref_name }} diff --git a/.github/workflows/npm-publish.yaml b/.github/workflows/npm-publish.yaml new file mode 100644 index 000000000..80fbb6959 --- /dev/null +++ b/.github/workflows/npm-publish.yaml @@ -0,0 +1,52 @@ +name: Publish packages to npm + +on: + workflow_dispatch: + inputs: + tag: + required: true + type: choice + description: tag to publish + default: latest + options: + - latest + - beta + - alpha + + package: + required: true + type: choice + description: package to publish + default: hardhat-zksync-deploy + options: + - hardhat-zksync-deploy + - hardhat-zksync-solc + - hardhat-zksync-upgradable + - hardhat-zksync-vyper + - hardhat-zksync-verify + - hardhat-zksync-toolbox + - hardhat-zksync-node + - hardhat-zksync-chai-matchers + - hardhat-zksync-ethers + - hardhat-zksync-verify-vyper + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: '16' + registry-url: 'https://registry.npmjs.org' + cache: 'yarn' + + - name: Setup environment + run: yarn && yarn build + + - name: Publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPMJS_NPM_MATTERLABS_AUTOMATION_TOKEN }} + run: | + npm publish @matterlabs/${{inputs.package}} --tag ${{ inputs.tag }} --workspace=packages/${{inputs.package}} --access=public diff --git a/.github/workflows/publish-chai-matchers.yml b/.github/workflows/publish-chai-matchers.yml deleted file mode 100644 index 421f80520..000000000 --- a/.github/workflows/publish-chai-matchers.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: Publish chai matchers plugin - -on: - push: - tags: - - '@matterlabs/hardhat-zksync-chai-matchers@*' - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: '16' - registry-url: 'https://registry.npmjs.org' - cache: 'yarn' - - - name: Setup environment - run: yarn && yarn build - - - name: Publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPMJS_NPM_MATTERLABS_AUTOMATION_TOKEN }} - run: | - if [[ ${{ github.ref }} == *"beta"* ]]; then - echo "Publishing package with beta tag" - npm publish @matterlabs/hardhat-zksync-chai-matchers --tag beta --workspace=packages/hardhat-zksync-chai-matchers --access=public - else - echo "Publishing package with latest tag" - npm publish @matterlabs/hardhat-zksync-chai-matchers --tag latest --workspace=packages/hardhat-zksync-chai-matchers --access=public - fi - - - name: Create github release from tags - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if [[ ${{ github.ref }} != *"beta"* ]]; then - node scripts/create-release-from-tags/run.js --package hardhat-zksync-chai-matchers - else - echo "Skipping github release creation for beta tag" - fi - - - name: Create the MM Message - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh -R matter-labs/hardhat-zksync release view "$GITHUB_REF_NAME" --json tagName,body --template '## {{.tagName}} {{"\n"}}{{.body}}' > ./release_info - jq --null-input --arg text "$(cat ./release_info)" '{"text": $text}' > mattermost.json - - - uses: mattermost/action-mattermost-notify@master - env: - MATTERMOST_USERNAME: "Hardhat Release Bot" - MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_URL }} diff --git a/.github/workflows/publish-deploy.yml b/.github/workflows/publish-deploy.yml deleted file mode 100644 index 719f9981e..000000000 --- a/.github/workflows/publish-deploy.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Publish deploy plugin - -on: - push: - tags: - - '@matterlabs/hardhat-zksync-deploy@*' - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: '16' - registry-url: 'https://registry.npmjs.org' - cache: 'yarn' - - - name: Setup environment - run: yarn && yarn build - - - name: Publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPMJS_NPM_MATTERLABS_AUTOMATION_TOKEN }} - run: | - if [[ ${{ github.ref }} == *"beta"* ]]; then - echo "Publishing package with beta tag" - npm publish @matterlabs/hardhat-zksync-deploy --tag beta --workspace=packages/hardhat-zksync-deploy --access=public - elif [[ ${{ github.ref }} == *"alpha"* ]]; then - echo "Publishing package with alpha tag" - npm publish @matterlabs/hardhat-zksync-deploy --tag alpha --workspace=packages/hardhat-zksync-deploy --access=public - else - echo "Publishing package with latest tag" - npm publish @matterlabs/hardhat-zksync-deploy --tag latest --workspace=packages/hardhat-zksync-deploy --access=public - fi - - - name: Create github release from tags - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if [[ ${{ github.ref }} != *"beta"* ]]; then - node scripts/create-release-from-tags/run.js --package hardhat-zksync-deploy - else - echo "Skipping github release creation for beta tag" - fi - - - name: Create the MM Message - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh -R matter-labs/hardhat-zksync release view "$GITHUB_REF_NAME" --json tagName,body --template '## {{.tagName}} {{"\n"}}{{.body}}' > ./release_info - jq --null-input --arg text "$(cat ./release_info)" '{"text": $text}' > mattermost.json - - - uses: mattermost/action-mattermost-notify@master - env: - MATTERMOST_USERNAME: "Hardhat Release Bot" - MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_URL }} diff --git a/.github/workflows/publish-node.yaml b/.github/workflows/publish-node.yaml deleted file mode 100644 index 0917afb08..000000000 --- a/.github/workflows/publish-node.yaml +++ /dev/null @@ -1,59 +0,0 @@ -name: Publish Node Plugin - -on: - push: - tags: - - '@matterlabs/hardhat-zksync-node@*' - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: '16' - registry-url: 'https://registry.npmjs.org' - cache: 'yarn' - - - name: Setup environment - run: yarn && yarn build - - - name: Publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPMJS_NPM_MATTERLABS_AUTOMATION_TOKEN }} - run: | - if [[ ${{ github.ref }} == *"beta"* ]]; then - echo "Publishing package with beta tag" - npm publish @matterlabs/hardhat-zksync-node --tag beta --workspace=packages/hardhat-zksync-node --access=public - elif [[ ${{ github.ref }} == *"alpha"* ]]; then - echo "Publishing package with alpha tag" - npm publish @matterlabs/hardhat-zksync-node --tag alpha --workspace=packages/hardhat-zksync-node --access=public - else - echo "Publishing package with latest tag" - npm publish @matterlabs/hardhat-zksync-node --tag latest --workspace=packages/hardhat-zksync-node --access=public - fi - - - name: Create github release from tags - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if [[ ${{ github.ref }} != *"beta"* ]] && [[ ${{ github.ref }} != *"alpha"* ]]; then - node scripts/create-release-from-tags/run.js --package hardhat-zksync-node - else - echo "Skipping github release creation for beta and alpha tags" - fi - - - name: Create the MM Message - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh -R matter-labs/hardhat-zksync release view "$GITHUB_REF_NAME" --json tagName,body --template '## {{.tagName}} {{"\n"}}{{.body}}' > ./release_info - jq --null-input --arg text "$(cat ./release_info)" '{"text": $text}' > mattermost.json - - - uses: mattermost/action-mattermost-notify@master - env: - MATTERMOST_USERNAME: "Hardhat Release Bot" - MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_URL }} - diff --git a/.github/workflows/publish-toolbox.yml b/.github/workflows/publish-toolbox.yml deleted file mode 100644 index 151525900..000000000 --- a/.github/workflows/publish-toolbox.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: Publish toolbox plugin - -on: - push: - tags: - - '@matterlabs/hardhat-zksync-toolbox@*' - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: '16' - registry-url: 'https://registry.npmjs.org' - cache: 'yarn' - - - name: Setup environment - run: yarn && yarn build - - - name: Publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPMJS_NPM_MATTERLABS_AUTOMATION_TOKEN }} - run: | - if [[ ${{ github.ref }} == *"beta"* ]]; then - echo "Publishing package with beta tag" - npm publish @matterlabs/hardhat-zksync-toolbox --tag beta --workspace=packages/hardhat-zksync-toolbox --access=public - else - echo "Publishing package with latest tag" - npm publish @matterlabs/hardhat-zksync-toolbox --tag latest --workspace=packages/hardhat-zksync-toolbox --access=public - fi - - - name: Create github release from tags - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if [[ ${{ github.ref }} != *"beta"* ]]; then - node scripts/create-release-from-tags/run.js --package hardhat-zksync-toolbox - else - echo "Skipping github release creation for beta tag" - fi - - - name: Create the MM Message - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh -R matter-labs/hardhat-zksync release view "$GITHUB_REF_NAME" --json tagName,body --template '## {{.tagName}} {{"\n"}}{{.body}}' > ./release_info - jq --null-input --arg text "$(cat ./release_info)" '{"text": $text}' > mattermost.json - - - uses: mattermost/action-mattermost-notify@master - env: - MATTERMOST_USERNAME: "Hardhat Release Bot" - MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_URL }} diff --git a/.github/workflows/publish-upgradable.yml b/.github/workflows/publish-upgradable.yml deleted file mode 100644 index 368f32ef7..000000000 --- a/.github/workflows/publish-upgradable.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: Publish upgradable plugin - -on: - push: - tags: - - '@matterlabs/hardhat-zksync-upgradable@*' - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: '16' - registry-url: 'https://registry.npmjs.org' - cache: 'yarn' - - - name: Setup environment - run: yarn && yarn build - - - name: Publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPMJS_NPM_MATTERLABS_AUTOMATION_TOKEN }} - run: | - if [[ ${{ github.ref }} == *"beta"* ]]; then - echo "Publishing package with beta tag" - npm publish @matterlabs/hardhat-zksync-upgradable --tag beta --workspace=packages/hardhat-zksync-upgradable --access=public - else - echo "Publishing package with latest tag" - npm publish @matterlabs/hardhat-zksync-upgradable --tag latest --workspace=packages/hardhat-zksync-upgradable --access=public - fi - - - name: Create github release from tags - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if [[ ${{ github.ref }} != *"beta"* ]]; then - node scripts/create-release-from-tags/run.js --package hardhat-zksync-upgradable - else - echo "Skipping github release creation for beta tag" - fi - - - name: Create the MM Message - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh -R matter-labs/hardhat-zksync release view "$GITHUB_REF_NAME" --json tagName,body --template '## {{.tagName}} {{"\n"}}{{.body}}' > ./release_info - jq --null-input --arg text "$(cat ./release_info)" '{"text": $text}' > mattermost.json - - - uses: mattermost/action-mattermost-notify@master - env: - MATTERMOST_USERNAME: "Hardhat Release Bot" - MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_URL }} diff --git a/.github/workflows/publish-verify.yml b/.github/workflows/publish-verify.yml deleted file mode 100644 index bcedc75dc..000000000 --- a/.github/workflows/publish-verify.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: Publish verify plugin - -on: - push: - tags: - - '@matterlabs/hardhat-zksync-verify@*' - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: '16' - registry-url: 'https://registry.npmjs.org' - cache: 'yarn' - - - name: Setup environment - run: yarn && yarn build - - - name: Publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPMJS_NPM_MATTERLABS_AUTOMATION_TOKEN }} - run: | - if [[ ${{ github.ref }} == *"beta"* ]]; then - echo "Publishing package with beta tag" - npm publish @matterlabs/hardhat-zksync-verify --tag beta --workspace=packages/hardhat-zksync-verify --access=public - else - echo "Publishing package with latest tag" - npm publish @matterlabs/hardhat-zksync-verify --tag latest --workspace=packages/hardhat-zksync-verify --access=public - fi - - - name: Create github release from tags - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if [[ ${{ github.ref }} != *"beta"* ]]; then - node scripts/create-release-from-tags/run.js --package hardhat-zksync-verify - else - echo "Skipping github release creation for beta tag" - fi - - - name: Create the MM Message - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh -R matter-labs/hardhat-zksync release view "$GITHUB_REF_NAME" --json tagName,body --template '## {{.tagName}} {{"\n"}}{{.body}}' > ./release_info - jq --null-input --arg text "$(cat ./release_info)" '{"text": $text}' > mattermost.json - - - uses: mattermost/action-mattermost-notify@master - env: - MATTERMOST_USERNAME: "Hardhat Release Bot" - MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_URL }} diff --git a/.github/workflows/publish-zksolc.yml b/.github/workflows/publish-zksolc.yml deleted file mode 100644 index 933266bf9..000000000 --- a/.github/workflows/publish-zksolc.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Publish zksolc plugin - -on: - push: - tags: - - '@matterlabs/hardhat-zksync-solc@*' - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: '16' - registry-url: 'https://registry.npmjs.org' - cache: 'yarn' - - - name: Setup environment - run: yarn && yarn build - - - name: Publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPMJS_NPM_MATTERLABS_AUTOMATION_TOKEN }} - run: | - if [[ ${{ github.ref }} == *"beta"* ]]; then - echo "Publishing package with beta tag" - npm publish @matterlabs/hardhat-zksync-solc --tag beta --workspace=packages/hardhat-zksync-solc --access=public - elif [[ ${{ github.ref }} == *"alpha"* ]]; then - echo "Publishing package with alpha tag" - npm publish @matterlabs/hardhat-zksync-solc --tag alpha --workspace=packages/hardhat-zksync-solc --access=public - else - echo "Publishing package with latest tag" - npm publish @matterlabs/hardhat-zksync-solc --tag latest --workspace=packages/hardhat-zksync-solc --access=public - fi - - - name: Create github release from tags - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if [[ ${{ github.ref }} != *"beta"* ]]; then - node scripts/create-release-from-tags/run.js --package hardhat-zksync-solc - else - echo "Skipping github release creation for beta tag" - fi - - - name: Create the MM Message - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh -R matter-labs/hardhat-zksync release view "$GITHUB_REF_NAME" --json tagName,body --template '## {{.tagName}} {{"\n"}}{{.body}}' > ./release_info - jq --null-input --arg text "$(cat ./release_info)" '{"text": $text}' > mattermost.json - - - uses: mattermost/action-mattermost-notify@master - env: - MATTERMOST_USERNAME: "Hardhat Release Bot" - MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_URL }} diff --git a/.github/workflows/publish-zksync-ethers.yaml b/.github/workflows/publish-zksync-ethers.yaml deleted file mode 100644 index 764756312..000000000 --- a/.github/workflows/publish-zksync-ethers.yaml +++ /dev/null @@ -1,59 +0,0 @@ -name: Publish zksync-ethers plugin - -on: - push: - tags: - - '@matterlabs/hardhat-zksync-ethers@*' - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: '16' - registry-url: 'https://registry.npmjs.org' - cache: 'yarn' - - - name: Setup environment - run: yarn && yarn build - - - name: Publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPMJS_NPM_MATTERLABS_AUTOMATION_TOKEN }} - run: | - if [[ ${{ github.ref }} == *"beta"* ]]; then - echo "Publishing package with beta tag" - npm publish @matterlabs/hardhat-zksync-ethers --tag beta --workspace=packages/hardhat-zksync-ethers --access=public - elif [[ ${{ github.ref }} == *"alpha"* ]]; then - echo "Publishing package with alpha tag" - npm publish @matterlabs/hardhat-zksync-ethers --tag alpha --workspace=packages/hardhat-zksync-ethers --access=public - else - echo "Publishing package with latest tag" - npm publish @matterlabs/hardhat-zksync-ethers --tag latest --workspace=packages/hardhat-zksync-ethers --access=public - fi - - - name: Create github release from tags - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if [[ ${{ github.ref }} != *"beta"* ]] && [[ ${{ github.ref }} != *"alpha"* ]]; then - node scripts/create-release-from-tags/run.js --package hardhat-zksync-ethers - else - echo "Skipping github release creation for beta and alpha tags" - fi - - - name: Create the MM Message - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh -R matter-labs/hardhat-zksync release view "$GITHUB_REF_NAME" --json tagName,body --template '## {{.tagName}} {{"\n"}}{{.body}}' > ./release_info - jq --null-input --arg text "$(cat ./release_info)" '{"text": $text}' > mattermost.json - - - uses: mattermost/action-mattermost-notify@master - env: - MATTERMOST_USERNAME: "Hardhat Release Bot" - MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_URL }} - diff --git a/.github/workflows/publish-zkvyper-verify.yml b/.github/workflows/publish-zkvyper-verify.yml deleted file mode 100644 index 9d4ab8f89..000000000 --- a/.github/workflows/publish-zkvyper-verify.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Publish zkvyper verify plugin - -on: - push: - tags: - - '@matterlabs/hardhat-zksync-verify-vyper@*' - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: '16' - registry-url: 'https://registry.npmjs.org' - cache: 'yarn' - - - name: Setup environment - run: yarn && yarn build - - - name: Publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPMJS_NPM_MATTERLABS_AUTOMATION_TOKEN }} - run: | - if [[ ${{ github.ref }} == *"beta"* ]]; then - echo "Publishing package with beta tag" - npm publish @matterlabs/hardhat-zksync-verify-vyper --tag beta --workspace=packages/hardhat-zksync-verify-vyper --access=public - elif [[ ${{ github.ref }} == *"alpha"* ]]; then - echo "Publishing package with alpha tag" - npm publish @matterlabs/hardhat-zksync-verify-vyper --tag alpha --workspace=packages/hardhat-zksync-verify-vyper --access=public - else - echo "Publishing package with latest tag" - npm publish @matterlabs/hardhat-zksync-verify-vyper --tag latest --workspace=packages/hardhat-zksync-verify-vyper --access=public - fi - - - name: Create github release from tags - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if [[ ${{ github.ref }} != *"beta"* ]]; then - node scripts/create-release-from-tags/run.js --package hardhat-zksync-verify-vyper - else - echo "Skipping github release creation for beta tag" - fi - - - name: Create the MM Message - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh -R matter-labs/hardhat-zksync release view "$GITHUB_REF_NAME" --json tagName,body --template '## {{.tagName}} {{"\n"}}{{.body}}' > ./release_info - jq --null-input --arg text "$(cat ./release_info)" '{"text": $text}' > mattermost.json - - - uses: mattermost/action-mattermost-notify@master - env: - MATTERMOST_USERNAME: "Hardhat Release Bot" - MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_URL }} diff --git a/.github/workflows/publish-zkvyper.yml b/.github/workflows/publish-zkvyper.yml deleted file mode 100644 index 04be00704..000000000 --- a/.github/workflows/publish-zkvyper.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: Publish zkvyper plugin - -on: - push: - tags: - - '@matterlabs/hardhat-zksync-vyper@*' - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: '16' - registry-url: 'https://registry.npmjs.org' - cache: 'yarn' - - - name: Setup environment - run: yarn && yarn build - - - name: Publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPMJS_NPM_MATTERLABS_AUTOMATION_TOKEN }} - run: | - if [[ ${{ github.ref }} == *"beta"* ]]; then - echo "Publishing package with beta tag" - npm publish @matterlabs/hardhat-zksync-vyper --tag beta --workspace=packages/hardhat-zksync-vyper --access=public - else - echo "Publishing package with latest tag" - npm publish @matterlabs/hardhat-zksync-vyper --tag latest --workspace=packages/hardhat-zksync-vyper --access=public - fi - - - name: Create github release from tags - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if [[ ${{ github.ref }} != *"beta"* ]]; then - node scripts/create-release-from-tags/run.js --package hardhat-zksync-vyper - else - echo "Skipping github release creation for beta tag" - fi - - - name: Create the MM Message - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh -R matter-labs/hardhat-zksync release view "$GITHUB_REF_NAME" --json tagName,body --template '## {{.tagName}} {{"\n"}}{{.body}}' > ./release_info - jq --null-input --arg text "$(cat ./release_info)" '{"text": $text}' > mattermost.json - - - uses: mattermost/action-mattermost-notify@master - env: - MATTERMOST_USERNAME: "Hardhat Release Bot" - MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_URL }} diff --git a/README.md b/README.md index a84b59d2d..2e7c004e4 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,28 @@ -# zkSync 2.0: Welcome to zkSync Hardhat plugins repository +# zkSync Era: Welcome to zkSync Hardhat plugins repository -![](https://user-images.githubusercontent.com/8230135/215079996-46ec1c91-e65d-4adb-8d7a-f7eecf851858.svg) +![Era Logo](https://github.com/matter-labs/era-contracts/raw/main/eraLogo.svg) -zkSync 2.0 is a layer 2 rollup that uses zero-knowledge proofs to scale Ethereum without compromising on security or +zkSync Era is a layer 2 rollup that uses zero-knowledge proofs to scale Ethereum without compromising on security or decentralization. Since it's EVM compatible (Solidity/Vyper), 99% of Ethereum projects can redeploy without refactoring -or re-auditing a single line of code. zkSync 2.0 also uses an LLVM-based compiler that will eventually let developers +or re-auditing a single line of code. zkSync Era also uses an LLVM-based compiler that will eventually let developers write smart contracts in C++, Rust and other popular languages. This repository contains a collection of plugins to aid in the development and deployment of smart contracts on the zkSync network. These plugins are designed to integrate seamlessly with the [Hardhat](https://hardhat.org/) development environment, providing developers with an easy-to-use and powerful toolset. Here is an overview of the plugins currently available: -**hardhat-zksync-solc**: This plugin is used to provide a convenient interface for compiling Solidity smart contracts before deploying them to the zkSync network. - -**hardhat-zksync-deploy**: This plugin simplifies the deployment of your smart contracts to the zkSync network by providing utilities for deploying smart contracts with artifacts built by the zkSync hardhat-zksync-solc or hardhat-zksync-vyper plugins. - -**hardhat-zksync-verify**: This plugin helps you to verify your smart contracts on the zkSync network by providing a set of tasks that automate the verification process. - -**hardhat-zksync-verify-vyper**: This plugin helps you to verify your vyper smart contracts on the zkSync network by providing a set of tasks that automate the verification process. - -**hardhat-zksync-vyper**: This plugin is used to provide a convenient interface for compiling Vyper smart contracts before deploying them to the zkSync network. - -**hardhat-zksync-chai-matchers**: This plugin adds additional chai matchers to be used when writing tests with specific zkSync features. - -**hardhat-zksync-toolbox**: This plugin provides a convenient method for bundling and accessing a range of zkSync-related Hardhat plugins. - -**hardhat-zksync-upgradeable**: This plugin provides a convenient method to deploy and upgrade smart contracts on the zkSync network. - -**hardhat-zksync-ethers**: This plugin is a wrapper around zksync-ethers sdk that gives additional methods to use for faster development. +| 🔌 Plugin | 📄 Description | +|-------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| hardhat-zksync-solc | Simplifies compiling Solidity contracts for the zkSync network, streamlining deployment preparation. | +| hardhat-zksync-deploy | Facilitates the deployment of contracts on zkSync, utilizing artifacts from hardhat-zksync-solc/vyper. | +| hardhat-zksync-verify | Automates the process of verifying smart contracts on the zkSync network, enhancing transparency and trust. | +| hardhat-zksync-verify-vyper | Specialized for automating the verification of Vyper contracts on the zkSync network. | +| hardhat-zksync-vyper | Streamlines the compilation of Vyper contracts for deployment on the zkSync network. | +| hardhat-zksync-chai-matchers | Extends chai with additional matchers, aiding in testing zkSync-specific features more effectively. | +| hardhat-zksync-toolbox | Offers a suite of zkSync-related Hardhat plugins in one package, enhancing accessibility and efficiency. | +| hardhat-zksync-upgradeable | Enables easier deployment and upgrading of smart contracts on the zkSync network, improving contract lifecycle management. | +| hardhat-zksync-node | Convenient plugin to run the zkSync era-test-node locally. | +| hardhat-zksync-ethers | A zksync-ethers SDK wrapper providing additional methods for accelerated development on zkSync. | You can find more detailed explanations on how to use hardhat zkSync plugins on our [documentation page](https://v2-docs.zksync.io/api/hardhat/plugins.html#plugins) where each plugin has its own section: @@ -39,12 +34,12 @@ You can find more detailed explanations on how to use hardhat zkSync plugins on [hardhat-zksync-chai-matchers](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-chai-matchers.html)\ [hardhat-zksync-toolbox](https://era.zksync.io/docs/tools/hardhat/plugins.html)\ [hardhat-zksync-upgradeable](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-upgradable.html)\ +[hardhat-zksync-node](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-node.html)\ [hardhat-zksync-ethers](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-ethers.html) We hope you find these plugins useful in your development efforts.\ -Happy coding!🙌🎉\o/ - +Happy coding!🙌🎉 ## License hardhat-zkSync is distributed under the terms of both the MIT license and the Apache License (Version 2.0). diff --git a/config/eslint/eslintrc.cjs b/config/eslint/eslintrc.cjs index bcdf23f2a..ef4739aee 100644 --- a/config/eslint/eslintrc.cjs +++ b/config/eslint/eslintrc.cjs @@ -173,7 +173,7 @@ module.exports = { "no-new-func": "error", "no-new-wrappers": "error", "no-return-await": "off", - "@typescript-eslint/return-await": "error", + "@typescript-eslint/return-await": "off", "no-sequences": "error", "no-sparse-arrays": "error", "no-template-curly-in-string": "error", diff --git a/examples/basic-example/README.md b/examples/basic-example/README.md index aa552c41e..a6aab74c7 100644 --- a/examples/basic-example/README.md +++ b/examples/basic-example/README.md @@ -1,6 +1,6 @@ -# zkSync 2.0 deploy environment example +# zkSync Era deploy environment example -This project demonstrates how to compile and deploy your contracts in zkSync 2.0 using the Hardhat plugins. +This project demonstrates how to compile and deploy your contracts in zkSync Era using the Hardhat plugins. ## Prerequisites diff --git a/examples/basic-example/deploy/002_factory.ts b/examples/basic-example/deploy/002_factory.ts index fc9ec7f25..2d6620755 100644 --- a/examples/basic-example/deploy/002_factory.ts +++ b/examples/basic-example/deploy/002_factory.ts @@ -39,7 +39,7 @@ export default async function (hre: HardhatRuntimeEnvironment) { // Call the deployed contract. const greetingFromContract = await factoryContract.getFooName(); - if (greetingFromContract == 'Foo') { + if (greetingFromContract === 'Foo') { console.info(chalk.green(`Successful greeting from the contract!`)); } else { throw new Error(`Contract returned unexpected greeting: ${greetingFromContract}`); diff --git a/examples/basic-example/deploy/003_account_abstraction.ts b/examples/basic-example/deploy/003_account_abstraction.ts index 35cf3482c..359297071 100644 --- a/examples/basic-example/deploy/003_account_abstraction.ts +++ b/examples/basic-example/deploy/003_account_abstraction.ts @@ -5,7 +5,7 @@ import { Deployer } from '@matterlabs/hardhat-zksync-deploy'; import chalk from 'chalk'; export default async function (hre: HardhatRuntimeEnvironment) { - //return; + // return; console.info(chalk.yellow('Running deploy script for the Account Abstraction')); // Initialize an Ethereum wallet. const testMnemonic = 'stuff slice staff easily soup parent arm payment cotton trade scatter struggle'; @@ -58,15 +58,15 @@ export default async function (hre: HardhatRuntimeEnvironment) { aaTx = { ...aaTx, from: multisigAddress, - gasLimit: gasLimit, - gasPrice: gasPrice, + gasLimit, + gasPrice, chainId: (await provider.getNetwork()).chainId, nonce: await provider.getTransactionCount(multisigAddress), type: 113, customData: { gasPerPubdata: zk.utils.DEFAULT_GAS_PER_PUBDATA_LIMIT, } as zk.types.Eip712Meta, - value: ethers.toBigInt(0) + value: ethers.toBigInt(0), }; const signedTxHash = zk.EIP712Signer.getSignedDigest(aaTx); @@ -84,11 +84,11 @@ export default async function (hre: HardhatRuntimeEnvironment) { console.log(`The multisig's nonce before the first tx is ${await provider.getTransactionCount(multisigAddress)}`); - const serialized = zk.utils.serializeEip712(aaTx) - + const serialized = zk.utils.serializeEip712(aaTx); + const tx = await provider.broadcastTransaction(serialized); await tx.wait(); - + console.log(`The multisig's nonce after the first tx is ${await provider.getTransactionCount(multisigAddress)}`); const greetingFromContract = await greeterContract.greet(); if (greetingFromContract === newGreeting) { @@ -96,5 +96,4 @@ export default async function (hre: HardhatRuntimeEnvironment) { } else { throw new Error(`Contract said something unexpected: ${greetingFromContract}`); } - } diff --git a/examples/basic-example/package.json b/examples/basic-example/package.json index 72bffc249..c9cf93126 100644 --- a/examples/basic-example/package.json +++ b/examples/basic-example/package.json @@ -32,7 +32,7 @@ "@matterlabs/hardhat-zksync-deploy": "link:../../packages/hardhat-zksync-deploy", "@matterlabs/hardhat-zksync-solc": "link:../../packages/hardhat-zksync-solc", "chalk": "4.1.2", - "hardhat": "^2.19.2", + "hardhat": "^2.19.4", "ethers": "^6.7.1", "zksync-ethers": "^6.0.0", "@matterlabs/zksync-contracts": "^0.6.1", diff --git a/examples/mixed-example/package.json b/examples/mixed-example/package.json index 47ed13993..9c48c1cdf 100644 --- a/examples/mixed-example/package.json +++ b/examples/mixed-example/package.json @@ -28,7 +28,7 @@ "@matterlabs/hardhat-zksync-vyper": "link:../../packages/hardhat-zksync-vyper", "@nomiclabs/hardhat-vyper": "^3.0.5", "ethers": "^6.7.1", - "hardhat": "^2.19.2" + "hardhat": "^2.19.4" }, "prettier": { "tabWidth": 4, diff --git a/examples/node-example/README.md b/examples/node-example/README.md index ecba36d69..5c92d68e5 100644 --- a/examples/node-example/README.md +++ b/examples/node-example/README.md @@ -1,4 +1,4 @@ -# zkSync 2.0 node environment example +# zkSync Era node environment example This project demonstrates how to run [era-test-node](https://era.zksync.io/docs/tools/testing/era-test-node.html) locally using the zksync's `hardhat-zksync-node` Hardhat plugin for testing purposes. diff --git a/examples/node-example/package.json b/examples/node-example/package.json index 1e27a3751..b9a3cdcc9 100644 --- a/examples/node-example/package.json +++ b/examples/node-example/package.json @@ -25,7 +25,6 @@ "eslint-plugin-import": "2.29.0", "eslint-plugin-no-only-tests": "3.1.0", "eslint-plugin-prettier": "5.0.1", - "chai": "^4.3.7", "mocha": "^10.1.0", "prettier": "3.1.0", "rimraf": "^3.0.2", @@ -33,12 +32,13 @@ "typescript": "^5.1.6" }, "dependencies": { + "chai": "^4.3.7", "@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.19.2", + "hardhat": "^2.19.4", "ethers": "^6.7.1", "zksync-ethers": "^6.0.0", "@matterlabs/zksync-contracts": "^0.6.1", diff --git a/examples/node-example/test/tests.ts b/examples/node-example/test/tests.ts index 5652be119..b3ceb201f 100644 --- a/examples/node-example/test/tests.ts +++ b/examples/node-example/test/tests.ts @@ -1,13 +1,13 @@ -import { expect } from "chai"; -import { Wallet, Provider,Contract } from "zksync-ethers"; -import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; +import { expect } from 'chai'; +import { Wallet, Provider, Contract } from 'zksync-ethers'; +import { Deployer } from '@matterlabs/hardhat-zksync-deploy'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; -import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; -import * as hre from "hardhat"; +import '@matterlabs/hardhat-zksync-node/dist/type-extensions'; +import * as hre from 'hardhat'; const RICH_PRIVATE_KEY = '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110'; -describe("Greeter", function () { +describe('Greeter', function () { let provider: Provider; let deployer: Deployer; let artifact: ZkSyncArtifact; @@ -22,20 +22,20 @@ describe("Greeter", function () { }); // Test the constructor - it("Should set the greeting to the constructor argument", async function () { - expect(await contract.greet()).to.equal("Hello, world!"); + 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!"); + 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 () { - //Added for preveting nonce errors. - await new Promise(resolve => setTimeout(resolve, 1000)); - await contract.setGreeting("Hello, Ethereum!"); - expect(await contract.greet()).to.equal("Hello, Ethereum!"); + it('Should set a new greeting', async function () { + // Added for preveting nonce errors. + await new Promise((resolve) => setTimeout(resolve, 1000)); + await contract.setGreeting('Hello, Ethereum!'); + expect(await contract.greet()).to.equal('Hello, Ethereum!'); }); }); diff --git a/examples/noninline-libraries-example/README.md b/examples/noninline-libraries-example/README.md index b1edf28e4..1304417a9 100644 --- a/examples/noninline-libraries-example/README.md +++ b/examples/noninline-libraries-example/README.md @@ -1,6 +1,6 @@ -# zkSync 2.0 non-inline libraries environment example +# zkSync Era non-inline libraries environment example -This project demonstrates how to compile and deploy your contracts in zkSync 2.0 when there are missing libraries. +This project demonstrates how to compile and deploy your contracts in zkSync Era when there are missing libraries. ## Prerequisites diff --git a/examples/noninline-libraries-example/deploy/001_deploy.ts b/examples/noninline-libraries-example/deploy/001_deploy.ts index 4e40ad172..d343233e7 100644 --- a/examples/noninline-libraries-example/deploy/001_deploy.ts +++ b/examples/noninline-libraries-example/deploy/001_deploy.ts @@ -8,7 +8,7 @@ export default async function (hre: HardhatRuntimeEnvironment) { console.info(chalk.yellow(`Running deploy script for the Test contract`)); // Create zkWallet object - const zkWallet = new zk.Wallet("0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110"); + const zkWallet = new zk.Wallet('0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110'); // // Create deployer object and load desired artifact. const deployer = new Deployer(hre, zkWallet); @@ -23,4 +23,4 @@ export default async function (hre: HardhatRuntimeEnvironment) { // Show the contract info. const contractAddress = await greeterContract.getAddress(); console.info(chalk.green(`${artifact.contractName} was deployed to ${contractAddress}!`)); -}; \ No newline at end of file +} diff --git a/examples/noninline-libraries-example/package.json b/examples/noninline-libraries-example/package.json index a6011d945..3058f2d09 100644 --- a/examples/noninline-libraries-example/package.json +++ b/examples/noninline-libraries-example/package.json @@ -32,7 +32,7 @@ "@matterlabs/hardhat-zksync-deploy": "link:../../packages/hardhat-zksync-deploy", "@matterlabs/hardhat-zksync-solc": "link:../../packages/hardhat-zksync-solc", "chalk": "4.1.2", - "hardhat": "^2.19.2", + "hardhat": "^2.19.4", "ethers": "^6.7.1", "zksync-ethers": "^6.0.0", "@matterlabs/zksync-contracts": "^0.6.1", diff --git a/examples/upgradable-example/README.md b/examples/upgradable-example/README.md index 1b7f2b30b..cd72017d6 100644 --- a/examples/upgradable-example/README.md +++ b/examples/upgradable-example/README.md @@ -1,6 +1,6 @@ -# zkSync 2.0 upgradable example +# zkSync Era upgradable example -This project demonstrates how to compile and deploy upgadable smart contracts in zkSync 2.0 using the Hardhat plugins. +This project demonstrates how to compile and deploy upgadable smart contracts in zkSync Era using the Hardhat plugins. ## Prerequisites diff --git a/examples/upgradable-example/package.json b/examples/upgradable-example/package.json index a80082ba3..6135309a3 100644 --- a/examples/upgradable-example/package.json +++ b/examples/upgradable-example/package.json @@ -24,18 +24,20 @@ "eslint-plugin-import": "2.29.0", "eslint-plugin-no-only-tests": "3.1.0", "eslint-plugin-prettier": "5.0.1", - "hardhat": "^2.19.2", "prettier": "3.1.0", "rimraf": "^3.0.2", "ts-node": "^10.6.0", - "typescript": "^5.1.6", - "zksync-ethers":"^6.0.0" + "typescript": "^5.1.6" }, "dependencies": { + "zksync-ethers":"^6.0.0", + "hardhat": "^2.19.4", "@matterlabs/hardhat-zksync-deploy": "link:../../packages/hardhat-zksync-deploy", "@matterlabs/hardhat-zksync-solc": "link:../../packages/hardhat-zksync-solc", "@matterlabs/hardhat-zksync-upgradable": "link:../../packages/hardhat-zksync-upgradable", "@matterlabs/hardhat-zksync-verify": "link:../../packages/hardhat-zksync-verify", + "chalk": "4.1.2", + "ethers": "^6.7.1", "@openzeppelin/contracts-upgradeable": "^4.9.2", "zksync": "^0.13.1" }, diff --git a/examples/upgradable-example/scripts/deploy-box-beacon.ts b/examples/upgradable-example/scripts/deploy-box-beacon.ts index 368d12073..656c178fb 100644 --- a/examples/upgradable-example/scripts/deploy-box-beacon.ts +++ b/examples/upgradable-example/scripts/deploy-box-beacon.ts @@ -6,7 +6,7 @@ import * as hre from 'hardhat'; async function main() { const contractName = 'Box'; - console.info(chalk.yellow('Deploying ' + contractName + '...')); + console.info(chalk.yellow(`Deploying ${contractName}...`)); const testMnemonic = 'stuff slice staff easily soup parent arm payment cotton trade scatter struggle'; const zkWallet = Wallet.fromMnemonic(testMnemonic); diff --git a/examples/upgradable-example/scripts/deploy-box-proxy.ts b/examples/upgradable-example/scripts/deploy-box-proxy.ts index 58bac2187..759093fb0 100644 --- a/examples/upgradable-example/scripts/deploy-box-proxy.ts +++ b/examples/upgradable-example/scripts/deploy-box-proxy.ts @@ -6,7 +6,7 @@ import * as hre from 'hardhat'; async function main() { const contractName = 'Box'; - console.info(chalk.yellow('Deploying ' + contractName + '...')); + console.info(chalk.yellow(`Deploying ${contractName}...`)); const testMnemonic = 'stuff slice staff easily soup parent arm payment cotton trade scatter struggle'; const zkWallet = Wallet.fromMnemonic(testMnemonic); diff --git a/examples/upgradable-example/scripts/deploy-box-uups.ts b/examples/upgradable-example/scripts/deploy-box-uups.ts index df6937502..a96dd4258 100644 --- a/examples/upgradable-example/scripts/deploy-box-uups.ts +++ b/examples/upgradable-example/scripts/deploy-box-uups.ts @@ -6,7 +6,7 @@ import * as hre from 'hardhat'; async function main() { const contractName = 'BoxUups'; - console.info(chalk.yellow('Deploying ' + contractName + '...')); + console.info(chalk.yellow(`Deploying ${contractName}...`)); const testMnemonic = 'stuff slice staff easily soup parent arm payment cotton trade scatter struggle'; const zkWallet = Wallet.fromMnemonic(testMnemonic); diff --git a/examples/upgradable-example/scripts/upgrade-box-beacon.ts b/examples/upgradable-example/scripts/upgrade-box-beacon.ts index 5f22b9ada..7f9832430 100644 --- a/examples/upgradable-example/scripts/upgrade-box-beacon.ts +++ b/examples/upgradable-example/scripts/upgrade-box-beacon.ts @@ -3,7 +3,7 @@ import { Wallet } from 'zksync-ethers'; import * as zk from 'zksync-ethers'; import chalk from 'chalk'; import * as hre from 'hardhat'; -import {Contract} from 'ethers' +import { Contract } from 'ethers'; async function main() { const testMnemonic = 'stuff slice staff easily soup parent arm payment cotton trade scatter struggle'; @@ -16,26 +16,25 @@ async function main() { const contract = await deployer.loadArtifact(contractName); const beacon = await hre.zkUpgrades.deployBeacon(deployer.zkWallet, contract); await beacon.waitForDeployment(); - + const beaconAddress = await beacon.getAddress(); - const boxBeaconProxy = await hre.zkUpgrades.deployBeaconProxy(deployer.zkWallet,beaconAddress, contract, [42]); + const boxBeaconProxy = await hre.zkUpgrades.deployBeaconProxy(deployer.zkWallet, beaconAddress, contract, [42]); await boxBeaconProxy.waitForDeployment(); // upgrade beacon const boxV2Implementation = await deployer.loadArtifact('BoxV2'); - await hre.zkUpgrades.upgradeBeacon(deployer.zkWallet,beaconAddress, boxV2Implementation); - console.info(chalk.green('Successfully upgraded beacon Box to BoxV2 on address: ',beaconAddress)); + await hre.zkUpgrades.upgradeBeacon(deployer.zkWallet, beaconAddress, boxV2Implementation); + console.info(chalk.green('Successfully upgraded beacon Box to BoxV2 on address: ', beaconAddress)); - const attachTo = new zk.ContractFactory( + const attachTo = new zk.ContractFactory( boxV2Implementation.abi, boxV2Implementation.bytecode, deployer.zkWallet, - deployer.deploymentType + deployer.deploymentType, ); - const upgradedBox = attachTo.attach(await boxBeaconProxy.getAddress()); - + const upgradedBox = attachTo.attach(await boxBeaconProxy.getAddress()); upgradedBox.connect(zkWallet); // wait some time before the next call diff --git a/examples/verify-example/README.md b/examples/verify-example/README.md index ce0013857..e2374468d 100644 --- a/examples/verify-example/README.md +++ b/examples/verify-example/README.md @@ -1,6 +1,6 @@ -# zkSync 2.0 verify environment example +# zkSync Era verify environment example -This project demonstrates how to compile and verify your contracts in zkSync 2.0 using the Hardhat plugins. +This project demonstrates how to compile and verify your contracts in zkSync Era using the Hardhat plugins. ## Prerequisites diff --git a/examples/verify-example/package.json b/examples/verify-example/package.json index cab2b378a..34bdf16db 100644 --- a/examples/verify-example/package.json +++ b/examples/verify-example/package.json @@ -33,7 +33,7 @@ "@matterlabs/hardhat-zksync-ethers": "link:../../packages/hardhat-zksync-ethers", "@matterlabs/hardhat-zksync-verify": "link:../../packages/hardhat-zksync-verify", "chalk": "4.1.2", - "hardhat": "^2.19.2", + "hardhat": "^2.19.4", "ethers": "^6.7.1" }, "prettier": { diff --git a/examples/verify-example/test/helpers.ts b/examples/verify-example/test/helpers.ts index 0f41d4798..31bbbf822 100644 --- a/examples/verify-example/test/helpers.ts +++ b/examples/verify-example/test/helpers.ts @@ -1,13 +1,12 @@ -import { TASK_COMPILE } from "@matterlabs/hardhat-zksync-verify/dist/src/constants"; +import { TASK_COMPILE } from '@matterlabs/hardhat-zksync-verify/dist/src/constants'; import { resetHardhatContext } from 'hardhat/plugins-testing'; -import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; export const MOCK_ADDRESS = '0x110eb1e16A63c608787236E728Fa1817C72e6950'; export const WRONG_NUMBER_OF_CONSTRUCTOR_ARGUMENTS_ERROR = 'The number of constructor arguments you provided (0) does not match the number of constructor arguments the contract has been deployed with (1).'; export const CONTRACT_ALREADY_VERIFIED_ERROR = 'This contract is already verified'; - declare module 'mocha' { interface Context { env: HardhatRuntimeEnvironment; @@ -25,4 +24,4 @@ export function useEnvironment(networkName = 'testnet') { after('Resetting hardhat', function () { resetHardhatContext(); }); -} \ No newline at end of file +} diff --git a/examples/verify-example/test/tests.ts b/examples/verify-example/test/tests.ts index f9561ca15..1c7ea1c60 100644 --- a/examples/verify-example/test/tests.ts +++ b/examples/verify-example/test/tests.ts @@ -1,10 +1,13 @@ import assert from 'assert'; -import { - TASK_CHECK_VERIFICATION_STATUS, -} from '@matterlabs/hardhat-zksync-verify/dist/src/constants'; +import { TASK_CHECK_VERIFICATION_STATUS } from '@matterlabs/hardhat-zksync-verify/dist/src/constants'; import chalk from 'chalk'; -import { CONTRACT_ALREADY_VERIFIED_ERROR, MOCK_ADDRESS, WRONG_NUMBER_OF_CONSTRUCTOR_ARGUMENTS_ERROR, useEnvironment } from './helpers'; +import { + CONTRACT_ALREADY_VERIFIED_ERROR, + MOCK_ADDRESS, + WRONG_NUMBER_OF_CONSTRUCTOR_ARGUMENTS_ERROR, + useEnvironment, +} from './helpers'; describe('verify plugin', async function () { const testnetVerifyURL = 'https://explorer.sepolia.era.zksync.dev/contract_verification'; @@ -49,7 +52,7 @@ describe('verify plugin', async function () { constructorArguments: constructorArgs, }); - const success = await this.env.run(TASK_CHECK_VERIFICATION_STATUS, { verificationId: verificationId }); + const success = await this.env.run(TASK_CHECK_VERIFICATION_STATUS, { verificationId }); assert.equal(success, true); }); @@ -114,7 +117,10 @@ describe('verify plugin', async function () { const contractName = 'MintingContract'; const factoryContract = await this.env.zksyncEthers.getContractFactory(contractName); - const contract = await factoryContract.deploy('ContractName', 'ContractSymbol', MOCK_ADDRESS, [MOCK_ADDRESS, MOCK_ADDRESS]); + const contract = await factoryContract.deploy('ContractName', 'ContractSymbol', MOCK_ADDRESS, [ + MOCK_ADDRESS, + MOCK_ADDRESS, + ]); this.deployedAddress = await contract.getAddress(); console.info(chalk.green(`${contractName} was deployed to ${this.deployedAddress}`)); @@ -130,7 +136,7 @@ describe('verify plugin', async function () { constructorArguments: constructorArgs, }); - const success = await this.env.run(TASK_CHECK_VERIFICATION_STATUS, { verificationId: verificationId }); + const success = await this.env.run(TASK_CHECK_VERIFICATION_STATUS, { verificationId }); assert.equal(success, true); }); @@ -179,4 +185,4 @@ describe('verify plugin', async function () { console.log('Verification ID: ', verificationId); }); }); -}); \ No newline at end of file +}); diff --git a/examples/verify-vyper-example/README.md b/examples/verify-vyper-example/README.md index a8678b85f..65d0bce88 100644 --- a/examples/verify-vyper-example/README.md +++ b/examples/verify-vyper-example/README.md @@ -1,6 +1,6 @@ -# zkSync 2.0 verify vyper environment example +# zkSync Era verify vyper environment example -This project demonstrates how to compile and deploy your contracts in zkSync 2.0 using the Hardhat plugins. +This project demonstrates how to compile and deploy your contracts in zkSync Era using the Hardhat plugins. ## Prerequisites diff --git a/examples/verify-vyper-example/package.json b/examples/verify-vyper-example/package.json index f46af747b..613df629c 100644 --- a/examples/verify-vyper-example/package.json +++ b/examples/verify-vyper-example/package.json @@ -18,6 +18,7 @@ "@typescript-eslint/eslint-plugin": "6.13.1", "@typescript-eslint/parser": "6.13.1", "eslint": "^8.54.0", + "chalk": "4.1.2", "eslint-config-prettier": "9.0.0", "eslint-plugin-import": "2.29.0", "eslint-plugin-no-only-tests": "3.1.0", @@ -30,10 +31,12 @@ "dependencies": { "@matterlabs/hardhat-zksync-deploy": "link:../../packages/hardhat-zksync-deploy", "@matterlabs/hardhat-zksync-vyper": "link:../../packages/hardhat-zksync-vyper", + "@matterlabs/hardhat-zksync-verify": "link:../../packages/hardhat-zksync-verify", "@matterlabs/hardhat-zksync-verify-vyper": "link:../../packages/hardhat-zksync-verify-vyper", + "chalk": "4.1.2", "@nomiclabs/hardhat-vyper": "^3.0.5", "ethers": "^6.7.1", - "hardhat": "^2.19.2" + "hardhat": "^2.19.4" }, "prettier": { "tabWidth": 4, diff --git a/examples/verify-vyper-example/test/helpers.ts b/examples/verify-vyper-example/test/helpers.ts index 23b3e628b..8aa56d4d2 100644 --- a/examples/verify-vyper-example/test/helpers.ts +++ b/examples/verify-vyper-example/test/helpers.ts @@ -1,13 +1,12 @@ -import { TASK_COMPILE } from "@matterlabs/hardhat-zksync-verify/dist/src/constants"; +import { TASK_COMPILE } from '@matterlabs/hardhat-zksync-verify/dist/src/constants'; import { resetHardhatContext } from 'hardhat/plugins-testing'; -import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; export const MOCK_ADDRESS = '0x110eb1e16A63c608787236E728Fa1817C72e6950'; export const WRONG_NUMBER_OF_CONSTRUCTOR_ARGUMENTS_ERROR = 'The number of constructor arguments you provided (2) does not match the number of constructor arguments the contract has been deployed with (0).'; export const CONTRACT_ALREADY_VERIFIED_ERROR = 'This contract is already verified'; - declare module 'mocha' { interface Context { env: HardhatRuntimeEnvironment; @@ -25,4 +24,4 @@ export function useEnvironment(networkName = 'testnet') { after('Resetting hardhat', function () { resetHardhatContext(); }); -} \ No newline at end of file +} diff --git a/examples/verify-vyper-example/test/tests.ts b/examples/verify-vyper-example/test/tests.ts index d7367f87f..3f284acf1 100644 --- a/examples/verify-vyper-example/test/tests.ts +++ b/examples/verify-vyper-example/test/tests.ts @@ -1,10 +1,12 @@ import assert from 'assert'; -import { - TASK_CHECK_VERIFICATION_STATUS, -} from '@matterlabs/hardhat-zksync-verify-vyper/dist/src/constants'; +import { TASK_CHECK_VERIFICATION_STATUS } from '@matterlabs/hardhat-zksync-verify-vyper/dist/src/constants'; import chalk from 'chalk'; -import { CONTRACT_ALREADY_VERIFIED_ERROR, MOCK_ADDRESS, WRONG_NUMBER_OF_CONSTRUCTOR_ARGUMENTS_ERROR, useEnvironment } from './helpers'; +import { + CONTRACT_ALREADY_VERIFIED_ERROR, + WRONG_NUMBER_OF_CONSTRUCTOR_ARGUMENTS_ERROR, + useEnvironment, +} from './helpers'; describe('verify plugin', async function () { const testnetVerifyURL = 'https://explorer.sepolia.era.zksync.dev/contract_verification'; @@ -30,7 +32,7 @@ describe('verify plugin', async function () { beforeEach('Deploy Greeter contract', async function () { const contractName = 'Greeter'; - const constructorArgs:any[] = []; + const constructorArgs: any[] = []; const factoryContract = await this.env.zksyncEthers.getContractFactory(contractName); const contract = await factoryContract.deploy(constructorArgs); @@ -41,20 +43,20 @@ describe('verify plugin', async function () { assert.equal(this.env.network.verifyURL, testnetVerifyURL); }); it('Test verification happy path of the simple Greeter contract', async function () { - const constructorArgs:any[] = []; + const constructorArgs: any[] = []; const verificationId = await this.env.run('verify:verify:vyper', { address: this.deployedAddress, constructorArguments: constructorArgs, }); - const success = await this.env.run(TASK_CHECK_VERIFICATION_STATUS, { verificationId: verificationId }); + const success = await this.env.run(TASK_CHECK_VERIFICATION_STATUS, { verificationId }); assert.equal(success, true); }); it('Test verification of the already verified contract', async function () { - const constructorArgs:any[] = []; + const constructorArgs: any[] = []; await this.env.run('verify:verify:vyper', { address: this.deployedAddress, @@ -80,12 +82,12 @@ describe('verify plugin', async function () { // Run the verification again on the previously verified contract await this.env.run('verify:verify:vyper', { address: this.deployedAddress, - constructorArguments: ["0x123","Wrong number"], + constructorArguments: ['0x123', 'Wrong number'], }); } catch (error: any) { - console.info(error.message) + console.info(error.message); assert(error.message.includes(WRONG_NUMBER_OF_CONSTRUCTOR_ARGUMENTS_ERROR)); } }); }); -}); \ No newline at end of file +}); diff --git a/examples/vyper-example/README.md b/examples/vyper-example/README.md index da2d4e13c..cf4e2ac3e 100644 --- a/examples/vyper-example/README.md +++ b/examples/vyper-example/README.md @@ -1,6 +1,6 @@ -# zkSync 2.0 vyper environment example +# zkSync Era vyper environment example -This project demonstrates how to compile and deploy your contracts in zkSync 2.0 using the Hardhat plugins. +This project demonstrates how to compile and deploy your contracts in zkSync Era using the Hardhat plugins. ## Prerequisites diff --git a/examples/vyper-example/deploy/deploy.ts b/examples/vyper-example/deploy/deploy.ts index e79eb51f1..866043968 100644 --- a/examples/vyper-example/deploy/deploy.ts +++ b/examples/vyper-example/deploy/deploy.ts @@ -47,7 +47,7 @@ export default async function (hre: HardhatRuntimeEnvironment) { // Call the deployed contract. const forwarderContract = new ethers.Contract(forwarderAddress, greeter.abi, deployer.zkWallet.provider); const greeting = await forwarderContract.greet(); - if (greeting == 'Hello world') { + if (greeting === 'Hello world') { console.info(chalk.green(`Successful greeting from the contract!`)); } else { throw new Error(`Contract returned unexpected greeting: ${greeting}`); diff --git a/examples/vyper-example/package.json b/examples/vyper-example/package.json index c4a64eeb1..9dab27af0 100644 --- a/examples/vyper-example/package.json +++ b/examples/vyper-example/package.json @@ -32,8 +32,9 @@ "@matterlabs/hardhat-zksync-deploy": "link:../../packages/hardhat-zksync-deploy", "@matterlabs/hardhat-zksync-vyper": "link:../../packages/hardhat-zksync-vyper", "@nomiclabs/hardhat-vyper": "^3.0.5", + "chalk": "4.1.2", "ethers": "^6.7.1", - "hardhat": "^2.19.2", + "hardhat": "^2.19.4", "zksync-ethers": "^6.0.0" }, "prettier": { diff --git a/examples/zksync-ethers-example/README.md b/examples/zksync-ethers-example/README.md index bf98180ff..6efe1a98e 100644 --- a/examples/zksync-ethers-example/README.md +++ b/examples/zksync-ethers-example/README.md @@ -1,6 +1,6 @@ -# zkSync 2.0 zksync-ethers environment example +# zkSync Era zksync-ethers environment example -This project demonstrates how to compile and deploy your contracts in zkSync 2.0 using the Hardhat plugins. +This project demonstrates how to compile and deploy your contracts in zkSync Era using the Hardhat plugins. ## Prerequisites diff --git a/examples/zksync-ethers-example/deploy/001_deploy.ts b/examples/zksync-ethers-example/deploy/001_deploy.ts index 921993f93..341411949 100644 --- a/examples/zksync-ethers-example/deploy/001_deploy.ts +++ b/examples/zksync-ethers-example/deploy/001_deploy.ts @@ -3,12 +3,12 @@ import chalk from 'chalk'; export default async function (hre: HardhatRuntimeEnvironment) { console.info(chalk.yellow(`Running deploy`)); - const greeterFactory = await hre.zksyncEthers.getContractFactory("Greeter"); - const greeter = await greeterFactory.deploy("Hello, world!"); - + const greeterFactory = await hre.zksyncEthers.getContractFactory('Greeter'); + const greeter = await greeterFactory.deploy('Hello, world!'); + console.info(chalk.green(`Greeter deployed to: ${await greeter.getAddress()}`)); console.info(chalk.green(`Greeter greeting set to: ${await greeter.greet()}`)); - const tx = await greeter.setGreeting("Hello, world again!"); + const tx = await greeter.setGreeting('Hello, world again!'); await tx.wait(); console.info(chalk.green(`Greeter greeting set to: ${await greeter.greet()}`)); } diff --git a/examples/zksync-ethers-example/package.json b/examples/zksync-ethers-example/package.json index b3d80746b..8623e83e6 100644 --- a/examples/zksync-ethers-example/package.json +++ b/examples/zksync-ethers-example/package.json @@ -33,7 +33,7 @@ "@matterlabs/hardhat-zksync-solc": "link:../../packages/hardhat-zksync-solc", "@matterlabs/hardhat-zksync-ethers": "link:../../packages/hardhat-zksync-ethers", "chalk": "4.1.2", - "hardhat": "^2.19.2", + "hardhat": "^2.19.4", "ethers": "^6.7.1", "zksync-ethers": "^6.0.0" }, diff --git a/packages/hardhat-zksync-chai-matchers/CHANGELOG.md b/packages/hardhat-zksync-chai-matchers/CHANGELOG.md index 60894d75f..a6e3f9a9b 100644 --- a/packages/hardhat-zksync-chai-matchers/CHANGELOG.md +++ b/packages/hardhat-zksync-chai-matchers/CHANGELOG.md @@ -1,5 +1,12 @@ # @matterlabs/hardhat-zksync-chai-matchers +## [1.2.1](https://github.com/matter-labs/hardhat-zksync/compare/@matterlabs/hardhat-zksync-chai-matchers@1.2.0...@matterlabs/hardhat-zksync-chai-matchers-v1.2.1) (2023-12-22) + + +### Fixes + +* **docs:** update readme files ([#612](https://github.com/matter-labs/hardhat-zksync/issues/612)) ([682338e](https://github.com/matter-labs/hardhat-zksync/commit/682338e60f52021206325ff6eeec2c394a118642)) + ## 1.2.0 ### Minor Changes diff --git a/packages/hardhat-zksync-chai-matchers/README.md b/packages/hardhat-zksync-chai-matchers/README.md index 144aae6a7..25b8c6b36 100644 --- a/packages/hardhat-zksync-chai-matchers/README.md +++ b/packages/hardhat-zksync-chai-matchers/README.md @@ -1,5 +1,97 @@ -# hardhat-zksync-chai-matchers +# hardhat-zksync-chai-matchers 🚀 -[Hardhat](https://hardhat.org/) plugin that adds zkSync-specific capabilities to the [Chai](https://chaijs.com/) assertion library, making your smart contract tests easy to write and read. +zkSync Era's integration into the [Chai](https://chaijs.com/) assertion library is enabled by this [Hardhat](https://hardhat.org/) plugin, adding capabilities that make writing and reading smart contract tests easy." -Check out the documentation page for the [zksync chai matchers plugin](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-chai-matchers.html) for more detailed explanation on how to use hardhat-zksync-chai-matchers. +![Era Logo](https://github.com/matter-labs/era-contracts/raw/main/eraLogo.svg) + +## ⚠️ Version Compatibility Warning + +Ensure you are using the correct version of the plugin with ethers: +- For plugin version **<1.0.0**: + - Compatible with ethers **v5**. + +- For plugin version **≥1.0.0**: + - Compatible with ethers **v6** (⭐ Recommended) + +## 📥 Installation + +To install **hardhat-zksync-chai-matchers** plugin, run: + +`npm install -D @matterlabs/hardhat-zksync-chai-matchers` + +or + +`yarn add -D @matterlabs/hardhat-zksync-chai-matchers @nomicfoundation/hardhat-chai-matchers chai @nomiclabs/hardhat-ethers ethers` + +## 📖 Usage + +After installing it, add the plugin to your Hardhat config: + +`import "@matterlabs/hardhat-zksync-chai-matchers";` + +Then you'll be able to use the matchers in your tests. + +**changeEtherBalance** + +Assert that the ether balance of an address changed by a specific amount: + +``` +await expect(() => + sender.transfer({ + to: receiver.address, + amount: 2000, + }) +).to.changeEtherBalance(sender.address, -2000); +``` + +**changeTokenBalance** + +Assert that an ERC20 token balance of an address changed by a specific amount: + +``` +await expect(sender.transfer({ to: receiver.address, amount: 5, token: token.address })).to.changeTokenBalance(token, sender, -5); + +await expect(token.transfer(receiver.address, 5)).to.not.changeTokenBalance(token, sender, 0); +``` + +**revertedWithCustomError** + +Assert that a transaction reverted with a specific custom error: + +``` +await expect(contract.setAmount(100)).to.be.reverted; +``` + +You can also use regular chai matchers like: + + +``` +await expect(contract.setAmount(100)).to.emit(contract, "AmountUpdated"); + +expect("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049").to.be.properAddress; + +expect(await contract.getAmount()).to.equal(100); + +``` + +## 📝 Documentation + +In addition to the [hardhat-zksync-chai-matchers](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-chai-matchers.html), zkSync's Era [website](https://era.zksync.io/docs/) offers a variety of resources including: + +[Guides to get started](https://era.zksync.io/docs/dev/building-on-zksync/hello-world.html): Learn how to start building on zkSync Era.\ +[Hardhat zkSync Era plugins](https://era.zksync.io/docs/tools/hardhat/getting-started.html): Overview and guides for all Hardhat zkSync Era plugins.\ +[Hyperscaling](https://era.zksync.io/docs/reference/concepts/hyperscaling.html#what-are-hyperchains): Deep dive into hyperscaling on zkSync Era. + +## 🤝 Contributing + +Contributions are always welcome! Feel free to open any issue or send a pull request. + +Go to [CONTRIBUTING.md](https://github.com/matter-labs/hardhat-zksync/blob/main/.github/CONTRIBUTING.md) to learn about steps and best practices for contributing to zkSync hardhat tooling base repository. + + +## 🙌 Feedback, help and news + +[zkSync Era Discord server](https://join.zksync.dev/): for questions and feedback.\ +[Follow zkSync Era on Twitter](https://twitter.com/zksync) + +## Happy building! \ No newline at end of file diff --git a/packages/hardhat-zksync-chai-matchers/package.json b/packages/hardhat-zksync-chai-matchers/package.json index 4e6615021..1307080d9 100644 --- a/packages/hardhat-zksync-chai-matchers/package.json +++ b/packages/hardhat-zksync-chai-matchers/package.json @@ -1,6 +1,6 @@ { "name": "@matterlabs/hardhat-zksync-chai-matchers", - "version": "1.2.0", + "version": "1.2.1", "description": "Hardhat plugin that adds zkSync-specific capabilities to the Chaiassertion library, making your smart contract tests easy to write and read", "repository": "github:matter-labs/hardhat-zksync", "homepage": "https://github.com/matter-labs/hardhat-zksync/tree/main/packages/hardhat-zksync-chai-matchers", @@ -39,7 +39,16 @@ "singleQuote": true, "bracketSpacing": true }, - "dependencies": {}, + "dependencies": { + "chai": "^4.3.7", + "zksync-ethers": "^6.0.0", + "ethers": "^6.7.1", + "hardhat": "^2.19.4", + "ordinal":"1.0.3", + "chai-as-promised": "^7.1.1", + "@matterlabs/hardhat-zksync-deploy": "1.1.2", + "@matterlabs/hardhat-zksync-solc": "1.0.6" + }, "devDependencies": { "@nomicfoundation/hardhat-chai-matchers": "^2.0.2", "@types/chai": "^4.2.0", @@ -48,20 +57,16 @@ "@typescript-eslint/eslint-plugin": "6.13.1", "@typescript-eslint/parser": "6.13.1", "bignumber.js": "^9.1.0", - "chai": "^4.3.7", "eslint": "^8.54.0", "eslint-config-prettier": "9.0.0", "eslint-plugin-import": "2.29.0", "eslint-plugin-no-only-tests": "3.1.0", "eslint-plugin-prettier": "5.0.1", - "ethers": "^6.7.1", - "hardhat": "^2.19.2", "mocha": "^10.1.0", "prettier": "3.1.0", "rimraf": "^3.0.2", "ts-node": "^10.6.0", "typescript": "^5.1.6", - "zksync-ethers": "^6.0.0", "@matterlabs/zksync-contracts": "^0.6.1", "@openzeppelin/contracts": "^4.8.3", "c8": "^8.0.1" diff --git a/packages/hardhat-zksync-chai-matchers/src/errors.ts b/packages/hardhat-zksync-chai-matchers/src/errors.ts index 09b2bf7eb..b57a06dd3 100644 --- a/packages/hardhat-zksync-chai-matchers/src/errors.ts +++ b/packages/hardhat-zksync-chai-matchers/src/errors.ts @@ -10,14 +10,14 @@ export class ZkSyncChaiMatchersPluginError extends HardhatPluginError { } } -export class ZkSyncChaiMatchersPluginDefaultError extends HardhatPluginError{ - constructor(message:string){ - super(PLUGIN_NAME,message) +export class ZkSyncChaiMatchersPluginDefaultError extends HardhatPluginError { + constructor(message: string) { + super(PLUGIN_NAME, message); } } export class ZkSyncChaiMatchersPluginAssertionError extends HardhatPluginError { constructor(message: string) { - super(PLUGIN_NAME,`Assertion error: ${message}`); + super(PLUGIN_NAME, `Assertion error: ${message}`); } -} \ No newline at end of file +} diff --git a/packages/hardhat-zksync-chai-matchers/src/internal/changeEtherBalance.ts b/packages/hardhat-zksync-chai-matchers/src/internal/changeEtherBalance.ts index bfc27a20b..05cf69004 100644 --- a/packages/hardhat-zksync-chai-matchers/src/internal/changeEtherBalance.ts +++ b/packages/hardhat-zksync-chai-matchers/src/internal/changeEtherBalance.ts @@ -4,9 +4,9 @@ import * as zk from 'zksync-ethers'; import { buildAssert } from '@nomicfoundation/hardhat-chai-matchers/utils'; import { ensure } from '@nomicfoundation/hardhat-chai-matchers/internal/calledOnContract/utils'; +import { HttpNetworkConfig } from 'hardhat/types'; import { Account, getAddressOf } from './misc/account'; import { BalanceChangeOptions } from './misc/balance'; -import { HttpNetworkConfig } from 'hardhat/types'; export function supportChangeEtherBalance(Assertion: Chai.AssertionStatic) { Assertion.addMethod( @@ -18,7 +18,7 @@ export function supportChangeEtherBalance(Assertion: Chai.AssertionStatic) { options?: { balanceChangeOptions?: BalanceChangeOptions; overrides?: ethers.Overrides; - } + }, ) { const negated = this.__flags.negate; const subject = this._obj; @@ -29,7 +29,7 @@ export function supportChangeEtherBalance(Assertion: Chai.AssertionStatic) { assert( actualChange === toBigInt(balanceChange), `Expected the ether balance of "${address}" to change by ${balanceChange.toString()} wei, but it changed by ${actualChange.toString()} wei`, - `Expected the ether balance of "${address}" NOT to change by ${balanceChange.toString()} wei, but it did` + `Expected the ether balance of "${address}" NOT to change by ${balanceChange.toString()} wei, but it did`, ); }; @@ -41,7 +41,7 @@ export function supportChangeEtherBalance(Assertion: Chai.AssertionStatic) { this.catch = derivedPromise.catch.bind(derivedPromise); this.promise = derivedPromise; return this; - } + }, ); } @@ -52,9 +52,9 @@ export async function getBalanceChange( | (() => Promise | zk.types.TransactionResponse), account: Account | string, options?: BalanceChangeOptions, - overrides?: ethers.Overrides + overrides?: ethers.Overrides, ) { - const hre = await import("hardhat"); + const hre = await import('hardhat'); const provider = new zk.Provider((hre.network.config as HttpNetworkConfig).url); let txResponse: zk.types.TransactionResponse; @@ -76,16 +76,14 @@ export async function getBalanceChange( const balanceAfterHex = await provider.send('eth_getBalance', [address, `0x${txBlockNumber.toString(16)}`]); const balanceBeforeHex = await provider.send('eth_getBalance', [address, `0x${(txBlockNumber - 1).toString(16)}`]); - + const balanceAfter = BigInt(balanceAfterHex); const balanceBefore = BigInt(balanceBeforeHex); if (options?.includeFee !== true && address === txResponse.from) { - const gasPrice = overrides?.maxFeePerGas - ? (overrides?.maxFeePerGas) - : txReceipt.gasPrice ?? txResponse.gasPrice; + const gasPrice = overrides?.maxFeePerGas ? overrides?.maxFeePerGas : txReceipt.gasPrice ?? txResponse.gasPrice; const gasUsed = txReceipt.gasUsed; - const txFee:bigint = toBigInt(gasPrice)*gasUsed; + const txFee: bigint = toBigInt(gasPrice) * gasUsed; return balanceAfter + txFee - balanceBefore; } else { diff --git a/packages/hardhat-zksync-chai-matchers/src/internal/changeEtherBalances.ts b/packages/hardhat-zksync-chai-matchers/src/internal/changeEtherBalances.ts index 31260110c..7dc1e8ef9 100644 --- a/packages/hardhat-zksync-chai-matchers/src/internal/changeEtherBalances.ts +++ b/packages/hardhat-zksync-chai-matchers/src/internal/changeEtherBalances.ts @@ -17,7 +17,7 @@ export function supportChangeEtherBalances(Assertion: Chai.AssertionStatic) { options?: { balanceChangeOptions?: BalanceChangeOptions; overrides?: ethers.Overrides; - } + }, ) { const negated = this.__flags.negate; @@ -37,10 +37,10 @@ export function supportChangeEtherBalances(Assertion: Chai.AssertionStatic) { if (!(change === toBigInt(balanceChanges[i]))) { lines.push( `Expected the ether balance of ${accountAddresses[i]} (the ${ordinal( - i + 1 + i + 1, )} address in the list) to change by ${balanceChanges[ i - ].toString()} wei, but it changed by ${change.toString()} wei` + ].toString()} wei, but it changed by ${change.toString()} wei`, ); } }); @@ -52,15 +52,15 @@ export function supportChangeEtherBalances(Assertion: Chai.AssertionStatic) { if (change === toBigInt(balanceChanges[i])) { lines.push( `Expected the ether balance of ${accountAddresses[i]} (the ${ordinal( - i + 1 + i + 1, )} address in the list) NOT to change by ${balanceChanges[ i - ].toString()} wei, but it did` + ].toString()} wei, but it did`, ); } }); return lines.join('\n'); - } + }, ); }; @@ -72,7 +72,7 @@ export function supportChangeEtherBalances(Assertion: Chai.AssertionStatic) { this.catch = derivedPromise.catch.bind(derivedPromise); this.promise = derivedPromise; return this; - } + }, ); } @@ -80,7 +80,7 @@ export async function getBalanceChanges( transaction: zk.types.TransactionResponse | Promise, accounts: Array, options?: BalanceChangeOptions, - overrides?: ethers.Overrides + overrides?: ethers.Overrides, ) { const txResponse = await transaction; @@ -99,23 +99,22 @@ async function getTxFees( accounts: Array, txResponse: zk.types.TransactionResponse, options?: BalanceChangeOptions, - overrides?: ethers.Overrides + overrides?: ethers.Overrides, ) { - return Promise.all( accounts.map(async (account) => { if (options?.includeFee !== true && (await getAddressOf(account)) === txResponse.from) { const txReceipt = await txResponse.wait(); const gasPrice = overrides?.maxFeePerGas - ? (overrides?.maxFeePerGas) + ? overrides?.maxFeePerGas : txReceipt.gasPrice ?? txResponse.gasPrice; const gasUsed = txReceipt.gasUsed; - const txFee = toBigInt(gasPrice)*gasUsed; + const txFee = toBigInt(gasPrice) * gasUsed; return txFee; } - return 0n - }) + return 0n; + }), ); } diff --git a/packages/hardhat-zksync-chai-matchers/src/internal/changeTokenBalance.ts b/packages/hardhat-zksync-chai-matchers/src/internal/changeTokenBalance.ts index cf8b2cd55..e42d04382 100644 --- a/packages/hardhat-zksync-chai-matchers/src/internal/changeTokenBalance.ts +++ b/packages/hardhat-zksync-chai-matchers/src/internal/changeTokenBalance.ts @@ -3,18 +3,14 @@ import * as zk from 'zksync-ethers'; import { buildAssert } from '@nomicfoundation/hardhat-chai-matchers/utils'; import { ensure } from '@nomicfoundation/hardhat-chai-matchers/internal/calledOnContract/utils'; -import { Account, getAddressOf } from './misc/account'; import { HttpNetworkConfig } from 'hardhat/types'; -import { BaseContract, BaseContractMethod, BigNumberish, ContractTransactionResponse, toBigInt } from 'ethers'; +import { BaseContractMethod, BigNumberish, ContractTransactionResponse, toBigInt } from 'ethers'; +import { Account, getAddressOf } from './misc/account'; export type Token = zk.Contract & { balanceOf: BaseContractMethod<[string], bigint, bigint>; name: BaseContractMethod<[], string, string>; - transfer: BaseContractMethod< - [string, BigNumberish], - boolean, - ContractTransactionResponse - >; + transfer: BaseContractMethod<[string, BigNumberish], boolean, ContractTransactionResponse>; symbol: BaseContractMethod<[], string, string>; }; @@ -22,7 +18,6 @@ export function supportChangeTokenBalance(Assertion: Chai.AssertionStatic) { Assertion.addMethod( 'changeTokenBalance', function (this: any, token: Token, account: Account | string, balanceChange: BigNumberish) { - const negated = this.__flags.negate; let subject = this._obj; @@ -38,7 +33,7 @@ export function supportChangeTokenBalance(Assertion: Chai.AssertionStatic) { assert( actualChange === toBigInt(balanceChange), `Expected the balance of ${tokenDescription} tokens for "${address}" to change by ${balanceChange.toString()}, but it changed by ${actualChange.toString()}`, - `Expected the balance of ${tokenDescription} tokens for "${address}" NOT to change by ${balanceChange.toString()}, but it did` + `Expected the balance of ${tokenDescription} tokens for "${address}" NOT to change by ${balanceChange.toString()}, but it did`, ); }; @@ -52,7 +47,7 @@ export function supportChangeTokenBalance(Assertion: Chai.AssertionStatic) { this.catch = derivedPromise.catch.bind(derivedPromise); return this; - } + }, ); Assertion.addMethod( @@ -69,19 +64,19 @@ export function supportChangeTokenBalance(Assertion: Chai.AssertionStatic) { if (accounts.length !== balanceChanges.length) { throw new Error( - `The number of accounts (${accounts.length}) is different than the number of expected balance changes (${balanceChanges.length})` + `The number of accounts (${accounts.length}) is different than the number of expected balance changes (${balanceChanges.length})`, ); } const balanceChangesPromise = Promise.all( - accounts.map((account) => getBalanceChange(subject, token, account)) + accounts.map((account) => getBalanceChange(subject, token, account)), ); const addressesPromise = Promise.all(accounts.map(getAddressOf)); const checkBalanceChanges = ([actualChanges, addresses, tokenDescription]: [ bigint[], string[], - string + string, ]) => { const assert = buildAssert(negated, checkBalanceChanges); @@ -92,7 +87,7 @@ export function supportChangeTokenBalance(Assertion: Chai.AssertionStatic) { }, respectively, but they changed by ${actualChanges as any}`, `Expected the balances of ${tokenDescription} tokens for ${addresses as any} NOT to change by ${ balanceChanges as any - }, respectively, but they did` + }, respectively, but they did`, ); }; @@ -106,14 +101,14 @@ export function supportChangeTokenBalance(Assertion: Chai.AssertionStatic) { this.catch = derivedPromise.catch.bind(derivedPromise); return this; - } + }, ); } function checkToken(token: unknown, method: string) { if (typeof token !== 'object' || token === null || !('interface' in token)) { throw new Error(`The first argument of ${method} must be the contract instance of the token`); - } else if ((token as any).interface.getFunction("balanceOf") === null) { + } else if ((token as any).interface.getFunction('balanceOf') === null) { throw new Error('The given contract instance is not an ERC20 token'); } } @@ -121,9 +116,9 @@ function checkToken(token: unknown, method: string) { export async function getBalanceChange( transaction: zk.types.TransactionResponse | Promise, token: Token, - account: Account | string + account: Account | string, ) { - const hre = await import("hardhat"); + const hre = await import('hardhat'); const provider = new zk.Provider((hre.network.config as HttpNetworkConfig).url); const txResponse = await transaction; diff --git a/packages/hardhat-zksync-chai-matchers/src/internal/misc/account.ts b/packages/hardhat-zksync-chai-matchers/src/internal/misc/account.ts index bd1b24601..7e22e072f 100644 --- a/packages/hardhat-zksync-chai-matchers/src/internal/misc/account.ts +++ b/packages/hardhat-zksync-chai-matchers/src/internal/misc/account.ts @@ -1,5 +1,5 @@ import assert from 'assert'; -import type { Signer, Wallet,Contract } from 'zksync-ethers'; +import type { Signer, Wallet, Contract } from 'zksync-ethers'; import { ZkSyncChaiMatchersPluginAssertionError } from '../../errors'; export type Account = Signer | Wallet | Contract; @@ -9,17 +9,15 @@ export function isWalletOrContract(account: Account): account is Contract | Wall return account instanceof zk.Contract || account instanceof zk.Wallet; } -export async function getAddressOf(account: Account | string):Promise { - const { isAddressable } = await import("ethers"); +export async function getAddressOf(account: Account | string): Promise { + const { isAddressable } = await import('ethers'); if (typeof account === 'string') { assert(/^0x[0-9a-fA-F]{40}$/.test(account), `Invalid address ${account}`); return account; } - - if(isAddressable(account)) return await account.getAddress(); - - throw new ZkSyncChaiMatchersPluginAssertionError( - `Expected string or addressable, got ${account as any}` - ); + + if (isAddressable(account)) return await account.getAddress(); + + throw new ZkSyncChaiMatchersPluginAssertionError(`Expected string or addressable, got ${account as any}`); } diff --git a/packages/hardhat-zksync-chai-matchers/src/internal/misc/balance.ts b/packages/hardhat-zksync-chai-matchers/src/internal/misc/balance.ts index d66b32341..b55429784 100644 --- a/packages/hardhat-zksync-chai-matchers/src/internal/misc/balance.ts +++ b/packages/hardhat-zksync-chai-matchers/src/internal/misc/balance.ts @@ -1,8 +1,8 @@ import * as zk from 'zksync-ethers'; -import { Account, getAddressOf } from './account'; import { HttpNetworkConfig } from 'hardhat/types'; import { toBigInt } from 'ethers'; +import { Account, getAddressOf } from './account'; export interface BalanceChangeOptions { includeFee?: boolean; @@ -13,14 +13,14 @@ export function getAddresses(accounts: Array) { } export async function getBalances(accounts: Array, blockNumber?: number) { - const hre = await import("hardhat"); + const hre = await import('hardhat'); const provider = new zk.Provider((hre.network.config as HttpNetworkConfig).url); return Promise.all( accounts.map(async (account) => { const address = await getAddressOf(account); const result = await provider.send('eth_getBalance', [address, `0x${blockNumber?.toString(16) ?? 0}`]); - return toBigInt(result) - }) + return toBigInt(result); + }), ); } diff --git a/packages/hardhat-zksync-chai-matchers/src/internal/reverted/reverted.ts b/packages/hardhat-zksync-chai-matchers/src/internal/reverted/reverted.ts index 9ccef8611..99c6ece23 100644 --- a/packages/hardhat-zksync-chai-matchers/src/internal/reverted/reverted.ts +++ b/packages/hardhat-zksync-chai-matchers/src/internal/reverted/reverted.ts @@ -1,9 +1,9 @@ import { buildAssert } from '@nomicfoundation/hardhat-chai-matchers/utils'; import * as zk from 'zksync-ethers'; -import { decodeReturnData, getReturnDataFromError } from './utils'; import { HttpNetworkConfig } from 'hardhat/types'; import { toBeHex } from 'ethers'; +import { decodeReturnData, getReturnDataFromError } from './utils'; export function supportReverted(Assertion: Chai.AssertionStatic) { Assertion.addProperty('reverted', function (this: any) { @@ -26,7 +26,7 @@ export function supportReverted(Assertion: Chai.AssertionStatic) { assert( receipt.status === 0, 'Expected transaction to be reverted', - 'Expected transaction NOT to be reverted' + 'Expected transaction NOT to be reverted', ); } else if (isTransactionReceipt(value)) { const receipt = value; @@ -34,7 +34,7 @@ export function supportReverted(Assertion: Chai.AssertionStatic) { assert( receipt.status === 0, 'Expected transaction to be reverted', - 'Expected transaction NOT to be reverted' + 'Expected transaction NOT to be reverted', ); } else { // If the subject of the assertion is not connected to a transaction @@ -60,15 +60,15 @@ export function supportReverted(Assertion: Chai.AssertionStatic) { assert( true, undefined, - `Expected transaction NOT to be reverted, but it reverted with reason '${decodedReturnData.reason}'` + `Expected transaction NOT to be reverted, but it reverted with reason '${decodedReturnData.reason}'`, ); } else if (decodedReturnData.kind === 'Panic') { assert( true, undefined, - `Expected transaction NOT to be reverted, but it reverted with panic code ${toBeHex(decodedReturnData.code)} (${ - decodedReturnData.description - })` + `Expected transaction NOT to be reverted, but it reverted with panic code ${toBeHex( + decodedReturnData.code, + )} (${decodedReturnData.description})`, ); } else { const _exhaustiveCheck: never = decodedReturnData; @@ -87,7 +87,7 @@ export function supportReverted(Assertion: Chai.AssertionStatic) { } async function getTransactionReceipt(hash: string) { - const hre = await import("hardhat"); + const hre = await import('hardhat'); const provider = new zk.Provider((hre.network.config as HttpNetworkConfig).url); return provider.getTransactionReceipt(hash); diff --git a/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWith.ts b/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWith.ts index 4ffc719c2..0fd6c4f53 100644 --- a/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWith.ts +++ b/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWith.ts @@ -1,7 +1,7 @@ import { buildAssert } from '@nomicfoundation/hardhat-chai-matchers/utils'; -import { decodeReturnData, getReturnDataFromError } from './utils'; import { toBeHex } from 'ethers'; +import { decodeReturnData, getReturnDataFromError } from './utils'; export function supportRevertedWith(Assertion: Chai.AssertionStatic) { Assertion.addMethod('revertedWith', function (this: any, expectedReason: unknown) { @@ -26,25 +26,25 @@ export function supportRevertedWith(Assertion: Chai.AssertionStatic) { if (decodedReturnData.kind === 'Empty') { assert( false, - `Expected transaction to be reverted with reason '${expectedReason}', but it reverted without a reason` + `Expected transaction to be reverted with reason '${expectedReason}', but it reverted without a reason`, ); } else if (decodedReturnData.kind === 'Error') { assert( decodedReturnData.reason === expectedReason, `Expected transaction to be reverted with reason '${expectedReason}', but it reverted with reason '${decodedReturnData.reason}'`, - `Expected transaction NOT to be reverted with reason '${expectedReason}', but it was` + `Expected transaction NOT to be reverted with reason '${expectedReason}', but it was`, ); } else if (decodedReturnData.kind === 'Panic') { assert( false, - `Expected transaction to be reverted with reason '${expectedReason}', but it reverted with panic code ${toBeHex(decodedReturnData.code)} (${ - decodedReturnData.description - })` + `Expected transaction to be reverted with reason '${expectedReason}', but it reverted with panic code ${toBeHex( + decodedReturnData.code, + )} (${decodedReturnData.description})`, ); } else if (decodedReturnData.kind === 'Custom') { assert( false, - `Expected transaction to be reverted with reason '${expectedReason}', but it reverted with a custom error` + `Expected transaction to be reverted with reason '${expectedReason}', but it reverted with a custom error`, ); } else { const _exhaustiveCheck: never = decodedReturnData; diff --git a/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWithCustomError.ts b/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWithCustomError.ts index 7cb7b3d5c..26c7c7f46 100644 --- a/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWithCustomError.ts +++ b/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWithCustomError.ts @@ -1,12 +1,12 @@ -import type EthersT from 'ethers' +// eslint-disable-next-line @typescript-eslint/naming-convention +import EthersT, { toBeHex } from 'ethers'; import { AssertionError } from 'chai'; import { buildAssert, Ssfi } from '@nomicfoundation/hardhat-chai-matchers/utils'; -import { decodeReturnData, getReturnDataFromError } from './utils'; -import { toBeHex } from 'ethers'; import { assertIsNotNull } from '../utils'; +import { decodeReturnData, getReturnDataFromError } from './utils'; export const REVERTED_WITH_CUSTOM_ERROR_CALLED = 'customErrorAssertionCalled'; @@ -19,22 +19,21 @@ interface CustomErrorAssertionData { export function supportRevertedWithCustomError(Assertion: Chai.AssertionStatic, utils: Chai.ChaiUtils) { Assertion.addMethod( 'revertedWithCustomError', - function (this: any, contract:EthersT.BaseContract, expectedCustomErrorName: string) { + function (this: any, contract: EthersT.BaseContract, expectedCustomErrorName: string) { const negated = this.__flags.negate; - const {iface,expectedCustomError} = validateInput(this._obj,contract,expectedCustomErrorName) + const { iface, expectedCustomError } = validateInput(this._obj, contract, expectedCustomErrorName); const onSuccess = () => { const assert = buildAssert(negated, onSuccess); assert( false, - `Expected transaction to be reverted with custom error '${expectedCustomErrorName}', but it didn't revert` + `Expected transaction to be reverted with custom error '${expectedCustomErrorName}', but it didn't revert`, ); }; const onError = (error: any) => { - const assert = buildAssert(negated, onError); const returnData = getReturnDataFromError(error); @@ -43,19 +42,19 @@ export function supportRevertedWithCustomError(Assertion: Chai.AssertionStatic, if (decodedReturnData.kind === 'Empty') { assert( false, - `Expected transaction to be reverted with custom error '${expectedCustomErrorName}', but it reverted without a reason` + `Expected transaction to be reverted with custom error '${expectedCustomErrorName}', but it reverted without a reason`, ); } else if (decodedReturnData.kind === 'Error') { assert( false, - `Expected transaction to be reverted with custom error '${expectedCustomErrorName}', but it reverted with reason '${decodedReturnData.reason}'` + `Expected transaction to be reverted with custom error '${expectedCustomErrorName}', but it reverted with reason '${decodedReturnData.reason}'`, ); } else if (decodedReturnData.kind === 'Panic') { assert( false, - `Expected transaction to be reverted with custom error '${expectedCustomErrorName}', but it reverted with panic code ${toBeHex(decodedReturnData.code)} (${ - decodedReturnData.description - })` + `Expected transaction to be reverted with custom error '${expectedCustomErrorName}', but it reverted with panic code ${toBeHex( + decodedReturnData.code, + )} (${decodedReturnData.description})`, ); } else if (decodedReturnData.kind === 'Custom') { if (decodedReturnData.id === expectedCustomError.selector) { @@ -70,22 +69,22 @@ export function supportRevertedWithCustomError(Assertion: Chai.AssertionStatic, assert( true, undefined, - `Expected transaction NOT to be reverted with custom error '${expectedCustomErrorName}', but it was` + `Expected transaction NOT to be reverted with custom error '${expectedCustomErrorName}', but it was`, ); } else { // try to decode the actual custom error // this will only work when the error comes from the given contract const actualCustomError = iface.getError(decodedReturnData.id); - + if (actualCustomError === null) { assert( false, - `Expected transaction to be reverted with custom error '${expectedCustomErrorName}', but it reverted with a different custom error` - ); - } else { + `Expected transaction to be reverted with custom error '${expectedCustomErrorName}', but it reverted with a different custom error`, + ); + } else { assert( false, - `Expected transaction to be reverted with custom error '${expectedCustomErrorName}', but it reverted with custom error '${actualCustomError.name}'` + `Expected transaction to be reverted with custom error '${expectedCustomErrorName}', but it reverted with custom error '${actualCustomError.name}'`, ); } } @@ -104,7 +103,7 @@ export function supportRevertedWithCustomError(Assertion: Chai.AssertionStatic, this.catch = derivedPromise.catch.bind(derivedPromise); return this; - } + }, ); } @@ -113,7 +112,7 @@ export async function revertedWithCustomErrorWithArgs( Assertion: Chai.AssertionStatic, utils: Chai.ChaiUtils, expectedArgs: any[], - ssfi: Ssfi + ssfi: Ssfi, ) { const negated = false; // .withArgs cannot be negated const assert = buildAssert(negated, ssfi); @@ -127,14 +126,12 @@ export async function revertedWithCustomErrorWithArgs( const { contractInterface, customError, returnData } = customErrorAssertionData; const errorFragment = contractInterface.getError(customError.name); - assertIsNotNull(errorFragment, "errorFragment"); + assertIsNotNull(errorFragment, 'errorFragment'); // We transform ether's Array-like object into an actual array as it's safer - const actualArgs = resultToArray( - contractInterface.decodeErrorResult(errorFragment, returnData) - ); + const actualArgs = resultToArray(contractInterface.decodeErrorResult(errorFragment, returnData)); new Assertion(actualArgs).to.have.same.length( expectedArgs.length, - `expected ${expectedArgs.length} args but got ${actualArgs.length}` + `expected ${expectedArgs.length} args but got ${actualArgs.length}`, ); for (const [i, actualArg] of actualArgs.entries()) { @@ -144,7 +141,7 @@ export async function revertedWithCustomErrorWithArgs( try { assert( expectedArg(actualArg), - `${errorPrefix} returned false` + `${errorPrefix} returned false`, // no need for a negated message, since we disallow mixing .not. with // .withArgs ); @@ -152,7 +149,7 @@ export async function revertedWithCustomErrorWithArgs( if (e instanceof AssertionError) { assert( false, - `${errorPrefix} threw an AssertionError: ${e.message}` + `${errorPrefix} threw an AssertionError: ${e.message}`, // no need for a negated message, since we disallow mixing .not. with // .withArgs ); @@ -173,7 +170,7 @@ interface CustomError { signature: string; } -function findCustomErrorByName(iface: any, name: string): CustomError | undefined { +function _findCustomErrorByName(iface: any, name: string): CustomError | undefined { const ethers = require('ethers'); const customErrorEntry = Object.entries(iface.errors).find(([, fragment]: any) => fragment.name === name); @@ -192,11 +189,11 @@ function findCustomErrorByName(iface: any, name: string): CustomError | undefine }; } -function findCustomErrorById(iface: any, id: string): CustomError | undefined { +function _findCustomErrorById(iface: any, id: string): CustomError | undefined { const ethers = require('ethers'); const customErrorEntry: any = Object.entries(iface.errors).find( - ([signature]: any) => ethers.utils.id(signature).slice(0, 10) === id + ([signature]: any) => ethers.utils.id(signature).slice(0, 10) === id, ); if (customErrorEntry === undefined) { @@ -213,48 +210,40 @@ function findCustomErrorById(iface: any, id: string): CustomError | undefined { function validateInput( obj: any, contract: EthersT.BaseContract, - expectedCustomErrorName: string - ): { iface: EthersT.Interface; expectedCustomError: EthersT.ErrorFragment } { + expectedCustomErrorName: string, +): { iface: EthersT.Interface; expectedCustomError: EthersT.ErrorFragment } { try { - // check the case where users forget to pass the contract as the first - // argument - if (typeof contract === "string" || contract?.interface === undefined) { - // discard subject since it could potentially be a rejected promise - throw new TypeError( - "The first argument of .revertedWithCustomError must be the contract that defines the custom error" - ); - } - - // validate custom error name - if (typeof expectedCustomErrorName !== "string") { - throw new TypeError("Expected the custom error name to be a string"); - } - - const iface = contract.interface; - const expectedCustomError = iface.getError(expectedCustomErrorName); - - // check that interface contains the given custom error - if (expectedCustomError === null) { - throw new Error( - `The given contract doesn't have a custom error named '${expectedCustomErrorName}'` - ); - } - - return { iface, expectedCustomError }; + // check the case where users forget to pass the contract as the first + // argument + if (typeof contract === 'string' || contract?.interface === undefined) { + // discard subject since it could potentially be a rejected promise + throw new TypeError( + 'The first argument of .revertedWithCustomError must be the contract that defines the custom error', + ); + } + + // validate custom error name + if (typeof expectedCustomErrorName !== 'string') { + throw new TypeError('Expected the custom error name to be a string'); + } + + const iface = contract.interface; + const expectedCustomError = iface.getError(expectedCustomErrorName); + + // check that interface contains the given custom error + if (expectedCustomError === null) { + throw new Error(`The given contract doesn't have a custom error named '${expectedCustomErrorName}'`); + } + + return { iface, expectedCustomError }; } catch (e) { - // if the input validation fails, we discard the subject since it could - // potentially be a rejected promise - Promise.resolve(obj).catch(() => {}); - throw e; + // if the input validation fails, we discard the subject since it could + // potentially be a rejected promise + Promise.resolve(obj).catch(() => {}); + throw e; } - } - - export function resultToArray(result: EthersT.Result): any[] { - return result - .toArray() - .map((x) => - typeof x === "object" && x !== null && "toArray" in x - ? resultToArray(x) - : x - ); - } \ No newline at end of file +} + +export function resultToArray(result: EthersT.Result): any[] { + return result.toArray().map((x) => (typeof x === 'object' && x !== null && 'toArray' in x ? resultToArray(x) : x)); +} diff --git a/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWithPanic.ts b/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWithPanic.ts index 13f77ce2a..5aacaaf5b 100644 --- a/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWithPanic.ts +++ b/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWithPanic.ts @@ -2,8 +2,8 @@ import { normalizeToBigInt } from 'hardhat/common'; import { panicErrorCodeToReason } from '@nomicfoundation/hardhat-chai-matchers/internal/reverted/panic'; import { buildAssert } from '@nomicfoundation/hardhat-chai-matchers/utils'; -import { decodeReturnData, getReturnDataFromError } from './utils'; import { toBeHex } from 'ethers'; +import { decodeReturnData, getReturnDataFromError } from './utils'; export function supportRevertedWithPanic(Assertion: Chai.AssertionStatic) { Assertion.addMethod('revertedWithPanic', function (this: any, expectedCodeArg: any) { @@ -16,7 +16,7 @@ export function supportRevertedWithPanic(Assertion: Chai.AssertionStatic) { } } catch { throw new TypeError( - `Expected the given panic code to be a number-like value, but got '${expectedCodeArg}'` + `Expected the given panic code to be a number-like value, but got '${expectedCodeArg}'`, ); } @@ -47,35 +47,35 @@ export function supportRevertedWithPanic(Assertion: Chai.AssertionStatic) { if (decodedReturnData.kind === 'Empty') { assert( false, - `Expected transaction to be reverted with ${formattedPanicCode}, but it reverted without a reason` + `Expected transaction to be reverted with ${formattedPanicCode}, but it reverted without a reason`, ); } else if (decodedReturnData.kind === 'Error') { assert( false, - `Expected transaction to be reverted with ${formattedPanicCode}, but it reverted with reason '${decodedReturnData.reason}'` + `Expected transaction to be reverted with ${formattedPanicCode}, but it reverted with reason '${decodedReturnData.reason}'`, ); } else if (decodedReturnData.kind === 'Panic') { if (code !== undefined) { assert( - decodedReturnData.code===(code), - `Expected transaction to be reverted with ${formattedPanicCode}, but it reverted with panic code ${toBeHex(decodedReturnData.code)} (${ - decodedReturnData.description - })`, - `Expected transaction NOT to be reverted with ${formattedPanicCode}, but it was` + decodedReturnData.code === code, + `Expected transaction to be reverted with ${formattedPanicCode}, but it reverted with panic code ${toBeHex( + decodedReturnData.code, + )} (${decodedReturnData.description})`, + `Expected transaction NOT to be reverted with ${formattedPanicCode}, but it was`, ); } else { assert( true, undefined, - `Expected transaction NOT to be reverted with ${formattedPanicCode}, but it reverted with panic code ${toBeHex(decodedReturnData.code)} (${ - decodedReturnData.description - })` + `Expected transaction NOT to be reverted with ${formattedPanicCode}, but it reverted with panic code ${toBeHex( + decodedReturnData.code, + )} (${decodedReturnData.description})`, ); } } else if (decodedReturnData.kind === 'Custom') { assert( false, - `Expected transaction to be reverted with ${formattedPanicCode}, but it reverted with a custom error` + `Expected transaction to be reverted with ${formattedPanicCode}, but it reverted with a custom error`, ); } else { const _exhaustiveCheck: never = decodedReturnData; diff --git a/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWithoutReason.ts b/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWithoutReason.ts index 9beb18079..e13d6336f 100644 --- a/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWithoutReason.ts +++ b/packages/hardhat-zksync-chai-matchers/src/internal/reverted/revertedWithoutReason.ts @@ -1,7 +1,7 @@ import { buildAssert } from '@nomicfoundation/hardhat-chai-matchers/utils'; -import { decodeReturnData, getReturnDataFromError } from './utils'; import { toBeHex } from 'ethers'; +import { decodeReturnData, getReturnDataFromError } from './utils'; export function supportRevertedWithoutReason(Assertion: Chai.AssertionStatic) { Assertion.addMethod('revertedWithoutReason', function (this: any) { @@ -22,21 +22,21 @@ export function supportRevertedWithoutReason(Assertion: Chai.AssertionStatic) { if (decodedReturnData.kind === 'Error') { assert( false, - `Expected transaction to be reverted without a reason, but it reverted with reason '${decodedReturnData.reason}'` + `Expected transaction to be reverted without a reason, but it reverted with reason '${decodedReturnData.reason}'`, ); } else if (decodedReturnData.kind === 'Empty') { assert(true, undefined, 'Expected transaction NOT to be reverted without a reason, but it was'); } else if (decodedReturnData.kind === 'Panic') { assert( false, - `Expected transaction to be reverted without a reason, but it reverted with panic code ${toBeHex(decodedReturnData.code)} (${ - decodedReturnData.description - })` + `Expected transaction to be reverted without a reason, but it reverted with panic code ${toBeHex( + decodedReturnData.code, + )} (${decodedReturnData.description})`, ); } else if (decodedReturnData.kind === 'Custom') { assert( false, - `Expected transaction to be reverted without a reason, but it reverted with a custom error` + `Expected transaction to be reverted without a reason, but it reverted with a custom error`, ); } else { const _exhaustiveCheck: never = decodedReturnData; diff --git a/packages/hardhat-zksync-chai-matchers/src/internal/reverted/utils.ts b/packages/hardhat-zksync-chai-matchers/src/internal/reverted/utils.ts index 85058d415..5d9b23c6e 100644 --- a/packages/hardhat-zksync-chai-matchers/src/internal/reverted/utils.ts +++ b/packages/hardhat-zksync-chai-matchers/src/internal/reverted/utils.ts @@ -1,4 +1,5 @@ -import type EthersT from "ethers"; +// eslint-disable-next-line @typescript-eslint/naming-convention +import type EthersT from 'ethers'; import { AssertionError } from 'chai'; @@ -14,90 +15,85 @@ const PANIC_CODE_PREFIX = '0x4e487b71'; export function getReturnDataFromError(error: any): string { if (!(error instanceof Error)) { - throw new AssertionError("Expected an Error object"); + throw new AssertionError('Expected an Error object'); } - + // cast to any again so we don't have to cast it every time we access // some property that doesn't exist on Error error = error as any; - + const errorData = error.data ?? error.error?.data; - + if (errorData === undefined) { - throw error; + throw error; } - - const returnData = typeof errorData === "string" ? errorData : errorData.data; - - if (returnData === undefined || typeof returnData !== "string") { - throw error; + + const returnData = typeof errorData === 'string' ? errorData : errorData.data; + + if (returnData === undefined || typeof returnData !== 'string') { + throw error; } - + return returnData; - } +} type DecodedReturnData = | { - kind: 'Error'; - reason: string; - } + kind: 'Error'; + reason: string; + } | { - kind: 'Empty'; - } + kind: 'Empty'; + } | { - kind: 'Panic'; - code: bigint; - description: string; - } + kind: 'Panic'; + code: bigint; + description: string; + } | { - kind: 'Custom'; - id: string; - data: string; - }; - -type FuncSelectorWithData = { - funcSelector: string; - data: string; -}; + kind: 'Custom'; + id: string; + data: string; + }; export function decodeReturnData(returnData: string): DecodedReturnData { - const { AbiCoder } = require("ethers") as typeof EthersT; + const { AbiCoder } = require('ethers') as typeof EthersT; const abi = new AbiCoder(); - if (returnData === "0x") { - return { kind: "Empty" }; + if (returnData === '0x') { + return { kind: 'Empty' }; } else if (returnData.startsWith(ERROR_STRING_PREFIX)) { const encodedReason = returnData.slice(ERROR_STRING_PREFIX.length); let reason: string; try { - reason = abi.decode(["string"], `0x${encodedReason}`)[0]; + reason = abi.decode(['string'], `0x${encodedReason}`)[0]; } catch (e: any) { - throw new ZkSyncChaiMatchersPluginError(encodedReason, "string", e); + throw new ZkSyncChaiMatchersPluginError(encodedReason, 'string', e); } return { - kind: "Error", + kind: 'Error', reason, }; } else if (returnData.startsWith(PANIC_CODE_PREFIX)) { const encodedReason = returnData.slice(PANIC_CODE_PREFIX.length); let code: bigint; try { - code = abi.decode(["uint256"], `0x${encodedReason}`)[0]; + code = abi.decode(['uint256'], `0x${encodedReason}`)[0]; } catch (e: any) { - throw new ZkSyncChaiMatchersPluginError(encodedReason, "uint256", e); + throw new ZkSyncChaiMatchersPluginError(encodedReason, 'uint256', e); } - const description = panicErrorCodeToReason(code) ?? "unknown panic code"; + const description = panicErrorCodeToReason(code) ?? 'unknown panic code'; return { - kind: "Panic", + kind: 'Panic', code, description, }; } return { - kind: "Custom", + kind: 'Custom', id: returnData.slice(0, 10), data: `0x${returnData.slice(10)}`, }; diff --git a/packages/hardhat-zksync-chai-matchers/src/internal/utils.ts b/packages/hardhat-zksync-chai-matchers/src/internal/utils.ts index ede2944e4..93632c55f 100644 --- a/packages/hardhat-zksync-chai-matchers/src/internal/utils.ts +++ b/packages/hardhat-zksync-chai-matchers/src/internal/utils.ts @@ -1,12 +1,7 @@ -import { ZkSyncChaiMatchersPluginDefaultError, ZkSyncChaiMatchersPluginError } from "../errors"; +import { ZkSyncChaiMatchersPluginDefaultError } from '../errors'; -export function assertIsNotNull( - value: T, - valueName: string - ): asserts value is Exclude { +export function assertIsNotNull(value: T, valueName: string): asserts value is Exclude { if (value === null) { - throw new ZkSyncChaiMatchersPluginDefaultError( - `${valueName} should not be null` - ); + throw new ZkSyncChaiMatchersPluginDefaultError(`${valueName} should not be null`); } - } \ No newline at end of file +} diff --git a/packages/hardhat-zksync-chai-matchers/src/internal/withArgs.ts b/packages/hardhat-zksync-chai-matchers/src/internal/withArgs.ts index 0d79fe06b..a897f5c9d 100644 --- a/packages/hardhat-zksync-chai-matchers/src/internal/withArgs.ts +++ b/packages/hardhat-zksync-chai-matchers/src/internal/withArgs.ts @@ -13,12 +13,12 @@ export function supportWithArgs(Assertion: Chai.AssertionStatic, utils: Chai.Cha if (!emitCalled && !revertedWithCustomErrorCalled) { throw new Error( - 'withArgs can only be used in combination with a previous .emit or .revertedWithCustomError assertion' + 'withArgs can only be used in combination with a previous .emit or .revertedWithCustomError assertion', ); } if (emitCalled && revertedWithCustomErrorCalled) { throw new Error( - 'withArgs called with both .emit and .revertedWithCustomError, but these assertions cannot be combined' + 'withArgs called with both .emit and .revertedWithCustomError, but these assertions cannot be combined', ); } diff --git a/packages/hardhat-zksync-chai-matchers/src/types.ts b/packages/hardhat-zksync-chai-matchers/src/types.ts index 8efd0c1d9..7ed5b448b 100644 --- a/packages/hardhat-zksync-chai-matchers/src/types.ts +++ b/packages/hardhat-zksync-chai-matchers/src/types.ts @@ -1 +1 @@ -import '@nomicfoundation/hardhat-chai-matchers/types'; \ No newline at end of file +import '@nomicfoundation/hardhat-chai-matchers/types'; diff --git a/packages/hardhat-zksync-chai-matchers/test/changeEtherBalance.ts b/packages/hardhat-zksync-chai-matchers/test/changeEtherBalance.ts index 703b89161..220fbf6ae 100644 --- a/packages/hardhat-zksync-chai-matchers/test/changeEtherBalance.ts +++ b/packages/hardhat-zksync-chai-matchers/test/changeEtherBalance.ts @@ -5,11 +5,11 @@ import util from 'util'; import { Deployer } from '@matterlabs/hardhat-zksync-deploy/src/deployer'; -import { useEnvironmentWithLocalSetup } from './helpers'; +import { HDNodeWallet } from 'ethers'; import '../src/internal/add-chai-matchers'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; import { HttpNetworkConfig } from 'hardhat/types'; -import { HDNodeWallet } from 'ethers'; +import { useEnvironmentWithLocalSetup } from './helpers'; const RICH_WALLET_PK = '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110'; @@ -29,11 +29,11 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { let contract: zk.Contract; let gasPrice: number; let gasUsed: number; - let txGasFees: number; - let overrides: {}; + let _txGasFees: number; + let _overrides: {}; beforeEach(async function () { - const hre = await import("hardhat"); + const hre = await import('hardhat'); provider = new zk.Provider((hre.network.config as HttpNetworkConfig).url); sender = new zk.Wallet(RICH_WALLET_PK, provider); receiver = zk.Wallet.createRandom(); @@ -44,9 +44,9 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { gasPrice = 250000000; gasUsed = 157263; - txGasFees = gasPrice * gasUsed; + _txGasFees = gasPrice * gasUsed; - overrides = { + _overrides = { type: 2, maxFeePerGas: 1 * gasPrice, maxPriorityFeePerGas: 1 * gasPrice, @@ -60,7 +60,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.changeEtherBalance(sender, '-200'); }); @@ -69,7 +69,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.changeEtherBalance(sender.address, '-200'); }); @@ -78,7 +78,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.changeEtherBalance(sender.address, '-200'); }); @@ -87,7 +87,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.changeEtherBalance(sender, '-200'); }); @@ -96,7 +96,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.changeEtherBalance(sender, -200); }); @@ -105,8 +105,8 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) - ).to.changeEtherBalance(sender,-200); + }), + ).to.changeEtherBalance(sender, -200); }); it('Should pass when given an ethers BigNumber - zkSync transfer', async () => { @@ -114,7 +114,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.changeEtherBalance(sender, -200); }); @@ -123,7 +123,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.changeEtherBalance(receiver, 200); }); @@ -132,7 +132,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.changeEtherBalance(receiver, 200); }); @@ -144,7 +144,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { overrides: { gasPrice, }, - }) + }), ).to.changeEtherBalance(receiver, 200, { balanceChangeOptions: { includeFee: true, @@ -156,9 +156,9 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await expect(() => sender.sendTransaction({ to: receiver.address, - gasPrice: gasPrice, + gasPrice, value: 200, - }) + }), ).to.changeEtherBalance(sender, -200); }); @@ -170,7 +170,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { overrides: { gasPrice, }, - }) + }), ).to.changeEtherBalance(sender, -200); }); @@ -179,8 +179,8 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) - ).to.changeEtherBalance(receiver,200); + }), + ).to.changeEtherBalance(receiver, 200); }); it('Should pass when expected balance change is passed as BN and is equal to an actual - zkSync tranfer', async () => { @@ -188,8 +188,8 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.changeEtherBalance(receiver,200); + }), + ).to.changeEtherBalance(receiver, 200); }); it('Should pass on negative case when expected balance change is not equal to an actual', async () => { @@ -197,8 +197,8 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) - ).to.not.changeEtherBalance(receiver,300); + }), + ).to.not.changeEtherBalance(receiver, 300); }); it('Should pass on negative case when expected balance change is not equal to an actual - zkSync transfer', async () => { @@ -206,8 +206,8 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.not.changeEtherBalance(receiver,300); + }), + ).to.not.changeEtherBalance(receiver, 300); }); it('Should throw when expected balance change value was different from an actual', async () => { @@ -216,11 +216,11 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) - ).to.changeEtherBalance(sender, -500) + }), + ).to.changeEtherBalance(sender, -500), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei` + `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei`, ); }); @@ -230,11 +230,11 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.changeEtherBalance(sender, '-500') + }), + ).to.changeEtherBalance(sender, '-500'), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei` + `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei`, ); }); @@ -244,11 +244,11 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) - ).to.not.changeEtherBalance(sender, '-200') + }), + ).to.not.changeEtherBalance(sender, '-200'), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did` + `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did`, ); }); @@ -258,24 +258,24 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.not.changeEtherBalance(sender, '-200') + }), + ).to.not.changeEtherBalance(sender, '-200'), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did` + `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did`, ); }); it('Should pass when given zero value tx', async () => { await expect(() => - sender.sendTransaction({ to: receiver.address, value: 0 }) + sender.sendTransaction({ to: receiver.address, value: 0 }), ).to.changeEtherBalance(sender, 0); }); it('Should pass when given zero value tx - zkSync transfer', async () => { await expect(() => sender.transfer({ to: receiver.address, amount: 0 })).to.changeEtherBalance( sender, - 0 + 0, ); }); @@ -286,12 +286,10 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.changeEtherBalance(sender, -200); - const receiverBalanceChange = (await provider.getBalance(receiver.address))-( - receiverBalanceBefore - ); + const receiverBalanceChange = (await provider.getBalance(receiver.address)) - receiverBalanceBefore; expect(receiverBalanceChange).to.equal(200); }); @@ -303,12 +301,10 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.changeEtherBalance(sender, -200); - const receiverBalanceChange = (await provider.getBalance(receiver.address))-( - receiverBalanceBefore - ); + const receiverBalanceChange = (await provider.getBalance(receiver.address)) - receiverBalanceBefore; expect(receiverBalanceChange).to.equal(200); }); @@ -320,16 +316,16 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: await contract.getAddress(), value: 200, - }) + }), ).to.changeEtherBalance(contract, 200); }); it('Should pass when expected balance change is passed as int and is equal to an actual - zkSync transfer', async () => { await expect(async () => sender.transfer({ - to:await contract.getAddress(), + to: await contract.getAddress(), amount: 200, - }) + }), ).to.changeEtherBalance(contract, 200); }); @@ -346,9 +342,9 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - overrides, - }) - ).to.changeEtherBalance(sender, '-200', { overrides }); + overrides: _overrides, + }), + ).to.changeEtherBalance(sender, '-200', { overrides: _overrides }); }); it('Should pass when expected balance change is passed as int and is equal to an actual', async () => { @@ -356,9 +352,9 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - ...overrides, - }) - ).to.changeEtherBalance(receiver, 200, { overrides }); + ..._overrides, + }), + ).to.changeEtherBalance(receiver, 200, { overrides: _overrides }); }); it('Should pass when expected balance change is passed as int and is equal to an actual - zkSync transfer', async () => { @@ -366,9 +362,9 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - overrides, - }) - ).to.changeEtherBalance(receiver, 200, { overrides }); + overrides: _overrides, + }), + ).to.changeEtherBalance(receiver, 200, { overrides: _overrides }); }); it("Should ignore fee if receiver's wallet is being checked and includeFee was set", async () => { @@ -376,13 +372,13 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - ...overrides, - }) + ..._overrides, + }), ).to.changeEtherBalance(receiver, 200, { balanceChangeOptions: { includeFee: true, }, - overrides, + overrides: _overrides, }); }); @@ -391,13 +387,13 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - overrides, - }) + overrides: _overrides, + }), ).to.changeEtherBalance(receiver, 200, { balanceChangeOptions: { includeFee: true, }, - overrides, + overrides: _overrides, }); }); @@ -406,9 +402,9 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - ...overrides, - }) - ).to.changeEtherBalance(sender, -200, { overrides }); + ..._overrides, + }), + ).to.changeEtherBalance(sender, -200, { overrides: _overrides }); }); it('Should take into account transaction fee by default - zkSync transfer', async () => { @@ -416,9 +412,9 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - overrides, - }) - ).to.changeEtherBalance(sender, -200, { overrides }); + overrides: _overrides, + }), + ).to.changeEtherBalance(sender, -200, { overrides: _overrides }); }); it('Should pass when expected balance change is passed as BN and is equal to an actual', async () => { @@ -426,8 +422,8 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) - ).to.changeEtherBalance(receiver, 200, { overrides }); + }), + ).to.changeEtherBalance(receiver, 200, { overrides: _overrides }); }); it('Should pass when expected balance change is passed as BN and is equal to an actual - zkSync transfer', async () => { @@ -435,8 +431,8 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.changeEtherBalance(receiver, 200, { overrides }); + }), + ).to.changeEtherBalance(receiver, 200, { overrides: _overrides }); }); it('Should pass on negative case when expected balance change is not equal to an actual', async () => { @@ -444,9 +440,9 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - ...overrides, - }) - ).to.not.changeEtherBalance(receiver, 300, { overrides }); + ..._overrides, + }), + ).to.not.changeEtherBalance(receiver, 300, { overrides: _overrides }); }); it('Should pass on negative case when expected balance change is not equal to an actual - zkSync transfer', async () => { @@ -454,9 +450,9 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - overrides, - }) - ).to.not.changeEtherBalance(receiver,300, { overrides }); + overrides: _overrides, + }), + ).to.not.changeEtherBalance(receiver, 300, { overrides: _overrides }); }); it('Should throw when expected balance change value was different from an actual', async () => { @@ -465,12 +461,12 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - ...overrides, - }) - ).to.changeEtherBalance(sender, '-500', { overrides }) + ..._overrides, + }), + ).to.changeEtherBalance(sender, '-500', { overrides: _overrides }), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei` + `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei`, ); }); @@ -480,12 +476,12 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - overrides, - }) - ).to.changeEtherBalance(sender, '-500', { overrides }) + overrides: _overrides, + }), + ).to.changeEtherBalance(sender, '-500', { overrides: _overrides }), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei` + `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei`, ); }); @@ -495,12 +491,12 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - ...overrides, - }) - ).to.not.changeEtherBalance(sender, '-200', { overrides }) + ..._overrides, + }), + ).to.not.changeEtherBalance(sender, '-200', { overrides: _overrides }), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did` + `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did`, ); }); @@ -510,12 +506,12 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - overrides, - }) - ).to.not.changeEtherBalance(sender, '-200', { overrides }) + overrides: _overrides, + }), + ).to.not.changeEtherBalance(sender, '-200', { overrides: _overrides }), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did` + `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did`, ); }); }); @@ -526,9 +522,9 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: await contract.getAddress(), value: 200, - ...overrides, - }) - ).to.changeEtherBalance(contract, 200, { overrides }); + ..._overrides, + }), + ).to.changeEtherBalance(contract, 200, { overrides: _overrides }); }); it('Should pass when expected balance change is passed as int and is equal to an actual - zkSync transfer', async () => { @@ -536,9 +532,9 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: await contract.getAddress(), amount: 200, - overrides, - }) - ).to.changeEtherBalance(contract, 200, { overrides }); + overrides: _overrides, + }), + ).to.changeEtherBalance(contract, 200, { overrides: _overrides }); }); // it('should pass when calling function that returns half the sent ether', async () => { @@ -558,11 +554,11 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - ...overrides, - }) - ).to.changeEtherBalance(sender, -200, { overrides }); + ..._overrides, + }), + ).to.changeEtherBalance(sender, -200, { overrides: _overrides }); - const receiverBalanceChange = (await provider.getBalance(receiver.address))-(receiverBalanceBefore); + const receiverBalanceChange = (await provider.getBalance(receiver.address)) - receiverBalanceBefore; expect(receiverBalanceChange).to.equal(200); }); @@ -574,11 +570,11 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - overrides, - }) - ).to.changeEtherBalance(sender, -200, { overrides }); + overrides: _overrides, + }), + ).to.changeEtherBalance(sender, -200, { overrides: _overrides }); - const receiverBalanceChange = (await provider.getBalance(receiver.address))-(receiverBalanceBefore); + const receiverBalanceChange = (await provider.getBalance(receiver.address)) - receiverBalanceBefore; expect(receiverBalanceChange).to.equal(200); }); @@ -591,7 +587,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.changeEtherBalance(sender, '-200'); }); @@ -600,7 +596,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.changeEtherBalance(sender, '-200'); }); @@ -609,7 +605,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.changeEtherBalance(receiver, 200); }); @@ -618,7 +614,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.changeEtherBalance(receiver, 200); }); @@ -627,7 +623,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.changeEtherBalance(sender, -200); }); @@ -636,8 +632,8 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.changeEtherBalance(sender,-200); + }), + ).to.changeEtherBalance(sender, -200); }); it('Should pass on negative case when expected balance change is not equal to an actual', async () => { @@ -645,7 +641,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.not.changeEtherBalance(receiver, 300); }); @@ -654,8 +650,8 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.not.changeEtherBalance(receiver,300); + }), + ).to.not.changeEtherBalance(receiver, 300); }); it('Should throw when expected balance change value was different from an actual', async () => { @@ -664,11 +660,11 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await sender.sendTransaction({ to: receiver.address, value: 200, - }) - ).to.changeEtherBalance(sender, '-500') + }), + ).to.changeEtherBalance(sender, '-500'), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei` + `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei`, ); }); @@ -678,11 +674,11 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.changeEtherBalance(sender, '-500') + }), + ).to.changeEtherBalance(sender, '-500'), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei` + `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei`, ); }); @@ -692,11 +688,11 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await sender.sendTransaction({ to: receiver.address, value: 200, - }) - ).to.not.changeEtherBalance(sender, '-200') + }), + ).to.not.changeEtherBalance(sender, '-200'), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did` + `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did`, ); }); @@ -706,11 +702,11 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.not.changeEtherBalance(sender, '-200') + }), + ).to.not.changeEtherBalance(sender, '-200'), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did` + `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did`, ); }); }); @@ -721,7 +717,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await sender.sendTransaction({ to: await contract.getAddress(), value: 200, - }) + }), ).to.changeEtherBalance(contract, 200); }); @@ -730,7 +726,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { await sender.transfer({ to: await contract.getAddress(), amount: 200, - }) + }), ).to.changeEtherBalance(contract, 200); }); }); @@ -743,7 +739,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.changeEtherBalance(sender, '-200'); }); @@ -752,7 +748,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.changeEtherBalance(sender, '-200'); }); @@ -761,7 +757,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.changeEtherBalance(receiver, 200); }); @@ -770,7 +766,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.changeEtherBalance(receiver, 200); }); @@ -779,7 +775,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.changeEtherBalance(sender, -200); }); @@ -788,7 +784,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.changeEtherBalance(sender, -200); }); @@ -797,7 +793,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.not.changeEtherBalance(receiver, 300); }); @@ -806,8 +802,8 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.not.changeEtherBalance(receiver,300); + }), + ).to.not.changeEtherBalance(receiver, 300); }); it('Should throw when expected balance change value was different from an actual', async () => { @@ -816,11 +812,11 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) - ).to.changeEtherBalance(sender, '-500') + }), + ).to.changeEtherBalance(sender, '-500'), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei` + `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei`, ); }); @@ -830,11 +826,11 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.changeEtherBalance(sender, '-500') + }), + ).to.changeEtherBalance(sender, '-500'), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei` + `Expected the ether balance of "${sender.address}" to change by -500 wei, but it changed by -200 wei`, ); }); @@ -844,11 +840,11 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) - ).to.not.changeEtherBalance(sender, '-200') + }), + ).to.not.changeEtherBalance(sender, '-200'), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did` + `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did`, ); }); @@ -858,11 +854,11 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.not.changeEtherBalance(sender, '-200') + }), + ).to.not.changeEtherBalance(sender, '-200'), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did` + `Expected the ether balance of "${sender.address}" NOT to change by -200 wei, but it did`, ); }); }); @@ -875,7 +871,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.changeEtherBalance(sender, -100); } catch (e: any) { expect(util.inspect(e)).to.include(path.join('test', 'changeEtherBalance.ts')); @@ -892,7 +888,7 @@ describe('INTEGRATION: changeEtherBalance matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.changeEtherBalance(sender, -100); } catch (e: any) { expect(util.inspect(e)).to.include(path.join('test', 'changeEtherBalance.ts')); diff --git a/packages/hardhat-zksync-chai-matchers/test/changeEtherBalances.ts b/packages/hardhat-zksync-chai-matchers/test/changeEtherBalances.ts index 5a2bd2cee..23c0bda27 100644 --- a/packages/hardhat-zksync-chai-matchers/test/changeEtherBalances.ts +++ b/packages/hardhat-zksync-chai-matchers/test/changeEtherBalances.ts @@ -6,10 +6,10 @@ import util from 'util'; import { Deployer } from '@matterlabs/hardhat-zksync-deploy/src/deployer'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; -import { useEnvironmentWithLocalSetup } from './helpers'; import '../src/internal/add-chai-matchers'; import { HttpNetworkConfig } from 'hardhat/types'; import { HDNodeWallet } from 'ethers'; +import { useEnvironmentWithLocalSetup } from './helpers'; const RICH_WALLET_PK = '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110'; @@ -29,11 +29,11 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { let contract: zk.Contract; let gasPrice: number; let gasUsed: number; - let txGasFees: number; - let overrides: {}; + let _txGasFees: number; + let _overrides: {}; beforeEach(async function () { - const hre = await import("hardhat"); + const hre = await import('hardhat'); provider = new zk.Provider((hre.network.config as HttpNetworkConfig).url); sender = new zk.Wallet(RICH_WALLET_PK, provider); receiver = zk.Wallet.createRandom(); @@ -44,9 +44,9 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { gasPrice = 250000000; gasUsed = 157263; - txGasFees = gasPrice * gasUsed; + _txGasFees = gasPrice * gasUsed; - overrides = { + _overrides = { type: 2, maxFeePerGas: 1 * gasPrice, maxPriorityFeePerGas: 1 * gasPrice, @@ -56,20 +56,20 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { describe('Transaction Callback', () => { describe('Change balances, one account, one contract', () => { it('Should pass when all expected balance changes are equal to actual values', async () => { - await expect(async() => + await expect(async () => sender.sendTransaction({ to: await contract.getAddress(), value: 200, - }) + }), ).to.changeEtherBalances([sender, contract], [-200, 200]); }); it('Should pass when all expected balance changes are equal to actual values - zkSync transfer', async () => { - await expect(async() => + await expect(async () => sender.transfer({ - to:await contract.getAddress(), + to: await contract.getAddress(), amount: 200, - }) + }), ).to.changeEtherBalances([sender, contract], [-200, 200]); }); }); @@ -90,7 +90,7 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { to: receiver.address, value: 200, gasPrice, - }) + }), ).to.changeEtherBalances([sender, receiver], ['-200', 200]); }); @@ -102,7 +102,7 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { overrides: { gasPrice, }, - }) + }), ).to.changeEtherBalances([sender, receiver], ['-200', 200]); }); @@ -112,7 +112,7 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { to: receiver.address, value: 200, gasPrice, - }) + }), ).to.changeEtherBalances([sender.address, receiver.address], ['-200', 200]); }); @@ -124,7 +124,7 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { overrides: { gasPrice, }, - }) + }), ).to.changeEtherBalances([sender.address, receiver.address], ['-200', 200]); }); @@ -134,7 +134,7 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { to: receiver.address, value: 200, gasPrice, - }) + }), ).to.changeEtherBalances([sender, receiver], [-200, 200]); }); @@ -146,7 +146,7 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { overrides: { gasPrice, }, - }) + }), ).to.changeEtherBalances([sender, receiver], [-200, 200]); }); @@ -156,8 +156,8 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { to: receiver.address, value: 200, gasPrice, - }) - ).to.changeEtherBalances([sender, receiver], [-200,200]); + }), + ).to.changeEtherBalances([sender, receiver], [-200, 200]); }); it('Should pass when given ethers BigNumber - zkSync transfer', async () => { @@ -168,20 +168,20 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { overrides: { gasPrice, }, - }) + }), ).to.changeEtherBalances([sender, receiver], [-200, 200]); }); it('Should pass when given a single address', async () => { await expect(() => - sender.sendTransaction({ to: receiver.address, value: 200 }) + sender.sendTransaction({ to: receiver.address, value: 200 }), ).to.changeEtherBalances([sender], [-200]); }); it('Should pass when given a single address - zkSync transfer', async () => { await expect(() => sender.transfer({ to: receiver.address, amount: 200 })).to.changeEtherBalances( [sender], - [-200] + [-200], ); }); @@ -191,13 +191,13 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { to: receiver.address, value: 200, gasPrice, - }) - ).to.not.changeEtherBalances([sender, receiver], [-(txGasFees + 201), 200]); + }), + ).to.not.changeEtherBalances([sender, receiver], [-(_txGasFees + 201), 200]); await expect(() => sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.not.changeEtherBalances([sender, receiver], [-200, 201], { balanceChangeOptions: { includeFee: true, @@ -213,13 +213,13 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { overrides: { gasPrice, }, - }) - ).to.not.changeEtherBalances([sender, receiver], [-(txGasFees + 201), 200]); + }), + ).to.not.changeEtherBalances([sender, receiver], [-(_txGasFees + 201), 200]); await expect(() => sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.not.changeEtherBalances([sender, receiver], [-200, 201], { balanceChangeOptions: { includeFee: true, @@ -234,11 +234,11 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { to: receiver.address, value: 200, gasPrice, - }) - ).to.changeEtherBalances([sender, receiver], [-200, 201]) + }), + ).to.changeEtherBalances([sender, receiver], [-200, 201]), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of ${receiver.address} (the 2nd address in the list) to change by 201 wei, but it changed by 200 wei` + `Expected the ether balance of ${receiver.address} (the 2nd address in the list) to change by 201 wei, but it changed by 200 wei`, ); await expect( expect(() => @@ -246,11 +246,11 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { to: receiver.address, value: 200, gasPrice, - }) - ).to.changeEtherBalances([sender, receiver], [-201, 200]) + }), + ).to.changeEtherBalances([sender, receiver], [-201, 200]), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of ${sender.address} (the 1st address in the list) to change by -201 wei, but it changed by -200 wei` + `Expected the ether balance of ${sender.address} (the 1st address in the list) to change by -201 wei, but it changed by -200 wei`, ); }); @@ -263,11 +263,11 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { overrides: { gasPrice, }, - }) - ).to.changeEtherBalances([sender, receiver], [-200, 201]) + }), + ).to.changeEtherBalances([sender, receiver], [-200, 201]), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of ${receiver.address} (the 2nd address in the list) to change by 201 wei, but it changed by 200 wei` + `Expected the ether balance of ${receiver.address} (the 2nd address in the list) to change by 201 wei, but it changed by 200 wei`, ); await expect( expect(() => @@ -277,11 +277,11 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { overrides: { gasPrice, }, - }) - ).to.changeEtherBalances([sender, receiver], [-201, 200]) + }), + ).to.changeEtherBalances([sender, receiver], [-201, 200]), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of ${sender.address} (the 1st address in the list) to change by -201 wei, but it changed by -200 wei` + `Expected the ether balance of ${sender.address} (the 1st address in the list) to change by -201 wei, but it changed by -200 wei`, ); }); @@ -292,11 +292,11 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { to: receiver.address, value: 200, gasPrice, - }) - ).to.not.changeEtherBalances([sender, receiver], [-200, 200]) + }), + ).to.not.changeEtherBalances([sender, receiver], [-200, 200]), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of ${sender.address} (the 1st address in the list) NOT to change by -200 wei` + `Expected the ether balance of ${sender.address} (the 1st address in the list) NOT to change by -200 wei`, ); }); @@ -309,11 +309,11 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { overrides: { gasPrice, }, - }) - ).to.not.changeEtherBalances([sender, receiver], [-200, 200]) + }), + ).to.not.changeEtherBalances([sender, receiver], [-200, 200]), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of ${sender.address} (the 1st address in the list) NOT to change by -200 wei` + `Expected the ether balance of ${sender.address} (the 1st address in the list) NOT to change by -200 wei`, ); }); }); @@ -326,10 +326,10 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { to: receiver.address, value: 200, gasPrice, - }) + }), ).to.changeEtherBalances([sender, receiver], [-200, 200]); - const receiverBalanceChange = (await provider.getBalance(receiver.address))-(receiverBalanceBefore); + const receiverBalanceChange = (await provider.getBalance(receiver.address)) - receiverBalanceBefore; expect(receiverBalanceChange).to.equal(200); }); @@ -344,10 +344,10 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { overrides: { gasPrice, }, - }) + }), ).to.changeEtherBalances([sender, receiver], [-200, 200]); - const receiverBalanceChange = (await provider.getBalance(receiver.address))-(receiverBalanceBefore); + const receiverBalanceChange = (await provider.getBalance(receiver.address)) - receiverBalanceBefore; expect(receiverBalanceChange).to.equal(200); }); @@ -360,7 +360,7 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { await sender.sendTransaction({ to: await contract.getAddress(), value: 200, - }) + }), ).to.changeEtherBalances([sender, contract], [-200, 200]); }); @@ -369,7 +369,7 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { await sender.transfer({ to: await contract.getAddress(), amount: 200, - }) + }), ).to.changeEtherBalances([sender, contract], [-200, 200]); }); }); @@ -380,14 +380,14 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { await sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.not.changeEtherBalances([sender, receiver], [-201, 200]); await expect( await sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.not.changeEtherBalances([sender, receiver], [-200, 201]); }); @@ -396,14 +396,14 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { await sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.not.changeEtherBalances([sender, receiver], [-201, 200]); await expect( await sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.not.changeEtherBalances([sender, receiver], [-200, 201]); }); @@ -413,11 +413,11 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { await sender.sendTransaction({ to: receiver.address, value: 200, - }) - ).to.changeEtherBalances([sender, receiver], [-200, 201]) + }), + ).to.changeEtherBalances([sender, receiver], [-200, 201]), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of ${receiver.address} (the 2nd address in the list) to change by 201 wei, but it changed by 200 wei` + `Expected the ether balance of ${receiver.address} (the 2nd address in the list) to change by 201 wei, but it changed by 200 wei`, ); await expect( @@ -425,11 +425,11 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { await sender.sendTransaction({ to: receiver.address, value: 200, - }) - ).to.changeEtherBalances([sender, receiver], [-201, 200]) + }), + ).to.changeEtherBalances([sender, receiver], [-201, 200]), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of ${sender.address} (the 1st address in the list) to change by -201 wei, but it changed by -200 wei` + `Expected the ether balance of ${sender.address} (the 1st address in the list) to change by -201 wei, but it changed by -200 wei`, ); }); @@ -439,11 +439,11 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { await sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.changeEtherBalances([sender, receiver], [-200, 201]) + }), + ).to.changeEtherBalances([sender, receiver], [-200, 201]), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of ${receiver.address} (the 2nd address in the list) to change by 201 wei, but it changed by 200 wei` + `Expected the ether balance of ${receiver.address} (the 2nd address in the list) to change by 201 wei, but it changed by 200 wei`, ); await expect( @@ -451,11 +451,11 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { await sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.changeEtherBalances([sender, receiver], [-201, 200]) + }), + ).to.changeEtherBalances([sender, receiver], [-201, 200]), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of ${sender.address} (the 1st address in the list) to change by -201 wei, but it changed by -200 wei` + `Expected the ether balance of ${sender.address} (the 1st address in the list) to change by -201 wei, but it changed by -200 wei`, ); }); @@ -465,11 +465,11 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { await sender.sendTransaction({ to: receiver.address, value: 200, - }) - ).to.not.changeEtherBalances([sender, receiver], [-200, 200]) + }), + ).to.not.changeEtherBalances([sender, receiver], [-200, 200]), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of ${sender.address} (the 1st address in the list) NOT to change by -200` + `Expected the ether balance of ${sender.address} (the 1st address in the list) NOT to change by -200`, ); }); @@ -479,11 +479,11 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { await sender.transfer({ to: receiver.address, amount: 200, - }) - ).to.not.changeEtherBalances([sender, receiver], [-200, 200]) + }), + ).to.not.changeEtherBalances([sender, receiver], [-200, 200]), ).to.be.eventually.rejectedWith( AssertionError, - `Expected the ether balance of ${sender.address} (the 1st address in the list) NOT to change by -200` + `Expected the ether balance of ${sender.address} (the 1st address in the list) NOT to change by -200`, ); }); }); @@ -496,7 +496,7 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { sender.sendTransaction({ to: receiver.address, value: 200, - }) + }), ).to.changeEtherBalances([sender, receiver], [-100, 100]); } catch (e: any) { expect(util.inspect(e)).to.include(path.join('test', 'changeEtherBalances.ts')); @@ -513,7 +513,7 @@ describe('INTEGRATION: changeEtherBalances matcher', function () { sender.transfer({ to: receiver.address, amount: 200, - }) + }), ).to.changeEtherBalances([sender, receiver], [-100, 100]); } catch (e: any) { expect(util.inspect(e)).to.include(path.join('test', 'changeEtherBalances.ts')); diff --git a/packages/hardhat-zksync-chai-matchers/test/changeTokenBalance.ts b/packages/hardhat-zksync-chai-matchers/test/changeTokenBalance.ts index 5f934713d..fb83ded67 100644 --- a/packages/hardhat-zksync-chai-matchers/test/changeTokenBalance.ts +++ b/packages/hardhat-zksync-chai-matchers/test/changeTokenBalance.ts @@ -7,11 +7,11 @@ import util from 'util'; import { Deployer } from '@matterlabs/hardhat-zksync-deploy/src/deployer'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; -import { Token, clearTokenDescriptionsCache } from '../src/internal/changeTokenBalance'; -import { useEnvironmentWithLocalSetup } from './helpers'; -import '../src/internal/add-chai-matchers'; import { HttpNetworkConfig } from 'hardhat/types'; import { HDNodeWallet } from 'ethers'; +import { clearTokenDescriptionsCache } from '../src/internal/changeTokenBalance'; +import { useEnvironmentWithLocalSetup } from './helpers'; +import '../src/internal/add-chai-matchers'; const RICH_WALLET_PK = '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110'; @@ -35,7 +35,7 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun let mockToken: zk.Contract; beforeEach(async function () { - const hre = await import("hardhat"); + const hre = await import('hardhat'); provider = new zk.Provider((hre.network.config as HttpNetworkConfig).url); sender = new zk.Wallet(RICH_WALLET_PK, provider); receiver = zk.Wallet.createRandom(); @@ -51,7 +51,7 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun sender.sendTransaction({ to: receiver.address }), mockToken, [sender, receiver], - [0, 0] + [0, 0], ); }); @@ -62,7 +62,7 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun }), mockToken, [sender, receiver], - [0, 0] + [0, 0], ); }); @@ -71,7 +71,7 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun () => sender.sendTransaction({ to: receiver.address }), mockToken, [sender, receiver], - [0, 0] + [0, 0], ); }); @@ -86,19 +86,19 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun await expect(sender.sendTransaction({ to: receiver.address })).to.changeTokenBalance( mockToken, sender.address, - 0 + 0, ); await expect(() => sender.sendTransaction({ to: receiver.address })).to.changeTokenBalances( mockToken, [sender.address, receiver.address], - [0, 0] + [0, 0], ); await expect(() => sender.sendTransaction({ to: receiver.address })).to.changeTokenBalances( mockToken, [sender.address, receiver], - [0, 0] + [0, 0], ); }); @@ -106,25 +106,25 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun await expect(sender.sendTransaction({ to: receiver.address })).to.not.changeTokenBalance( mockToken, sender, - 1 + 1, ); await expect(() => sender.sendTransaction({ to: receiver.address })).to.not.changeTokenBalances( mockToken, [sender, receiver], - [0, 1] + [0, 1], ); await expect(() => sender.sendTransaction({ to: receiver.address })).to.not.changeTokenBalances( mockToken, [sender, receiver], - [1, 0] + [1, 0], ); await expect(() => sender.sendTransaction({ to: receiver.address })).to.not.changeTokenBalances( mockToken, [sender, receiver], - [1, 1] + [1, 1], ); }); @@ -134,22 +134,22 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun expect(sender.sendTransaction({ to: receiver.address })).to.changeTokenBalance( mockToken, sender, - 1 - ) + 1, + ), ).to.be.rejectedWith( AssertionError, - /Expected the balance of MCK tokens for "0x\w{40}" to change by 1, but it changed by 0/ + /Expected the balance of MCK tokens for "0x\w{40}" to change by 1, but it changed by 0/, ); }); it("doesn't change balance as expected - zkSync transfer", async function () { await expect( expect( - sender.transfer({ to: receiver.address, amount: 2, token: await mockToken.getAddress() }) - ).to.changeTokenBalance(mockToken, sender, 1) + sender.transfer({ to: receiver.address, amount: 2, token: await mockToken.getAddress() }), + ).to.changeTokenBalance(mockToken, sender, 1), ).to.be.rejectedWith( AssertionError, - /Expected the balance of MCK tokens for "0x\w{40}" to change by 1, but it changed by -2/ + /Expected the balance of MCK tokens for "0x\w{40}" to change by 1, but it changed by -2/, ); }); @@ -158,22 +158,22 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun expect(sender.sendTransaction({ to: receiver.address })).to.not.changeTokenBalance( mockToken, sender, - 0 - ) + 0, + ), ).to.be.rejectedWith( AssertionError, - /Expected the balance of MCK tokens for "0x\w{40}" NOT to change by 0, but it did/ + /Expected the balance of MCK tokens for "0x\w{40}" NOT to change by 0, but it did/, ); }); it('changes balance in the way it was not expected - zkSync transfer', async function () { await expect( expect( - sender.transfer({ to: receiver.address, amount: 1, token: await mockToken.getAddress()}) - ).to.not.changeTokenBalance(mockToken, sender, -1) + sender.transfer({ to: receiver.address, amount: 1, token: await mockToken.getAddress() }), + ).to.not.changeTokenBalance(mockToken, sender, -1), ).to.be.rejectedWith( AssertionError, - /Expected the balance of MCK tokens for "0x\w{40}" NOT to change by -1, but it did/ + /Expected the balance of MCK tokens for "0x\w{40}" NOT to change by -1, but it did/, ); }); @@ -182,16 +182,16 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun expect(sender.sendTransaction({ to: receiver.address })).to.changeTokenBalances( mockToken, [sender, receiver], - [1, 0] - ) + [1, 0], + ), ).to.be.rejectedWith(AssertionError); }); it("the first account doesn't change its balance as expected - zkSync transfer", async function () { await expect( expect( - sender.transfer({ to: receiver.address, amount: 2, token: await mockToken.getAddress() }) - ).to.changeTokenBalances(mockToken, [sender, receiver], [1, 2]) + sender.transfer({ to: receiver.address, amount: 2, token: await mockToken.getAddress() }), + ).to.changeTokenBalances(mockToken, [sender, receiver], [1, 2]), ).to.be.rejectedWith(AssertionError); }); @@ -200,16 +200,16 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun expect(sender.sendTransaction({ to: receiver.address })).to.changeTokenBalances( mockToken, [sender, receiver], - [0, 1] - ) + [0, 1], + ), ).to.be.rejectedWith(AssertionError); }); it("the second account doesn't change its balance as expected - zkSync transfer", async function () { await expect( expect( - sender.transfer({ to: receiver.address, amount: 2, token: await mockToken.getAddress() }) - ).to.changeTokenBalances(mockToken, [sender, receiver], [-2, 1]) + sender.transfer({ to: receiver.address, amount: 2, token: await mockToken.getAddress() }), + ).to.changeTokenBalances(mockToken, [sender, receiver], [-2, 1]), ).to.be.rejectedWith(AssertionError); }); @@ -218,16 +218,16 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun expect(sender.sendTransaction({ to: receiver.address })).to.changeTokenBalances( mockToken, [sender, receiver], - [1, 1] - ) + [1, 1], + ), ).to.be.rejectedWith(AssertionError); }); it('neither account changes its balance as expected - zkSync transfer', async function () { await expect( expect( - sender.transfer({ to: receiver.address, amount: 2, token:await mockToken.getAddress()}) - ).to.changeTokenBalances(mockToken, [sender, receiver], [1, 1]) + sender.transfer({ to: receiver.address, amount: 2, token: await mockToken.getAddress() }), + ).to.changeTokenBalances(mockToken, [sender, receiver], [1, 1]), ).to.be.rejectedWith(AssertionError); }); @@ -236,16 +236,16 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun expect(sender.sendTransaction({ to: receiver.address })).to.not.changeTokenBalances( mockToken, [sender, receiver], - [0, 0] - ) + [0, 0], + ), ).to.be.rejectedWith(AssertionError); }); it('accounts change their balance in the way it was not expected - zkSync transfer', async function () { await expect( expect( - sender.transfer({ to: receiver.address, amount: 2, token: await mockToken.getAddress()}) - ).to.not.changeTokenBalances(mockToken, [sender, receiver], [-2, 2]) + sender.transfer({ to: receiver.address, amount: 2, token: await mockToken.getAddress() }), + ).to.not.changeTokenBalances(mockToken, [sender, receiver], [-2, 2]), ).to.be.rejectedWith(AssertionError); }); }); @@ -259,7 +259,7 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun mockToken.transfer(receiver.address, 100), mockToken, [sender, receiver], - [-100, 100] + [-100, 100], ); }); @@ -268,7 +268,7 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun await mockToken.transfer(receiver.address, 150), mockToken, [sender, receiver], - [-150, 150] + [-150, 150], ); }); @@ -277,7 +277,7 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun () => mockToken.transfer(receiver.address, 200), mockToken, [sender, receiver], - [-200, 200] + [-200, 200], ); }); @@ -292,10 +292,10 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun await expect(() => mockToken.transfer(receiver.address, 50)).to.changeTokenBalance( mockToken, receiver, - 50 + 50, ); - const receiverBalanceChange = (await mockToken.balanceOf(receiver.address)) - (receiverBalanceBefore); + const receiverBalanceChange = (await mockToken.balanceOf(receiver.address)) - receiverBalanceBefore; expect(receiverBalanceChange).to.equal(50); }); @@ -306,10 +306,10 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun await expect(() => mockToken.transfer(receiver.address, 50)).to.changeTokenBalances( mockToken, [sender, receiver], - [-50, 50] + [-50, 50], ); - const receiverBalanceChange = (await mockToken.balanceOf(receiver.address)) - (receiverBalanceBefore); + const receiverBalanceChange = (await mockToken.balanceOf(receiver.address)) - receiverBalanceBefore; expect(receiverBalanceChange).to.equal(50); }); @@ -321,27 +321,31 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun await expect(mockToken.transfer(receiver.address, 50)).to.not.changeTokenBalances( mockToken, [sender, receiver], - [0, 0] + [0, 0], ); await expect(mockToken.transfer(receiver.address, 50)).to.not.changeTokenBalances( mockToken, [sender, receiver], - [-50, 0] + [-50, 0], ); await expect(mockToken.transfer(receiver.address, 50)).to.not.changeTokenBalances( mockToken, [sender, receiver], - [0, 50] + [0, 50], ); }); describe('assertion failures', function () { it("doesn't change balance as expected", async function () { await expect( - expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalance(mockToken, receiver, 500) + expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalance( + mockToken, + receiver, + 500, + ), ).to.be.rejectedWith( AssertionError, - /Expected the balance of MCK tokens for "0x\w{40}" to change by 500, but it changed by 50/ + /Expected the balance of MCK tokens for "0x\w{40}" to change by 500, but it changed by 50/, ); }); @@ -350,11 +354,11 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun expect(mockToken.transfer(receiver.address, 50)).to.not.changeTokenBalance( mockToken, receiver, - 50 - ) + 50, + ), ).to.be.rejectedWith( AssertionError, - /Expected the balance of MCK tokens for "0x\w{40}" NOT to change by 50, but it did/ + /Expected the balance of MCK tokens for "0x\w{40}" NOT to change by 50, but it did/, ); }); @@ -363,8 +367,8 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalances( mockToken, [sender, receiver], - [-100, 50] - ) + [-100, 50], + ), ).to.be.rejectedWith(AssertionError); }); @@ -373,8 +377,8 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalances( mockToken, [sender, receiver], - [-50, 100] - ) + [-50, 100], + ), ).to.be.rejectedWith(AssertionError); }); @@ -383,8 +387,8 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalances( mockToken, [sender, receiver], - [0, 0] - ) + [0, 0], + ), ).to.be.rejectedWith(AssertionError); }); @@ -393,8 +397,8 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun expect(mockToken.transfer(receiver.address, 50)).to.not.changeTokenBalances( mockToken, [sender, receiver], - [-50, 50] - ) + [-50, 50], + ), ).to.be.rejectedWith(AssertionError); }); @@ -406,22 +410,22 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun expect(tokenWithOnlyName.transfer(receiver.address, 50)).to.changeTokenBalance( tokenWithOnlyName, receiver, - 500 - ) + 500, + ), ).to.be.rejectedWith( AssertionError, - /Expected the balance of MockToken tokens for "0x\w{40}" to change by 500, but it changed by 50/ + /Expected the balance of MockToken tokens for "0x\w{40}" to change by 500, but it changed by 50/, ); await expect( expect(tokenWithOnlyName.transfer(receiver.address, 50)).to.not.changeTokenBalance( tokenWithOnlyName, receiver, - 50 - ) + 50, + ), ).to.be.rejectedWith( AssertionError, - /Expected the balance of MockToken tokens for "0x\w{40}" NOT to change by 50, but it did/ + /Expected the balance of MockToken tokens for "0x\w{40}" NOT to change by 50, but it did/, ); }); @@ -433,22 +437,22 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun expect(tokenWithoutNameNorSymbol.transfer(receiver.address, 50)).to.changeTokenBalance( tokenWithoutNameNorSymbol, receiver, - 500 - ) + 500, + ), ).to.be.rejectedWith( AssertionError, - /Expected the balance of tokens for "0x\w{40}" to change by 500, but it changed by 50/ + /Expected the balance of tokens for "0x\w{40}" to change by 500, but it changed by 50/, ); await expect( expect(tokenWithoutNameNorSymbol.transfer(receiver.address, 50)).to.not.changeTokenBalance( tokenWithoutNameNorSymbol, receiver, - 50 - ) + 50, + ), ).to.be.rejectedWith( AssertionError, - /Expected the balance of tokens for "0x\w{40}" NOT to change by 50, but it did/ + /Expected the balance of tokens for "0x\w{40}" NOT to change by 50, but it did/, ); }); }); @@ -456,30 +460,22 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun describe('accepted number types', function () { it('native bigints are accepted', async function () { - await expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalance( - mockToken, - sender, - -50 - ); + await expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalance(mockToken, sender, -50); await expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalances( mockToken, [sender, receiver], - [-50, 50] + [-50, 50], ); }); it("ethers's bignumbers are accepted", async function () { - await expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalance( - mockToken, - sender, - -50 - ); + await expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalance(mockToken, sender, -50); await expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalances( mockToken, [sender, receiver], - [-50,50] + [-50, 50], ); }); @@ -487,13 +483,13 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun await expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalances( mockToken, [sender, receiver], - [-50, 50] + [-50, 50], ); await expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalances( mockToken, [sender, receiver], - [-50, 50] + [-50, 50], ); }); }); @@ -506,7 +502,7 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun await expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalance( mockToken, sender, - -100 + -100, ); } catch (e: any) { hasProperStackTrace = util.inspect(e).includes(path.join('test', 'changeTokenBalance.ts')); @@ -522,7 +518,7 @@ describe('INTEGRATION: changeTokenBalance and changeTokenBalances matchers', fun await expect(mockToken.transfer(receiver.address, 50)).to.changeTokenBalances( mockToken, [sender, receiver], - [-100, 100] + [-100, 100], ); } catch (e: any) { expect(util.inspect(e)).to.include(path.join('test', 'changeTokenBalance.ts')); @@ -556,7 +552,7 @@ async function runAllAsserts( | (() => Promise), token: zk.Contract, accounts: Array, - balances: Array + balances: Array, ) { await expect(expr).to.changeTokenBalances(token, accounts, balances); await expect(expr).to.changeTokenBalances(token, [], []); diff --git a/packages/hardhat-zksync-chai-matchers/test/helpers.ts b/packages/hardhat-zksync-chai-matchers/test/helpers.ts index 603573f18..a0f9db430 100644 --- a/packages/hardhat-zksync-chai-matchers/test/helpers.ts +++ b/packages/hardhat-zksync-chai-matchers/test/helpers.ts @@ -73,13 +73,13 @@ export async function runFailedAsserts({ await expect(failedAssert(matchers[method](...args))).to.be.rejectedWith(AssertionError, failedAssertReason); await expect(failedAssert(matchers[`${method}View`](...args))).to.be.rejectedWith( AssertionError, - failedAssertReason + failedAssertReason, ); // await expect( // failedAssert(matchers.estimateGas[method](...args)) // ).to.be.rejectedWith(AssertionError, failedAssertReason); await expect(failedAssert(matchers[method].staticCall(...args))).to.be.rejectedWith( AssertionError, - failedAssertReason + failedAssertReason, ); } diff --git a/packages/hardhat-zksync-chai-matchers/test/reverted/reverted.ts b/packages/hardhat-zksync-chai-matchers/test/reverted/reverted.ts index a34f279b7..994d8fb11 100644 --- a/packages/hardhat-zksync-chai-matchers/test/reverted/reverted.ts +++ b/packages/hardhat-zksync-chai-matchers/test/reverted/reverted.ts @@ -7,9 +7,9 @@ import * as ethers from 'ethers'; import { Deployer } from '@matterlabs/hardhat-zksync-deploy/src/deployer'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; +import { HttpNetworkConfig } from 'hardhat/types'; import { runSuccessfulAsserts, runFailedAsserts, useEnvironmentWithLocalSetup } from '../helpers'; import '../../src/internal/add-chai-matchers'; -import { HttpNetworkConfig } from 'hardhat/types'; const RICH_WALLET_1_PK = '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110'; const RICH_WALLET_2_PK = '0xac1e735be8536c6534bb4f17f06f6afc73b2b5ba84ac2cfb12f7461b20c0bbe3'; @@ -32,7 +32,7 @@ describe('INTEGRATION: Reverted', function () { let aaDeployer: Deployer; beforeEach('deploy matchers contract', async function () { - const hre = await import("hardhat"); + const hre = await import('hardhat'); provider = new zk.Provider((hre.network.config as HttpNetworkConfig).url); wallet1 = new zk.Wallet(RICH_WALLET_1_PK, provider); wallet2 = new zk.Wallet(RICH_WALLET_2_PK, provider); @@ -42,7 +42,7 @@ describe('INTEGRATION: Reverted', function () { matchers = await deployer.deploy(artifact); aaDeployer = new Deployer(this.hre, wallet1, 'createAccount'); - artifact = await deployer.loadArtifact("TwoUserMultisig"); + artifact = await deployer.loadArtifact('TwoUserMultisig'); aaAccount = await aaDeployer.deploy(artifact, [wallet1.address, wallet2.address], undefined, []); }); @@ -55,24 +55,24 @@ describe('INTEGRATION: Reverted', function () { it('invalid string', async function () { await expect(expect('0x123').to.be.reverted).to.be.rejectedWith( TypeError, - "Expected a valid transaction hash, but got '0x123'" + "Expected a valid transaction hash, but got '0x123'", ); await expect(expect('0x123').to.not.be.reverted).to.be.rejectedWith( TypeError, - "Expected a valid transaction hash, but got '0x123'" + "Expected a valid transaction hash, but got '0x123'", ); }); it('promise of an invalid string', async function () { await expect(expect(Promise.resolve('0x123')).to.be.reverted).to.be.rejectedWith( TypeError, - "Expected a valid transaction hash, but got '0x123'" + "Expected a valid transaction hash, but got '0x123'", ); await expect(expect(Promise.resolve('0x123')).to.not.be.reverted).to.be.rejectedWith( TypeError, - "Expected a valid transaction hash, but got '0x123'" + "Expected a valid transaction hash, but got '0x123'", ); }); }); @@ -183,24 +183,25 @@ describe('INTEGRATION: Reverted', function () { describe('calling abstraction account', function () { it('successfuly reverts', async function () { + await ( + await wallet1.sendTransaction({ to: await aaAccount.getAddress(), value: ethers.parseEther('0.8') }) + ).wait(); - await (await wallet1.sendTransaction({to: await aaAccount.getAddress(), value: ethers.parseEther("0.8")})).wait(); - let aaTx = await matchers.succeeds(); const gasLimit = await provider.estimateGas(aaTx); const gasPrice = await provider.getGasPrice(); - + aaTx = { ...aaTx, from: await aaAccount.getAddress(), - gasLimit: gasLimit, - gasPrice: gasPrice, + gasLimit, + gasPrice, chainId: (await provider.getNetwork()).chainId, nonce: await provider.getTransactionCount(await aaAccount.getAddress()), type: 113, customData: { - gasPerPubdata: zk.utils.DEFAULT_GAS_PER_PUBDATA_LIMIT, + gasPerPubdata: zk.utils.DEFAULT_GAS_PER_PUBDATA_LIMIT, } as zk.types.Eip712Meta, value: 0n, }; diff --git a/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWith.ts b/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWith.ts index a1699c872..e6a8376e7 100644 --- a/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWith.ts +++ b/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWith.ts @@ -6,9 +6,9 @@ import util from 'util'; import { Deployer } from '@matterlabs/hardhat-zksync-deploy/src/deployer'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; +import { HttpNetworkConfig } from 'hardhat/types'; import { runSuccessfulAsserts, runFailedAsserts, useEnvironmentWithLocalSetup } from '../helpers'; import '../../src/internal/add-chai-matchers'; -import { HttpNetworkConfig } from 'hardhat/types'; const RICH_WALLET_PK = '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110'; @@ -27,7 +27,7 @@ describe('INTEGRATION: Reverted with', function () { let artifact: ZkSyncArtifact; beforeEach('deploy matchers contract', async function () { - const hre = await import("hardhat"); + const hre = await import('hardhat'); provider = new zk.Provider((hre.network.config as HttpNetworkConfig).url); wallet = new zk.Wallet(RICH_WALLET_PK, provider); @@ -150,7 +150,7 @@ describe('INTEGRATION: Reverted with', function () { it('non-errors as subject', async function () { await expect(expect(Promise.reject({})).to.be.revertedWith('some reason')).to.be.rejectedWith( AssertionError, - 'Expected an Error object' + 'Expected an Error object', ); }); }); diff --git a/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWithCustomError.ts b/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWithCustomError.ts index 6c781bc93..45c2494a1 100644 --- a/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWithCustomError.ts +++ b/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWithCustomError.ts @@ -7,9 +7,9 @@ import { Deployer } from '@matterlabs/hardhat-zksync-deploy/src/deployer'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; import { anyUint, anyValue } from '@nomicfoundation/hardhat-chai-matchers/internal/withArgs'; +import { HttpNetworkConfig } from 'hardhat/types'; import { runSuccessfulAsserts, runFailedAsserts, useEnvironmentWithLocalSetup } from '../helpers'; import '../../src/internal/add-chai-matchers'; -import { HttpNetworkConfig } from 'hardhat/types'; const RICH_WALLET_PK = '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110'; @@ -28,7 +28,7 @@ describe('INTEGRATION: Reverted with custom error', function () { let artifact: ZkSyncArtifact; beforeEach('deploy matchers contract', async function () { - const hre = await import("hardhat"); + const hre = await import('hardhat'); provider = new zk.Provider((hre.network.config as HttpNetworkConfig).url); wallet = new zk.Wallet(RICH_WALLET_PK, provider); @@ -164,7 +164,7 @@ describe('INTEGRATION: Reverted with custom error', function () { await expect( expect(matchers.revertWithCustomErrorWithUint(1)) .to.be.revertedWithCustomError(matchers, 'CustomErrorWithUint') - .withArgs(2) + .withArgs(2), ).to.be.rejectedWith(AssertionError, 'expected 1 to equal 2'); }); @@ -176,13 +176,13 @@ describe('INTEGRATION: Reverted with custom error', function () { await expect( expect(matchers.revertWithCustomErrorWithUintAndString(1, 'foo')) .to.be.revertedWithCustomError(matchers, 'CustomErrorWithUintAndString') - .withArgs(2, 'foo') + .withArgs(2, 'foo'), ).to.be.rejectedWith(AssertionError, 'expected 1 to equal 2'); await expect( expect(matchers.revertWithCustomErrorWithUintAndString(1, 'foo')) .to.be.revertedWithCustomError(matchers, 'CustomErrorWithUintAndString') - .withArgs(1, 'bar') + .withArgs(1, 'bar'), ).to.be.rejectedWith(AssertionError, "expected 'foo' to equal 'bar'"); await expect( @@ -190,7 +190,7 @@ describe('INTEGRATION: Reverted with custom error', function () { .to.be.revertedWithCustomError(matchers, 'CustomErrorWithUintAndString') .withArgs(() => { throw new Error('user-defined error'); - }, 'foo') + }, 'foo'), ).to.be.rejectedWith(Error, 'user-defined error'); }); @@ -198,13 +198,13 @@ describe('INTEGRATION: Reverted with custom error', function () { await expect( expect(matchers.revertWithCustomErrorWithUintAndString(1, 's')) .to.be.revertedWithCustomError(matchers, 'CustomErrorWithUintAndString') - .withArgs(1) + .withArgs(1), ).to.be.rejectedWith(AssertionError, 'expected 1 args but got 2'); await expect( expect(matchers.revertWithCustomErrorWithUintAndString(1, 's')) .to.be.revertedWithCustomError(matchers, 'CustomErrorWithUintAndString') - .withArgs(1, 's', 3) + .withArgs(1, 's', 3), ).to.be.rejectedWith(AssertionError, 'expected 3 args but got 2'); }); @@ -216,7 +216,7 @@ describe('INTEGRATION: Reverted with custom error', function () { await expect( expect(matchers.revertWithCustomErrorWithPair(1, 2)) .to.be.revertedWithCustomError(matchers, 'CustomErrorWithPair') - .withArgs([3, 2]) + .withArgs([3, 2]), ).to.be.rejectedWith(AssertionError, /expected \[.*\] to deeply equal \[ 3, 2 \]/s); }); @@ -224,7 +224,7 @@ describe('INTEGRATION: Reverted with custom error', function () { expect(() => expect(matchers.revertWithSomeCustomError()) .to.not.be.revertedWithCustomError(matchers, 'SomeCustomError') - .withArgs(1) + .withArgs(1), ).to.throw(Error, 'Do not combine .not. with .withArgs()'); }); @@ -232,10 +232,10 @@ describe('INTEGRATION: Reverted with custom error', function () { expect(() => expect(matchers.revertWithCustomErrorWithUint(1)) // @ts-expect-error - .withArgs(1) + .withArgs(1), ).to.throw( Error, - 'withArgs can only be used in combination with a previous .emit or .revertedWithCustomError assertion' + 'withArgs can only be used in combination with a previous .emit or .revertedWithCustomError assertion', ); }); @@ -244,10 +244,10 @@ describe('INTEGRATION: Reverted with custom error', function () { expect(matchers.revertWithSomeCustomError()) .to.emit(matchers, 'SomeEvent') .and.to.be.revertedWithCustomError(matchers, 'SomeCustomError') - .withArgs(1) + .withArgs(1), ).to.throw( Error, - 'withArgs called with both .emit and .revertedWithCustomError, but these assertions cannot be combined' + 'withArgs called with both .emit and .revertedWithCustomError, but these assertions cannot be combined', ); }); @@ -259,10 +259,10 @@ describe('INTEGRATION: Reverted with custom error', function () { await expect( expect(matchers.revertWithCustomErrorWithUint(1)) .to.be.revertedWithCustomError(matchers, 'CustomErrorWithUint') - .withArgs(() => false) + .withArgs(() => false), ).to.be.rejectedWith( AssertionError, - 'The predicate for custom error argument with index 0 returned false' + 'The predicate for custom error argument with index 0 returned false', ); await expect(matchers.revertWithCustomErrorWithUint(1)) @@ -272,10 +272,10 @@ describe('INTEGRATION: Reverted with custom error', function () { await expect( expect(matchers.revertWithCustomErrorWithInt(-1)) .to.be.revertedWithCustomError(matchers, 'CustomErrorWithInt') - .withArgs(anyUint) + .withArgs(anyUint), ).to.be.rejectedWith( AssertionError, - 'The predicate for custom error argument with index 0 threw an AssertionError: anyUint expected its argument to be an unsigned integer, but it was negative, with value -1' + 'The predicate for custom error argument with index 0 threw an AssertionError: anyUint expected its argument to be an unsigned integer, but it was negative, with value -1', ); }); }); @@ -283,7 +283,7 @@ describe('INTEGRATION: Reverted with custom error', function () { describe('invalid values', function () { it('non-errors as subject', async function () { await expect( - expect(Promise.reject({})).to.be.revertedWithCustomError(matchers, 'SomeCustomError') + expect(Promise.reject({})).to.be.revertedWithCustomError(matchers, 'SomeCustomError'), ).to.be.rejectedWith(AssertionError, 'Expected an Error object'); }); @@ -291,10 +291,10 @@ describe('INTEGRATION: Reverted with custom error', function () { expect(() => expect(matchers.revertWithSomeCustomError()) .to.be // @ts-expect-error - .revertedWithCustomError('SomeCustomError') + .revertedWithCustomError('SomeCustomError'), ).to.throw( TypeError, - 'The first argument of .revertedWithCustomError must be the contract that defines the custom error' + 'The first argument of .revertedWithCustomError must be the contract that defines the custom error', ); }); @@ -302,11 +302,10 @@ describe('INTEGRATION: Reverted with custom error', function () { expect(() => expect(matchers.revertWithSomeCustomError()).to.be.revertedWithCustomError( matchers, - 'SomeCustmError' - ) + 'SomeCustmError', + ), ).to.throw(Error, "The given contract doesn't have a custom error named 'SomeCustmError'"); }); - }); describe('stack traces', function () { @@ -314,7 +313,7 @@ describe('INTEGRATION: Reverted with custom error', function () { try { await expect(matchers.revertedWith('some reason')).to.be.revertedWithCustomError( matchers, - 'SomeCustomError' + 'SomeCustomError', ); } catch (e: any) { expect(util.inspect(e)).to.include(path.join('test', 'reverted', 'revertedWithCustomError.ts')); diff --git a/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWithPanic.ts b/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWithPanic.ts index 8b28275c9..932964653 100644 --- a/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWithPanic.ts +++ b/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWithPanic.ts @@ -1,15 +1,15 @@ import { AssertionError, expect } from 'chai'; import * as zk from 'zksync-ethers'; -import path, { resolve } from 'path'; +import path from 'path'; import util from 'util'; import { Deployer } from '@matterlabs/hardhat-zksync-deploy/src/deployer'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; import { PANIC_CODES } from '@nomicfoundation/hardhat-chai-matchers/internal/reverted/panic'; +import { HttpNetworkConfig } from 'hardhat/types'; import { runSuccessfulAsserts, runFailedAsserts, useEnvironmentWithLocalSetup } from '../helpers'; import '../../src/internal/add-chai-matchers'; -import { HttpNetworkConfig } from 'hardhat/types'; const RICH_WALLET_PK = '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110'; @@ -28,7 +28,7 @@ describe('INTEGRATION: Reverted with panic', function () { let artifact: ZkSyncArtifact; beforeEach('deploy matchers contract', async function () { - const hre = await import("hardhat"); + const hre = await import('hardhat'); provider = new zk.Provider((hre.network.config as HttpNetworkConfig).url); wallet = new zk.Wallet(RICH_WALLET_PK, provider); @@ -39,14 +39,14 @@ describe('INTEGRATION: Reverted with panic', function () { describe('calling a method that succeeds', function () { it('successful asserts', async function () { - //wait some time to update + // wait some time to update await new Promise((resolve) => setTimeout(resolve, 1000)); await runSuccessfulAsserts({ matchers, method: 'succeeds', successfulAssert: (x) => expect(x).not.to.be.revertedWithPanic(), }); - //wait some time to update + // wait some time to update await new Promise((resolve) => setTimeout(resolve, 1000)); await runSuccessfulAsserts({ @@ -57,7 +57,7 @@ describe('INTEGRATION: Reverted with panic', function () { }); it('failed asserts', async function () { - //wait some time to update + // wait some time to update await new Promise((resolve) => setTimeout(resolve, 1000)); await runFailedAsserts({ matchers, @@ -66,7 +66,7 @@ describe('INTEGRATION: Reverted with panic', function () { failedAssertReason: "Expected transaction to be reverted with some panic code, but it didn't revert", }); - //wait some time to update + // wait some time to update await new Promise((resolve) => setTimeout(resolve, 1000)); await runFailedAsserts({ @@ -247,7 +247,7 @@ describe('INTEGRATION: Reverted with panic', function () { it('non-errors as subject', async function () { await expect(expect(Promise.reject({})).to.be.revertedWithPanic(1)).to.be.rejectedWith( AssertionError, - 'Expected an Error object' + 'Expected an Error object', ); }); }); diff --git a/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWithoutReason.ts b/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWithoutReason.ts index aff7d8894..5be2b1951 100644 --- a/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWithoutReason.ts +++ b/packages/hardhat-zksync-chai-matchers/test/reverted/revertedWithoutReason.ts @@ -6,9 +6,9 @@ import util from 'util'; import { Deployer } from '@matterlabs/hardhat-zksync-deploy/src/deployer'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; +import { HttpNetworkConfig } from 'hardhat/types'; import { runSuccessfulAsserts, runFailedAsserts, useEnvironmentWithLocalSetup } from '../helpers'; import '../../src/internal/add-chai-matchers'; -import { HttpNetworkConfig } from 'hardhat/types'; const RICH_WALLET_PK = '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110'; @@ -27,7 +27,7 @@ describe('INTEGRATION: Reverted without reason', function () { let artifact: ZkSyncArtifact; beforeEach('deploy matchers contract', async function () { - const hre = await import("hardhat"); + const hre = await import('hardhat'); provider = new zk.Provider((hre.network.config as HttpNetworkConfig).url); wallet = new zk.Wallet(RICH_WALLET_PK, provider); @@ -122,7 +122,7 @@ describe('INTEGRATION: Reverted without reason', function () { it('non-errors as subject', async function () { await expect(expect(Promise.reject({})).to.be.revertedWithoutReason()).to.be.rejectedWith( AssertionError, - 'Expected an Error object' + 'Expected an Error object', ); }); }); diff --git a/packages/hardhat-zksync-deploy/CHANGELOG.md b/packages/hardhat-zksync-deploy/CHANGELOG.md index c54b71e5f..6f001aca0 100644 --- a/packages/hardhat-zksync-deploy/CHANGELOG.md +++ b/packages/hardhat-zksync-deploy/CHANGELOG.md @@ -1,5 +1,12 @@ # @matterlabs/hardhat-zksync-deploy +## [1.1.2](https://github.com/matter-labs/hardhat-zksync/compare/@matterlabs/hardhat-zksync-deploy@1.1.1...@matterlabs/hardhat-zksync-deploy-v1.1.2) (2023-12-22) + + +### Fixes + +* **docs:** update readme files ([#612](https://github.com/matter-labs/hardhat-zksync/issues/612)) ([682338e](https://github.com/matter-labs/hardhat-zksync/commit/682338e60f52021206325ff6eeec2c394a118642)) + ## 1.1.1 ### Patch Changes diff --git a/packages/hardhat-zksync-deploy/README.md b/packages/hardhat-zksync-deploy/README.md index 3e87f4fc4..bed8a45b5 100644 --- a/packages/hardhat-zksync-deploy/README.md +++ b/packages/hardhat-zksync-deploy/README.md @@ -1,6 +1,169 @@ -# hardhat-zksync-deploy +## hardhat-zksync-deploy 🚀 -[Hardhat](https://hardhat.org/) plugin that adds zkSync-specific capabilities to deploy contracts into the zkSync network. +zkSync Era capabilities for contract deployment are enhanced with this [Hardhat](https://hardhat.org/) plugin, specifically designed to add zkSync-specific features to the network. +![Era Logo](https://github.com/matter-labs/era-contracts/raw/main/eraLogo.svg) -Check out the documentation page for the [zksync deploy plugin](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-deploy.html) for more detailed explanation on how to use hardhat-zksync-deploy. \ No newline at end of file +This plugin provides utilities for deploying smart contracts on zkSync Era with artifacts built by the [@matterlabs/hardhat-zksync-solc](https://www.npmjs.com/package/@matterlabs/hardhat-zksync-solc) or [@matterlabs/hardhat-zksync-vyper](https://www.npmjs.com/package/@matterlabs/hardhat-zksync-vyper) plugins. + +## ⚠️ Version Compatibility Warning + +Ensure you are using the correct version of the plugin with ethers: +- For plugin version **<1.0.0**: + - Compatible with ethers **v5**. + +- For plugin version **≥1.0.0**: + - Compatible with ethers **v6** (⭐ Recommended) + +## 📣 Prerequisite + +- You are already familiar with deploying smart contracts on zkSync Era. If not, please refer to the first section of the [quickstart tutorail](https://era.zksync.io/docs/dev/building-on-zksync/hello-world.html). +- You have a wallet with sufficient Sepolia or Goerli **ETH** on L1 to pay for bridging funds to zkSync as well as deploying smart contracts. Use the third party faucets to get some test tokens in your account. +- You know how to get your [private key from your MetaMask wallet](https://support.metamask.io/hc/en-us/articles/360015289632-How-to-export-an-account-s-private-key). + +## 📥 Installation + +To install **hardhat-zksync-deploy** plugin, run: + +`npm install -D @matterlabs/hardhat-zksync-deploy` + +or + +`yarn add -D @matterlabs/hardhat-zksync-deploy ethers zksync-ethers` + + +## 📤 Exports + +The main export of this plugin is the Deployer class. It is used to wrap a zksync-ethers Wallet instance and provides a convenient interface to deploy smart contracts and account abstractions. +It's main methods are: + +``` + * @param hre Hardhat runtime environment. This object is provided to scripts by hardhat itself. + * @param zkWallet The wallet which will be used to deploy the contracts. + * @param deploymentType Optional deployment type that relates to the ContractDeployer system contract function to be called. Defaults to deploying regular smart contracts. +``` + - `constructor(hre: HardhatRuntimeEnvironment, zkWallet: zk.Wallet, deploymentType?: zk.types.DeploymentType)` + +``` + * Created a `Deployer` object on ethers.Wallet object. + * + * @param hre Hardhat runtime environment. This object is provided to scripts by hardhat itself. + * @param ethWallet The wallet used to deploy smart contracts. + * @param deploymentType The optional deployment type that relates to the `ContractDeployer` system contract function to be called. Defaults to deploying regular smart contracts. +``` + - `static fromEthWallet(hre: HardhatRuntimeEnvironment, ethWallet: ethers.Wallet, deploymentType?: zk.types.DeploymentType)` + +``` + * Loads an artifact and verifies that it was compiled by `zksolc`. + * + * @param contractNameOrFullyQualifiedName The name of the contract. + * It can be a bare contract name (e.g. "Token") if it's + * unique in your project, or a fully qualified contract name + * (e.g. "contract/token.sol:Token") otherwise. + * + * @throws Throws an error if a non-unique contract name is used, + * indicating which fully qualified names can be used instead. + * + * @throws Throws an error if an artifact was not compiled by `zksolc`. +``` + - `public async loadArtifact(contractNameOrFullyQualifiedName: string): Promise` + +``` + * Estimates the price of calling a deploy transaction in a certain fee token. + * + * @param artifact The previously loaded artifact object. + * @param constructorArguments The list of arguments to be passed to the contract constructor. + * + * @returns Calculated fee in ETH wei. + */ +``` + - `public async estimateDeployFee(artifact: ZkSyncArtifact,constructorArguments: any[]): Promise` + +``` + * Sends a deploy transaction to the zkSync network. + * For now it uses defaults values for the transaction parameters: + * + * @param artifact The previously loaded artifact object. + * @param constructorArguments The list of arguments to be passed to the contract constructor. + * @param overrides Optional object with additional deploy transaction parameters. + * @param additionalFactoryDeps Additional contract bytecodes to be added to the factory dependencies list. + * The fee amount is requested automatically from the zkSync Era server. + * + * @returns A contract object. +``` + - `public async deploy(artifact: ZkSyncArtifact,constructorArguments: any[],overrides?: OverridesadditionalFactoryDeps?: ethers.BytesLike[],): Promise` + +``` + * Extracts factory dependencies from the artifact. + * + * @param artifact Artifact to extract dependencies from + * + * @returns Factory dependencies in the format expected by SDK. +``` + - `async extractFactoryDeps(artifact: ZkSyncArtifact): Promise` + +## 📖 Example + +After installing it, add the plugin to your Hardhat config: + +`import "@matterlabs/hardhat-zksync-deploy";` + +Then you'll be able to use the Deployer class in your files. + +Create your script in **deploy** folder, + +Import Deployer like this: + +`import { Deployer } from '@matterlabs/hardhat-zksync-deploy';` + +or + +`const { Deployer } = require('@matterlabs/hardhat-zksync-deploy');` + +Create a deployer instance: + +`const deployer = new Deployer(hre, zkWallet);` + +Note: +- **hre** - hardhat runtime enviroment +- **zkWallet** - instace of Wallet using [zksync-ethers](https://www.npmjs.com/package/zksync-ethers) plugin + +Load your contract artifacts: + +`const artifact = await deployer.loadArtifact('Greeter');` + +Deploy your contract: + +`const myContract = await deployer.deploy(artifact, [...contractArguments]);` + +Check the deployed address: + +`const address = await myContract.getAddress()` + +## 🕹 Commands + +`yarn hardhat deploy-zksync` -- runs through all the scripts in the **deploy** folder.\ +`hardhat deploy-zksync --script script-name.ts` -- run a specific script from **deploy** folder.\ +`yarn hardhat deploy-zksync:libraries --private-key ` -- uns compilation and deployment of missing libraries (the list of all missing libraries is provided by the output of [matterlabs/hardhat-zksync-solc](https://www.npmjs.com/package/@matterlabs/hardhat-zksync-solc) plugin). Read more about how zkSync deals with libraries on this [link](https://era.zksync.io/docs/tools/hardhat/compiling-libraries.html). + +## 📝 Documentation + +In addition to the [hardhat-zksync-deploy](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-deploy.html), zkSync's Era [website](https://era.zksync.io/docs/) offers a variety of resources including: + +[Guides to get started](https://era.zksync.io/docs/dev/building-on-zksync/hello-world.html): Learn how to start building on zkSync Era.\ +[Hardhat zkSync Era plugins](https://era.zksync.io/docs/tools/hardhat/getting-started.html): Overview and guides for all Hardhat zkSync Era plugins.\ +[Hyperscaling](https://era.zksync.io/docs/reference/concepts/hyperscaling.html#what-are-hyperchains): Deep dive into hyperscaling on zkSync Era. + +## 🤝 Contributing + +Contributions are always welcome! Feel free to open any issue or send a pull request. + +Go to [CONTRIBUTING.md](https://github.com/matter-labs/hardhat-zksync/blob/main/.github/CONTRIBUTING.md) to learn about steps and best practices for contributing to zkSync hardhat tooling base repository. + + +## 🙌 Feedback, help and news + +[zkSync Era Discord server](https://join.zksync.dev/): for questions and feedback.\ +[Follow zkSync Era on Twitter](https://twitter.com/zksync) + +## Happy building! 👷‍♀️👷‍♂️ \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/package.json b/packages/hardhat-zksync-deploy/package.json index 274068331..4ffedabdf 100644 --- a/packages/hardhat-zksync-deploy/package.json +++ b/packages/hardhat-zksync-deploy/package.json @@ -1,6 +1,6 @@ { "name": "@matterlabs/hardhat-zksync-deploy", - "version": "1.1.1", + "version": "1.1.2", "description": "Hardhat plugin to deploy smart contracts into the zkSync network", "repository": "github:matter-labs/hardhat-zksync", "homepage": "https://github.com/matter-labs/hardhat-zksync/tree/main/packages/hardhat-zksync-deploy", @@ -35,7 +35,8 @@ "dependencies": { "@matterlabs/hardhat-zksync-solc": "^1.0.4", "chalk": "4.1.2", - "ts-morph": "^21.0.1" + "ts-morph": "^21.0.1", + "chai": "^4.3.6" }, "devDependencies": { "@types/chai": "^4.2.0", @@ -43,14 +44,13 @@ "@types/node": "^18.11.17", "@typescript-eslint/eslint-plugin": "6.13.1", "@typescript-eslint/parser": "6.13.1", - "chai": "^4.3.6", "eslint": "^8.54.0", "eslint-config-prettier": "9.0.0", "eslint-plugin-import": "2.29.0", "eslint-plugin-no-only-tests": "3.1.0", "eslint-plugin-prettier": "5.0.1", "ethers": "^6.7.1", - "hardhat": "^2.19.2", + "hardhat": "^2.19.4", "mocha": "^9.2.1", "prettier": "3.1.0", "rimraf": "^3.0.2", @@ -61,7 +61,7 @@ }, "peerDependencies": { "ethers": "^6.7.1", - "hardhat": "^2.19.2", + "hardhat": "^2.19.4", "zksync-ethers": "^6.0.0" }, "prettier": { diff --git a/packages/hardhat-zksync-deploy/src/deployer.ts b/packages/hardhat-zksync-deploy/src/deployer.ts index 8df2ea80b..b74e3140b 100644 --- a/packages/hardhat-zksync-deploy/src/deployer.ts +++ b/packages/hardhat-zksync-deploy/src/deployer.ts @@ -9,7 +9,7 @@ import { isHttpNetworkConfig, isValidEthNetworkURL } from './utils'; const ZKSOLC_ARTIFACT_FORMAT_VERSION = 'hh-zksolc-artifact-1'; const ZKVYPER_ARTIFACT_FORMAT_VERSION = 'hh-zkvyper-artifact-1'; -const SUPPORTED_L1_TESTNETS = ['mainnet', 'rinkeby', 'ropsten', 'kovan', 'goerli','sepolia']; +const SUPPORTED_L1_TESTNETS = ['mainnet', 'rinkeby', 'ropsten', 'kovan', 'goerli', 'sepolia']; /** * An entity capable of deploying contracts to the zkSync network. @@ -23,27 +23,30 @@ export class Deployer { constructor(hre: HardhatRuntimeEnvironment, zkWallet: zk.Wallet, deploymentType?: zk.types.DeploymentType) { this.hre = hre; this.deploymentType = deploymentType; - let l2Provider; interface Providers { ethWeb3Provider: ethers.Provider; - zkWeb3Provider: zk.Provider; + zkWeb3Provider: zk.Provider; } // Initalize two providers: one for the Ethereum RPC (layer 1), and one for the zkSync RPC (layer 2). - const { ethWeb3Provider, zkWeb3Provider }:Providers = this._createProviders(hre.config.networks, hre.network); + const { ethWeb3Provider, zkWeb3Provider }: Providers = this._createProviders(hre.config.networks, hre.network); - l2Provider = zkWallet.provider === null ? zkWeb3Provider : zkWallet.provider; + const l2Provider = zkWallet.provider === null ? zkWeb3Provider : zkWallet.provider; this.zkWallet = zkWallet.connect(l2Provider).connectToL1(ethWeb3Provider); this.ethWallet = this.zkWallet.ethWallet(); } - static fromEthWallet(hre: HardhatRuntimeEnvironment, ethWallet: ethers.Wallet, deploymentType?: zk.types.DeploymentType) { + public static fromEthWallet( + hre: HardhatRuntimeEnvironment, + ethWallet: ethers.Wallet, + deploymentType?: zk.types.DeploymentType, + ) { return new Deployer(hre, new zk.Wallet(ethWallet.privateKey), deploymentType); } private _createProviders( networks: NetworksConfig, - network: Network + network: Network, ): { ethWeb3Provider: ethers.Provider; zkWeb3Provider: zk.Provider; @@ -52,7 +55,7 @@ export class Deployer { if (!network.zksync) { throw new ZkSyncDeployPluginError( - `Only deploying to zkSync network is supported.\nNetwork '${networkName}' in 'hardhat.config' needs to have 'zksync' flag set to 'true'.` + `Only deploying to zkSync network is supported.\nNetwork '${networkName}' in 'hardhat.config' needs to have 'zksync' flag set to 'true'.`, ); } @@ -67,17 +70,17 @@ export class Deployer { if (!isHttpNetworkConfig(networkConfig)) { throw new ZkSyncDeployPluginError( - `Only deploying to zkSync network is supported.\nNetwork '${networkName}' in 'hardhat.config' needs to have 'url' specified.` + `Only deploying to zkSync network is supported.\nNetwork '${networkName}' in 'hardhat.config' needs to have 'url' specified.`, ); } if (networkConfig.ethNetwork === undefined) { throw new ZkSyncDeployPluginError( - `Only deploying to zkSync network is supported.\nNetwork '${networkName}' in 'hardhat.config' needs to have 'ethNetwork' (layer 1) specified.` + `Only deploying to zkSync network is supported.\nNetwork '${networkName}' in 'hardhat.config' needs to have 'ethNetwork' (layer 1) specified.`, ); } - let ethWeb3Provider, zkWeb3Provider; + let ethWeb3Provider; const ethNetwork = networkConfig.ethNetwork; if (SUPPORTED_L1_TESTNETS.includes(ethNetwork)) { @@ -98,7 +101,7 @@ export class Deployer { } } - zkWeb3Provider = new zk.Provider((network.config as HttpNetworkConfig).url); + const zkWeb3Provider = new zk.Provider((network.config as HttpNetworkConfig).url); return { ethWeb3Provider, zkWeb3Provider }; } @@ -133,7 +136,7 @@ export class Deployer { artifact._format !== ZKVYPER_ARTIFACT_FORMAT_VERSION ) { throw new ZkSyncDeployPluginError( - `Artifact ${contractNameOrFullyQualifiedName} was not compiled by zksolc or zkvyper` + `Artifact ${contractNameOrFullyQualifiedName} was not compiled by zksolc or zkvyper`, ); } return artifact as ZkSyncArtifact; @@ -150,7 +153,7 @@ export class Deployer { public async estimateDeployFee(artifact: ZkSyncArtifact, constructorArguments: any[]): Promise { const gas = await this.estimateDeployGas(artifact, constructorArguments); const gasPrice = await this.zkWallet.provider.getGasPrice(); - return gas*gasPrice; + return gas * gasPrice; } /** @@ -162,7 +165,7 @@ export class Deployer { * @returns Calculated amount of gas. */ public async estimateDeployGas(artifact: ZkSyncArtifact, constructorArguments: any[]): Promise { - const factoryDeps = await this.extractFactoryDeps(artifact); + const factoryDeps = await this._extractFactoryDeps(artifact); const factory = new zk.ContractFactory(artifact.abi, artifact.bytecode, this.zkWallet, this.deploymentType); // Encode deploy transaction so it can be estimated. @@ -192,15 +195,18 @@ export class Deployer { artifact: ZkSyncArtifact, constructorArguments: any[] = [], overrides?: ethers.Overrides, - additionalFactoryDeps?: ethers.BytesLike[] + additionalFactoryDeps?: ethers.BytesLike[], ): Promise { - const baseDeps = await this.extractFactoryDeps(artifact); - const additionalDeps = additionalFactoryDeps - ? additionalFactoryDeps.map((val) => ethers.hexlify(val)) - : []; + const baseDeps = await this._extractFactoryDeps(artifact); + const additionalDeps = additionalFactoryDeps ? additionalFactoryDeps.map((val) => ethers.hexlify(val)) : []; const factoryDeps = [...baseDeps, ...additionalDeps]; - const factory = new zk.ContractFactory(artifact.abi, artifact.bytecode, this.zkWallet, this.deploymentType); + const factory = new zk.ContractFactory( + artifact.abi, + artifact.bytecode, + this.zkWallet, + this.deploymentType, + ); const { customData, ..._overrides } = overrides ?? {}; // Encode and send the deploy transaction providing factory dependencies. @@ -208,12 +214,12 @@ export class Deployer { ..._overrides, customData: { ...customData, - salt:ethers.ZeroHash, + salt: ethers.ZeroHash, factoryDeps, }, }); await contract.waitForDeployment(); - + return contract; } @@ -224,23 +230,24 @@ export class Deployer { * * @returns Factory dependencies in the format expected by SDK. */ - async extractFactoryDeps(artifact: ZkSyncArtifact): Promise { + private async _extractFactoryDeps(artifact: ZkSyncArtifact): Promise { const visited = new Set(); visited.add(`${artifact.sourceName}:${artifact.contractName}`); - return await this.extractFactoryDepsRecursive(artifact, visited); + return await this._extractFactoryDepsRecursive(artifact, visited); } - private async extractFactoryDepsRecursive(artifact: ZkSyncArtifact, visited: Set): Promise { + private async _extractFactoryDepsRecursive(artifact: ZkSyncArtifact, visited: Set): Promise { // Load all the dependency bytecodes. // We transform it into an array of bytecodes. const factoryDeps: string[] = []; for (const dependencyHash in artifact.factoryDeps) { + if (!dependencyHash) continue; const dependencyContract = artifact.factoryDeps[dependencyHash]; if (!visited.has(dependencyContract)) { const dependencyArtifact = await this.loadArtifact(dependencyContract); factoryDeps.push(dependencyArtifact.bytecode); visited.add(dependencyContract); - const transitiveDeps = await this.extractFactoryDepsRecursive(dependencyArtifact, visited); + const transitiveDeps = await this._extractFactoryDepsRecursive(dependencyArtifact, visited); factoryDeps.push(...transitiveDeps); } } diff --git a/packages/hardhat-zksync-deploy/src/index.ts b/packages/hardhat-zksync-deploy/src/index.ts index 5bc48ecc0..44c7a2b51 100644 --- a/packages/hardhat-zksync-deploy/src/index.ts +++ b/packages/hardhat-zksync-deploy/src/index.ts @@ -1,10 +1,10 @@ import { extendEnvironment, task } from 'hardhat/config'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import chalk from 'chalk'; +import { int, string } from 'hardhat/internal/core/params/argumentTypes'; import { TASK_DEPLOY_ZKSYNC, TASK_DEPLOY_ZKSYNC_LIBRARIES } from './task-names'; import './type-extensions'; import { zkSyncDeploy, zkSyncLibraryDeploy } from './task-actions'; -import { int, string } from 'hardhat/internal/core/params/argumentTypes'; -import chalk from 'chalk'; export * from './deployer'; @@ -19,13 +19,22 @@ task(TASK_DEPLOY_ZKSYNC, 'Runs the deploy scripts for zkSync network') task(TASK_DEPLOY_ZKSYNC_LIBRARIES, 'Runs the library deploy for zkSync network') .addOptionalParam('privateKey', 'Private key of the account that will deploy the libraries', undefined, string) .addOptionalParam('accountNumber', 'Network account index', 0, int) - .addOptionalParam('externalConfigObjectPath', 'Config file imported in hardhat config file that represent HardhatUserConfig type variable', undefined) - .addOptionalParam('exportedConfigObject', 'Object in hardhat config file that represent HardhatUserConfig type variable', 'config', string) + .addOptionalParam( + 'externalConfigObjectPath', + 'Config file imported in hardhat config file that represent HardhatUserConfig type variable', + undefined, + ) + .addOptionalParam( + 'exportedConfigObject', + 'Object in hardhat config file that represent HardhatUserConfig type variable', + 'config', + string, + ) .addFlag('noAutoPopulateConfig', 'Flag to disable auto population of config file') .addFlag('compileAllContracts', 'Flag to compile all contracts at the end of the process') .setAction(zkSyncLibraryDeploy); try { require.resolve('zksync2-js'); - console.info(chalk.red('The package zksync2-js is deprecated. Please use zksync-ethers.')) -} catch { } \ No newline at end of file + console.info(chalk.red('The package zksync2-js is deprecated. Please use zksync-ethers.')); +} catch {} diff --git a/packages/hardhat-zksync-deploy/src/morph-ts-builder.ts b/packages/hardhat-zksync-deploy/src/morph-ts-builder.ts index 9316661b2..0678525ef 100644 --- a/packages/hardhat-zksync-deploy/src/morph-ts-builder.ts +++ b/packages/hardhat-zksync-deploy/src/morph-ts-builder.ts @@ -1,4 +1,4 @@ -import { Expression, Project, SourceFile, SyntaxKind } from "ts-morph"; +import { Expression, Project, SourceFile, SyntaxKind } from 'ts-morph'; import * as fs from 'fs'; export class MorphTsBuilder { @@ -10,52 +10,66 @@ export class MorphTsBuilder { this._sourceFile = project.createSourceFile(_filePath, fileContent, { overwrite: true }); } - public intialStep(steps: (MorphBuilderInitialStepType | MorphBuilderInitialStepVariable | MorphBuilderInitialStepModule)[]) { + public intialStep( + steps: Array, + ) { return new MorphTs(steps, this._sourceFile, this._filePath); } } export class MorphTs { - private _currentStep : Expression = undefined as any as Expression; - - constructor(private _steps: (MorphBuilderInitialStepType | MorphBuilderInitialStepVariable | MorphBuilderInitialStepModule)[], private _sourceFile: SourceFile, private _filePath: string) { - let initialValue : any; - - for(let _step of _steps) { + private _currentStep: Expression = undefined as any as Expression; + + constructor( + private _steps: Array< + MorphBuilderInitialStepType | MorphBuilderInitialStepVariable | MorphBuilderInitialStepModule + >, + private _sourceFile: SourceFile, + private _filePath: string, + ) { + let initialValue: any; + + for (const _step of _steps) { if (isMorphBuilderInitialStepType(_step)) { - initialValue = _sourceFile.getVariableDeclaration(v => v.getType().getText().includes((_step as MorphBuilderInitialStepType).initialVariableType)); - - if(!initialValue || initialValue === undefined) { + initialValue = _sourceFile.getVariableDeclaration((v) => + v + .getType() + .getText() + .includes((_step as MorphBuilderInitialStepType).initialVariableType), + ); + + if (!initialValue || initialValue === undefined) { continue; } - const intialStep = initialValue.getInitializer() - ?.asKindOrThrow(SyntaxKind.ObjectLiteralExpression); + const intialStep = initialValue.getInitializer()?.asKindOrThrow(SyntaxKind.ObjectLiteralExpression); this._currentStep = intialStep; return; } else if (isMorphBuilderInitialStepModule(_step)) { initialValue = _sourceFile - .getStatements().find(ea => ea.getText().startsWith((_step as MorphBuilderInitialStepModule).initialModule)) - ?.asKind(SyntaxKind.ExpressionStatement) - ?.getFirstChildByKind(SyntaxKind.BinaryExpression) - ?.getRight() + .getStatements() + .find((ea) => ea.getText().startsWith((_step as MorphBuilderInitialStepModule).initialModule)) + ?.asKind(SyntaxKind.ExpressionStatement) + ?.getFirstChildByKind(SyntaxKind.BinaryExpression) + ?.getRight(); - if(!initialValue || initialValue === undefined) { + if (!initialValue || initialValue === undefined) { continue; } - + this._currentStep = initialValue; return; } else { - initialValue = _sourceFile.getVariableDeclaration((_step as MorphBuilderInitialStepVariable).initialVariable); + initialValue = _sourceFile.getVariableDeclaration( + (_step as MorphBuilderInitialStepVariable).initialVariable, + ); - if(!initialValue || initialValue === undefined) { + if (!initialValue || initialValue === undefined) { continue; } - const intialStep = initialValue.getInitializer() - ?.asKindOrThrow(SyntaxKind.ObjectLiteralExpression); + const intialStep = initialValue.getInitializer()?.asKindOrThrow(SyntaxKind.ObjectLiteralExpression); this._currentStep = intialStep; return; @@ -68,9 +82,9 @@ export class MorphTs { } public nextStep(step: MorphTsNextStep) { - let previousStep = this._currentStep; + const previousStep = this._currentStep; - let presentStep = previousStep + const presentStep = previousStep ?.asKindOrThrow(SyntaxKind.ObjectLiteralExpression) .getProperty(step.propertyName); @@ -83,12 +97,12 @@ export class MorphTs { ?.asKindOrThrow(SyntaxKind.ObjectLiteralExpression) .addPropertyAssignment({ name: step.propertyName, - initializer: JSON.stringify({}, null, 2) + initializer: JSON.stringify({}, null, 2), }) .getParentIfKindOrThrow(SyntaxKind.ObjectLiteralExpression); } - let newPresentStep = previousStep + const newPresentStep = previousStep ?.asKindOrThrow(SyntaxKind.ObjectLiteralExpression) .getProperty(step.propertyName) ?.getFirstChildByKindOrThrow(SyntaxKind.ObjectLiteralExpression); @@ -105,9 +119,9 @@ export class MorphTs { public replaceStep(step: MorphTsReplaceStep) { this.nextStep({ propertyName: step.propertyName }); - let previousStep = this._currentStep; + const previousStep = this._currentStep; - let presentStep = previousStep + const presentStep = previousStep ?.asKindOrThrow(SyntaxKind.ObjectLiteralExpression) .getParentIfKindOrThrow(SyntaxKind.PropertyAssignment) .setInitializer(JSON.stringify(step.replaceObject, null, 2)) @@ -158,4 +172,4 @@ function isMorphBuilderInitialStepType(object: any): object is MorphBuilderIniti function isMorphBuilderInitialStepModule(object: any): object is MorphBuilderInitialStepModule { return 'initialModule' in object; -} \ No newline at end of file +} diff --git a/packages/hardhat-zksync-deploy/src/plugin.ts b/packages/hardhat-zksync-deploy/src/plugin.ts index c3c11cf71..44894874d 100644 --- a/packages/hardhat-zksync-deploy/src/plugin.ts +++ b/packages/hardhat-zksync-deploy/src/plugin.ts @@ -1,13 +1,20 @@ -import { existsSync } from 'fs'; +import fs, { existsSync } from 'fs'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; import * as path from 'path'; -import fs from 'fs'; +import chalk from 'chalk'; import { ZkSyncDeployPluginError } from './errors'; import { Deployer } from './deployer'; import { ContractFullQualifiedName, ContractInfo, MissingLibrary } from './types'; -import chalk from 'chalk'; -import { compileContracts, fillLibrarySettings, generateFullQuailfiedNameString, getLibraryInfos, getWallet, removeLibraryInfoFile, updateHardhatConfigFile } from './utils'; +import { + compileContracts, + fillLibrarySettings, + generateFullQuailfiedNameString, + getLibraryInfos, + getWallet, + removeLibraryInfoFile, + updateHardhatConfigFile, +} from './utils'; function getAllFiles(dir: string): string[] { const files = []; @@ -32,7 +39,7 @@ export function findDeployScripts(hre: HardhatRuntimeEnvironment): string[] { } const deployScripts = getAllFiles(deployScriptsDir).filter( - (file) => path.extname(file) == '.ts' || path.extname(file) == '.js' + (file) => path.extname(file) === '.ts' || path.extname(file) === '.js', ); return deployScripts; @@ -41,7 +48,7 @@ export function findDeployScripts(hre: HardhatRuntimeEnvironment): string[] { export async function callDeployScripts(hre: HardhatRuntimeEnvironment, targetScript: string) { const scripts = findDeployScripts(hre); - if (targetScript == '') { + if (targetScript === '') { // Target script not specified, run everything. for (const script of scripts) { await runScript(hre, script); @@ -57,7 +64,7 @@ export async function callDeployScripts(hre: HardhatRuntimeEnvironment, targetSc } } if (!found) { - console.error(`Script ${targetScript} was not found, no scripts were run`); + throw new ZkSyncDeployPluginError(`Script ${targetScript} was not found, no scripts were run`); } } } @@ -78,13 +85,13 @@ async function runScript(hre: HardhatRuntimeEnvironment, script: string) { } export async function deployLibraries( - hre: HardhatRuntimeEnvironment, + hre: HardhatRuntimeEnvironment, privateKey: string, - accountNumber: number, - externalConfigObjectPath: string, + accountNumber: number, + externalConfigObjectPath: string, exportedConfigObject: string, - noAutoPopulateConfig: boolean, - compileAllContracts: boolean + noAutoPopulateConfig: boolean, + compileAllContracts: boolean, ) { const wallet = getWallet(hre, privateKey, accountNumber); const deployer = new Deployer(hre, wallet); @@ -92,12 +99,12 @@ export async function deployLibraries( const libraryInfos = getLibraryInfos(hre); const allDeployedLibraries: ContractInfo[] = []; - //@ts-ignore + // @ts-ignore hre.config.zksolc.settings.contractsToCompile = []; for (const libraryInfo of libraryInfos) { const compileInfo = await deployLibrary(hre, deployer, libraryInfo, libraryInfos, allDeployedLibraries); - fillLibrarySettings(hre, [compileInfo]); + const _ = fillLibrarySettings(hre, [compileInfo]); } console.info(chalk.green('All libraries deployed successfully!')); @@ -108,7 +115,7 @@ export async function deployLibraries( removeLibraryInfoFile(hre); - if(compileAllContracts) { + if (compileAllContracts) { console.info(chalk.yellow('Compiling all contracts')); await compileContracts(hre, []); } else { @@ -121,12 +128,13 @@ async function deployLibrary( deployer: Deployer, missingLibrary: MissingLibrary, missingLibraries: MissingLibrary[], - allDeployedLibraries: ContractInfo[] + allDeployedLibraries: ContractInfo[], ): Promise { - const deployedLibrary = allDeployedLibraries - .find(deployedLibrary => generateFullQuailfiedNameString(missingLibrary) - .includes(generateFullQuailfiedNameString(deployedLibrary.contractFQN)) - ); + const deployedLibrary = allDeployedLibraries.find((deplLibrary) => + generateFullQuailfiedNameString(missingLibrary).includes( + generateFullQuailfiedNameString(deplLibrary.contractFQN), + ), + ); if (deployedLibrary) { return deployedLibrary; @@ -134,36 +142,35 @@ async function deployLibrary( const contractFQN = { contractName: missingLibrary.contractName, - contractPath: missingLibrary.contractPath + contractPath: missingLibrary.contractPath, }; - if (missingLibrary.missingLibraries.length == 0) { + if (missingLibrary.missingLibraries.length === 0) { return await compileAndDeploy(hre, deployer, contractFQN, allDeployedLibraries); } const dependentLibraries = findDependentLibraries(missingLibrary.missingLibraries, missingLibraries); const contractInfos = await Promise.all( - Array.from(dependentLibraries) - .map(async dependentLibrary => - await deployLibrary(hre, deployer, dependentLibrary, missingLibraries, allDeployedLibraries) - ) + Array.from(dependentLibraries).map(async (dependentLibrary) => + deployLibrary(hre, deployer, dependentLibrary, missingLibraries, allDeployedLibraries), + ), ); - fillLibrarySettings(hre, contractInfos); + const _ = fillLibrarySettings(hre, contractInfos); return await compileAndDeploy(hre, deployer, contractFQN, allDeployedLibraries); } function findDependentLibraries(dependentLibraries: string[], missingLibraries: MissingLibrary[]): MissingLibrary[] { - return dependentLibraries.map(dependentLibrary => { + return dependentLibraries.map((dependentLibrary) => { const dependentFQNString = dependentLibrary.split(':'); const dependentFQN = { contractName: dependentFQNString[1], - contractPath: dependentFQNString[0] - } + contractPath: dependentFQNString[0], + }; - const foundMissingLibrary = missingLibraries - .find(missingLibrary => generateFullQuailfiedNameString(missingLibrary) - .includes(generateFullQuailfiedNameString(dependentFQN))); + const foundMissingLibrary = missingLibraries.find((missingLibrary) => + generateFullQuailfiedNameString(missingLibrary).includes(generateFullQuailfiedNameString(dependentFQN)), + ); if (!foundMissingLibrary) { throw new ZkSyncDeployPluginError(`Missing library ${dependentLibrary} not found`); @@ -176,17 +183,19 @@ function findDependentLibraries(dependentLibraries: string[], missingLibraries: async function deployOneLibrary( deployer: Deployer, contractFQN: ContractFullQualifiedName, - allDeployedLibraries: ContractInfo[] + allDeployedLibraries: ContractInfo[], ): Promise { const artifact = await deployer.loadArtifact(generateFullQuailfiedNameString(contractFQN)); console.info(chalk.yellow(`Deploying ${generateFullQuailfiedNameString(contractFQN)} .....`)); const contract = await deployer.deploy(artifact, []); - console.info(chalk.green(`Deployed ${generateFullQuailfiedNameString(contractFQN)} at ${await contract.getAddress()}`)); + console.info( + chalk.green(`Deployed ${generateFullQuailfiedNameString(contractFQN)} at ${await contract.getAddress()}`), + ); - const contractInfo:ContractInfo = { + const contractInfo: ContractInfo = { contractFQN, - address: await contract.getAddress() + address: await contract.getAddress(), }; allDeployedLibraries.push(contractInfo); @@ -197,7 +206,7 @@ async function compileAndDeploy( hre: HardhatRuntimeEnvironment, deployer: Deployer, contractFQN: ContractFullQualifiedName, - allDeployedLibraries: ContractInfo[] + allDeployedLibraries: ContractInfo[], ): Promise { await compileContracts(hre, [contractFQN.contractPath]); diff --git a/packages/hardhat-zksync-deploy/src/task-actions.ts b/packages/hardhat-zksync-deploy/src/task-actions.ts index e5d361390..a957a8003 100644 --- a/packages/hardhat-zksync-deploy/src/task-actions.ts +++ b/packages/hardhat-zksync-deploy/src/task-actions.ts @@ -1,10 +1,18 @@ import { HardhatRuntimeEnvironment, TaskArguments } from 'hardhat/types'; -import { callDeployScripts, deployLibraries} from './plugin'; +import { callDeployScripts, deployLibraries } from './plugin'; export async function zkSyncDeploy(taskArgs: TaskArguments, hre: HardhatRuntimeEnvironment) { await callDeployScripts(hre, taskArgs.script); } export async function zkSyncLibraryDeploy(taskArgs: TaskArguments, hre: HardhatRuntimeEnvironment) { - await deployLibraries(hre, taskArgs.privateKey, taskArgs.accountNumber, taskArgs.externalConfigObjectPath, taskArgs.exportedConfigObject, taskArgs.noAutoPopulateConfig, taskArgs.compileAllContracts); + await deployLibraries( + hre, + taskArgs.privateKey, + taskArgs.accountNumber, + taskArgs.externalConfigObjectPath, + taskArgs.exportedConfigObject, + taskArgs.noAutoPopulateConfig, + taskArgs.compileAllContracts, + ); } diff --git a/packages/hardhat-zksync-deploy/src/types.ts b/packages/hardhat-zksync-deploy/src/types.ts index 872d99d37..ccb389c5d 100644 --- a/packages/hardhat-zksync-deploy/src/types.ts +++ b/packages/hardhat-zksync-deploy/src/types.ts @@ -12,29 +12,29 @@ export type EthNetwork = string; * Dependencies are contracts that can be deployed by this contract via `CREATE` operation. */ export interface FactoryDeps { - // A mapping from the contract hash to the contract bytecode. - [contractHash: string]: string; + // A mapping from the contract hash to the contract bytecode. + [contractHash: string]: string; } export interface ZkSyncArtifact extends Artifact { - // List of factory dependencies of a contract. - factoryDeps: FactoryDeps; - // Mapping from the bytecode to the zkEVM assembly (used for tracing). - sourceMapping: string; + // List of factory dependencies of a contract. + factoryDeps: FactoryDeps; + // Mapping from the bytecode to the zkEVM assembly (used for tracing). + sourceMapping: string; } export interface MissingLibrary { - contractName: string; - contractPath: string; - missingLibraries: Array; + contractName: string; + contractPath: string; + missingLibraries: string[]; } export interface ContractInfo { - contractFQN: ContractFullQualifiedName; - address: string; + contractFQN: ContractFullQualifiedName; + address: string; } export interface ContractFullQualifiedName { - contractName: string; - contractPath: string; + contractName: string; + contractPath: string; } diff --git a/packages/hardhat-zksync-deploy/src/utils.ts b/packages/hardhat-zksync-deploy/src/utils.ts index 06b21f647..ccdf7f89b 100644 --- a/packages/hardhat-zksync-deploy/src/utils.ts +++ b/packages/hardhat-zksync-deploy/src/utils.ts @@ -1,48 +1,64 @@ -//@ts-nocheck -import { HardhatNetworkAccountConfig, HardhatNetworkHDAccountsConfig, HardhatRuntimeEnvironment, HttpNetworkConfig, NetworkConfig } from 'hardhat/types'; +// @ts-nocheck +import { + HardhatNetworkAccountConfig, + HardhatNetworkHDAccountsConfig, + HardhatRuntimeEnvironment, + HttpNetworkConfig, + NetworkConfig, +} from 'hardhat/types'; +import fs from 'fs'; +import { Wallet } from 'zksync-ethers'; import { ContractFullQualifiedName, ContractInfo, MissingLibrary } from './types'; import { MorphTsBuilder } from './morph-ts-builder'; -import fs from 'fs'; import { ZkSyncDeployPluginError } from './errors'; -import { Wallet } from 'zksync-ethers'; export function isHttpNetworkConfig(networkConfig: NetworkConfig): networkConfig is HttpNetworkConfig { return 'url' in networkConfig; } -export function updateHardhatConfigFile(hre: HardhatRuntimeEnvironment, externalConfigObjectPath: string, exportedConfigObject: string) { - try { +export function updateHardhatConfigFile( + hre: HardhatRuntimeEnvironment, + externalConfigObjectPath: string, + exportedConfigObject: string, +) { + try { new MorphTsBuilder(externalConfigObjectPath ?? hre.config.paths.configFile) - .intialStep([{initialVariableType: "HardhatUserConfig"}, {initialVariable: exportedConfigObject}, {initialModule: "module.exports"}]) - .nextStep({propertyName: 'zksolc', isRequired: true}) - .nextStep({ propertyName: 'settings'}) - .replaceStep({ propertyName: 'libraries', replaceObject: hre.config.zksolc.settings.libraries}) - .save(); + .intialStep([ + { initialVariableType: 'HardhatUserConfig' }, + { initialVariable: exportedConfigObject }, + { initialModule: 'module.exports' }, + ]) + .nextStep({ propertyName: 'zksolc', isRequired: true }) + .nextStep({ propertyName: 'settings' }) + .replaceStep({ propertyName: 'libraries', replaceObject: hre.config.zksolc.settings.libraries }) + .save(); } catch (error) { - throw new ZkSyncDeployPluginError('Failed to update hardhat config file, please use addresses from console output'); + throw new ZkSyncDeployPluginError( + 'Failed to update hardhat config file, please use addresses from console output', + ); } } export function generateFullQuailfiedNameString(contractFQN: ContractFullQualifiedName | MissingLibrary): string { - return contractFQN.contractPath + ":" + contractFQN.contractName; + return `${contractFQN.contractPath}:${contractFQN.contractName}`; } export async function fillLibrarySettings(hre: HardhatRuntimeEnvironment, libraries: ContractInfo[]) { libraries.forEach((library) => { - let contractPath = library.contractFQN.contractPath; - let contractName = library.contractFQN.contractName; + const contractPath = library.contractFQN.contractPath; + const contractName = library.contractFQN.contractName; if (!hre.config.zksolc.settings.libraries) { hre.config.zksolc.settings.libraries = {}; } hre.config.zksolc.settings.libraries[contractPath] = { - [contractName]: library.address + [contractName]: library.address, }; }); } -export function getLibraryInfos(hre: HardhatRuntimeEnvironment): Array { +export function getLibraryInfos(hre: HardhatRuntimeEnvironment): MissingLibrary[] { const libraryPathFile = hre.config.zksolc.settings.missingLibrariesPath!; if (!fs.existsSync(libraryPathFile)) { @@ -82,28 +98,28 @@ export function getWallet(hre: HardhatRuntimeEnvironment, privateKey: string, ac const accounts = hre.network.config.accounts; - if(!accounts) { + if (!accounts) { throw new ZkSyncDeployPluginError('Accounts for selected newtwork are not specified'); } - if(isHardhatNetworkAccountsConfigStrings(accounts)) { + if (isHardhatNetworkAccountsConfigStrings(accounts)) { const accountPrivateKey = (accounts as string[])[accountNumber]; - if(!accountPrivateKey) { + if (!accountPrivateKey) { throw new ZkSyncDeployPluginError('Account private key with specified index is not found'); } return new Wallet(accountPrivateKey); } - if(isHardhatNetworkHDAccountsConfig(accounts)) { - const account = (accounts as HardhatNetworkHDAccountsConfig); - return Wallet.fromMnemonic(account.mnemonic); + if (isHardhatNetworkHDAccountsConfig(accounts)) { + const hdAccount = accounts as HardhatNetworkHDAccountsConfig; + return Wallet.fromMnemonic(hdAccount.mnemonic); } const account = (accounts as HardhatNetworkAccountConfig[])[accountNumber]; - if(!account) { + if (!account) { throw new ZkSyncDeployPluginError('Account with specified index is not found'); } @@ -116,4 +132,4 @@ function isHardhatNetworkHDAccountsConfig(object: any): object is HardhatNetwork function isHardhatNetworkAccountsConfigStrings(object: any): object is string[] { return typeof object[0] === 'string'; -} \ No newline at end of file +} diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/ChildChildLib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/ChildChildLib.sol new file mode 100644 index 000000000..8762d9ebd --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/ChildChildLib.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +library ChildChildLib { + + function plus(uint a, uint b) public pure returns (uint) { + return a - b; + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/ChildLib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/ChildLib.sol new file mode 100644 index 000000000..54818b75b --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/ChildLib.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +import "./ChildChildLib.sol"; +library ChildLib { + + using ChildChildLib for uint; + + function plus(uint a, uint b) public view returns (uint, address) { + return (a.plus(b), address(this)); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/DLib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/DLib.sol new file mode 100644 index 000000000..4d3d38623 --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/DLib.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +library DLib { + + function multiply(uint a, uint b) public view returns (uint, address) { + return (a * b, address(this)); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/Math2Lib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/Math2Lib.sol new file mode 100644 index 000000000..99c1d1005 --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/Math2Lib.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +import "./ChildLib.sol"; +library Math2Lib { + + using ChildLib for uint; + + function multiply(uint a, uint b) public view returns (uint, address) { + return (a * b, address(this)); + } + + function plus(uint a, uint b) public view returns (uint, address) { + return a.plus(b); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/MathLib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/MathLib.sol new file mode 100644 index 000000000..5f6bcd86d --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/MathLib.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +import "./ChildLib.sol"; +library MathLib { + + using ChildLib for uint; + + function multiply(uint a, uint b) public view returns (uint, address) { + return (a * b, address(this)); + } + + function plus(uint a, uint b) public view returns (uint, address) { + return a.plus(b); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/TestCoin.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/TestCoin.sol new file mode 100644 index 000000000..f360f5a8f --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/contracts/TestCoin.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +import "./MathLib.sol"; + +contract TestCoin { + + using MathLib for uint; + address owner = address(this); + + function multiplyExample(uint _a, uint _b) public view returns (uint, address) { + return _a.multiply(_b); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/hardhat.config.ts b/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/hardhat.config.ts new file mode 100644 index 000000000..eccc7a7fb --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/deployment-with-mnemonic/hardhat.config.ts @@ -0,0 +1,36 @@ +// eslint-disable-next-line +import '@matterlabs/hardhat-zksync-deploy'; +import '@matterlabs/hardhat-zksync-solc'; + +import { HardhatUserConfig } from 'hardhat/config'; + +const config: HardhatUserConfig = { + zksolc: { + settings: {}, + }, + defaultNetwork: 'zkSyncNetwork', + networks: { + ethNetwork: { + url: 'http://0.0.0.0:8545', + }, + zkSyncNetwork: { + url: 'http://0.0.0.0:3050', + ethNetwork: 'ethNetwork', + zksync: true, + accounts: { + // found in zksync-era github repo + mnemonic: 'fine music test violin matrix prize squirrel panther purchase material script deal', + path: "m/44'/60'/0'/1", + initialIndex: 0, + count: 2, + }, + }, + }, + // 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/packages/hardhat-zksync-deploy/test/fixture-projects/missing-deploy-folder/Greeter.sol/Greeter.json b/packages/hardhat-zksync-deploy/test/fixture-projects/missing-deploy-folder/Greeter.sol/Greeter.json new file mode 100644 index 000000000..af6efad8e --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/missing-deploy-folder/Greeter.sol/Greeter.json @@ -0,0 +1,36 @@ +{ + "_format": "hh-zksolc-artifact-1", + "_additionalKey": "some_value", + "contractName": "Greeter", + "sourceName": "contracts/Greeter.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "_greeting", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "greet", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x0e000000e00500000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000e000200200000000000000000000000000000000000000000000000000000000e000200200100000000000000000000000000000000000000000000000000000e000200200300000000000000000000000000000000000000000000000000000c802000017c7ee91ae51bb0ed9b9e413333bf74f700000000000000000000000c80200000b75ee1170f6b7c3ce4583c65d82198c400000000000000000000000e8000006001000000000000000000000000000000000000000000000000000005022000010000000000000000000000000000000000000000000000000000000e800000600500000000000000000000000000000000000000000000000000000c802000010000000000000000ffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffffffffffff00000000000000000000000e004000010000000000000000000000000000000000000000000000000000000e000100600200000000000000000000000000000000000000000000000000001a844000020000000000000000000000000000000000000000000000000000000c000401010000000000000000010000000000000000000000000000000000000c000401000000000000000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c8020000000000000000000000000000000000000000000000000000000000002020002000000000000000000000000000000000000000000000000000000000e000100600300000000000000000000000000000000000000000000000000000e0004006006000000000000000000000000000000000000000000000000000004040400000000000000000000000000000000000000000000000000000000000a000000041a001900000000000000000000000000000000000000000000000002080002000000000000000000000000000000000000000000000000000000000e0008006004000000000000000000000000000000000000000000000000000002020002000000000000000000000000000000000000000000000000000000000e0000016105000000000000000000000000000000000000000000000000000004100000000000000000000000000000000000000000000000000000000000000a0000000420001f00000000000000000000000000000000000000000000000002080002000000000000000000000000000000000000000000000000000000000e0040006106000000000000000000000000000000000000000000000000000004900000000000000000000000000000000000000000000000000000000000000a000000042300280000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c8020000000000000000000000000000083bbcae300000000000000000000000e0080006103000000000000000000000000000000000000000000000000000004080100000000000000000000000000000000000000000000000000000000000a00000004dd002800000000000000000000000000000000000000000000000004100000000000000000000000000000000000000000000000000000000000000a000000042a002f0000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c80200000000000000000000000000000c9a6bc8e00000000000000000000000e0080006102000000000000000000000000000000000000000000000000000004080100000000000000000000000000000000000000000000000000000000000a00000002dd002f0000000000000000000000000000000000000000000000000e002000610400000000000000000000000000000000000000000000000000001aa04000020000000000000000000000000000000000000000000000000000000c802000018000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000200200000000000000000000000000000000000000000000000000000e002000010000000000000000000000000000000000000000000000000000001a80200002ffffffff00000000000000000000000000000000000000000000001a004100020100000000000000000000000000000000000000000000000000000c008200010100000000000000000000000000000000000000000000000000000c0082000000000000000000000000000000000000000000000000000000000004040200000000000000000000000000000000000000000000000000000000000a000000043b008300000000000000000000000000000000000000000000000002804000001f00000000000000000000000000000000000000000000000000001a00410002e0ffffff010000000000000000000000000000000000000000000002004100008000000000000000000000000000000000000000000000000000000e000100200200000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000001a804000021f00000000000000000000000000000000000000000000000000000e00010060060000000000000000000000000000000000000000000000000000188000010205000000000000000000000000000000000000000000000000000004100000000000000000000000000000000000000000000000000000000000000a000000044f004600000000000000000000000000000000000000000000000018000202000500000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000008204100000000000000000000000000000000000000000000000000000000000e040002010100000000000000000000000000000000000000000000000000000e04080020040000000000000000000000000000000000000000000000000000020082000001000000000000000000000000000000000000000000000000000004080400000000000000000000000000000000000000000000000000000000000a0000000246004f0000000000000000000000000000000000000000000000000e0040006106000000000000000000000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000000000000a0000000474005200000000000000000000000000000000000000000000000018000101000300000000000000000000000000000000000000000000000000000c004100010001000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000004044400000000000000000000000000000000000000000000000000000000000c00820001ffffffffffffffffffffffffffffffff00000000000000000000000c00820000ffffffffffffffffffffffffffffffff00000000000000000000000e000200600500000000000000000000000000000000000000000000000000000c00080201ff00000000000000000000000000000000000000000000000000000c000802000000000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000e0002006006000000000000000000000000000000000000000000000000000004040800000000000000000000000000000000000000000000000000000000000a000000086500600000000000000000000000000000000000000000000000000e000200600400000000000000000000000000000000000000000000000000000e0080006105000000000000000000000000000000000000000000000000000018044200000000000000000000000000000000000000000000000000000000000e008000610400000000000000000000000000000000000000000000000000000e000100600600000000000000000000000000000000000000000000000000001a80400002e0ffffff000000000000000000000000000000000000000000000004100800000000000000000000000000000000000000000000000000000000000a000000086a00680000000000000000000000000000000000000000000000000e0080006105000000000000000000000000000000000000000000000000000018108200020000000000000000000000000000000000000000000000000000000c000401012000000000000000000000000000000000000000000000000000000c0004010000000000000000000000000000000000000000000000000000000008044400000000000000000000000000000000000000000000000000000000000e040001210400000000000000000000000000000000000000000000000000001a108200020000000000000000000000000000000000000000000000000000000e040001010100000000000000000000000000000000000000000000000000000e000002610600000000000000000000000000000000000000000000000000001a100801020000000000000000000000000000000000000000000000000000001a108200040000000000000000000000000000000000000000000000000000000e0402002004000000000000000000000000000000000000000000000000000002801000008000000000000000000000000000000000000000000000000000000700000003b601000000000000000000000000000000000000000000000000000a00000002e8007700000000000000000000000000000000000000000000000007000000037002000000000000000000000000000000000000000000000000000a00000002ea007900000000000000000000000000000000000000000000000007000000032303000000000000000000000000000000000000000000000000000a00000002ec007b0000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e0040006101000000000000000000000000000000000000000000000000000005840000000000000000000000000000000000000000000000000000000000000a00000001db00db0000000000000000000000000000000000000000000000000e000200600600000000000000000000000000000000000000000000000000001a80200002fcffffff000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000a00000004e300870000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000ffffffff00000000000000000000000e002000010100000000000000000000000000000000000000000000000000000e000100600500000000000000000000000000000000000000000000000000001a022100020000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c004100000000000000000000000000001732aecf000000000000000000000004020100000000000000000000000000000000000000000000000000000000000a000000049000e300000000000000000000000000000000000000000000000007000000034503000000000000000000000000000000000000000000000000000a00000002ee009200000000000000000000000000000000000000000000000002402000001f00000000000000000000000000000000000000000000000000000c00410001e0ffffffffffffffffffffffffffffff00000000000000000000000c00410000ffffffffffffffffffffffffffffffff00000000000000000000000e000100600400000000000000000000000000000000000000000000000000001a020102020000000000000000000000000000000000000000000000000000000c8020000180ffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffffffffffff000000000000000000000002a02000000000000000000000000000000000000000000000000000000000000c004100017fffffffffffffff000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000004020100000000000000000000000000000000000000000000000000000000000a00000008f0009e0000000000000000000000000000000000000000000000000e000800200200000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000008202100000000000000000000000000000000000000000000000000000000000e000100600600000000000000000000000000000000000000000000000000000e020100200000000000000000000000000000000000000000000000000000000e008000210400000000000000000000000000000000000000000000000000000e020200200100000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000002002800004000000000000000000000000000000000000000000000000000000e0002006005000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a00000004c000ac00000000000000000000000000000000000000000000000002848000000000000000000000000000000000000000000000000000000000000e00000161060000000000000000000000000000000000000000000000000000080884000000000000000000000000000000000000000000000000000000000008040401000000000000000000000000000000000000000000000000000000000e100001210500000000000000000000000000000000000000000000000000000e0804002000000000000000000000000000000000000000000000000000000002004100002000000000000000000000000000000000000000000000000000000e0080006105000000000000000000000000000000000000000000000000000004040200000000000000000000000000000000000000000000000000000000000a00000002ac00b60000000000000000000000000000000000000000000000000e0080006105000000000000000000000000000000000000000000000000000004040200000000000000000000000000000000000000000000000000000000000a00000006c000b90000000000000000000000000000000000000000000000000e0040006105000000000000000000000000000000000000000000000000000002842000000000000000000000000000000000000000000000000000000000000c401000010000000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000e000800600300000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000e000002610300000000000000000000000000000000000000000000000000000e0020006105000000000000000000000000000000000000000000000000000002802000005f00000000000000000000000000000000000000000000000000000e004000610400000000000000000000000000000000000000000000000000001a024100020000000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000e0001000000000000000000000000000000000000000000000000000000000018002100020500000000000000000000000000000000000000000000000000000e000200600400000000000000000000000000000000000000000000000000000e8000006005000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000a00000004d900cc0000000000000000000000000000000000000000000000001800020100050000000000000000000000000000000000000000000000000000022020000000000000000000000000000000000000000000000000000000000002020402000000000000000000000000000000000000000000000000000000000e00400061060000000000000000000000000000000000000000000000000000081001010000000000000000000000000000000000000000000000000000000008200102000000000000000000000000000000000000000000000000000000000e200002210000000000000000000000000000000000000000000000000000000e10080000010000000000000000000000000000000000000000000000000000020200020000000000000000000000000000000000000000000000000000000002008200000100000000000000000000000000000000000000000000000000000e0020006105000000000000000000000000000000000000000000000000000004880000000000000000000000000000000000000000000000000000000000000a00000002cc00d90000000000000000000000000000000000000000000000000e0020006104000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000c004100010100000000000000000000000000000000000000000000000000000c004100000000000000000000000000000000000000000000000000000000000e000100000000000000000000000000000000000000000000000000000000000e800000000100000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c80200000000000000000000000000000717b484e00000000000000000000000e800000200000000000000000000000000000000000000000000000000000000c401000014100000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000c802000010400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000c802000012400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000002028000000000000000000000000000000000000000000000000000000000000a00000001040104010000000000000000000000000000000000000000000000021080000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000a0000000a04010e010000000000000000000000000000000000000000000000180002010005000000000000000000000000000000000000000000000000000008100101000000000000000000000000000000000000000000000000000000000e100002210000000000000000000000000000000000000000000000000000000e1008000001000000000000000000000000000000000000000000000000000002000201000100000000000000000000000000000000000000000000000000000e0000026106000000000000000000000000000000000000000000000000000004100200000000000000000000000000000000000000000000000000000000000a0000000201010c01000000000000000000000000000000000000000000000002020002000000000000000000000000000000000000000000000000000000000a000000010101010100000000000000000000000000000000000000000000000c80200001ffffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffff0000000000000000000000000000000e004000010200000000000000000000000000000000000000000000000000001a842000020000000000000000000000000000000000000000000000000000000e004000210100000000000000000000000000000000000000000000000000000e008000610500000000000000000000000000000000000000000000000000001a044200020000000000000000000000000000000000000000000000000000001a842000040000000000000000000000000000000000000000000000000000000e800000000200000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e00400000000000000000000000000000000000000000000000000000e0001006005000000000000000000000000000000000000000000000000000002020001000000000000000000000000000000000000000000000000000000000e400000600400000000000000000000000000000000000000000000000000000c00080201ffffffffffffffff000000000000000000000000000000000000000c0008020000000000000000000000000000000000000000000000000000000004100800000000000000000000000000000000000000000000000000000000000a000000085a012101000000000000000000000000000000000000000000000002004400003f00000000000000000000000000000000000000000000000000000c00820001e0ffffffffffffffffffffffffffffff00000000000000000000000c00820000ffffffffffffffffffffffffffffffff00000000000000000000001a044200020000000000000000000000000000000000000000000000000000000e0020002102000000000000000000000000000000000000000000000000000002024100000000000000000000000000000000000000000000000000000000000c008200010100000000000000000000000000000000000000000000000000000c0082000000000000000000000000000000000000000000000000000000000004840000000000000000000000000000000000000000000000000000000000000a000000022d012b0100000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c0082000000000000000000000000000000000000000000000000000000000004040800000000000000000000000000000000000000000000000000000000000a0000000879012f01000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a0000000a7901310100000000000000000000000000000000000000000000000e0001002002000000000000000000000000000000000000000000000000000002101000000000000000000000000000000000000000000000000000000000000e000400600300000000000000000000000000000000000000000000000000000e800000600100000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000e004000610300000000000000000000000000000000000000000000000000000e0080006104000000000000000000000000000000000000000000000000000002042200000000000000000000000000000000000000000000000000000000000e0000016105000000000000000000000000000000000000000000000000000004020400000000000000000000000000000000000000000000000000000000000a0000000899013c0100000000000000000000000000000000000000000000000e0020006101000000000000000000000000000000000000000000000000000002802000002000000000000000000000000000000000000000000000000000000e800000600200000000000000000000000000000000000000000000000000000c000401010000000000000000000000000000000000000000000000000000000c0004010000000000000000000000000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000000000000a000000045701430100000000000000000000000000000000000000000000000e0004006005000000000000000000000000000000000000000000000000000002101200000000000000000000000000000000000000000000000000000000000700000003ac03000000000000000000000000000000000000000000000000000e002000610200000000000000000000000000000000000000000000000000000e0040006105000000000000000000000000000000000000000000000000000002842000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000e000001610500000000000000000000000000000000000000000000000000000e008000610400000000000000000000000000000000000000000000000000000e00200061030000000000000000000000000000000000000000000000000000020004010020000000000000000000000000000000000000000000000000000004900000000000000000000000000000000000000000000000000000000000000a0000000243015001000000000000000000000000000000000000000000000004900000000000000000000000000000000000000000000000000000000000000a000000065701520100000000000000000000000000000000000000000000000e0040006102000000000000000000000000000000000000000000000000000002842000000000000000000000000000000000000000000000000000000000000c401000010000000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000e001000610100000000000000000000000000000000000000000000000000000e000000e104000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c80200000000000000000000000000000717b484e00000000000000000000000e800000200000000000000000000000000000000000000000000000000000000c401000014100000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000c802000010400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000c802000012400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000002028000000000000000000000000000000000000000000000000000000000000a000000016e016e010000000000000000000000000000000000000000000000021080000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000a0000000a6e019e010000000000000000000000000000000000000000000000180002010005000000000000000000000000000000000000000000000000000008100101000000000000000000000000000000000000000000000000000000000e100002210000000000000000000000000000000000000000000000000000000e1008000001000000000000000000000000000000000000000000000000000002000201000100000000000000000000000000000000000000000000000000000c000802010100000000000000000000000000000000000000000000000000000c0008020000000000000000000000000000000000000000000000000000000004100200000000000000000000000000000000000000000000000000000000000a000000026b017701000000000000000000000000000000000000000000000002020002000000000000000000000000000000000000000000000000000000000a000000016b016b0100000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c80200000000000000000000000000000717b484e00000000000000000000000e800000200000000000000000000000000000000000000000000000000000000c401000014100000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000c802000010400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000c802000012400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000c000401010000000000000000000000000000000000000000000000000000000c000401000000000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c004100000000000000000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e000400600500000000000000000000000000000000000000000000000000000a000000018f018f010000000000000000000000000000000000000000000000022000010000000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a0000000a8f01aa010000000000000000000000000000000000000000000000180084000005000000000000000000000000000000000000000000000000000008088100000000000000000000000000000000000000000000000000000000000e080002210000000000000000000000000000000000000000000000000000000e080800000100000000000000000000000000000000000000000000000000000200040200010000000000000000000000000000000000000000000000000000020280000000000000000000000000000000000000000000000000000000000004200400000000000000000000000000000000000000000000000000000000000a000000028c01970100000000000000000000000000000000000000000000000e008000610500000000000000000000000000000000000000000000000000000a000000018c018c0100000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000e000000e104000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c80200001ffffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffff0000000000000000000000000000000e004000010200000000000000000000000000000000000000000000000000001a842000020000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000ffffffff00000000000000000000000e008000210100000000000000000000000000000000000000000000000000001a084100020000000000000000000000000000000000000000000000000000001a842000040000000000000000000000000000000000000000000000000000000e800000000200000000000000000000000000000000000000000000000000000e000000e104000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c80200001ffffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffff0000000000000000000000000000000e004000010200000000000000000000000000000000000000000000000000001a842000020000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000ffffffff00000000000000000000000e008000210100000000000000000000000000000000000000000000000000001a084100020000000000000000000000000000000000000000000000000000001a842000040000000000000000000000000000000000000000000000000000000e800000000200000000000000000000000000000000000000000000000000000e000000e104000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e002000000000000000000000000000000000000000000000000000002014000000000000000000000000000000000000000000000000000000000000c8020000180ffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffffffffffff00000000000000000000000e0001006003000000000000000000000000000000000000000000000000000002840001000000000000000000000000000000000000000000000000000000000c000802012000000000000000000000000000000000000000000000000000000c000802000000000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000000000800000000000000000000000020420000000000000000000000000000000000000000000000000000000000004100800000000000000000000000000000000000000000000000000000000000a00000002c601c501000000000000000000000000000000000000000000000002082000000000000000000000000000000000000000000000000000000000001a10010202000000000000000000000000000000000000000000000000000000020400010000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000a00000008cb01ca010000000000000000000000000000000000000000000000020800010000000000000000000000000000000000000000000000000000000004200100000000000000000000000000000000000000000000000000000000000a00000004ce01cd010000000000000000000000000000000000000000000000020200010000000000000000000000000000000000000000000000000000000004100000000000000000000000000000000000000000000000000000000000000a0000000afa01d00100000000000000000000000000000000000000000000000e004000210400000000000000000000000000000000000000000000000000000c80200001ffffffffffffffff000000000000000000000000000000000000000c8020000000000000000000000000000000000000000000000000000000000004840000000000000000000000000000000000000000000000000000000000000a00000006d501ff0100000000000000000000000000000000000000000000000e0001006002000000000000000000000000000000000000000000000000000002000101009f00000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000080000000000000000000000002040002000000000000000000000000000000000000000000000000000000000e0020006103000000000000000000000000000000000000000000000000000004900000000000000000000000000000000000000000000000000000000000000a00000002e001df01000000000000000000000000000000000000000000000002080002000000000000000000000000000000000000000000000000000000000e000800600100000000000000000000000000000000000000000000000000000e002000610300000000000000000000000000000000000000000000000000001a020102020000000000000000000000000000000000000000000000000000001a10210002000000000000000000000000000000000000000000000000000000020400010000000000000000000000000000000000000000000000000000000004020800000000000000000000000000000000000000000000000000000000000a00000008e801e701000000000000000000000000000000000000000000000002080001000000000000000000000000000000000000000000000000000000001a0228000800000000000000000000000000000000000000000000000000000004020100000000000000000000000000000000000000000000000000000000000a00000004ec01eb0100000000000000000000000000000000000000000000000e0000016101000000000000000000000000000000000000000000000000000004100000000000000000000000000000000000000000000000000000000000000a0000000aee01040200000000000000000000000000000000000000000000000e0020006102000000000000000000000000000000000000000000000000000002801000008000000000000000000000000000000000000000000000000000000700000003ac03000000000000000000000000000000000000000000000000000e002000610200000000000000000000000000000000000000000000000000000280400000a00000000000000000000000000000000000000000000000000000020120000000000000000000000000000000000000000000000000000000000002041000000000000000000000000000000000000000000000000000000000000e0040006103000000000000000000000000000000000000000000000000000007000000031901000000000000000000000000000000000000000000000000000a000000020902f80100000000000000000000000000000000000000000000000e000000e102000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000e000000e102000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000e000000e102000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000e000000e102000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e102000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e00000000000000000000000000000000000000000000000000000001a4040000201000000000000000000000000000000000000000000000000000018408000020100000000000000000000000000000000000000000000000000001a002200027f000000000000000000000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000000000000a0000000412021102000000000000000000000000000000000000000000000002082000000000000000000000000000000000000000000000000000000000000c000802011f00000000000000000000000000000000000000000000000000000c000802000000000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000e000200600100000000000000000000000000000000000000000000000000000c008200010100000000000000000000000000000000000000000000000000000c00820000000000000000000000000000000000000000000000000000000000020800010000000000000000000000000000000000000000000000000000000004020800000000000000000000000000000000000000000000000000000000000a000000081d021c0200000000000000000000000000000000000000000000000e0000016101000000000000000000000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000000000000a0000000a20021f0200000000000000000000000000000000000000000000000e008000610100000000000000000000000000000000000000000000000000001a0844000800000000000000000000000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000000000000a0000000a26022302000000000000000000000000000000000000000000000002021000000000000000000000000000000000000000000000000000000000000e000000e100000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c80200000000000000000000000000000717b484e00000000000000000000000e800000200000000000000000000000000000000000000000000000000000000c401000012200000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000c802000010400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000c802000012400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000c000401010000000000000000000000000000000000000000000000000000000c000401000000000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c004100000000000000000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e000400600100000000000000000000000000000000000000000000000000000a000000013c023c020000000000000000000000000000000000000000000000022000010000000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a0000000a3c0246020000000000000000000000000000000000000000000000180084000005000000000000000000000000000000000000000000000000000008088100000000000000000000000000000000000000000000000000000000000e080002210000000000000000000000000000000000000000000000000000000e080800000100000000000000000000000000000000000000000000000000000200040200010000000000000000000000000000000000000000000000000000020280000000000000000000000000000000000000000000000000000000000004200400000000000000000000000000000000000000000000000000000000000a000000023902440200000000000000000000000000000000000000000000000e008000610100000000000000000000000000000000000000000000000000000a000000013902390200000000000000000000000000000000000000000000000c80200001ffffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffff0000000000000000000000000000000e004000010200000000000000000000000000000000000000000000000000001a842000020000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000ffffffff00000000000000000000000e008000210100000000000000000000000000000000000000000000000000001a084100020000000000000000000000000000000000000000000000000000001a842000040000000000000000000000000000000000000000000000000000000e800000000200000000000000000000000000000000000000000000000000000e000000e100000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c004100011f00000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000004010100000000000000000000000000000000000000000000000000000000000a0000000856026f0200000000000000000000000000000000000000000000000c000401012000000000000000000000000000000000000000000000000000000c000401000000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000000000000000000000000000000000020480000000000000000000000000000000000000000000000000000000000004020400000000000000000000000000000000000000000000000000000000000a000000025f025d02000000000000000000000000000000000000000000000002802000001f000000000000000000000000000000000000000000000000000018808000020500000000000000000000000000000000000000000000000000000e0001002000000000000000000000000000000000000000000000000000000003040000010000000000000000000000000000000000000000000000000000000900400000000000000000000000000000000000000000000000000000000000020422000000000000000000000000000000000000000000000000000000000002408000001f00000000000000000000000000000000000000000000000000001800820002050000000000000000000000000000000000000000000000000000020442000000000000000000000000000000000000000000000000000000000004020100000000000000000000000000000000000000000000000000000000000a0000000c6f026802000000000000000000000000000000000000000000000005028000010000000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000502020000000000000000000000000000000000000000000000000000000000028020000001000000000000000000000000000000000000000000000000000004020100000000000000000000000000000000000000000000000000000000000a0000000268026f02000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000e000000e00500000000000000000000000000000000000000000000000000000e400000600300000000000000000000000000000000000000000000000000000700000003ac030000000000000000000000000000000000000000000000000002014000000000000000000000000000000000000000000000000000000000000c80200001ffffffffffffffff000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e0001006001000000000000000000000000000000000000000000000000000004840000000000000000000000000000000000000000000000000000000000000a00000008df02790200000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e80000060060000000000000000000000000000000000000000000000000000050210000100000000000000000000000000000000000000000000000000000007000000030b02000000000000000000000000000000000000000000000000000a00000002fe027f0200000000000000000000000000000000000000000000000e0020006101000000000000000000000000000000000000000000000000000007000000035202000000000000000000000000000000000000000000000000000e000002610100000000000000000000000000000000000000000000000000000c802000011f00000000000000000000000000000000000000000000000000000c8020000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000000000a000000088602c40200000000000000000000000000000000000000000000000c80200001e0ffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffffffffffff00000000000000000000001aa04000020000000000000000000000000000000000000000000000000000000c000401010000000000000000000000000000000000000000000000000000000c000401000000000000000000000000000000000000000000000000000000000e0004002000000000000000000000000000000000000000000000000000000003100000010000000000000000000000000000000000000000000000000000000c008200012000000000000000000000000000000000000000000000000000000c0082000000000000000000000000000000000000000000000000000000000009002000000000000000000000000000000000000000000000000000000000000e000002610300000000000000000000000000000000000000000000000000000e0001006002000000000000000000000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000000000000a00000004a402940200000000000000000000000000000000000000000000000e000400600400000000000000000000000000000000000000000000000000000e000200600500000000000000000000000000000000000000000000000000000e800000600600000000000000000000000000000000000000000000000000000e0020006103000000000000000000000000000000000000000000000000000002881000000000000000000000000000000000000000000000000000000000000700000003ac03000000000000000000000000000000000000000000000000000e000001610400000000000000000000000000000000000000000000000000000e008000610500000000000000000000000000000000000000000000000000000e0020006106000000000000000000000000000000000000000000000000000005420000000000000000000000000000000000000000000000000000000000000200820000200000000000000000000000000000000000000000000000000000028020000001000000000000000000000000000000000000000000000000000002000401002000000000000000000000000000000000000000000000000000000e0040006102000000000000000000000000000000000000000000000000000004100100000000000000000000000000000000000000000000000000000000000a000000029402a40200000000000000000000000000000000000000000000000e004000610100000000000000000000000000000000000000000000000000000e0000016102000000000000000000000000000000000000000000000000000004100100000000000000000000000000000000000000000000000000000000000a00000002a802c00200000000000000000000000000000000000000000000000e8000006006000000000000000000000000000000000000000000000000000018002100000300000000000000000000000000000000000000000000000000001a80200002f800000000000000000000000000000000000000000000000000000c00040101ffffffffffffffffffffffffffffffff00000000000000000000000c00040100ffffffffffffffffffffffffffffffff00000000000000000000000c00410001ff00000000000000000000000000000000000000000000000000000c004100000000000000000000000000000000000000000000000000000000000c000802010000000000000000000000000000000000000000000000000000000c0008020000000000000000000000000000000000000000000000000000000004020100000000000000000000000000000000000000000000000000000000000a00000008b402b302000000000000000000000000000000000000000000000018020402020000000000000000000000000000000000000000000000000000000e000800600400000000000000000000000000000000000000000000000000000e0020006103000000000000000000000000000000000000000000000000000002881000000000000000000000000000000000000000000000000000000000000e000400600500000000000000000000000000000000000000000000000000000700000003ac03000000000000000000000000000000000000000000000000000e002000610500000000000000000000000000000000000000000000000000000e004000610400000000000000000000000000000000000000000000000000001a842000080000000000000000000000000000000000000000000000000000001a812000020000000000000000000000000000000000000000000000000000000e0040006106000000000000000000000000000000000000000000000000000005840000000000000000000000000000000000000000000000000000000000000e004000610100000000000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c8020000000000000000000000000000000000000000000000000000000000018004100000100000000000000000000000000000000000000000000000000000a00000001d902d90200000000000000000000000000000000000000000000000e002000610600000000000000000000000000000000000000000000000000000e0040006103000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000a00000004cc02c802000000000000000000000000000000000000000000000002001100002000000000000000000000000000000000000000000000000000000700000003ac03000000000000000000000000000000000000000000000000000e00000261010000000000000000000000000000000000000000000000000000020120000000000000000000000000000000000000000000000000000000000018008800000300000000000000000000000000000000000000000000000000000c00410001ffffffffffffffffffffffffffffffff00000000000000000000000c00410000ffffffffffffffffffffffffffffffff00000000000000000000000c00040101ff00000000000000000000000000000000000000000000000000000c0004010000000000000000000000000000000000000000000000000000000004080400000000000000000000000000000000000000000000000000000000000a00000008d502d302000000000000000000000000000000000000000000000018088100020000000000000000000000000000000000000000000000000000000e000200600600000000000000000000000000000000000000000000000000000e008000610600000000000000000000000000000000000000000000000000001a084100080000000000000000000000000000000000000000000000000000001a0241000200000000000000000000000000000000000000000000000000000018002800000100000000000000000000000000000000000000000000000000001a842000040000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000005840000000000000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c80200000000000000000000000000000717b484e00000000000000000000000e800000200000000000000000000000000000000000000000000000000000000c401000014100000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000c802000010400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000c802000012400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000002028000000000000000000000000000000000000000000000000000000000000a00000001f302f3020000000000000000000000000000000000000000000000021080000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000a0000000af30200030000000000000000000000000000000000000000000000180002010005000000000000000000000000000000000000000000000000000008100101000000000000000000000000000000000000000000000000000000000e100002210000000000000000000000000000000000000000000000000000000e1008000001000000000000000000000000000000000000000000000000000002000201000100000000000000000000000000000000000000000000000000000c000802010100000000000000000000000000000000000000000000000000000c0008020000000000000000000000000000000000000000000000000000000004100200000000000000000000000000000000000000000000000000000000000a00000002f002fc02000000000000000000000000000000000000000000000002020002000000000000000000000000000000000000000000000000000000000a00000001f002f00200000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c80200001ffffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffff0000000000000000000000000000000e004000010200000000000000000000000000000000000000000000000000001a842000020000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000ffffffff00000000000000000000000e008000210100000000000000000000000000000000000000000000000000001a084100020000000000000000000000000000000000000000000000000000001a842000040000000000000000000000000000000000000000000000000000000e800000000200000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c802000011f00000000000000000000000000000000000000000000000000000c8020000000000000000000000000000000000000000000000000000000000004810000000000000000000000000000000000000000000000000000000000000a000000081003220300000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e80000020000000000000000000000000000000000000000000000000000000030200000100000000000000000000000000000000000000000000000000000002402000001f0000000000000000000000000000000000000000000000000000188020000205000000000000000000000000000000000000000000000000000009004000000000000000000000000000000000000000000000000000000000000284200000000000000000000000000000000000000000000000000000000000020041000005000000000000000000000000000000000000000000000000000004840000000000000000000000000000000000000000000000000000000000000a0000000c22031b03000000000000000000000000000000000000000000000005048000010000000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000504020000000000000000000000000000000000000000000000000000000000020041000001000000000000000000000000000000000000000000000000000004840000000000000000000000000000000000000000000000000000000000000a000000021b032203000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000e000000e00000000000000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e80000060010000000000000000000000000000000000000000000000000000050210000100000000000000000000000000000000000000000000000000000007000000030b02000000000000000000000000000000000000000000000000000a0000000243032a03000000000000000000000000000000000000000000000007000000030c03000000000000000000000000000000000000000000000000000c802000010301000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e0040006101000000000000000000000000000000000000000000000000000005840000000000000000000000000000000000000000000000000000000000000e0001002000000000000000000000000000000000000000000000000000000003040000010000000000000000000000000000000000000000000000000000000c802000016161616161616161616161616161616100000000000000000000000c802000006161616161616161616161616161616200000000000000000000000900400000000000000000000000000000000000000000000000000000000000058400000000000000000000000000000000000000000000000000000000000002002100000100000000000000000000000000000000000000000000000000000c008200016161616161616161616161616161616100000000000000000000000c008200006161616161616161616161616161616100000000000000000000000502020000000000000000000000000000000000000000000000000000000000020021000002000000000000000000000000000000000000000000000000000005020200000000000000000000000000000000000000000000000000000000000200210000030000000000000000000000000000000000000000000000000000050202000000000000000000000000000000000000000000000000000000000002002100000400000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000064000000000000000000000005020100000000000000000000000000000000000000000000000000000000000e000000e100000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000e000000e100000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e00100000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e8000006002000000000000000000000000000000000000000000000000000005022000010000000000000000000000000000000000000000000000000000001a808000020100000000000000000000000000000000000000000000000000000e8000006001000000000000000000000000000000000000000000000000000018800001020100000000000000000000000000000000000000000000000000001a002400027f000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a0000000451035003000000000000000000000000000000000000000000000002102000000000000000000000000000000000000000000000000000000000000c004100011f00000000000000000000000000000000000000000000000000000c004100000000000000000000000000000000000000000000000000000000000c000802010100000000000000000000000000000000000000000000000000000c00080200000000000000000000000000000000000000000000000000000000022000010000000000000000000000000000000000000000000000000000000004020100000000000000000000000000000000000000000000000000000000000a000000085903580300000000000000000000000000000000000000000000000e0000016102000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a0000000a5c035b0300000000000000000000000000000000000000000000000e000002610200000000000000000000000000000000000000000000000000001a2044000800000000000000000000000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000000000000a0000000a80035f0300000000000000000000000000000000000000000000000e8000002004000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a0000000462036a0300000000000000000000000000000000000000000000000c8020000100ffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffffffffffff00000000000000000000000e004000610100000000000000000000000000000000000000000000000000001a842000020000000000000000000000000000000000000000000000000000000e800000200500000000000000000000000000000000000000000000000000000c00820001c000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000a000000017d037d0300000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c004100000000000000000000000000000000000000000000000000000000000e0001002000000000000000000000000000000000000000000000000000000003040000010000000000000000000000000000000000000000000000000000000c00820001a000000000000000000000000000000000000000000000000000000c00820000000000000000000000000000000000000000000000000000000000090000010000000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000a000000047d03730300000000000000000000000000000000000000000000000c000802012000000000000000000000000000000000000000000000000000000c00080200000000000000000000000000000000000000000000000000000000080888000000000000000000000000000000000000000000000000000000000005100002010000000000000000000000000000000000000000000000000000000e080800200000000000000000000000000000000000000000000000000000000200810000c000000000000000000000000000000000000000000000000000000200040100010000000000000000000000000000000000000000000000000000020041000020000000000000000000000000000000000000000000000000000004840000000000000000000000000000000000000000000000000000000000000a0000000273037d03000000000000000000000000000000000000000000000002081000000000000000000000000000000000000000000000000000000000000e000000e101000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c80200000000000000000000000000000717b484e00000000000000000000000e800000200000000000000000000000000000000000000000000000000000000c401000012200000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000c802000010400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000c802000012400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000c000401010000000000000000000000000000000000000000000000000000000c000401000000000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c004100000000000000000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e000400600200000000000000000000000000000000000000000000000000000a00000001960396030000000000000000000000000000000000000000000000022000010000000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a0000000a9603a0030000000000000000000000000000000000000000000000180084000005000000000000000000000000000000000000000000000000000008088100000000000000000000000000000000000000000000000000000000000e080002210000000000000000000000000000000000000000000000000000000e080800000100000000000000000000000000000000000000000000000000000200040200010000000000000000000000000000000000000000000000000000020280000000000000000000000000000000000000000000000000000000000004200400000000000000000000000000000000000000000000000000000000000a0000000293039e0300000000000000000000000000000000000000000000000e008000610200000000000000000000000000000000000000000000000000000a000000019303930300000000000000000000000000000000000000000000000c80200001ffffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffff0000000000000000000000000000000e004000010200000000000000000000000000000000000000000000000000001a842000020000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000ffffffff00000000000000000000000e008000210100000000000000000000000000000000000000000000000000001a084100020000000000000000000000000000000000000000000000000000001a842000040000000000000000000000000000000000000000000000000000000e800000000200000000000000000000000000000000000000000000000000000e000000e101000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c80200001e0ffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffffffffffff00000000000000000000001a812000020000000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000008024100000000000000000000000000000000000000000000000000000000001a402000021f000000000000000000000000000000000000000000000000000018808000000300000000000000000000000000000000000000000000000000000e0420002100000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a00000004be03b70300000000000000000000000000000000000000000000000c000401010001000000000000000000000000000000000000000000000000000c0004010000000000000000000000000000000000000000000000000000000004100201000000000000000000000000000000000000000000000000000000000e04400021010000000000000000000000000000000000000000000000000000181041000200000000000000000000000000000000000000000000000000000018882000000000000000000000000000000000000000000000000000000000001a84200004000000000000000000000000000000000000000000000000000000020210000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000e000000e00100000000000000000000000000000000000000000000000000000c00410001e0ffffffffffffffffffffffffffffff00000000000000000000000c00410000ffffffffffffffffffffffffffffffff00000000000000000000001a024100020000000000000000000000000000000000000000000000000000000c008200012000000000000000000000000000000000000000000000000000000c0082000000000000000000000000000000000000000000000000000000000008044200000000000000000000000000000000000000000000000000000000001a802000021f000000000000000000000000000000000000000000000000000018802000000300000000000000000000000000000000000000000000000000000c008200010001000000000000000000000000000000000000000000000000000c0082000000000000000000000000000000000000000000000000000000000004888000000000000000000000000000000000000000000000000000000000000e000200600200000000000000000000000000000000000000000000000000000c00080201ffffffffffffffffffffffffffffffff00000000000000000000000c00080200ffffffffffffffffffffffffffffffff0000000000000000000000020180000000000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000a00000004d803d20300000000000000000000000000000000000000000000000e0080006102000000000000000000000000000000000000000000000000000018088800000000000000000000000000000000000000000000000000000000000e040001210000000000000000000000000000000000000000000000000000001a1082000200000000000000000000000000000000000000000000000000000018420001020000000000000000000000000000000000000000000000000000001a108200040000000000000000000000000000000000000000000000000000000e0400012101000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000a00000004e403db0300000000000000000000000000000000000000000000000e000800600100000000000000000000000000000000000000000000000000000e0000026102000000000000000000000000000000000000000000000000000018600002000000000000000000000000000000000000000000000000000000000e000800600200000000000000000000000000000000000000000000000000000e0000026101000000000000000000000000000000000000000000000000000018022800020000000000000000000000000000000000000000000000000000001a902000020000000000000000000000000000000000000000000000000000000e000001610200000000000000000000000000000000000000000000000000001a900001040000000000000000000000000000000000000000000000000000000e040400200100000000000000000000000000000000000000000000000000000e040200200000000000000000000000000000000000000000000000000000000e000000e10100000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000", + "deployedBytecode": "0x0e000000e00500000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000e000200200000000000000000000000000000000000000000000000000000000e000200200100000000000000000000000000000000000000000000000000000e000200200300000000000000000000000000000000000000000000000000000c802000017c7ee91ae51bb0ed9b9e413333bf74f700000000000000000000000c80200000b75ee1170f6b7c3ce4583c65d82198c400000000000000000000000e8000006001000000000000000000000000000000000000000000000000000005022000010000000000000000000000000000000000000000000000000000000e800000600500000000000000000000000000000000000000000000000000000c802000010000000000000000ffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffffffffffff00000000000000000000000e004000010000000000000000000000000000000000000000000000000000000e000100600200000000000000000000000000000000000000000000000000001a844000020000000000000000000000000000000000000000000000000000000c000401010000000000000000010000000000000000000000000000000000000c000401000000000000000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c8020000000000000000000000000000000000000000000000000000000000002020002000000000000000000000000000000000000000000000000000000000e000100600300000000000000000000000000000000000000000000000000000e0004006006000000000000000000000000000000000000000000000000000004040400000000000000000000000000000000000000000000000000000000000a000000041a001900000000000000000000000000000000000000000000000002080002000000000000000000000000000000000000000000000000000000000e0008006004000000000000000000000000000000000000000000000000000002020002000000000000000000000000000000000000000000000000000000000e0000016105000000000000000000000000000000000000000000000000000004100000000000000000000000000000000000000000000000000000000000000a0000000420001f00000000000000000000000000000000000000000000000002080002000000000000000000000000000000000000000000000000000000000e0040006106000000000000000000000000000000000000000000000000000004900000000000000000000000000000000000000000000000000000000000000a000000042300280000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c8020000000000000000000000000000083bbcae300000000000000000000000e0080006103000000000000000000000000000000000000000000000000000004080100000000000000000000000000000000000000000000000000000000000a00000004dd002800000000000000000000000000000000000000000000000004100000000000000000000000000000000000000000000000000000000000000a000000042a002f0000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c80200000000000000000000000000000c9a6bc8e00000000000000000000000e0080006102000000000000000000000000000000000000000000000000000004080100000000000000000000000000000000000000000000000000000000000a00000002dd002f0000000000000000000000000000000000000000000000000e002000610400000000000000000000000000000000000000000000000000001aa04000020000000000000000000000000000000000000000000000000000000c802000018000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000200200000000000000000000000000000000000000000000000000000e002000010000000000000000000000000000000000000000000000000000001a80200002ffffffff00000000000000000000000000000000000000000000001a004100020100000000000000000000000000000000000000000000000000000c008200010100000000000000000000000000000000000000000000000000000c0082000000000000000000000000000000000000000000000000000000000004040200000000000000000000000000000000000000000000000000000000000a000000043b008300000000000000000000000000000000000000000000000002804000001f00000000000000000000000000000000000000000000000000001a00410002e0ffffff010000000000000000000000000000000000000000000002004100008000000000000000000000000000000000000000000000000000000e000100200200000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000001a804000021f00000000000000000000000000000000000000000000000000000e00010060060000000000000000000000000000000000000000000000000000188000010205000000000000000000000000000000000000000000000000000004100000000000000000000000000000000000000000000000000000000000000a000000044f004600000000000000000000000000000000000000000000000018000202000500000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000008204100000000000000000000000000000000000000000000000000000000000e040002010100000000000000000000000000000000000000000000000000000e04080020040000000000000000000000000000000000000000000000000000020082000001000000000000000000000000000000000000000000000000000004080400000000000000000000000000000000000000000000000000000000000a0000000246004f0000000000000000000000000000000000000000000000000e0040006106000000000000000000000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000000000000a0000000474005200000000000000000000000000000000000000000000000018000101000300000000000000000000000000000000000000000000000000000c004100010001000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000004044400000000000000000000000000000000000000000000000000000000000c00820001ffffffffffffffffffffffffffffffff00000000000000000000000c00820000ffffffffffffffffffffffffffffffff00000000000000000000000e000200600500000000000000000000000000000000000000000000000000000c00080201ff00000000000000000000000000000000000000000000000000000c000802000000000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000e0002006006000000000000000000000000000000000000000000000000000004040800000000000000000000000000000000000000000000000000000000000a000000086500600000000000000000000000000000000000000000000000000e000200600400000000000000000000000000000000000000000000000000000e0080006105000000000000000000000000000000000000000000000000000018044200000000000000000000000000000000000000000000000000000000000e008000610400000000000000000000000000000000000000000000000000000e000100600600000000000000000000000000000000000000000000000000001a80400002e0ffffff000000000000000000000000000000000000000000000004100800000000000000000000000000000000000000000000000000000000000a000000086a00680000000000000000000000000000000000000000000000000e0080006105000000000000000000000000000000000000000000000000000018108200020000000000000000000000000000000000000000000000000000000c000401012000000000000000000000000000000000000000000000000000000c0004010000000000000000000000000000000000000000000000000000000008044400000000000000000000000000000000000000000000000000000000000e040001210400000000000000000000000000000000000000000000000000001a108200020000000000000000000000000000000000000000000000000000000e040001010100000000000000000000000000000000000000000000000000000e000002610600000000000000000000000000000000000000000000000000001a100801020000000000000000000000000000000000000000000000000000001a108200040000000000000000000000000000000000000000000000000000000e0402002004000000000000000000000000000000000000000000000000000002801000008000000000000000000000000000000000000000000000000000000700000003b601000000000000000000000000000000000000000000000000000a00000002e8007700000000000000000000000000000000000000000000000007000000037002000000000000000000000000000000000000000000000000000a00000002ea007900000000000000000000000000000000000000000000000007000000032303000000000000000000000000000000000000000000000000000a00000002ec007b0000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e0040006101000000000000000000000000000000000000000000000000000005840000000000000000000000000000000000000000000000000000000000000a00000001db00db0000000000000000000000000000000000000000000000000e000200600600000000000000000000000000000000000000000000000000001a80200002fcffffff000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000a00000004e300870000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000ffffffff00000000000000000000000e002000010100000000000000000000000000000000000000000000000000000e000100600500000000000000000000000000000000000000000000000000001a022100020000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c004100000000000000000000000000001732aecf000000000000000000000004020100000000000000000000000000000000000000000000000000000000000a000000049000e300000000000000000000000000000000000000000000000007000000034503000000000000000000000000000000000000000000000000000a00000002ee009200000000000000000000000000000000000000000000000002402000001f00000000000000000000000000000000000000000000000000000c00410001e0ffffffffffffffffffffffffffffff00000000000000000000000c00410000ffffffffffffffffffffffffffffffff00000000000000000000000e000100600400000000000000000000000000000000000000000000000000001a020102020000000000000000000000000000000000000000000000000000000c8020000180ffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffffffffffff000000000000000000000002a02000000000000000000000000000000000000000000000000000000000000c004100017fffffffffffffff000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000004020100000000000000000000000000000000000000000000000000000000000a00000008f0009e0000000000000000000000000000000000000000000000000e000800200200000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000008202100000000000000000000000000000000000000000000000000000000000e000100600600000000000000000000000000000000000000000000000000000e020100200000000000000000000000000000000000000000000000000000000e008000210400000000000000000000000000000000000000000000000000000e020200200100000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000002002800004000000000000000000000000000000000000000000000000000000e0002006005000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a00000004c000ac00000000000000000000000000000000000000000000000002848000000000000000000000000000000000000000000000000000000000000e00000161060000000000000000000000000000000000000000000000000000080884000000000000000000000000000000000000000000000000000000000008040401000000000000000000000000000000000000000000000000000000000e100001210500000000000000000000000000000000000000000000000000000e0804002000000000000000000000000000000000000000000000000000000002004100002000000000000000000000000000000000000000000000000000000e0080006105000000000000000000000000000000000000000000000000000004040200000000000000000000000000000000000000000000000000000000000a00000002ac00b60000000000000000000000000000000000000000000000000e0080006105000000000000000000000000000000000000000000000000000004040200000000000000000000000000000000000000000000000000000000000a00000006c000b90000000000000000000000000000000000000000000000000e0040006105000000000000000000000000000000000000000000000000000002842000000000000000000000000000000000000000000000000000000000000c401000010000000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000e000800600300000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000e000002610300000000000000000000000000000000000000000000000000000e0020006105000000000000000000000000000000000000000000000000000002802000005f00000000000000000000000000000000000000000000000000000e004000610400000000000000000000000000000000000000000000000000001a024100020000000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000e0001000000000000000000000000000000000000000000000000000000000018002100020500000000000000000000000000000000000000000000000000000e000200600400000000000000000000000000000000000000000000000000000e8000006005000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000a00000004d900cc0000000000000000000000000000000000000000000000001800020100050000000000000000000000000000000000000000000000000000022020000000000000000000000000000000000000000000000000000000000002020402000000000000000000000000000000000000000000000000000000000e00400061060000000000000000000000000000000000000000000000000000081001010000000000000000000000000000000000000000000000000000000008200102000000000000000000000000000000000000000000000000000000000e200002210000000000000000000000000000000000000000000000000000000e10080000010000000000000000000000000000000000000000000000000000020200020000000000000000000000000000000000000000000000000000000002008200000100000000000000000000000000000000000000000000000000000e0020006105000000000000000000000000000000000000000000000000000004880000000000000000000000000000000000000000000000000000000000000a00000002cc00d90000000000000000000000000000000000000000000000000e0020006104000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000c004100010100000000000000000000000000000000000000000000000000000c004100000000000000000000000000000000000000000000000000000000000e000100000000000000000000000000000000000000000000000000000000000e800000000100000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c80200000000000000000000000000000717b484e00000000000000000000000e800000200000000000000000000000000000000000000000000000000000000c401000014100000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000c802000010400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000c802000012400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000002028000000000000000000000000000000000000000000000000000000000000a00000001040104010000000000000000000000000000000000000000000000021080000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000a0000000a04010e010000000000000000000000000000000000000000000000180002010005000000000000000000000000000000000000000000000000000008100101000000000000000000000000000000000000000000000000000000000e100002210000000000000000000000000000000000000000000000000000000e1008000001000000000000000000000000000000000000000000000000000002000201000100000000000000000000000000000000000000000000000000000e0000026106000000000000000000000000000000000000000000000000000004100200000000000000000000000000000000000000000000000000000000000a0000000201010c01000000000000000000000000000000000000000000000002020002000000000000000000000000000000000000000000000000000000000a000000010101010100000000000000000000000000000000000000000000000c80200001ffffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffff0000000000000000000000000000000e004000010200000000000000000000000000000000000000000000000000001a842000020000000000000000000000000000000000000000000000000000000e004000210100000000000000000000000000000000000000000000000000000e008000610500000000000000000000000000000000000000000000000000001a044200020000000000000000000000000000000000000000000000000000001a842000040000000000000000000000000000000000000000000000000000000e800000000200000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e00400000000000000000000000000000000000000000000000000000e0001006005000000000000000000000000000000000000000000000000000002020001000000000000000000000000000000000000000000000000000000000e400000600400000000000000000000000000000000000000000000000000000c00080201ffffffffffffffff000000000000000000000000000000000000000c0008020000000000000000000000000000000000000000000000000000000004100800000000000000000000000000000000000000000000000000000000000a000000085a012101000000000000000000000000000000000000000000000002004400003f00000000000000000000000000000000000000000000000000000c00820001e0ffffffffffffffffffffffffffffff00000000000000000000000c00820000ffffffffffffffffffffffffffffffff00000000000000000000001a044200020000000000000000000000000000000000000000000000000000000e0020002102000000000000000000000000000000000000000000000000000002024100000000000000000000000000000000000000000000000000000000000c008200010100000000000000000000000000000000000000000000000000000c0082000000000000000000000000000000000000000000000000000000000004840000000000000000000000000000000000000000000000000000000000000a000000022d012b0100000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c0082000000000000000000000000000000000000000000000000000000000004040800000000000000000000000000000000000000000000000000000000000a0000000879012f01000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a0000000a7901310100000000000000000000000000000000000000000000000e0001002002000000000000000000000000000000000000000000000000000002101000000000000000000000000000000000000000000000000000000000000e000400600300000000000000000000000000000000000000000000000000000e800000600100000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000e004000610300000000000000000000000000000000000000000000000000000e0080006104000000000000000000000000000000000000000000000000000002042200000000000000000000000000000000000000000000000000000000000e0000016105000000000000000000000000000000000000000000000000000004020400000000000000000000000000000000000000000000000000000000000a0000000899013c0100000000000000000000000000000000000000000000000e0020006101000000000000000000000000000000000000000000000000000002802000002000000000000000000000000000000000000000000000000000000e800000600200000000000000000000000000000000000000000000000000000c000401010000000000000000000000000000000000000000000000000000000c0004010000000000000000000000000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000000000000a000000045701430100000000000000000000000000000000000000000000000e0004006005000000000000000000000000000000000000000000000000000002101200000000000000000000000000000000000000000000000000000000000700000003ac03000000000000000000000000000000000000000000000000000e002000610200000000000000000000000000000000000000000000000000000e0040006105000000000000000000000000000000000000000000000000000002842000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000e000001610500000000000000000000000000000000000000000000000000000e008000610400000000000000000000000000000000000000000000000000000e00200061030000000000000000000000000000000000000000000000000000020004010020000000000000000000000000000000000000000000000000000004900000000000000000000000000000000000000000000000000000000000000a0000000243015001000000000000000000000000000000000000000000000004900000000000000000000000000000000000000000000000000000000000000a000000065701520100000000000000000000000000000000000000000000000e0040006102000000000000000000000000000000000000000000000000000002842000000000000000000000000000000000000000000000000000000000000c401000010000000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000e001000610100000000000000000000000000000000000000000000000000000e000000e104000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c80200000000000000000000000000000717b484e00000000000000000000000e800000200000000000000000000000000000000000000000000000000000000c401000014100000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000c802000010400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000c802000012400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000002028000000000000000000000000000000000000000000000000000000000000a000000016e016e010000000000000000000000000000000000000000000000021080000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000a0000000a6e019e010000000000000000000000000000000000000000000000180002010005000000000000000000000000000000000000000000000000000008100101000000000000000000000000000000000000000000000000000000000e100002210000000000000000000000000000000000000000000000000000000e1008000001000000000000000000000000000000000000000000000000000002000201000100000000000000000000000000000000000000000000000000000c000802010100000000000000000000000000000000000000000000000000000c0008020000000000000000000000000000000000000000000000000000000004100200000000000000000000000000000000000000000000000000000000000a000000026b017701000000000000000000000000000000000000000000000002020002000000000000000000000000000000000000000000000000000000000a000000016b016b0100000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c80200000000000000000000000000000717b484e00000000000000000000000e800000200000000000000000000000000000000000000000000000000000000c401000014100000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000c802000010400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000c802000012400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000c000401010000000000000000000000000000000000000000000000000000000c000401000000000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c004100000000000000000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e000400600500000000000000000000000000000000000000000000000000000a000000018f018f010000000000000000000000000000000000000000000000022000010000000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a0000000a8f01aa010000000000000000000000000000000000000000000000180084000005000000000000000000000000000000000000000000000000000008088100000000000000000000000000000000000000000000000000000000000e080002210000000000000000000000000000000000000000000000000000000e080800000100000000000000000000000000000000000000000000000000000200040200010000000000000000000000000000000000000000000000000000020280000000000000000000000000000000000000000000000000000000000004200400000000000000000000000000000000000000000000000000000000000a000000028c01970100000000000000000000000000000000000000000000000e008000610500000000000000000000000000000000000000000000000000000a000000018c018c0100000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000e000000e104000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c80200001ffffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffff0000000000000000000000000000000e004000010200000000000000000000000000000000000000000000000000001a842000020000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000ffffffff00000000000000000000000e008000210100000000000000000000000000000000000000000000000000001a084100020000000000000000000000000000000000000000000000000000001a842000040000000000000000000000000000000000000000000000000000000e800000000200000000000000000000000000000000000000000000000000000e000000e104000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c80200001ffffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffff0000000000000000000000000000000e004000010200000000000000000000000000000000000000000000000000001a842000020000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000ffffffff00000000000000000000000e008000210100000000000000000000000000000000000000000000000000001a084100020000000000000000000000000000000000000000000000000000001a842000040000000000000000000000000000000000000000000000000000000e800000000200000000000000000000000000000000000000000000000000000e000000e104000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e002000000000000000000000000000000000000000000000000000002014000000000000000000000000000000000000000000000000000000000000c8020000180ffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffffffffffff00000000000000000000000e0001006003000000000000000000000000000000000000000000000000000002840001000000000000000000000000000000000000000000000000000000000c000802012000000000000000000000000000000000000000000000000000000c000802000000000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000000000800000000000000000000000020420000000000000000000000000000000000000000000000000000000000004100800000000000000000000000000000000000000000000000000000000000a00000002c601c501000000000000000000000000000000000000000000000002082000000000000000000000000000000000000000000000000000000000001a10010202000000000000000000000000000000000000000000000000000000020400010000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000a00000008cb01ca010000000000000000000000000000000000000000000000020800010000000000000000000000000000000000000000000000000000000004200100000000000000000000000000000000000000000000000000000000000a00000004ce01cd010000000000000000000000000000000000000000000000020200010000000000000000000000000000000000000000000000000000000004100000000000000000000000000000000000000000000000000000000000000a0000000afa01d00100000000000000000000000000000000000000000000000e004000210400000000000000000000000000000000000000000000000000000c80200001ffffffffffffffff000000000000000000000000000000000000000c8020000000000000000000000000000000000000000000000000000000000004840000000000000000000000000000000000000000000000000000000000000a00000006d501ff0100000000000000000000000000000000000000000000000e0001006002000000000000000000000000000000000000000000000000000002000101009f00000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000080000000000000000000000002040002000000000000000000000000000000000000000000000000000000000e0020006103000000000000000000000000000000000000000000000000000004900000000000000000000000000000000000000000000000000000000000000a00000002e001df01000000000000000000000000000000000000000000000002080002000000000000000000000000000000000000000000000000000000000e000800600100000000000000000000000000000000000000000000000000000e002000610300000000000000000000000000000000000000000000000000001a020102020000000000000000000000000000000000000000000000000000001a10210002000000000000000000000000000000000000000000000000000000020400010000000000000000000000000000000000000000000000000000000004020800000000000000000000000000000000000000000000000000000000000a00000008e801e701000000000000000000000000000000000000000000000002080001000000000000000000000000000000000000000000000000000000001a0228000800000000000000000000000000000000000000000000000000000004020100000000000000000000000000000000000000000000000000000000000a00000004ec01eb0100000000000000000000000000000000000000000000000e0000016101000000000000000000000000000000000000000000000000000004100000000000000000000000000000000000000000000000000000000000000a0000000aee01040200000000000000000000000000000000000000000000000e0020006102000000000000000000000000000000000000000000000000000002801000008000000000000000000000000000000000000000000000000000000700000003ac03000000000000000000000000000000000000000000000000000e002000610200000000000000000000000000000000000000000000000000000280400000a00000000000000000000000000000000000000000000000000000020120000000000000000000000000000000000000000000000000000000000002041000000000000000000000000000000000000000000000000000000000000e0040006103000000000000000000000000000000000000000000000000000007000000031901000000000000000000000000000000000000000000000000000a000000020902f80100000000000000000000000000000000000000000000000e000000e102000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000e000000e102000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000e000000e102000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000e000000e102000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e102000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e00000000000000000000000000000000000000000000000000000001a4040000201000000000000000000000000000000000000000000000000000018408000020100000000000000000000000000000000000000000000000000001a002200027f000000000000000000000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000000000000a0000000412021102000000000000000000000000000000000000000000000002082000000000000000000000000000000000000000000000000000000000000c000802011f00000000000000000000000000000000000000000000000000000c000802000000000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000e000200600100000000000000000000000000000000000000000000000000000c008200010100000000000000000000000000000000000000000000000000000c00820000000000000000000000000000000000000000000000000000000000020800010000000000000000000000000000000000000000000000000000000004020800000000000000000000000000000000000000000000000000000000000a000000081d021c0200000000000000000000000000000000000000000000000e0000016101000000000000000000000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000000000000a0000000a20021f0200000000000000000000000000000000000000000000000e008000610100000000000000000000000000000000000000000000000000001a0844000800000000000000000000000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000000000000a0000000a26022302000000000000000000000000000000000000000000000002021000000000000000000000000000000000000000000000000000000000000e000000e100000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c80200000000000000000000000000000717b484e00000000000000000000000e800000200000000000000000000000000000000000000000000000000000000c401000012200000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000c802000010400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000c802000012400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000c000401010000000000000000000000000000000000000000000000000000000c000401000000000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c004100000000000000000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e000400600100000000000000000000000000000000000000000000000000000a000000013c023c020000000000000000000000000000000000000000000000022000010000000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a0000000a3c0246020000000000000000000000000000000000000000000000180084000005000000000000000000000000000000000000000000000000000008088100000000000000000000000000000000000000000000000000000000000e080002210000000000000000000000000000000000000000000000000000000e080800000100000000000000000000000000000000000000000000000000000200040200010000000000000000000000000000000000000000000000000000020280000000000000000000000000000000000000000000000000000000000004200400000000000000000000000000000000000000000000000000000000000a000000023902440200000000000000000000000000000000000000000000000e008000610100000000000000000000000000000000000000000000000000000a000000013902390200000000000000000000000000000000000000000000000c80200001ffffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffff0000000000000000000000000000000e004000010200000000000000000000000000000000000000000000000000001a842000020000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000ffffffff00000000000000000000000e008000210100000000000000000000000000000000000000000000000000001a084100020000000000000000000000000000000000000000000000000000001a842000040000000000000000000000000000000000000000000000000000000e800000000200000000000000000000000000000000000000000000000000000e000000e100000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c004100011f00000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000004010100000000000000000000000000000000000000000000000000000000000a0000000856026f0200000000000000000000000000000000000000000000000c000401012000000000000000000000000000000000000000000000000000000c000401000000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000000000000000000000000000000000020480000000000000000000000000000000000000000000000000000000000004020400000000000000000000000000000000000000000000000000000000000a000000025f025d02000000000000000000000000000000000000000000000002802000001f000000000000000000000000000000000000000000000000000018808000020500000000000000000000000000000000000000000000000000000e0001002000000000000000000000000000000000000000000000000000000003040000010000000000000000000000000000000000000000000000000000000900400000000000000000000000000000000000000000000000000000000000020422000000000000000000000000000000000000000000000000000000000002408000001f00000000000000000000000000000000000000000000000000001800820002050000000000000000000000000000000000000000000000000000020442000000000000000000000000000000000000000000000000000000000004020100000000000000000000000000000000000000000000000000000000000a0000000c6f026802000000000000000000000000000000000000000000000005028000010000000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000502020000000000000000000000000000000000000000000000000000000000028020000001000000000000000000000000000000000000000000000000000004020100000000000000000000000000000000000000000000000000000000000a0000000268026f02000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000e000000e00500000000000000000000000000000000000000000000000000000e400000600300000000000000000000000000000000000000000000000000000700000003ac030000000000000000000000000000000000000000000000000002014000000000000000000000000000000000000000000000000000000000000c80200001ffffffffffffffff000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e0001006001000000000000000000000000000000000000000000000000000004840000000000000000000000000000000000000000000000000000000000000a00000008df02790200000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e80000060060000000000000000000000000000000000000000000000000000050210000100000000000000000000000000000000000000000000000000000007000000030b02000000000000000000000000000000000000000000000000000a00000002fe027f0200000000000000000000000000000000000000000000000e0020006101000000000000000000000000000000000000000000000000000007000000035202000000000000000000000000000000000000000000000000000e000002610100000000000000000000000000000000000000000000000000000c802000011f00000000000000000000000000000000000000000000000000000c8020000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000000000a000000088602c40200000000000000000000000000000000000000000000000c80200001e0ffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffffffffffff00000000000000000000001aa04000020000000000000000000000000000000000000000000000000000000c000401010000000000000000000000000000000000000000000000000000000c000401000000000000000000000000000000000000000000000000000000000e0004002000000000000000000000000000000000000000000000000000000003100000010000000000000000000000000000000000000000000000000000000c008200012000000000000000000000000000000000000000000000000000000c0082000000000000000000000000000000000000000000000000000000000009002000000000000000000000000000000000000000000000000000000000000e000002610300000000000000000000000000000000000000000000000000000e0001006002000000000000000000000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000000000000a00000004a402940200000000000000000000000000000000000000000000000e000400600400000000000000000000000000000000000000000000000000000e000200600500000000000000000000000000000000000000000000000000000e800000600600000000000000000000000000000000000000000000000000000e0020006103000000000000000000000000000000000000000000000000000002881000000000000000000000000000000000000000000000000000000000000700000003ac03000000000000000000000000000000000000000000000000000e000001610400000000000000000000000000000000000000000000000000000e008000610500000000000000000000000000000000000000000000000000000e0020006106000000000000000000000000000000000000000000000000000005420000000000000000000000000000000000000000000000000000000000000200820000200000000000000000000000000000000000000000000000000000028020000001000000000000000000000000000000000000000000000000000002000401002000000000000000000000000000000000000000000000000000000e0040006102000000000000000000000000000000000000000000000000000004100100000000000000000000000000000000000000000000000000000000000a000000029402a40200000000000000000000000000000000000000000000000e004000610100000000000000000000000000000000000000000000000000000e0000016102000000000000000000000000000000000000000000000000000004100100000000000000000000000000000000000000000000000000000000000a00000002a802c00200000000000000000000000000000000000000000000000e8000006006000000000000000000000000000000000000000000000000000018002100000300000000000000000000000000000000000000000000000000001a80200002f800000000000000000000000000000000000000000000000000000c00040101ffffffffffffffffffffffffffffffff00000000000000000000000c00040100ffffffffffffffffffffffffffffffff00000000000000000000000c00410001ff00000000000000000000000000000000000000000000000000000c004100000000000000000000000000000000000000000000000000000000000c000802010000000000000000000000000000000000000000000000000000000c0008020000000000000000000000000000000000000000000000000000000004020100000000000000000000000000000000000000000000000000000000000a00000008b402b302000000000000000000000000000000000000000000000018020402020000000000000000000000000000000000000000000000000000000e000800600400000000000000000000000000000000000000000000000000000e0020006103000000000000000000000000000000000000000000000000000002881000000000000000000000000000000000000000000000000000000000000e000400600500000000000000000000000000000000000000000000000000000700000003ac03000000000000000000000000000000000000000000000000000e002000610500000000000000000000000000000000000000000000000000000e004000610400000000000000000000000000000000000000000000000000001a842000080000000000000000000000000000000000000000000000000000001a812000020000000000000000000000000000000000000000000000000000000e0040006106000000000000000000000000000000000000000000000000000005840000000000000000000000000000000000000000000000000000000000000e004000610100000000000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c8020000000000000000000000000000000000000000000000000000000000018004100000100000000000000000000000000000000000000000000000000000a00000001d902d90200000000000000000000000000000000000000000000000e002000610600000000000000000000000000000000000000000000000000000e0040006103000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000a00000004cc02c802000000000000000000000000000000000000000000000002001100002000000000000000000000000000000000000000000000000000000700000003ac03000000000000000000000000000000000000000000000000000e00000261010000000000000000000000000000000000000000000000000000020120000000000000000000000000000000000000000000000000000000000018008800000300000000000000000000000000000000000000000000000000000c00410001ffffffffffffffffffffffffffffffff00000000000000000000000c00410000ffffffffffffffffffffffffffffffff00000000000000000000000c00040101ff00000000000000000000000000000000000000000000000000000c0004010000000000000000000000000000000000000000000000000000000004080400000000000000000000000000000000000000000000000000000000000a00000008d502d302000000000000000000000000000000000000000000000018088100020000000000000000000000000000000000000000000000000000000e000200600600000000000000000000000000000000000000000000000000000e008000610600000000000000000000000000000000000000000000000000001a084100080000000000000000000000000000000000000000000000000000001a0241000200000000000000000000000000000000000000000000000000000018002800000100000000000000000000000000000000000000000000000000001a842000040000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000005840000000000000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c80200000000000000000000000000000717b484e00000000000000000000000e800000200000000000000000000000000000000000000000000000000000000c401000014100000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000c802000010400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000c802000012400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000002028000000000000000000000000000000000000000000000000000000000000a00000001f302f3020000000000000000000000000000000000000000000000021080000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000a0000000af30200030000000000000000000000000000000000000000000000180002010005000000000000000000000000000000000000000000000000000008100101000000000000000000000000000000000000000000000000000000000e100002210000000000000000000000000000000000000000000000000000000e1008000001000000000000000000000000000000000000000000000000000002000201000100000000000000000000000000000000000000000000000000000c000802010100000000000000000000000000000000000000000000000000000c0008020000000000000000000000000000000000000000000000000000000004100200000000000000000000000000000000000000000000000000000000000a00000002f002fc02000000000000000000000000000000000000000000000002020002000000000000000000000000000000000000000000000000000000000a00000001f002f00200000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c80200001ffffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffff0000000000000000000000000000000e004000010200000000000000000000000000000000000000000000000000001a842000020000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000ffffffff00000000000000000000000e008000210100000000000000000000000000000000000000000000000000001a084100020000000000000000000000000000000000000000000000000000001a842000040000000000000000000000000000000000000000000000000000000e800000000200000000000000000000000000000000000000000000000000000e000000e105000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c802000011f00000000000000000000000000000000000000000000000000000c8020000000000000000000000000000000000000000000000000000000000004810000000000000000000000000000000000000000000000000000000000000a000000081003220300000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e80000020000000000000000000000000000000000000000000000000000000030200000100000000000000000000000000000000000000000000000000000002402000001f0000000000000000000000000000000000000000000000000000188020000205000000000000000000000000000000000000000000000000000009004000000000000000000000000000000000000000000000000000000000000284200000000000000000000000000000000000000000000000000000000000020041000005000000000000000000000000000000000000000000000000000004840000000000000000000000000000000000000000000000000000000000000a0000000c22031b03000000000000000000000000000000000000000000000005048000010000000000000000000000000000000000000000000000000000000c008200010000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000504020000000000000000000000000000000000000000000000000000000000020041000001000000000000000000000000000000000000000000000000000004840000000000000000000000000000000000000000000000000000000000000a000000021b032203000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000e000000e00000000000000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e80000060010000000000000000000000000000000000000000000000000000050210000100000000000000000000000000000000000000000000000000000007000000030b02000000000000000000000000000000000000000000000000000a0000000243032a03000000000000000000000000000000000000000000000007000000030c03000000000000000000000000000000000000000000000000000c802000010301000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e0040006101000000000000000000000000000000000000000000000000000005840000000000000000000000000000000000000000000000000000000000000e0001002000000000000000000000000000000000000000000000000000000003040000010000000000000000000000000000000000000000000000000000000c802000016161616161616161616161616161616100000000000000000000000c802000006161616161616161616161616161616200000000000000000000000900400000000000000000000000000000000000000000000000000000000000058400000000000000000000000000000000000000000000000000000000000002002100000100000000000000000000000000000000000000000000000000000c008200016161616161616161616161616161616100000000000000000000000c008200006161616161616161616161616161616100000000000000000000000502020000000000000000000000000000000000000000000000000000000000020021000002000000000000000000000000000000000000000000000000000005020200000000000000000000000000000000000000000000000000000000000200210000030000000000000000000000000000000000000000000000000000050202000000000000000000000000000000000000000000000000000000000002002100000400000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000064000000000000000000000005020100000000000000000000000000000000000000000000000000000000000e000000e100000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000e000000e100000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000e000000e00100000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e8000006002000000000000000000000000000000000000000000000000000005022000010000000000000000000000000000000000000000000000000000001a808000020100000000000000000000000000000000000000000000000000000e8000006001000000000000000000000000000000000000000000000000000018800001020100000000000000000000000000000000000000000000000000001a002400027f000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a0000000451035003000000000000000000000000000000000000000000000002102000000000000000000000000000000000000000000000000000000000000c004100011f00000000000000000000000000000000000000000000000000000c004100000000000000000000000000000000000000000000000000000000000c000802010100000000000000000000000000000000000000000000000000000c00080200000000000000000000000000000000000000000000000000000000022000010000000000000000000000000000000000000000000000000000000004020100000000000000000000000000000000000000000000000000000000000a000000085903580300000000000000000000000000000000000000000000000e0000016102000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a0000000a5c035b0300000000000000000000000000000000000000000000000e000002610200000000000000000000000000000000000000000000000000001a2044000800000000000000000000000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000000000000a0000000a80035f0300000000000000000000000000000000000000000000000e8000002004000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a0000000462036a0300000000000000000000000000000000000000000000000c8020000100ffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffffffffffff00000000000000000000000e004000610100000000000000000000000000000000000000000000000000001a842000020000000000000000000000000000000000000000000000000000000e800000200500000000000000000000000000000000000000000000000000000c00820001c000000000000000000000000000000000000000000000000000000c008200000000000000000000000000000000000000000000000000000000000a000000017d037d0300000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c004100000000000000000000000000000000000000000000000000000000000e0001002000000000000000000000000000000000000000000000000000000003040000010000000000000000000000000000000000000000000000000000000c00820001a000000000000000000000000000000000000000000000000000000c00820000000000000000000000000000000000000000000000000000000000090000010000000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000a000000047d03730300000000000000000000000000000000000000000000000c000802012000000000000000000000000000000000000000000000000000000c00080200000000000000000000000000000000000000000000000000000000080888000000000000000000000000000000000000000000000000000000000005100002010000000000000000000000000000000000000000000000000000000e080800200000000000000000000000000000000000000000000000000000000200810000c000000000000000000000000000000000000000000000000000000200040100010000000000000000000000000000000000000000000000000000020041000020000000000000000000000000000000000000000000000000000004840000000000000000000000000000000000000000000000000000000000000a0000000273037d03000000000000000000000000000000000000000000000002081000000000000000000000000000000000000000000000000000000000000e000000e101000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000c802000010000000000000000000000000000000000000000000000000000000c80200000000000000000000000000000717b484e00000000000000000000000e800000200000000000000000000000000000000000000000000000000000000c401000012200000000000000000000000000000000000000000000000000000c401000000000000000000000000000000000000000000000000000000000000c802000010400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000700000003c003000000000000000000000000000000000000000000000000000c802000012400000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000c000401010000000000000000000000000000000000000000000000000000000c000401000000000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c004100000000000000000000000000000000000000000000000000000000000c802000010100000000000000000000000000000000000000000000000000000c802000000000000000000000000000000000000000000000000000000000000e000400600200000000000000000000000000000000000000000000000000000a00000001960396030000000000000000000000000000000000000000000000022000010000000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a0000000a9603a0030000000000000000000000000000000000000000000000180084000005000000000000000000000000000000000000000000000000000008088100000000000000000000000000000000000000000000000000000000000e080002210000000000000000000000000000000000000000000000000000000e080800000100000000000000000000000000000000000000000000000000000200040200010000000000000000000000000000000000000000000000000000020280000000000000000000000000000000000000000000000000000000000004200400000000000000000000000000000000000000000000000000000000000a0000000293039e0300000000000000000000000000000000000000000000000e008000610200000000000000000000000000000000000000000000000000000a000000019303930300000000000000000000000000000000000000000000000c80200001ffffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffff0000000000000000000000000000000e004000010200000000000000000000000000000000000000000000000000001a842000020000000000000000000000000000000000000000000000000000000c004100010000000000000000000000000000000000000000000000000000000c00410000000000000000000000000000ffffffff00000000000000000000000e008000210100000000000000000000000000000000000000000000000000001a084100020000000000000000000000000000000000000000000000000000001a842000040000000000000000000000000000000000000000000000000000000e800000000200000000000000000000000000000000000000000000000000000e000000e101000000000000000000000000000000000000000000000000000007000000040000000000000000000000000000000000000000000000000000000c80200001e0ffffffffffffffffffffffffffffff00000000000000000000000c80200000ffffffffffffffffffffffffffffffff00000000000000000000001a812000020000000000000000000000000000000000000000000000000000000c004100012000000000000000000000000000000000000000000000000000000c0041000000000000000000000000000000000000000000000000000000000008024100000000000000000000000000000000000000000000000000000000001a402000021f000000000000000000000000000000000000000000000000000018808000000300000000000000000000000000000000000000000000000000000e0420002100000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000a00000004be03b70300000000000000000000000000000000000000000000000c000401010001000000000000000000000000000000000000000000000000000c0004010000000000000000000000000000000000000000000000000000000004100201000000000000000000000000000000000000000000000000000000000e04400021010000000000000000000000000000000000000000000000000000181041000200000000000000000000000000000000000000000000000000000018882000000000000000000000000000000000000000000000000000000000001a84200004000000000000000000000000000000000000000000000000000000020210000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000e000000e00100000000000000000000000000000000000000000000000000000c00410001e0ffffffffffffffffffffffffffffff00000000000000000000000c00410000ffffffffffffffffffffffffffffffff00000000000000000000001a024100020000000000000000000000000000000000000000000000000000000c008200012000000000000000000000000000000000000000000000000000000c0082000000000000000000000000000000000000000000000000000000000008044200000000000000000000000000000000000000000000000000000000001a802000021f000000000000000000000000000000000000000000000000000018802000000300000000000000000000000000000000000000000000000000000c008200010001000000000000000000000000000000000000000000000000000c0082000000000000000000000000000000000000000000000000000000000004888000000000000000000000000000000000000000000000000000000000000e000200600200000000000000000000000000000000000000000000000000000c00080201ffffffffffffffffffffffffffffffff00000000000000000000000c00080200ffffffffffffffffffffffffffffffff0000000000000000000000020180000000000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000a00000004d803d20300000000000000000000000000000000000000000000000e0080006102000000000000000000000000000000000000000000000000000018088800000000000000000000000000000000000000000000000000000000000e040001210000000000000000000000000000000000000000000000000000001a1082000200000000000000000000000000000000000000000000000000000018420001020000000000000000000000000000000000000000000000000000001a108200040000000000000000000000000000000000000000000000000000000e0400012101000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000a00000004e403db0300000000000000000000000000000000000000000000000e000800600100000000000000000000000000000000000000000000000000000e0000026102000000000000000000000000000000000000000000000000000018600002000000000000000000000000000000000000000000000000000000000e000800600200000000000000000000000000000000000000000000000000000e0000026101000000000000000000000000000000000000000000000000000018022800020000000000000000000000000000000000000000000000000000001a902000020000000000000000000000000000000000000000000000000000000e000001610200000000000000000000000000000000000000000000000000001a900001040000000000000000000000000000000000000000000000000000000e040400200100000000000000000000000000000000000000000000000000000e040200200000000000000000000000000000000000000000000000000000000e000000e10100000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/missing-deploy-folder/contracts/Greeter.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/missing-deploy-folder/contracts/Greeter.sol new file mode 100644 index 000000000..d28a48208 --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/missing-deploy-folder/contracts/Greeter.sol @@ -0,0 +1,17 @@ +// 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; + } + +} diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/missing-deploy-folder/hardhat.config.ts b/packages/hardhat-zksync-deploy/test/fixture-projects/missing-deploy-folder/hardhat.config.ts new file mode 100644 index 000000000..63e30448d --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/missing-deploy-folder/hardhat.config.ts @@ -0,0 +1,21 @@ +import '../../../src/index'; +import { HardhatUserConfig } from 'hardhat/types'; +import { ETH_NETWORK_RPC_URL, ZKSYNC_NETWORK_NAME, ZKSYNC_NETWORK_RPC_URL } from '../../constants'; + +const config: HardhatUserConfig = { + networks: { + hardhat: { + zksync: true, + }, + sepolia: { + url: ETH_NETWORK_RPC_URL, + }, + [ZKSYNC_NETWORK_NAME]: { + url: ZKSYNC_NETWORK_RPC_URL, + ethNetwork: 'sepolia', + zksync: true, + }, + }, +}; + +export default config; diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/non-successfull-compilation/contracts/Greeter.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/non-successfull-compilation/contracts/Greeter.sol new file mode 100644 index 000000000..a3f9a3ad3 --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/non-successfull-compilation/contracts/Greeter.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.17; +pragma abicoder v2; + +contract Greeter { + + string greeting; + string bad; + constructor(string memory _greeting) { + greeting = _greeting; + bad = "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad"; + } + + function greet() public view returns (string memory) { + return greeting; + } + +} diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/non-successfull-compilation/deploy/001_deploy.ts b/packages/hardhat-zksync-deploy/test/fixture-projects/non-successfull-compilation/deploy/001_deploy.ts new file mode 100644 index 000000000..89fd2a2ad --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/non-successfull-compilation/deploy/001_deploy.ts @@ -0,0 +1,13 @@ +import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { Wallet } from 'zksync-ethers'; +import chalk from 'chalk'; +import { Deployer } from '../../../../src/index'; +import { WALLET_PRIVATE_KEY } from '../../../constants'; + +export default async function (hre: HardhatRuntimeEnvironment) { + const zkWallet = new Wallet(WALLET_PRIVATE_KEY); + const deployer = new Deployer(hre, zkWallet); + const artifact = await deployer.loadArtifact('Greeter'); + + console.info(chalk.yellow(`${artifact.contractName} was loaded`)); +} diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/non-successfull-compilation/hardhat.config.ts b/packages/hardhat-zksync-deploy/test/fixture-projects/non-successfull-compilation/hardhat.config.ts new file mode 100644 index 000000000..f4f1c68d7 --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/non-successfull-compilation/hardhat.config.ts @@ -0,0 +1,24 @@ +import '../../../src/index'; +import { HardhatUserConfig } from 'hardhat/types'; +import { ETH_NETWORK_RPC_URL, ZKSYNC_NETWORK_NAME, ZKSYNC_NETWORK_RPC_URL } from '../../constants'; + +const config: HardhatUserConfig = { + networks: { + hardhat: { + zksync: true, + }, + sepolia: { + url: ETH_NETWORK_RPC_URL, + }, + [ZKSYNC_NETWORK_NAME]: { + url: ZKSYNC_NETWORK_RPC_URL, + ethNetwork: 'sepolia', + zksync: true, + }, + }, + solidity: { + version: '0.8.17', + }, +}; + +export default config; diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/ChildChildLib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/ChildChildLib.sol new file mode 100644 index 000000000..8762d9ebd --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/ChildChildLib.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +library ChildChildLib { + + function plus(uint a, uint b) public pure returns (uint) { + return a - b; + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/ChildLib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/ChildLib.sol new file mode 100644 index 000000000..54818b75b --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/ChildLib.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +import "./ChildChildLib.sol"; +library ChildLib { + + using ChildChildLib for uint; + + function plus(uint a, uint b) public view returns (uint, address) { + return (a.plus(b), address(this)); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/DLib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/DLib.sol new file mode 100644 index 000000000..4d3d38623 --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/DLib.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +library DLib { + + function multiply(uint a, uint b) public view returns (uint, address) { + return (a * b, address(this)); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/Math2Lib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/Math2Lib.sol new file mode 100644 index 000000000..99c1d1005 --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/Math2Lib.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +import "./ChildLib.sol"; +library Math2Lib { + + using ChildLib for uint; + + function multiply(uint a, uint b) public view returns (uint, address) { + return (a * b, address(this)); + } + + function plus(uint a, uint b) public view returns (uint, address) { + return a.plus(b); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/MathLib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/MathLib.sol new file mode 100644 index 000000000..5f6bcd86d --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/MathLib.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +import "./ChildLib.sol"; +library MathLib { + + using ChildLib for uint; + + function multiply(uint a, uint b) public view returns (uint, address) { + return (a * b, address(this)); + } + + function plus(uint a, uint b) public view returns (uint, address) { + return a.plus(b); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/TestCoin.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/TestCoin.sol new file mode 100644 index 000000000..f360f5a8f --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/contracts/TestCoin.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +import "./MathLib.sol"; + +contract TestCoin { + + using MathLib for uint; + address owner = address(this); + + function multiplyExample(uint _a, uint _b) public view returns (uint, address) { + return _a.multiply(_b); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/hardhat.config.ts b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/hardhat.config.ts new file mode 100644 index 000000000..c51284b2b --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v1/hardhat.config.ts @@ -0,0 +1,29 @@ +// eslint-disable-next-line +import '@matterlabs/hardhat-zksync-deploy'; +import '@matterlabs/hardhat-zksync-solc'; + +import { HardhatUserConfig } from 'hardhat/config'; + +const config: HardhatUserConfig = { + zksolc: { + settings: {}, + }, + defaultNetwork: 'zkSyncNetwork', + networks: { + 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/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/ChildChildLib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/ChildChildLib.sol new file mode 100644 index 000000000..8762d9ebd --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/ChildChildLib.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +library ChildChildLib { + + function plus(uint a, uint b) public pure returns (uint) { + return a - b; + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/ChildLib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/ChildLib.sol new file mode 100644 index 000000000..54818b75b --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/ChildLib.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +import "./ChildChildLib.sol"; +library ChildLib { + + using ChildChildLib for uint; + + function plus(uint a, uint b) public view returns (uint, address) { + return (a.plus(b), address(this)); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/DLib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/DLib.sol new file mode 100644 index 000000000..4d3d38623 --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/DLib.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +library DLib { + + function multiply(uint a, uint b) public view returns (uint, address) { + return (a * b, address(this)); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/Math2Lib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/Math2Lib.sol new file mode 100644 index 000000000..99c1d1005 --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/Math2Lib.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +import "./ChildLib.sol"; +library Math2Lib { + + using ChildLib for uint; + + function multiply(uint a, uint b) public view returns (uint, address) { + return (a * b, address(this)); + } + + function plus(uint a, uint b) public view returns (uint, address) { + return a.plus(b); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/MathLib.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/MathLib.sol new file mode 100644 index 000000000..5f6bcd86d --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/MathLib.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +import "./ChildLib.sol"; +library MathLib { + + using ChildLib for uint; + + function multiply(uint a, uint b) public view returns (uint, address) { + return (a * b, address(this)); + } + + function plus(uint a, uint b) public view returns (uint, address) { + return a.plus(b); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/TestCoin.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/TestCoin.sol new file mode 100644 index 000000000..f360f5a8f --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/contracts/TestCoin.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.11; +import "./MathLib.sol"; + +contract TestCoin { + + using MathLib for uint; + address owner = address(this); + + function multiplyExample(uint _a, uint _b) public view returns (uint, address) { + return _a.multiply(_b); + } +} \ No newline at end of file diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/hardhat.config.ts b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/hardhat.config.ts new file mode 100644 index 000000000..72ef2e513 --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/noninline-libraries-v2/hardhat.config.ts @@ -0,0 +1,30 @@ +// eslint-disable-next-line +import '@matterlabs/hardhat-zksync-deploy'; +import '@matterlabs/hardhat-zksync-solc'; + +import { HardhatUserConfig } from 'hardhat/config'; + +const config: HardhatUserConfig = { + zksolc: { + settings: {}, + }, + defaultNetwork: 'zkSyncNetwork', + networks: { + ethNetwork: { + url: 'http://0.0.0.0:8545', + }, + zkSyncNetwork: { + url: 'http://0.0.0.0:3050', + ethNetwork: 'ethNetwork', + zksync: true, + accounts: ['0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110'], + }, + }, + // 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/packages/hardhat-zksync-deploy/test/fixture-projects/not-zksync-network/contracts/Greeter.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/not-zksync-network/contracts/Greeter.sol new file mode 100644 index 000000000..d28a48208 --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/not-zksync-network/contracts/Greeter.sol @@ -0,0 +1,17 @@ +// 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; + } + +} diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/not-zksync-network/deploy/001_deploy.ts b/packages/hardhat-zksync-deploy/test/fixture-projects/not-zksync-network/deploy/001_deploy.ts new file mode 100644 index 000000000..89fd2a2ad --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/not-zksync-network/deploy/001_deploy.ts @@ -0,0 +1,13 @@ +import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { Wallet } from 'zksync-ethers'; +import chalk from 'chalk'; +import { Deployer } from '../../../../src/index'; +import { WALLET_PRIVATE_KEY } from '../../../constants'; + +export default async function (hre: HardhatRuntimeEnvironment) { + const zkWallet = new Wallet(WALLET_PRIVATE_KEY); + const deployer = new Deployer(hre, zkWallet); + const artifact = await deployer.loadArtifact('Greeter'); + + console.info(chalk.yellow(`${artifact.contractName} was loaded`)); +} diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/not-zksync-network/hardhat.config.ts b/packages/hardhat-zksync-deploy/test/fixture-projects/not-zksync-network/hardhat.config.ts new file mode 100644 index 000000000..d61b22e04 --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/not-zksync-network/hardhat.config.ts @@ -0,0 +1,12 @@ +import '../../../src/index'; +import { HardhatUserConfig } from 'hardhat/types'; + +const config: HardhatUserConfig = { + networks: { + hardhat: { + zksync: false, + }, + }, +}; + +export default config; diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/contracts/Greeter.sol b/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/contracts/Greeter.sol index d28a48208..a10bf9b39 100644 --- a/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/contracts/Greeter.sol +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/contracts/Greeter.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.16; pragma abicoder v2; contract Greeter { diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/deploy/001_deploy.ts b/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/deploy/001_deploy.ts index 6433112d5..89fd2a2ad 100644 --- a/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/deploy/001_deploy.ts +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/deploy/001_deploy.ts @@ -1,8 +1,8 @@ import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { Wallet } from 'zksync-ethers'; +import chalk from 'chalk'; import { Deployer } from '../../../../src/index'; import { WALLET_PRIVATE_KEY } from '../../../constants'; -import chalk from 'chalk'; export default async function (hre: HardhatRuntimeEnvironment) { const zkWallet = new Wallet(WALLET_PRIVATE_KEY); diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/deploy/invalid_script.ts b/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/deploy/invalid_script.ts new file mode 100644 index 000000000..ca5efbd2d --- /dev/null +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/deploy/invalid_script.ts @@ -0,0 +1,13 @@ +import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { Wallet } from 'zksync-ethers'; +import chalk from 'chalk'; +import { Deployer } from '../../../../src/index'; +import { WALLET_PRIVATE_KEY } from '../../../constants'; + +const _ = async (hre: HardhatRuntimeEnvironment) => { + const zkWallet = new Wallet(WALLET_PRIVATE_KEY); + const deployer = new Deployer(hre, zkWallet); + const artifact = await deployer.loadArtifact('Greeter'); + + console.info(chalk.yellow(`${artifact.contractName} was loaded`)); +}; diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/hardhat.config.ts b/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/hardhat.config.ts index 63e30448d..88341f91f 100644 --- a/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/hardhat.config.ts +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/plugin-functionalities/hardhat.config.ts @@ -1,8 +1,18 @@ import '../../../src/index'; import { HardhatUserConfig } from 'hardhat/types'; import { ETH_NETWORK_RPC_URL, ZKSYNC_NETWORK_NAME, ZKSYNC_NETWORK_RPC_URL } from '../../constants'; +import '@matterlabs/hardhat-zksync-solc'; const config: HardhatUserConfig = { + zksolc: { + compilerSource: 'binary', + settings: { + isSystem: true, + optimizer: { + enabled: true, + }, + }, + }, networks: { hardhat: { zksync: true, @@ -15,6 +25,21 @@ const config: HardhatUserConfig = { ethNetwork: 'sepolia', zksync: true, }, + ethNetwork: { + url: 'http://0.0.0.0:8545', + }, + zkSyncNetwork2: { + url: 'http://0.0.0.0:3050', + ethNetwork: 'ethNetwork', + zksync: true, + accounts: [ + '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110', + '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110', + ], + }, + }, + solidity: { + version: '0.8.17', }, }; diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/successful-compilation/deploy/001_deploy.ts b/packages/hardhat-zksync-deploy/test/fixture-projects/successful-compilation/deploy/001_deploy.ts index 6433112d5..89fd2a2ad 100644 --- a/packages/hardhat-zksync-deploy/test/fixture-projects/successful-compilation/deploy/001_deploy.ts +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/successful-compilation/deploy/001_deploy.ts @@ -1,8 +1,8 @@ import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { Wallet } from 'zksync-ethers'; +import chalk from 'chalk'; import { Deployer } from '../../../../src/index'; import { WALLET_PRIVATE_KEY } from '../../../constants'; -import chalk from 'chalk'; export default async function (hre: HardhatRuntimeEnvironment) { const zkWallet = new Wallet(WALLET_PRIVATE_KEY); diff --git a/packages/hardhat-zksync-deploy/test/fixture-projects/successful-compilation/hardhat.config.ts b/packages/hardhat-zksync-deploy/test/fixture-projects/successful-compilation/hardhat.config.ts index 63e30448d..fcd2a7a0d 100644 --- a/packages/hardhat-zksync-deploy/test/fixture-projects/successful-compilation/hardhat.config.ts +++ b/packages/hardhat-zksync-deploy/test/fixture-projects/successful-compilation/hardhat.config.ts @@ -15,6 +15,15 @@ const config: HardhatUserConfig = { ethNetwork: 'sepolia', zksync: true, }, + ethNetwork: { + url: 'http://0.0.0.0:8545', + }, + zkSyncNetwork2: { + url: 'http://0.0.0.0:3050', + ethNetwork: 'ethNetwork', + zksync: true, + accounts: ['0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110'], + }, }, }; diff --git a/packages/hardhat-zksync-deploy/test/tests.ts b/packages/hardhat-zksync-deploy/test/tests.ts index 32605b24f..0b8d37362 100644 --- a/packages/hardhat-zksync-deploy/test/tests.ts +++ b/packages/hardhat-zksync-deploy/test/tests.ts @@ -1,11 +1,12 @@ -import { assert } from 'chai'; +import { assert, expect } from 'chai'; import * as path from 'path'; import { ethers } from 'ethers'; import { Provider, Wallet } from 'zksync-ethers'; +import chalk from 'chalk'; import { callDeployScripts, findDeployScripts } from '../src/plugin'; -import { TASK_DEPLOY_ZKSYNC } from '../src/task-names'; -import { useEnvironment } from './helpers'; +import { TASK_DEPLOY_ZKSYNC, TASK_DEPLOY_ZKSYNC_LIBRARIES } from '../src/task-names'; import { Deployer } from '../src/deployer'; +import { useEnvironment } from './helpers'; import { ETH_NETWORK_RPC_URL, ZKSYNC_NETWORK_RPC_URL, ZKSYNC_NETWORK_NAME, WALLET_PRIVATE_KEY } from './constants'; describe('Plugin tests', async function () { @@ -17,6 +18,7 @@ describe('Plugin tests', async function () { assert(artifactExists, "Greeter artifact doesn't exist"); const artifact = await this.env.artifacts.readArtifact('Greeter'); + assert.equal(artifact._format, 'hh-zksolc-artifact-1', 'Incorrect artifact build'); // Check that we can load an additional key (it turns that we can which is great). @@ -34,11 +36,93 @@ describe('Plugin tests', async function () { await callDeployScripts(this.env, ''); }); + it('Should call deploy script', async function () { + await callDeployScripts(this.env, '001_deploy.ts'); + }); + it('Should call deploy scripts through HRE', async function () { await this.env.run(TASK_DEPLOY_ZKSYNC); }); }); + describe('Missing deploy folder', async function () { + useEnvironment('missing-deploy-folder'); + + it('Should not find deploy scripts', async function () { + try { + const _files = findDeployScripts(this.env); + assert.fail('Expected ZkSyncDeployPluginError was not thrown'); + } catch (error: any) { + assert.strictEqual(error.message, 'No deploy folder was found', 'Error message does not match'); + } + }); + }); + + describe('noninline-libraries artifact', async function () { + useEnvironment('noninline-libraries-v1', 'zkSyncNetwork'); + + it('Should compile libraries', async function () { + const libraries = this.env.config.zksolc.settings.libraries; + assert.deepEqual(libraries, {}, 'Libraries present in hardhat config. Delete them!'); + chalk.yellow('Compiling libraries...'); + await this.env.run('compile'); + }); + + it('Should deploy libraries with private key', async function () { + assert.deepEqual( + this.env.config.zksolc.settings.libraries, + {}, + 'Libraries must not be present in hardhat config. Delete them!', + ); + chalk.yellow('Deploying libraries...'); + await this.env.run(TASK_DEPLOY_ZKSYNC_LIBRARIES, { + privateKey: '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110', + }); + }); + }); + + describe('noninline-libraries artifact with private key in config', async function () { + useEnvironment('noninline-libraries-v2', 'zkSyncNetwork'); + + it('Should compile libraries', async function () { + const libraries = this.env.config.zksolc.settings.libraries; + assert.deepEqual(libraries, {}, 'Libraries present in hardhat config. Delete them!'); + chalk.yellow('Compiling libraries...'); + await this.env.run('compile'); + }); + + it('Should deploy libraries with private key in hardhat.config', async function () { + assert.deepEqual( + this.env.config.zksolc.settings.libraries, + {}, + 'Libraries must not be present in hardhat config. Delete them!', + ); + chalk.yellow('Deploying libraries...'); + await this.env.run(TASK_DEPLOY_ZKSYNC_LIBRARIES, { compileAllContracts: true }); + }); + }); + + describe('should deploy libraries using mnemonic', async function () { + useEnvironment('deployment-with-mnemonic', 'zkSyncNetwork'); + + it('Should compile libraries', async function () { + const libraries = this.env.config.zksolc.settings.libraries; + assert.deepEqual(libraries, {}, 'Libraries present in hardhat config. Delete them!'); + chalk.yellow('Compiling libraries...'); + await this.env.run('compile'); + }); + + it('Should deploy libraries with private key in hardhat.config', async function () { + assert.deepEqual( + this.env.config.zksolc.settings.libraries, + {}, + 'Libraries must not be present in hardhat config. Delete them!', + ); + chalk.yellow('Deploying libraries...'); + await this.env.run(TASK_DEPLOY_ZKSYNC_LIBRARIES, { noAutoPopulateConfig: true }); + }); + }); + describe('Deployer with zkSync network provided', async function () { useEnvironment('successful-compilation', ZKSYNC_NETWORK_NAME); @@ -49,9 +133,13 @@ describe('Plugin tests', async function () { assert.equal( (deployer.ethWallet.provider as ethers.JsonRpcProvider)._getConnection().url, ETH_NETWORK_RPC_URL, - 'Incorrect L1 network url' + 'Incorrect L1 network url', + ); + assert.equal( + deployer.zkWallet.provider._getConnection().url, + ZKSYNC_NETWORK_RPC_URL, + 'Incorrect L2 network url', ); - assert.equal(deployer.zkWallet.provider._getConnection().url, ZKSYNC_NETWORK_RPC_URL, 'Incorrect L2 network url'); }); }); @@ -65,22 +153,20 @@ describe('Plugin tests', async function () { assert.equal( (deployer.ethWallet.provider as ethers.JsonRpcProvider)._getConnection().url, 'http://localhost:8545', - 'Incorrect default L1 network provider' + 'Incorrect default L1 network provider', ); assert.equal( deployer.zkWallet.provider._getConnection().url, 'http://localhost:3050', - 'Incorrect default L2 network provider' + 'Incorrect default L2 network provider', ); }); }); - describe('Test plugin functionalities', async function () { useEnvironment('plugin-functionalities'); it('Should use the provider from the wallet instance passed as an argument', async function () { - const MOCKED_PROVIDER_URL = 'http://localhost:1234'; const provider: Provider = new Provider(MOCKED_PROVIDER_URL); @@ -90,8 +176,78 @@ describe('Plugin tests', async function () { assert.equal( deployer.zkWallet.provider._getConnection().url, MOCKED_PROVIDER_URL, - 'Incorrect default L2 network provider' + 'Incorrect default L2 network provider', ); }); + + it('Should not find the deploy function', async function () { + const targetScript = '002_deploy.ts'; + + try { + await callDeployScripts(this.env, targetScript); + assert.fail('Function did not throw expected error'); + } catch (error: any) { + const expectedMessage = `Script ${targetScript} was not found, no scripts were run`; + assert.include( + error.message, + expectedMessage, + `Error message does not contain expected text: ${error.message}`, + ); + } + }); + + it('Should not find deploy function', async function () { + try { + await callDeployScripts(this.env, 'invalid_script.ts'); + assert.fail('Function did not throw expected error'); + } catch (error: any) { + assert.include( + error.message, + 'Deploy function does not exist or exported invalidly', + `Error message does not contain expected text: ${error.message}`, + ); + } + }); + + it('Should estimate deploy fee', async function () { + const zkWallet = new Wallet(WALLET_PRIVATE_KEY); + const deployer = new Deployer(this.env, zkWallet); + await this.env.run('compile'); + const artifact = await deployer.loadArtifact('Greeter'); + const result = await deployer.estimateDeployFee(artifact, ['Hi there!']); + expect(typeof result).to.equal('bigint'); + }); + }); + + describe('Test not zksync network', async function () { + useEnvironment('not-zksync-network'); + + it('Should fail deploying because zksync is not set to true', async function () { + try { + await callDeployScripts(this.env, '001_deploy.ts'); + throw new Error('Expected an error but none was thrown'); + } catch (error: any) { + expect(error.message).to.include("'zksync' flag set to 'true'"); + } + }); + }); + + describe('Test wrong compiler used', async function () { + useEnvironment('non-successfull-compilation'); + + it('should fail because wrong compiler is used for compilation of contract', async function () { + const zkWallet = new Wallet(WALLET_PRIVATE_KEY); + const deployer = new Deployer(this.env, zkWallet); + await this.env.run('compile'); + let errorOccurred = false; + try { + const _ = await deployer.loadArtifact('Greeter'); + } catch (error: any) { + errorOccurred = true; + expect(error.message).to.include('Artifact Greeter was not compiled by zksolc or zkvyper'); + } + + expect(errorOccurred).to.equal(true); + }); }); }); diff --git a/packages/hardhat-zksync-ethers/README.md b/packages/hardhat-zksync-ethers/README.md index e1191fc52..984a8eafe 100644 --- a/packages/hardhat-zksync-ethers/README.md +++ b/packages/hardhat-zksync-ethers/README.md @@ -1,5 +1,73 @@ -# hardhat-zksync-ethers +# hardhat-zksync-ethers 🚀 -[Hardhat](https://hardhat.org/) plugin that is a wrapper around zksync-ethers sdk that gives additional methods to use for faster development. +zkSync Era [Hardhat](https://hardhat.org/) plugin that is a wrapper around [zksync-ethers](https://www.npmjs.com/package/zksync-ethers) sdk that gives additional methods to use for faster development. -Check out the [documentation page](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-ethers.html) for more detailed explanation on how to use hardhat-zksync-ethers. \ No newline at end of file +![Era Logo](https://github.com/matter-labs/era-contracts/raw/main/eraLogo.svg) + +## 📥 Installation + +To install **hardhat-zksync-ethers** plugin, run: + +`npm install -D @matterlabs/hardhat-zksync-ethers` + +or + +`yarn add -D @matterlabs/hardhat-zksync-ethers zksync-ethers ethers` + +## Helpers + +| 🙏 Helper | 📄 Description | +|-----------------------------------------------|---------------------------------------------------------------------------------------------------------------| +| provider | Retruns a Provider automatically connected to the selected network. | +| getWallet | Returns Wallet for the given private key or index. | +| getContractFactory variant1 | Returns a ContractFactory for provided artifact name. | +| getContractFactory variant2 | Returns a ContractFactory for provided artifact abi and bytecode. | +| getContractFactoryFromArtifact | Returns a ContractFactory for provided artifact. | +| getContractAt | Returns Contract for provided artifact name or abi and address of deployed contract. | +| getContractAtFromArtifact | Returns ContractFactory for provided artifact and address of deployed contract | +| getImpersonatedSigner | Impersonates Signer from address | +| extractFactoryDeps | Extracts factory deps from artifact | +| loadArtifact | Load ZkSyncArtifact from contract name | +| deployContract | Deploys a contract to the network | + +## 📖 Example + +After installing it, add the plugin to your Hardhat config: + +`import "@matterlabs/hardhat-zksync-ethers";` + +This plugin extends hardhat runtime environment, use it like this: + +Retrieve your contract factory: + +`const myContractFactory = await hre.zksyncEthers.getContractFactory("MyContract");` + +Deploy your contract: + +`const myContract = await myContractFactory.deploy("Hello, world!");` + +Find deployed address: + +`console.info(await myContract.getAddress());` + +## 📝 Documentation + +In addition to the [hardhat-zksync-ethers](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-ethers.html), zkSync's Era [website](https://era.zksync.io/docs/) offers a variety of resources including: + +[Guides to get started](https://era.zksync.io/docs/dev/building-on-zksync/hello-world.html): Learn how to start building on zkSync Era.\ +[Hardhat zkSync Era plugins](https://era.zksync.io/docs/tools/hardhat/getting-started.html): Overview and guides for all Hardhat zkSync Era plugins.\ +[Hyperscaling](https://era.zksync.io/docs/reference/concepts/hyperscaling.html#what-are-hyperchains): Deep dive into hyperscaling on zkSync Era. + +## 🤝 Contributing + +Contributions are always welcome! Feel free to open any issue or send a pull request. + +Go to [CONTRIBUTING.md](https://github.com/matter-labs/hardhat-zksync/blob/main/.github/CONTRIBUTING.md) to learn about steps and best practices for contributing to zkSync hardhat tooling base repository. + + +## 🙌 Feedback, help and news + +[zkSync Era Discord server](https://join.zksync.dev/): for questions and feedback.\ +[Follow zkSync Era on Twitter](https://twitter.com/zksync) + +## Happy building! 👷‍♀️👷‍♂️ \ No newline at end of file diff --git a/packages/hardhat-zksync-ethers/package.json b/packages/hardhat-zksync-ethers/package.json index e3a15f4fd..ff717fa9f 100644 --- a/packages/hardhat-zksync-ethers/package.json +++ b/packages/hardhat-zksync-ethers/package.json @@ -1,6 +1,6 @@ { "name": "@matterlabs/hardhat-zksync-ethers", - "version": "0.0.1-beta.1", + "version": "0.0.1-beta.2", "description": "Hardhat plugin for integration with zksync-ethers library", "repository": "github:matter-labs/hardhat-zksync", "homepage": "https://github.com/matter-labs/hardhat-zksync/tree/main/packages/hardhat-zksync-ethers", @@ -34,7 +34,11 @@ "README.md" ], "dependencies": { - "chalk": "5.3.0" + "chalk": "5.3.0", + "hardhat": "^2.19.4", + "chai": "^4.2.0", + "@matterlabs/hardhat-zksync-solc":"1.0.6", + "@matterlabs/hardhat-zksync-deploy":"1.1.2" }, "devDependencies": { "@types/chai": "^4.2.0", @@ -45,13 +49,11 @@ "@types/sinon": "^9.0.8", "@typescript-eslint/eslint-plugin": "6.13.1", "@typescript-eslint/parser": "6.13.1", - "chai": "^4.2.0", "eslint": "^8.54.0", "eslint-config-prettier": "9.0.0", "eslint-plugin-import": "2.29.0", "eslint-plugin-no-only-tests": "3.1.0", "eslint-plugin-prettier": "5.0.1", - "hardhat": "^2.19.2", "mocha": "^10.0.0", "prettier": "3.1.0", "rimraf": "^5.0.5", diff --git a/packages/hardhat-zksync-ethers/src/constants.ts b/packages/hardhat-zksync-ethers/src/constants.ts index 4b229355e..608b64918 100644 --- a/packages/hardhat-zksync-ethers/src/constants.ts +++ b/packages/hardhat-zksync-ethers/src/constants.ts @@ -1,10 +1,9 @@ export const PLUGIN_NAME = '@matterlabs/hardhat-zksync-ethers'; export const LOCAL_CHAIN_IDS = [ - '0x104', //era-node - '0x10e', //local-setup + '0x104', // era-node + '0x10e', // local-setup ]; export const ZKSOLC_ARTIFACT_FORMAT_VERSION = 'hh-zksolc-artifact-1'; export const ZKVYPER_ARTIFACT_FORMAT_VERSION = 'hh-zkvyper-artifact-1'; - diff --git a/packages/hardhat-zksync-ethers/src/helpers.ts b/packages/hardhat-zksync-ethers/src/helpers.ts index b8ce10bde..835a46435 100644 --- a/packages/hardhat-zksync-ethers/src/helpers.ts +++ b/packages/hardhat-zksync-ethers/src/helpers.ts @@ -2,14 +2,12 @@ import { Contract, ContractFactory, Provider, Signer, Wallet } from 'zksync-ethe import * as ethers from 'ethers'; -import { - HardhatRuntimeEnvironment -} from 'hardhat/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { Address, DeploymentType } from 'zksync-ethers/build/src/types'; import { FactoryOptions, ZkSyncArtifact } from './types'; import { ZkSyncEthersPluginError } from './errors'; -import { rich_wallets } from './rich-wallets'; +import { richWallets } from './rich-wallets'; import { getWalletsFromAccount, isArtifact, isFactoryOptions, isNumber, isString } from './utils'; import { ZKSOLC_ARTIFACT_FORMAT_VERSION, ZKVYPER_ARTIFACT_FORMAT_VERSION } from './constants'; @@ -29,7 +27,7 @@ export async function getWallet(hre: HardhatRuntimeEnvironment, privateKeyOrInde throw new ZkSyncEthersPluginError('Account private key with specified index is not found'); } - if (wallets.length == 0) { + if (wallets.length === 0) { throw new ZkSyncEthersPluginError('Accounts are not configured for this network'); } @@ -42,8 +40,8 @@ export async function getWallets(hre: HardhatRuntimeEnvironment): Promise wallet.address); +function _getSigners(hre: HardhatRuntimeEnvironment): Signer[] { + const accounts: string[] = richWallets.map((wallet) => wallet.address); const signersWithAddress = accounts.map((account) => getSigner(hre, account)); @@ -59,35 +57,26 @@ export async function getImpersonatedSigner(hre: HardhatRuntimeEnvironment, addr return getSigner(hre, address); } -export async function getContractFactory< - A extends any[] = any[], - I = Contract ->( +export async function getContractFactory( hre: HardhatRuntimeEnvironment, name: string, - walletOrOption?: Wallet | FactoryOptions + walletOrOption?: Wallet | FactoryOptions, ): Promise>; -export async function getContractFactory< - A extends any[] = any[], - I = Contract ->( +export async function getContractFactory( hre: HardhatRuntimeEnvironment, abi: any[], bytecode: ethers.BytesLike, wallet?: Wallet, - deploymentType?: DeploymentType + deploymentType?: DeploymentType, ): Promise>; -export async function getContractFactory< - A extends any[] = any[], - I = Contract ->( +export async function getContractFactory( hre: HardhatRuntimeEnvironment, nameOrAbi: string | any[], bytecodeOrFactoryOptions?: (Wallet | FactoryOptions) | ethers.BytesLike, wallet?: Wallet, - deploymentType?: DeploymentType + deploymentType?: DeploymentType, ): Promise> { if (typeof nameOrAbi === 'string') { const artifact = await loadArtifact(hre, nameOrAbi); @@ -96,7 +85,7 @@ export async function getContractFactory< hre, artifact, bytecodeOrFactoryOptions as Wallet | FactoryOptions | undefined, - deploymentType + deploymentType, ); } @@ -105,24 +94,21 @@ export async function getContractFactory< nameOrAbi, bytecodeOrFactoryOptions as ethers.BytesLike, wallet, - deploymentType + deploymentType, ); } -export async function getContractFactoryFromArtifact< - A extends any[] = any[], - I = Contract ->( +export async function getContractFactoryFromArtifact( hre: HardhatRuntimeEnvironment, artifact: ZkSyncArtifact, walletOrOptions?: Wallet | FactoryOptions, - deploymentType?: DeploymentType + deploymentType?: DeploymentType, ): Promise> { let wallet: Wallet | undefined; if (!isArtifact(artifact)) { throw new ZkSyncEthersPluginError( - `You are trying to create a contract factory from an artifact, but you have not passed a valid artifact parameter.` + `You are trying to create a contract factory from an artifact, but you have not passed a valid artifact parameter.`, ); } @@ -135,22 +121,19 @@ export async function getContractFactoryFromArtifact< if (artifact.bytecode === '0x') { throw new ZkSyncEthersPluginError( `You are trying to create a contract factory for the contract ${artifact.contractName}, which is abstract and can't be deployed. -If you want to call a contract using ${artifact.contractName} as its interface use the "getContractAt" function instead.` +If you want to call a contract using ${artifact.contractName} as its interface use the "getContractAt" function instead.`, ); } return getContractFactoryByAbiAndBytecode(hre, artifact.abi, artifact.bytecode, wallet, deploymentType); } -async function getContractFactoryByAbiAndBytecode< - A extends any[] = any[], - I = Contract ->( +async function getContractFactoryByAbiAndBytecode( hre: HardhatRuntimeEnvironment, abi: any[], bytecode: ethers.BytesLike, wallet?: Wallet, - deploymentType?: DeploymentType + deploymentType?: DeploymentType, ): Promise> { if (!wallet) { wallet = await getWallet(hre); @@ -163,7 +146,7 @@ export async function getContractAt( hre: HardhatRuntimeEnvironment, nameOrAbi: string | any[], address: string | Address, - wallet?: Wallet + wallet?: Wallet, ): Promise { if (typeof nameOrAbi === 'string') { const artifact = await loadArtifact(hre, nameOrAbi); @@ -186,11 +169,11 @@ export async function getContractAtFromArtifact( hre: HardhatRuntimeEnvironment, artifact: ZkSyncArtifact, address: string | Address, - wallet?: Wallet + wallet?: Wallet, ): Promise { if (!isArtifact(artifact)) { throw new ZkSyncEthersPluginError( - `You are trying to create a contract by artifact, but you have not passed a valid artifact parameter.` + `You are trying to create a contract by artifact, but you have not passed a valid artifact parameter.`, ); } @@ -213,7 +196,7 @@ export async function deployContract( wallet?: Wallet, constructorArguments: any[] = [], overrides?: ethers.Overrides, - additionalFactoryDeps?: ethers.BytesLike[] + additionalFactoryDeps?: ethers.BytesLike[], ): Promise { if (!wallet) { wallet = await getWallet(hre); @@ -244,14 +227,14 @@ export async function deployContract( export async function loadArtifact( hre: HardhatRuntimeEnvironment, - contractNameOrFullyQualifiedName: string + contractNameOrFullyQualifiedName: string, ): Promise { const artifact = await hre.artifacts.readArtifact(contractNameOrFullyQualifiedName); // Verify that this artifact was compiled by the zkSync compiler, and not `solc` or `vyper`. if (artifact._format !== ZKSOLC_ARTIFACT_FORMAT_VERSION && artifact._format !== ZKVYPER_ARTIFACT_FORMAT_VERSION) { throw new ZkSyncEthersPluginError( - `Artifact ${contractNameOrFullyQualifiedName} was not compiled by zksolc or zkvyper` + `Artifact ${contractNameOrFullyQualifiedName} was not compiled by zksolc or zkvyper`, ); } return artifact as ZkSyncArtifact; @@ -266,12 +249,13 @@ export async function extractFactoryDeps(hre: HardhatRuntimeEnvironment, artifac async function extractFactoryDepsRecursive( hre: HardhatRuntimeEnvironment, artifact: ZkSyncArtifact, - visited: Set + visited: Set, ): Promise { // Load all the dependency bytecodes. // We transform it into an array of bytecodes. const factoryDeps: string[] = []; for (const dependencyHash in artifact.factoryDeps) { + if (!dependencyHash) continue; const dependencyContract = artifact.factoryDeps[dependencyHash]; if (!visited.has(dependencyContract)) { const dependencyArtifact = await loadArtifact(hre, dependencyContract); diff --git a/packages/hardhat-zksync-ethers/src/index.ts b/packages/hardhat-zksync-ethers/src/index.ts index c3c83eaf0..9bd712afe 100644 --- a/packages/hardhat-zksync-ethers/src/index.ts +++ b/packages/hardhat-zksync-ethers/src/index.ts @@ -4,6 +4,8 @@ import { extendEnvironment } from 'hardhat/config'; import { lazyObject } from 'hardhat/plugins'; import './type-extensions'; +import { ethers } from 'ethers'; +import { Address, DeploymentType } from 'zksync-ethers/build/src/types'; import { extractFactoryDeps, getContractAt, @@ -17,8 +19,6 @@ import { deployContract, } from './helpers'; import { FactoryOptions, ZkSyncArtifact } from './types'; -import { ethers } from 'ethers'; -import { Address, DeploymentType } from 'zksync-ethers/build/src/types'; extendEnvironment((hre) => { hre.zksyncEthers = lazyObject(() => { @@ -36,9 +36,12 @@ extendEnvironment((hre) => { getContractFactoryFromArtifact: ( artifact: ZkSyncArtifact, walletOrOptions?: Wallet | FactoryOptions, - deploymentType?: DeploymentType) => getContractFactoryFromArtifact(hre, artifact, walletOrOptions, deploymentType), - getContractAt: (nameOrAbi: string | any[], address: string | Address, wallet?: Wallet) => getContractAt(hre, nameOrAbi, address, wallet), - getContractAtFromArtifact: (artifact: ZkSyncArtifact, address: string | Address, wallet?: Wallet) => getContractAtFromArtifact(hre, artifact, address, wallet), + deploymentType?: DeploymentType, + ) => getContractFactoryFromArtifact(hre, artifact, walletOrOptions, deploymentType), + getContractAt: (nameOrAbi: string | any[], address: string | Address, wallet?: Wallet) => + getContractAt(hre, nameOrAbi, address, wallet), + getContractAtFromArtifact: (artifact: ZkSyncArtifact, address: string | Address, wallet?: Wallet) => + getContractAtFromArtifact(hre, artifact, address, wallet), extractFactoryDeps: (artifact: ZkSyncArtifact) => extractFactoryDeps(hre, artifact), loadArtifact: (name: string) => loadArtifact(hre, name), deployContract: ( @@ -46,7 +49,7 @@ extendEnvironment((hre) => { constructorArguments: any[], wallet?: Wallet, overrides?: ethers.Overrides, - additionalFactoryDeps?: ethers.BytesLike[] + additionalFactoryDeps?: ethers.BytesLike[], ) => deployContract(hre, artifact, wallet, (constructorArguments = []), overrides, additionalFactoryDeps), }; }); diff --git a/packages/hardhat-zksync-ethers/src/rich-wallets.ts b/packages/hardhat-zksync-ethers/src/rich-wallets.ts index e3fc19335..1324e4830 100644 --- a/packages/hardhat-zksync-ethers/src/rich-wallets.ts +++ b/packages/hardhat-zksync-ethers/src/rich-wallets.ts @@ -1,4 +1,4 @@ -export const rich_wallets = [ +export const richWallets = [ { address: '0x36615Cf349d7F6344891B1e7CA7C72883F5dc049', privateKey: '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110', diff --git a/packages/hardhat-zksync-ethers/src/types.ts b/packages/hardhat-zksync-ethers/src/types.ts index 1475f86d3..53e07264b 100644 --- a/packages/hardhat-zksync-ethers/src/types.ts +++ b/packages/hardhat-zksync-ethers/src/types.ts @@ -19,23 +19,17 @@ export interface FactoryOptions { wallet?: Wallet; } -export declare function getContractFactory< - A extends any[] = any[], - I = Contract ->( +export declare function getContractFactory( name: string, wallet?: Wallet, - deploymentType?: DeploymentType + deploymentType?: DeploymentType, ): Promise>; -export declare function getContractFactory< - A extends any[] = any[], - I = Contract ->( +export declare function getContractFactory( abi: any[], bytecode: ethers.BytesLike, wallet?: Wallet, - deploymentType?: DeploymentType + deploymentType?: DeploymentType, ): Promise>; export declare function deployContract( @@ -43,16 +37,13 @@ export declare function deployContract( constructorArguments: any[], wallet?: Wallet, overrides?: ethers.Overrides, - additionalFactoryDeps?: ethers.BytesLike[] + additionalFactoryDeps?: ethers.BytesLike[], ): Promise; -export declare function getContractFactoryFromArtifact< - A extends any[] = any[], - I = Contract ->( +export declare function getContractFactoryFromArtifact( artifact: ZkSyncArtifact, wallet?: Wallet, - deploymentType?: DeploymentType + deploymentType?: DeploymentType, ): Promise>; export interface HardhatZksyncEthersHelpers { @@ -71,6 +62,6 @@ export interface HardhatZksyncEthersHelpers { constructorArguments: any[], wallet?: Wallet, overrides?: ethers.Overrides, - additionalFactoryDeps?: ethers.BytesLike[] + additionalFactoryDeps?: ethers.BytesLike[], ) => Promise; } diff --git a/packages/hardhat-zksync-ethers/src/utils.ts b/packages/hardhat-zksync-ethers/src/utils.ts index f7bfd67f1..e46e2cdfd 100644 --- a/packages/hardhat-zksync-ethers/src/utils.ts +++ b/packages/hardhat-zksync-ethers/src/utils.ts @@ -1,8 +1,13 @@ -import { HardhatNetworkAccountsConfig, HardhatNetworkHDAccountsConfig, HardhatRuntimeEnvironment, HttpNetworkAccountsConfig } from 'hardhat/types'; -import { FactoryOptions, ZkSyncArtifact } from './types'; +import { + HardhatNetworkAccountsConfig, + HardhatNetworkHDAccountsConfig, + HardhatRuntimeEnvironment, + HttpNetworkAccountsConfig, +} from 'hardhat/types'; import { Wallet } from 'zksync-ethers'; +import { FactoryOptions, ZkSyncArtifact } from './types'; import { LOCAL_CHAIN_IDS } from './constants'; -import { rich_wallets } from './rich-wallets'; +import { richWallets } from './rich-wallets'; export function isHardhatNetworkHDAccountsConfig(object: any): object is HardhatNetworkHDAccountsConfig { return 'mnemonic' in object; @@ -22,12 +27,12 @@ export function isNumber(object: any): object is number { export async function getWalletsFromAccount( hre: HardhatRuntimeEnvironment, - accounts: HardhatNetworkAccountsConfig | HttpNetworkAccountsConfig + accounts: HardhatNetworkAccountsConfig | HttpNetworkAccountsConfig, ): Promise { - if (!accounts || accounts == 'remote') { + if (!accounts || accounts === 'remote') { const chainId = await hre.zksyncEthers.provider.send('eth_chainId', []); if (LOCAL_CHAIN_IDS.includes(chainId)) { - return rich_wallets.map((wallet) => new Wallet(wallet.privateKey, hre.zksyncEthers.provider)); + return richWallets.map((wallet) => new Wallet(wallet.privateKey, hre.zksyncEthers.provider)); } return []; } @@ -36,7 +41,7 @@ export async function getWalletsFromAccount( const accountPrivateKeys = accounts as string[]; const wallets = accountPrivateKeys.map( - (accountPrivateKey) => new Wallet(accountPrivateKey, hre.zksyncEthers.provider) + (accountPrivateKey) => new Wallet(accountPrivateKey, hre.zksyncEthers.provider), ); return wallets; } diff --git a/packages/hardhat-zksync-ethers/test/tests.ts b/packages/hardhat-zksync-ethers/test/tests.ts index f5d45bacb..1df39d666 100644 --- a/packages/hardhat-zksync-ethers/test/tests.ts +++ b/packages/hardhat-zksync-ethers/test/tests.ts @@ -1,7 +1,7 @@ import { assert } from 'chai'; -import { useEnvironment } from './helpers'; import { Contract, Wallet } from 'zksync-ethers'; -import { rich_wallets } from '../src/rich-wallets'; +import { richWallets } from '../src/rich-wallets'; +import { useEnvironment } from './helpers'; describe('Plugin tests', async function () { describe('successful-compilation artifact', async function () { @@ -61,7 +61,7 @@ describe('Plugin tests', async function () { assert.equal(await wallet.getAddress(), '0x36615Cf349d7F6344891B1e7CA7C72883F5dc049'); }); it('get specific wallet', async function () { - const wallet = await this.env.zksyncEthers.getWallet(rich_wallets[6].privateKey); + const wallet = await this.env.zksyncEthers.getWallet(richWallets[6].privateKey); assert.isDefined(wallet); assert.equal((await wallet.getAddress()).length, 42); @@ -127,7 +127,7 @@ describe('Plugin tests', async function () { assert.strictEqual( await this.env.zksyncEthers.provider.getBalance(secWallet.address), - 1000000000000000000000000000000n + 1000000000000000000000000000000n, ); }); it('should return the transaction count of the account', async function () { @@ -153,7 +153,7 @@ describe('Plugin tests', async function () { } catch (err: any) { assert.equal( err.message, - 'You are trying to create a contract factory for the contract IGreeter, which is abstract and can\'t be deployed.\nIf you want to call a contract using IGreeter as its interface use the "getContractAt" function instead.' + 'You are trying to create a contract factory for the contract IGreeter, which is abstract and can\'t be deployed.\nIf you want to call a contract using IGreeter as its interface use the "getContractAt" function instead.', ); } }); @@ -166,10 +166,7 @@ describe('Plugin tests', async function () { it('Should be able to send txs and make calls', async function () { const artifact = await this.env.zksyncEthers.loadArtifact('Greeter'); - const Greeter = await this.env.zksyncEthers.getContractFactory( - artifact.abi, - artifact.bytecode - ); + const Greeter = await this.env.zksyncEthers.getContractFactory(artifact.abi, artifact.bytecode); const greeter = await Greeter.deploy(); @@ -186,7 +183,10 @@ describe('Plugin tests', async function () { }); it('Should return an instance of a contract', async function () { const wallet = await this.env.zksyncEthers.getWallet(); - const contract = await this.env.zksyncEthers.getContractAt('Greeter', deployedGreeter.target.toString()); + const contract = await this.env.zksyncEthers.getContractAt( + 'Greeter', + deployedGreeter.target.toString(), + ); assert.exists(contract.greet); @@ -194,7 +194,10 @@ describe('Plugin tests', async function () { }); it('Should return an instance of an interface', async function () { const wallet = await this.env.zksyncEthers.getWallet(); - const contract = await this.env.zksyncEthers.getContractAt('IGreeter', deployedGreeter.target.toString()); + const contract = await this.env.zksyncEthers.getContractAt( + 'IGreeter', + deployedGreeter.target.toString(), + ); assert.isNotNull(contract.interface.getFunction('greet')); @@ -235,13 +238,13 @@ describe('Plugin tests', async function () { }); it('get invalid third wallet', async function () { try { - const wallet = await this.env.zksyncEthers.getWallet(3); + const _ = await this.env.zksyncEthers.getWallet(3); } catch (err: any) { assert.equal(err.message, 'Account private key with specified index is not found'); } }); it('get wallet with private key', async function () { - const wallet = await this.env.zksyncEthers.getWallet(rich_wallets[6].privateKey); + const wallet = await this.env.zksyncEthers.getWallet(richWallets[6].privateKey); assert.isDefined(wallet); assert.equal((await wallet.getAddress()).length, 42); @@ -268,13 +271,13 @@ describe('Plugin tests', async function () { }); it('get invalid second wallet with mnemonic', async function () { try { - const wallet = await this.env.zksyncEthers.getWallet(1); + const _ = await this.env.zksyncEthers.getWallet(1); } catch (err: any) { assert.equal(err.message, 'Account private key with specified index is not found'); } }); it('get wallet with private key and with mnemonic', async function () { - const wallet = await this.env.zksyncEthers.getWallet(rich_wallets[6].privateKey); + const wallet = await this.env.zksyncEthers.getWallet(richWallets[6].privateKey); assert.isDefined(wallet); assert.equal((await wallet.getAddress()).length, 42); @@ -289,20 +292,20 @@ describe('Plugin tests', async function () { useEnvironment('simple-accounts', 'zkSyncNetworkEmptyAccounts'); it('get default wallet from empty accounts', async function () { try { - const wallet = await this.env.zksyncEthers.getWallet(); + const _ = await this.env.zksyncEthers.getWallet(); } catch (err: any) { assert.equal(err.message, 'Accounts are not configured for this network'); } }); it('get invalid second wallet with empty accounts', async function () { try { - const wallet = await this.env.zksyncEthers.getWallet(1); + const _ = await this.env.zksyncEthers.getWallet(1); } catch (err: any) { assert.equal(err.message, 'Account private key with specified index is not found'); } }); it('get wallet with private key and with empty accounts', async function () { - const wallet = await this.env.zksyncEthers.getWallet(rich_wallets[6].privateKey); + const wallet = await this.env.zksyncEthers.getWallet(richWallets[6].privateKey); assert.isDefined(wallet); assert.equal((await wallet.getAddress()).length, 42); diff --git a/packages/hardhat-zksync-node/.eslintrc.js b/packages/hardhat-zksync-node/.eslintrc.js index 56ca0b4bd..b0d17045a 100644 --- a/packages/hardhat-zksync-node/.eslintrc.js +++ b/packages/hardhat-zksync-node/.eslintrc.js @@ -4,4 +4,18 @@ module.exports = { project: `${__dirname}/eslint-tsconfig.json`, sourceType: "module", }, + rules: { + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "property", + "format": ["camelCase", "PascalCase", "snake_case"], + "leadingUnderscore": "allow", + "filter": { + "regex": "^[a-zA-Z0-9-_]+$", + "match": false + } + }, + ], + }, }; diff --git a/packages/hardhat-zksync-node/CHANGELOG.md b/packages/hardhat-zksync-node/CHANGELOG.md index 7c81c4e9c..dc844f7d2 100644 --- a/packages/hardhat-zksync-node/CHANGELOG.md +++ b/packages/hardhat-zksync-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @matterlabs/hardhat-zksync-node +## [1.0.1](https://github.com/matter-labs/hardhat-zksync/compare/@matterlabs/hardhat-zksync-node@1.0.0...@matterlabs/hardhat-zksync-node-v1.0.1) (2023-12-22) + + +### Fixes + +* **docs:** update readme files ([#612](https://github.com/matter-labs/hardhat-zksync/issues/612)) ([682338e](https://github.com/matter-labs/hardhat-zksync/commit/682338e60f52021206325ff6eeec2c394a118642)) + ## 1.0.0 ### Major Changes diff --git a/packages/hardhat-zksync-node/README.md b/packages/hardhat-zksync-node/README.md index c5783a7b5..bcbe1b75f 100644 --- a/packages/hardhat-zksync-node/README.md +++ b/packages/hardhat-zksync-node/README.md @@ -1,3 +1,72 @@ -# hardhat-zksync-node +# hardhat-zksync-node 🚀 -[Hardhat](https://hardhat.org/) plugin to run the zkSync era-test-node locally. +zkSync Era [Hardhat](https://hardhat.org/) plugin to run the zkSync era-test-node locally. + +![Era Logo](https://github.com/matter-labs/era-contracts/raw/main/eraLogo.svg) + +## ⚠️ Version Compatibility Warning + +Ensure you are using the correct version of the plugin with ethers: +- For plugin version **<1.0.0**: + - Compatible with ethers **v5**. + +- For plugin version **≥1.0.0**: + - Compatible with ethers **v6** (⭐ Recommended) + +## 📥 Installation + +To install **hardhat-zksync-node** plugin, run: + +`npm install -D @matterlabs/hardhat-zksync-node` + +or + +`yarn add -D @matterlabs/hardhat-zksync-node` + +## 🕹 Commands + +`yarn hardhat node-zksync` + +This command runs a local zkSync In-memory node by initiating a JSON-RPC server. It uses the provided or default configurations to set up and run the zkSync node, allowing for blockchain operations in a local environment. The command also handles tasks such as downloading the necessary JSON-RPC server binary if it's not already present. + +| 🔧 Command | 📄 Description | +|-------------------------------------|----------------------------------------------------------------------------------------------------------------------| +| --port | Port on which the server should listen. Defaults to 8011. | +| --log | Log filter level. Accepted values are: error, warn, info, and debug. Defaults to info. | +| --log-file-path | Path to the file where logs should be written. Defaults to era_test_node.log | +| --cache | Type of cache to use. Accepted values are: none, disk, and memory. Defaults to disk. | +| --cache-dir | Directory location for the disk cache. Defaults to .cache | +| --reset-cache | Flag to reset the local disk cache. | +| --show-calls | Determines which call debug information to show. Accepted values are: none, user, system, and all. Defaults to none. | +| --show-storage-logs | Determines which storage logs to show. Accepted values are: none, read, write, and all. Defaults to none. | +| --show-vm-details | Specifies the level of Virtual Machine (VM) details to show. Accepted values are: none and all. Defaults to none. | +| --show-gas-details | Specifies the level of gas details to show. Accepted values are: none and all. Defaults to none. | +| --resolve-hashes | When enabled, it makes the debug log more readable but might decrease performance. | +| --dev-use-local-contracts | Flag to load locally compiled system contracts. Useful when making changes to system contracts or bootloader. | +| ---fork | Starts a local network that is a fork of another network. Accepted values are: testnet, mainnet, or a specific URL. | +| --fork-block-number | Specifies the block height at which to fork. | +| --replay-tx | Transaction hash to replay. | + +**Restrictions**: The --replay-tx and --fork-block-number parameters cannot be specified simultaneously. + +## 📝 Documentation + +In addition to the [hardhat-zksync-node](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-node.html), zkSync's Era [website](https://era.zksync.io/docs/) offers a variety of resources including: + +[Guides to get started](https://era.zksync.io/docs/dev/building-on-zksync/hello-world.html): Learn how to start building on zkSync Era.\ +[Hardhat zkSync Era plugins](https://era.zksync.io/docs/tools/hardhat/getting-started.html): Overview and guides for all Hardhat zkSync Era plugins.\ +[Hyperscaling](https://era.zksync.io/docs/reference/concepts/hyperscaling.html#what-are-hyperchains): Deep dive into hyperscaling on zkSync Era. + +## 🤝 Contributing + +Contributions are always welcome! Feel free to open any issue or send a pull request. + +Go to [CONTRIBUTING.md](https://github.com/matter-labs/hardhat-zksync/blob/main/.github/CONTRIBUTING.md) to learn about steps and best practices for contributing to zkSync hardhat tooling base repository. + + +## 🙌 Feedback, help and news + +[zkSync Era Discord server](https://join.zksync.dev/): for questions and feedback.\ +[Follow zkSync Era on Twitter](https://twitter.com/zksync) + +## Happy building! 👷‍♀️👷‍♂️ \ No newline at end of file diff --git a/packages/hardhat-zksync-node/package.json b/packages/hardhat-zksync-node/package.json index ce4966a7a..471363104 100644 --- a/packages/hardhat-zksync-node/package.json +++ b/packages/hardhat-zksync-node/package.json @@ -1,6 +1,6 @@ { "name": "@matterlabs/hardhat-zksync-node", - "version": "1.0.0", + "version": "1.0.1", "description": "Hardhat plugin to run zkSync era-test-node locally", "repository": "github:matter-labs/hardhat-zksync", "homepage": "https://github.com/matter-labs/hardhat-zksync/tree/main/packages/hardhat-zksync-node", @@ -36,7 +36,12 @@ "@matterlabs/hardhat-zksync-solc": "^1.0.5", "axios": "^1.6.2", "chalk": "4.1.2", - "fs-extra": "^11.1.1" + "fs-extra": "^11.1.1", + "proxyquire": "^2.1.3", + "chai": "^4.3.6", + "undici": "^5.14.0", + "sinon-chai": "^3.7.0", + "sinon": "^16.0.0" }, "devDependencies": { "@types/chai": "^4.2.0", @@ -47,26 +52,22 @@ "@types/sinon-chai": "^3.2.10", "@typescript-eslint/eslint-plugin": "6.13.1", "@typescript-eslint/parser": "6.13.1", - "chai": "^4.3.6", "eslint": "^8.54.0", "eslint-config-prettier": "9.0.0", "eslint-plugin-import": "2.29.0", "eslint-plugin-no-only-tests": "3.1.0", "eslint-plugin-prettier": "5.0.1", - "hardhat": "^2.19.2", + "hardhat": "^2.19.4", "mocha": "^9.2.1", "prettier": "3.1.0", - "proxyquire": "^2.1.3", "rimraf": "^3.0.2", - "sinon": "^16.0.0", - "sinon-chai": "^3.7.0", "ts-node": "^10.6.0", "typescript": "^5.1.6", "zksync-ethers": "^6.0.0", "c8": "^8.0.1" }, "peerDependencies": { - "hardhat": "^2.19.2", + "hardhat": "^2.19.4", "zksync-ethers": "^6.0.0" }, "prettier": { diff --git a/packages/hardhat-zksync-node/src/constants.ts b/packages/hardhat-zksync-node/src/constants.ts index a6576d05b..ed5ef0367 100644 --- a/packages/hardhat-zksync-node/src/constants.ts +++ b/packages/hardhat-zksync-node/src/constants.ts @@ -3,7 +3,8 @@ export const PLUGIN_NAME = '@matterlabs/hardhat-zksync-node'; export const ZKNODE_BIN_OWNER = 'matter-labs'; export const ZKNODE_BIN_REPOSITORY_NAME = 'era-test-node'; // User agent of MacOSX Chrome 120.0.0.0 -export const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; +export const USER_AGENT = + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; export const TASK_NODE_ZKSYNC = 'node-zksync'; export const TASK_NODE_ZKSYNC_CREATE_SERVER = 'node-zksync:create-server'; diff --git a/packages/hardhat-zksync-node/src/downloader.ts b/packages/hardhat-zksync-node/src/downloader.ts index 59f098b37..ed53e6eeb 100644 --- a/packages/hardhat-zksync-node/src/downloader.ts +++ b/packages/hardhat-zksync-node/src/downloader.ts @@ -1,5 +1,6 @@ import path from 'path'; import fse from 'fs-extra'; +import chalk from 'chalk'; import { download, getAssetToDownload, getRelease } from './utils'; import { ZkSyncNodePluginError } from './errors'; import { @@ -10,7 +11,6 @@ import { ZKNODE_BIN_OWNER, ZKNODE_BIN_REPOSITORY_NAME, } from './constants'; -import chalk from 'chalk'; export class RPCServerDownloader { private readonly _binaryDir: string; @@ -30,30 +30,32 @@ export class RPCServerDownloader { return; } - if (this.isLatestTag()) { + if (this._isLatestTag()) { if (!(await this._isLatestReleaseInfoValid())) { - const release = await getRelease(ZKNODE_BIN_OWNER, ZKNODE_BIN_REPOSITORY_NAME, USER_AGENT, this._tag); - - if (await this._isBinaryPathExists(release.tag_name)) { - await this._postProcessDownload(release.tag_name); + const releaseData = await getRelease( + ZKNODE_BIN_OWNER, + ZKNODE_BIN_REPOSITORY_NAME, + USER_AGENT, + this._tag, + ); + + if (await this._isBinaryPathExists(releaseData.tag_name)) { + await this._postProcessDownload(releaseData.tag_name); return; } - await this._download(release); + await this._download(releaseData); return; } const info = await this._getLatestReleaseInfo(); - if (info && await this._isBinaryPathExists(info.latest)) { + if (info && (await this._isBinaryPathExists(info.latest))) { return; } const release = await getRelease(ZKNODE_BIN_OWNER, ZKNODE_BIN_REPOSITORY_NAME, USER_AGENT, this._tag); - if (info - && info.latest === release.tag_name - && await this._isBinaryPathExists(release.tag_name)) { - + if (info && info.latest === release.tag_name && (await this._isBinaryPathExists(release.tag_name))) { await this._postProcessDownload(release.tag_name); return; } @@ -75,13 +77,13 @@ export class RPCServerDownloader { await this._createBinaryPath(release.tag_name), PLUGIN_NAME, release.tag_name, - 30000 + 30000, ); await this._postProcessDownload(release.tag_name); console.info(chalk.green('era-test-node binary downloaded successfully')); } catch (error: any) { throw new ZkSyncNodePluginError( - `Error downloading binary from URL ${assetToDownload.browser_download_url}: ${error.message}` + `Error downloading binary from URL ${assetToDownload.browser_download_url}: ${error.message}`, ); } } @@ -102,13 +104,13 @@ export class RPCServerDownloader { const binaryPath = await this.getBinaryPath(tag); fse.chmodSync(binaryPath, 0o755); - if (this.isLatestTag()) { + if (this._isLatestTag()) { await fse.writeJSON(this._releaseInfoFilePath, { latest: tag }); } } private async _getReleaseTag() { - return this.isLatestTag() ? (await this._getLatestReleaseInfo()).latest : this._tag; + return this._isLatestTag() ? (await this._getLatestReleaseInfo()).latest : this._tag; } private async _isLatestReleaseInfoValid() { @@ -134,7 +136,7 @@ export class RPCServerDownloader { return await fse.readJSON(this._releaseInfoFilePath); } - private isLatestTag() { + private _isLatestTag() { return this._tag === 'latest'; } } diff --git a/packages/hardhat-zksync-node/src/index.ts b/packages/hardhat-zksync-node/src/index.ts index 957dfe7d2..667524f7c 100644 --- a/packages/hardhat-zksync-node/src/index.ts +++ b/packages/hardhat-zksync-node/src/index.ts @@ -7,6 +7,7 @@ import { TASK_TEST_RUN_MOCHA_TESTS, } from 'hardhat/builtin-tasks/task-names'; +import { HARDHAT_NETWORK_NAME } from 'hardhat/plugins'; import { MAX_PORT_ATTEMPTS, START_PORT, @@ -27,7 +28,6 @@ import { } from './utils'; import { RPCServerDownloader } from './downloader'; import { ZkSyncNodePluginError } from './errors'; -import { HARDHAT_NETWORK_NAME } from 'hardhat/plugins'; // Subtask to download the binary subtask(TASK_NODE_ZKSYNC_DOWNLOAD_BINARY, 'Downloads the JSON-RPC server binary') @@ -42,7 +42,7 @@ subtask(TASK_NODE_ZKSYNC_DOWNLOAD_BINARY, 'Downloads the JSON-RPC server binary' force: boolean; tag: string; }, - hre + _hre, ) => { // Directory where the binaries are stored const rpcServerBinaryDir = await getRPCServerBinariesDir(); @@ -53,7 +53,7 @@ subtask(TASK_NODE_ZKSYNC_DOWNLOAD_BINARY, 'Downloads the JSON-RPC server binary' // Download binary if needed await downloader.downloadIfNeeded(force); return await downloader.getBinaryPath(); - } + }, ); // Subtask to create the server @@ -66,13 +66,13 @@ subtask(TASK_NODE_ZKSYNC_CREATE_SERVER, 'Creates a JSON-RPC server for zkSync no }: { binaryPath: string; }, - hre + _hre, ) => { // Create the server const server: JsonRpcServer = new JsonRpcServer(binaryPath); return server; - } + }, ); // Main task of the plugin. It starts the server and listens for requests. @@ -83,53 +83,53 @@ task(TASK_NODE_ZKSYNC, 'Starts a JSON-RPC server for zkSync node') 'logFilePath', 'Path to the file where logs should be written - default: `era_test_node.log`', undefined, - types.string + types.string, ) .addOptionalParam('cache', 'Cache type (none, disk, memory) - default: disk', undefined, types.string) .addOptionalParam( 'cacheDir', 'Cache directory location for `disk` cache - default: `.cache`', undefined, - types.string + types.string, ) .addFlag('resetCache', 'Reset the local `disk` cache') .addOptionalParam( 'showCalls', 'Show call debug information (none, user, system, all) - default: none', undefined, - types.string + types.string, ) .addOptionalParam( 'showStorageLogs', 'Show storage log information (none, read, write, all) - default: none', undefined, - types.string + types.string, ) .addOptionalParam( 'showVmDetails', 'Show VM details information (none, all) - default: none', undefined, - types.string + types.string, ) .addOptionalParam( 'showGasDetails', 'Show Gas details information (none, all) - default: none', undefined, - types.string + types.string, ) .addFlag( 'resolveHashes', - 'Try to contact openchain to resolve the ABI & topic names. It enabled, it makes debug log more readable, but will decrease the performance' + 'Try to contact openchain to resolve the ABI & topic names. It enabled, it makes debug log more readable, but will decrease the performance', ) .addFlag( 'devUseLocalContracts', - 'Loads the locally compiled system contracts (useful when doing changes to system contracts or bootloader)' + 'Loads the locally compiled system contracts (useful when doing changes to system contracts or bootloader)', ) .addOptionalParam( 'fork', 'Starts a local network that is a fork of another network (testnet, mainnet, http://XXX:YY)', undefined, - types.string + types.string, ) .addOptionalParam('forkBlockNumber', 'Fork at the specified block height', undefined, types.int) .addOptionalParam('replayTx', 'Transaction hash to replay', undefined, types.string) @@ -172,7 +172,7 @@ task(TASK_NODE_ZKSYNC, 'Starts a JSON-RPC server for zkSync node') replayTx: string; tag: string; }, - { run } + { run }, ) => { const commandArgs = constructCommandArgs({ port, @@ -203,12 +203,12 @@ task(TASK_NODE_ZKSYNC, 'Starts a JSON-RPC server for zkSync node') } catch (error: any) { throw new ZkSyncNodePluginError(`Failed when running node: ${error.message}`); } - } + }, ); subtask(TASK_RUN_NODE_ZKSYNC_IN_SEPARATE_PROCESS, 'Runs a Hardhat node-zksync task in a separate process.') .addVariadicPositionalParam('taskArgs', 'Arguments for the Hardhat node-zksync task.') - .setAction(async ({ taskArgs = [] }, hre) => { + .setAction(async ({ taskArgs = [] }, _hre) => { const currentPort = await getAvailablePort(START_PORT, MAX_PORT_ATTEMPTS); const adjustedArgs = adjustTaskArgsForPort(taskArgs, currentPort); @@ -240,7 +240,7 @@ task( grep?: string; }, { run, network }, - runSuper + runSuper, ) => { if (network.zksync !== true || network.name !== HARDHAT_NETWORK_NAME) { return await runSuper(); @@ -289,7 +289,7 @@ task( } catch (error: any) { throw new ZkSyncNodePluginError(`Failed when running node: ${error.message}`); } - } + }, ); export { ZkSyncProviderAdapter } from './zksync-provider-adapter'; diff --git a/packages/hardhat-zksync-node/src/server.ts b/packages/hardhat-zksync-node/src/server.ts index d0a35c948..620a853fe 100644 --- a/packages/hardhat-zksync-node/src/server.ts +++ b/packages/hardhat-zksync-node/src/server.ts @@ -6,7 +6,8 @@ import { PROCESS_TERMINATION_SIGNALS } from './constants'; export class JsonRpcServer { private serverProcess: ChildProcess | null = null; - constructor(private readonly serverBinaryPath: string) { } + // eslint-disable-next-line @typescript-eslint/naming-convention + constructor(private readonly serverBinaryPath: string) {} public listen(args: string[] = [], blockProcess: boolean = true): Promise { return new Promise((resolve, reject) => { @@ -29,7 +30,7 @@ export class JsonRpcServer { this.serverProcess.on('error', (error) => { console.info(chalk.red('Error running the server:', error)); - reject(new Error('Error running the server: ' + error.message)); + reject(new Error(`Error running the server: ${error.message}`)); }); this.serverProcess.on('exit', (code, signal) => { @@ -54,7 +55,7 @@ export class JsonRpcServer { // this.serverProcess.on('exit', () => { // resolve(); // }); - } + } resolve(); }); } diff --git a/packages/hardhat-zksync-node/src/utils.ts b/packages/hardhat-zksync-node/src/utils.ts index 9d0a4d6d8..8786bfe29 100644 --- a/packages/hardhat-zksync-node/src/utils.ts +++ b/packages/hardhat-zksync-node/src/utils.ts @@ -6,8 +6,9 @@ import net from 'net'; import fse from 'fs-extra'; import { exec } from 'child_process'; import type { Dispatcher } from 'undici'; -import { ZkSyncProviderAdapter } from './zksync-provider-adapter'; import { Provider } from 'zksync-ethers'; +import { getCompilersDir } from 'hardhat/internal/util/global-dir'; +import { ZkSyncProviderAdapter } from './zksync-provider-adapter'; import { ALLOWED_CACHE_VALUES, @@ -29,9 +30,6 @@ import { import { ZkSyncNodePluginError } from './errors'; import { CommandArguments } from './types'; -import { getCompilersDir } from 'hardhat/internal/util/global-dir'; -import exp from 'constants'; - // Generates command arguments for running the era-test-node binary export function constructCommandArgs(args: CommandArguments): string[] { const commandArgs: string[] = []; @@ -104,13 +102,13 @@ export function constructCommandArgs(args: CommandArguments): string[] { if (args.forkBlockNumber && args.replayTx) { throw new ZkSyncNodePluginError( - `Cannot specify both --fork-block-number and --replay-tx. Please specify only one of them.` + `Cannot specify both --fork-block-number and --replay-tx. Please specify only one of them.`, ); } if ((args.replayTx || args.forkBlockNumber) && !args.fork) { throw new ZkSyncNodePluginError( - `Cannot specify --replay-tx or --fork-block-number parameters without --fork param.` + `Cannot specify --replay-tx or --fork-block-number parameters without --fork param.`, ); } @@ -155,7 +153,7 @@ export async function getRPCServerBinariesDir(): Promise { // Get latest release from GitHub of the era-test-node binary export async function getRelease(owner: string, repo: string, userAgent: string, tag?: string): Promise { let url = `https://api.github.com/repos/${owner}/${repo}/releases/`; - url = tag != 'latest' ? url + `tags/${tag}` : url + `latest`; + url = tag !== 'latest' ? `${url}tags/${tag}` : `${url}latest`; try { const response = await axios.get(url, { @@ -171,7 +169,7 @@ export async function getRelease(owner: string, repo: string, userAgent: string, throw new ZkSyncNodePluginError( `Failed to get ${tag} release for ${owner}/${repo}. Status: ${ error.response.status - }, Data: ${JSON.stringify(error.response.data)}` + }, Data: ${JSON.stringify(error.response.data)}`, ); } else if (error.request) { // The request was made but no response was received @@ -192,7 +190,7 @@ export async function getAssetToDownload(latestRelease: any): Promise { throw new ZkSyncNodePluginError(`Unsupported platform: ${platform}`); } - const prefix = 'era_test_node-' + latestRelease.tag_name; + const prefix = `era_test_node-${latestRelease.tag_name}`; const expectedAssetName = `${prefix}-${getArch()}-${platform}.tar.gz`; return latestRelease.assets.find((asset: any) => asset.name === expectedAssetName); @@ -203,7 +201,7 @@ function isTarGzFile(filePath: string): boolean { } function ensureTarGzExtension(filePath: string): string { - return filePath.endsWith('.tar.gz') ? filePath : filePath + '.tar.gz'; + return filePath.endsWith('.tar.gz') ? filePath : `${filePath}.tar.gz`; } async function ensureDirectory(filePath: string): Promise { @@ -231,7 +229,7 @@ async function extractTarGz(tmpFilePath: string, filePath: string): Promise { - exec(`tar -xzf ${tmpFilePath} -C ${tempExtractionDir}`, (error, stdout, stderr) => { + exec(`tar -xzf ${tmpFilePath} -C ${tempExtractionDir}`, (error, stdout, _stderr) => { if (error) { reject(error); } else { @@ -259,13 +257,13 @@ export async function download( userAgent: string, version: string, timeoutMillis = 10000, - extraHeaders: { [name: string]: string } = {} + extraHeaders: { [name: string]: string } = {}, ) { const { pipeline } = await import('stream'); const { getGlobalDispatcher, request } = await import('undici'); const streamPipeline = util.promisify(pipeline); - let dispatcher: Dispatcher = getGlobalDispatcher(); + const dispatcher: Dispatcher = getGlobalDispatcher(); // Fetch the url const response = await request(url, { @@ -391,7 +389,7 @@ function getNetworkConfig(url: string) { gasMultiplier: 1, httpHeaders: {}, timeout: 20000, - url: url, + url, ethNetwork: NETWORK_ETH.LOCALHOST, zksync: true, }; diff --git a/packages/hardhat-zksync-node/src/zksync-provider-adapter.ts b/packages/hardhat-zksync-node/src/zksync-provider-adapter.ts index 9c1652375..e649d42a4 100644 --- a/packages/hardhat-zksync-node/src/zksync-provider-adapter.ts +++ b/packages/hardhat-zksync-node/src/zksync-provider-adapter.ts @@ -7,19 +7,19 @@ export class ZkSyncProviderAdapter extends EventEmitter implements EthereumProvi super(); } - async send(method: string, params: any): Promise { - //@ts-ignore + public async send(method: string, params: any): Promise { + // @ts-ignore return await this._zkSyncProvider.send(method, params); } - async request(payload: { method: string; params?: any[] }): Promise { - //@ts-ignore + public async request(payload: { method: string; params?: any[] }): Promise { + // @ts-ignore return await this._zkSyncProvider.send(payload.method, payload.params ?? []); } - async sendAsync(payload: any, callback: (error: Error | null, result?: any) => void): Promise { + public async sendAsync(payload: any, callback: (error: Error | null, result?: any) => void): Promise { try { - //@ts-ignore + // @ts-ignore const result = await this._zkSyncProvider.send(payload.method, payload.params); callback(null, result); } catch (error) { diff --git a/packages/hardhat-zksync-node/test/tests.ts b/packages/hardhat-zksync-node/test/tests.ts index 9e6cb67c7..efa56cbb7 100644 --- a/packages/hardhat-zksync-node/test/tests.ts +++ b/packages/hardhat-zksync-node/test/tests.ts @@ -1,5 +1,4 @@ -import { expect, assert } from 'chai'; -import chai from 'chai'; +import chai, { expect, assert } from 'chai'; import sinonChai from 'sinon-chai'; import chalk from 'chalk'; import sinon from 'sinon'; @@ -78,7 +77,7 @@ describe('node-zksync plugin', async function () { it('should throw error when both forkBlockNumber and replayTx are specified in all args', () => { const args = { fork: 'mainnet', forkBlockNumber: 100, replayTx: '0x1234567890abcdef' }; expect(() => constructCommandArgs(args)).to.throw( - 'Cannot specify both --fork-block-number and --replay-tx. Please specify only one of them.' + 'Cannot specify both --fork-block-number and --replay-tx. Please specify only one of them.', ); }); @@ -90,7 +89,7 @@ describe('node-zksync plugin', async function () { it('should throw error when there is no fork arg', () => { const args = { forkBlockNumber: 100 }; expect(() => constructCommandArgs(args)).to.throw( - 'Cannot specify --replay-tx or --fork-block-number parameters without --fork param.' + 'Cannot specify --replay-tx or --fork-block-number parameters without --fork param.', ); }); @@ -306,14 +305,14 @@ describe('node-zksync plugin', async function () { it('should correctly identify an available port', async () => { const port = await getPort(); const result = await utils.isPortAvailable(port); - expect(result).to.be.true; + expect(result).to.equal(true); }); it('should correctly identify a port that is in use', async () => { const port = await getPort(); server = net.createServer().listen(port); const result = await utils.isPortAvailable(port); - expect(result).to.be.false; + expect(result).to.equal(false); }); }); @@ -348,7 +347,6 @@ describe('node-zksync plugin', async function () { }); describe('RPCServerDownloader', () => { - const mockRelease = { url: 'https://api.github.com/repos/matter-labs/era-test-node/releases/assets/latest', tag_name: 'v1.0.0', @@ -356,9 +354,9 @@ describe('node-zksync plugin', async function () { const mockAsset = { browser_download_url: - 'https://github.com/matter-labs/era-test-node/releases/download/v0.1.0/era_test_node-v0.1.0-aarch64-apple-darwin.tar.gz', - } - + 'https://github.com/matter-labs/era-test-node/releases/download/v0.1.0/era_test_node-v0.1.0-aarch64-apple-darwin.tar.gz', + }; + let downloadStub: sinon.SinonStub; let existsSyncStub: sinon.SinonStub; let postProcessDownloadStub: sinon.SinonStub; @@ -433,12 +431,12 @@ describe('node-zksync plugin', async function () { signal?: string; } - let spawnStub = sinon.stub(); + const spawnStub = sinon.stub(); let consoleInfoStub: sinon.SinonStub; // Because we cannot stub the execSync method directly, we use proxyquire to stub the entire 'child_process' module const { JsonRpcServer } = proxyquire('../src/server', { - 'child_process': { spawn: spawnStub }, + child_process: { spawn: spawnStub }, }); beforeEach(() => { @@ -482,7 +480,7 @@ describe('node-zksync plugin', async function () { sinon.assert.calledWithMatch( consoleInfoStub, - chalk.yellow(`Received ${PROCESS_TERMINATION_SIGNALS[0]} signal. The server process has exited.`) + chalk.yellow(`Received ${PROCESS_TERMINATION_SIGNALS[0]} signal. The server process has exited.`), ); }); @@ -494,8 +492,8 @@ describe('node-zksync plugin', async function () { try { server.listen(); expect.fail('Expected an error to be thrown'); - } catch (error: any) { - expect(error.message).to.equal('Expected an error to be thrown'); + } catch (err: any) { + expect(err.message).to.equal('Expected an error to be thrown'); } }); }); @@ -506,7 +504,7 @@ describe('node-zksync plugin', async function () { let serverProcess: ChildProcess; - function delay(ms: number): Promise { + function _delay(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } diff --git a/packages/hardhat-zksync-solc/.eslintrc.js b/packages/hardhat-zksync-solc/.eslintrc.js index 56ca0b4bd..ce7756b34 100644 --- a/packages/hardhat-zksync-solc/.eslintrc.js +++ b/packages/hardhat-zksync-solc/.eslintrc.js @@ -4,4 +4,18 @@ module.exports = { project: `${__dirname}/eslint-tsconfig.json`, sourceType: "module", }, + rules: { + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "property", + "format": ["camelCase", "PascalCase", "snake_case"], + "leadingUnderscore": "allow", + "filter": { + "regex": "^[a-zA-Z0-9-_/*\\.]+$", + "match": false + } + }, + ], + }, }; diff --git a/packages/hardhat-zksync-solc/CHANGELOG.md b/packages/hardhat-zksync-solc/CHANGELOG.md index ad05fbb7c..7b2cedd77 100644 --- a/packages/hardhat-zksync-solc/CHANGELOG.md +++ b/packages/hardhat-zksync-solc/CHANGELOG.md @@ -1,5 +1,12 @@ # @matterlabs/hardhat-zksync-solc +## [1.0.6](https://github.com/matter-labs/hardhat-zksync/compare/@matterlabs/hardhat-zksync-solc@1.0.5...@matterlabs/hardhat-zksync-solc-v1.0.6) (2023-12-22) + + +### Fixes + +* **docs:** update readme files ([#612](https://github.com/matter-labs/hardhat-zksync/issues/612)) ([682338e](https://github.com/matter-labs/hardhat-zksync/commit/682338e60f52021206325ff6eeec2c394a118642)) + ## 1.0.5 ### Patch Changes diff --git a/packages/hardhat-zksync-solc/README.md b/packages/hardhat-zksync-solc/README.md index 4d7cb6c59..ccd578bf9 100644 --- a/packages/hardhat-zksync-solc/README.md +++ b/packages/hardhat-zksync-solc/README.md @@ -1,6 +1,92 @@ -# hardhat-zksync-solc +# hardhat-zksync-solc 🚀 -[Hardhat](https://hardhat.org/) plugin to compile smart contracts for the zkSync network. +zkSync Era [Hardhat](https://hardhat.org/) plugin to compile smart contracts for the zkSync network. +![Era Logo](https://github.com/matter-labs/era-contracts/raw/main/eraLogo.svg) -Check out the documentation page for the [zksync solc plugin](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-solc.html) for more detailed explanation on how to use hardhat-zksync-solc. \ No newline at end of file +## 📥 Installation + +To install **hardhat-zksync-solc** plugin, run: + +`npm install -D @matterlabs/hardhat-zksync-solc` + +or + +`yarn add -D @matterlabs/hardhat-zksync-solc` + +## 🔩 Configuration + +Import the package in the hardhat.config.ts file: + +`import "@matterlabs/hardhat-zksync-solc";` + +Any configuration parameters should be added inside a zksolc property in the hardhat.config.ts file: + +``` +zksolc: { + version: "latest", // optional. + settings: { + compilerPath: "zksolc", // optional. Ignored for compilerSource "docker". Can be used if compiler is located in a specific folder + libraries:{}, // optional. References to non-inlinable libraries + missingLibrariesPath: "./.zksolc-libraries-cache/missingLibraryDependencies.json" // optional. This path serves as a cache that stores all the libraries that are missing or have dependencies on other libraries. A `hardhat-zksync-deploy` plugin uses this cache later to compile and deploy the libraries, especially when the `deploy-zksync:libraries` task is executed + isSystem: false, // optional. Enables Yul instructions available only for zkSync system contracts and libraries + forceEvmla: false, // optional. Falls back to EVM legacy assembly if there is a bug with Yul + optimizer: { + enabled: true, // optional. True by default + mode: '3' // optional. 3 by default, z to optimize bytecode size + }, + experimental: { + dockerImage: '', // deprecated + tag: '' // deprecated + }, + } +}, +``` +| 🔧 Properties | 📄 Description | +|-----------------------------|----------------------------------------------------------------------------------------------------------------------| +| version | zksolc compiler version. | +| compilerSource | Indicates the compiler source and can be either binary (default) or docker (deprecated). | +| compilerPath | (optional) field with the path to the zksolc binary. By default, the binary in $PATH is used | +| libraries | If your contract uses non-inlinable libraries as dependencies, they have to be defined here. | +| missingLibrariesPath | (optional) serves as a cache that stores all the libraries that are missing or have dependencies on other libraries. | +| isSystem | Required if contracts use enables Yul instructions available only for zkSync system contracts and libraries | +| forceEvmla | Falls back to EVM legacy assembly if there is an issue with the Yul IR compilation pipeline. | +| optimizer | Compiler optimizations (enabled: true (default) or false), mode: 3 (default) recommended for most projects. | +| metadata | Metadata settings. If the option is omitted, the metadata hash appends by default: bytecodeHash. Can only be none. | +| dockerImage | (deprecated) option used to identify the name of the compiler docker image. | + +Learn more about [compiling libraries here](https://era.zksync.io/docs/tools/hardhat/compiling-libraries.html) + +Setting the forceEvmla field to true can have the following negative impacts: + +- No support for recursion. +- No support for internal function pointers. +- Possible contract size and performance impact. + +## 🕹 Commands + +`yarn hardhat compile` + +Compiles all the smart contracts in the contracts directory and creates the artifacts-zk folder with all the compilation artifacts, including factory dependencies for the contracts, which could be used for contract deployment. + +## 📝 Documentation + +In addition to the [hardhat-zksync-solc](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-solc.html), zkSync's Era [website](https://era.zksync.io/docs/) offers a variety of resources including: + +[Guides to get started](https://era.zksync.io/docs/dev/building-on-zksync/hello-world.html): Learn how to start building on zkSync Era.\ +[Hardhat zkSync Era plugins](https://era.zksync.io/docs/tools/hardhat/getting-started.html): Overview and guides for all Hardhat zkSync Era plugins.\ +[Hyperscaling](https://era.zksync.io/docs/reference/concepts/hyperscaling.html#what-are-hyperchains): Deep dive into hyperscaling on zkSync Era. + +## 🤝 Contributing + +Contributions are always welcome! Feel free to open any issue or send a pull request. + +Go to [CONTRIBUTING.md](https://github.com/matter-labs/hardhat-zksync/blob/main/.github/CONTRIBUTING.md) to learn about steps and best practices for contributing to zkSync hardhat tooling base repository. + + +## 🙌 Feedback, help and news + +[zkSync Era Discord server](https://join.zksync.dev/): for questions and feedback.\ +[Follow zkSync Era on Twitter](https://twitter.com/zksync) + +## Happy building! 👷‍♀️👷‍♂️ \ No newline at end of file diff --git a/packages/hardhat-zksync-solc/package.json b/packages/hardhat-zksync-solc/package.json index 4561a384a..f76289bcf 100644 --- a/packages/hardhat-zksync-solc/package.json +++ b/packages/hardhat-zksync-solc/package.json @@ -1,6 +1,6 @@ { "name": "@matterlabs/hardhat-zksync-solc", - "version": "1.0.5", + "version": "1.0.6", "description": "Hardhat plugin to compile smart contracts for the zkSync network", "repository": "github:matter-labs/hardhat-zksync", "homepage": "https://github.com/matter-labs/hardhat-zksync/tree/main/packages/hardhat-zksync-solc", @@ -22,7 +22,7 @@ "fmt": "yarn prettier --write", "eslint": "eslint 'src/**/*.ts' 'test/**/*.ts'", "prettier": "prettier 'src/**/*.ts' 'test/**/*.ts'", - "test": "c8 mocha test/tests.ts --no-timeout --exit", + "test": "c8 mocha --recursive \"test/tests/**/*.ts\" --exit", "build": "tsc --build .", "clean": "rimraf dist" }, @@ -37,8 +37,12 @@ "chalk": "4.1.2", "dockerode": "^4.0.0", "fs-extra": "^11.1.1", + "chai": "^4.3.6", + "undici": "^5.14.0", "semver": "^7.5.1", - "proper-lockfile": "^4.1.2" + "proper-lockfile": "^4.1.2", + "sinon": "^16.0.0", + "sinon-chai": "^3.7.0" }, "devDependencies": { "@types/chai": "^4.2.0", @@ -49,13 +53,12 @@ "@types/fs-extra": "^5.1.0", "@typescript-eslint/eslint-plugin": "6.13.1", "@typescript-eslint/parser": "6.13.1", - "chai": "^4.3.6", "eslint": "^8.54.0", "eslint-config-prettier": "9.0.0", "eslint-plugin-import": "2.29.0", "eslint-plugin-no-only-tests": "3.1.0", "eslint-plugin-prettier": "5.0.1", - "hardhat": "^2.19.2", + "hardhat": "^2.19.4", "mocha": "^9.2.1", "prettier": "3.1.0", "rimraf": "^3.0.2", @@ -64,7 +67,7 @@ "c8": "^8.0.1" }, "peerDependencies": { - "hardhat": "^2.19.2" + "hardhat": "^2.19.4" }, "prettier": { "tabWidth": 4, diff --git a/packages/hardhat-zksync-solc/src/compile/binary.ts b/packages/hardhat-zksync-solc/src/compile/binary.ts index 6c3a1367d..d75629e2f 100644 --- a/packages/hardhat-zksync-solc/src/compile/binary.ts +++ b/packages/hardhat-zksync-solc/src/compile/binary.ts @@ -1,10 +1,17 @@ import { exec } from 'child_process'; import { ZkSolcConfig } from '../types'; -export async function compileWithBinary(input: any, config: ZkSolcConfig, solcPath: string, detectMissingLibrariesMode: boolean = false): Promise { +export async function compileWithBinary( + input: any, + config: ZkSolcConfig, + solcPath: string, + detectMissingLibrariesMode: boolean = false, +): Promise { const { compilerPath, isSystem, forceEvmla } = config.settings; - const processCommand = `${compilerPath} --standard-json ${isSystem ? '--system-mode' : ''} ${forceEvmla ? '--force-evmla' : ''} --solc ${solcPath} ${detectMissingLibrariesMode ? '--detect-missing-libraries' : ''}`; + const processCommand = `${compilerPath} --standard-json ${isSystem ? '--system-mode' : ''} ${ + forceEvmla ? '--force-evmla' : '' + } --solc ${solcPath} ${detectMissingLibrariesMode ? '--detect-missing-libraries' : ''}`; const output: string = await new Promise((resolve, reject) => { const process = exec( @@ -17,7 +24,7 @@ export async function compileWithBinary(input: any, config: ZkSolcConfig, solcPa return reject(err); } resolve(stdout); - } + }, ); process.stdin!.write(JSON.stringify(input)); diff --git a/packages/hardhat-zksync-solc/src/compile/docker.ts b/packages/hardhat-zksync-solc/src/compile/docker.ts index b9a794af4..b8946c45e 100644 --- a/packages/hardhat-zksync-solc/src/compile/docker.ts +++ b/packages/hardhat-zksync-solc/src/compile/docker.ts @@ -10,9 +10,9 @@ import { import Docker, { ContainerCreateOptions } from 'dockerode'; import { CompilerInput } from 'hardhat/types'; import { Writable } from 'stream'; +import chalk from 'chalk'; import { ZkSyncSolcPluginError } from '../errors'; import { ZkSolcConfig } from '../types'; -import chalk from 'chalk'; async function runContainer(docker: Docker, image: Image, command: string[], input: string) { const createOptions: ContainerCreateOptions = { @@ -32,7 +32,7 @@ async function runContainer(docker: Docker, image: Image, command: string[], inp let output = Buffer.from(''); let chunk = Buffer.from(''); const stream = new Writable({ - write: function (incoming: Buffer, _encoding, next) { + write(incoming: Buffer, _encoding, next) { // Please refer to the 'Stream format' chapter at // https://docs.docker.com/engine/api/v1.37/#operation/ContainerAttach // to understand the details of this implementation. @@ -80,7 +80,7 @@ export async function validateDockerIsInstalled() { if (!(await HardhatDocker.isInstalled())) { throw new ZkSyncSolcPluginError( 'Docker Desktop is not installed.\n' + - 'Please install it by following the instructions on https://www.docker.com/get-started' + 'Please install it by following the instructions on https://www.docker.com/get-started', ); } } @@ -119,7 +119,7 @@ export async function compileWithDocker( input: CompilerInput, docker: HardhatDocker, image: Image, - zksolcConfig: ZkSolcConfig + zksolcConfig: ZkSolcConfig, ) { const command = ['zksolc', '--standard-json']; if (zksolcConfig.settings.isSystem) { @@ -139,7 +139,7 @@ export async function compileWithDocker( } catch { throw new ZkSyncSolcPluginError(compilerOutput); } - })() + })(), ); } @@ -150,7 +150,7 @@ export async function getSolcVersion(docker: HardhatDocker, image: Image) { (async () => { const versionOutput = await runContainer(dockerInstance, image, ['solc', '--version'], ''); return versionOutput.split('\n')[1]; - })() + })(), ); } @@ -161,14 +161,14 @@ async function handleCommonErrors(promise: Promise): Promise { if (error instanceof DockerNotRunningError || error instanceof DockerBadGatewayError) { throw new ZkSyncSolcPluginError( 'Docker Desktop is not running.\nPlease open it and wait until it finishes booting.', - error + error, ); } if (error instanceof DockerHubConnectionError) { throw new ZkSyncSolcPluginError( 'Error connecting to Docker Hub.\nPlease check your internet connection.', - error + error, ); } @@ -179,7 +179,7 @@ async function handleCommonErrors(promise: Promise): Promise { if (error instanceof ImageDoesntExistError) { throw new ZkSyncSolcPluginError( `Docker image ${HardhatDocker.imageToRepoTag(error.image)} doesn't exist.\n` + - 'Make sure you chose a valid zksolc version.' + 'Make sure you chose a valid zksolc version.', ); } diff --git a/packages/hardhat-zksync-solc/src/compile/downloader.ts b/packages/hardhat-zksync-solc/src/compile/downloader.ts index fb7b7deae..d3bf65d78 100644 --- a/packages/hardhat-zksync-solc/src/compile/downloader.ts +++ b/packages/hardhat-zksync-solc/src/compile/downloader.ts @@ -1,9 +1,17 @@ -import path from "path"; -import fsExtra from "fs-extra"; -import chalk from "chalk"; +import path from 'path'; +import fsExtra from 'fs-extra'; +import chalk from 'chalk'; import { spawnSync } from 'child_process'; -import { download, getZksolcUrl, isURL, isVersionInRange, saltFromUrl, saveDataToFile, getLatestRelease } from "../utils"; +import { + download, + getZksolcUrl, + isURL, + isVersionInRange, + saltFromUrl, + saveDataToFile, + getLatestRelease, +} from '../utils'; import { COMPILER_BINARY_CORRUPTION_ERROR, COMPILER_VERSION_INFO_FILE_NOT_FOUND_ERROR, @@ -15,7 +23,8 @@ import { ZKSOLC_COMPILER_VERSION_MIN_VERSION, ZKSOLC_BIN_OWNER, ZKSOLC_BIN_REPOSITORY_NAME, - USER_AGENT} from "../constants"; + USER_AGENT, +} from '../constants'; import { ZkSyncSolcPluginError } from './../errors'; export interface CompilerVersionInfo { @@ -27,7 +36,6 @@ export interface CompilerVersionInfo { * This class is responsible for downloading the zksolc binary. */ export class ZksolcCompilerDownloader { - public static async getDownloaderWithVersionValidated( version: string, configCompilerPath: string, @@ -35,7 +43,10 @@ export class ZksolcCompilerDownloader { ): Promise { if (!ZksolcCompilerDownloader._instance) { let compilerVersionInfo = await ZksolcCompilerDownloader._getCompilerVersionInfo(compilersDir); - if (compilerVersionInfo === undefined || (await ZksolcCompilerDownloader._shouldDownloadCompilerVersionInfo(compilersDir))) { + if ( + compilerVersionInfo === undefined || + (await ZksolcCompilerDownloader._shouldDownloadCompilerVersionInfo(compilersDir)) + ) { await ZksolcCompilerDownloader._downloadCompilerVersionInfo(compilersDir); compilerVersionInfo = await ZksolcCompilerDownloader._getCompilerVersionInfo(compilersDir); } @@ -47,12 +58,18 @@ export class ZksolcCompilerDownloader { if (version === 'latest' || version === compilerVersionInfo.latest) { version = compilerVersionInfo.latest; } else if (!isVersionInRange(version, compilerVersionInfo)) { - throw new ZkSyncSolcPluginError(COMPILER_VERSION_RANGE_ERROR(version, compilerVersionInfo.minVersion, compilerVersionInfo.latest)); + throw new ZkSyncSolcPluginError( + COMPILER_VERSION_RANGE_ERROR(version, compilerVersionInfo.minVersion, compilerVersionInfo.latest), + ); } else { console.info(chalk.yellow(COMPILER_VERSION_WARNING(version, compilerVersionInfo.latest))); - }; + } - ZksolcCompilerDownloader._instance = new ZksolcCompilerDownloader(version, configCompilerPath, compilersDir); + ZksolcCompilerDownloader._instance = new ZksolcCompilerDownloader( + version, + configCompilerPath, + compilersDir, + ); } return ZksolcCompilerDownloader._instance; @@ -119,7 +136,10 @@ export class ZksolcCompilerDownloader { public async downloadCompiler(): Promise { let compilerVersionInfo = await ZksolcCompilerDownloader._getCompilerVersionInfo(this._compilersDirectory); - if (compilerVersionInfo === undefined || (await ZksolcCompilerDownloader._shouldDownloadCompilerVersionInfo(this._compilersDirectory))) { + if ( + compilerVersionInfo === undefined || + (await ZksolcCompilerDownloader._shouldDownloadCompilerVersionInfo(this._compilersDirectory)) + ) { await ZksolcCompilerDownloader._downloadCompilerVersionInfo(this._compilersDirectory); compilerVersionInfo = await ZksolcCompilerDownloader._getCompilerVersionInfo(this._compilersDirectory); } @@ -128,7 +148,9 @@ export class ZksolcCompilerDownloader { throw new ZkSyncSolcPluginError(COMPILER_VERSION_INFO_FILE_NOT_FOUND_ERROR); } if (!isVersionInRange(this._version, compilerVersionInfo)) { - throw new ZkSyncSolcPluginError(COMPILER_VERSION_RANGE_ERROR(this._version, compilerVersionInfo.minVersion, compilerVersionInfo.latest)); + throw new ZkSyncSolcPluginError( + COMPILER_VERSION_RANGE_ERROR(this._version, compilerVersionInfo.minVersion, compilerVersionInfo.latest), + ); } try { @@ -152,8 +174,8 @@ export class ZksolcCompilerDownloader { const releaseToSave = { latest: latestRelease, - minVersion: ZKSOLC_COMPILER_VERSION_MIN_VERSION - } + minVersion: ZKSOLC_COMPILER_VERSION_MIN_VERSION, + }; const savePath = this._getCompilerVersionInfoPath(compilersDir); await saveDataToFile(releaseToSave, savePath); } @@ -193,7 +215,6 @@ export class ZksolcCompilerDownloader { if (!(await fsExtra.pathExists(compilerVersionInfoPath))) { return undefined; } - return await this._readCompilerVersionInfo(compilerVersionInfoPath); } @@ -211,7 +232,7 @@ export class ZksolcCompilerDownloader { .match(/\d+\.\d+\.\d+/) ?.toString(); - if (versionOutput.status !== 0 || version == null) { + if (versionOutput.status !== 0 || version === null) { throw new ZkSyncSolcPluginError(COMPILER_BINARY_CORRUPTION_ERROR(compilerPath)); } } diff --git a/packages/hardhat-zksync-solc/src/compile/index.ts b/packages/hardhat-zksync-solc/src/compile/index.ts index cada2fe90..433b5aced 100644 --- a/packages/hardhat-zksync-solc/src/compile/index.ts +++ b/packages/hardhat-zksync-solc/src/compile/index.ts @@ -1,7 +1,10 @@ -import { ZkSolcConfig } from '../types'; -import { compileWithBinary } from './binary'; import { HardhatDocker, Image } from '@nomiclabs/hardhat-docker'; import semver from 'semver'; +import { CompilerInput } from 'hardhat/types'; +import { ZkSolcConfig } from '../types'; +import { ZkSyncSolcPluginError } from '../errors'; +import { findMissingLibraries, mapMissingLibraryDependencies, writeLibrariesToFile } from '../utils'; +import { DETECT_MISSING_LIBRARY_MODE_COMPILER_VERSION } from '../constants'; import { validateDockerIsInstalled, createDocker, @@ -10,19 +13,16 @@ import { compileWithDocker, getSolcVersion, } from './docker'; -import { CompilerInput } from 'hardhat/types'; -import { ZkSyncSolcPluginError } from '../errors'; -import { findMissingLibraries, mapMissingLibraryDependencies, writeLibrariesToFile } from '../utils'; -import { DETECT_MISSING_LIBRARY_MODE_COMPILER_VERSION } from '../constants'; +import { compileWithBinary } from './binary'; export async function compile(zksolcConfig: ZkSolcConfig, input: CompilerInput, solcPath?: string) { let compiler: ICompiler; - if (zksolcConfig.compilerSource == 'binary') { - if (solcPath == null) { + if (zksolcConfig.compilerSource === 'binary') { + if (solcPath === null) { throw new ZkSyncSolcPluginError('solc executable is not specified'); } - compiler = new BinaryCompiler(solcPath); - } else if (zksolcConfig.compilerSource == 'docker') { + compiler = new BinaryCompiler(solcPath!); + } else if (zksolcConfig.compilerSource === 'docker') { compiler = await DockerCompiler.initialize(zksolcConfig); } else { throw new ZkSyncSolcPluginError(`Incorrect compiler source: ${zksolcConfig.compilerSource}`); @@ -48,7 +48,7 @@ export class BinaryCompiler implements ICompiler { if (!config.settings.missingLibrariesPath) { throw new ZkSyncSolcPluginError('Missing libraries path is not specified'); } - + const missingLibraryDependencies = mapMissingLibraryDependencies(zkSolcOutput, missingLibraries); // Write missing libraries to file const missingLibrariesPath = config.settings.missingLibrariesPath!; @@ -64,7 +64,10 @@ export class BinaryCompiler implements ICompiler { } export class DockerCompiler implements ICompiler { - protected constructor(public dockerImage: Image, public docker: HardhatDocker) {} + protected constructor( + public dockerCompilerImage: Image, + public docker: HardhatDocker, + ) {} public static async initialize(config: ZkSolcConfig): Promise { await validateDockerIsInstalled(); @@ -78,12 +81,11 @@ export class DockerCompiler implements ICompiler { public async compile(input: CompilerInput, config: ZkSolcConfig) { // We don't check here for missing libraries because docker is using older versions of zksolc and it's deprecated - - return await compileWithDocker(input, this.docker, this.dockerImage, config); + return await compileWithDocker(input, this.docker, this.dockerCompilerImage, config); } public async solcVersion() { - const versionOutput = await getSolcVersion(this.docker, this.dockerImage); + const versionOutput = await getSolcVersion(this.docker, this.dockerCompilerImage); const longVersion = versionOutput.match(/^Version: (.*)$/)![1]; const version = longVersion.split('+')[0]; return { version, longVersion }; diff --git a/packages/hardhat-zksync-solc/src/compile/solc-downloader.ts b/packages/hardhat-zksync-solc/src/compile/solc-downloader.ts index d7f381945..b9cb9a784 100644 --- a/packages/hardhat-zksync-solc/src/compile/solc-downloader.ts +++ b/packages/hardhat-zksync-solc/src/compile/solc-downloader.ts @@ -1,9 +1,9 @@ -import path from "path"; -import fsExtra from "fs-extra"; -import chalk from "chalk"; +import path from 'path'; +import fsExtra from 'fs-extra'; +import chalk from 'chalk'; import { spawnSync } from 'child_process'; -import { download, isVersionInRange, saveDataToFile, getLatestRelease, getZkVmSolcUrl } from "../utils"; +import { download, isVersionInRange, saveDataToFile, getLatestRelease, getZkVmSolcUrl } from '../utils'; import { DEFAULT_COMPILER_VERSION_INFO_CACHE_PERIOD, DEFAULT_TIMEOUT_MILISECONDS, @@ -15,7 +15,8 @@ import { COMPILER_VERSION_RANGE_ERROR_ZKVM_SOLC, COMPILER_VERSION_WARNING_ZKVM_SOLC, COMPILER_BINARY_CORRUPTION_ERROR_ZKVM_SOLC, - ZKVM_SOLC_COMPILER_VERSION_MIN_VERSION} from "../constants"; + ZKVM_SOLC_COMPILER_VERSION_MIN_VERSION, +} from '../constants'; import { ZkSyncSolcPluginError } from './../errors'; export interface CompilerVersionInfo { @@ -27,7 +28,6 @@ export interface CompilerVersionInfo { * This class is responsible for downloading the zksolc binary. */ export class ZkVmSolcCompilerDownloader { - public static async getDownloaderWithVersionValidated( zkVmSolcVersion: string, solcVersion: string, @@ -35,7 +35,10 @@ export class ZkVmSolcCompilerDownloader { ): Promise { if (!ZkVmSolcCompilerDownloader._instance) { let compilerVersionInfo = await ZkVmSolcCompilerDownloader._getCompilerVersionInfo(compilersDir); - if (compilerVersionInfo === undefined || (await ZkVmSolcCompilerDownloader._shouldDownloadCompilerVersionInfo(compilersDir))) { + if ( + compilerVersionInfo === undefined || + (await ZkVmSolcCompilerDownloader._shouldDownloadCompilerVersionInfo(compilersDir)) + ) { await ZkVmSolcCompilerDownloader._downloadCompilerVersionInfo(compilersDir); compilerVersionInfo = await ZkVmSolcCompilerDownloader._getCompilerVersionInfo(compilersDir); } @@ -47,12 +50,24 @@ export class ZkVmSolcCompilerDownloader { if (zkVmSolcVersion === 'latest' || zkVmSolcVersion === compilerVersionInfo.latest) { zkVmSolcVersion = compilerVersionInfo.latest; } else if (!isVersionInRange(zkVmSolcVersion, compilerVersionInfo)) { - throw new ZkSyncSolcPluginError(COMPILER_VERSION_RANGE_ERROR_ZKVM_SOLC(zkVmSolcVersion, compilerVersionInfo.minVersion, compilerVersionInfo.latest)); + throw new ZkSyncSolcPluginError( + COMPILER_VERSION_RANGE_ERROR_ZKVM_SOLC( + zkVmSolcVersion, + compilerVersionInfo.minVersion, + compilerVersionInfo.latest, + ), + ); } else { - console.info(chalk.yellow(COMPILER_VERSION_WARNING_ZKVM_SOLC(zkVmSolcVersion, compilerVersionInfo.latest))); - }; + console.info( + chalk.yellow(COMPILER_VERSION_WARNING_ZKVM_SOLC(zkVmSolcVersion, compilerVersionInfo.latest)), + ); + } - ZkVmSolcCompilerDownloader._instance = new ZkVmSolcCompilerDownloader(solcVersion, zkVmSolcVersion, compilersDir); + ZkVmSolcCompilerDownloader._instance = new ZkVmSolcCompilerDownloader( + solcVersion, + zkVmSolcVersion, + compilersDir, + ); } return ZkVmSolcCompilerDownloader._instance; @@ -113,7 +128,10 @@ export class ZkVmSolcCompilerDownloader { public async downloadCompiler(): Promise { let compilerVersionInfo = await ZkVmSolcCompilerDownloader._getCompilerVersionInfo(this._compilersDirectory); - if (compilerVersionInfo === undefined || (await ZkVmSolcCompilerDownloader._shouldDownloadCompilerVersionInfo(this._compilersDirectory))) { + if ( + compilerVersionInfo === undefined || + (await ZkVmSolcCompilerDownloader._shouldDownloadCompilerVersionInfo(this._compilersDirectory)) + ) { await ZkVmSolcCompilerDownloader._downloadCompilerVersionInfo(this._compilersDirectory); compilerVersionInfo = await ZkVmSolcCompilerDownloader._getCompilerVersionInfo(this._compilersDirectory); } @@ -123,7 +141,13 @@ export class ZkVmSolcCompilerDownloader { } if (!isVersionInRange(this._zkVmSolcVersion, compilerVersionInfo)) { - throw new ZkSyncSolcPluginError(COMPILER_VERSION_RANGE_ERROR_ZKVM_SOLC(this._zkVmSolcVersion, compilerVersionInfo.minVersion, compilerVersionInfo.latest)); + throw new ZkSyncSolcPluginError( + COMPILER_VERSION_RANGE_ERROR_ZKVM_SOLC( + this._zkVmSolcVersion, + compilerVersionInfo.minVersion, + compilerVersionInfo.latest, + ), + ); } try { @@ -143,11 +167,11 @@ export class ZkVmSolcCompilerDownloader { We are currently limited in that each new version requires an update of the plugin version. */ private static async _downloadCompilerVersionInfo(compilersDir: string): Promise { - const latestRelease = await getLatestRelease(ZKSOLC_BIN_OWNER, ZKVM_SOLC_BIN_REPOSITORY_NAME, USER_AGENT, ""); + const latestRelease = await getLatestRelease(ZKSOLC_BIN_OWNER, ZKVM_SOLC_BIN_REPOSITORY_NAME, USER_AGENT, ''); const releaseToSave = { latest: latestRelease.split('-')[1], - minVersion: ZKVM_SOLC_COMPILER_VERSION_MIN_VERSION - } + minVersion: ZKVM_SOLC_COMPILER_VERSION_MIN_VERSION, + }; const savePath = this._getCompilerVersionInfoPath(compilersDir); await saveDataToFile(releaseToSave, savePath); } @@ -200,7 +224,7 @@ export class ZkVmSolcCompilerDownloader { .match(/\d+\.\d+\.\d+/) ?.toString(); - if (versionOutput.status !== 0 || version == null) { + if (versionOutput.status !== 0 || version === null) { throw new ZkSyncSolcPluginError(COMPILER_BINARY_CORRUPTION_ERROR_ZKVM_SOLC(compilerPath)); } } diff --git a/packages/hardhat-zksync-solc/src/compile/solc-js-executable.ts b/packages/hardhat-zksync-solc/src/compile/solc-js-executable.ts index 5a78bba82..da9369d16 100644 --- a/packages/hardhat-zksync-solc/src/compile/solc-js-executable.ts +++ b/packages/hardhat-zksync-solc/src/compile/solc-js-executable.ts @@ -17,14 +17,14 @@ function packageExists(packagePath: string): boolean { // Returns the path to the package, or undefined if it doesn't exist function findPackagePath(packageName: string, workingDir: string): string | undefined { let currentDir = workingDir; - let packagePath = currentDir + '/node_modules/' + packageName; - + let packagePath = `${currentDir}/node_modules/${packageName}`; + while (currentDir !== '/') { if (packageExists(packagePath)) { return packagePath; } currentDir = path.dirname(currentDir); - packagePath = currentDir + '/node_modules/' + packageName; + packagePath = `${currentDir}/node_modules/${packageName}`; } return undefined; @@ -32,14 +32,12 @@ function findPackagePath(packageName: string, workingDir: string): string | unde // Returns a solc-js wrapper async function getSolc(_pathToSolcJs: string, workingDir: string) { - // + // const packagePath = findPackagePath('solc/wrapper', workingDir); // @ts-ignore const { default: solcWrapper } = await import(packagePath); - const _loadedSolc = solcWrapper( - _loadCompilerSources(_pathToSolcJs) - ); + const _loadedSolc = solcWrapper(_loadCompilerSources(_pathToSolcJs)); return _loadedSolc; } @@ -54,45 +52,42 @@ export function _loadCompilerSources(compilerPath: string) { return require(compilerPath); } - const previousHook = Module._extensions[".js"]; + const previousHook = Module._extensions['.js']; - Module._extensions[".js"] = function ( - module: NodeJS.Module, - filename: string - ) { - const content = fs.readFileSync(filename, "utf8"); + Module._extensions['.js'] = function (module: NodeJS.Module, filename: string) { + const content = fs.readFileSync(filename, 'utf8'); Object.getPrototypeOf(module)._compile.call(module, content, filename); }; const loadedSolc = require(compilerPath); - Module._extensions[".js"] = previousHook; + Module._extensions['.js'] = previousHook; return loadedSolc; } // Read stdin into a string async function readStdin(): Promise { - return new Promise(resolve => { - let inputString = ''; - process.stdin.on('data', chunk => inputString += chunk); - process.stdin.on('end', () => resolve(inputString)); + return new Promise((resolve) => { + let inputString = ''; + process.stdin.on('data', (chunk) => (inputString += chunk)); + process.stdin.on('end', () => resolve(inputString)); }); } -(async function () { +const _ = (async function () { // Strings that need to be replaced by the caller when generating the file const solcJsPath = 'SOLCJS_PATH'; const workingDir = 'WORKING_DIR'; - + // Wrapped solc-js compiler const solcJsCompiler = await getSolc(solcJsPath, workingDir); - if (process.argv.includes("--version")) { + if (process.argv.includes('--version')) { const version = await solcJsCompiler.version(); - process.stdout.write("solc, the solidity compiler commandline interface" + os.EOL); - process.stdout.write("Version: " + version + os.EOL); + process.stdout.write(`solc, the solidity compiler commandline interface${os.EOL}`); + process.stdout.write(`Version: ${version}${os.EOL}`); } else { const input = await readStdin(); const jsonOutput = solcJsCompiler.compile(input); diff --git a/packages/hardhat-zksync-solc/src/constants.ts b/packages/hardhat-zksync-solc/src/constants.ts index 351bb8555..a95153cb1 100644 --- a/packages/hardhat-zksync-solc/src/constants.ts +++ b/packages/hardhat-zksync-solc/src/constants.ts @@ -7,7 +7,8 @@ export const ZKVM_SOLC_BIN_REPOSITORY = 'https://github.com/matter-labs/era-soli export const DEFAULT_TIMEOUT_MILISECONDS = 30000; export const DETECT_MISSING_LIBRARY_MODE_COMPILER_VERSION = '1.3.14'; // User agent of MacOSX Chrome 120.0.0.0 -export const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; +export const USER_AGENT = + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; export const defaultZkSolcConfig: ZkSolcConfig = { version: 'latest', @@ -23,45 +24,52 @@ export const defaultZkSolcConfig: ZkSolcConfig = { experimental: {}, }, }; - +/* eslint-disable @typescript-eslint/naming-convention */ export const ZKSOLC_COMPILERS_SELECTOR_MAP = { - '1.3.5': [ - 'abi', - 'evm.methodIdentifiers', - 'storageLayout', - 'irOptimized', - 'evm.legacyAssembly', - 'ast', - ] + '1.3.5': ['abi', 'evm.methodIdentifiers', 'storageLayout', 'irOptimized', 'evm.legacyAssembly', 'ast'], }; +/* eslint-enable @typescript-eslint/naming-convention */ -export const ZKSOLC_COMPILER_VERSION_MIN_VERSION = "1.3.13"; +export const ZKSOLC_COMPILER_VERSION_MIN_VERSION = '1.3.13'; export const ZKSOLC_BIN_OWNER = 'matter-labs'; export const ZKSOLC_BIN_REPOSITORY_NAME = 'zksolc-bin'; export const ZKVM_SOLC_BIN_REPOSITORY_NAME = 'era-solidity'; -export const ZKVM_SOLC_COMPILER_VERSION_MIN_VERSION = "1.0.0"; +export const ZKVM_SOLC_COMPILER_VERSION_MIN_VERSION = '1.0.0'; export const DEFAULT_COMPILER_VERSION_INFO_CACHE_PERIOD = 24 * 60 * 60 * 1000; // 24 hours export const COMPILER_VERSION_INFO_FILE_NOT_FOUND_ERROR = 'Could not find zksolc compiler version info file.'; -export const COMPILER_VERSION_RANGE_ERROR = (version: string, minVersion: string, latestVersion: string) => `The zksolc compiler version (${version}) in the hardhat config file is not within the allowed range. Please use versions ${minVersion} to ${latestVersion}.`; -export const COMPILER_VERSION_WARNING = (version: string, latestVersion: string) => `The zksolc compiler version in your Hardhat config file (${version}) is not the latest. We recommend using the latest version ${latestVersion}.`; -export const COMPILER_BINARY_CORRUPTION_ERROR = (compilerPath: string) => `The zksolc binary at path ${compilerPath} is corrupted. Please delete it and try again.`; -export const COMPILING_INFO_MESSAGE = (zksolcVersion: string, solcVersion: string) => `Compiling contracts for zkSync Era with zksolc v${zksolcVersion} and solc v${solcVersion}`; +export const COMPILER_VERSION_RANGE_ERROR = (version: string, minVersion: string, latestVersion: string) => + `The zksolc compiler version (${version}) in the hardhat config file is not within the allowed range. Please use versions ${minVersion} to ${latestVersion}.`; +export const COMPILER_VERSION_WARNING = (version: string, latestVersion: string) => + `The zksolc compiler version in your Hardhat config file (${version}) is not the latest. We recommend using the latest version ${latestVersion}.`; +export const COMPILER_BINARY_CORRUPTION_ERROR = (compilerPath: string) => + `The zksolc binary at path ${compilerPath} is corrupted. Please delete it and try again.`; +export const COMPILING_INFO_MESSAGE = (zksolcVersion: string, solcVersion: string) => + `Compiling contracts for zkSync Era with zksolc v${zksolcVersion} and solc v${solcVersion}`; -export const COMPILING_INFO_MESSAGE_ZKVM_SOLC = (zksolcVersion: string, zkvmSolcVersion: string) => `Compiling contracts for zkSync Era with zksolc v${zksolcVersion} and zkvm-solc v${zkvmSolcVersion}`; -export const COMPILER_BINARY_CORRUPTION_ERROR_ZKVM_SOLC = (compilerPath: string) => `The zkvm-solc binary at path ${compilerPath} is corrupted. Please delete it and try again.`; -export const COMPILER_VERSION_RANGE_ERROR_ZKVM_SOLC = (version: string, minVersion: string, latestVersion: string) => `The zkvm-solc compiler version (${version}) in the hardhat config file is not within the allowed range. Please use versions ${minVersion} to ${latestVersion}.`; -export const COMPILER_VERSION_WARNING_ZKVM_SOLC = (version: string, latestVersion: string) => `The zkvm-solc compiler version in your Hardhat config file (${version}) is not the latest. We recommend using the latest version ${latestVersion}.`; +export const COMPILING_INFO_MESSAGE_ZKVM_SOLC = (zksolcVersion: string, zkvmSolcVersion: string) => + `Compiling contracts for zkSync Era with zksolc v${zksolcVersion} and zkvm-solc v${zkvmSolcVersion}`; +export const COMPILER_BINARY_CORRUPTION_ERROR_ZKVM_SOLC = (compilerPath: string) => + `The zkvm-solc binary at path ${compilerPath} is corrupted. Please delete it and try again.`; +export const COMPILER_VERSION_RANGE_ERROR_ZKVM_SOLC = (version: string, minVersion: string, latestVersion: string) => + `The zkvm-solc compiler version (${version}) in the hardhat config file is not within the allowed range. Please use versions ${minVersion} to ${latestVersion}.`; +export const COMPILER_VERSION_WARNING_ZKVM_SOLC = (version: string, latestVersion: string) => + `The zkvm-solc compiler version in your Hardhat config file (${version}) is not the latest. We recommend using the latest version ${latestVersion}.`; -export const COMPILERS_CONFLICT_ZKVM_SOLC = (version: string) => `Solidity compiler versions in your Hardhat config file are in conflict for version ${version}. Please use only version with eraVersion or only version without eraVersion.`; +export const COMPILERS_CONFLICT_ZKVM_SOLC = (version: string) => + `Solidity compiler versions in your Hardhat config file are in conflict for version ${version}. Please use only version with eraVersion or only version without eraVersion.`; -export const COMPILER_VERSION_INFO_FILE_NOT_FOUND_ERROR_ZKVM_SOLC = 'Could not find zkvm-solc compiler version info file.'; +export const COMPILER_VERSION_INFO_FILE_NOT_FOUND_ERROR_ZKVM_SOLC = + 'Could not find zkvm-solc compiler version info file.'; -export const MISSING_LIBRARIES_NOTICE = 'zksolc compiler detected missing libraries! For more details, visit: https://era.zksync.io/docs/tools/hardhat/compiling-libraries.html.'; -export const COMPILE_AND_DEPLOY_LIBRARIES_INSTRUCTIONS = 'To compile and deploy libraries, please run: `yarn hardhat deploy-zksync:libraries --private-key `'; -export const MISSING_LIBRARY_LINK = 'For more details on how to use deploy-zksync:libraries task from hardhat-zksync-deploy plugin, visit: https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-deploy.html.'; +export const MISSING_LIBRARIES_NOTICE = + 'zksolc compiler detected missing libraries! For more details, visit: https://era.zksync.io/docs/tools/hardhat/compiling-libraries.html.'; +export const COMPILE_AND_DEPLOY_LIBRARIES_INSTRUCTIONS = + 'To compile and deploy libraries, please run: `yarn hardhat deploy-zksync:libraries --private-key `'; +export const MISSING_LIBRARY_LINK = + 'For more details on how to use deploy-zksync:libraries task from hardhat-zksync-deploy plugin, visit: https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-deploy.html.'; export const SOLCJS_EXECUTABLE_CODE = `#!/usr/bin/env node "use strict";var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,r,o){void 0===o&&(o=r);var i=Object.getOwnPropertyDescriptor(t,r);i&&("get"in i?t.__esModule:!i.writable&&!i.configurable)||(i={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,o,i)}:function(e,t,r,o){void 0===o&&(o=r),e[o]=t[r]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),__importStar=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&__createBinding(t,e,r);return __setModuleDefault(t,e),t},__importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0}),exports._loadCompilerSources=void 0;const os_1=__importDefault(require("os")),fs_1=__importDefault(require("fs")),path_1=__importDefault(require("path"));function packageExists(e){try{return require.resolve(e),!0}catch(e){return!1}}function findPackagePath(e,t){let r=t,o=r+"/node_modules/"+e;for(;"/"!==r;){if(packageExists(o))return o;r=path_1.default.dirname(r),o=r+"/node_modules/"+e}}async function getSolc(e,t){var r=findPackagePath("solc/wrapper",t);const{default:o}=await Promise.resolve().then(()=>__importStar(require(r)));return o(_loadCompilerSources(e))}function _loadCompilerSources(e){const t=module.constructor;if(void 0===t._extensions)return require(e);var r=t._extensions[".js"];t._extensions[".js"]=function(e,t){var r=fs_1.default.readFileSync(t,"utf8");Object.getPrototypeOf(e)._compile.call(e,r,t)};e=require(e);return t._extensions[".js"]=r,e}exports._loadCompilerSources=_loadCompilerSources;async function readStdin(){return new Promise(e=>{let t="";process.stdin.on("data",e=>t+=e),process.stdin.on("end",()=>e(t))})}!async function(){var e;const t=await getSolc("SOLCJS_PATH","WORKING_DIR");process.argv.includes("--version")?(e=await t.version(),process.stdout.write("solc, the solidity compiler commandline interface"+os_1.default.EOL),process.stdout.write("Version: "+e+os_1.default.EOL)):(e=await readStdin(),e=t.compile(e),process.stdout.write(e))}();`; diff --git a/packages/hardhat-zksync-solc/src/extractor.ts b/packages/hardhat-zksync-solc/src/extractor.ts index 81e294c36..7bc0bc30f 100644 --- a/packages/hardhat-zksync-solc/src/extractor.ts +++ b/packages/hardhat-zksync-solc/src/extractor.ts @@ -1,80 +1,64 @@ -import { MultiSolcUserConfig, SolcUserConfig, SolidityUserConfig } from "hardhat/types"; +import { MultiSolcUserConfig, SolcUserConfig, SolidityUserConfig } from 'hardhat/types'; export interface SolcUserConfigExtractor { - extract(solidityConfig: SolidityUserConfig | undefined): SolcUserConfigData; - suitable(solidityConfig: SolidityUserConfig | undefined): boolean; + extract(_solidityConfig: SolidityUserConfig | undefined): SolcUserConfigData; + suitable(_solidityConfig: SolidityUserConfig | undefined): boolean; } export class SolcSoloUserConfigExtractor implements SolcUserConfigExtractor { - suitable(solidityConfig: SolidityUserConfig | undefined): boolean { - if (!solidityConfig) { + public suitable(_solidityConfig: SolidityUserConfig | undefined): boolean { + if (!_solidityConfig) { return false; } - return isSolcUserConfig(solidityConfig); + return isSolcUserConfig(_solidityConfig); } - extract(solidityConfig: SolcUserConfig | undefined): SolcUserConfigData { - if (!solidityConfig) { - return { - compilers: [], - }; - } - + public extract(_solidityConfig: SolcUserConfig | undefined): SolcUserConfigData { return { - compilers: [solidityConfig] + compilers: [_solidityConfig!], }; } } export class SolcMultiUserConfigExtractor implements SolcUserConfigExtractor { - suitable(solidityConfig: SolidityUserConfig | undefined): boolean { - if (!solidityConfig) { + public suitable(_solidityConfig: SolidityUserConfig | undefined): boolean { + if (!_solidityConfig) { return false; } - return isMultiSolcUserConfig(solidityConfig); + return isMultiSolcUserConfig(_solidityConfig); } - extract(solidityConfig: MultiSolcUserConfig | undefined): SolcUserConfigData { - if (!solidityConfig) { - return { - compilers: [], - }; - } - let overrides: Map = new Map(); - for (const [file, compiler] of Object.entries(solidityConfig.overrides ?? {})) { + public extract(_solidityConfig: MultiSolcUserConfig | undefined): SolcUserConfigData { + const overrides: Map = new Map(); + for (const [file, compiler] of Object.entries(_solidityConfig!.overrides ?? {})) { overrides.set(file, compiler); } + return { - compilers: solidityConfig.compilers, + compilers: _solidityConfig!.compilers, overides: overrides, }; } } export class SolcStringUserConfigExtractor implements SolcUserConfigExtractor { - suitable(solidityConfig: string | undefined): boolean { - if (!solidityConfig) { + public suitable(_solidityConfig: string | undefined): boolean { + if (!_solidityConfig) { return false; } - return typeof solidityConfig === 'string'; + return typeof _solidityConfig === 'string'; } - extract(solidityConfig: MultiSolcUserConfig | undefined): SolcUserConfigData { + public extract(_solidityConfig: string | undefined): SolcUserConfigData { return { - compilers: [] - } + compilers: [], + }; } } -export const extractors: SolcUserConfigExtractor[] = [ - new SolcStringUserConfigExtractor(), - new SolcSoloUserConfigExtractor(), - new SolcMultiUserConfigExtractor(), -]; - export interface SolcUserConfigEntry { config: SolcUserConfig; fileName?: string; @@ -91,4 +75,4 @@ export function isSolcUserConfig(object: any): object is SolcUserConfig { export function isMultiSolcUserConfig(object: any): object is MultiSolcUserConfig { return 'compilers' in object; -} \ No newline at end of file +} diff --git a/packages/hardhat-zksync-solc/src/index.ts b/packages/hardhat-zksync-solc/src/index.ts index af27bb28b..61f7a1c72 100644 --- a/packages/hardhat-zksync-solc/src/index.ts +++ b/packages/hardhat-zksync-solc/src/index.ts @@ -15,9 +15,11 @@ import { import { extendEnvironment, extendConfig, subtask } from 'hardhat/internal/core/config/config-env'; import { getCompilersDir } from 'hardhat/internal/util/global-dir'; import './type-extensions'; -import { FactoryDeps } from './types'; import { Artifacts, getArtifactFromContractOutput } from 'hardhat/internal/artifacts'; import { Mutex } from 'hardhat/internal/vendor/await-semaphore'; +import fs from 'fs'; +import chalk from 'chalk'; +import { CompilationJob, CompilerInput, CompilerOutput, SolcBuild } from 'hardhat/types'; import { compile } from './compile'; import { zeroxlify, @@ -25,21 +27,45 @@ import { pluralize, saltFromUrl, generateSolcJSExecutableCode, - updateCompilerConf + updateCompilerConf, } from './utils'; -import fs from 'fs'; -import chalk from 'chalk'; -import { defaultZkSolcConfig, ZKSOLC_BIN_REPOSITORY, ZK_ARTIFACT_FORMAT_VERSION, COMPILING_INFO_MESSAGE, MISSING_LIBRARIES_NOTICE, COMPILE_AND_DEPLOY_LIBRARIES_INSTRUCTIONS, MISSING_LIBRARY_LINK, COMPILING_INFO_MESSAGE_ZKVM_SOLC } from './constants'; -import { CompilationJob, CompilerInput, CompilerOutput, SolcBuild, SolcUserConfig } from 'hardhat/types'; +import { + defaultZkSolcConfig, + ZKSOLC_BIN_REPOSITORY, + ZK_ARTIFACT_FORMAT_VERSION, + COMPILING_INFO_MESSAGE, + MISSING_LIBRARIES_NOTICE, + COMPILE_AND_DEPLOY_LIBRARIES_INSTRUCTIONS, + MISSING_LIBRARY_LINK, + COMPILING_INFO_MESSAGE_ZKVM_SOLC, +} from './constants'; import { ZksolcCompilerDownloader } from './compile/downloader'; import { ZkVmSolcCompilerDownloader } from './compile/solc-downloader'; -import { extractors } from './extractor'; +import { + SolcMultiUserConfigExtractor, + SolcSoloUserConfigExtractor, + SolcStringUserConfigExtractor, + SolcUserConfigExtractor, +} from './extractor'; +import { FactoryDeps } from './types'; + +const extractors: SolcUserConfigExtractor[] = [ + new SolcStringUserConfigExtractor(), + new SolcSoloUserConfigExtractor(), + new SolcMultiUserConfigExtractor(), +]; extendConfig((config, userConfig) => { config.zksolc = { ...defaultZkSolcConfig, ...userConfig?.zksolc }; config.zksolc.settings = { ...defaultZkSolcConfig.settings, ...userConfig?.zksolc?.settings }; - config.zksolc.settings.optimizer = { ...defaultZkSolcConfig.settings.optimizer, ...userConfig?.zksolc?.settings?.optimizer }; - config.zksolc.settings.libraries = { ...defaultZkSolcConfig.settings.libraries, ...userConfig?.zksolc?.settings?.libraries }; + config.zksolc.settings.optimizer = { + ...defaultZkSolcConfig.settings.optimizer, + ...userConfig?.zksolc?.settings?.optimizer, + }; + config.zksolc.settings.libraries = { + ...defaultZkSolcConfig.settings.libraries, + ...userConfig?.zksolc?.settings?.libraries, + }; }); extendEnvironment((hre) => { @@ -48,12 +74,12 @@ extendEnvironment((hre) => { let artifactsPath = hre.config.paths.artifacts; if (!artifactsPath.endsWith('-zk')) { - artifactsPath = artifactsPath + '-zk'; + artifactsPath = `${artifactsPath}-zk`; } let cachePath = hre.config.paths.cache; if (!cachePath.endsWith('-zk')) { - cachePath = cachePath + '-zk'; + cachePath = `${cachePath}-zk`; } // Forcibly update the artifacts object. @@ -63,12 +89,16 @@ extendEnvironment((hre) => { const userSolidityConfig = hre.userConfig.solidity; - const extractedConfigs = extractors.find((extractor) => extractor.suitable(userSolidityConfig))?.extract(userSolidityConfig); + const extractedConfigs = extractors + .find((extractor) => extractor.suitable(userSolidityConfig)) + ?.extract(userSolidityConfig); // Update compilers config. - hre.config.solidity.compilers.forEach((compiler) => updateCompilerConf({compiler}, hre.config.zksolc, extractedConfigs?.compilers ?? [])); + hre.config.solidity.compilers.forEach((compiler) => + updateCompilerConf({ compiler }, hre.config.zksolc, extractedConfigs?.compilers ?? []), + ); for (const [file, compiler] of Object.entries(hre.config.solidity.overrides)) { - updateCompilerConf({compiler, file}, hre.config.zksolc, extractedConfigs?.overides ?? new Map()); + updateCompilerConf({ compiler, file }, hre.config.zksolc, extractedConfigs?.overides ?? new Map()); } } }); @@ -86,7 +116,9 @@ subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_NAMES, async (args: { sourcePaths: stri const sourceNames: string[] = await runSuper(args); - return sourceNames.filter((sourceName) => contractsToCompile.some(contractToCompile => sourceName.includes(contractToCompile))); + return sourceNames.filter((sourceName) => + contractsToCompile.some((contractToCompile) => sourceName.includes(contractToCompile)), + ); }); // This override is needed to invalidate cache when zksolc config is changed. @@ -104,16 +136,15 @@ subtask(TASK_COMPILE_SOLIDITY_GET_COMPILATION_JOBS, async (args, hre, runSuper) const zksolcDownloader = await ZksolcCompilerDownloader.getDownloaderWithVersionValidated( hre.config.zksolc.version, hre.config.zksolc.settings.compilerPath ?? '', - compilersCache + compilersCache, ); const isZksolcDownloaded = await zksolcDownloader.isCompilerDownloaded(); if (!isZksolcDownloaded) { await zksolcDownloader.downloadCompiler(); } - hre.config.zksolc.settings.compilerPath = await zksolcDownloader.getCompilerPath(); - hre.config.zksolc.version = await zksolcDownloader.getVersion(); - + hre.config.zksolc.settings.compilerPath = zksolcDownloader.getCompilerPath(); + hre.config.zksolc.version = zksolcDownloader.getVersion(); }); jobs.forEach((job: any) => { @@ -140,7 +171,7 @@ subtask( contractName: string; contractOutput: any; }, - hre + hre, ): Promise => { if (hre.network.zksync !== true) { return getArtifactFromContractOutput(sourceName, contractName, contractOutput); @@ -149,8 +180,8 @@ subtask( contractOutput.evm?.bytecode?.object || contractOutput.evm?.deployedBytecode?.object || ''; bytecode = zeroxlify(bytecode); - let factoryDeps: FactoryDeps = {}; - let entries: Array<[string, string]> = Object.entries(contractOutput.factoryDependencies || {}); + const factoryDeps: FactoryDeps = {}; + const entries: Array<[string, string]> = Object.entries(contractOutput.factoryDependencies || {}); for (const [hash, dependency] of entries) { factoryDeps[zeroxlify(hash)] = dependency; } @@ -172,7 +203,7 @@ subtask( // zkSync-specific field factoryDeps, }; - } + }, ); subtask(TASK_COMPILE_SOLIDITY_RUN_SOLC, async (args: { input: any; solcPath: string }, hre, runSuper) => { @@ -203,7 +234,8 @@ subtask(TASK_COMPILE_SOLIDITY_RUN_SOLCJS, async (args: { input: any; solcJsPath: * - use valid zkvm solc version if that is needed and return valid SolcBuild object */ -subtask(TASK_COMPILE_SOLIDITY_COMPILE_SOLC, +subtask( + TASK_COMPILE_SOLIDITY_COMPILE_SOLC, async ( args: { input: CompilerInput; @@ -212,20 +244,19 @@ subtask(TASK_COMPILE_SOLIDITY_COMPILE_SOLC, compilationJob: CompilationJob; compilationJobs: CompilationJob[]; compilationJobIndex: number; - }, hre, runSuper + }, + hre, + runSuper, ): Promise<{ output: CompilerOutput; solcBuild: SolcBuild }> => { if (hre.network.zksync !== true) { return await runSuper(args); } - const solcBuild: SolcBuild = await hre.run( - TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, - { - quiet: args.quiet, - solcVersion: args.solcVersion, - compilationJob: args.compilationJob - } - ); + const solcBuild: SolcBuild = await hre.run(TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, { + quiet: args.quiet, + solcVersion: args.solcVersion, + compilationJob: args.compilationJob, + }); await hre.run(TASK_COMPILE_SOLIDITY_LOG_RUN_COMPILER_START, { compilationJob: args.compilationJob, @@ -251,84 +282,86 @@ subtask(TASK_COMPILE_SOLIDITY_COMPILE_SOLC, compilationJob: args.compilationJob, compilationJobs: args.compilationJobs, compilationJobIndex: args.compilationJobIndex, - output: output, + output, quiet: args.quiet, }); return { output, solcBuild }; - } + }, ); // This task is overriden to: // - prevent unnecessary solc downloads when using docker // - download zksolc binary if needed // - validate zksolc binary -subtask(TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, async (args: { solcVersion: string, compilationJob: CompilationJob }, hre, runSuper) => { - if (hre.network.zksync !== true) { - return await runSuper(args); - } - - if (hre.config.zksolc.compilerSource === 'docker') { - // Versions are wrong here when using docker, because there is no - // way to know them beforehand except to run the docker image, which - // adds 5-10 seconds to startup time. We cannot read them from artifacts, - // since that would make cache invalid every time, if the version is - // different from the one in the docker image. - // - // If you wish to know the actual versions from build-info files, - // please look at `output.version`, `output.long_version` - // and `output.zk_version` in the generated JSON. - return { - compilerPath: '', - isSolcJs: false, - version: args.solcVersion, - longVersion: '', - }; - } - - var compiler = args.compilationJob.getSolcConfig(); +subtask( + TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, + async (args: { solcVersion: string; compilationJob: CompilationJob }, hre, runSuper) => { + if (hre.network.zksync !== true) { + return await runSuper(args); + } - if (compiler.eraVersion) { - const compilersCache = await getCompilersDir(); - let path: string = ''; - let version: string = ''; + if (hre.config.zksolc.compilerSource === 'docker') { + // Versions are wrong here when using docker, because there is no + // way to know them beforehand except to run the docker image, which + // adds 5-10 seconds to startup time. We cannot read them from artifacts, + // since that would make cache invalid every time, if the version is + // different from the one in the docker image. + // + // If you wish to know the actual versions from build-info files, + // please look at `output.version`, `output.long_version` + // and `output.zk_version` in the generated JSON. + return { + compilerPath: '', + isSolcJs: false, + version: args.solcVersion, + longVersion: '', + }; + } - const mutex = new Mutex(); - await mutex.use(async () => { - const zkVmSolcCompilerDownloader = await ZkVmSolcCompilerDownloader.getDownloaderWithVersionValidated( - compiler.eraVersion!, - compiler.version, - compilersCache - ); + const compiler = args.compilationJob.getSolcConfig(); - const isZksolcDownloaded = await zkVmSolcCompilerDownloader.isCompilerDownloaded(); - if (!isZksolcDownloaded) { - await zkVmSolcCompilerDownloader.downloadCompiler(); - } + if (compiler.eraVersion) { + const compilersCache = await getCompilersDir(); + let path: string = ''; + let version: string = ''; - path = zkVmSolcCompilerDownloader.getCompilerPath(); - version = zkVmSolcCompilerDownloader.getVersion(); - }); - console.info(chalk.yellow(COMPILING_INFO_MESSAGE_ZKVM_SOLC(hre.config.zksolc.version, version))); + const mutex = new Mutex(); + await mutex.use(async () => { + const zkVmSolcCompilerDownloader = await ZkVmSolcCompilerDownloader.getDownloaderWithVersionValidated( + compiler.eraVersion!, + compiler.version, + compilersCache, + ); - return { - compilerPath: path, - isSolcJs: false, - version: version, - longVersion: version, - }; + const isZksolcDownloaded = await zkVmSolcCompilerDownloader.isCompilerDownloaded(); + if (!isZksolcDownloaded) { + await zkVmSolcCompilerDownloader.downloadCompiler(); + } - } else { - const solcBuild = await runSuper(args); + path = zkVmSolcCompilerDownloader.getCompilerPath(); + version = zkVmSolcCompilerDownloader.getVersion(); + }); + console.info(chalk.yellow(COMPILING_INFO_MESSAGE_ZKVM_SOLC(hre.config.zksolc.version, version))); + + return { + compilerPath: path, + isSolcJs: false, + version, + longVersion: version, + }; + } else { + const solcBuild = await runSuper(args); - console.info(chalk.yellow(COMPILING_INFO_MESSAGE(hre.config.zksolc.version, args.solcVersion))); - return solcBuild; - } -}); + console.info(chalk.yellow(COMPILING_INFO_MESSAGE(hre.config.zksolc.version, args.solcVersion))); + return solcBuild; + } + }, +); subtask( TASK_COMPILE_SOLIDITY_LOG_COMPILATION_RESULT, - async ({ compilationJobs }: { compilationJobs: CompilationJob[] }, hre, runSuper) => { + async ({ compilationJobs }: { compilationJobs: CompilationJob[] }, hre, _runSuper) => { if (hre.config.zksolc.settings.areLibrariesMissing) { console.info(chalk.yellow(MISSING_LIBRARIES_NOTICE)); console.info(chalk.red(COMPILE_AND_DEPLOY_LIBRARIES_INSTRUCTIONS)); @@ -343,46 +376,40 @@ subtask( console.info(chalk.green(`Successfully compiled ${count} Solidity ${pluralize(count, 'file')}`)); } } - } + }, ); -subtask(TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_START) - .setAction( - async ({ - isCompilerDownloaded, - solcVersion, - }: { - isCompilerDownloaded: boolean; - quiet: boolean; - solcVersion: string; - }) => { - if (isCompilerDownloaded) { - return; - } - - console.info(chalk.yellow(`Downloading solc ${solcVersion}`)); +subtask(TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_START).setAction( + async ({ + isCompilerDownloaded, + solcVersion, + }: { + isCompilerDownloaded: boolean; + quiet: boolean; + solcVersion: string; + }) => { + if (isCompilerDownloaded) { + return; } - ); -subtask(TASK_COMPILE_SOLIDITY_LOG_RUN_COMPILER_START) - .setAction( - async ({ - compilationJob, - }: { - compilationJob: CompilationJob; - compilationJobs: CompilationJob[]; - compilationJobIndex: number; - }) => { - let count = compilationJob.getResolvedFiles().length; - if (count > 0) { - console.info( - chalk.yellow( - `Compiling ${count} Solidity ${pluralize(count, 'file')}` - ) - ); - } + console.info(chalk.yellow(`Downloading solc ${solcVersion}`)); + }, +); + +subtask(TASK_COMPILE_SOLIDITY_LOG_RUN_COMPILER_START).setAction( + async ({ + compilationJob, + }: { + compilationJob: CompilationJob; + compilationJobs: CompilationJob[]; + compilationJobIndex: number; + }) => { + const count = compilationJob.getResolvedFiles().length; + if (count > 0) { + console.info(chalk.yellow(`Compiling ${count} Solidity ${pluralize(count, 'file')}`)); } - ); + }, +); subtask(TASK_COMPILE_REMOVE_OBSOLETE_ARTIFACTS, async (taskArgs, hre, runSuper) => { if (hre.network.zksync !== true || !hre.config.zksolc.settings.areLibrariesMissing) { @@ -397,8 +424,4 @@ subtask(TASK_COMPILE_REMOVE_OBSOLETE_ARTIFACTS, async (taskArgs, hre, runSuper) fs.rmSync(cacheDir, { recursive: true }); }); -export { - getZksolcUrl, - ZKSOLC_BIN_REPOSITORY, - saltFromUrl -}; +export { getZksolcUrl, ZKSOLC_BIN_REPOSITORY, saltFromUrl }; diff --git a/packages/hardhat-zksync-solc/src/types.ts b/packages/hardhat-zksync-solc/src/types.ts index 7cc45ea30..2713e5d38 100644 --- a/packages/hardhat-zksync-solc/src/types.ts +++ b/packages/hardhat-zksync-solc/src/types.ts @@ -20,7 +20,7 @@ export interface ZkSolcConfig { // Remove metadata hash from bytecode. If the option is ommited, the metadata hash will be appended by default. metadata?: { bytecodeHash?: 'none'; - }, + }; // addresses of external libraries libraries?: { [file: string]: { @@ -62,5 +62,5 @@ export interface ZkSyncArtifact extends Artifact { export interface MissingLibrary { contractName: string; contractPath: string; - missingLibraries: Array; + missingLibraries: string[]; } diff --git a/packages/hardhat-zksync-solc/src/utils.ts b/packages/hardhat-zksync-solc/src/utils.ts index c53669f8e..b5195bd90 100644 --- a/packages/hardhat-zksync-solc/src/utils.ts +++ b/packages/hardhat-zksync-solc/src/utils.ts @@ -1,20 +1,28 @@ import semver from 'semver'; -import { ZKSOLC_COMPILERS_SELECTOR_MAP, SOLCJS_EXECUTABLE_CODE, DEFAULT_TIMEOUT_MILISECONDS, COMPILERS_CONFLICT_ZKVM_SOLC } from './constants'; -import { CompilerOutputSelection, MissingLibrary, ZkSolcConfig } from './types'; import crypto from 'crypto'; import { SolcConfig, SolcUserConfig } from 'hardhat/types'; -import { CompilerVersionInfo } from './compile/downloader'; import fse from 'fs-extra'; import lockfile from 'proper-lockfile'; +import fs from 'fs'; +import path from 'path'; +import util from 'util'; +import type { Dispatcher } from 'undici'; +import { CompilerVersionInfo } from './compile/downloader'; +import { CompilerOutputSelection, MissingLibrary, ZkSolcConfig } from './types'; +import { + ZKSOLC_COMPILERS_SELECTOR_MAP, + SOLCJS_EXECUTABLE_CODE, + DEFAULT_TIMEOUT_MILISECONDS, + COMPILERS_CONFLICT_ZKVM_SOLC, +} from './constants'; import { ZkSyncSolcPluginError } from './errors'; -import fs from "fs"; -import path from "path"; -import util from "util"; -import type { Dispatcher } from "undici"; -const TEMP_FILE_PREFIX = "tmp-"; +const TEMP_FILE_PREFIX = 'tmp-'; -export function filterSupportedOutputSelections(outputSelection: CompilerOutputSelection, zkCompilerVersion: string): CompilerOutputSelection { +export function filterSupportedOutputSelections( + outputSelection: CompilerOutputSelection, + zkCompilerVersion: string, +): CompilerOutputSelection { const filteredOutputSelection: CompilerOutputSelection = {}; const versionComponents = getVersionComponents(zkCompilerVersion); let supportedOutputSelections: string[]; @@ -33,7 +41,7 @@ export function filterSupportedOutputSelections(outputSelection: CompilerOutputS for (const [contract, outputs] of Object.entries(contractSelection)) { filteredOutputSelection[file][contract] = outputs.filter((output) => - supportedOutputSelections.includes(output) + supportedOutputSelections.includes(output), ); } } @@ -41,13 +49,17 @@ export function filterSupportedOutputSelections(outputSelection: CompilerOutputS return filteredOutputSelection; } -export function updateCompilerConf(solcConfigData: SolcConfigData, zksolc: ZkSolcConfig, userConfigCompilers: SolcUserConfig[] | Map) { - var compiler = solcConfigData.compiler; +export function updateCompilerConf( + solcConfigData: SolcConfigData, + zksolc: ZkSolcConfig, + userConfigCompilers: SolcUserConfig[] | Map, +) { + const compiler = solcConfigData.compiler; const [major, minor] = getVersionComponents(compiler.version); if (major === 0 && minor < 8 && zksolc.settings.forceEvmla) { console.warn('zksolc solidity compiler versions < 0.8 work with forceEvmla enabled by default'); } - let settings = compiler.settings || {}; + const settings = compiler.settings || {}; // Override the default solc optimizer settings with zksolc optimizer settings. compiler.settings = { ...settings, optimizer: { ...zksolc.settings.optimizer } }; @@ -60,9 +72,14 @@ export function updateCompilerConf(solcConfigData: SolcConfigData, zksolc: ZkSol } // zkSolc supports only a subset of solc output selections - compiler.settings.outputSelection = filterSupportedOutputSelections(compiler.settings.outputSelection, zksolc.version); + compiler.settings.outputSelection = filterSupportedOutputSelections( + compiler.settings.outputSelection, + zksolc.version, + ); - solcUpdaters.find((updater) => updater.suituble(userConfigCompilers, solcConfigData.file))?.update(compiler, userConfigCompilers, solcConfigData.file); + solcUpdaters + .find((updater) => updater.suituble(userConfigCompilers, solcConfigData.file)) + ?.update(compiler, userConfigCompilers, solcConfigData.file); } export interface SolcConfigData { @@ -71,52 +88,58 @@ export interface SolcConfigData { } export interface SolcUserConfigUpdater { - suituble(solcUserConfig: SolcUserConfig[] | Map, file?: string): boolean; - update(compiler: SolcConfig, solcUserConfig: SolcUserConfig[] | Map, file?: string): void; + suituble(_solcUserConfig: SolcUserConfig[] | Map, _file?: string): boolean; + update( + _compiler: SolcConfig, + _solcUserConfig: SolcUserConfig[] | Map, + _file?: string, + ): void; } export class OverrideCompilerSolcUserConfigUpdater implements SolcUserConfigUpdater { - suituble(solcUserConfig: SolcUserConfig[] | Map, file?: string): boolean { - return solcUserConfig instanceof Map && file !== undefined; + public suituble(_solcUserConfig: SolcUserConfig[] | Map, _file?: string): boolean { + return _solcUserConfig instanceof Map && _file !== undefined; } - update(compiler: SolcConfig, userConfigCompilers: Map, file: string): void { - let compilerInfo = userConfigCompilers.get(file); + public update(_compiler: SolcConfig, _userConfigCompilers: Map, _file: string): void { + const compilerInfo = _userConfigCompilers.get(_file); if (compilerInfo?.eraVersion) { - compiler.eraVersion = compilerInfo.eraVersion; + _compiler.eraVersion = compilerInfo.eraVersion; } } } export class CompilerSolcUserConfigUpdater implements SolcUserConfigUpdater { - suituble(solcUserConfig: SolcUserConfig[] | Map, file?: string): boolean { - return solcUserConfig instanceof Array && file === undefined; + public suituble(solcUserConfig: SolcUserConfig[] | Map, _file?: string): boolean { + return solcUserConfig instanceof Array && _file === undefined; } - update(compiler: SolcConfig, userConfigCompilers: SolcUserConfig[], file?: string): void { - let compilerInfos = userConfigCompilers.filter((compilerInfo) => compilerInfo.version === compiler.version); + public update(_compiler: SolcConfig, _userConfigCompilers: SolcUserConfig[], _file?: string): void { + const compilerInfos = _userConfigCompilers.filter( + (userCompilerInfo) => userCompilerInfo.version === _compiler.version, + ); if (compilerInfos.length > 1) { - let compilerInfosWithEraVersion = compilerInfos.filter((compilerInfo) => compilerInfo.eraVersion); + const compilerInfosWithEraVersion = compilerInfos.filter((userCompilerInfo) => userCompilerInfo.eraVersion); if (compilerInfosWithEraVersion.length > 0 && compilerInfosWithEraVersion.length !== compilerInfos.length) { - throw new ZkSyncSolcPluginError(COMPILERS_CONFLICT_ZKVM_SOLC(compiler.version)); + throw new ZkSyncSolcPluginError(COMPILERS_CONFLICT_ZKVM_SOLC(_compiler.version)); } } - let compilerInfo = compilerInfos[0]; + const compilerInfo = compilerInfos[0]; if (compilerInfo?.eraVersion) { - compiler.eraVersion = compilerInfo.eraVersion; + _compiler.eraVersion = compilerInfo.eraVersion; } } } const solcUpdaters: SolcUserConfigUpdater[] = [ new OverrideCompilerSolcUserConfigUpdater(), - new CompilerSolcUserConfigUpdater() -] + new CompilerSolcUserConfigUpdater(), +]; export function zeroxlify(hex: string): string { hex = hex.toLowerCase(); @@ -145,8 +168,8 @@ export function getZksolcUrl(repo: string, version: string, isRelease: boolean = const platform = { darwin: 'macosx', linux: 'linux', win32: 'windows' }[process.platform]; // @ts-ignore const toolchain = { linux: '-musl', win32: '-gnu', darwin: '' }[process.platform]; - const arch = process.arch == 'x64' ? 'amd64' : process.arch; - const ext = process.platform == 'win32' ? '.exe' : ''; + const arch = process.arch === 'x64' ? 'amd64' : process.arch; + const ext = process.platform === 'win32' ? '.exe' : ''; if (isRelease) { return `${repo}/releases/download/v${version}/zksolc-${platform}-${arch}${toolchain}-v${version}${ext}`; @@ -159,8 +182,8 @@ export function getZkVmSolcUrl(repo: string, version: string, isRelease: boolean // @ts-ignore const platform = { darwin: 'macosx', linux: 'linux', win32: 'windows' }[process.platform]; // @ts-ignore - const arch = process.arch == 'x64' ? 'amd64' : process.arch; - const ext = process.platform == 'win32' ? '.exe' : ''; + const arch = process.arch === 'x64' ? 'amd64' : process.arch; + const ext = process.platform === 'win32' ? '.exe' : ''; if (isRelease) { return `${repo}/releases/download/${version}/solc-${platform}-${arch}-${version}${ext}`; } @@ -182,11 +205,7 @@ export function pluralize(n: number, singular: string, plural?: string) { export function getVersionComponents(version: string): number[] { const versionComponents = version.split('.'); - return [ - parseInt(versionComponents[0]), - parseInt(versionComponents[1]), - parseInt(versionComponents[2]) - ]; + return [parseInt(versionComponents[0], 10), parseInt(versionComponents[1], 10), parseInt(versionComponents[2], 10)]; } export function isVersionInRange(version: string, versionInfo: CompilerVersionInfo): boolean { @@ -198,17 +217,17 @@ export function isVersionInRange(version: string, versionInfo: CompilerVersionIn // Generate SolcJS executable code export function generateSolcJSExecutableCode(solcJsPath: string, workingDir: string): string { - return SOLCJS_EXECUTABLE_CODE - .replace(/SOLCJS_PATH/g, solcJsPath) - .replace(/WORKING_DIR/g, workingDir); + return SOLCJS_EXECUTABLE_CODE.replace(/SOLCJS_PATH/g, solcJsPath).replace(/WORKING_DIR/g, workingDir); } // Find all the libraries that are missing from the contracts export function findMissingLibraries(zkSolcOutput: any): Set { const missingLibraries = new Set(); - for (let filePath in zkSolcOutput.contracts) { - for (let contractName in zkSolcOutput.contracts[filePath]) { + for (const filePath in zkSolcOutput.contracts) { + if (!filePath) continue; + for (const contractName in zkSolcOutput.contracts[filePath]) { + if (!contractName) continue; const contract = zkSolcOutput.contracts[filePath][contractName]; if (contract.missingLibraries && contract.missingLibraries.length > 0) { contract.missingLibraries.forEach((library: string) => { @@ -221,18 +240,18 @@ export function findMissingLibraries(zkSolcOutput: any): Set { return missingLibraries; } -export function mapMissingLibraryDependencies(zkSolcOutput: any, missingLibraries: Set): Array { +export function mapMissingLibraryDependencies(zkSolcOutput: any, missingLibraries: Set): MissingLibrary[] { const dependencyMap = new Array(); - missingLibraries.forEach(library => { - const [libFilePath, libContractName] = library.split(":"); + missingLibraries.forEach((library) => { + const [libFilePath, libContractName] = library.split(':'); if (zkSolcOutput.contracts[libFilePath] && zkSolcOutput.contracts[libFilePath][libContractName]) { const contract = zkSolcOutput.contracts[libFilePath][libContractName]; if (contract.missingLibraries) { dependencyMap.push({ contractName: libContractName, contractPath: libFilePath, - missingLibraries: contract.missingLibraries + missingLibraries: contract.missingLibraries, }); } } @@ -242,29 +261,29 @@ export function mapMissingLibraryDependencies(zkSolcOutput: any, missingLibrarie } // Get or create the libraries file. If the file doesn't exist, create it with an empty array -const getOrCreateLibraries = async (path: string): Promise => { +const getOrCreateLibraries = async (filePath: string): Promise => { // Ensure the file exists - if (!(await fse.pathExists(path))) { - await fse.outputFile(path, '[]'); // Initialize with an empty array + if (!(await fse.pathExists(filePath))) { + await fse.outputFile(filePath, '[]'); // Initialize with an empty array } // Return the file's content - return await fse.readJSON(path); + return await fse.readJSON(filePath); }; // Write missing libraries to file and lock the file while writing -export const writeLibrariesToFile = async (path: string, libraries: any[]): Promise => { +export const writeLibrariesToFile = async (filePath: string, libraries: any[]): Promise => { try { - let existingLibraries = await getOrCreateLibraries(path); // Ensure that the file exists - await lockfile.lock(path, { retries: { retries: 10, maxTimeout: 1000 } }); + let existingLibraries = await getOrCreateLibraries(filePath); // Ensure that the file exists + await lockfile.lock(filePath, { retries: { retries: 10, maxTimeout: 1000 } }); - existingLibraries = await getOrCreateLibraries(path); // Read again after locking + existingLibraries = await getOrCreateLibraries(filePath); // Read again after locking const combinedLibraries = [...existingLibraries, ...libraries]; - fse.outputFileSync(path, JSON.stringify(combinedLibraries, null, 4)); + fse.outputFileSync(filePath, JSON.stringify(combinedLibraries, null, 4)); } catch (e) { throw new ZkSyncSolcPluginError(`Failed to write missing libraries file: ${e}`); } finally { - await lockfile.unlock(path); + await lockfile.unlock(filePath); } }; @@ -284,23 +303,23 @@ export async function download( userAgent: string, version: string, timeoutMillis = 10000, - extraHeaders: { [name: string]: string } = {} + extraHeaders: { [name: string]: string } = {}, ) { - const { pipeline } = await import("stream"); - const { getGlobalDispatcher, request } = await import("undici"); + const { pipeline } = await import('stream'); + const { getGlobalDispatcher, request } = await import('undici'); const streamPipeline = util.promisify(pipeline); - let dispatcher: Dispatcher = getGlobalDispatcher(); + const dispatcher: Dispatcher = getGlobalDispatcher(); // Fetch the url const response = await request(url, { dispatcher, headersTimeout: timeoutMillis, maxRedirections: 10, - method: "GET", + method: 'GET', headers: { ...extraHeaders, - "User-Agent": `${userAgent} ${version}`, + 'User-Agent': `${userAgent} ${version}`, }, }); @@ -321,18 +340,24 @@ export async function download( ); } -export async function getLatestRelease(owner: string, repo: string, userAgent: string, tagPrefix: string = "v", timeout: number = DEFAULT_TIMEOUT_MILISECONDS): Promise { - let url = `https://github.com/${owner}/${repo}/releases/latest`; - let redirectUrlPattern = `https://github.com/${owner}/${repo}/releases/tag/${tagPrefix}`; +export async function getLatestRelease( + owner: string, + repo: string, + userAgent: string, + tagPrefix: string = 'v', + timeout: number = DEFAULT_TIMEOUT_MILISECONDS, +): Promise { + const url = `https://github.com/${owner}/${repo}/releases/latest`; + const redirectUrlPattern = `https://github.com/${owner}/${repo}/releases/tag/${tagPrefix}`; - const { request } = await import("undici"); + const { request } = await import('undici'); const response = await request(url, { headersTimeout: timeout, maxRedirections: 0, - method: "GET", + method: 'GET', headers: { - "User-Agent": `${userAgent}`, + 'User-Agent': `${userAgent}`, }, }); @@ -360,4 +385,4 @@ export async function getLatestRelease(owner: string, repo: string, userAgent: s export async function saveDataToFile(data: any, targetPath: string) { await fse.ensureDir(path.dirname(targetPath)); await fse.writeJSON(targetPath, data, { spaces: 2 }); -} \ No newline at end of file +} diff --git a/packages/hardhat-zksync-solc/test/common.config.ts b/packages/hardhat-zksync-solc/test/common.config.ts index 9ff4b4078..29a57824d 100644 --- a/packages/hardhat-zksync-solc/test/common.config.ts +++ b/packages/hardhat-zksync-solc/test/common.config.ts @@ -11,7 +11,7 @@ const config: HardhatUserConfig = { }, }, solidity: { - version: process.env.SOLC_VERSION || '0.8.17' + version: process.env.SOLC_VERSION || '0.8.17', }, }; diff --git a/packages/hardhat-zksync-solc/test/compiler-files/linux/solc b/packages/hardhat-zksync-solc/test/compiler-files/linux/solc new file mode 100755 index 000000000..3d32e2cb8 Binary files /dev/null and b/packages/hardhat-zksync-solc/test/compiler-files/linux/solc differ diff --git a/packages/hardhat-zksync-solc/test/compiler-files/linux/zksolc b/packages/hardhat-zksync-solc/test/compiler-files/linux/zksolc new file mode 100755 index 000000000..82d9bbfa0 Binary files /dev/null and b/packages/hardhat-zksync-solc/test/compiler-files/linux/zksolc differ diff --git a/packages/hardhat-zksync-solc/test/compiler-files/macos/solc b/packages/hardhat-zksync-solc/test/compiler-files/macos/solc new file mode 100755 index 000000000..a7fe2d216 Binary files /dev/null and b/packages/hardhat-zksync-solc/test/compiler-files/macos/solc differ diff --git a/packages/hardhat-zksync-solc/test/compiler-files/macos/solc-macos:Zone.Identifier b/packages/hardhat-zksync-solc/test/compiler-files/macos/solc-macos:Zone.Identifier new file mode 100644 index 000000000..86e5542db --- /dev/null +++ b/packages/hardhat-zksync-solc/test/compiler-files/macos/solc-macos:Zone.Identifier @@ -0,0 +1,4 @@ +[ZoneTransfer] +ZoneId=3 +ReferrerUrl=https://github.com/ethereum/solidity/releases +HostUrl=https://objects.githubusercontent.com/github-production-release-asset-2e65be/40892817/d8c0c93a-8522-4772-be33-c8cb04a26d5d?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20231225%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20231225T225829Z&X-Amz-Expires=300&X-Amz-Signature=61e0d8865c9f98cd83643330f76ef3bb17b565f0d77aa45da8f5ca438d673973&X-Amz-SignedHeaders=host&actor_id=131957563&key_id=0&repo_id=40892817&response-content-disposition=attachment%3B%20filename%3Dsolc-macos&response-content-type=application%2Foctet-stream diff --git a/packages/hardhat-zksync-solc/test/compiler-files/macos/zksolc b/packages/hardhat-zksync-solc/test/compiler-files/macos/zksolc new file mode 100755 index 000000000..47f207713 Binary files /dev/null and b/packages/hardhat-zksync-solc/test/compiler-files/macos/zksolc differ diff --git a/packages/hardhat-zksync-solc/test/compiler-files/macos/zksolc-macosx-arm64-v1.3.17:Zone.Identifier b/packages/hardhat-zksync-solc/test/compiler-files/macos/zksolc-macosx-arm64-v1.3.17:Zone.Identifier new file mode 100644 index 000000000..f8abbf1f9 --- /dev/null +++ b/packages/hardhat-zksync-solc/test/compiler-files/macos/zksolc-macosx-arm64-v1.3.17:Zone.Identifier @@ -0,0 +1,4 @@ +[ZoneTransfer] +ZoneId=3 +ReferrerUrl=https://github.com/matter-labs/zksolc-bin/releases +HostUrl=https://objects.githubusercontent.com/github-production-release-asset-2e65be/423900857/a181a32b-51d2-443d-9c35-101bc5898a91?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20231225%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20231225T225424Z&X-Amz-Expires=300&X-Amz-Signature=b8068888dfde3a94f2627d1617a39d4117cedb8e47c1a93d087df2eb4b9af6f1&X-Amz-SignedHeaders=host&actor_id=131957563&key_id=0&repo_id=423900857&response-content-disposition=attachment%3B%20filename%3Dzksolc-macosx-arm64-v1.3.17&response-content-type=application%2Foctet-stream diff --git a/packages/hardhat-zksync-solc/test/fixture-projects/docker-compile/contracts/Greeter.sol b/packages/hardhat-zksync-solc/test/fixture-projects/docker-compile/contracts/Greeter.sol new file mode 100644 index 000000000..8045d4954 --- /dev/null +++ b/packages/hardhat-zksync-solc/test/fixture-projects/docker-compile/contracts/Greeter.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.4.22 <0.9.0; + +contract Greeter { + + string greeting; + string bad; + constructor(string memory _greeting) { + greeting = _greeting; + bad = "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad"; + } + + function greet() public view returns (string memory) { + return greeting; + } + +} diff --git a/packages/hardhat-zksync-solc/test/fixture-projects/docker-compile/hardhat.config.ts b/packages/hardhat-zksync-solc/test/fixture-projects/docker-compile/hardhat.config.ts new file mode 100644 index 000000000..9804eb498 --- /dev/null +++ b/packages/hardhat-zksync-solc/test/fixture-projects/docker-compile/hardhat.config.ts @@ -0,0 +1,24 @@ +import '../../../src/index'; +import { HardhatUserConfig } from 'hardhat/config'; + +const config: HardhatUserConfig = { + zksolc: { + compilerSource: 'docker', + settings: { + experimental: { + dockerImage: 'matterlabs/zksolc', + tag: 'latest', + }, + }, + }, + networks: { + hardhat: { + zksync: true, + }, + }, + solidity: { + version: '0.8.17', + }, +}; + +export default config; diff --git a/packages/hardhat-zksync-solc/test/fixture-projects/multiple-compilers/contracts/Greeter.sol b/packages/hardhat-zksync-solc/test/fixture-projects/multiple-compilers/contracts/Greeter.sol new file mode 100644 index 000000000..8045d4954 --- /dev/null +++ b/packages/hardhat-zksync-solc/test/fixture-projects/multiple-compilers/contracts/Greeter.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.4.22 <0.9.0; + +contract Greeter { + + string greeting; + string bad; + constructor(string memory _greeting) { + greeting = _greeting; + bad = "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad"; + } + + function greet() public view returns (string memory) { + return greeting; + } + +} diff --git a/packages/hardhat-zksync-solc/test/fixture-projects/multiple-compilers/contracts/Greeter2.sol b/packages/hardhat-zksync-solc/test/fixture-projects/multiple-compilers/contracts/Greeter2.sol new file mode 100644 index 000000000..70ea81bca --- /dev/null +++ b/packages/hardhat-zksync-solc/test/fixture-projects/multiple-compilers/contracts/Greeter2.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.4.22 <0.9.0; + +contract Greeter2 { + + string greeting; + string bad; + constructor(string memory _greeting) { + greeting = _greeting; + bad = "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad"; + } + + function greet() public view returns (string memory) { + return greeting; + } + +} diff --git a/packages/hardhat-zksync-solc/test/fixture-projects/multiple-compilers/hardhat.config.ts b/packages/hardhat-zksync-solc/test/fixture-projects/multiple-compilers/hardhat.config.ts new file mode 100644 index 000000000..0b51d2278 --- /dev/null +++ b/packages/hardhat-zksync-solc/test/fixture-projects/multiple-compilers/hardhat.config.ts @@ -0,0 +1,27 @@ +import '../../../src/index'; +import { HardhatUserConfig } from 'hardhat/config'; + +const config: HardhatUserConfig = { + zksolc: { + compilerSource: 'binary', + }, + networks: { + hardhat: { + zksync: true, + }, + }, + solidity: { + compilers: [ + { + version: '0.8.17', + }, + ], + overrides: { + 'contracts/Greeter2.sol': { + version: '0.8.16', + }, + }, + }, +}; + +export default config; diff --git a/packages/hardhat-zksync-solc/test/fixture-projects/multiple-contracts/contracts/Greeter.sol b/packages/hardhat-zksync-solc/test/fixture-projects/multiple-contracts/contracts/Greeter.sol new file mode 100644 index 000000000..8045d4954 --- /dev/null +++ b/packages/hardhat-zksync-solc/test/fixture-projects/multiple-contracts/contracts/Greeter.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.4.22 <0.9.0; + +contract Greeter { + + string greeting; + string bad; + constructor(string memory _greeting) { + greeting = _greeting; + bad = "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad"; + } + + function greet() public view returns (string memory) { + return greeting; + } + +} diff --git a/packages/hardhat-zksync-solc/test/fixture-projects/multiple-contracts/contracts/Greeter2.sol b/packages/hardhat-zksync-solc/test/fixture-projects/multiple-contracts/contracts/Greeter2.sol new file mode 100644 index 000000000..70ea81bca --- /dev/null +++ b/packages/hardhat-zksync-solc/test/fixture-projects/multiple-contracts/contracts/Greeter2.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.4.22 <0.9.0; + +contract Greeter2 { + + string greeting; + string bad; + constructor(string memory _greeting) { + greeting = _greeting; + bad = "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad"; + } + + function greet() public view returns (string memory) { + return greeting; + } + +} diff --git a/packages/hardhat-zksync-solc/test/fixture-projects/multiple-contracts/hardhat.config.js b/packages/hardhat-zksync-solc/test/fixture-projects/multiple-contracts/hardhat.config.js new file mode 100644 index 000000000..12575417f --- /dev/null +++ b/packages/hardhat-zksync-solc/test/fixture-projects/multiple-contracts/hardhat.config.js @@ -0,0 +1 @@ +module.exports = require('../../common.config'); diff --git a/packages/hardhat-zksync-solc/test/fixture-projects/no-zksync/contracts/Greeter.sol b/packages/hardhat-zksync-solc/test/fixture-projects/no-zksync/contracts/Greeter.sol new file mode 100644 index 000000000..8045d4954 --- /dev/null +++ b/packages/hardhat-zksync-solc/test/fixture-projects/no-zksync/contracts/Greeter.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.4.22 <0.9.0; + +contract Greeter { + + string greeting; + string bad; + constructor(string memory _greeting) { + greeting = _greeting; + bad = "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad"; + } + + function greet() public view returns (string memory) { + return greeting; + } + +} diff --git a/packages/hardhat-zksync-solc/test/fixture-projects/no-zksync/hardhat.config.ts b/packages/hardhat-zksync-solc/test/fixture-projects/no-zksync/hardhat.config.ts new file mode 100644 index 000000000..85eedc4c6 --- /dev/null +++ b/packages/hardhat-zksync-solc/test/fixture-projects/no-zksync/hardhat.config.ts @@ -0,0 +1,18 @@ +import '../../../src/index'; +import { HardhatUserConfig } from 'hardhat/config'; + +const config: HardhatUserConfig = { + zksolc: { + compilerSource: 'binary', + }, + networks: { + hardhat: { + zksync: false, + }, + }, + solidity: { + version: process.env.SOLC_VERSION || '0.8.17', + }, +}; + +export default config; diff --git a/packages/hardhat-zksync-solc/test/helpers.ts b/packages/hardhat-zksync-solc/test/helpers.ts index 899a4ab39..f527b6805 100644 --- a/packages/hardhat-zksync-solc/test/helpers.ts +++ b/packages/hardhat-zksync-solc/test/helpers.ts @@ -15,7 +15,7 @@ export function useEnvironment(fixtureProjectName: string, networkName = 'hardha process.env.HARDHAT_NETWORK = networkName; this.env = require('hardhat'); - this.env.run(TASK_CLEAN); + const _ = this.env.run(TASK_CLEAN); }); afterEach('Resetting hardhat', function () { diff --git a/packages/hardhat-zksync-solc/test/tests.ts b/packages/hardhat-zksync-solc/test/tests.ts deleted file mode 100644 index 47b68d110..000000000 --- a/packages/hardhat-zksync-solc/test/tests.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { assert } from 'chai'; -import { TASK_COMPILE } from 'hardhat/builtin-tasks/task-names'; -import { ZkSyncArtifact } from '../src/types'; -import chalk from 'chalk'; -import fs from 'fs'; - -import { useEnvironment } from './helpers'; - -describe('zksolc plugin', async function () { - describe('Simple', async function () { - useEnvironment('simple'); - - it('Should successfully compile a simple contract', async function () { - await this.env.run(TASK_COMPILE); - - const artifact = this.env.artifacts.readArtifactSync('Greeter') as ZkSyncArtifact; - - assert.equal(artifact.contractName, 'Greeter'); - - // Check that zkSync-specific artifact information was added. - assert.deepEqual(artifact.factoryDeps, {}, 'Contract unexpectedly has dependencies'); - }); - }); - - describe('Inlined library', async function () { - useEnvironment('library'); - - it('Should successfully compile the contract with inlined library', async function () { - if (this.env.config.solidity.compilers[0].version.startsWith('0.4')) { - console.info(chalk.cyan('Test skipped since is not applicable to Solidity 0.4.x.')); - return; - } - await this.env.run(TASK_COMPILE); - assert.equal(this.env.artifacts.readArtifactSync('contracts/Foo.sol:Foo').contractName, 'Foo'); - assert.equal(this.env.artifacts.readArtifactSync('contracts/Import.sol:Import').contractName, 'Import'); - }); - }); - - describe('Linked library', async function () { - useEnvironment('linked'); - - it('Should successfully compile the contract with linked library', async function () { - if (this.env.config.solidity.compilers[0].version.startsWith('0.4')) { - console.info(chalk.cyan('Test skipped since is not applicable to Solidity 0.4.x.')); - return; - } - await this.env.run(TASK_COMPILE); - assert.equal(this.env.artifacts.readArtifactSync('contracts/Foo.sol:Foo').contractName, 'Foo'); - assert.equal(this.env.artifacts.readArtifactSync('contracts/Import.sol:Import').contractName, 'Import'); - }); - }); - - describe('Factory', async function () { - useEnvironment('factory'); - - it('Should successfully compile the factory contract', async function () { - await this.env.run(TASK_COMPILE); - - const factoryArtifact = this.env.artifacts.readArtifactSync( - 'contracts/Factory.sol:Factory' - ) as ZkSyncArtifact; - const depArtifact = this.env.artifacts.readArtifactSync('contracts/Factory.sol:Dep') as ZkSyncArtifact; - - assert.equal(factoryArtifact.contractName, 'Factory'); - assert.equal(depArtifact.contractName, 'Dep'); - - // Check that zkSync-specific artifact information was added. - - // Factory contract should have one dependency. - // We do not check for the actual value of the hash, as it depends on the bytecode yielded by the compiler and thus not static. - // Instead we only check that it's a hash indeed. - const depHash = Object.keys(factoryArtifact.factoryDeps)[0]; - const expectedLength = 32 * 2 + 2; // 32 bytes in hex + '0x'. - assert(depHash.startsWith('0x') && depHash.length === expectedLength, 'Contract hash is malformed'); - - const depName = 'contracts/Factory.sol:Dep'; - assert.equal(depName, factoryArtifact.factoryDeps[depHash], 'No required dependency in the artifact'); - - // For the dependency contract should be no further dependencies. - assert.deepEqual(depArtifact.factoryDeps, {}, 'Unexpected factory-deps for a dependency contract'); - }); - }); - - describe('Nested Factory', async function () { - useEnvironment('nested'); - - it('Should successfully compile nested contracts', async function () { - await this.env.run(TASK_COMPILE); - - const factoryArtifact = this.env.artifacts.readArtifactSync('NestedFactory') as ZkSyncArtifact; - const fooDepArtifact = this.env.artifacts.readArtifactSync( - 'contracts/deps/Foo.sol:FooDep' - ) as ZkSyncArtifact; - const barDepArtifact = this.env.artifacts.readArtifactSync( - 'contracts/deps/more_deps/Bar.sol:BarDep' - ) as ZkSyncArtifact; - - // Check that zkSync-specific artifact information was added. - - // Factory contract should have one dependency. - // We do not check for the actual value of the hash, as it depends on the bytecode yielded by the compiler and thus not static. - // Instead we only check that it's a hash indeed. - const fooDepName = 'contracts/deps/Foo.sol:FooDep'; - const barDepName = 'contracts/deps/more_deps/Bar.sol:BarDep'; - for (const depName of [fooDepName, barDepName]) { - assert( - Object.values(factoryArtifact.factoryDeps).includes(depName), - `No required dependency in the artifact: ${depName}` - ); - } - for (const depHash in factoryArtifact.factoryDeps) { - const expectedLength = 32 * 2 + 2; // 32 bytes in hex + '0x'. - assert(depHash.startsWith('0x') && depHash.length === expectedLength, 'Contract hash is malformed'); - } - - // For the dependency contract should be no further dependencies. - for (const depArtifact of [fooDepArtifact, barDepArtifact]) { - assert.deepEqual(depArtifact.factoryDeps, {}, 'Unexpected factory-deps for a dependency contract'); - } - - // Each factory dependency should be accessible through `readArtifact` without changing it's identifier. - const fooDepArtifactFromFactoryDeps = this.env.artifacts.readArtifactSync(fooDepName); - assert.equal( - fooDepArtifactFromFactoryDeps.contractName, - fooDepArtifact.contractName, - 'Artifacts do not match' - ); - assert.equal(fooDepArtifactFromFactoryDeps.bytecode, fooDepArtifact.bytecode, 'Artifacts do not match'); - assert.deepEqual(fooDepArtifactFromFactoryDeps.abi, fooDepArtifact.abi, 'Artifacts do not match'); - }); - }); - - describe('Missing Library', async function () { - useEnvironment('missing-libraries'); - - it('Should successfully identify all the missing libraries', async function () { - if (this.env.config.solidity.compilers[0].version.startsWith('0.4')) { - console.info(chalk.cyan('Test skipped since is not applicable to Solidity 0.4.x.')); - return; - } - - await this.env.run(TASK_COMPILE); - - // Assert that there is a json file with the list of missing libraries at the location this.env.config.zksolc.settings.missingLibrariesPath. - const missingLibraries = JSON.parse(fs.readFileSync(this.env.config.zksolc.settings.missingLibrariesPath!, 'utf8')); - assert.isNotEmpty(missingLibraries); - - const expectedMissingLibraries = [ - { - "contractName": "ChildChildLib", - "contractPath": "contracts/ChildChildLib.sol", - "missingLibraries": [] - }, - { - "contractName": "ChildLib", - "contractPath": "contracts/ChildLib.sol", - "missingLibraries": [ - "contracts/ChildChildLib.sol:ChildChildLib" - ] - }, - { - "contractName": "MathLib", - "contractPath": "contracts/MathLib.sol", - "missingLibraries": [ - "contracts/ChildLib.sol:ChildLib" - ] - } - ]; - - // Assert that list of missing libraries is correct. - assert.deepEqual(missingLibraries, expectedMissingLibraries); - }); - - afterEach(async function () { - if (this.env.config.solidity.compilers[0].version.startsWith('0.4')) { - console.info(chalk.cyan('Test skipped since is not applicable to Solidity 0.4.x.')); - return; - } - - // Remove the file with the list of missing libraries. - fs.unlinkSync(this.env.config.zksolc.settings.missingLibrariesPath!); - }); - }); -}); diff --git a/packages/hardhat-zksync-solc/test/tests/compile/docker.test.ts b/packages/hardhat-zksync-solc/test/tests/compile/docker.test.ts new file mode 100644 index 000000000..94d5169be --- /dev/null +++ b/packages/hardhat-zksync-solc/test/tests/compile/docker.test.ts @@ -0,0 +1,232 @@ +import { expect } from 'chai'; +import { HardhatDocker } from '@nomiclabs/hardhat-docker'; +import { CompilerInput } from 'hardhat/types'; +import sinon from 'sinon'; +import { ZkSolcConfig } from '../../../src/types'; +import { + dockerImage, + validateDockerIsInstalled, + createDocker, + pullImageIfNecessary, + compileWithDocker, + getSolcVersion, +} from '../../../src/compile/docker'; + +describe.skip('Docker', () => { + let docker: HardhatDocker; + + before(async () => { + await validateDockerIsInstalled(); + docker = await HardhatDocker.create(); + }); + + describe('dockerImage', () => { + it('should throw an error if no image name is specified', () => { + expect(() => dockerImage()).to.throw('Docker source was chosen but no image was specified'); + }); + + it('should return the correct image object', () => { + const imageName = 'matterlabs/zksolc'; + const imageTag = 'latest'; + const image = dockerImage(imageName, imageTag); + + expect(image.repository).to.equal(imageName); + expect(image.tag).to.equal(imageTag); + }); + }); + + describe('createDocker', () => { + it('should create a new HardhatDocker instance', async () => { + const hardhatDocker = await createDocker(); + + expect(hardhatDocker).to.be.an('object'); + expect(hardhatDocker).to.have.property('_docker'); + }); + }); + + describe('pullImageIfNecessary', () => { + describe('pullImageIfNecessaryInner', () => { + let hasPulledImageStub: sinon.SinonStub; + let pullImageStub: sinon.SinonStub; + let isImageUpToDateStub: sinon.SinonStub; + + async function booleanPromise(bool: boolean): Promise { + return bool; + } + + afterEach(() => { + hasPulledImageStub.restore(); + pullImageStub.restore(); + isImageUpToDateStub.restore(); + }); + + it('should pull the Docker image if it has not been pulled before', async () => { + hasPulledImageStub = sinon + .stub(HardhatDocker.prototype, 'hasPulledImage') + .returns(booleanPromise(false)); + pullImageStub = sinon.stub(HardhatDocker.prototype, 'pullImage').resolves(); + isImageUpToDateStub = sinon + .stub(HardhatDocker.prototype, 'isImageUpToDate') + .returns(booleanPromise(true)); + + const image = { repository: 'matterlabs/test', tag: 'latest' }; + + await pullImageIfNecessary(docker, image); + + expect(await docker.hasPulledImage(image)).to.equal(false); + sinon.assert.calledOnce(pullImageStub); + }); + + it('should check for image updates if the Docker image has been pulled before', async () => { + hasPulledImageStub = sinon + .stub(HardhatDocker.prototype, 'hasPulledImage') + .returns(booleanPromise(true)); + isImageUpToDateStub = sinon + .stub(HardhatDocker.prototype, 'isImageUpToDate') + .returns(booleanPromise(true)); + pullImageStub = sinon.stub(HardhatDocker.prototype, 'pullImage').resolves(); + + const image = { repository: 'matterlabs/test', tag: 'latest' }; + + await pullImageIfNecessary(docker, image); + + expect(await docker.hasPulledImage(image)).to.equal(true); + sinon.assert.calledOnce(isImageUpToDateStub); + sinon.assert.notCalled(pullImageStub); + }); + + it('should check for image updates if the Docker image has been pulled before with image not up to date', async () => { + hasPulledImageStub = sinon + .stub(HardhatDocker.prototype, 'hasPulledImage') + .returns(booleanPromise(true)); + isImageUpToDateStub = sinon + .stub(HardhatDocker.prototype, 'isImageUpToDate') + .returns(booleanPromise(false)); + pullImageStub = sinon.stub(HardhatDocker.prototype, 'pullImage').resolves(); + + const image = { repository: 'matterlabs/test', tag: 'latest' }; + + await pullImageIfNecessary(docker, image); + + expect(await docker.hasPulledImage(image)).to.equal(true); + sinon.assert.calledOnce(isImageUpToDateStub); + sinon.assert.calledOnce(pullImageStub); + }); + }); + }); + + describe('compileWithDocker', () => { + before(async () => { + const imageName = 'matterlabs/zksolc'; + const imageTag = 'latest'; + const image = dockerImage(imageName, imageTag); + + await pullImageIfNecessary(docker, image); + }); + + it('should compile the contract using Docker', async () => { + const imageName = 'matterlabs/zksolc'; + const imageTag = 'latest'; + const image = dockerImage(imageName, imageTag); + + const zksolcConfig: ZkSolcConfig = { + version: 'latest', + compilerSource: 'docker', + settings: { + optimizer: { + enabled: false, + runs: 150, + }, + metadata: {}, + experimental: { + dockerImage: imageName, + tag: imageTag, + }, + }, + }; + + const input: CompilerInput = { + language: 'Solidity', + sources: { + 'contracts/Greeter.sol': { + content: + '// SPDX-License-Identifier: MIT\n\npragma solidity >=0.4.22 <0.9.0;\n\ncontract Greeter {\n\n string greeting;\n string bad;\n constructor(string memory _greeting) {\n greeting = _greeting;\n bad = "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad";\n }\n\n function greet() public view returns (string memory) {\n return greeting;\n }\n\n}\n', + }, + }, + settings: { + outputSelection: { + '*': { + '*': ['abi', 'metadata'], + }, + }, + viaIR: false, + optimizer: { + enabled: true, + }, + }, + }; + + const output = await compileWithDocker(input, docker, image, zksolcConfig); + expect(output).to.be.an('object'); + expect(output).to.have.property('contracts'); + expect(output.contracts).to.be.an('object'); + expect(output.contracts).to.have.property('contracts/Greeter.sol'); + expect(output.contracts['contracts/Greeter.sol']).to.be.an('object'); + expect(output.contracts['contracts/Greeter.sol']).to.have.property('Greeter'); + expect(output.contracts['contracts/Greeter.sol'].Greeter).to.be.an('object'); + expect(output.contracts['contracts/Greeter.sol'].Greeter).to.have.property('evm'); + expect(output.contracts['contracts/Greeter.sol'].Greeter.evm).to.be.an('object'); + expect(output.contracts['contracts/Greeter.sol'].Greeter.evm).to.have.property('bytecode'); + expect(output.contracts['contracts/Greeter.sol'].Greeter.evm.bytecode).to.be.an('object'); + expect(output.contracts['contracts/Greeter.sol'].Greeter.evm.bytecode).to.have.property('object'); + expect(output.contracts['contracts/Greeter.sol'].Greeter.evm.bytecode.object).to.be.a('string'); + + expect(output.errors).to.be.an('array'); + expect(output.version).eq('0.8.16'); + expect(output.zk_version).eq('1.1.6'); + }); + }); + + describe('getSolcVersion', () => { + it('should return the version of the solc compiler', async () => { + const imageName = 'matterlabs/zksolc'; + const imageTag = 'latest'; + const image = dockerImage(imageName, imageTag); + + const version = await getSolcVersion(docker, image); + expect(version).to.be.a('string'); + }); + }); + + describe('Docker', () => { + describe('validateDockerIsInstalled', () => { + it('should throw an error if Docker Desktop is not installed', async () => { + // Mock the isInstalled method to return false + HardhatDocker.isInstalled = async () => false; + try { + await validateDockerIsInstalled(); + // If the function does not throw an error, fail the test + expect.fail('Expected an error to be thrown'); + } catch (error: any) { + // Assert that the error message is correct + expect(error.message).to.eq( + 'Docker Desktop is not installed.\n' + + 'Please install it by following the instructions on https://www.docker.com/get-started', + ); + } + }); + + it('should not throw an error if Docker Desktop is installed', async () => { + // Mock the isInstalled method to return true + HardhatDocker.isInstalled = async () => true; + + try { + await validateDockerIsInstalled(); + } catch (error) { + // If the function throws an error, fail the test + expect.fail('Expected no error to be thrown'); + } + }); + }); + }); +}); diff --git a/packages/hardhat-zksync-solc/test/tests/compile/downloader.test.ts b/packages/hardhat-zksync-solc/test/tests/compile/downloader.test.ts new file mode 100644 index 000000000..bbcfe8ada --- /dev/null +++ b/packages/hardhat-zksync-solc/test/tests/compile/downloader.test.ts @@ -0,0 +1,62 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { ZksolcCompilerDownloader } from '../../../src/compile/downloader'; + +describe('Downloader', async () => { + const sandbox = sinon.createSandbox(); + + async function isCompilerDownloaded(isZksolcDownloaded: boolean): Promise { + return isZksolcDownloaded; + } + + beforeEach(() => { + sandbox.stub(ZksolcCompilerDownloader.prototype, 'downloadCompiler').resolves(); + sandbox.stub(ZksolcCompilerDownloader.prototype, 'getVersion').returns('0.1.0'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + after(() => { + (ZksolcCompilerDownloader as any)._instance = undefined; + }); + + describe('getCompilerPath', async () => { + it('should return the configured compiler path if it exists', async () => { + sandbox + .stub(ZksolcCompilerDownloader as any, '_getCompilerVersionInfo') + .resolves({ latest: '0.1.0', minVersion: '0.0.1' }); + sandbox + .stub(ZksolcCompilerDownloader.prototype, 'isCompilerDownloaded') + .returns(isCompilerDownloaded(false)); + sandbox.stub(ZksolcCompilerDownloader as any, '_shouldDownloadCompilerVersionInfo').resolves(false); + const downloader = await ZksolcCompilerDownloader.getDownloaderWithVersionValidated( + '0.1.0', + 'path/zksolc', + 'zksolc/0.1.0', + ); + const compilerPath = downloader.getCompilerPath(); + + expect(compilerPath).to.equal('path/zksolc'); + }); + + it('should return the default compiler path with salt if no configured compiler path', async () => { + sandbox + .stub(ZksolcCompilerDownloader as any, '_getCompilerVersionInfo') + .resolves({ latest: '0.1.0', minVersion: '0.0.1' }); + sandbox + .stub(ZksolcCompilerDownloader.prototype, 'isCompilerDownloaded') + .returns(isCompilerDownloaded(false)); + sandbox.stub(ZksolcCompilerDownloader as any, '_shouldDownloadCompilerVersionInfo').resolves(false); + const downloader = await ZksolcCompilerDownloader.getDownloaderWithVersionValidated( + '0.1.0', + 'path/zksolc', + 'zksolc/0.1.0', + ); + const compilerPath = downloader.getCompilerPath(); + + expect(compilerPath).to.equal('path/zksolc'); + }); + }); +}); diff --git a/packages/hardhat-zksync-solc/test/tests/compile/index.test.ts b/packages/hardhat-zksync-solc/test/tests/compile/index.test.ts new file mode 100644 index 000000000..1f31d5924 --- /dev/null +++ b/packages/hardhat-zksync-solc/test/tests/compile/index.test.ts @@ -0,0 +1,90 @@ +import { expect } from 'chai'; +import { CompilerInput } from 'hardhat/types'; +import path from 'path'; +import { compile } from '../../../src/compile'; +import { ZkSolcConfig } from '../../../src/types'; + +describe('compile', () => { + const input: CompilerInput = { + language: 'Solidity', + sources: { + 'contracts/Greeter.sol': { + content: + '// SPDX-License-Identifier: MIT\n\npragma solidity >=0.4.22 <0.9.0;\n\ncontract Greeter {\n\n string greeting;\n string bad;\n constructor(string memory _greeting) {\n greeting = _greeting;\n bad = "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad";\n }\n\n function greet() public view returns (string memory) {\n return greeting;\n }\n\n}\n', + }, + }, + settings: { + outputSelection: { + '*': { + '*': ['abi', 'metadata'], + }, + }, + viaIR: false, + optimizer: { + enabled: true, + }, + }, + }; + + it.skip('should compile with binary compiler', async () => { + let zksolcPath; + let solcPath; + + if (process.platform) { + zksolcPath = path.resolve('./test/compiler-files/linux/zksolc'); + solcPath = path.resolve('./test/compiler-files/linux/solc'); + } else { + zksolcPath = path.resolve('./test/compiler-files/macos/zksolc'); + solcPath = path.resolve('./test/compiler-files/macos/solc'); + } + + const zksolcConfig: ZkSolcConfig = { + compilerSource: 'binary', + version: '1.3.17', + settings: { + compilerPath: zksolcPath, + }, + }; + + const result = await compile(zksolcConfig, input, solcPath); + + expect(result).to.be.an('object'); + expect(result).to.have.property('errors'); + }); + + it.skip('should compile with docker compiler', async () => { + const zksolcConfig: ZkSolcConfig = { + compilerSource: 'docker', + version: '1.3.17', + settings: { + experimental: { + dockerImage: 'matterlabs/zksolc', + tag: 'latest', + }, + }, + }; + + const result = await compile(zksolcConfig, input); + + expect(result).to.be.an('object'); + expect(result).to.have.property('errors'); + }); + + it('should throw an error for incorrect compiler source', async () => { + const zksolcConfig: ZkSolcConfig = { + compilerSource: undefined, + version: '', + settings: {}, + }; + + try { + await compile(zksolcConfig, input); + // If the function does not throw an error, fail the test + expect.fail('Expected ZkSyncSolcPluginError to be thrown'); + } catch (error: any) { + // Add your assertions here + // For example, you can check if the error message is as expected + expect(error.message).to.equal('Incorrect compiler source: undefined'); + } + }); +}); diff --git a/packages/hardhat-zksync-solc/test/tests/error.test.ts b/packages/hardhat-zksync-solc/test/tests/error.test.ts new file mode 100644 index 000000000..f3165a1c4 --- /dev/null +++ b/packages/hardhat-zksync-solc/test/tests/error.test.ts @@ -0,0 +1,23 @@ +import { expect } from 'chai'; +import { ZkSyncSolcPluginError } from '../../src/errors'; + +describe('ZkSyncSolcPluginError', () => { + it('should create a new ZkSyncSolcPluginError instance', () => { + const message = 'Test error message'; + const parentError = new Error('Parent error'); + const error = new ZkSyncSolcPluginError(message, parentError); + + expect(error.name).to.equal('ZkSyncSolcPluginError'); + expect(error.message).to.equal(message); + expect(error.parent?.message).to.equal(parentError.message); + }); + + it('should have the correct stack trace', () => { + const message = 'Test error message'; + const error = new ZkSyncSolcPluginError(message); + + expect(error.stack).to.be.a('string'); + expect(error.stack).to.include('ZkSyncSolcPluginError'); + expect(error.stack).to.include(message); + }); +}); diff --git a/packages/hardhat-zksync-solc/test/tests/extractor.test.ts b/packages/hardhat-zksync-solc/test/tests/extractor.test.ts new file mode 100644 index 000000000..8c0967d38 --- /dev/null +++ b/packages/hardhat-zksync-solc/test/tests/extractor.test.ts @@ -0,0 +1,239 @@ +import { expect } from 'chai'; +import { + SolcMultiUserConfigExtractor, + SolcSoloUserConfigExtractor, + SolcStringUserConfigExtractor, +} from '../../src/extractor'; + +describe('SolcMultiUserConfigExtractor', () => { + describe('suitable', () => { + it('should return false if solidityConfig is undefined', () => { + const solidityConfig = undefined; + const extractor = new SolcMultiUserConfigExtractor(); + + const result = extractor.suitable(solidityConfig); + + expect(result).to.be.equal(false); + }); + + it('should return true if solidityConfig is a MultiSolcUserConfig', () => { + const solidityConfig = { + compilers: [], + overrides: {}, + }; + const extractor = new SolcMultiUserConfigExtractor(); + + const result = extractor.suitable(solidityConfig); + + expect(result).to.be.equal(true); + }); + + it('should return true if solidityConfig is valid MultiSolcUserConfig', () => { + const solidityConfig = { + compilers: [ + { + version: '0.8.17', + eraVersion: 'latest', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + outputSelection: {}, + metadata: {}, + }, + }, + ], + overrides: {}, + }; + const extractor = new SolcMultiUserConfigExtractor(); + + const result = extractor.suitable(solidityConfig); + + expect(result).to.be.equal(true); + }); + }); + + describe('extract', () => { + it('should extract the solc user config data', () => { + const solidityConfig = { + compilers: [ + { + version: '0.8.17', + eraVersion: 'latest', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + outputSelection: {}, + metadata: {}, + }, + }, + ], + overrides: { + 'file1.sol': { + version: '0.8.17', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + outputSelection: {}, + metadata: {}, + }, + }, + 'file2.sol': { + version: '0.7.3', + settings: { + optimizer: { + enabled: false, + runs: 150, + }, + outputSelection: {}, + metadata: {}, + }, + }, + }, + }; + const extractor = new SolcMultiUserConfigExtractor(); + + const result = extractor.extract(solidityConfig); + + expect(result.compilers).to.deep.equal([ + { + version: '0.8.17', + eraVersion: 'latest', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + outputSelection: {}, + metadata: {}, + }, + }, + ]); + expect(result.overides).to.deep.equal( + new Map([ + [ + 'file1.sol', + { + version: '0.8.17', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + outputSelection: {}, + metadata: {}, + }, + }, + ], + [ + 'file2.sol', + { + version: '0.7.3', + settings: { + optimizer: { + enabled: false, + runs: 150, + }, + outputSelection: {}, + metadata: {}, + }, + }, + ], + ]), + ); + }); + }); +}); + +describe('SolcSoloUserConfigExtractor', () => { + describe('suitable', () => { + it('should return false if solidityConfig is undefined', () => { + const solidityConfig = undefined; + const extractor = new SolcSoloUserConfigExtractor(); + + const result = extractor.suitable(solidityConfig); + + expect(result).to.be.equal(false); + }); + + it('should return true if solidityConfig is a SolcUserConfig', () => { + const solidityConfig = { + version: '0.8.17', + eraVersion: 'latest', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + outputSelection: {}, + metadata: {}, + }, + }; + const extractor = new SolcSoloUserConfigExtractor(); + + const result = extractor.suitable(solidityConfig); + + expect(result).to.be.equal(true); + }); + }); + + describe('extract', () => { + it('should extract the solc user config data', () => { + const solidityConfig = { + version: '0.8.17', + eraVersion: 'latest', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + outputSelection: {}, + metadata: {}, + }, + }; + const extractor = new SolcSoloUserConfigExtractor(); + + const result = extractor.extract(solidityConfig); + + expect(result.compilers).to.deep.equal([solidityConfig]); + }); + }); +}); + +describe('SolcStringUserConfigExtractor', () => { + describe('suitable', () => { + it('should return false if solidityConfig is undefined', () => { + const solidityConfig = undefined; + const extractor = new SolcStringUserConfigExtractor(); + + const result = extractor.suitable(solidityConfig); + + expect(result).to.be.equal(false); + }); + + it('should return true if solidityConfig is a string', () => { + const solidityConfig = 'solc-config'; + const extractor = new SolcStringUserConfigExtractor(); + + const result = extractor.suitable(solidityConfig); + + expect(result).to.be.equal(true); + }); + }); + + describe('extract', () => { + it('should extract the solc user config data', () => { + const solidityConfig = 'solc-config'; + const extractor = new SolcStringUserConfigExtractor(); + + const result = extractor.extract(solidityConfig); + + expect(result.compilers).to.deep.equal([]); + }); + }); +}); diff --git a/packages/hardhat-zksync-solc/test/tests/tests.ts b/packages/hardhat-zksync-solc/test/tests/tests.ts new file mode 100644 index 000000000..8ecd585a3 --- /dev/null +++ b/packages/hardhat-zksync-solc/test/tests/tests.ts @@ -0,0 +1,472 @@ +import { + TASK_COMPILE, + TASK_COMPILE_SOLIDITY_GET_COMPILATION_JOBS, + TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH, + TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, + TASK_COMPILE_SOLIDITY_GET_SOURCE_NAMES, +} from 'hardhat/builtin-tasks/task-names'; +import chalk from 'chalk'; +import fs from 'fs'; +import sinonChai from 'sinon-chai'; +import chai, { assert } from 'chai'; + +import { DependencyGraph } from 'hardhat/types/builtin-tasks/compile'; +import path from 'path'; +import { SOLIDITY_FILES_CACHE_FILENAME } from 'hardhat/internal/constants'; +import sinon from 'sinon'; +import { CompilerDownloader } from 'hardhat/internal/solidity/compiler/downloader'; +import { ZksolcCompilerDownloader } from '../../src/compile/downloader'; +import { useEnvironment } from '../helpers'; +import { ZkSyncArtifact } from '../../src/types'; + +chai.use(sinonChai); + +describe('zksolc plugin', async function () { + describe('Extend HRE', async function () { + useEnvironment('multiple-compilers'); + + it('Should extend environment', async function () { + assert.isDefined(this.env.config.zksolc); + assert.isDefined(this.env.config.zksolc.settings); + assert.isDefined(this.env.config.zksolc.settings.libraries); + assert.isDefined(this.env.config.zksolc.settings.missingLibrariesPath); + + assert.include(this.env.config.paths.artifacts, '/fixture-projects/multiple-compilers/artifacts-zk'); + assert.include(this.env.config.paths.cache, '/fixture-projects/multiple-compilers/cache-zk'); + + const compilers = this.env.config.solidity.compilers; + + assert.equal(compilers.length, 1); + assert.equal(compilers[0].version, '0.8.17'); + assert.equal(compilers[0].settings.optimizer.enabled, true); + + const overrides = this.env.config.solidity.overrides; + assert.equal(overrides['contracts/Greeter2.sol'].version, '0.8.16'); + }); + }); + + describe('Filter contracts', async function () { + useEnvironment('multiple-contracts'); + + it('Should successfully return all contracts for compiling', async function () { + const rootPath = this.env.config.paths.root; + const sourcePaths: string[] = [`${rootPath}/contracts/Greeter.sol`, `${rootPath}/contracts/Greeter2.sol`]; + + const sourceNames: string[] = await this.env.run(TASK_COMPILE_SOLIDITY_GET_SOURCE_NAMES, { + rootPath, + sourcePaths, + }); + + assert.equal(2, sourceNames.length); + assert.equal('contracts/Greeter.sol', sourceNames[0]); + assert.equal('contracts/Greeter2.sol', sourceNames[1]); + + this.env.config.zksolc.settings.contractsToCompile = []; + + const sourceNames1: string[] = await this.env.run(TASK_COMPILE_SOLIDITY_GET_SOURCE_NAMES, { + rootPath, + sourcePaths, + }); + + assert.equal(2, sourceNames1.length); + assert.equal('contracts/Greeter.sol', sourceNames1[0]); + assert.equal('contracts/Greeter2.sol', sourceNames1[1]); + }); + + it('Should successfully return only Greeter contracts for compiling', async function () { + this.env.config.zksolc.settings.contractsToCompile = ['contracts/Greeter.sol']; + + const rootPath = this.env.config.paths.root; + const sourcePaths: string[] = [`${rootPath}/contracts/Greeter.sol`, `${rootPath}/contracts/Greeter2.sol`]; + + const sourceNames: string[] = await this.env.run(TASK_COMPILE_SOLIDITY_GET_SOURCE_NAMES, { + rootPath, + sourcePaths, + }); + + assert.equal(1, sourceNames.length); + assert.equal('contracts/Greeter.sol', sourceNames[0]); + }); + }); + + describe('Compilation jobs', async function () { + useEnvironment('multiple-contracts'); + + const compilerVersion = process.env.SOLC_VERSION || '0.8.17'; + + const sandbox = sinon.createSandbox(); + + let isZksolcDownloadedStub: sinon.SinonStub; + let getZksolcCompilerPathStub: sinon.SinonStub; + let getZksolcCompilerVersionStub: sinon.SinonStub; + let downloadCompilerStub: sinon.SinonStub; + + async function isCompilerDownloaded(isZksolcDownloaded: boolean): Promise { + return isZksolcDownloaded; + } + + beforeEach(() => { + downloadCompilerStub = sandbox.stub(ZksolcCompilerDownloader.prototype, 'downloadCompiler').resolves(); + getZksolcCompilerPathStub = sandbox + .stub(ZksolcCompilerDownloader.prototype, 'getCompilerPath') + .returns('zksolc/zksolc-version-0'); + getZksolcCompilerVersionStub = sandbox + .stub(ZksolcCompilerDownloader.prototype, 'getVersion') + .returns('zksolc-version-0'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + after(() => { + (ZksolcCompilerDownloader as any)._instance = undefined; + }); + + it('Should download compiler and update jobs', async function () { + isZksolcDownloadedStub = sandbox + .stub(ZksolcCompilerDownloader.prototype, 'isCompilerDownloaded') + .returns(isCompilerDownloaded(false)); + + const rootPath = this.env.config.paths.root; + const sourceNames: string[] = ['contracts/Greeter.sol', 'contracts/Greeter2.sol']; + + const solidityFilesCachePath = path.join(this.env.config.paths.cache, SOLIDITY_FILES_CACHE_FILENAME); + + const dependencyGraph: DependencyGraph = await this.env.run(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH, { + rootPath, + sourceNames, + solidityFilesCachePath, + }); + + const { jobs, errors } = await this.env.run(TASK_COMPILE_SOLIDITY_GET_COMPILATION_JOBS, { + dependencyGraph, + solidityFilesCachePath, + }); + + sandbox.assert.calledOnce(isZksolcDownloadedStub); + sandbox.assert.calledOnce(getZksolcCompilerPathStub); + sandbox.assert.calledOnce(getZksolcCompilerVersionStub); + sandbox.assert.calledOnceWithExactly(downloadCompilerStub); + + assert.equal(2, jobs.length); + assert.equal(0, errors.length); + + jobs.forEach((job: any) => { + const solidityConfig = job.solidityConfig; + assert.equal(solidityConfig.version, compilerVersion); + assert.equal(solidityConfig.zksolc.version, 'zksolc-version-0'); + assert.equal(solidityConfig.zksolc.settings.compilerPath, 'zksolc/zksolc-version-0'); + // assert.equal(solidityConfig.zksolc.settings.libraries, {}); + }); + }); + + it('Should not download compiler and update jobs with libraries', async function () { + isZksolcDownloadedStub = sandbox + .stub(ZksolcCompilerDownloader.prototype, 'isCompilerDownloaded') + .returns(isCompilerDownloaded(true)); + + const rootPath = this.env.config.paths.root; + const sourceNames: string[] = ['contracts/Greeter.sol', 'contracts/Greeter2.sol']; + + this.env.config.zksolc.settings.libraries = { + 'contracts/Greeter.sol': { + 'contracts/Greeter.sol': '0x1234567890123456789012345678901234567890', + }, + }; + + const solidityFilesCachePath = path.join(this.env.config.paths.cache, SOLIDITY_FILES_CACHE_FILENAME); + + const dependencyGraph: DependencyGraph = await this.env.run(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH, { + rootPath, + sourceNames, + solidityFilesCachePath, + }); + + const { jobs, errors } = await this.env.run(TASK_COMPILE_SOLIDITY_GET_COMPILATION_JOBS, { + dependencyGraph, + solidityFilesCachePath, + }); + + sandbox.assert.calledOnce(isZksolcDownloadedStub); + sandbox.assert.calledOnce(getZksolcCompilerPathStub); + sandbox.assert.calledOnce(getZksolcCompilerVersionStub); + sandbox.assert.notCalled(downloadCompilerStub); + + assert.equal(2, jobs.length); + assert.equal(0, errors.length); + + jobs.forEach((job: any) => { + const solidityConfig = job.solidityConfig; + assert.equal(solidityConfig.version, compilerVersion); + assert.equal(solidityConfig.zksolc.version, 'zksolc-version-0'); + assert.equal(solidityConfig.zksolc.settings.compilerPath, 'zksolc/zksolc-version-0'); + assert.equal( + solidityConfig.zksolc.settings.libraries['contracts/Greeter.sol']['contracts/Greeter.sol'], + '0x1234567890123456789012345678901234567890', + ); + }); + }); + }); + + describe('Solc build', async function () { + describe('Solc build for docker', async function () { + useEnvironment('docker-compile'); + + it('Should get solc build for docker compiler', async function () { + const build = await this.env.run(TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, { + quiet: true, + solcVersion: '0.8.17', + }); + + assert.equal(build.compilerPath, ''); + assert.equal(build.isSolcJs, false); + assert.equal(build.version, '0.8.17'); + assert.equal(build.longVersion, ''); + }); + }); + describe('Solc build for binary', async function () { + useEnvironment('multiple-contracts'); + const sandbox = sinon.createSandbox(); + + async function isCompilerDownloaded(): Promise { + return true; + } + + beforeEach(() => { + sandbox.stub(CompilerDownloader.prototype, 'isCompilerDownloaded').returns(isCompilerDownloaded()); + sandbox.stub(CompilerDownloader.prototype, 'getCompiler').resolves({ + compilerPath: 'solc/solc-version-0', + version: '0.8.17', + longVersion: 'solc/solc-version-0-long', + isSolcJs: false, + }); + }); + + afterEach(() => { + sandbox.restore(); + }); + + after(() => { + (ZksolcCompilerDownloader as any)._instance = undefined; + }); + + it('Should get solc build for binary compiler', async function () { + const build = await this.env.run(TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, { + quiet: true, + solcVersion: '0.8.17', + compilationJob: { + getSolcConfig: () => { + return { + version: '0.8.17', + }; + }, + }, + }); + + assert.equal(build.compilerPath, 'solc/solc-version-0'); + assert.equal(build.isSolcJs, false); + assert.equal(build.version, '0.8.17'); + assert.equal(build.longVersion, 'solc/solc-version-0-long'); + }); + }); + }); + + describe('zksolc plugin compile without zksolc flag', async function () { + useEnvironment('no-zksync'); + + it('Should successfully compile a simple contract without zksync flag', async function () { + await this.env.run(TASK_COMPILE); + + const artifact = this.env.artifacts.readArtifactSync('Greeter') as ZkSyncArtifact; + + assert.equal(artifact.contractName, 'Greeter'); + + // Check that zkSync-specific artifact information was added. + assert.isUndefined(artifact.factoryDeps); + assert.isTrue(fs.existsSync(this.env.config.paths.cache)); + assert.isTrue(fs.existsSync(this.env.config.paths.artifacts)); + + assert.include(this.env.config.paths.artifacts, 'fixture-projects/no-zksync/artifacts'); + assert.include(this.env.config.paths.cache, 'fixture-projects/no-zksync/cache'); + }); + }); + + describe('zksolc plugin compile with zksolc flag', async function () { + describe('Simple', async function () { + useEnvironment('simple'); + + it('Should successfully compile a simple contract', async function () { + await this.env.run(TASK_COMPILE); + + const artifact = this.env.artifacts.readArtifactSync('Greeter') as ZkSyncArtifact; + + assert.equal(artifact.contractName, 'Greeter'); + + // Check that zkSync-specific artifact information was added. + assert.deepEqual(artifact.factoryDeps, {}, 'Contract unexpectedly has dependencies'); + }); + }); + + describe('Inlined library', async function () { + useEnvironment('library'); + + it('Should successfully compile the contract with inlined library', async function () { + if (this.env.config.solidity.compilers[0].version.startsWith('0.4')) { + console.info(chalk.cyan('Test skipped since is not applicable to Solidity 0.4.x.')); + return; + } + await this.env.run(TASK_COMPILE); + assert.equal(this.env.artifacts.readArtifactSync('contracts/Foo.sol:Foo').contractName, 'Foo'); + assert.equal(this.env.artifacts.readArtifactSync('contracts/Import.sol:Import').contractName, 'Import'); + }); + }); + + describe('Linked library', async function () { + useEnvironment('linked'); + + it('Should successfully compile the contract with linked library', async function () { + if (this.env.config.solidity.compilers[0].version.startsWith('0.4')) { + console.info(chalk.cyan('Test skipped since is not applicable to Solidity 0.4.x.')); + return; + } + await this.env.run(TASK_COMPILE); + assert.equal(this.env.artifacts.readArtifactSync('contracts/Foo.sol:Foo').contractName, 'Foo'); + assert.equal(this.env.artifacts.readArtifactSync('contracts/Import.sol:Import').contractName, 'Import'); + }); + }); + + describe('Factory', async function () { + useEnvironment('factory'); + + it('Should successfully compile the factory contract', async function () { + await this.env.run(TASK_COMPILE); + + const factoryArtifact = this.env.artifacts.readArtifactSync( + 'contracts/Factory.sol:Factory', + ) as ZkSyncArtifact; + const depArtifact = this.env.artifacts.readArtifactSync('contracts/Factory.sol:Dep') as ZkSyncArtifact; + + assert.equal(factoryArtifact.contractName, 'Factory'); + assert.equal(depArtifact.contractName, 'Dep'); + + // Check that zkSync-specific artifact information was added. + + // Factory contract should have one dependency. + // We do not check for the actual value of the hash, as it depends on the bytecode yielded by the compiler and thus not static. + // Instead we only check that it's a hash indeed. + const depHash = Object.keys(factoryArtifact.factoryDeps)[0]; + const expectedLength = 32 * 2 + 2; // 32 bytes in hex + '0x'. + assert(depHash.startsWith('0x') && depHash.length === expectedLength, 'Contract hash is malformed'); + + const depName = 'contracts/Factory.sol:Dep'; + assert.equal(depName, factoryArtifact.factoryDeps[depHash], 'No required dependency in the artifact'); + + // For the dependency contract should be no further dependencies. + assert.deepEqual(depArtifact.factoryDeps, {}, 'Unexpected factory-deps for a dependency contract'); + }); + }); + + describe('Nested Factory', async function () { + useEnvironment('nested'); + + it('Should successfully compile nested contracts', async function () { + await this.env.run(TASK_COMPILE); + + const factoryArtifact = this.env.artifacts.readArtifactSync('NestedFactory') as ZkSyncArtifact; + const fooDepArtifact = this.env.artifacts.readArtifactSync( + 'contracts/deps/Foo.sol:FooDep', + ) as ZkSyncArtifact; + const barDepArtifact = this.env.artifacts.readArtifactSync( + 'contracts/deps/more_deps/Bar.sol:BarDep', + ) as ZkSyncArtifact; + + // Check that zkSync-specific artifact information was added. + + // Factory contract should have one dependency. + // We do not check for the actual value of the hash, as it depends on the bytecode yielded by the compiler and thus not static. + // Instead we only check that it's a hash indeed. + const fooDepName = 'contracts/deps/Foo.sol:FooDep'; + const barDepName = 'contracts/deps/more_deps/Bar.sol:BarDep'; + for (const depName of [fooDepName, barDepName]) { + assert( + Object.values(factoryArtifact.factoryDeps).includes(depName), + `No required dependency in the artifact: ${depName}`, + ); + } + for (const depHash in factoryArtifact.factoryDeps) { + if (!depHash) { + continue; + } + const expectedLength = 32 * 2 + 2; // 32 bytes in hex + '0x'. + assert(depHash.startsWith('0x') && depHash.length === expectedLength, 'Contract hash is malformed'); + } + + // For the dependency contract should be no further dependencies. + for (const depArtifact of [fooDepArtifact, barDepArtifact]) { + assert.deepEqual(depArtifact.factoryDeps, {}, 'Unexpected factory-deps for a dependency contract'); + } + + // Each factory dependency should be accessible through `readArtifact` without changing it's identifier. + const fooDepArtifactFromFactoryDeps = this.env.artifacts.readArtifactSync(fooDepName); + assert.equal( + fooDepArtifactFromFactoryDeps.contractName, + fooDepArtifact.contractName, + 'Artifacts do not match', + ); + assert.equal(fooDepArtifactFromFactoryDeps.bytecode, fooDepArtifact.bytecode, 'Artifacts do not match'); + assert.deepEqual(fooDepArtifactFromFactoryDeps.abi, fooDepArtifact.abi, 'Artifacts do not match'); + }); + }); + + describe('Missing Library', async function () { + useEnvironment('missing-libraries'); + + it('Should successfully identify all the missing libraries', async function () { + if (this.env.config.solidity.compilers[0].version.startsWith('0.4')) { + console.info(chalk.cyan('Test skipped since is not applicable to Solidity 0.4.x.')); + return; + } + + await this.env.run(TASK_COMPILE); + + // Assert that there is a json file with the list of missing libraries at the location this.env.config.zksolc.settings.missingLibrariesPath. + const missingLibraries = JSON.parse( + fs.readFileSync(this.env.config.zksolc.settings.missingLibrariesPath!, 'utf8'), + ); + assert.isNotEmpty(missingLibraries); + + const expectedMissingLibraries = [ + { + contractName: 'ChildChildLib', + contractPath: 'contracts/ChildChildLib.sol', + missingLibraries: [], + }, + { + contractName: 'ChildLib', + contractPath: 'contracts/ChildLib.sol', + missingLibraries: ['contracts/ChildChildLib.sol:ChildChildLib'], + }, + { + contractName: 'MathLib', + contractPath: 'contracts/MathLib.sol', + missingLibraries: ['contracts/ChildLib.sol:ChildLib'], + }, + ]; + + // Assert that list of missing libraries is correct. + assert.deepEqual(missingLibraries, expectedMissingLibraries); + }); + + afterEach(async function () { + if (this.env.config.solidity.compilers[0].version.startsWith('0.4')) { + console.info(chalk.cyan('Test skipped since is not applicable to Solidity 0.4.x.')); + return; + } + + // Remove the file with the list of missing libraries. + fs.unlinkSync(this.env.config.zksolc.settings.missingLibrariesPath!); + }); + }); + }); +}); diff --git a/packages/hardhat-zksync-solc/test/tests/utils.test.ts b/packages/hardhat-zksync-solc/test/tests/utils.test.ts new file mode 100644 index 000000000..ed254cba5 --- /dev/null +++ b/packages/hardhat-zksync-solc/test/tests/utils.test.ts @@ -0,0 +1,297 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { SolcConfig } from 'hardhat/types'; +import fs from 'fs'; +import { MockAgent, setGlobalDispatcher } from 'undici'; +import { CompilerOutputSelection, ZkSolcConfig } from '../../src/types'; +import { + filterSupportedOutputSelections, + updateCompilerConf, + getVersionComponents, + writeLibrariesToFile, + download, + getLatestRelease, + saveDataToFile, + getZksolcUrl, + pluralize, + sha1, +} from '../../src/utils'; + +describe('Utils', () => { + describe('filterSupportedOutputSelections', () => { + it('should filter unsupported output selections based on zkCompilerVersion', () => { + const outputSelection = { + 'file1.sol': { + Contract1: ['abi', 'evm.bytecode'], + }, + 'file2.sol': { + Contract2: ['abi', 'evm.bytecode', 'metadata'], + }, + }; + const zkCompilerVersion = '1.3.7'; + + const filteredOutputSelection = filterSupportedOutputSelections(outputSelection, zkCompilerVersion); + + expect(filteredOutputSelection).to.deep.equal({ + 'file1.sol': { + Contract1: ['abi'], + }, + 'file2.sol': { + Contract2: ['abi', 'metadata'], + }, + }); + }); + }); + + describe('updateCompilerConf', () => { + it('should update compiler configuration with zksolc settings', () => { + const outputSelection: CompilerOutputSelection = { + 'file1.sol': { + Contract1: ['abi', 'evm.bytecode'], + }, + 'file2.sol': { + Contract2: ['abi', 'evm.bytecode', 'metadata'], + }, + }; + + const compiler: SolcConfig = { + version: '0.8.17', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + outputSelection, + metadata: {}, + }, + }; + const zksolc: ZkSolcConfig = { + version: '1.3.17', + settings: { + optimizer: { + enabled: false, + runs: 150, + }, + metadata: {}, + }, + }; + + updateCompilerConf({ compiler }, zksolc, [{ version: '0.8.17' }]); + + expect(compiler.settings.optimizer).to.deep.equal(zksolc.settings.optimizer); + }); + + it('should update compiler configuration with zksolc and with zkvm solc', () => { + const outputSelection: CompilerOutputSelection = { + 'file1.sol': { + Contract1: ['abi', 'evm.bytecode'], + }, + 'file2.sol': { + Contract2: ['abi', 'evm.bytecode', 'metadata'], + }, + }; + + const compiler: SolcConfig = { + version: '0.8.17', + eraVersion: 'latest', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + outputSelection, + metadata: {}, + }, + }; + const zksolc: ZkSolcConfig = { + version: '1.3.17', + settings: { + optimizer: { + enabled: false, + runs: 150, + }, + metadata: {}, + }, + }; + + updateCompilerConf({ compiler }, zksolc, [{ version: '0.8.17', eraVersion: 'latest' }]); + + expect(compiler.settings.optimizer).to.deep.equal(zksolc.settings.optimizer); + }); + }); + + describe('getVersionComponents', () => { + it('should return an array of version components', () => { + const version = '0.7.2'; + + const versionComponents = getVersionComponents(version); + + expect(versionComponents).to.deep.equal([0, 7, 2]); + }); + }); + + describe('writeLibrariesToFile', () => { + it('should write libraries to file', async () => { + const path = './test/libraries.json'; + const libraries = [ + { name: 'Library1', address: '0x1234567890' }, + { name: 'Library2', address: '0xabcdef1234' }, + ]; + + await writeLibrariesToFile(path, libraries); + + expect(fs.existsSync(path)).to.equal(true); + + fs.rmSync(path); + }); + }); + + describe('download', () => { + const mockAgent = new MockAgent(); + setGlobalDispatcher(mockAgent); + + it('should download a file from a URL', async () => { + const url = 'https://example.com/'; + const filePath = './file.txt'; + const userAgent = 'Test User Agent'; + const version = '1.0.0'; + + const mockPool = mockAgent.get('https://example.com/'); + + mockPool + .intercept({ + path: '/', + method: 'GET', + headers: {}, + body: JSON.stringify({ + 'User-Agent': `${userAgent} ${version}`, + }), + }) + .reply(200, { + message: 'all good', + }); + + await download(url, filePath, userAgent, version); + + expect(fs.existsSync(filePath)).to.equal(true); + + fs.rmSync(filePath); + }); + }); + + describe('getRelease', () => { + const mockAgent = new MockAgent(); + setGlobalDispatcher(mockAgent); + + it('should get release information from GitHub', async () => { + const owner = 'example'; + const repo = 'project'; + const userAgent = 'Test User Agent'; + const tag = 'v1.0.0'; + + const mockPool = mockAgent.get('https://github.com'); + + mockPool + .intercept({ + path: `/${owner}/${repo}/releases/latest`, + method: 'GET', + headers: { + 'User-Agent': `${userAgent}`, + }, + }) + .reply( + 302, + {}, + { + headers: { + location: `https://github.com/${owner}/${repo}/releases/tag/${tag}`, + }, + }, + ); + + const release = await getLatestRelease(owner, repo, userAgent); + + expect(release).to.deep.equal('1.0.0'); + }); + }); + + describe('saveDataToFile', () => { + it('should save data to a file', async () => { + const testData = { name: 'Test Data' }; + const path = './test/libraries.json'; + + await saveDataToFile(testData, path); + + expect(fs.existsSync(path)).to.equal(true); + + fs.rmSync(path); + }); + }); +}); + +describe('getZksolcUrl', () => { + const platformStub = sinon.stub(process, 'platform'); + + it('should return the release URL when isRelease is true', () => { + platformStub.value('linux'); // Mock the process.platform property + const repo = 'example/repo'; + const version = '1.0.0'; + let expectedUrl = 'example/repo/releases/download/v1.0.0/zksolc-linux-amd64-musl-v1.0.0'; + + const url = getZksolcUrl(repo, version, true); + + expect(url).to.equal(expectedUrl); + + platformStub.value('darwin'); // Mock the process.platform property + expectedUrl = 'example/repo/releases/download/v1.0.0/zksolc-macosx-amd64-v1.0.0'; + + const urlMac = getZksolcUrl(repo, version, true); + + expect(urlMac).to.equal(expectedUrl); + }); + + it('should return the raw URL when isRelease is false', () => { + platformStub.value('linux'); // Mock the process.platform property + const repo = 'example/repo'; + const version = '1.0.0'; + let expectedUrl = 'example/repo/raw/main/linux-amd64/zksolc-linux-amd64-musl-v1.0.0'; + + const url = getZksolcUrl(repo, version, false); + + expect(url).to.equal(expectedUrl); + + platformStub.value('darwin'); // Mock the process.platform property + expectedUrl = 'example/repo/raw/main/macosx-amd64/zksolc-macosx-amd64-v1.0.0'; + + const urlMac = getZksolcUrl(repo, version, false); + + expect(urlMac).to.equal(expectedUrl); + }); +}); +describe('pluralize', () => { + it('should return singular when n is 1', () => { + const result = pluralize(1, 'apple', 'apples'); + expect(result).to.equal('apple'); + }); + + it('should return plural when n is not 1 and plural is provided', () => { + const result = pluralize(3, 'apple', 'apples'); + expect(result).to.equal('apples'); + }); + + it('should return singular with "s" appended when n is not 1 and plural is not provided', () => { + const result = pluralize(3, 'apple'); + expect(result).to.equal('apples'); + }); + + describe('sha1', () => { + it('should return the SHA1 hash of a string', () => { + const str = 'Hello, World!'; + const expectedHash = '0a0a9f2a6772942557ab5355d76af442f8f65e01'; + + const hash = sha1(str); + + expect(hash).to.equal(expectedHash); + }); + }); +}); diff --git a/packages/hardhat-zksync-toolbox/CHANGELOG.md b/packages/hardhat-zksync-toolbox/CHANGELOG.md index 3a49fdb98..91f0c0c8d 100644 --- a/packages/hardhat-zksync-toolbox/CHANGELOG.md +++ b/packages/hardhat-zksync-toolbox/CHANGELOG.md @@ -1,5 +1,12 @@ # @matterlabs/hardhat-zksync-toolbox +## [1.2.1](https://github.com/matter-labs/hardhat-zksync/compare/@matterlabs/hardhat-zksync-toolbox@1.2.0...@matterlabs/hardhat-zksync-toolbox-v1.2.1) (2023-12-22) + + +### Fixes + +* **docs:** update readme files ([#612](https://github.com/matter-labs/hardhat-zksync/issues/612)) ([682338e](https://github.com/matter-labs/hardhat-zksync/commit/682338e60f52021206325ff6eeec2c394a118642)) + ## 1.2.0 ### Minor Changes diff --git a/packages/hardhat-zksync-toolbox/README.md b/packages/hardhat-zksync-toolbox/README.md index 2aa1f7401..afcdb879e 100644 --- a/packages/hardhat-zksync-toolbox/README.md +++ b/packages/hardhat-zksync-toolbox/README.md @@ -1,5 +1,46 @@ -# hardhat-zksync-toolbox +# hardhat-zksync-toolbox 🚀 -[Hardhat](https://hardhat.org/) plugin provides a convenient method for bundling and accessing a range of zkSync-related Hardhat plugins. +zkSync Era [Hardhat](https://hardhat.org/) plugin provides a convenient method for bundling and accessing a range of zkSync-related Hardhat plugins. -Check out the documentation page for the [zksync toolbox plugin](https://era.zksync.io/docs/tools/hardhat/plugins.html) for more detailed explanation on how to use hardhat-zksync-toolbox. \ No newline at end of file +![Era Logo](https://github.com/matter-labs/era-contracts/raw/main/eraLogo.svg) + +## ⚠️ Version Compatibility Warning + +Ensure you are using the correct version of the plugin with ethers: +- For plugin version **<1.0.0**: + - Compatible with ethers **v5**. + +- For plugin version **≥1.0.0**: + - Compatible with ethers **v6** (⭐ Recommended) + +## 📥 Installation + +To install **hardhat-zksync-toolbox** plugin, run: + +`npm i -D @matterlabs/hardhat-zksync-toolbox` + +or + +`yarn add -D @matterlabs/hardhat-zksync-toolbox ethers zksync-ethers` + +## 📝 Documentation + +In addition to the [hardhat-zksync-toolbox](https://era.zksync.io/docs/tools/hardhat/plugins.html), zkSync's Era [website](https://era.zksync.io/docs/) offers a variety of resources including: + +[Guides to get started](https://era.zksync.io/docs/dev/building-on-zksync/hello-world.html): Learn how to start building on zkSync Era.\ +[Hardhat zkSync Era plugins](https://era.zksync.io/docs/tools/hardhat/getting-started.html): Overview and guides for all Hardhat zkSync Era plugins.\ +[Hyperscaling](https://era.zksync.io/docs/reference/concepts/hyperscaling.html#what-are-hyperchains): Deep dive into hyperscaling on zkSync Era. + +## 🤝 Contributing + +Contributions are always welcome! Feel free to open any issue or send a pull request. + +Go to [CONTRIBUTING.md](https://github.com/matter-labs/hardhat-zksync/blob/main/.github/CONTRIBUTING.md) to learn about steps and best practices for contributing to zkSync hardhat tooling base repository. + + +## 🙌 Feedback, help and news + +[zkSync Era Discord server](https://join.zksync.dev/): for questions and feedback.\ +[Follow zkSync Era on Twitter](https://twitter.com/zksync) + +## Happy building! 👷‍♀️👷‍♂️ \ No newline at end of file diff --git a/packages/hardhat-zksync-toolbox/package.json b/packages/hardhat-zksync-toolbox/package.json index eec884898..2ab5dbc9e 100644 --- a/packages/hardhat-zksync-toolbox/package.json +++ b/packages/hardhat-zksync-toolbox/package.json @@ -1,6 +1,6 @@ { "name": "@matterlabs/hardhat-zksync-toolbox", - "version": "1.2.0", + "version": "1.2.1", "description": "zkSync bundle of Hardhat plugins", "repository": "github:matter-labs/hardhat-zksync-toolbox", "homepage": "https://github.com/matter-labs/hardhat-zksync/tree/main/packages/hardhat-zksync-toolbox", @@ -32,13 +32,17 @@ "LICENSE", "README.md" ], - "dependencies": {}, + "dependencies": { + "hardhat": "^2.19.4", + "zksync-ethers": "^6.0.0", + "@nomicfoundation/hardhat-chai-matchers": "^2.0.2", + "chai": "^4.3.7" + }, "devDependencies": { "@matterlabs/hardhat-zksync-chai-matchers": "^1.2.0", "@matterlabs/hardhat-zksync-deploy": "^1.1.0", "@matterlabs/hardhat-zksync-solc": "^1.0.3", "@matterlabs/hardhat-zksync-verify": "^1.2.0", - "@nomicfoundation/hardhat-chai-matchers": "^2.0.2", "@nomicfoundation/hardhat-ethers": "^3.0.4", "@types/chai": "^4.2.0", "@types/mocha": "^9.1.0", @@ -46,20 +50,17 @@ "@types/semver": "^7.3.9", "@typescript-eslint/eslint-plugin": "6.13.1", "@typescript-eslint/parser": "6.13.1", - "chai": "^4.3.7", "eslint": "^8.54.0", "eslint-config-prettier": "9.0.0", "eslint-plugin-import": "2.29.0", "eslint-plugin-no-only-tests": "3.1.0", "eslint-plugin-prettier": "5.0.1", "ethers": "^6.7.1", - "hardhat": "^2.19.2", "mocha": "^10.1.0", "prettier": "3.1.0", "rimraf": "^3.0.2", "ts-node": "^10.6.0", "typescript": "^5.1.6", - "zksync-ethers": "^6.0.0", "c8": "^8.0.1" }, "peerDependencies": { diff --git a/packages/hardhat-zksync-toolbox/test/helpers.ts b/packages/hardhat-zksync-toolbox/test/helpers.ts index ccc6e4a27..545e35c63 100644 --- a/packages/hardhat-zksync-toolbox/test/helpers.ts +++ b/packages/hardhat-zksync-toolbox/test/helpers.ts @@ -17,7 +17,7 @@ export function useEnvironmentWithLocalSetup(fixtureProjectName: string, network process.env.HARDHAT_NETWORK = networkName; this.env = require('hardhat'); - this.env.run(TASK_CLEAN); + const _ = this.env.run(TASK_CLEAN); }); after(async function () { diff --git a/packages/hardhat-zksync-toolbox/test/tests.ts b/packages/hardhat-zksync-toolbox/test/tests.ts index 3e0dda157..ca6028c6b 100644 --- a/packages/hardhat-zksync-toolbox/test/tests.ts +++ b/packages/hardhat-zksync-toolbox/test/tests.ts @@ -15,7 +15,7 @@ describe('zksync toolbox plugin', function () { useEnvironmentWithLocalSetup('simple'); it('All tasks should be registered in HRE', async function () { - const taskNames = Object.keys(this.env['tasks']); + const taskNames = Object.keys(this.env.tasks); assert(taskNames.includes(TASK_COMPILE)); assert(taskNames.includes(TASK_DEPLOY_ZKSYNC)); @@ -36,6 +36,7 @@ describe('zksync toolbox plugin', function () { }); it('Should test for properPrivateKey chai matcher', async function () { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions expect(RICH_WALLET_PK).to.be.properPrivateKey; }); diff --git a/packages/hardhat-zksync-upgradable/.eslintrc.js b/packages/hardhat-zksync-upgradable/.eslintrc.js index 56ca0b4bd..b0d17045a 100644 --- a/packages/hardhat-zksync-upgradable/.eslintrc.js +++ b/packages/hardhat-zksync-upgradable/.eslintrc.js @@ -4,4 +4,18 @@ module.exports = { project: `${__dirname}/eslint-tsconfig.json`, sourceType: "module", }, + rules: { + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "property", + "format": ["camelCase", "PascalCase", "snake_case"], + "leadingUnderscore": "allow", + "filter": { + "regex": "^[a-zA-Z0-9-_]+$", + "match": false + } + }, + ], + }, }; diff --git a/packages/hardhat-zksync-upgradable/CHANGELOG.md b/packages/hardhat-zksync-upgradable/CHANGELOG.md index 821fd6dc5..a8fa3aa19 100644 --- a/packages/hardhat-zksync-upgradable/CHANGELOG.md +++ b/packages/hardhat-zksync-upgradable/CHANGELOG.md @@ -1,5 +1,12 @@ # @matterlabs/hardhat-zksync-upgradable +## [1.2.1](https://github.com/matter-labs/hardhat-zksync/compare/@matterlabs/hardhat-zksync-upgradable@1.2.0...@matterlabs/hardhat-zksync-upgradable-v1.2.1) (2023-12-22) + + +### Fixes + +* **docs:** update readme files ([#612](https://github.com/matter-labs/hardhat-zksync/issues/612)) ([682338e](https://github.com/matter-labs/hardhat-zksync/commit/682338e60f52021206325ff6eeec2c394a118642)) + ## 1.2.0 ### Minor Changes diff --git a/packages/hardhat-zksync-upgradable/README.md b/packages/hardhat-zksync-upgradable/README.md index fbd17faec..713ca85ae 100644 --- a/packages/hardhat-zksync-upgradable/README.md +++ b/packages/hardhat-zksync-upgradable/README.md @@ -1,6 +1,133 @@ # hardhat-zksync-upgradable -[Hardhat](https://hardhat.org/) plugin to deploy and upgrade smart contracts on the zkSync network. +zkSync Era's [Hardhat](https://hardhat.org/) plugin to deploy and upgrade smart contracts easily. -Check out the documentation page for the [zksync upgradeable plugin](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-upgradable.html) for more detailed explanation on how to use hardhat-zksync-upgradable. -. \ No newline at end of file +![Era Logo](https://github.com/matter-labs/era-contracts/raw/main/eraLogo.svg) + +## ⚠️ Version Compatibility Warning + +Ensure you are using the correct version of the plugin with ethers: +- For plugin version **<1.0.0**: + - Compatible with ethers **v5**. + +- For plugin version **≥1.0.0**: + - Compatible with ethers **v6** (⭐ Recommended) + +## 📥 Installation + +To install **hardhat-zksync-upgradable** plugin, run: + +`npm install -D @matterlabs/hardhat-zksync-upgradable` + +or + +`yarn add -D @matterlabs/hardhat-zksync-upgradable @openzeppelin/contracts @openzeppelin/contracts-upgradeable` + +## 📖 Example + +The plugin supports three types of proxies: Transparent upgradable proxies, UUPS proxies, and beacon proxies. + +Upgradability methods are all part of the zkUpgrades property in the HardhatRuntimeEnvironment and you only need to interact with it in order to deploy or upgrade your contracts. + +- **Deploying proxies** + +To deploy a simple upgradable contract on zkSync Era local setup, first create a test wallet and add it to the new Deployer. +After that, load the your contract artifact and call the deployProxy method from the zkUpgrades hre property. + + +``` +const zkWallet = new Wallet("PRIVATE_KEY"); +const deployer = new Deployer(hre, zkWallet); +const contract = await deployer.loadArtifact("YourContractName"); +await hre.zkUpgrades.deployProxy(deployer.zkWallet, contract, [initializerFunctionArguments], { initializer: "initialize" }); +``` + +The deployProxy method deploys your implementation contract on zkSync Era, deploys the proxy admin contract, and finally, deploys the transparent proxy. + +- **Deploying UUPS proxies** + +The UUPS proxy pattern is similar to the transparent proxy pattern, except that the upgrade is triggered via the logic contract instead of from the proxy contract. +To deploy the UUPS contract, use the same script as for the transparent upgradable proxy. +When you run the script, the plugin detects that the proxy type is UUPS, it executes the deployment, and saves the deployment info in your manifest file. + +- **Beacon proxies** + +Beacon proxies enable a more advanced upgrade pattern, where multiple implementation contracts can be deployed and "hot-swapped" on the fly with no disruption to the contract's operation. + +Start by creating a Deployer for the zkSync Era network and load the Box artifact: + +- **Upgrading proxies** + +In order for a smart contract implementation to be upgradable, it has to follow specific [rules](https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable). + +To upgrade the implementation of the transparent upgradeable contract, use the upgradeProxy method from the zkUpgrades. + +``` +const myContractV2 = await deployer.loadArtifact('contractV2'); +await hre.zkUpgrades.upgradeProxy(deployer.zkWallet, , myContractV2); +``` + +- **Upgrade UUPS proxy** + +Similar to the deployment script, there are no modifications needed to upgrade the implementation of the UUPS contract, compared to upgrading the transparent upgradable contract. + +- **Upgrade beacon proxy** + +Beacon proxy implementation can be upgraded using a similarly structured method from the zkUpgrades called upgradeBeacon + +``` +const myContractV2 = await deployer.loadArtifact('contractV2'); +await hre.zkUpgrades.upgradeBeacon(deployer.zkWallet, , myContractV2); +``` + +The hardhat-zksync-upgradable plugin supports proxy verification, which means you can verify all the contracts deployed during the proxy deployment with a single verify command. +Check how to verify on this [link](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-upgradable.html#proxy-verification) + +## 💼 Proxy validations + +The hardhat-zksync-upgradable plugin has built-in checks to ensure that your smart contract's newest implementation version follows the necessary requirements when upgrading your smart contract. + +You can learn more about what those restrictions are in [OpenZeppelin's documentation](https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable). + +## 🧯 Proxy gas fee estimation + +Should you wish to estimate the total gas used throughout the proxy deployment process, consider utilizing the upgradable plugin's gas estimation functions. We offer three types of gas estimation functions for your convenience: + +- estimateGasProxy +- estimateGasBeacon +- estimateGasBeaconProxy + +To estimate the deployment fee for the Transparent upgradable proxies and UUPS proxies, use the estimateGasProxy method from the zkUpgrades.estimation. +This method calculates the fee for deploying the implementation contract, transparent proxy/UUPS contract, and the ProxyAdmin smart contract. + +`const totalGasEstimation = await hre.zkUpgrades.estimation.estimateGasProxy(deployer, contract, [], { kind: "transparent" });` + +To estimate the deployment fee for the beacon contract and its corresponding implementation, use the estimateGasBeacon method: + +`const totalGasEstimation = await hre.zkUpgrades.estimation.estimateGasBeacon(deployer, contract, []);` + +If you want to get the estimation for the beacon proxy contract, please use the estimateGasBeaconProxy method: + +`const totalGasEstimation = await hre.zkUpgrades.estimation.estimateGasBeacon(deployer, contract, []);` + +## 📝 Documentation + +In addition to the [hardhat-zksync-upgradable](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-upgradable.html), zkSync's Era [website](https://era.zksync.io/docs/) offers a variety of resources including: + +[Guides to get started](https://era.zksync.io/docs/dev/building-on-zksync/hello-world.html): Learn how to start building on zkSync Era.\ +[Hardhat zkSync Era plugins](https://era.zksync.io/docs/tools/hardhat/getting-started.html): Overview and guides for all Hardhat zkSync Era plugins.\ +[Hyperscaling](https://era.zksync.io/docs/reference/concepts/hyperscaling.html#what-are-hyperchains): Deep dive into hyperscaling on zkSync Era. + +## 🤝 Contributing + +Contributions are always welcome! Feel free to open any issue or send a pull request. + +Go to [CONTRIBUTING.md](https://github.com/matter-labs/hardhat-zksync/blob/main/.github/CONTRIBUTING.md) to learn about steps and best practices for contributing to zkSync hardhat tooling base repository. + + +## 🙌 Feedback, help and news + +[zkSync Era Discord server](https://join.zksync.dev/): for questions and feedback.\ +[Follow zkSync Era on Twitter](https://twitter.com/zksync) + +## Happy building! \ No newline at end of file diff --git a/packages/hardhat-zksync-upgradable/package.json b/packages/hardhat-zksync-upgradable/package.json index 9ae48b8e0..e0e034811 100644 --- a/packages/hardhat-zksync-upgradable/package.json +++ b/packages/hardhat-zksync-upgradable/package.json @@ -1,6 +1,6 @@ { "name": "@matterlabs/hardhat-zksync-upgradable", - "version": "1.2.0", + "version": "1.2.1", "description": "Hardhat plugin to deploy and update upgradable smart contracts for the zkSync network", "repository": "github:matter-labs/hardhat-zksync", "homepage": "https://github.com/matter-labs/hardhat-zksync/tree/main/packages/hardhat-zksync-upgradable", @@ -35,7 +35,16 @@ "dependencies": { "@matterlabs/hardhat-zksync-deploy": "^1.1.0", "@matterlabs/hardhat-zksync-solc": "^1.0.3", - "@openzeppelin/upgrades-core": "^1.31.0" + "@openzeppelin/upgrades-core": "^1.31.0", + "chalk": "4.1.2", + "hardhat": "^2.19.4", + "fs-extra": "^7.0.1", + "ethereumjs-util": "^6.2.1", + "zksync-ethers": "^6.0.0", + "ethers": "^6.7.1", + "solidity-ast": "^0.4.51", + "proper-lockfile": "^4.1.1", + "compare-versions": "^6.0.0" }, "devDependencies": { "@matterlabs/hardhat-zksync-deploy": "^1.1.0", @@ -46,14 +55,11 @@ "@types/proper-lockfile": "^4.1.2", "@typescript-eslint/eslint-plugin": "6.13.1", "@typescript-eslint/parser": "6.13.1", - "chalk": "4.1.2", "eslint": "^8.54.0", "eslint-config-prettier": "9.0.0", "eslint-plugin-import": "2.29.0", "eslint-plugin-no-only-tests": "3.1.0", "eslint-plugin-prettier": "5.0.1", - "ethers": "^6.7.1", - "hardhat": "^2.19.2", "mocha": "^10.2.0", "prettier": "3.1.0", "rimraf": "^3.0.2", diff --git a/packages/hardhat-zksync-upgradable/src/admin.ts b/packages/hardhat-zksync-upgradable/src/admin.ts index db4bc5a70..6bbec59da 100644 --- a/packages/hardhat-zksync-upgradable/src/admin.ts +++ b/packages/hardhat-zksync-upgradable/src/admin.ts @@ -1,8 +1,8 @@ import chalk from 'chalk'; import type { HardhatRuntimeEnvironment } from 'hardhat/types'; import { getAdminAddress } from '@openzeppelin/upgrades-core'; +import { Wallet, Contract } from 'zksync-ethers'; import { Manifest } from './core/manifest'; -import { Wallet,Contract } from 'zksync-ethers'; import { getAdminFactory } from './proxy-deployment/deploy-proxy-admin'; import { ZkSyncUpgradablePluginError } from './errors'; @@ -16,9 +16,9 @@ export function makeChangeProxyAdmin(hre: HardhatRuntimeEnvironment): ChangeAdmi const proxyAdminAddress = await getAdminAddress(wallet.provider, proxyAddress); - if (await proxyAdminManifest.getAddress() !== proxyAdminAddress) { + if ((await proxyAdminManifest.getAddress()) !== proxyAdminAddress) { throw new ZkSyncUpgradablePluginError('Proxy admin is not the one registered in the network manifest'); - } else if (await proxyAdminManifest.getAddress() !== newAdmin) { + } else if ((await proxyAdminManifest.getAddress()) !== newAdmin) { await proxyAdminManifest.changeProxyAdmin(proxyAddress, newAdmin); } }; @@ -33,7 +33,7 @@ export function makeTransferProxyAdminOwnership(hre: HardhatRuntimeEnvironment): const manifest = await Manifest.forNetwork(wallet.provider); const { proxies } = await manifest.read(); for (const { address, kind } of proxies) { - if (await admin.getAddress() == (await getAdminAddress(wallet.provider, address))) { + if ((await admin.getAddress()) === (await getAdminAddress(wallet.provider, address))) { console.info(chalk.green(`${address} (${kind}) proxy ownership transfered through admin proxy`)); } else { console.info(chalk.red(`${address} (${kind}) proxy ownership not affected by admin proxy`)); diff --git a/packages/hardhat-zksync-upgradable/src/constants.ts b/packages/hardhat-zksync-upgradable/src/constants.ts index f7327f4f7..fed26f4fa 100644 --- a/packages/hardhat-zksync-upgradable/src/constants.ts +++ b/packages/hardhat-zksync-upgradable/src/constants.ts @@ -28,7 +28,7 @@ export const TOPIC_LOGS_NOT_FOUND_ERROR = (topic: string, address: string) => export const EVENT_NOT_FOUND_ERROR = (address: string, events: string[]) => `Could not find an event with any of the following topics in the logs for address ${address}: ${events.join( - ', ' + ', ', )}` + 'If the proxy was recently deployed, the transaction may not be available on Block Explorer yet. Try running the verify task again after waiting a few blocks.'; diff --git a/packages/hardhat-zksync-upgradable/src/core/function.ts b/packages/hardhat-zksync-upgradable/src/core/function.ts index fbd41c9ef..54ef263dd 100644 --- a/packages/hardhat-zksync-upgradable/src/core/function.ts +++ b/packages/hardhat-zksync-upgradable/src/core/function.ts @@ -8,7 +8,7 @@ function serializeParameterType(parameter: VariableDeclaration, deref: ASTDerefe if (storageLocation === 'storage') { assert(typeof typeName.typeDescriptions.typeString === 'string'); - return typeName.typeDescriptions.typeString.replace(/^struct /, '') + ' storage'; + return `${typeName.typeDescriptions.typeString.replace(/^struct /, '')} storage`; } return serializeTypeName(typeName, deref); @@ -25,11 +25,11 @@ function serializeTypeName(typeName: TypeName, deref: ASTDereferencer): string { case 'UserDefinedTypeName': { const userDefinedType = deref( ['StructDefinition', 'EnumDefinition', 'ContractDefinition', 'UserDefinedValueTypeDefinition'], - typeName.referencedDeclaration + typeName.referencedDeclaration, ); switch (userDefinedType.nodeType) { case 'StructDefinition': - return '(' + userDefinedType.members.map((member) => serializeParameterType(member, deref)) + ')'; + return `(${userDefinedType.members.map((member) => serializeParameterType(member, deref))})`; case 'EnumDefinition': assert(userDefinedType.members.length < 256); diff --git a/packages/hardhat-zksync-upgradable/src/core/impl-store.ts b/packages/hardhat-zksync-upgradable/src/core/impl-store.ts index 1da042a52..6b2fa66c5 100644 --- a/packages/hardhat-zksync-upgradable/src/core/impl-store.ts +++ b/packages/hardhat-zksync-upgradable/src/core/impl-store.ts @@ -8,9 +8,9 @@ import { getClientVersion, } from '@openzeppelin/upgrades-core'; -import { Manifest, ManifestData, ImplDeployment } from './manifest'; import assert from 'assert'; import * as zk from 'zksync-ethers'; +import { Manifest, ManifestData, ImplDeployment } from './manifest'; import { getChainId } from './provider'; interface ManifestLens { @@ -26,33 +26,34 @@ export interface ManifestField { } async function fetchOrDeployGeneric( + // eslint-disable-next-line @typescript-eslint/no-shadow lens: ManifestLens, provider: zk.Provider, deploy: () => Promise, opts?: DeployOpts, - merge?: boolean + merge?: boolean, ): Promise { const manifest = await Manifest.forNetwork(provider); try { const deployment = await manifest.lockedRun(async () => { const data = await manifest.read(); - const deployment = lens(data); - if (merge && !deployment.merge) { + const deploymentInternal = lens(data); + if (merge && !deploymentInternal.merge) { throw new Error( - 'fetchOrDeployGeneric was called with merge set to true but the deployment lens does not have a merge function' + 'fetchOrDeployGeneric was called with merge set to true but the deployment lens does not have a merge function', ); } - const stored = deployment.get(); - const updated = await resumeOrDeploy(provider, stored, deploy, lens.type, opts, deployment, merge); + const stored = deploymentInternal.get(); + const updated = await resumeOrDeploy(provider, stored, deploy, lens.type, opts, deploymentInternal, merge); if (updated !== stored) { - if (merge && deployment.merge) { + if (merge && deploymentInternal.merge) { await checkForAddressClash(provider, data, updated, false); - deployment.merge(updated); + deploymentInternal.merge(updated); } else { await checkForAddressClash(provider, data, updated, true); - deployment.set(updated); + deploymentInternal.set(updated); } await manifest.write(data); } @@ -90,7 +91,7 @@ export async function fetchOrDeploy( provider: zk.Provider, deploy: () => Promise, opts?: DeployOpts, - merge?: boolean + merge?: boolean, ): Promise { return (await fetchOrDeployGeneric(implLens(version.linkedWithoutMetadata), provider, deploy, opts, merge)).address; } @@ -100,7 +101,7 @@ export async function fetchOrDeployGetDeployment( provider: zk.Provider, deploy: () => Promise, opts?: DeployOpts, - merge?: boolean + merge?: boolean, ): Promise { return fetchOrDeployGeneric(implLens(version.linkedWithoutMetadata), provider, deploy, opts, merge); } @@ -135,7 +136,7 @@ export function mergeAddresses(existing: ImplDeployment, value: ImplDeployment) export async function fetchOrDeployAdmin( provider: zk.Provider, deploy: () => Promise, - opts?: DeployOpts + opts?: DeployOpts, ): Promise { return (await fetchOrDeployGeneric(adminLens, provider, deploy, opts)).address; } @@ -164,7 +165,7 @@ async function checkForAddressClash( provider: zk.Provider, data: ManifestData, updated: Deployment, - checkAllAddresses: boolean + checkAllAddresses: boolean, ): Promise { const clash = lookupDeployment(data, updated.address, checkAllAddresses); if (clash !== undefined) { @@ -172,9 +173,11 @@ async function checkForAddressClash( clash.set(undefined); } else { throw new Error( - `The following deployment clashes with an existing one at ${updated.address}\n\n` + - JSON.stringify(updated, null, 2) + - `\n\n` + `The following deployment clashes with an existing one at ${updated.address}\n\n${JSON.stringify( + updated, + null, + 2, + )}\n\n`, ); } } @@ -183,7 +186,7 @@ async function checkForAddressClash( function lookupDeployment( data: ManifestData, address: string, - checkAllAddresses: boolean + checkAllAddresses: boolean, ): ManifestField | undefined { if (data.admin?.address === address) { return adminLens(data); diff --git a/packages/hardhat-zksync-upgradable/src/core/manifest-storage-layout.ts b/packages/hardhat-zksync-upgradable/src/core/manifest-storage-layout.ts index 3da144e79..947c18738 100644 --- a/packages/hardhat-zksync-upgradable/src/core/manifest-storage-layout.ts +++ b/packages/hardhat-zksync-upgradable/src/core/manifest-storage-layout.ts @@ -11,16 +11,16 @@ import { Manifest } from './manifest'; export async function getStorageLayoutForAddress( manifest: Manifest, validations: ValidationData, - implAddress: string + implAddress: string, ): Promise { const data = await manifest.read(); const versionWithoutMetadata = Object.keys(data.impls).find( - (v) => data.impls[v]?.address === implAddress || data.impls[v]?.allAddresses?.includes(implAddress) + (v) => data.impls[v]?.address === implAddress || data.impls[v]?.allAddresses?.includes(implAddress), ); if (versionWithoutMetadata === undefined) { throw new UpgradesError( `Deployment at address ${implAddress} is not registered`, - () => 'To register a previously deployed proxy for upgrading, use the forceImport function.' + () => 'To register a previously deployed proxy for upgrading, use the forceImport function.', ); } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -34,10 +34,10 @@ export async function getStorageLayoutForAddress( return layout; } else { await manifest.lockedRun(async () => { - const data = await manifest.read(); + const manifestData = await manifest.read(); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - data.impls[versionWithoutMetadata]!.layout = updatedLayout; - await manifest.write(data); + manifestData.impls[versionWithoutMetadata]!.layout = updatedLayout; + await manifest.write(manifestData); }); return updatedLayout; } @@ -52,7 +52,7 @@ export async function getStorageLayoutForAddress( export function getUpdatedStorageLayout( data: ValidationData, versionWithoutMetadata: string, - layout: StorageLayout + layout: StorageLayout, ): StorageLayout | undefined { // In this map we store the candidate storage layouts, based on having the // same versionWithoutMetadata and a sufficiently similar layout. The keys of @@ -62,7 +62,7 @@ export function getUpdatedStorageLayout( outer: for (const [contractName, validationRun] of findVersionWithoutMetadataMatches( data, - versionWithoutMetadata + versionWithoutMetadata, )) { const updatedLayout = unfoldStorageLayout(validationRun, contractName); diff --git a/packages/hardhat-zksync-upgradable/src/core/manifest.ts b/packages/hardhat-zksync-upgradable/src/core/manifest.ts index 5f7c6f3b8..a3e48d42d 100644 --- a/packages/hardhat-zksync-upgradable/src/core/manifest.ts +++ b/packages/hardhat-zksync-upgradable/src/core/manifest.ts @@ -5,11 +5,11 @@ import { compare as compareVersions } from 'compare-versions'; import type { Deployment } from '@openzeppelin/upgrades-core/src/deployment'; import type { StorageLayout } from '@openzeppelin/upgrades-core/src/storage'; -import { mapValues, pick } from '../utils/utils-general'; import * as zk from 'zksync-ethers'; -import { getChainId, networkNames } from './provider'; +import { mapValues, pick } from '../utils/utils-general'; import { MANIFEST_DEFAULT_DIR } from '../constants'; import { ZkSyncUpgradablePluginError } from '../errors'; +import { getChainId, networkNames } from './provider'; const currentManifestVersion = '3.2'; @@ -42,8 +42,8 @@ function defaultManifest(): ManifestData { } export class Manifest { - readonly chainId: number; - readonly file: string; + private readonly chainId: number; + private readonly file: string; private readonly dir: string; private readonly chainIdSuffix: string; @@ -51,7 +51,7 @@ export class Manifest { private locked = false; - static async forNetwork(provider: zk.Provider): Promise { + public static async forNetwork(provider: zk.Provider): Promise { const chainId = await getChainId(provider); return new Manifest(chainId); } @@ -61,21 +61,21 @@ export class Manifest { this.chainIdSuffix = `${chainId}`; this.dir = MANIFEST_DEFAULT_DIR; - + const defaultFallbackName = `unknown-network-${chainId}`; const networkName = networkNames[chainId] !== undefined ? networkNames[chainId] : defaultFallbackName; this.file = path.join(MANIFEST_DEFAULT_DIR, `${networkName}.json`); } - async getAdmin(): Promise { + public async getAdmin(): Promise { return (await this.read()).admin; } - async getDeploymentFromAddress(address: string): Promise { + public async getDeploymentFromAddress(address: string): Promise { const data = await this.read(); const deployment = Object.values(data.impls).find( - (d) => d?.address === address || d?.allAddresses?.includes(address) + (d) => d?.address === address || d?.allAddresses?.includes(address), ); if (deployment === undefined) { @@ -84,7 +84,7 @@ export class Manifest { return deployment; } - async getProxyFromAddress(address: string): Promise { + public async getProxyFromAddress(address: string): Promise { const data = await this.read(); const deployment = data.proxies.find((d) => d?.address === address); if (deployment === undefined) { @@ -93,7 +93,7 @@ export class Manifest { return deployment; } - async addProxy(proxy: ProxyDeployment): Promise { + public async addProxy(proxy: ProxyDeployment): Promise { await this.lockedRun(async () => { const data = await this.read(); const existing = data.proxies.findIndex((p) => p.address === proxy.address); @@ -105,18 +105,18 @@ export class Manifest { }); } - private async readFile(): Promise { + private async _readFile(): Promise { return await fs.readFile(this.file, 'utf8'); } - private async writeFile(content: string): Promise { + private async _writeFile(content: string): Promise { await fs.writeFile(this.file, content); } - async read(retries?: number): Promise { - const release = this.locked ? undefined : await this.lock(retries); + public async read(retries?: number): Promise { + const release = this.locked ? undefined : await this._lock(retries); try { - const data = JSON.parse(await this.readFile()) as ManifestData; + const data = JSON.parse(await this._readFile()) as ManifestData; return validateOrUpdateManifestVersion(data); } catch (e: any) { if (e.code === 'ENOENT') { @@ -133,19 +133,19 @@ export class Manifest { } } - async write(data: ManifestData): Promise { + public async write(data: ManifestData): Promise { if (!this.locked) { throw new ZkSyncUpgradablePluginError('Manifest must be locked'); } const normalized = normalizeManifestData(data); - await this.writeFile(JSON.stringify(normalized, null, 2) + '\n'); + await this._writeFile(`${JSON.stringify(normalized, null, 2)}\n`); } - async lockedRun(cb: () => Promise): Promise { + public async lockedRun(cb: () => Promise): Promise { if (this.locked) { throw new ZkSyncUpgradablePluginError('Manifest is already locked'); } - const release = await this.lock(); + const release = await this._lock(); try { return await cb(); } finally { @@ -153,7 +153,7 @@ export class Manifest { } } - private async lock(retries = 3) { + private async _lock(retries = 3) { const lockfileName = path.join(this.dir, `chain-${this.chainIdSuffix}`); await fs.mkdir(path.dirname(lockfileName), { recursive: true }); @@ -171,7 +171,7 @@ function validateOrUpdateManifestVersion(data: ManifestData): ManifestData { throw new ZkSyncUpgradablePluginError('Manifest version is missing'); } else if (compareVersions(data.manifestVersion, '3.0', '<')) { throw new ZkSyncUpgradablePluginError( - 'Found a manifest file for OpenZeppelin CLI. An automated migration is not yet available.' + 'Found a manifest file for OpenZeppelin CLI. An automated migration is not yet available.', ); } else if (compareVersions(data.manifestVersion, currentManifestVersion, '<')) { return migrateManifest(data); @@ -206,7 +206,7 @@ function normalizeDeployment(input: D): Deployment; function normalizeDeployment(input: D, include: K[]): Deployment & Pick; function normalizeDeployment( input: D, - include: K[] = [] + include: K[] = [], ): Deployment & Pick { return pick(input, ['address', 'txHash', ...include]); } diff --git a/packages/hardhat-zksync-upgradable/src/core/proxy-kind.ts b/packages/hardhat-zksync-upgradable/src/core/proxy-kind.ts index 338a44736..0548b917d 100644 --- a/packages/hardhat-zksync-upgradable/src/core/proxy-kind.ts +++ b/packages/hardhat-zksync-upgradable/src/core/proxy-kind.ts @@ -8,14 +8,14 @@ import { BeaconProxyUnsupportedError, Version, } from '@openzeppelin/upgrades-core'; -import { Manifest, DeploymentNotFound, ProxyDeployment } from './manifest'; import * as zk from 'zksync-ethers'; import { ZkSyncUpgradablePluginError } from '../errors'; +import { Manifest, DeploymentNotFound, ProxyDeployment } from './manifest'; export async function setProxyKind( provider: zk.Provider, proxyAddress: string, - opts: ValidationOptions + opts: ValidationOptions, ): Promise { const manifest = await Manifest.forNetwork(provider); @@ -31,7 +31,7 @@ export async function setProxyKind( opts.kind = manifestDeployment?.kind ?? 'transparent'; } else if (manifestDeployment && opts.kind !== manifestDeployment.kind) { throw new ZkSyncUpgradablePluginError( - `Requested an upgrade of kind ${opts.kind} but proxy is ${manifestDeployment.kind}` + `Requested an upgrade of kind ${opts.kind} but proxy is ${manifestDeployment.kind}`, ); } @@ -43,7 +43,7 @@ export async function processProxyKind( proxyAddress: string | undefined, opts: ValidationOptions, data: ValidationData, - version: Version + version: Version, ) { if (opts.kind === undefined) { if (proxyAddress !== undefined && (await isBeaconProxy(provider, proxyAddress))) { diff --git a/packages/hardhat-zksync-upgradable/src/core/validate.ts b/packages/hardhat-zksync-upgradable/src/core/validate.ts index 762066768..619fef4ee 100644 --- a/packages/hardhat-zksync-upgradable/src/core/validate.ts +++ b/packages/hardhat-zksync-upgradable/src/core/validate.ts @@ -1,13 +1,12 @@ import { getVersion, SolcOutput, ValidationRunData } from '@openzeppelin/upgrades-core'; import { SrcDecoder } from '@openzeppelin/upgrades-core/src/src-decoder'; -import { astDereferencer } from 'solidity-ast/utils'; import { extractStorageLayout } from '@openzeppelin/upgrades-core/dist/storage'; -import { isNodeType, findAll, ASTDereferencer } from 'solidity-ast/utils'; +import { isNodeType, findAll, ASTDereferencer, astDereferencer } from 'solidity-ast/utils'; import { Node } from 'solidity-ast/node'; import type { ContractDefinition, FunctionDefinition } from 'solidity-ast'; -import { getFunctionSignature } from './function'; import { isNullish } from '../utils/utils-general'; +import { getFunctionSignature } from './function'; export type ValidationError = | ValidationErrorConstructor @@ -29,7 +28,7 @@ const errorKinds = [ interface ValidationErrorBase { src: string; - kind: typeof errorKinds[number]; + kind: (typeof errorKinds)[number]; } interface ValidationErrorWithName extends ValidationErrorBase { @@ -73,7 +72,9 @@ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVers const delegateCallCache = initOpcodeCache(); for (const source in solcOutput.contracts) { + if (!source) continue; for (const contractName in solcOutput.contracts[source]) { + if (!contractName) continue; const bytecode = solcOutput.contracts[source][contractName].evm.bytecode?.object || null; const version = bytecode === null ? undefined : getVersion(bytecode); @@ -113,7 +114,7 @@ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVers contractDef, decodeSrc, deref, - solcOutput.contracts[source][contractDef.name].storageLayout + solcOutput.contracts[source][contractDef.name].storageLayout, ); validation[key].methods = [...findAll('FunctionDefinition', contractDef)] @@ -124,10 +125,12 @@ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVers } for (const key in inheritIds) { + if (!key) continue; validation[key].inherit = inheritIds[key].map((id) => fromId[id]); } for (const key in libraryIds) { + if (!key) continue; validation[key].libraries = libraryIds[key].map((id) => fromId[id]); } @@ -162,7 +165,7 @@ function* getOpcodeErrors( contractDef: ContractDefinition, deref: ASTDereferencer, decodeSrc: SrcDecoder, - delegateCallCache: OpcodeCache + delegateCallCache: OpcodeCache, ): Generator { yield* getContractOpcodeErrors(contractDef, deref, decodeSrc, OPCODES.delegatecall, 'main', delegateCallCache); } @@ -178,7 +181,7 @@ function* getContractOpcodeErrors( decodeSrc: SrcDecoder, opcode: OpcodePattern, scope: Scope, - cache: OpcodeCache + cache: OpcodeCache, ): Generator { const cached = getCachedOpcodes(contractDef.id, scope, cache); if (cached !== undefined) { @@ -188,7 +191,7 @@ function* getContractOpcodeErrors( setCachedOpcodes(contractDef.id, scope, cache, errors); errors.push( ...getFunctionOpcodeErrors(contractDef, deref, decodeSrc, opcode, scope, cache), - ...getInheritedContractOpcodeErrors(contractDef, deref, decodeSrc, opcode, cache) + ...getInheritedContractOpcodeErrors(contractDef, deref, decodeSrc, opcode, cache), ); yield* errors; } @@ -200,7 +203,7 @@ function* getFunctionOpcodeErrors( decodeSrc: SrcDecoder, opcode: OpcodePattern, scope: Scope, - cache: OpcodeCache + cache: OpcodeCache, ): Generator { const parentContractDef = getParentDefinition(deref, contractOrFunctionDef); if (parentContractDef === undefined || !skipCheck(opcode.kind, parentContractDef)) { @@ -226,7 +229,7 @@ function* getInheritedContractOpcodeErrors( deref: ASTDereferencer, decodeSrc: SrcDecoder, opcode: OpcodePattern, - cache: OpcodeCache + cache: OpcodeCache, ) { if (!skipCheckReachable(opcode.kind, contractDef)) { for (const base of contractDef.baseContracts) { @@ -246,7 +249,7 @@ function isInternalFunction(node: Node) { function* getStateVariableErrors( contractDef: ContractDefinition, - decodeSrc: SrcDecoder + decodeSrc: SrcDecoder, ): Generator { for (const varDecl of contractDef.nodes) { if (isNodeType('VariableDeclaration', varDecl)) { @@ -293,12 +296,12 @@ function* getDirectFunctionOpcodeErrors( contractOrFunctionDef: ContractDefinition | FunctionDefinition, decodeSrc: SrcDecoder, opcode: OpcodePattern, - scope: Scope + scope: Scope, ) { for (const fnCall of findAll( 'FunctionCall', contractOrFunctionDef, - (node) => skipCheck(opcode.kind, node) || (scope === 'inherited' && isInternalFunction(node)) + (node) => skipCheck(opcode.kind, node) || (scope === 'inherited' && isInternalFunction(node)), )) { const fn = fnCall.expression; if (fn.typeDescriptions.typeIdentifier?.match(opcode.pattern)) { @@ -316,12 +319,12 @@ function* getReferencedFunctionOpcodeErrors( decodeSrc: SrcDecoder, opcode: OpcodePattern, scope: Scope, - cache: OpcodeCache + cache: OpcodeCache, ) { for (const fnCall of findAll( 'FunctionCall', contractOrFunctionDef, - (node) => skipCheckReachable(opcode.kind, node) || (scope === 'inherited' && isInternalFunction(node)) + (node) => skipCheckReachable(opcode.kind, node) || (scope === 'inherited' && isInternalFunction(node)), )) { const fn = fnCall.expression; if ('referencedDeclaration' in fn && fn.referencedDeclaration && fn.referencedDeclaration > 0) { @@ -380,7 +383,7 @@ export function getAnnotationArgs(doc: string, tag: string) { const result: string[] = []; for (const { groups } of execall( /^\s*(?:@(?\w+)(?::(?<tag>[a-z][a-z-]*))? )?(?<args>(?:(?!^\s*@\w+)[^])*)/m, - doc + doc, )) { if (groups && groups.title === 'custom' && groups.tag === tag) { const trimmedArgs = groups.args.trim(); diff --git a/packages/hardhat-zksync-upgradable/src/gas-estimation/estimate-gas-beacon-proxy.ts b/packages/hardhat-zksync-upgradable/src/gas-estimation/estimate-gas-beacon-proxy.ts index aed4b88c1..82f7e75eb 100644 --- a/packages/hardhat-zksync-upgradable/src/gas-estimation/estimate-gas-beacon-proxy.ts +++ b/packages/hardhat-zksync-upgradable/src/gas-estimation/estimate-gas-beacon-proxy.ts @@ -2,29 +2,32 @@ import type { HardhatRuntimeEnvironment } from 'hardhat/types'; import chalk from 'chalk'; import assert from 'assert'; import path from 'path'; +import { Deployer } from '@matterlabs/hardhat-zksync-deploy'; import { DeployProxyOptions } from '../utils/options'; import { ZkSyncUpgradablePluginError } from '../errors'; import { convertGasPriceToEth } from '../utils/utils-general'; import { BEACON_PROXY_JSON } from '../constants'; -import { Deployer } from '@matterlabs/hardhat-zksync-deploy'; import { getMockedBeaconData } from './estimate-gas-beacon'; -export interface EstimateBeaconGasFunction { - (deployer: Deployer, args?: DeployProxyOptions[], opts?: DeployProxyOptions, quiet?: boolean): Promise<bigint>; -} +export type EstimateBeaconGasFunction = ( + deployer: Deployer, + args?: DeployProxyOptions[], + opts?: DeployProxyOptions, + quiet?: boolean, +) => Promise<bigint>; export function makeEstimateGasBeaconProxy(hre: HardhatRuntimeEnvironment): EstimateBeaconGasFunction { return async function estimateGasBeaconProxy( deployer: Deployer, args: DeployProxyOptions[] = [], opts: DeployProxyOptions = {}, - quiet: boolean = false + quiet: boolean = false, ) { const { mockedBeaconAddress, data } = await getMockedBeaconData(deployer, hre, args, opts); const beaconProxyPath = (await hre.artifacts.getArtifactPaths()).find((artifactPath) => - artifactPath.includes(path.sep + BEACON_PROXY_JSON) + artifactPath.includes(path.sep + BEACON_PROXY_JSON), ); assert(beaconProxyPath, 'Beacon proxy artifact not found'); const beaconProxyContract = await import(beaconProxyPath); @@ -38,9 +41,9 @@ export function makeEstimateGasBeaconProxy(hre: HardhatRuntimeEnvironment): Esti console.info( chalk.cyan( `Deployment of the beacon proxy contract is estimated to cost: ${convertGasPriceToEth( - beaconProxyGasCost - )} ETH` - ) + beaconProxyGasCost, + )} ETH`, + ), ); console.info(chalk.cyan(`Total estimated gas cost: ${convertGasPriceToEth(beaconProxyGasCost)} ETH`)); } diff --git a/packages/hardhat-zksync-upgradable/src/gas-estimation/estimate-gas-beacon.ts b/packages/hardhat-zksync-upgradable/src/gas-estimation/estimate-gas-beacon.ts index ca96b8f38..705877dd8 100644 --- a/packages/hardhat-zksync-upgradable/src/gas-estimation/estimate-gas-beacon.ts +++ b/packages/hardhat-zksync-upgradable/src/gas-estimation/estimate-gas-beacon.ts @@ -15,15 +15,13 @@ import { getAdminArtifact, getAdminFactory } from '../proxy-deployment/deploy-pr import { getChainId } from '../core/provider'; import { deploy } from '../proxy-deployment/deploy'; -export interface EstimateGasFunction { - ( - deployer: Deployer, - artifact: ZkSyncArtifact, - args?: DeployProxyOptions[], - opts?: DeployProxyOptions, - quiet?: boolean - ): Promise<bigint>; -} +export type EstimateGasFunction = ( + deployer: Deployer, + artifact: ZkSyncArtifact, + args?: DeployProxyOptions[], + opts?: DeployProxyOptions, + quiet?: boolean, +) => Promise<bigint>; async function deployProxyAdminLocally(adminFactory: zk.ContractFactory) { const mockContract = await deploy(adminFactory); @@ -32,7 +30,7 @@ async function deployProxyAdminLocally(adminFactory: zk.ContractFactory) { async function deployBeaconLocally(impl: string, hre: HardhatRuntimeEnvironment, wallet: zk.Wallet) { const upgradableBeaconPath = (await hre.artifacts.getArtifactPaths()).find((x) => - x.includes(path.sep + UPGRADABLE_BEACON_JSON) + x.includes(path.sep + UPGRADABLE_BEACON_JSON), ); assert(upgradableBeaconPath, 'Upgradable beacon artifact not found'); const upgradeableBeaconContract = await import(upgradableBeaconPath); @@ -40,7 +38,7 @@ async function deployBeaconLocally(impl: string, hre: HardhatRuntimeEnvironment, const upgradeableBeaconFactory = new zk.ContractFactory( upgradeableBeaconContract.abi, upgradeableBeaconContract.bytecode, - wallet + wallet, ); return await deploy(upgradeableBeaconFactory, impl); } @@ -49,7 +47,7 @@ export async function getMockedBeaconData( deployer: Deployer, hre: HardhatRuntimeEnvironment, args: DeployProxyOptions[], - opts: DeployProxyOptions + opts: DeployProxyOptions, ): Promise<{ mockedBeaconAddress: string; data: string }> { const chainId = await getChainId(deployer.zkWallet.provider); @@ -61,7 +59,7 @@ export async function getMockedBeaconData( let mockImplAddress: string; let data: string; - if (chainId == 270) { + if (chainId === 270) { const adminFactory = await getAdminFactory(hre, deployer.zkWallet); mockImplAddress = await deployProxyAdminLocally(adminFactory); mockedBeaconAddress = (await deployBeaconLocally(mockImplAddress, hre, deployer.zkWallet)).address; @@ -72,7 +70,7 @@ export async function getMockedBeaconData( data = getInitializerData(new ethers.Interface(mockArtifact.abi), args, opts.initializer); } - return { mockedBeaconAddress: mockedBeaconAddress, data }; + return { mockedBeaconAddress, data }; } export function makeEstimateGasBeacon(hre: HardhatRuntimeEnvironment): EstimateGasFunction { @@ -81,25 +79,25 @@ export function makeEstimateGasBeacon(hre: HardhatRuntimeEnvironment): EstimateG artifact: ZkSyncArtifact, args: DeployProxyOptions[] = [], opts: DeployProxyOptions = {}, - quiet = false + quiet = false, ) { - let beaconGasCost:bigint = 0n; + let beaconGasCost: bigint = 0n; const { mockedBeaconAddress } = await getMockedBeaconData(deployer, hre, args, opts); - const implGasCost:bigint = await deployer.estimateDeployFee(artifact, []); + const implGasCost: bigint = await deployer.estimateDeployFee(artifact, []); if (!quiet) { console.info( chalk.cyan( `Deployment of the implementation contract is estimated to cost: ${convertGasPriceToEth( - implGasCost - )} ETH` - ) + implGasCost, + )} ETH`, + ), ); } const upgradableBeaconPath = (await hre.artifacts.getArtifactPaths()).find((x) => - x.includes(path.sep + UPGRADABLE_BEACON_JSON) + x.includes(path.sep + UPGRADABLE_BEACON_JSON), ); assert(upgradableBeaconPath, 'Upgradable beacon artifact not found'); const upgradeableBeaconContract = await import(upgradableBeaconPath); @@ -110,17 +108,17 @@ export function makeEstimateGasBeacon(hre: HardhatRuntimeEnvironment): EstimateG console.info( chalk.cyan( `Deployment of the upgradeable beacon contract is estimated to cost: ${convertGasPriceToEth( - beaconGasCost - )} ETH` - ) + beaconGasCost, + )} ETH`, + ), ); console.info( - chalk.cyan(`Total estimated gas cost: ${convertGasPriceToEth(implGasCost + beaconGasCost)} ETH`) + chalk.cyan(`Total estimated gas cost: ${convertGasPriceToEth(implGasCost + beaconGasCost)} ETH`), ); } } catch (error: any) { throw new ZkSyncUpgradablePluginError(`Error estimating gas cost: ${error.reason}`); } - return beaconGasCost+implGasCost; + return beaconGasCost + implGasCost; }; } diff --git a/packages/hardhat-zksync-upgradable/src/gas-estimation/estimate-gas-proxy.ts b/packages/hardhat-zksync-upgradable/src/gas-estimation/estimate-gas-proxy.ts index 2b173fb2a..58ed3c225 100644 --- a/packages/hardhat-zksync-upgradable/src/gas-estimation/estimate-gas-proxy.ts +++ b/packages/hardhat-zksync-upgradable/src/gas-estimation/estimate-gas-proxy.ts @@ -16,16 +16,13 @@ import { ERC1967_PROXY_JSON, TUP_JSON, defaultImplAddresses } from '../constants import { getAdminArtifact, getAdminFactory } from '../proxy-deployment/deploy-proxy-admin'; import { deploy } from '../proxy-deployment/deploy'; - -export interface EstimateProxyGasFunction { - ( - deployer: Deployer, - artifact: ZkSyncArtifact, - args?: DeployProxyOptions[], - opts?: DeployProxyOptions, - quiet?: boolean - ): Promise<bigint>; -} +export type EstimateProxyGasFunction = ( + deployer: Deployer, + artifact: ZkSyncArtifact, + args?: DeployProxyOptions[], + opts?: DeployProxyOptions, + quiet?: boolean, +) => Promise<bigint>; interface GasCosts { adminGasCost: bigint; proxyGasCost: bigint; @@ -42,10 +39,10 @@ export function makeEstimateGasProxy(hre: HardhatRuntimeEnvironment): EstimatePr artifact: ZkSyncArtifact, args: DeployProxyOptions[] = [], opts: DeployProxyOptions = {}, - quiet: boolean = false - ):Promise<bigint> { + quiet: boolean = false, + ): Promise<bigint> { let data; - let totalGasCost:bigint; + let totalGasCost: bigint; let mockImplAddress: string; const mockArtifact = await getAdminArtifact(hre); @@ -56,25 +53,25 @@ export function makeEstimateGasProxy(hre: HardhatRuntimeEnvironment): EstimatePr if (!chainId) { throw new ZkSyncUpgradablePluginError(`Chain id ${chainId} is not supported!`); } - if (chainId == 270) { + if (chainId === 270) { const adminFactory = await getAdminFactory(hre, deployer.zkWallet); mockImplAddress = await deployProxyAdminLocally(adminFactory); - //videti tamo u openzeppeln + // videti tamo u openzeppeln data = getInitializerData(adminFactory.interface, args, opts.initializer); } else { mockImplAddress = defaultImplAddresses[chainId].contractAddress; data = getInitializerData(new ethers.Interface(mockArtifact.abi), args, opts.initializer); } - const implGasCost:bigint = await deployer.estimateDeployFee(artifact, []); + const implGasCost: bigint = await deployer.estimateDeployFee(artifact, []); if (!quiet) { console.info( chalk.cyan( `Deployment of the implementation contract is estimated to cost: ${convertGasPriceToEth( - implGasCost - )} ETH` - ) + implGasCost, + )} ETH`, + ), ); } @@ -84,7 +81,7 @@ export function makeEstimateGasProxy(hre: HardhatRuntimeEnvironment): EstimatePr } case 'uups': { - const uupsGasCost:bigint = await estimateGasUUPS(hre, deployer, mockImplAddress, data, quiet); + const uupsGasCost: bigint = await estimateGasUUPS(hre, deployer, mockImplAddress, data, quiet); totalGasCost = implGasCost + uupsGasCost; break; } @@ -95,7 +92,7 @@ export function makeEstimateGasProxy(hre: HardhatRuntimeEnvironment): EstimatePr deployer, mockImplAddress, data, - quiet + quiet, ); totalGasCost = implGasCost + adminGasCost + proxyGasCost; break; @@ -108,7 +105,7 @@ export function makeEstimateGasProxy(hre: HardhatRuntimeEnvironment): EstimatePr if (!quiet) { console.info( - chalk.cyan(`Total deployment cost is estimated to cost: ${convertGasPriceToEth(totalGasCost)} ETH`) + chalk.cyan(`Total deployment cost is estimated to cost: ${convertGasPriceToEth(totalGasCost)} ETH`), ); } return totalGasCost; @@ -120,23 +117,23 @@ async function estimateGasUUPS( deployer: Deployer, mockImplAddress: string, data: string, - quiet: boolean = false + quiet: boolean = false, ): Promise<bigint> { const ERC1967ProxyPath = (await hre.artifacts.getArtifactPaths()).find((x) => - x.includes(path.sep + ERC1967_PROXY_JSON) + x.includes(path.sep + ERC1967_PROXY_JSON), ); assert(ERC1967ProxyPath, 'ERC1967Proxy artifact not found'); const proxyContract = await import(ERC1967ProxyPath); try { - const uupsGasCost:bigint = await deployer.estimateDeployFee(proxyContract, [mockImplAddress, data]); + const uupsGasCost: bigint = await deployer.estimateDeployFee(proxyContract, [mockImplAddress, data]); if (!quiet) { console.info( chalk.cyan( `Deployment of the UUPS proxy contract is estimated to cost: ${convertGasPriceToEth( - uupsGasCost - )} ETH` - ) + uupsGasCost, + )} ETH`, + ), ); } return uupsGasCost; @@ -150,7 +147,7 @@ async function estimateGasTransparent( deployer: Deployer, mockImplAddress: string, data: string, - quiet: boolean = false + quiet: boolean = false, ): Promise<GasCosts> { const adminArtifact = await getAdminArtifact(hre); const adminGasCost = await deployer.estimateDeployFee(adminArtifact, []); @@ -158,8 +155,10 @@ async function estimateGasTransparent( if (!quiet) { console.info( chalk.cyan( - `Deployment of the admin proxy contract is estimated to cost: ${convertGasPriceToEth(adminGasCost)} ETH` - ) + `Deployment of the admin proxy contract is estimated to cost: ${convertGasPriceToEth( + adminGasCost, + )} ETH`, + ), ); } @@ -173,14 +172,14 @@ async function estimateGasTransparent( console.info( chalk.cyan( `Deployment of the transparent proxy contract is estimated to cost: ${convertGasPriceToEth( - proxyGasCost - )} ETH` - ) + proxyGasCost, + )} ETH`, + ), ); } } catch (error: any) { throw new ZkSyncUpgradablePluginError(`Error estimating gas cost: ${error.reason}`); } - return { adminGasCost: adminGasCost, proxyGasCost: proxyGasCost }; + return { adminGasCost, proxyGasCost }; } diff --git a/packages/hardhat-zksync-upgradable/src/index.ts b/packages/hardhat-zksync-upgradable/src/index.ts index d9322390f..b8507c101 100644 --- a/packages/hardhat-zksync-upgradable/src/index.ts +++ b/packages/hardhat-zksync-upgradable/src/index.ts @@ -1,6 +1,6 @@ import '@matterlabs/hardhat-zksync-solc'; import '@matterlabs/hardhat-zksync-deploy'; -import './type-extensions'; +import './type-extensions'; import { extendEnvironment, subtask } from 'hardhat/internal/core/config/config-env'; @@ -80,4 +80,4 @@ subtask('verify:verify').setAction(async (args, hre, runSuper) => { return await verify(args, hre, runSuper); }); -export * from './type-extensions'; \ No newline at end of file +export * from './type-extensions'; diff --git a/packages/hardhat-zksync-upgradable/src/interfaces.ts b/packages/hardhat-zksync-upgradable/src/interfaces.ts index 1ee5cbf30..e1cc0b4c5 100644 --- a/packages/hardhat-zksync-upgradable/src/interfaces.ts +++ b/packages/hardhat-zksync-upgradable/src/interfaces.ts @@ -15,7 +15,7 @@ import { EstimateProxyGasFunction } from './gas-estimation/estimate-gas-proxy'; export type ValidateImplementationFunction = ( ImplFactory: zk.ContractFactory, - opts?: ValidateImplementationOptions + opts?: ValidateImplementationOptions, ) => Promise<void>; export interface HardhatUpgrades { diff --git a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-beacon-proxy.ts b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-beacon-proxy.ts index 526baf987..fa50c03be 100644 --- a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-beacon-proxy.ts +++ b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-beacon-proxy.ts @@ -10,16 +10,15 @@ import { import * as zk from 'zksync-ethers'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; -import { ContractAddressOrInstance, getContractAddress } from '../utils/utils-general'; +import chalk from 'chalk'; +import assert from 'assert'; +import path from 'path'; +import { ContractAddressOrInstance, getContractAddress, getInitializerData } from '../utils/utils-general'; import { DeployBeaconProxyOptions } from '../utils/options'; -import { getInitializerData } from '../utils/utils-general'; -import { deploy, DeployTransaction } from './deploy'; import { BEACON_PROXY_JSON } from '../constants'; import { ZkSyncUpgradablePluginError } from '../errors'; import { Manifest } from '../core/manifest'; -import chalk from 'chalk'; -import assert from 'assert'; -import path from 'path'; +import { deploy, DeployTransaction } from './deploy'; export interface DeployBeaconProxyFunction { ( @@ -28,7 +27,7 @@ export interface DeployBeaconProxyFunction { artifact: ZkSyncArtifact, args?: unknown[], opts?: DeployBeaconProxyOptions, - quiet?: boolean + quiet?: boolean, ): Promise<zk.Contract>; ( wallet: zk.Wallet, @@ -38,21 +37,21 @@ export interface DeployBeaconProxyFunction { ): Promise<zk.Contract>; } -export function makeDeployBeaconProxy(hre: HardhatRuntimeEnvironment): DeployBeaconProxyFunction { +export function makeDeployBeaconProxy(hre: HardhatRuntimeEnvironment): DeployBeaconProxyFunction { return async function deployBeaconProxy( wallet: zk.Wallet, beacon: ContractAddressOrInstance, artifact: ZkSyncArtifact, args: unknown[] | DeployBeaconProxyOptions = [], opts: DeployBeaconProxyOptions = {}, - quiet: boolean = false + quiet: boolean = false, ) { - const attachTo = new zk.ContractFactory<any[],zk.Contract>(artifact.abi, artifact.bytecode, wallet); + const attachTo = new zk.ContractFactory<any[], zk.Contract>(artifact.abi, artifact.bytecode, wallet); if (!(attachTo instanceof zk.ContractFactory)) { throw new ZkSyncUpgradablePluginError( `attachTo must specify a contract factory\n` + - `Include the contract factory for the beacon's current implementation in the attachTo parameter` + `Include the contract factory for the beacon's current implementation in the attachTo parameter`, ); } if (!Array.isArray(args)) { @@ -80,27 +79,27 @@ export function makeDeployBeaconProxy(hre: HardhatRuntimeEnvironment): DeployBea chalk.yellow(`A proxy admin was previously deployed on this network`, [ `This is not natively used with the current kind of proxy ('beacon').`, `Changes to the admin will have no effect on this new proxy.`, - ]) + ]), ); } } const beaconProxyPath = (await hre.artifacts.getArtifactPaths()).find((artifactPath) => - artifactPath.includes(path.sep + BEACON_PROXY_JSON) + artifactPath.includes(path.sep + BEACON_PROXY_JSON), ); assert(beaconProxyPath, 'Beacon proxy artifact not found'); const beaconProxyContract = await import(beaconProxyPath); - const beaconProxyFactory = new zk.ContractFactory<any[],zk.Contract>( + const beaconProxyFactory = new zk.ContractFactory<any[], zk.Contract>( beaconProxyContract.abi, beaconProxyContract.bytecode, - wallet + wallet, ); - const proxyDeployment: Required<ProxyDeployment & DeployTransaction> = Object.assign( - { kind: opts.kind }, - await deploy(beaconProxyFactory, beaconAddress, data) - ); + const proxyDeployment: Required<ProxyDeployment & DeployTransaction> = { + kind: opts.kind, + ...(await deploy(beaconProxyFactory, beaconAddress, data)), + }; if (!quiet) { console.info(chalk.green('Beacon proxy deployed at: ', proxyDeployment.address)); } diff --git a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-beacon.ts b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-beacon.ts index 9774b0f3f..1c71edba4 100644 --- a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-beacon.ts +++ b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-beacon.ts @@ -4,26 +4,29 @@ import { Deployment } from '@openzeppelin/upgrades-core'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; -import { DeployBeaconOptions } from '../utils/options'; -import { deploy, DeployTransaction } from './deploy'; import * as zk from 'zksync-ethers'; -import { deployBeaconImpl } from './deploy-impl'; -import { UPGRADABLE_BEACON_JSON } from '../constants'; import chalk from 'chalk'; import assert from 'assert'; import path from 'path'; +import { UPGRADABLE_BEACON_JSON } from '../constants'; +import { DeployBeaconOptions } from '../utils/options'; +import { deployBeaconImpl } from './deploy-impl'; +import { deploy, DeployTransaction } from './deploy'; -export interface DeployBeaconFunction { - (wallet: zk.Wallet, artifact: ZkSyncArtifact, opts?: DeployBeaconOptions, quiet?: boolean): Promise<zk.Contract>; -} +export type DeployBeaconFunction = ( + wallet: zk.Wallet, + artifact: ZkSyncArtifact, + opts?: DeployBeaconOptions, + quiet?: boolean, +) => Promise<zk.Contract>; export function makeDeployBeacon(hre: HardhatRuntimeEnvironment): DeployBeaconFunction { return async function deployBeacon( wallet: zk.Wallet, artifact: ZkSyncArtifact, opts: DeployBeaconOptions = {}, - quiet: boolean = false - ):Promise<zk.Contract> { + quiet: boolean = false, + ): Promise<zk.Contract> { const beaconImplFactory = new zk.ContractFactory(artifact.abi, artifact.bytecode, wallet); opts.provider = wallet.provider; @@ -33,15 +36,15 @@ export function makeDeployBeacon(hre: HardhatRuntimeEnvironment): DeployBeaconFu } const upgradableBeaconPath = (await hre.artifacts.getArtifactPaths()).find((x) => - x.includes(path.sep + UPGRADABLE_BEACON_JSON) + x.includes(path.sep + UPGRADABLE_BEACON_JSON), ); assert(upgradableBeaconPath, 'Upgradable beacon artifact not found'); const upgradeableBeaconContract = await import(upgradableBeaconPath); - const upgradeableBeaconFactory = new zk.ContractFactory<any[],zk.Contract>( + const upgradeableBeaconFactory = new zk.ContractFactory<any[], zk.Contract>( upgradeableBeaconContract.abi, upgradeableBeaconContract.bytecode, - wallet + wallet, ); const beaconDeployment: Required<Deployment & DeployTransaction> = await deploy(upgradeableBeaconFactory, impl); if (!quiet) { diff --git a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-impl.ts b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-impl.ts index cd7368d6f..d953f94d8 100644 --- a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-impl.ts +++ b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-impl.ts @@ -2,7 +2,6 @@ import { getStorageLayout, getVersion, StorageLayout, - UpgradesError, ValidationDataCurrent, Version, } from '@openzeppelin/upgrades-core'; @@ -10,15 +9,15 @@ import { import * as zk from 'zksync-ethers'; import type { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { TransactionResponse } from 'zksync-ethers/src/types'; import { DeployProxyOptions, UpgradeOptions, withDefaults } from '../utils/options'; import { validateBeaconImpl, validateProxyImpl } from '../validations/validate-impl'; import { readValidations } from '../validations/validations'; -import { deploy } from './deploy'; import { fetchOrDeployGetDeployment } from '../core/impl-store'; -import { TransactionResponse } from 'zksync-ethers/src/types'; import { FORMAT_TYPE_MINIMAL, IMPL_CONTRACT_NOT_DEPLOYED_ERROR } from '../constants'; import { ZkSyncUpgradablePluginError } from '../errors'; +import { deploy } from './deploy'; export interface DeployData { provider: zk.Provider; @@ -33,7 +32,7 @@ export interface DeployData { export async function getDeployData( hre: HardhatRuntimeEnvironment, contractFactory: zk.ContractFactory, - opts: UpgradeOptions + opts: UpgradeOptions, ): Promise<DeployData> { const provider = opts.provider; const validations = await readValidations(hre); @@ -49,7 +48,7 @@ export async function deployProxyImpl( hre: HardhatRuntimeEnvironment, contractFactory: zk.ContractFactory, opts: DeployProxyOptions, - proxyAddress?: string + proxyAddress?: string, ): Promise<any> { const deployData = await getDeployData(hre, contractFactory, opts); await validateProxyImpl(deployData, opts, proxyAddress); @@ -60,7 +59,7 @@ async function deployImpl( hre: HardhatRuntimeEnvironment, deployData: DeployData, factory: zk.ContractFactory, - opts: UpgradeOptions + opts: UpgradeOptions, ): Promise<any> { const layout = deployData.layout; @@ -68,20 +67,20 @@ async function deployImpl( deployData.version, deployData.provider, async () => { - const abi = factory.interface.format(FORMAT_TYPE_MINIMAL==='minimal') as string[]; + const abi = factory.interface.format(FORMAT_TYPE_MINIMAL === 'minimal') as string[]; const attemptDeploy = async () => { if (opts.useDeployedImplementation) { throw new ZkSyncUpgradablePluginError(IMPL_CONTRACT_NOT_DEPLOYED_ERROR); } else { - const deployed = await deploy(factory,...deployData.fullOpts.constructorArgs); + const deployed = await deploy(factory, ...deployData.fullOpts.constructorArgs); return deployed; } }; - const deployment = Object.assign({ abi }, await attemptDeploy()); - return { ...deployment, layout }; + const deploymentInternal = { abi, ...(await attemptDeploy()) }; + return { ...deploymentInternal, layout }; }, - opts + opts, ); return { impl: deployment.address, kind: opts.kind }; @@ -96,7 +95,7 @@ export async function deployBeaconImpl( hre: HardhatRuntimeEnvironment, factory: zk.ContractFactory, opts: UpgradeOptions, - beaconAddress?: string + beaconAddress?: string, ): Promise<DeployedBeaconImpl> { const deployData = await getDeployData(hre, factory, opts); await validateBeaconImpl(deployData, opts, beaconAddress); diff --git a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-proxy-admin.ts b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-proxy-admin.ts index e2dfab77c..01f0697db 100644 --- a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-proxy-admin.ts +++ b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-proxy-admin.ts @@ -1,16 +1,14 @@ import type { HardhatRuntimeEnvironment } from 'hardhat/types'; import * as zk from 'zksync-ethers'; import path from 'path'; +import assert from 'assert'; +import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; import { DeployProxyAdminOptions } from '../utils/options'; -import { deploy } from './deploy'; import { PROXY_ADMIN_JSON } from '../constants'; import { fetchOrDeployAdmin } from '../core/impl-store'; -import assert from 'assert'; -import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; +import { deploy } from './deploy'; -export interface DeployAdminFunction { -(wallet?: zk.Wallet, opts?: DeployProxyAdminOptions): Promise<string>; -} +export type DeployAdminFunction = (wallet?: zk.Wallet, opts?: DeployProxyAdminOptions) => Promise<string>; export function makeDeployProxyAdmin(hre: HardhatRuntimeEnvironment): any { return async function deployProxyAdmin(wallet: zk.Wallet, opts: DeployProxyAdminOptions = {}) { @@ -21,13 +19,16 @@ export function makeDeployProxyAdmin(hre: HardhatRuntimeEnvironment): any { export async function getAdminArtifact(hre: HardhatRuntimeEnvironment): Promise<ZkSyncArtifact> { const proxyAdminPath = (await hre.artifacts.getArtifactPaths()).find((x) => - x.includes(path.sep + PROXY_ADMIN_JSON) + x.includes(path.sep + PROXY_ADMIN_JSON), ); assert(proxyAdminPath, 'Proxy admin artifact not found'); return await import(proxyAdminPath); } -export async function getAdminFactory(hre: HardhatRuntimeEnvironment, wallet: zk.Wallet): Promise<zk.ContractFactory<any[],zk.Contract>> { +export async function getAdminFactory( + hre: HardhatRuntimeEnvironment, + wallet: zk.Wallet, +): Promise<zk.ContractFactory<any[], zk.Contract>> { const proxyAdminContract = await getAdminArtifact(hre); - return new zk.ContractFactory<any[],zk.Contract>(proxyAdminContract.abi, proxyAdminContract.bytecode, wallet); + return new zk.ContractFactory<any[], zk.Contract>(proxyAdminContract.abi, proxyAdminContract.bytecode, wallet); } diff --git a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-proxy.ts b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-proxy.ts index 5669f643e..bd97e734a 100644 --- a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-proxy.ts +++ b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-proxy.ts @@ -6,18 +6,22 @@ import { BeaconProxyUnsupportedError } from '@openzeppelin/upgrades-core'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; -import { DeployTransaction, deploy } from './deploy'; -import { deployProxyImpl } from './deploy-impl'; +import assert from 'assert'; import { getInitializerData } from '../utils/utils-general'; import { ERC1967_PROXY_JSON, TUP_JSON } from '../constants'; import { Manifest, ProxyDeployment } from '../core/manifest'; import { DeployProxyOptions } from '../utils/options'; import { ZkSyncUpgradablePluginError } from '../errors'; -import assert from 'assert'; +import { deployProxyImpl } from './deploy-impl'; +import { DeployTransaction, deploy } from './deploy'; -export interface DeployFunction { - (wallet: zk.Wallet, artifact: ZkSyncArtifact, args?: unknown[], opts?: DeployProxyOptions, quiet?: boolean): Promise<zk.Contract>; -} +export type DeployFunction = ( + wallet: zk.Wallet, + artifact: ZkSyncArtifact, + args?: unknown[], + opts?: DeployProxyOptions, + quiet?: boolean, +) => Promise<zk.Contract>; export function makeDeployProxy(hre: HardhatRuntimeEnvironment): DeployFunction { return async function deployProxy( @@ -25,8 +29,8 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment): DeployFunction artifact, args: unknown[] | DeployProxyOptions = [], opts: DeployProxyOptions = {}, - quiet: boolean = false - ):Promise<zk.Contract> { + quiet: boolean = false, + ): Promise<zk.Contract> { if (!Array.isArray(args)) { opts = args; args = []; @@ -34,10 +38,10 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment): DeployFunction opts.provider = wallet.provider; const manifest = await Manifest.forNetwork(wallet.provider); - const factory = new zk.ContractFactory<any[],zk.Contract>(artifact.abi, artifact.bytecode, wallet); + const factory = new zk.ContractFactory<any[], zk.Contract>(artifact.abi, artifact.bytecode, wallet); const { impl, kind } = await deployProxyImpl(hre, factory, opts); if (!quiet) { - console.info(chalk.green('Implementation contract was deployed to ' + impl)); + console.info(chalk.green(`Implementation contract was deployed to ${impl}`)); } const data = getInitializerData(factory.interface, args, opts.initializer); @@ -47,8 +51,8 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment): DeployFunction if (!quiet) { console.info( chalk.yellow( - `A proxy admin was previously deployed on this network\nThis is not natively used with the current kind of proxy ('uups')\nChanges to the admin will have no effect on this new proxy` - ) + `A proxy admin was previously deployed on this network\nThis is not natively used with the current kind of proxy ('uups')\nChanges to the admin will have no effect on this new proxy`, + ), ); } } @@ -62,12 +66,12 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment): DeployFunction case 'uups': { const ERC1967ProxyPath = (await hre.artifacts.getArtifactPaths()).find((x) => - x.includes(path.sep + ERC1967_PROXY_JSON) + x.includes(path.sep + ERC1967_PROXY_JSON), ); assert(ERC1967ProxyPath, 'ERC1967Proxy artifact not found'); const proxyContract = await import(ERC1967ProxyPath); const proxyFactory = new zk.ContractFactory(proxyContract.abi, proxyContract.bytecode, wallet); - proxyDeployment = Object.assign({ kind }, await deploy(proxyFactory, impl, data)); + proxyDeployment = { kind, ...(await deploy(proxyFactory, impl, data)) }; if (!quiet) { console.info(chalk.green(`UUPS proxy was deployed to ${proxyDeployment.address}`)); @@ -78,7 +82,7 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment): DeployFunction case 'transparent': { const adminAddress = await hre.zkUpgrades.deployProxyAdmin(wallet, {}); if (!quiet) { - console.info(chalk.green('Admin was deployed to ' + adminAddress)); + console.info(chalk.green(`Admin was deployed to ${adminAddress}`)); } const TUPPath = (await hre.artifacts.getArtifactPaths()).find((x) => x.includes(path.sep + TUP_JSON)); @@ -86,7 +90,7 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment): DeployFunction const TUPContract = await import(TUPPath); const TUPFactory = new zk.ContractFactory(TUPContract.abi, TUPContract.bytecode, wallet); - proxyDeployment = Object.assign({ kind }, await deploy(TUPFactory, impl, adminAddress, data)); + proxyDeployment = { kind, ...(await deploy(TUPFactory, impl, adminAddress, data)) }; if (!quiet) { console.info(chalk.green(`Transparent proxy was deployed to ${proxyDeployment.address}`)); diff --git a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy.ts b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy.ts index b9a77b274..28ce203dc 100644 --- a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy.ts +++ b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy.ts @@ -14,7 +14,9 @@ export async function deploy( const deploymentTransaction = contractInstance.deploymentTransaction(); const deployTransaction = contractInstance.deploymentTransaction(); - if (deployTransaction === null) {throw new Error('Broken invariant: deploymentTransaction is null');} + if (deployTransaction === null) { + throw new Error('Broken invariant: deploymentTransaction is null'); + } const address = await contractInstance.getAddress(); const txHash = deploymentTransaction!.hash; diff --git a/packages/hardhat-zksync-upgradable/src/proxy-upgrade/upgrade-beacon.ts b/packages/hardhat-zksync-upgradable/src/proxy-upgrade/upgrade-beacon.ts index cae91d4db..2bf452472 100644 --- a/packages/hardhat-zksync-upgradable/src/proxy-upgrade/upgrade-beacon.ts +++ b/packages/hardhat-zksync-upgradable/src/proxy-upgrade/upgrade-beacon.ts @@ -3,20 +3,19 @@ import * as zk from 'zksync-ethers'; import path from 'path'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; -import { ContractAddressOrInstance } from '../utils/utils-general'; +import chalk from 'chalk'; +import assert from 'assert'; +import { ContractAddressOrInstance, getContractAddress } from '../utils/utils-general'; import { UpgradeBeaconOptions } from '../utils/options'; import { deployBeaconImpl } from '../proxy-deployment/deploy-impl'; -import { getContractAddress } from '../utils/utils-general'; import { UPGRADABLE_BEACON_JSON } from '../constants'; -import chalk from 'chalk'; -import assert from 'assert'; export type UpgradeBeaconFunction = ( wallet: zk.Wallet, beacon: ContractAddressOrInstance, artifact: ZkSyncArtifact, opts?: UpgradeBeaconOptions, - quiet?: boolean + quiet?: boolean, ) => Promise<zk.Contract>; export function makeUpgradeBeacon(hre: HardhatRuntimeEnvironment): UpgradeBeaconFunction { @@ -25,12 +24,12 @@ export function makeUpgradeBeacon(hre: HardhatRuntimeEnvironment): UpgradeBeacon beaconImplementation, newImplementationArtifact, opts: UpgradeBeaconOptions = {}, - quiet: boolean = false + quiet: boolean = false, ) { const factory = new zk.ContractFactory( newImplementationArtifact.abi, newImplementationArtifact.bytecode, - wallet + wallet, ); opts.provider = wallet.provider; @@ -41,15 +40,15 @@ export function makeUpgradeBeacon(hre: HardhatRuntimeEnvironment): UpgradeBeacon } const upgradableBeaconPath = (await hre.artifacts.getArtifactPaths()).find((x) => - x.includes(path.sep + UPGRADABLE_BEACON_JSON) + x.includes(path.sep + UPGRADABLE_BEACON_JSON), ); assert(upgradableBeaconPath, 'Upgradable beacon artifact not found'); const upgradeableBeaconContract = await import(upgradableBeaconPath); - const upgradeableBeaconFactory = new zk.ContractFactory<any[],zk.Contract>( + const upgradeableBeaconFactory = new zk.ContractFactory<any[], zk.Contract>( upgradeableBeaconContract.abi, upgradeableBeaconContract.bytecode, - wallet + wallet, ); const beaconContract = upgradeableBeaconFactory.attach(beaconImplementationAddress); diff --git a/packages/hardhat-zksync-upgradable/src/proxy-upgrade/upgrade-proxy.ts b/packages/hardhat-zksync-upgradable/src/proxy-upgrade/upgrade-proxy.ts index bf4fa40d2..7657a7f05 100644 --- a/packages/hardhat-zksync-upgradable/src/proxy-upgrade/upgrade-proxy.ts +++ b/packages/hardhat-zksync-upgradable/src/proxy-upgrade/upgrade-proxy.ts @@ -6,21 +6,21 @@ import { getAdminAddress, getCode, isEmptySlot } from '@openzeppelin/upgrades-co import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types'; +import chalk from 'chalk'; +import assert from 'assert'; import { ContractAddressOrInstance } from '../interfaces'; import { UpgradeProxyOptions } from '../utils/options'; import { getContractAddress } from '../utils/utils-general'; import { deployProxyImpl } from '../proxy-deployment/deploy-impl'; import { Manifest } from '../core/manifest'; import { ITUP_JSON, PROXY_ADMIN_JSON } from '../constants'; -import chalk from 'chalk'; -import assert from 'assert'; export type UpgradeFunction = ( wallet: zk.Wallet, proxy: ContractAddressOrInstance, artifact: ZkSyncArtifact, opts?: UpgradeProxyOptions, - quiet?: boolean + quiet?: boolean, ) => Promise<zk.Contract>; export function makeUpgradeProxy(hre: HardhatRuntimeEnvironment): UpgradeFunction { @@ -29,15 +29,15 @@ export function makeUpgradeProxy(hre: HardhatRuntimeEnvironment): UpgradeFunctio proxy, newImplementationArtifact, opts: UpgradeProxyOptions = {}, - quiet: boolean = false - ):Promise<zk.Contract> { + quiet: boolean = false, + ): Promise<zk.Contract> { const proxyAddress = await getContractAddress(proxy); opts.provider = wallet.provider; - const newImplementationFactory = new zk.ContractFactory<any[],zk.Contract>( + const newImplementationFactory = new zk.ContractFactory<any[], zk.Contract>( newImplementationArtifact.abi, newImplementationArtifact.bytecode, - wallet + wallet, ); const { impl: nextImpl } = await deployProxyImpl(hre, newImplementationFactory, opts, proxyAddress); @@ -68,10 +68,10 @@ export function makeUpgradeProxy(hre: HardhatRuntimeEnvironment): UpgradeFunctio assert(TUPPath, 'Transparent upgradeable proxy artifact not found'); const transparentUpgradeableProxyContract = await import(TUPPath); - const transparentUpgradeableProxyFactory = new zk.ContractFactory<any[],zk.Contract>( + const transparentUpgradeableProxyFactory = new zk.ContractFactory<any[], zk.Contract>( transparentUpgradeableProxyContract.abi, transparentUpgradeableProxyContract.bytecode, - wallet + wallet, ); const proxy = transparentUpgradeableProxyFactory.attach(proxyAddress); @@ -80,21 +80,21 @@ export function makeUpgradeProxy(hre: HardhatRuntimeEnvironment): UpgradeFunctio const manifest = await Manifest.forNetwork(provider); const proxyAdminPath = (await hre.artifacts.getArtifactPaths()).find((x) => - x.includes(path.sep + PROXY_ADMIN_JSON) + x.includes(path.sep + PROXY_ADMIN_JSON), ); assert(proxyAdminPath, 'Proxy admin artifact not found'); const proxyAdminContract = await import(proxyAdminPath); - const proxyAdminFactory = new zk.ContractFactory<any[],zk.Contract>( + const proxyAdminFactory = new zk.ContractFactory<any[], zk.Contract>( proxyAdminContract.abi, proxyAdminContract.bytecode, - wallet + wallet, ); const admin = proxyAdminFactory.attach(adminAddress); const manifestAdmin = await manifest.getAdmin(); - if (await admin.getAddress() !== manifestAdmin?.address) { + if ((await admin.getAddress()) !== manifestAdmin?.address) { throw new Error('Proxy admin is not the one registered in the network manifest'); } diff --git a/packages/hardhat-zksync-upgradable/src/utils/options.ts b/packages/hardhat-zksync-upgradable/src/utils/options.ts index 28a872028..e852f6e35 100644 --- a/packages/hardhat-zksync-upgradable/src/utils/options.ts +++ b/packages/hardhat-zksync-upgradable/src/utils/options.ts @@ -29,9 +29,9 @@ export function withDefaults(opts: UpgradeOptions = {}): Required<UpgradeOptions }; } -type Initializer = { +interface Initializer { initializer?: string | false; -}; +} export type DeployBeaconProxyOptions = ProxyKindOption & Initializer; export type DeployBeaconOptions = StandaloneOptions; diff --git a/packages/hardhat-zksync-upgradable/src/utils/utils-general.ts b/packages/hardhat-zksync-upgradable/src/utils/utils-general.ts index c9ad44206..b1ccdf914 100644 --- a/packages/hardhat-zksync-upgradable/src/utils/utils-general.ts +++ b/packages/hardhat-zksync-upgradable/src/utils/utils-general.ts @@ -1,12 +1,11 @@ -import { MaybeSolcOutput } from '../interfaces'; -import { TOPIC_LOGS_NOT_FOUND_ERROR } from '../constants'; import { keccak256 } from 'ethereumjs-util'; -import { Interface } from 'ethers'; +import { Interface, ethers } from 'ethers'; import chalk from 'chalk'; import * as zk from 'zksync-ethers'; import { SolcConfig } from 'hardhat/types'; -import { ethers } from 'ethers'; import { UpgradesError } from '@openzeppelin/upgrades-core'; +import { TOPIC_LOGS_NOT_FOUND_ERROR } from '../constants'; +import { MaybeSolcOutput } from '../interfaces'; export type ContractAddressOrInstance = string | { getAddress(): Promise<string> }; @@ -71,8 +70,8 @@ export async function getContractCreationTxHash(provider: zk.Provider, address: const params = { fromBlock: 0, toBlock: 'latest', - address: address, - topics: ['0x' + keccak256(Buffer.from(topic)).toString('hex')], + address, + topics: [`0x${keccak256(Buffer.from(topic)).toString('hex')}`], }; const logs = await provider.getLogs(params); @@ -95,25 +94,26 @@ export function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> { export function mapValues<V, W>(obj: Record<string, V>, fn: (value: V) => W): Record<string, W> { const res: Partial<Record<string, W>> = {}; for (const k in obj) { + if (!k) continue; res[k] = fn(obj[k]); } return res as Record<string, W>; } export function isFullZkSolcOutput(output: MaybeSolcOutput | undefined): boolean { - if (output?.contracts == undefined || output?.sources == undefined) { + if (output?.contracts === undefined || output?.sources === undefined) { return false; } for (const fileName of Object.keys(output.contracts)) { const file = output.contracts[fileName]; - if (file == undefined) { + if (file === undefined) { return false; } } for (const file of Object.values(output.sources)) { - if (file?.ast == undefined || file?.id == undefined) { + if (file?.ast === undefined || file?.id === undefined) { return false; } } @@ -126,7 +126,7 @@ export function isNullish(value: unknown): value is null | undefined { } export function extendCompilerOutputSelection(compiler: SolcConfig) { - if (!compiler.settings.outputSelection['*']['*'].find((o: string) => o == 'storageLayout')) { + if (!compiler.settings.outputSelection['*']['*'].find((o: string) => o === 'storageLayout')) { compiler.settings.outputSelection['*']['*'].push('storageLayout'); } } diff --git a/packages/hardhat-zksync-upgradable/src/validations/validate-impl.ts b/packages/hardhat-zksync-upgradable/src/validations/validate-impl.ts index a22978e28..20502c24d 100644 --- a/packages/hardhat-zksync-upgradable/src/validations/validate-impl.ts +++ b/packages/hardhat-zksync-upgradable/src/validations/validate-impl.ts @@ -29,7 +29,7 @@ async function processBeaconImpl(deployData: DeployData, beaconAddress: string) export async function validateImpl( deployData: DeployData, opts: ValidationOptions, - currentImplAddress?: string + currentImplAddress?: string, ): Promise<void> { assertUpgradeSafe(deployData.validations, deployData.version, deployData.fullOpts); @@ -45,7 +45,7 @@ export async function validateImpl( export async function validateProxyImpl( deployData: DeployData, opts: ValidationOptions, - proxyAddress?: string + proxyAddress?: string, ): Promise<void> { const currentImplAddress = await processProxyImpl(deployData, proxyAddress, opts); return validateImpl(deployData, opts, currentImplAddress); @@ -54,7 +54,7 @@ export async function validateProxyImpl( export async function validateBeaconImpl( deployData: DeployData, opts: ValidationOptions, - beaconAddress?: string + beaconAddress?: string, ): Promise<void> { const currentImplAddress = beaconAddress !== undefined ? await processBeaconImpl(deployData, beaconAddress) : undefined; diff --git a/packages/hardhat-zksync-upgradable/src/validations/validate-implementation.ts b/packages/hardhat-zksync-upgradable/src/validations/validate-implementation.ts index 1cfe993de..8ab7221c3 100644 --- a/packages/hardhat-zksync-upgradable/src/validations/validate-implementation.ts +++ b/packages/hardhat-zksync-upgradable/src/validations/validate-implementation.ts @@ -1,8 +1,8 @@ import { HardhatRuntimeEnvironment } from 'hardhat/types'; -import { validateImpl } from './validate-impl'; import { getDeployData } from '../proxy-deployment/deploy-impl'; import { ValidateImplementationFunction } from '../interfaces'; import { ValidateImplementationOptions } from '../utils/options'; +import { validateImpl } from './validate-impl'; export function makeValidateImplementation(hre: HardhatRuntimeEnvironment): ValidateImplementationFunction { return async function validateImplementation(ImplFactory, opts: ValidateImplementationOptions = {}) { diff --git a/packages/hardhat-zksync-upgradable/src/validations/validations.ts b/packages/hardhat-zksync-upgradable/src/validations/validations.ts index 95dd0ef95..fee237723 100644 --- a/packages/hardhat-zksync-upgradable/src/validations/validations.ts +++ b/packages/hardhat-zksync-upgradable/src/validations/validations.ts @@ -36,7 +36,7 @@ export async function writeValidations(hre: HardhatRuntimeEnvironment, newRunDat export async function readValidations( hre: HardhatRuntimeEnvironment, - acquireLock = true + acquireLock = true, ): Promise<ValidationDataCurrent> { const cachePath = getValidationsCachePath(hre); let releaseLock; diff --git a/packages/hardhat-zksync-upgradable/src/verify/verify-beacon.ts b/packages/hardhat-zksync-upgradable/src/verify/verify-beacon.ts index c947d4d21..7bcb48638 100644 --- a/packages/hardhat-zksync-upgradable/src/verify/verify-beacon.ts +++ b/packages/hardhat-zksync-upgradable/src/verify/verify-beacon.ts @@ -1,18 +1,18 @@ import { getBeaconAddress, getImplementationAddressFromBeacon } from '@openzeppelin/upgrades-core'; import { HardhatRuntimeEnvironment, RunSuperFunction } from 'hardhat/types'; -import { verifyWithArtifact } from './verify-proxy'; -import { verifyImplementation } from './verify-impl'; -import { verifiableContracts } from '../constants'; import { Provider } from 'zksync-ethers'; import chalk from 'chalk'; +import { verifiableContracts } from '../constants'; +import { verifyWithArtifact } from './verify-proxy'; +import { verifyImplementation } from './verify-impl'; export async function fullVerifyBeacon( hre: HardhatRuntimeEnvironment, beaconAddress: any, hardhatVerify: (address: string) => Promise<any>, runSuper: RunSuperFunction<any>, - quiet: boolean = false + quiet: boolean = false, ) { const networkConfig: any = hre.network.config; const provider = new Provider(networkConfig.url); @@ -34,7 +34,7 @@ export async function fullVerifyBeaconProxy( proxyAddress: any, hardhatVerify: (address: string) => Promise<any>, runSuper: RunSuperFunction<any>, - quiet: boolean = false + quiet: boolean = false, ) { const networkConfig: any = hre.network.config; const provider = new Provider(networkConfig.url); diff --git a/packages/hardhat-zksync-upgradable/src/verify/verify-impl.ts b/packages/hardhat-zksync-upgradable/src/verify/verify-impl.ts index 45e3124a3..3ebec2e36 100644 --- a/packages/hardhat-zksync-upgradable/src/verify/verify-impl.ts +++ b/packages/hardhat-zksync-upgradable/src/verify/verify-impl.ts @@ -3,7 +3,7 @@ import chalk from 'chalk'; export async function verifyImplementation( hardhatVerify: (address: string) => Promise<any>, implAddress: string, - quiet: boolean = false + quiet: boolean = false, ) { try { if (!quiet) { diff --git a/packages/hardhat-zksync-upgradable/src/verify/verify-proxy.ts b/packages/hardhat-zksync-upgradable/src/verify/verify-proxy.ts index 3fd3ee46e..8c3fc2e8d 100644 --- a/packages/hardhat-zksync-upgradable/src/verify/verify-proxy.ts +++ b/packages/hardhat-zksync-upgradable/src/verify/verify-proxy.ts @@ -3,14 +3,14 @@ import { getTransactionByHash, isTransparentOrUUPSProxy, isBeacon, isBeaconProxy import { HardhatRuntimeEnvironment, RunSuperFunction } from 'hardhat/types'; import chalk from 'chalk'; -import { fullVerifyBeacon, fullVerifyBeaconProxy } from './verify-beacon'; -import { fullVerifyTransparentOrUUPS } from './verify-transparent-uups'; +import * as zk from 'zksync-ethers'; +import { ethers } from 'ethers'; import { EVENT_NOT_FOUND_ERROR, UPGRADE_VERIFY_ERROR } from '../constants'; import { getContractCreationTxHash } from '../utils/utils-general'; import { VerifiableContractInfo } from '../interfaces'; import { ZkSyncUpgradablePluginError } from '../errors'; -import * as zk from 'zksync-ethers'; -import { ethers } from 'ethers'; +import { fullVerifyTransparentOrUUPS } from './verify-transparent-uups'; +import { fullVerifyBeacon, fullVerifyBeaconProxy } from './verify-beacon'; /** * Overrides verify's plugin `verify:verify` subtask to fully verify a proxy or beacon. @@ -52,8 +52,7 @@ export async function verify(args: any, hre: HardhatRuntimeEnvironment, runSuper * @returns the xHash for the first event found */ async function searchEvent(provider: zk.Provider, address: string, possibleContractInfo: VerifiableContractInfo[]) { - for (let i = 0; i < possibleContractInfo.length; i++) { - const contractInfo = possibleContractInfo[i]; + for (const contractInfo of possibleContractInfo) { const txHash = await getContractCreationTxHash(provider, address, contractInfo.event); if (txHash !== undefined) { return { contractInfo, txHash }; @@ -70,7 +69,7 @@ export async function verifyWithArtifact( hre: HardhatRuntimeEnvironment, address: string, possibleContractInfo: VerifiableContractInfo[], - runSuper: RunSuperFunction<any> + runSuper: RunSuperFunction<any>, ) { try { await attemptVerifyWithCreationEvent(hre, address, possibleContractInfo, runSuper); @@ -101,7 +100,7 @@ async function attemptVerifyWithCreationEvent( hre: HardhatRuntimeEnvironment, address: string, possibleContractInfo: VerifiableContractInfo[], - runSuper: RunSuperFunction<any> + runSuper: RunSuperFunction<any>, ) { const networkConfig: any = hre.network.config; const provider = new zk.Provider(networkConfig.url); @@ -111,14 +110,14 @@ async function attemptVerifyWithCreationEvent( const tx = await getTransactionByHash(provider, txHash); if (tx === null) { throw new ZkSyncUpgradablePluginError( - `The transaction hash ${txHash} from the contract's logs was not found on the network` + `The transaction hash ${txHash} from the contract's logs was not found on the network`, ); } const decodedInputData = ethers.AbiCoder.defaultAbiCoder().decode( ['bytes32', 'bytes32', 'bytes'], - ethers.dataSlice(tx.input, 4) + ethers.dataSlice(tx.input, 4), ); const constructorArgs = decodedInputData[2]; - await runSuper({ address: address, constructorArguments: constructorArgs, libraries: {} }); + await runSuper({ address, constructorArguments: constructorArgs, libraries: {} }); } diff --git a/packages/hardhat-zksync-upgradable/src/verify/verify-transparent-uups.ts b/packages/hardhat-zksync-upgradable/src/verify/verify-transparent-uups.ts index 98acc1769..2be5a3132 100644 --- a/packages/hardhat-zksync-upgradable/src/verify/verify-transparent-uups.ts +++ b/packages/hardhat-zksync-upgradable/src/verify/verify-transparent-uups.ts @@ -1,12 +1,12 @@ import { HardhatRuntimeEnvironment, RunSuperFunction } from 'hardhat/types'; -import { verifyWithArtifact } from './verify-proxy'; -import { verifyImplementation } from './verify-impl'; -import { verifiableContracts } from '../constants'; import { getImplementationAddress, isEmptySlot, getAdminAddress } from '@openzeppelin/upgrades-core'; import * as zk from 'zksync-ethers'; import chalk from 'chalk'; +import { verifiableContracts } from '../constants'; +import { verifyImplementation } from './verify-impl'; +import { verifyWithArtifact } from './verify-proxy'; /** * Fully verifies all contracts related to the given transparent or UUPS proxy address: implementation, admin (if any), and proxy. @@ -27,7 +27,7 @@ export async function fullVerifyTransparentOrUUPS( proxyAddress: any, hardhatVerify: (address: string) => Promise<any>, runSuper: RunSuperFunction<any>, - quiet: boolean = false + quiet: boolean = false, ) { const networkConfig: any = hre.network.config; const provider = new zk.Provider(networkConfig.url); @@ -59,7 +59,7 @@ export async function fullVerifyTransparentOrUUPS( hre, proxyAddress, [verifiableContracts.transparentUpgradeableProxy, verifiableContracts.erc1967proxy], - runSuper + runSuper, ); } } diff --git a/packages/hardhat-zksync-upgradable/test/constants.ts b/packages/hardhat-zksync-upgradable/test/constants.ts index b6ed7f6a1..d738253eb 100644 --- a/packages/hardhat-zksync-upgradable/test/constants.ts +++ b/packages/hardhat-zksync-upgradable/test/constants.ts @@ -8,7 +8,8 @@ export const standaloneValidationErrors = { USE_OF_DELEGATE_CALL: 'Use of delegatecall is not allowed', STATE_VARIABLE_ASSIGNMENT: 'Variable `value` is assigned an initial value', STATE_VARIABLE_IMMUTABLE: 'Variable `secondValue` is immutable', - MISSING_PUBLIC_UPGRADE_TO:'Implementation is missing a public `upgradeTo(address)` or `upgradeToAndCall(address,bytes)` function' , + MISSING_PUBLIC_UPGRADE_TO: + 'Implementation is missing a public `upgradeTo(address)` or `upgradeToAndCall(address,bytes)` function', }; export const storageLayoutErrors = { diff --git a/packages/hardhat-zksync-upgradable/test/tests.ts b/packages/hardhat-zksync-upgradable/test/tests.ts index 2cd45154d..1c627bd9d 100644 --- a/packages/hardhat-zksync-upgradable/test/tests.ts +++ b/packages/hardhat-zksync-upgradable/test/tests.ts @@ -1,23 +1,21 @@ import assert from 'assert'; -import { useEnvironment } from './helpers'; -import { ContractFactory, Provider,Contract } from 'zksync-ethers'; +import { ContractFactory, Provider, Contract } from 'zksync-ethers'; import chalk from 'chalk'; import fsExtra from 'fs-extra'; import path from 'path'; -import { TEST_ADDRESS, authorizationErrors, standaloneValidationErrors, storageLayoutErrors } from './constants'; +import { getAdminAddress } from '@openzeppelin/upgrades-core'; +import { describe } from 'node:test'; import { LOCAL_SETUP_ZKSYNC_NETWORK, MANIFEST_DEFAULT_DIR } from '../src/constants'; import { getAdminFactory } from '../src/proxy-deployment/deploy-proxy-admin'; import { deploy } from '../src/proxy-deployment/deploy'; import { getManifestAdmin } from '../src/admin'; +import { TEST_ADDRESS, authorizationErrors, standaloneValidationErrors, storageLayoutErrors } from './constants'; import richWallets from './rich-wallets.json'; -import { getAdminAddress } from '@openzeppelin/upgrades-core'; -import { describe } from 'node:test'; - -import '../src/type-extensions' - +import { useEnvironment } from './helpers'; +import '../src/type-extensions'; describe('Upgradable plugin tests', async function () { describe('Test transparent upgradable proxy deployment and upgrade functionalities', async function () { @@ -28,7 +26,7 @@ describe('Upgradable plugin tests', async function () { before('Deploy Box proxy and contract implementation', async function () { const contractName = 'Box'; - console.info(chalk.yellow('Deploying ' + contractName + ' transparent proxy...')); + console.info(chalk.yellow(`Deploying ${contractName} transparent proxy...`)); const boxArtifact = await this.deployer.loadArtifact(contractName); boxProxy = await this.env.zkUpgrades.deployProxy(this.deployer.zkWallet, boxArtifact, [42], { @@ -43,25 +41,29 @@ describe('Upgradable plugin tests', async function () { const value = await boxProxy.retrieve(); assert.equal(value, 42n); }); - + it('Should update proxy contract implementation', async function () { const contractName = 'BoxV2'; - console.info(chalk.yellow('Upgrading Box to ' + contractName + '...')); + console.info(chalk.yellow(`Upgrading Box to ${contractName}...`)); const BoxV2 = await this.deployer.loadArtifact(contractName); - const box2 = await this.env.zkUpgrades.upgradeProxy(this.deployer.zkWallet, await boxProxy.getAddress(), BoxV2); + const box2 = await this.env.zkUpgrades.upgradeProxy( + this.deployer.zkWallet, + await boxProxy.getAddress(), + BoxV2, + ); await box2.waitForDeployment(); - //give it some time to upgrade + // give it some time to upgrade await new Promise((resolve) => setTimeout(resolve, 1500)); box2.connect(this.deployer.zkWallet); const value = await box2.retrieve(); assert.equal(value, 'V2: 42'); }); - + it('Should fail to deploy proxy for implementation that is not upgrade safe', async function () { const contractName = 'BoxUpgradeUnsafe'; - console.info(chalk.yellow('Deploying ' + contractName + '...')); + console.info(chalk.yellow(`Deploying ${contractName}...`)); const contract = await this.deployer.loadArtifact(contractName); @@ -75,7 +77,7 @@ describe('Upgradable plugin tests', async function () { error.message.includes(standaloneValidationErrors.STATE_VARIABLE_ASSIGNMENT) && error.message.includes(standaloneValidationErrors.STATE_VARIABLE_IMMUTABLE) ); - } + }, ); }); }); @@ -92,9 +94,9 @@ describe('Upgradable plugin tests', async function () { boxUupsProxy = await this.env.zkUpgrades.deployProxy(this.deployer.zkWallet, boxArtifact, [42], { initializer: 'initialize', }); - await boxUupsProxy.waitForDeployment() + await boxUupsProxy.waitForDeployment(); - console.info(chalk.yellow('Deploying ' + contractName2 + ' uups proxy...')); + console.info(chalk.yellow(`Deploying ${contractName2} uups proxy...`)); const boxPublicArtifact = await this.deployer.loadArtifact(contractName2); boxUupsPublicProxy = await this.env.zkUpgrades.deployProxy( @@ -103,14 +105,14 @@ describe('Upgradable plugin tests', async function () { [42], { initializer: 'initialize', - } + }, ); await boxUupsPublicProxy.waitForDeployment(); }); it('Should deploy uups proxy and contract implementation', async function () { await boxUupsProxy.waitForDeployment(); - //await new Promise((resolve) => setTimeout(resolve, 1500)); + // await new Promise((resolve) => setTimeout(resolve, 1500)); boxUupsProxy.connect(this.deployer.zkWallet); const value = await boxUupsProxy.retrieve(); @@ -120,15 +122,18 @@ describe('Upgradable plugin tests', async function () { it('Should update proxy contract implementation', async function () { const contractName = 'BoxUupsV2'; - console.info(chalk.yellow('Upgrading BoxUups to ' + contractName + '...')); + console.info(chalk.yellow(`Upgrading BoxUups to ${contractName}...`)); const BoxV2 = await this.deployer.loadArtifact(contractName); - const box2 = await this.env.zkUpgrades.upgradeProxy(this.deployer.zkWallet, await boxUupsProxy.getAddress(), BoxV2); + const box2 = await this.env.zkUpgrades.upgradeProxy( + this.deployer.zkWallet, + await boxUupsProxy.getAddress(), + BoxV2, + ); await new Promise((resolve) => setTimeout(resolve, 1500)); box2.connect(this.deployer.zkWallet); - const value = await box2.retrieve(); - assert.equal(value, 'V2: 42'); - + const value = await box2.retrieve(); + assert.equal(value, 'V2: 42'); }); it('Should throw an owner access update proxy error', async function () { @@ -138,29 +143,33 @@ describe('Upgradable plugin tests', async function () { await assert.rejects( this.env.zkUpgrades.upgradeProxy(this.zkWallet2, await boxUupsProxy.getAddress(), BoxV2), - (error: any) => error.message.includes(authorizationErrors.CALLER_NOT_OWNER) + (error: any) => error.message.includes(authorizationErrors.CALLER_NOT_OWNER), ); }); it('Should allow other wallets to upgrade the contract', async function () { const contractName = 'BoxUupsV2'; - console.info(chalk.yellow('Upgrading BoxUupsPublic to ' + contractName + '...')); + console.info(chalk.yellow(`Upgrading BoxUupsPublic to ${contractName}...`)); const BoxV2 = await this.deployer.loadArtifact(contractName); - const box2 = await this.env.zkUpgrades.upgradeProxy(this.zkWallet2, await boxUupsPublicProxy.getAddress(), BoxV2); + const box2 = await this.env.zkUpgrades.upgradeProxy( + this.zkWallet2, + await boxUupsPublicProxy.getAddress(), + BoxV2, + ); await box2.waitForDeployment(); console.info(chalk.green('Successfully upgraded BoxUupsPublic to BoxUupsV2')); - //give it some time to upgrade + // give it some time to upgrade await new Promise((resolve) => setTimeout(resolve, 1500)); box2.connect(this.deployer.zkWallet); - const value = await box2.retrieve(); - assert.equal(value, 'V2: 42'); + const value = await box2.retrieve(); + assert.equal(value, 'V2: 42'); }); it('Should throw a missing public upgradeTo error when deploying', async function () { const contractName = 'BoxUupsMissingUpgradeTo'; - console.info(chalk.yellow('Deploying ' + contractName + '...')); + console.info(chalk.yellow(`Deploying ${contractName}...`)); const contract = await this.deployer.loadArtifact(contractName); @@ -169,13 +178,13 @@ describe('Upgradable plugin tests', async function () { initializer: 'initialize', kind: 'uups', }), - (error: any) => error.message.includes(standaloneValidationErrors.MISSING_PUBLIC_UPGRADE_TO) + (error: any) => error.message.includes(standaloneValidationErrors.MISSING_PUBLIC_UPGRADE_TO), ); }); it('Should throw a missing public upgradeTo error when upgrading', async function () { const contractName = 'BoxUupsMissingUpgradeTo'; - console.info(chalk.yellow('Upgrading BoxUups to ' + contractName + '...')); + console.info(chalk.yellow(`Upgrading BoxUups to ${contractName}...`)); const boxV2 = await this.deployer.loadArtifact(contractName); @@ -183,10 +192,11 @@ describe('Upgradable plugin tests', async function () { this.env.zkUpgrades.upgradeProxy(this.deployer.zkWallet, await boxUupsProxy.getAddress(), boxV2, { kind: 'uups', }), - (error:any) => - error.message.includes(standaloneValidationErrors.MISSING_PUBLIC_UPGRADE_TO) && - error.message.includes('is not upgrade safe'), - 'Expected error not thrown for missing upgradeTo function.' ); + (error: any) => + error.message.includes(standaloneValidationErrors.MISSING_PUBLIC_UPGRADE_TO) && + error.message.includes('is not upgrade safe'), + 'Expected error not thrown for missing upgradeTo function.', + ); }); }); describe.skip('Test beacon proxy deployment and upgrade functionalities', async function () { @@ -198,16 +208,16 @@ describe('Upgradable plugin tests', async function () { before('Deploy beacon proxy and contract implementation', async function () { const contractName = 'Box'; - console.info(chalk.yellow('Deploying ' + contractName + ' beacon proxy...')); + console.info(chalk.yellow(`Deploying ${contractName} beacon proxy...`)); const contract = await this.deployer.loadArtifact(contractName); beaconImplementation = await this.env.zkUpgrades.deployBeacon(this.deployer.zkWallet, contract); beaconProxy = await this.env.zkUpgrades.deployBeaconProxy( this.deployer.zkWallet, - //@ts-ignore + // @ts-ignore beaconImplementation, contract, - [42] + [42], ); await beaconProxy.waitForDeployment(); }); @@ -215,8 +225,8 @@ describe('Upgradable plugin tests', async function () { it('Should deploy beacon proxy and contract implementation', async function () { await beaconProxy.waitForDeployment(); beaconProxy.connect(this.deployer.zkWallet); - const value = await beaconProxy.retrieve(); - assert.equal(value, 42n); + const value = await beaconProxy.retrieve(); + assert.equal(value, 42n); }); it('Should upgrade beacon proxy contract implementation', async function () { @@ -226,19 +236,19 @@ describe('Upgradable plugin tests', async function () { await this.env.zkUpgrades.upgradeBeacon( this.deployer.zkWallet, await beaconImplementation.getAddress(), - boxV2Implementation + boxV2Implementation, ); - const attachTo = new ContractFactory<any[],Contract>( + const attachTo = new ContractFactory<any[], Contract>( boxV2Implementation.abi, boxV2Implementation.bytecode, this.deployer.zkWallet, - this.deployer.deploymentType + this.deployer.deploymentType, ); const boxV2 = attachTo.attach(await beaconProxy.getAddress()); boxV2.connect(this.deployer.zkWallet); - //give it some time to upgrade + // give it some time to upgrade await new Promise((resolve) => setTimeout(resolve, 2000)); const value = await boxV2.retrieve(); assert.equal(value, 'V2: 42'); @@ -246,12 +256,12 @@ describe('Upgradable plugin tests', async function () { }); describe.skip('Test upgradable contracts admin functionalities', async function () { useEnvironment('admin'); - + const provider = new Provider(LOCAL_SETUP_ZKSYNC_NETWORK); it('Should return the smart contract admin instance', async function () { const contractName = 'Box'; - console.info(chalk.yellow('Deploying ' + contractName + '...')); + console.info(chalk.yellow(`Deploying ${contractName}...`)); const contract = await this.deployer.loadArtifact(contractName); const deployedContract = await this.env.zkUpgrades.deployProxy(this.deployer.zkWallet, contract, [42], { @@ -270,13 +280,13 @@ describe('Upgradable plugin tests', async function () { await fsExtra.remove(path.join(this.env.config.paths.root, MANIFEST_DEFAULT_DIR)); await assert.rejects(this.env.zkUpgrades.admin.getInstance(this.deployer.zkWallet), (error: any) => - error.message.includes(authorizationErrors.NO_PROXY_ADMIN_FOUND) + error.message.includes(authorizationErrors.NO_PROXY_ADMIN_FOUND), ); }); it('Should change the admin of an upgradable smart contract', async function () { const contractName = 'Box'; - console.info(chalk.yellow('Deploying ' + contractName + '...')); + console.info(chalk.yellow(`Deploying ${contractName}...`)); const contract = await this.deployer.loadArtifact(contractName); const deployedContract = await this.env.zkUpgrades.deployProxy(this.deployer.zkWallet, contract, [42], { @@ -287,21 +297,21 @@ describe('Upgradable plugin tests', async function () { await this.env.zkUpgrades.admin.changeProxyAdmin( await deployedContract.getAddress(), richWallets[1].address, - this.deployer.zkWallet + this.deployer.zkWallet, ); // wait 2 seconds before the next call await new Promise((resolve) => setTimeout(resolve, 2000)); const updatedAdminInstance = await getAdminAddress(provider, await deployedContract.getAddress()); - assert(updatedAdminInstance !==await adminInstance.getAddress()); + assert(updatedAdminInstance !== (await adminInstance.getAddress())); assert(updatedAdminInstance, richWallets[1].address); }); it('Should fail to upgrade the proxy without admin', async function () { const contractName = 'Box'; const contractV2Name = 'BoxV2'; - console.info(chalk.yellow('Deploying ' + contractName + '...')); + console.info(chalk.yellow(`Deploying ${contractName}...`)); const contract = await this.deployer.loadArtifact(contractName); const contractV2 = await this.deployer.loadArtifact(contractV2Name); @@ -314,22 +324,26 @@ describe('Upgradable plugin tests', async function () { await this.env.zkUpgrades.admin.changeProxyAdmin( await deployedContract.getAddress(), - await newAdminContract.address, - this.deployer.zkWallet + newAdminContract.address, + this.deployer.zkWallet, ); // wait 2 seconds before the next call await new Promise((resolve) => setTimeout(resolve, 2000)); await assert.rejects( - this.env.zkUpgrades.upgradeProxy(this.deployer.zkWallet, await deployedContract.getAddress(), contractV2), - (error: any) => error.message.includes(authorizationErrors.WRONG_PROXY_ADMIN) + this.env.zkUpgrades.upgradeProxy( + this.deployer.zkWallet, + await deployedContract.getAddress(), + contractV2, + ), + (error: any) => error.message.includes(authorizationErrors.WRONG_PROXY_ADMIN), ); }); it('Should fail to change the admin - wrong signer', async function () { const contractName = 'Box'; - console.info(chalk.yellow('Deploying ' + contractName + '...')); + console.info(chalk.yellow(`Deploying ${contractName}...`)); const contract = await this.deployer.loadArtifact(contractName); const deployedContract = await this.env.zkUpgrades.deployProxy(this.deployer.zkWallet, contract, [42], { @@ -340,15 +354,15 @@ describe('Upgradable plugin tests', async function () { this.env.zkUpgrades.admin.changeProxyAdmin( await deployedContract.getAddress(), richWallets[1].address, - this.zkWallet2 + this.zkWallet2, ), - (error: any) => error.message.includes(authorizationErrors.CALLER_NOT_OWNER) + (error: any) => error.message.includes(authorizationErrors.CALLER_NOT_OWNER), ); }); it('Should change the owner of the upgradable smart contract', async function () { const contractName = 'Box'; - console.info(chalk.yellow('Deploying ' + contractName + '...')); + console.info(chalk.yellow(`Deploying ${contractName}...`)); const contract = await this.deployer.loadArtifact(contractName); await this.env.zkUpgrades.deployProxy(this.deployer.zkWallet, contract, [42], { @@ -365,7 +379,7 @@ describe('Upgradable plugin tests', async function () { it('Should fail to change the owner - wrong signer', async function () { const contractName = 'Box'; - console.info(chalk.yellow('Deploying ' + contractName + '...')); + console.info(chalk.yellow(`Deploying ${contractName}...`)); const contract = await this.deployer.loadArtifact(contractName); await this.env.zkUpgrades.deployProxy(this.deployer.zkWallet, contract, [42], { @@ -374,7 +388,7 @@ describe('Upgradable plugin tests', async function () { await assert.rejects( this.env.zkUpgrades.admin.transferProxyAdminOwnership(TEST_ADDRESS, this.zkWallet2), - (error: any) => error.message.includes(authorizationErrors.CALLER_NOT_OWNER) + (error: any) => error.message.includes(authorizationErrors.CALLER_NOT_OWNER), ); }); @@ -384,7 +398,7 @@ describe('Upgradable plugin tests', async function () { await assert.rejects( this.env.zkUpgrades.admin.transferProxyAdminOwnership(TEST_ADDRESS, this.zkWallet2), - (error: any) => error.message.includes(authorizationErrors.NO_PROXY_ADMIN_FOUND) + (error: any) => error.message.includes(authorizationErrors.NO_PROXY_ADMIN_FOUND), ); }); }); @@ -398,14 +412,14 @@ describe('Upgradable plugin tests', async function () { const contractName1 = 'Box'; const contractName2 = 'BoxWithStorageGap'; - console.info(chalk.yellow('Deploying ' + contractName1 + '...')); + console.info(chalk.yellow(`Deploying ${contractName1}...`)); const boxArtifact = await this.deployer.loadArtifact(contractName1); boxProxy = await this.env.zkUpgrades.deployProxy(this.deployer.zkWallet, boxArtifact, [42], { initializer: 'store', }); - console.info(chalk.yellow('Deploying ' + contractName2 + '...')); + console.info(chalk.yellow(`Deploying ${contractName2}...`)); const boxWithStorageGapArtifact = await this.deployer.loadArtifact(contractName2); boxWithStorageGap = await this.env.zkUpgrades.deployProxy( @@ -414,7 +428,7 @@ describe('Upgradable plugin tests', async function () { [42], { initializer: 'store', - } + }, ); // wait 2 seconds before the next call @@ -423,13 +437,13 @@ describe('Upgradable plugin tests', async function () { it('Should upgrade Box proxy to compatible implementation', async function () { const contractName = 'BoxV2'; - console.info(chalk.yellow('Upgrading Box to ' + contractName + '...')); + console.info(chalk.yellow(`Upgrading Box to ${contractName}...`)); const boxV2Artifact = await this.deployer.loadArtifact(contractName); const boxV2 = await this.env.zkUpgrades.upgradeProxy( this.deployer.zkWallet, await boxProxy.getAddress(), - boxV2Artifact + boxV2Artifact, ); await new Promise((resolve) => setTimeout(resolve, 1500)); @@ -440,7 +454,7 @@ describe('Upgradable plugin tests', async function () { it('Should fail do upgrade proxy to the implementation that violates storage layout restrictions', async function () { const contractName = 'BoxV2Invalid'; - console.info(chalk.yellow('Upgrading Box to ' + contractName + '...')); + console.info(chalk.yellow(`Upgrading Box to ${contractName}...`)); const boxV2 = await this.deployer.loadArtifact(contractName); @@ -451,33 +465,37 @@ describe('Upgradable plugin tests', async function () { error.message.includes(storageLayoutErrors.INSERTED_VARIABLE) && error.message.includes(storageLayoutErrors.CHANGE_VARIABLE_TYPE) && error.message.includes(storageLayoutErrors.RENAMED_VARIABLE) && - error.message.includes(storageLayoutErrors.DELETED_VARIABLE) + error.message.includes(storageLayoutErrors.DELETED_VARIABLE), ); }); it('Should fail do upgrade proxy to the implementation that does not reduce storage gap properly', async function () { const contractName = 'BoxWithStorageGapV2Invalid'; - console.info(chalk.yellow('Upgrading BoxWithStorageGap to ' + contractName + '...')); + console.info(chalk.yellow(`Upgrading BoxWithStorageGap to ${contractName}...`)); const boxV2Artifact = await this.deployer.loadArtifact(contractName); await assert.rejects( - this.env.zkUpgrades.upgradeProxy(this.deployer.zkWallet, await boxWithStorageGap.getAddress(), boxV2Artifact), + this.env.zkUpgrades.upgradeProxy( + this.deployer.zkWallet, + await boxWithStorageGap.getAddress(), + boxV2Artifact, + ), (error: any) => error.message.includes(storageLayoutErrors.INCOMPATIBLE_STORAGE_LAYOUT) && - error.message.includes(storageLayoutErrors.STORAGE_GAP_SIZE) + error.message.includes(storageLayoutErrors.STORAGE_GAP_SIZE), ); }); it('Should upgrade BoxWithStorageGap proxy to compatible implementation', async function () { const contractName = 'BoxWithStorageGapV2'; - console.info(chalk.yellow('Upgrading BoxWithStorageGap to ' + contractName + '...')); + console.info(chalk.yellow(`Upgrading BoxWithStorageGap to ${contractName}...`)); const boxV2Artifact = await this.deployer.loadArtifact(contractName); const boxV2 = await this.env.zkUpgrades.upgradeProxy( this.deployer.zkWallet, await boxWithStorageGap.getAddress(), - boxV2Artifact + boxV2Artifact, ); await new Promise((resolve) => setTimeout(resolve, 1500)); @@ -492,7 +510,7 @@ describe('Upgradable plugin tests', async function () { it('Should estimate gas for transparent proxy deployment on local setup', async function () { const contractName = 'Box'; - console.info(chalk.yellow('Estimating gas for ' + contractName + '...')); + console.info(chalk.yellow(`Estimating gas for ${contractName}...`)); const contract = await this.deployer.loadArtifact(contractName); const balance = await this.deployer.zkWallet.provider.getBalance(this.deployer.zkWallet.address); @@ -506,40 +524,40 @@ describe('Upgradable plugin tests', async function () { }); await box.waitForDeployment(); - const newBalance:bigint = await this.deployer.zkWallet.provider.getBalance(this.deployer.zkWallet.address); + const newBalance: bigint = await this.deployer.zkWallet.provider.getBalance(this.deployer.zkWallet.address); - if (gasEstimation>MINIMUM_GAS_LIMIT) assert(gasEstimation > balance-newBalance); + if (gasEstimation > MINIMUM_GAS_LIMIT) assert(gasEstimation > balance - newBalance); }); it('Should estimate gas for uups proxy deployment on local setup', async function () { const contractName = 'BoxUups'; - console.info(chalk.yellow('Estimating gas for ' + contractName + '...')); + console.info(chalk.yellow(`Estimating gas for ${contractName}...`)); const contract = await this.deployer.loadArtifact(contractName); const balance = await this.deployer.zkWallet.provider.getBalance(this.deployer.zkWallet.address); - const gasEstimation:bigint = await this.env.zkUpgrades.estimation.estimateGasProxy( + const gasEstimation: bigint = await this.env.zkUpgrades.estimation.estimateGasProxy( this.deployer, contract, [], { kind: 'uups' }, - true + true, ); - let box = await this.env.zkUpgrades.deployProxy(this.deployer.zkWallet, contract, [42], { + const box = await this.env.zkUpgrades.deployProxy(this.deployer.zkWallet, contract, [42], { initializer: 'initialize', kind: 'uups', }); - await box.waitForDeployment(); + await box.waitForDeployment(); const newBalance = await this.deployer.zkWallet.provider.getBalance(this.deployer.zkWallet.address); - if (gasEstimation> MINIMUM_GAS_LIMIT) assert(gasEstimation > balance - newBalance); + if (gasEstimation > MINIMUM_GAS_LIMIT) assert(gasEstimation > balance - newBalance); }); it('Should estimate gas for beacon contract deployment on local setup', async function () { const contractName = 'Box'; - console.info(chalk.yellow('Estimating gas for ' + contractName + '...')); + console.info(chalk.yellow(`Estimating gas for ${contractName}...`)); const contract = await this.deployer.loadArtifact(contractName); const balance = await this.deployer.zkWallet.provider.getBalance(this.deployer.zkWallet.address); @@ -551,43 +569,43 @@ describe('Upgradable plugin tests', async function () { const newBalance = await this.deployer.zkWallet.provider.getBalance(this.deployer.zkWallet.address); - if (gasEstimation>(MINIMUM_GAS_LIMIT)) assert(gasEstimation > balance-newBalance); + if (gasEstimation > MINIMUM_GAS_LIMIT) assert(gasEstimation > balance - newBalance); }); it('Should estimate gas for beacon proxy deployment on local setup', async function () { const contractName = 'Box'; - console.info(chalk.yellow('Estimating gas for ' + contractName + '...')); + console.info(chalk.yellow(`Estimating gas for ${contractName}...`)); const contract = await this.deployer.loadArtifact(contractName); const balance = await this.deployer.zkWallet.provider.getBalance(this.deployer.zkWallet.address); - const gasEstimationBeacon:bigint = await this.env.zkUpgrades.estimation.estimateGasBeacon( + const gasEstimationBeacon: bigint = await this.env.zkUpgrades.estimation.estimateGasBeacon( this.deployer, contract, [], {}, - true + true, ); - const gasEstimationProxy:bigint = await this.env.zkUpgrades.estimation.estimateGasBeaconProxy( + const gasEstimationProxy: bigint = await this.env.zkUpgrades.estimation.estimateGasBeaconProxy( this.deployer, [], {}, - true + true, ); - const gasEstimation = gasEstimationBeacon+gasEstimationProxy; + const gasEstimation = gasEstimationBeacon + gasEstimationProxy; const boxBeacon = await this.env.zkUpgrades.deployBeacon(this.deployer.zkWallet, contract); const boxProxy = await this.env.zkUpgrades.deployBeaconProxy( this.deployer.zkWallet, await boxBeacon.getAddress(), contract, - [42] + [42], ); await boxProxy.waitForDeployment(); const newBalance = await this.deployer.zkWallet.provider.getBalance(this.deployer.zkWallet.address); - if (gasEstimation>MINIMUM_GAS_LIMIT) assert(gasEstimation >balance-newBalance); + if (gasEstimation > MINIMUM_GAS_LIMIT) assert(gasEstimation > balance - newBalance); }); }); }); diff --git a/packages/hardhat-zksync-verify-vyper/.eslintrc.js b/packages/hardhat-zksync-verify-vyper/.eslintrc.js index 56ca0b4bd..b0d17045a 100644 --- a/packages/hardhat-zksync-verify-vyper/.eslintrc.js +++ b/packages/hardhat-zksync-verify-vyper/.eslintrc.js @@ -4,4 +4,18 @@ module.exports = { project: `${__dirname}/eslint-tsconfig.json`, sourceType: "module", }, + rules: { + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "property", + "format": ["camelCase", "PascalCase", "snake_case"], + "leadingUnderscore": "allow", + "filter": { + "regex": "^[a-zA-Z0-9-_]+$", + "match": false + } + }, + ], + }, }; diff --git a/packages/hardhat-zksync-verify-vyper/README.md b/packages/hardhat-zksync-verify-vyper/README.md index 57ceabcf2..320e55a12 100644 --- a/packages/hardhat-zksync-verify-vyper/README.md +++ b/packages/hardhat-zksync-verify-vyper/README.md @@ -1,5 +1,118 @@ -# hardhat-zksync-verify-vyper +# hardhat-zksync-verify-vyper 🚀 [Hardhat](https://hardhat.org/) plugin that adds zkSync-specific capabilities to verify vyper smart contracts. -Check out the documentation page for the [zksync verify vyper plugin](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-verify-vyper.html) for more detailed explanation on how to use hardhat-zksync-verify-vyper. +![Era Logo](https://github.com/matter-labs/era-contracts/raw/main/eraLogo.svg) + +## 📥 Installation + +To install **hardhat-zksync-verify-vyper** plugin, run: + +`npm install -D @matterlabs/hardhat-zksync-verify-vyper` + +or + +`yarn add -D @matterlabs/hardhat-zksync-verify-vyper` + +## 🔩 Configuration + +Import the plugin in the hardhat.config.ts file: + +`import "@matterlabs/hardhat-zksync-verify-vyper";` + +Add the verifyURL property to the zkSync Era network in the hardhat.config.ts file as shown below: + +``` +networks: { + sepolia: { + url: "https://sepolia.infura.io/v3/<API_KEY>" // The Ethereum Web3 RPC URL (optional). + }, + zkTestnet: { + url: "https://sepolia.era.zksync.dev", // The testnet RPC URL of zkSync Era network. + ethNetwork: "sepolia", // The Ethereum Web3 RPC URL, or the identifier of the network (e.g. `mainnet` or `sepolia`) + zksync: true, + // Verification endpoint for Sepolia + verifyURL: 'https://explorer.sepolia.era.zksync.dev/contract_verification' + } +}, +// defaultNetwork: "zkTestnet", // optional (if not set, use '--network zkTestnet') +``` + +| 🔧 properties | 📄 Description | +|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------| +| zkTestnet | Arbitrary zkSync Era network name. You can select this as the default network using the defaultNetwork property. | +| url | Field is required for all zkSync Era and Ethereum networks used by this plugin. For zkSync network, set it to true | +| ethNetwork | Field with the URL of the Ethereum node. | +| ethers | Provider for the network if the configuration is not provided. This field is required for all zkSync networks used by this plugin. | +| zksync | Flag that indicates a zkSync Era network configuration. This field is set to true for all zkSync Era networks. | +| verifyURL | Field that points to the verification endpoint for the specific zkSync network. This parameter is optional. | + +Default values for verifyURL are: + +- Testnet: https://explorer.sepolia.era.zksync.dev/contract_verification +- Mainnet: https://zksync2-mainnet-explorer.zksync.io/contract_verification + +## 🕹 Commands + +`yarn hardhat verify:vyper --network <network> <contract address>` + +This command verifies the contract on the given network with the given contract's address. +When executed in this manner, the verification task attempts to compare the compiled bytecode of all the contracts in your local environment with the deployed bytecode of the contract you are seeking to verify. If there is no match, it reports an error. + +`yarn hardhat verify:vyper --network <network> <contract address> --contract <fully qualified name>` + +With the --contract parameter you can also specify which contract from your local setup you want to verify by specifying its Fully qualified name. Fully qualified name structure looks like this: "contracts/Contract.vy:Contract" + +The following command checks the status of the verification request for the specific verification ID: + +`yarn hardhat verify-status:vyper --verification-id <your verification id>` + + +**Constructor arguments** + +If your contract was deployed with specific constructor arguments, you need to specify them when running the verify task. For example: + +`yarn hardhat verify:vyper --network testnet 0x7cf08341524AAF292255F3ecD435f8EE1a910AbF "Hi there!"` + +**Verification status check** + +The verification process consists of two steps: + +- A verification request is sent to confirm if the given parameters for your contract are correct. + +- Then, we check the verification status of that request. Both steps run when you run the verify:vyper task, but you will be able to see your specific verification request ID. You can then use this ID to check the status of your verification request without running the whole process from the beginning. + +**Verify smart contract programmatically** + +If you need to run the verification task directly from your code, you can use the hardhat verify:verify:vyper task with the previously mentioned parameters. + +Example: + +``` +const verificationId = await hre.run("verify:verify:vyper", { + address: contractAddress, + contract: contractFullyQualifedName, + constructorArguments: [...] +}); +``` +## 📝 Documentation + +In addition to the [hardhat-zksync-verify-vyper](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-verify-vyper.html), zkSync's Era [website](https://era.zksync.io/docs/) offers a variety of resources including: + +[Guides to get started](https://era.zksync.io/docs/dev/building-on-zksync/hello-world.html): Learn how to start building on zkSync Era.\ +[Hardhat zkSync Era plugins](https://era.zksync.io/docs/tools/hardhat/getting-started.html): Overview and guides for all Hardhat zkSync Era plugins.\ +[Hyperscaling](https://era.zksync.io/docs/reference/concepts/hyperscaling.html#what-are-hyperchains): Deep dive into hyperscaling on zkSync Era. + +## 🤝 Contributing + +Contributions are always welcome! Feel free to open any issue or send a pull request. + +Go to [CONTRIBUTING.md](https://github.com/matter-labs/hardhat-zksync/blob/main/.github/CONTRIBUTING.md) to learn about steps and best practices for contributing to zkSync hardhat tooling base repository. + + +## 🙌 Feedback, help and news + +[zkSync Era Discord server](https://join.zksync.dev/): for questions and feedback.\ +[Follow zkSync Era on Twitter](https://twitter.com/zksync) + +## Happy building! 👷‍♀️👷‍♂️ \ No newline at end of file diff --git a/packages/hardhat-zksync-verify-vyper/package.json b/packages/hardhat-zksync-verify-vyper/package.json index d6ac7dcc1..f456d9a7f 100644 --- a/packages/hardhat-zksync-verify-vyper/package.json +++ b/packages/hardhat-zksync-verify-vyper/package.json @@ -1,6 +1,6 @@ { "name": "@matterlabs/hardhat-zksync-verify-vyper", - "version": "0.0.1-alpha.5", + "version": "0.0.1-alpha.6", "description": "Hardhat plugin to verify Vyper smart contracts for the zkSync network", "repository": "github:matter-labs/hardhat-zksync", "homepage": "https://github.com/matter-labs/hardhat-zksync/tree/main/packages/hardhat-zksync-verify-vyper", @@ -36,11 +36,14 @@ "dependencies": { "@matterlabs/hardhat-zksync-vyper": "^1.0.0", "axios": "^1.6.2", + "chai": "^4.3.6", "chalk": "4.1.2", - "zksync-ethers": "^6.0.0" + "zksync-ethers": "^6.0.0", + "@nomiclabs/hardhat-vyper": "^3.0.5", + "@ethersproject/abi":"^5.1.2", + "@ethersproject/address": "5.7.0" }, "devDependencies": { - "@nomiclabs/hardhat-vyper": "^3.0.5", "@matterlabs/hardhat-zksync-vyper": "^1.0.0", "@types/chai": "^4.2.0", "@types/debug": "^4.1.7", @@ -52,13 +55,12 @@ "@types/sinon": "^10.0.13", "@typescript-eslint/eslint-plugin": "5.61.0", "@typescript-eslint/parser": "5.61.0", - "chai": "^4.3.6", "eslint": "^8.44.0", "eslint-config-prettier": "8.3.0", "eslint-plugin-import": "2.27.5", "eslint-plugin-no-only-tests": "3.0.0", "eslint-plugin-prettier": "3.4.0", - "hardhat": "^2.19.2", + "hardhat": "^2.19.4", "mocha": "^9.2.1", "prettier": "2.5.1", "rimraf": "^3.0.2", @@ -68,7 +70,7 @@ "c8": "^8.0.1" }, "peerDependencies": { - "hardhat": "^2.19.2" + "hardhat": "^2.19.4" }, "prettier": { "tabWidth": 4, diff --git a/packages/hardhat-zksync-verify-vyper/src/constants.ts b/packages/hardhat-zksync-verify-vyper/src/constants.ts index cf117a5ca..9effd0ee6 100644 --- a/packages/hardhat-zksync-verify-vyper/src/constants.ts +++ b/packages/hardhat-zksync-verify-vyper/src/constants.ts @@ -2,7 +2,7 @@ export const PLUGIN_NAME = '@matterlabs/hardhat-zksync-verify-vyper'; export const TESTNET_VERIFY_URL = 'https://explorer.sepolia.era.zksync.dev/contract_verification'; -export const TASK_COMPILE_VYPER = "compile:vyper"; +export const TASK_COMPILE_VYPER = 'compile:vyper'; export const TASK_VERIFY_VYPER = 'verify:vyper'; export const TASK_CHECK_VERIFICATION_STATUS = 'verify-status:vyper'; @@ -38,20 +38,18 @@ If your constructor arguments are already encoded, they should be passed as a no `; export const ENCODED_ARAGUMENTS_NOT_FOUND_ERROR = (constructorArgsModulePath: string) => - `The module ${constructorArgsModulePath} doesn't export a list and does not start with "0x"\n` + - `Please export a list of constructor arguments or a single string starting with "0x".`; + `The module ${constructorArgsModulePath} doesn't export a list and does not start with "0x"\n` + + `Please export a list of constructor arguments or a single string starting with "0x".`; export const CONSTRUCTOR_MODULE_IMPORTING_ERROR = ( - errorMessage: string + errorMessage: string ) => `Importing the module for the constructor arguments list failed. Reason: ${errorMessage}`; -export const BYTECODES_ARE_NOT_SAME = - "Deployed and stored bytecodes are not the same."; - +export const BYTECODES_ARE_NOT_SAME = 'Deployed and stored bytecodes are not the same.'; export const NO_VERIFIABLE_ADDRESS_ERROR = - "You did not provide any address. Please re-run the 'verify:vyper' task with the address of the contract you want to verify."; + "You did not provide any address. Please re-run the 'verify:vyper' task with the address of the contract you want to verify."; export const NO_MATCHING_CONTRACT = `The address provided as argument contains a contract, but its bytecode doesn't match any of your local contracts. @@ -80,10 +78,10 @@ export const CONTRACT_NAME_NOT_FOUND = `You did not provide any contract name. P Qualified names look like this: contracts/Example.vy:Example`; export const COMPILER_VERSION_NOT_SUPPORTED = - 'Vyper compiler you used to compile the contract is not currently supported by zkSync block explorer!\nPlease use one of the supporting versions'; + 'Vyper compiler you used to compile the contract is not currently supported by zkSync block explorer!\nPlease use one of the supporting versions'; export const ZK_COMPILER_VERSION_NOT_SUPPORTED = - 'ZkVyper compiler you used to compile the contract is not currently supported.\n Please use one of the supporting versions'; + 'ZkVyper compiler you used to compile the contract is not currently supported.\n Please use one of the supporting versions'; export const WRONG_CONSTRUCTOR_ARGUMENTS = 'types/values length mismatch'; diff --git a/packages/hardhat-zksync-verify-vyper/src/index.ts b/packages/hardhat-zksync-verify-vyper/src/index.ts index c128be2e7..27aedec9d 100644 --- a/packages/hardhat-zksync-verify-vyper/src/index.ts +++ b/packages/hardhat-zksync-verify-vyper/src/index.ts @@ -19,39 +19,42 @@ extendEnvironment((hre: HardhatRuntimeEnvironment) => { }); task(TASK_VERIFY_VYPER, 'Verifies contract on Ethereum and zkSync networks') - .addOptionalPositionalParam("address", "Address of the contract to verify") + .addOptionalPositionalParam('address', 'Address of the contract to verify') .addOptionalVariadicPositionalParam( - "constructorArgsParams", - "Contract constructor arguments. Cannot be used if the --constructor-args option is provided", + 'constructorArgsParams', + 'Contract constructor arguments. Cannot be used if the --constructor-args option is provided', [] ) .addOptionalParam( - "constructorArgs", - "Path to a Javascript module that exports the constructor arguments", + 'constructorArgs', + 'Path to a Javascript module that exports the constructor arguments', undefined, types.inputFile ) .addOptionalParam( - "contract", - "Fully qualified name of the contract to verify. Skips automatic detection of the contract. " + - "Use if the deployed bytecode matches more than one contract in your project" - ).setAction(verify); + 'contract', + 'Fully qualified name of the contract to verify. Skips automatic detection of the contract. ' + + 'Use if the deployed bytecode matches more than one contract in your project' + ) + .setAction(verify); task(TASK_CHECK_VERIFICATION_STATUS) .addParam('verificationId', 'An ID returned by the verification request', undefined, types.int) .setAction(checkVerificationStatus); subtask(TASK_VERIFY_VERIFY_VYPER) - .addOptionalParam("address") - .addOptionalParam("constructorArguments", undefined, [], types.any) - .addOptionalParam("contract") + .addOptionalParam('address') + .addOptionalParam('constructorArguments', undefined, [], types.any) + .addOptionalParam('contract') .setAction(verifyContract); subtask(TASK_VERIFY_GET_CONSTRUCTOR_ARGUMENTS).setAction(getConstructorArguments); subtask(TASK_VERIFY_GET_ARTIFACT) - .addOptionalParam("contract", - "Fully qualified name of the contract to verify. Skips automatic detection of the contract. " + - "Use if the deployed bytecode matches more than one contract in your project") - .addParam("deployedBytecode", "Bytecode of the deployed contract") + .addOptionalParam( + 'contract', + 'Fully qualified name of the contract to verify. Skips automatic detection of the contract. ' + + 'Use if the deployed bytecode matches more than one contract in your project' + ) + .addParam('deployedBytecode', 'Bytecode of the deployed contract') .setAction(getArtifact); diff --git a/packages/hardhat-zksync-verify-vyper/src/plugin.ts b/packages/hardhat-zksync-verify-vyper/src/plugin.ts index cf5bbc589..77ccf53d1 100644 --- a/packages/hardhat-zksync-verify-vyper/src/plugin.ts +++ b/packages/hardhat-zksync-verify-vyper/src/plugin.ts @@ -1,5 +1,12 @@ import { Artifact, Artifacts, HardhatRuntimeEnvironment } from 'hardhat/types'; import { isFullyQualifiedName } from 'hardhat/utils/contract-names'; +import chalk from 'chalk'; +import { VyperFilesCache, getVyperFilesCachePath } from '@nomiclabs/hardhat-vyper/dist/src/cache'; +import { Parser } from '@nomiclabs/hardhat-vyper/dist/src/parser'; +import { Resolver, ResolvedFile } from '@nomiclabs/hardhat-vyper/dist/src/resolver'; +import { TASK_COMPILE_VYPER_READ_FILE } from '@nomiclabs/hardhat-vyper/dist/src/task-names'; +import { areSameBytecodes, encodeArguments } from './utils'; +import { ZkSyncVerifyPluginError } from './errors'; import { MULTIPLE_MATCHING_CONTRACTS, CONTRACT_NAME_NOT_FOUND, @@ -7,21 +14,11 @@ import { CONST_ARGS_ARRAY_ERROR, PENDING_CONTRACT_INFORMATION_MESSAGE, } from './constants'; -import { ZkSyncVerifyPluginError } from './errors'; -import { areSameBytecodes, encodeArguments } from './utils'; -import chalk from 'chalk'; -import { VyperFilesCache, getVyperFilesCachePath } from '@nomiclabs/hardhat-vyper/dist/src/cache'; -import { Parser } from '@nomiclabs/hardhat-vyper/dist/src/parser'; -import { Resolver, ResolvedFile } from '@nomiclabs/hardhat-vyper/dist/src/resolver'; -import { TASK_COMPILE_VYPER_READ_FILE } from '@nomiclabs/hardhat-vyper/dist/src/task-names'; import { VerificationStatusResponse } from './zksync-block-explorer/verification-status-response'; import { checkVerificationStatusService } from './zksync-block-explorer/service'; import { CacheResolveFileInfo } from './types'; -export async function inferContractArtifacts( - artifacts: Artifacts, - deployedBytecode: string -): Promise<any> { +export async function inferContractArtifacts(artifacts: Artifacts, deployedBytecode: string): Promise<any> { const artifactMatches = []; const fqNames = await artifacts.getAllFullyQualifiedNames(); @@ -61,14 +58,13 @@ Instead, this name was received: ${contractFQN}` if (!(await artifacts.artifactExists(contractFQN))) { throw new ZkSyncVerifyPluginError(`The contract ${contractFQN} is not present in your project.`); } - } - else { + } else { throw new ZkSyncVerifyPluginError(CONTRACT_NAME_NOT_FOUND); } } export async function checkVerificationStatus(args: { verificationId: number }, hre: HardhatRuntimeEnvironment) { - let isValidVerification = await executeVeificationWithRetry(args.verificationId, hre.network.verifyURL); + const isValidVerification = await executeVeificationWithRetry(args.verificationId, hre.network.verifyURL); if (isValidVerification?.errorExists()) { throw new ZkSyncVerifyPluginError(isValidVerification.getError()); @@ -77,7 +73,6 @@ export async function checkVerificationStatus(args: { verificationId: number }, return true; } - export async function getDeployArgumentEncoded(constructorArguments: any, artifact: Artifact): Promise<string> { if (!Array.isArray(constructorArguments)) { if (!constructorArguments.startsWith('0x')) { @@ -85,15 +80,17 @@ export async function getDeployArgumentEncoded(constructorArguments: any, artifa } return constructorArguments; } - return '0x' + (await encodeArguments(artifact.abi, constructorArguments)); + return `0x${await encodeArguments(artifact.abi, constructorArguments)}`; } -export async function getCacheResolvedFileInformation(contractFQN: string, sourceName: string, hre: HardhatRuntimeEnvironment): Promise<CacheResolveFileInfo> { +export async function getCacheResolvedFileInformation( + contractFQN: string, + sourceName: string, + hre: HardhatRuntimeEnvironment +): Promise<CacheResolveFileInfo> { const vyperFilesCachePath = getVyperFilesCachePath(hre.config.paths); - let vyperFilesCache = await VyperFilesCache.readFromFile( - vyperFilesCachePath - ); + const vyperFilesCache = await VyperFilesCache.readFromFile(vyperFilesCachePath); const contractCache = vyperFilesCache.getEntries().find((entry) => contractFQN.includes(entry.sourceName)); @@ -103,11 +100,8 @@ export async function getCacheResolvedFileInformation(contractFQN: string, sourc const parser = new Parser(vyperFilesCache); - const resolver = new Resolver( - hre.config.paths.root, - parser, - (absolutePath: string) => - hre.run(TASK_COMPILE_VYPER_READ_FILE, { absolutePath }) + const resolver = new Resolver(hre.config.paths.root, parser, (absolutePath: string) => + hre.run(TASK_COMPILE_VYPER_READ_FILE, { absolutePath }) ); const resolvedFile = await resolver.resolveSourceName(sourceName); @@ -117,9 +111,9 @@ export async function getCacheResolvedFileInformation(contractFQN: string, sourc } return { - resolvedFile: resolvedFile, - contractCache: contractCache - } + resolvedFile, + contractCache, + }; } export async function executeVeificationWithRetry( @@ -148,9 +142,7 @@ export function delay(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } - export async function getResolvedFiles(hre: HardhatRuntimeEnvironment): Promise<ResolvedFile[]> { - const resolvedFiles: ResolvedFile[] = []; const fqNames = await hre.artifacts.getAllFullyQualifiedNames(); @@ -162,4 +154,3 @@ export async function getResolvedFiles(hre: HardhatRuntimeEnvironment): Promise< return resolvedFiles; } - diff --git a/packages/hardhat-zksync-verify-vyper/src/task-actions.ts b/packages/hardhat-zksync-verify-vyper/src/task-actions.ts index d95de549b..0d236ccb2 100644 --- a/packages/hardhat-zksync-verify-vyper/src/task-actions.ts +++ b/packages/hardhat-zksync-verify-vyper/src/task-actions.ts @@ -1,4 +1,7 @@ import { Artifact, HardhatRuntimeEnvironment, TaskArguments } from 'hardhat/types'; +import path from 'path'; +import chalk from 'chalk'; +import { ResolvedFile } from '@nomiclabs/hardhat-vyper/dist/src/resolver'; import { BYTECODES_ARE_NOT_SAME, COMPILER_VERSION_NOT_SUPPORTED, @@ -14,13 +17,16 @@ import { TASK_VERIFY_VERIFY_VYPER, ZK_COMPILER_VERSION_NOT_SUPPORTED, } from './constants'; -import path from 'path'; import { ZkSyncVerifyPluginError } from './errors'; -import chalk from 'chalk'; -import { COMPILER_TYPE, getSupportedCompilerVersions, verifyContractRequest } from './zksync-block-explorer/service'; +import { CompilerType, getSupportedCompilerVersions, verifyContractRequest } from './zksync-block-explorer/service'; import { areSameBytecodes, retrieveContractBytecode } from './utils'; -import { checkContractName, getCacheResolvedFileInformation, getDeployArgumentEncoded, getResolvedFiles, inferContractArtifacts } from './plugin'; -import { ResolvedFile } from '@nomiclabs/hardhat-vyper/dist/src/resolver'; +import { + checkContractName, + getCacheResolvedFileInformation, + getDeployArgumentEncoded, + getResolvedFiles, + inferContractArtifacts, +} from './plugin'; export async function verify( args: { @@ -31,7 +37,6 @@ export async function verify( }, hre: HardhatRuntimeEnvironment ) { - if (args.address === undefined) { throw new ZkSyncVerifyPluginError(NO_VERIFIABLE_ADDRESS_ERROR); } @@ -46,11 +51,10 @@ export async function verify( constructorArgsParams: args.constructorArgsParams, }); - await hre.run(TASK_VERIFY_VERIFY_VYPER, { address: args.address, - constructorArguments: constructorArguments, - contract: args.contract + constructorArguments, + contract: args.contract, }); } @@ -64,7 +68,7 @@ export async function verifyContract( const artifact = await hre.run(TASK_VERIFY_GET_ARTIFACT, { contractFQN, deployedBytecode }); const artificatBytecode = artifact.bytecode; - contractFQN = contractFQN ?? artifact.sourceName + ':' + artifact.contractName; + contractFQN = contractFQN ?? `${artifact.sourceName}:${artifact.contractName}`; const deployArgumentsEncoded = await getDeployArgumentEncoded(constructorArguments, artifact); @@ -78,14 +82,14 @@ export async function verifyContract( const vyperVersion = contractCache.vyperConfig.version; - const compilerPossibleVersions = await getSupportedCompilerVersions(hre.network.verifyURL, COMPILER_TYPE.VYPER); + const compilerPossibleVersions = await getSupportedCompilerVersions(hre.network.verifyURL, CompilerType.VYPER); if (!compilerPossibleVersions.includes(vyperVersion)) { throw new ZkSyncVerifyPluginError(COMPILER_VERSION_NOT_SUPPORTED); } - const zkVyperVersion = 'v' + hre.config.zkvyper.version; + const zkVyperVersion = `v${hre.config.zkvyper.version}`; - const zkCompilerPossibleVersions = await getSupportedCompilerVersions(hre.network.verifyURL, COMPILER_TYPE.ZKVYPER); + const zkCompilerPossibleVersions = await getSupportedCompilerVersions(hre.network.verifyURL, CompilerType.ZKVYPER); if (!zkCompilerPossibleVersions.includes(zkVyperVersion)) { throw new ZkSyncVerifyPluginError(ZK_COMPILER_VERSION_NOT_SUPPORTED); } @@ -95,7 +99,7 @@ export async function verifyContract( resolvedFiles.map((file) => [file.sourceName, file.content.rawContent]) ); - let request = { + const request = { contractAddress: address, sourceCode: contractsSourceCodesMap, codeFormat: JSON_INPUT_CODE_FORMAT, @@ -108,17 +112,15 @@ export async function verifyContract( const response = await verifyContractRequest(request, hre.network.verifyURL); - let verificationId = parseInt(response.message); - console.info(chalk.cyan('Your verification ID is: ' + verificationId)); + const verificationId = parseInt(response.message, 10); + console.info(chalk.cyan(`Your verification ID is: ${verificationId}`)); - await hre.run(TASK_CHECK_VERIFICATION_STATUS, { verificationId: verificationId }); + await hre.run(TASK_CHECK_VERIFICATION_STATUS, { verificationId }); return verificationId; } -export async function getConstructorArguments( - args: any -): Promise<any> { +export async function getConstructorArguments(args: any): Promise<any> { if (typeof args.constructorArgsModule !== 'string') { return args.constructorArgsParams; } @@ -142,9 +144,8 @@ export async function getArtifact( { contractFQN, deployedBytecode }: TaskArguments, { artifacts }: HardhatRuntimeEnvironment ): Promise<Artifact> { - if (contractFQN !== undefined) { - checkContractName(artifacts, contractFQN); + const _ = checkContractName(artifacts, contractFQN); const artifact = await artifacts.readArtifact(contractFQN); diff --git a/packages/hardhat-zksync-verify-vyper/src/types.ts b/packages/hardhat-zksync-verify-vyper/src/types.ts index 2c6732ffc..d01f32a87 100644 --- a/packages/hardhat-zksync-verify-vyper/src/types.ts +++ b/packages/hardhat-zksync-verify-vyper/src/types.ts @@ -1,8 +1,7 @@ - import { CacheEntry } from '@nomiclabs/hardhat-vyper/dist/src/cache'; import { ResolvedFile } from '@nomiclabs/hardhat-vyper/dist/src/types'; -export type CacheResolveFileInfo = { +export interface CacheResolveFileInfo { resolvedFile: ResolvedFile; contractCache: CacheEntry; -} \ No newline at end of file +} diff --git a/packages/hardhat-zksync-verify-vyper/src/utils.ts b/packages/hardhat-zksync-verify-vyper/src/utils.ts index 2157ced59..9c4a3c6a5 100644 --- a/packages/hardhat-zksync-verify-vyper/src/utils.ts +++ b/packages/hardhat-zksync-verify-vyper/src/utils.ts @@ -48,13 +48,10 @@ export function parseWrongConstructorArgumentsError(string: string): string { // extract the values of the "types" and "values" keys from the string const data = JSON.parse(string.split('count=')[1].split(', value=')[0]); - return `The number of constructor arguments you provided (${data['values']}) does not match the number of constructor arguments the contract has been deployed with (${data['types']}).`; + return `The number of constructor arguments you provided (${data.values}) does not match the number of constructor arguments the contract has been deployed with (${data.types}).`; } -export function areSameBytecodes( - deployedBytecode: string, - runtimeBytecode: string -): boolean { +export function areSameBytecodes(deployedBytecode: string, runtimeBytecode: string): boolean { if (deployedBytecode !== runtimeBytecode) { return false; } diff --git a/packages/hardhat-zksync-verify-vyper/src/zksync-block-explorer/service.ts b/packages/hardhat-zksync-verify-vyper/src/zksync-block-explorer/service.ts index d522f9337..a01f746ce 100644 --- a/packages/hardhat-zksync-verify-vyper/src/zksync-block-explorer/service.ts +++ b/packages/hardhat-zksync-verify-vyper/src/zksync-block-explorer/service.ts @@ -39,13 +39,17 @@ export async function verifyContractRequest( } } -export enum COMPILER_TYPE { - VYPER, ZKVYPER +export enum CompilerType { + VYPER, + ZKVYPER, } -export async function getSupportedCompilerVersions(verifyURL: string | undefined, compilerType: COMPILER_TYPE): Promise<string[]> { +export async function getSupportedCompilerVersions( + verifyURL: string | undefined, + compilerType: CompilerType +): Promise<string[]> { try { - const compilerTypePath = compilerType == COMPILER_TYPE.VYPER ? '/vyper_versions' : '/zkvyper_versions' + const compilerTypePath = compilerType === CompilerType.VYPER ? '/vyper_versions' : '/zkvyper_versions'; const response = await axios.get(verifyURL + compilerTypePath); return response.data; } catch (error) { @@ -60,7 +64,7 @@ export async function checkVerificationStatusService( let verificationStatusResponse; try { - let data = await axios.get(verifyURL + `/${requestId}`); + const data = await axios.get(`${verifyURL}/${requestId}`); verificationStatusResponse = new VerificationStatusResponse(data); return verificationStatusResponse; diff --git a/packages/hardhat-zksync-verify-vyper/src/zksync-block-explorer/verification-status-response.ts b/packages/hardhat-zksync-verify-vyper/src/zksync-block-explorer/verification-status-response.ts index baefed80d..f0ec1fa1e 100644 --- a/packages/hardhat-zksync-verify-vyper/src/zksync-block-explorer/verification-status-response.ts +++ b/packages/hardhat-zksync-verify-vyper/src/zksync-block-explorer/verification-status-response.ts @@ -1,14 +1,14 @@ enum VerificationStatusEnum { - successful = 'successful', - failed = 'failed', - queued = 'queued', - inProgress = 'in_progress', + SUCCESSFUL = 'successful', + FAILED = 'failed', + QUEUED = 'queued', + IN_PROGRESS = 'in_progress', } export class VerificationStatusResponse { public readonly status: VerificationStatusEnum; public readonly error: string | undefined; - public readonly compilationErrors: Array<string> | undefined; + public readonly compilationErrors: string[] | undefined; constructor(response: any) { this.status = response.data.status; @@ -35,18 +35,18 @@ export class VerificationStatusResponse { } public isPending() { - return this.status === VerificationStatusEnum.inProgress; + return this.status === VerificationStatusEnum.IN_PROGRESS; } public isVerificationFailure() { - return this.status === VerificationStatusEnum.failed; + return this.status === VerificationStatusEnum.FAILED; } public isQueued() { - return this.status === VerificationStatusEnum.queued; + return this.status === VerificationStatusEnum.QUEUED; } public isVerificationSuccess() { - return this.status === VerificationStatusEnum.successful; + return this.status === VerificationStatusEnum.SUCCESSFUL; } } diff --git a/packages/hardhat-zksync-verify-vyper/src/zksync-block-explorer/verify-contract-request.ts b/packages/hardhat-zksync-verify-vyper/src/zksync-block-explorer/verify-contract-request.ts index c2f74455f..8747e332c 100644 --- a/packages/hardhat-zksync-verify-vyper/src/zksync-block-explorer/verify-contract-request.ts +++ b/packages/hardhat-zksync-verify-vyper/src/zksync-block-explorer/verify-contract-request.ts @@ -1,4 +1,3 @@ - export interface ZkSyncVyperBlockExplorerVerifyRequest { contractAddress: string; contractName: string; @@ -8,4 +7,4 @@ export interface ZkSyncVyperBlockExplorerVerifyRequest { compilerZkvyperVersion: string; optimizationUsed: boolean; constructorArguments: string; -} \ No newline at end of file +} diff --git a/packages/hardhat-zksync-verify-vyper/test/common.config.ts b/packages/hardhat-zksync-verify-vyper/test/common.config.ts index 5bc0ce224..8ebba498d 100644 --- a/packages/hardhat-zksync-verify-vyper/test/common.config.ts +++ b/packages/hardhat-zksync-verify-vyper/test/common.config.ts @@ -1,13 +1,12 @@ -import "@nomiclabs/hardhat-vyper"; -import "@matterlabs/hardhat-zksync-vyper"; +import '@nomiclabs/hardhat-vyper'; +import '@matterlabs/hardhat-zksync-vyper'; import '../src/index'; import { HardhatUserConfig } from 'hardhat/config'; const config: HardhatUserConfig = { zkvyper: { compilerSource: 'binary', - settings: { - }, + settings: {}, }, networks: { hardhat: { @@ -25,7 +24,7 @@ const config: HardhatUserConfig = { }, vyper: { version: '0.3.3', - } + }, }; export default config; diff --git a/packages/hardhat-zksync-verify-vyper/test/fixture-projects/localGreeter/hardhat.config.ts b/packages/hardhat-zksync-verify-vyper/test/fixture-projects/localGreeter/hardhat.config.ts index 9130b8229..12575417f 100644 --- a/packages/hardhat-zksync-verify-vyper/test/fixture-projects/localGreeter/hardhat.config.ts +++ b/packages/hardhat-zksync-verify-vyper/test/fixture-projects/localGreeter/hardhat.config.ts @@ -1 +1 @@ -module.exports = require('../../common.config'); \ No newline at end of file +module.exports = require('../../common.config'); diff --git a/packages/hardhat-zksync-verify-vyper/test/helpers.ts b/packages/hardhat-zksync-verify-vyper/test/helpers.ts index 2eb7cba69..6696d3674 100644 --- a/packages/hardhat-zksync-verify-vyper/test/helpers.ts +++ b/packages/hardhat-zksync-verify-vyper/test/helpers.ts @@ -1,6 +1,5 @@ import { resetHardhatContext } from 'hardhat/plugins-testing'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; -import { TASK_CLEAN } from 'hardhat/builtin-tasks/task-names'; import '../src/type-extensions'; import path from 'path'; import { TASK_COMPILE_VYPER } from '../src/constants'; @@ -15,8 +14,7 @@ export function useEnvironment(fixtureProjectName: string, networkName = 'hardha process.chdir(path.join(__dirname, 'fixture-projects', fixtureProjectName)); process.env.HARDHAT_NETWORK = networkName; this.env = require('hardhat'); - await this.env.run(TASK_COMPILE_VYPER, { quiet: false}); - + await this.env.run(TASK_COMPILE_VYPER, { quiet: false }); }); afterEach('Resetting hardhat', function () { diff --git a/packages/hardhat-zksync-verify-vyper/test/tests.ts b/packages/hardhat-zksync-verify-vyper/test/tests.ts index aac0de785..7d4dd3265 100644 --- a/packages/hardhat-zksync-verify-vyper/test/tests.ts +++ b/packages/hardhat-zksync-verify-vyper/test/tests.ts @@ -1,11 +1,17 @@ import { assert } from 'chai'; -import { useEnvironment } from './helpers'; import { TASK_VERIFY_GET_ARTIFACT } from '../src/constants'; -import { getCacheResolvedFileInformation } from '../src/plugin'; +import { + checkContractName, + checkVerificationStatus, + delay, + getCacheResolvedFileInformation, + getDeployArgumentEncoded, + getResolvedFiles, +} from '../src/plugin'; +import { useEnvironment } from './helpers'; describe('verify plugin', async function () { const sourceName: string = 'contracts/Greeter.vy'; - const contractName: string = 'Greeter'; const testnetVerifyURL = 'https://explorer.sepolia.era.zksync.dev/contract_verification'; describe('Testnet verifyURL extraction from config', async function () { @@ -29,13 +35,206 @@ describe('verify plugin', async function () { it('Verifies contract with provided source name', async function () { const contractName = 'contracts/Greeter.vy:Greeter'; - const artifact = await this.env.run(TASK_VERIFY_GET_ARTIFACT, { contractFQN: contractName, deployedBytecode: '0x' }); - - const { resolvedFile, contractCache } = await getCacheResolvedFileInformation(sourceName, artifact.sourceName, this.env); - + const artifact = await this.env.run(TASK_VERIFY_GET_ARTIFACT, { + contractFQN: contractName, + deployedBytecode: '0x', + }); + + const { resolvedFile, contractCache } = await getCacheResolvedFileInformation( + sourceName, + artifact.sourceName, + this.env + ); + assert.equal(resolvedFile.sourceName, sourceName); assert.equal(contractCache.sourceName, sourceName); assert.equal(contractCache.vyperConfig.version, '0.3.3'); }); }); + + describe('Check verification status', async function () { + useEnvironment('localGreeter', 'testnet'); + const validId = 1; + // id is invalid because its too big and there are for sure not this many verified contracts. + const invalidId = 1000000000; + + it('should pass the verification', async function () { + let isErr = 0; + try { + const x = await checkVerificationStatus({ verificationId: validId }, this.env); + await delay(100); + assert(x === true, `Contract with verificationId: ${validId} is not verified.`); + } catch (e: any) { + isErr = 1; + console.info(e); + } + assert(!isErr, 'Unexpectedly not passed the valid verification'); + }); + + it('should not pass the verification (AXIOS - Bad Request)', async function () { + let isErr = 0; + try { + await checkVerificationStatus({ verificationId: invalidId }, this.env); + } catch (e: any) { + isErr = 1; + } + assert(isErr, `Contract with verificationId: ${invalidId} is verified.`); + }); + }); + + describe('Checks Artifacts', async function () { + useEnvironment('localGreeter', 'testnet'); + + it('fails to checks contract name', async function () { + try { + await checkContractName(this.env.artifacts, 'TestContract.vy'); + } catch (e: any) { + assert( + e.message.includes( + 'A valid fully qualified name was expected. Fully qualified names look like this: ' + ), + 'Error message does not include the expected text' + ); + } + }); + + it('fails to find contract name', async function () { + try { + await checkContractName(this.env.artifacts, 'contracts/Greeter.vy:GreeterFailed'); + } catch (e: any) { + assert( + e.message.includes('is not present in your project'), + 'Error message does not include the expected text' + ); + } + }); + + it('Checks contract name', async function () { + let isErr = 0; + try { + await checkContractName(this.env.artifacts, 'contracts/Greeter.vy:Greeter'); + } catch (e: any) { + isErr = 1; + } + assert(!isErr, 'contracts/Greeter.vy:Greeter should have been valid fully qualified name'); + }); + + it('fails to find fully qualified contract name', async function () { + try { + await checkContractName(this.env.artifacts, undefined as any); + } catch (e: any) { + assert( + e.message.includes('You did not provide any contract name.'), + 'Error message does not include the expected text' + ); + } + }); + }); + + describe('Resolve file', async function () { + useEnvironment('localGreeter', 'testnet'); + it('Resolve single vyper contract', async function () { + let isErr = 0; + try { + const resolvedFiles = await getResolvedFiles(this.env); + assert( + resolvedFiles[0].sourceName === 'contracts/Greeter.vy', + 'Greeter.vy not found at position 0 of resolved files.' + ); + } catch (e: any) { + isErr = 1; + console.info(e); + } + assert(!isErr, 'Unexpectedly not passed the resolving test'); + }); + }); + + describe('Constructor argument encoding', async function () { + useEnvironment('localGreeter', 'testnet'); + const contractFQN = 'contracts/Greeter.vy:Greeter'; + const deployedBytecode = '0x'; + + it('Fails to encode', async function () { + try { + const artifact = await this.env.run(TASK_VERIFY_GET_ARTIFACT, { contractFQN, deployedBytecode }); + await getDeployArgumentEncoded('', artifact); + } catch (e: any) { + assert( + e.message.includes('Wrong constructor arguments format:'), + 'Error message does not include the expected text' + ); + } + }); + + it('Fails to encode because missmatch in length', async function () { + try { + const artifact = await this.env.run(TASK_VERIFY_GET_ARTIFACT, { contractFQN, deployedBytecode }); + await getDeployArgumentEncoded([], artifact); + } catch (e: any) { + assert( + e.message.includes( + 'The number of constructor arguments you provided (0) does not match the number of constructor arguments the contract has been deployed with (1).' + ), + 'Error message does not include the expected text' + ); + } + }); + + it('Encode correctly, variation 1', async function () { + let isErr = 0; + try { + const artifact = await this.env.run(TASK_VERIFY_GET_ARTIFACT, { contractFQN, deployedBytecode }); + const result = await getDeployArgumentEncoded([''], artifact); + assert( + result === + '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000', + 'Invalid encoding!' + ); + } catch (e: any) { + isErr = 1; + console.info(e); + } + assert(!isErr, 'Should not fail to encode'); + }); + + it('Encode correctly, variation 2', async function () { + let isErr = 0; + try { + const artifact = await this.env.run(TASK_VERIFY_GET_ARTIFACT, { contractFQN, deployedBytecode }); + const result = await getDeployArgumentEncoded(['Hello'], artifact); + assert( + result === + '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000', + '"Invalid encoding!"' + ); + } catch (e: any) { + isErr = 1; + console.info(e); + } + assert(!isErr, 'Should not fail to encode'); + }); + }); + + describe('Verification fail', async function () { + useEnvironment('localGreeter', 'testnet'); + const contractFQN = 'contracts/Greeter.vy:Greeter'; + const deployedBytecode = '0x'; + const placeholderAddress = '0x481c92dA8df49B5B96f59a65aB91eb67235aD648'; + it('Should not match any bytecode', async function () { + try { + const _artifact = await this.env.run(TASK_VERIFY_GET_ARTIFACT, { contractFQN, deployedBytecode }); + const _verificationId = await this.env.run('verify:verify:vyper', { + address: placeholderAddress, + constructorArguments: [], + }); + } catch (e: any) { + assert( + e.message.includes( + "The address provided as argument contains a contract, but its bytecode doesn't match any of your local contracts." + ), + 'Error message does not include the expected text' + ); + } + }); + }); }); diff --git a/packages/hardhat-zksync-verify/.eslintrc.js b/packages/hardhat-zksync-verify/.eslintrc.js index 56ca0b4bd..ce7756b34 100644 --- a/packages/hardhat-zksync-verify/.eslintrc.js +++ b/packages/hardhat-zksync-verify/.eslintrc.js @@ -4,4 +4,18 @@ module.exports = { project: `${__dirname}/eslint-tsconfig.json`, sourceType: "module", }, + rules: { + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "property", + "format": ["camelCase", "PascalCase", "snake_case"], + "leadingUnderscore": "allow", + "filter": { + "regex": "^[a-zA-Z0-9-_/*\\.]+$", + "match": false + } + }, + ], + }, }; diff --git a/packages/hardhat-zksync-verify/CHANGELOG.md b/packages/hardhat-zksync-verify/CHANGELOG.md index 9ef053fd4..279ef01d1 100644 --- a/packages/hardhat-zksync-verify/CHANGELOG.md +++ b/packages/hardhat-zksync-verify/CHANGELOG.md @@ -1,5 +1,12 @@ # @matterlabs/hardhat-zksync-verify +## [1.2.2](https://github.com/matter-labs/hardhat-zksync/compare/@matterlabs/hardhat-zksync-verify@1.2.1...@matterlabs/hardhat-zksync-verify-v1.2.2) (2023-12-22) + + +### Fixes + +* **docs:** update readme files ([#612](https://github.com/matter-labs/hardhat-zksync/issues/612)) ([682338e](https://github.com/matter-labs/hardhat-zksync/commit/682338e60f52021206325ff6eeec2c394a118642)) + ## 1.2.1 ### Patch Changes diff --git a/packages/hardhat-zksync-verify/README.md b/packages/hardhat-zksync-verify/README.md index d32cfed0c..7f6069d0a 100644 --- a/packages/hardhat-zksync-verify/README.md +++ b/packages/hardhat-zksync-verify/README.md @@ -1,7 +1,127 @@ -# hardhat-zksync-verify +# hardhat-zksync-verify 🚀 -[Hardhat](https://hardhat.org/) plugin to verify contracts on the zkSync network. +zkSync Era [Hardhat](https://hardhat.org/) plugin to verify contracts on the zkSync network. -Check out the [documentation page](https://v2-docs.zksync.io/dev/developer-guides/contracts/contract-verification.html) for more detailed explanation on how to verify smart contracts. +![Era Logo](https://github.com/matter-labs/era-contracts/raw/main/eraLogo.svg) -Check out the documentation page for the [zksync verify plugin](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-verify.html#verification-status-check) for more detailed explanation on how to use hardhat-zksync-verify. \ No newline at end of file +## ⚠️ Version Compatibility Warning + +Ensure you are using the correct version of the plugin with ethers: +- For plugin version **<1.0.0**: + - Compatible with ethers **v5**. + +- For plugin version **≥1.0.0**: + - Compatible with ethers **v6** (⭐ Recommended) + +## 📥 Installation + +To install **hardhat-zksync-verify** plugin, run: + +`npm install -D @matterlabs/hardhat-zksync-verify` + +or + +`yarn add -D @matterlabs/hardhat-zksync-verify @nomicfoundation/hardhat-verify` + +## 🔩 Configuration + +Import the plugin in the hardhat.config.ts file: + +`import "@matterlabs/hardhat-zksync-verify";` + +Add the verifyURL property to the zkSync Era network in the hardhat.config.ts file as shown below: + +``` +networks: { + sepolia: { + url: "https://sepolia.infura.io/v3/<API_KEY>" // The Ethereum Web3 RPC URL (optional). + }, + zkTestnet: { + url: "https://sepolia.era.zksync.dev", // The testnet RPC URL of zkSync Era network. + ethNetwork: "sepolia", // The Ethereum Web3 RPC URL, or the identifier of the network (e.g. `mainnet` or `sepolia`) + zksync: true, + // Verification endpoint for Sepolia + verifyURL: 'https://explorer.sepolia.era.zksync.dev/contract_verification' + } +}, + +``` +| 🔧 properties | 📄 Description | +|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------| +| zkTestnet | Arbitrary zkSync Era network name. You can select this as the default network using the defaultNetwork property. | +| url | Field is required for all zkSync Era and Ethereum networks used by this plugin. For zkSync network, set it to true | +| ethNetwork | Field with the URL of the Ethereum node. | +| ethers | Provider for the network if the configuration is not provided. This field is required for all zkSync networks used by this plugin. | +| zksync | Flag that indicates a zkSync Era network configuration. This field is set to true for all zkSync Era networks. | +| verifyURL | Field that points to the verification endpoint for the specific zkSync network. This parameter is optional. | + +Default values for verifyURL are: + +- Testnet: https://explorer.sepolia.era.zksync.dev/contract_verification +- Mainnet: https://zksync2-mainnet-explorer.zksync.io/contract_verification + +## 🕹 Commands + +`yarn hardhat verify --network <network> <contract address>` + +This command verifies the contract on the given network with the given contract's address. +When executed in this manner, the verification task attempts to compare the compiled bytecode of all the contracts in your local environment with the deployed bytecode of the contract you are seeking to verify. If there is no match, it reports an error. + +`yarn hardhat verify --network <network> <contract address> --contract <fully qualified name>` + +With the --contract parameter you can also specify which contract from your local setup you want to verify by specifying its Fully qualified name. Fully qualified name structure looks like this: "contracts/AContract.sol:TheContract" + +The following command checks the status of the verification request for the specific verification ID: + +`yarn hardhat verify-status --verification-id <your verification id>` + + + +**Constructor arguments** + +If your contract was deployed with the specific constructor arguments, you need to specify them when running the verify task. For example: + +`yarn hardhat verify --network testnet 0x7cf08341524AAF292255F3ecD435f8EE1a910AbF "Hi there!"` + +**Verification status check** + +The verification process consists of two steps: + +- A verification request is sent to confirm if the given parameters for your contract are correct. + +- Then, we check the verification status of that request. Both steps run when you run the verify task, but you will be able to see your specific verification request ID. You can then use this ID to check the status of your verification request without running the whole process from the beginning. + +**Verify smart contract programmatically** + +If you need to run the verification task directly from your code, you can use the hardhat verify:verify task with the previously mentioned parameters with the difference in using --address parameter when specifying contract's address. + +Example: + +``` +const verificationId = await hre.run("verify:verify", { + address: contractAddress, + contract: contractFullyQualifedName, + constructorArguments: [...] +}); +``` +## 📝 Documentation + +In addition to the [hardhat-zksync-verify](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-verify.html), zkSync's Era [website](https://era.zksync.io/docs/) offers a variety of resources including: + +[Guides to get started](https://era.zksync.io/docs/dev/building-on-zksync/hello-world.html): Learn how to start building on zkSync Era.\ +[Hardhat zkSync Era plugins](https://era.zksync.io/docs/tools/hardhat/getting-started.html): Overview and guides for all Hardhat zkSync Era plugins.\ +[Hyperscaling](https://era.zksync.io/docs/reference/concepts/hyperscaling.html#what-are-hyperchains): Deep dive into hyperscaling on zkSync Era. + +## 🤝 Contributing + +Contributions are always welcome! Feel free to open any issue or send a pull request. + +Go to [CONTRIBUTING.md](https://github.com/matter-labs/hardhat-zksync/blob/main/.github/CONTRIBUTING.md) to learn about steps and best practices for contributing to zkSync hardhat tooling base repository. + + +## 🙌 Feedback, help and news + +[zkSync Era Discord server](https://join.zksync.dev/): for questions and feedback.\ +[Follow zkSync Era on Twitter](https://twitter.com/zksync) + +## Happy building! 👷‍♀️👷‍♂️ \ No newline at end of file diff --git a/packages/hardhat-zksync-verify/package.json b/packages/hardhat-zksync-verify/package.json index cbefc12d6..9c9d4f871 100644 --- a/packages/hardhat-zksync-verify/package.json +++ b/packages/hardhat-zksync-verify/package.json @@ -1,6 +1,6 @@ { "name": "@matterlabs/hardhat-zksync-verify", - "version": "1.2.1", + "version": "1.2.2", "description": "Hardhat plugin to verify smart contracts for the zkSync network", "repository": "github:matter-labs/hardhat-zksync", "homepage": "https://github.com/matter-labs/hardhat-zksync/tree/main/packages/hardhat-zksync-verify", @@ -22,7 +22,7 @@ "fmt": "yarn prettier --write", "eslint": "eslint 'src/**/*.ts' 'test/**/*.ts'", "prettier": "prettier 'src/**/*.ts' 'test/**/*.ts'", - "test": "NODE_ENV=test c8 mocha test/tests.ts --no-timeout --exit", + "test": "c8 mocha --recursive \"test/tests/**/*.ts\" --exit", "build": "tsc --build .", "clean": "rimraf dist" }, @@ -36,8 +36,17 @@ "@matterlabs/hardhat-zksync-solc": "^1.0.3", "@nomicfoundation/hardhat-verify": "^2.0.0", "axios": "^1.6.2", + "chai": "^4.3.6", + "hardhat": "^2.19.4", "chalk": "4.1.2", - "zksync-ethers": "^6.0.0" + "@ethersproject/abi":"^5.1.2", + "@ethersproject/address": "5.7.0", + "zksync-ethers": "^6.0.0", + "cbor": "^8.1.0", + "debug":"^4.1.1", + "@openzeppelin/contracts": "^4.9.2", + "sinon-chai": "^3.7.0", + "sinon": "^16.0.0" }, "devDependencies": { "@nomicfoundation/hardhat-verify": "^2.0.0", @@ -46,13 +55,11 @@ "@types/node": "^18.11.17", "@typescript-eslint/eslint-plugin": "6.13.1", "@typescript-eslint/parser": "6.13.1", - "chai": "^4.3.6", "eslint": "^8.54.0", "eslint-config-prettier": "9.0.0", "eslint-plugin-import": "2.29.0", "eslint-plugin-no-only-tests": "3.1.0", "eslint-plugin-prettier": "5.0.1", - "hardhat": "^2.19.2", "mocha": "^10.1.0", "prettier": "3.1.0", "rimraf": "^3.0.2", diff --git a/packages/hardhat-zksync-verify/src/constants.ts b/packages/hardhat-zksync-verify/src/constants.ts index 178b0e386..19a08e7e1 100644 --- a/packages/hardhat-zksync-verify/src/constants.ts +++ b/packages/hardhat-zksync-verify/src/constants.ts @@ -40,7 +40,7 @@ If your constructor arguments are already encoded, they should be passed as a no `; export const BUILD_INFO_NOT_FOUND_ERROR = ( - contractFQN: string + contractFQN: string, ) => `We couldn't find the sources of your "${contractFQN}" contract in the project. Please make sure that it has been compiled by Hardhat and that it is written in Solidity.`; @@ -52,7 +52,7 @@ export const ENCODED_ARAGUMENTS_NOT_FOUND_ERROR = (constructorArgsModulePath: st `Please export a list of constructor arguments or a single string starting with "0x".`; export const CONSTRUCTOR_MODULE_IMPORTING_ERROR = ( - errorMessage: string + errorMessage: string, ) => `Importing the module for the constructor arguments list failed. Reason: ${errorMessage}`; diff --git a/packages/hardhat-zksync-verify/src/index.ts b/packages/hardhat-zksync-verify/src/index.ts index da9a7723f..fb3b5a2a0 100644 --- a/packages/hardhat-zksync-verify/src/index.ts +++ b/packages/hardhat-zksync-verify/src/index.ts @@ -22,7 +22,7 @@ extendEnvironment((hre: HardhatRuntimeEnvironment) => { }); task(TASK_VERIFY, 'Verifies contract on Ethereum and zkSync networks') - .addFlag("noCompile", "Run verify without compile") + .addFlag('noCompile', 'Run verify without compile') .setAction(verify); subtask(TASK_VERIFY_VERIFY).setAction(verifyContract); diff --git a/packages/hardhat-zksync-verify/src/plugin.ts b/packages/hardhat-zksync-verify/src/plugin.ts index dfb2568c7..3022152cf 100644 --- a/packages/hardhat-zksync-verify/src/plugin.ts +++ b/packages/hardhat-zksync-verify/src/plugin.ts @@ -1,22 +1,17 @@ import { TASK_FLATTEN_GET_FLATTENED_SOURCE } from 'hardhat/builtin-tasks/task-names'; import { Artifacts, HardhatRuntimeEnvironment, ResolvedFile } from 'hardhat/types'; import { isFullyQualifiedName, parseFullyQualifiedName } from 'hardhat/utils/contract-names'; -import { - MULTIPLE_MATCHING_CONTRACTS, - CONTRACT_NAME_NOT_FOUND, - NO_MATCHING_CONTRACT, - LIBRARIES_EXPORT_ERROR, -} from './constants'; +import path from 'path'; +import chalk from 'chalk'; +import { CONTRACT_NAME_NOT_FOUND, NO_MATCHING_CONTRACT, LIBRARIES_EXPORT_ERROR } from './constants'; import { Bytecode, extractMatchingContractInformation } from './solc/bytecode'; import { ZkSyncVerifyPluginError } from './errors'; import { executeVeificationWithRetry } from './utils'; -import path from 'path'; -import chalk from 'chalk'; export async function inferContractArtifacts( artifacts: Artifacts, matchingCompilerVersions: string[], - deployedBytecode: Bytecode + deployedBytecode: Bytecode, ): Promise<any> { const contractMatches = []; const fqNames = await artifacts.getAllFullyQualifiedNames(); @@ -38,7 +33,7 @@ export async function inferContractArtifacts( sourceName, contractName, buildInfo, - deployedBytecode + deployedBytecode, ); if (contractInformation !== null) { contractMatches.push(contractInformation); @@ -48,8 +43,6 @@ export async function inferContractArtifacts( if (contractMatches.length === 0) throw new ZkSyncVerifyPluginError(NO_MATCHING_CONTRACT); - if (contractMatches.length > 1) throw new ZkSyncVerifyPluginError(MULTIPLE_MATCHING_CONTRACTS); - return contractMatches[0]; } @@ -64,7 +57,7 @@ export async function checkContractName(artifacts: Artifacts, contractFQN: strin if (!isFullyQualifiedName(contractFQN)) { throw new ZkSyncVerifyPluginError( `A valid fully qualified name was expected. Fully qualified names look like this: "contracts/AContract.sol:TheContract" -Instead, this name was received: ${contractFQN}` +Instead, this name was received: ${contractFQN}`, ); } @@ -80,7 +73,7 @@ export function getSolidityStandardJsonInput(hre: HardhatRuntimeEnvironment, res return { language: 'Solidity', sources: Object.fromEntries( - resolvedFiles.map((file) => [file.sourceName, { content: file.content.rawContent }]) + resolvedFiles.map((file) => [file.sourceName, { content: file.content.rawContent }]), ), settings: hre.config.zksolc.settings, }; @@ -104,16 +97,16 @@ export async function getLibraries(librariesModule: string) { } catch (error: any) { throw new ZkSyncVerifyPluginError( `Importing the module for the libraries dictionary failed. Reason: ${error.message}`, - error + error, ); } } export async function checkVerificationStatus(args: { verificationId: number }, hre: HardhatRuntimeEnvironment) { - let isValidVerification = await executeVeificationWithRetry(args.verificationId, hre.network.verifyURL); + const isValidVerification = await executeVeificationWithRetry(args.verificationId, hre.network.verifyURL); if (isValidVerification?.errorExists()) { - throw new ZkSyncVerifyPluginError("Backend verification error: " + isValidVerification.getError()); + throw new ZkSyncVerifyPluginError(`Backend verification error: ${isValidVerification.getError()}`); } console.info(chalk.green(`Contract successfully verified on zkSync block explorer!`)); return true; diff --git a/packages/hardhat-zksync-verify/src/solc/bytecode.ts b/packages/hardhat-zksync-verify/src/solc/bytecode.ts index 8d0f5c67c..f04f1e92e 100644 --- a/packages/hardhat-zksync-verify/src/solc/bytecode.ts +++ b/packages/hardhat-zksync-verify/src/solc/bytecode.ts @@ -50,7 +50,7 @@ export async function extractMatchingContractInformation( sourceName: SourceName, contractName: ContractName, buildInfo: BuildInfo, - deployedBytecode: Bytecode + deployedBytecode: Bytecode, ): Promise<ContractInformation | null> { const contract = buildInfo.output.contracts[sourceName][contractName]; @@ -80,7 +80,7 @@ export async function extractMatchingContractInformation( export async function compareBytecode( deployedBytecode: Bytecode, - runtimeBytecodeSymbols: CompilerOutputBytecode + runtimeBytecodeSymbols: CompilerOutputBytecode, ): Promise<BytecodeExtractedData | null> { // We will ignore metadata information when comparing. Etherscan seems to do the same. const deployedExecutableSection = deployedBytecode.getExecutableSection(); @@ -95,7 +95,7 @@ export async function compareBytecode( const { normalizedBytecode: referenceBytecode } = await normalizeBytecode( runtimeBytecodeSymbols.object, - runtimeBytecodeSymbols + runtimeBytecodeSymbols, ); if ( @@ -113,7 +113,7 @@ export async function compareBytecode( export async function normalizeBytecode( bytecode: string, - symbols: CompilerOutputBytecode + symbols: CompilerOutputBytecode, ): Promise<BytecodeExtractedData> { const nestedSliceReferences: NestedSliceReferences = []; diff --git a/packages/hardhat-zksync-verify/src/task-actions.ts b/packages/hardhat-zksync-verify/src/task-actions.ts index 6ce03614c..02783d59e 100644 --- a/packages/hardhat-zksync-verify/src/task-actions.ts +++ b/packages/hardhat-zksync-verify/src/task-actions.ts @@ -1,5 +1,9 @@ import { DependencyGraph, HardhatRuntimeEnvironment, RunSuperFunction, TaskArguments } from 'hardhat/types'; +import { parseFullyQualifiedName } from 'hardhat/utils/contract-names'; +import chalk from 'chalk'; +import path from 'path'; +import { TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH } from 'hardhat/builtin-tasks/task-names'; import { getSupportedCompilerVersions, verifyContractRequest } from './zksync-block-explorer/service'; import { @@ -21,18 +25,14 @@ import { BUILD_INFO_NOT_FOUND_ERROR, } from './constants'; -import { encodeArguments, retrieveContractBytecode } from './utils'; +import { encodeArguments, extractModule, retrieveContractBytecode } from './utils'; import { Libraries } from './types'; import { ZkSyncVerifyPluginError } from './errors'; -import { parseFullyQualifiedName } from 'hardhat/utils/contract-names'; -import chalk from 'chalk'; -import path from 'path'; import { Bytecode, extractMatchingContractInformation } from './solc/bytecode'; import { ContractInformation } from './solc/types'; import { checkContractName, getLibraries, getSolidityStandardJsonInput, inferContractArtifacts } from './plugin'; -import { TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH } from 'hardhat/builtin-tasks/task-names'; export async function verify( args: { @@ -44,7 +44,7 @@ export async function verify( noCompile: boolean; }, hre: HardhatRuntimeEnvironment, - runSuper: RunSuperFunction<TaskArguments> + runSuper: RunSuperFunction<TaskArguments>, ) { if (!hre.network.zksync) { return await runSuper(args); @@ -67,7 +67,7 @@ export async function verify( await hre.run(TASK_VERIFY_VERIFY, { address: args.address, - constructorArguments: constructorArguments, + constructorArguments, contract: args.contract, libraries, noCompile: args.noCompile, @@ -77,7 +77,7 @@ export async function verify( export async function getCompilerVersions( _: TaskArguments, hre: HardhatRuntimeEnvironment, - runSuper: RunSuperFunction<TaskArguments> + runSuper: RunSuperFunction<TaskArguments>, ): Promise<string[]> { if (!hre.network.zksync) { return await runSuper(); @@ -96,7 +96,7 @@ export async function getCompilerVersions( export async function getConstructorArguments( args: any, hre: HardhatRuntimeEnvironment, - runSuper: RunSuperFunction<TaskArguments> + runSuper: RunSuperFunction<TaskArguments>, ): Promise<any> { if (!hre.network.zksync) { return await runSuper(args); @@ -109,7 +109,7 @@ export async function getConstructorArguments( const constructorArgsModulePath = path.resolve(process.cwd(), args.constructorArgsModule); try { - const constructorArguments = (await import(constructorArgsModulePath)).default; + const constructorArguments = await extractModule(constructorArgsModulePath); // Since our plugin supports both encoded and decoded constructor arguments, we need to check how are they passed if (!Array.isArray(constructorArguments) && !constructorArguments.startsWith('0x')) { @@ -124,7 +124,7 @@ export async function getConstructorArguments( export async function verifyContract( { address, contract: contractFQN, constructorArguments, libraries, noCompile }: TaskArguments, hre: HardhatRuntimeEnvironment, - runSuper: RunSuperFunction<TaskArguments> + runSuper: RunSuperFunction<TaskArguments>, ): Promise<number> { if (!hre.network.zksync) { return await runSuper({ address, contractFQN, constructorArguments, libraries }); @@ -145,10 +145,10 @@ export async function verifyContract( } const contractInformation: ContractInformation = await hre.run(TASK_VERIFY_GET_CONTRACT_INFORMATION, { - contractFQN: contractFQN, - deployedBytecode: deployedBytecode, + contractFQN, + deployedBytecode, matchingCompilerVersions: compilerVersions, - libraries:libraries + libraries, }); const solcVersion = contractInformation.solcVersion; @@ -161,8 +161,10 @@ export async function verifyContract( throw new ZkSyncVerifyPluginError(chalk.red(CONST_ARGS_ARRAY_ERROR)); } } else { - deployArgumentsEncoded = - '0x' + (await encodeArguments(contractInformation.contractOutput.abi, constructorArguments)); + deployArgumentsEncoded = `0x${await encodeArguments( + contractInformation.contractOutput.abi, + constructorArguments, + )}`; } const compilerPossibleVersions = await getSupportedCompilerVersions(hre.network.verifyURL); @@ -170,32 +172,32 @@ export async function verifyContract( if (!compilerPossibleVersions.includes(compilerVersion)) { throw new ZkSyncVerifyPluginError(COMPILER_VERSION_NOT_SUPPORTED); } - const compilerZksolcVersion = 'v' + contractInformation.contractOutput.metadata.zk_version; + const compilerZksolcVersion = `v${contractInformation.contractOutput.metadata.zk_version}`; - contractInformation.contractName = contractInformation.sourceName + ':' + contractInformation.contractName; + contractInformation.contractName = `${contractInformation.sourceName}:${contractInformation.contractName}`; const dependencyGraph: DependencyGraph = await hre.run(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH, { sourceNames: [contractInformation.sourceName], }); - let request = { + const request = { contractAddress: address, sourceCode: getSolidityStandardJsonInput(hre, dependencyGraph.getResolvedFiles()), codeFormat: JSON_INPUT_CODE_FORMAT, contractName: contractInformation.contractName, compilerSolcVersion: solcVersion, - compilerZksolcVersion: compilerZksolcVersion, + compilerZksolcVersion, constructorArguments: deployArgumentsEncoded, optimizationUsed: true, }; const response = await verifyContractRequest(request, hre.network.verifyURL); - let verificationId = parseInt(response.message); + const verificationId = parseInt(response.message, 10); - console.info(chalk.cyan('Your verification ID is: ' + verificationId)); + console.info(chalk.cyan(`Your verification ID is: ${verificationId}`)); try { - await hre.run(TASK_CHECK_VERIFICATION_STATUS, { verificationId: verificationId }); + await hre.run(TASK_CHECK_VERIFICATION_STATUS, { verificationId }); } catch (error: any) { // The first verirication attempt with 'minimal' source code was unnsuccessful. // Now try with the full source code from the compilation context. @@ -206,11 +208,11 @@ export async function verifyContract( request.sourceCode = contractInformation.compilerInput; - const response = await verifyContractRequest(request, hre.network.verifyURL); - let verificationId = parseInt(response.message); + const fallbackResponse = await verifyContractRequest(request, hre.network.verifyURL); + const fallbackVerificationId = parseInt(fallbackResponse.message, 10); - console.info(chalk.cyan('Your verification ID is: ' + verificationId)); - await hre.run(TASK_CHECK_VERIFICATION_STATUS, { verificationId: verificationId }); + console.info(chalk.cyan(`Your verification ID is: ${fallbackVerificationId}`)); + await hre.run(TASK_CHECK_VERIFICATION_STATUS, { verificationId: fallbackVerificationId }); } return verificationId; @@ -219,17 +221,17 @@ export async function verifyContract( export async function getContractInfo( { contractFQN, deployedBytecode, matchingCompilerVersions, libraries }: TaskArguments, hre: HardhatRuntimeEnvironment, - runSuper: RunSuperFunction<TaskArguments> + runSuper: RunSuperFunction<TaskArguments>, ): Promise<any> { if (!hre.network.zksync) { return await runSuper({ contractFQN, deployedBytecode, matchingCompilerVersions, libraries }); } - let artifacts = hre.artifacts; + const artifacts = hre.artifacts; let contractInformation; if (contractFQN !== undefined) { - checkContractName(artifacts, contractFQN); + const _ = checkContractName(artifacts, contractFQN); // Process BuildInfo here to check version and throw an error if unexpected version is found. const buildInfo: any = await artifacts.getBuildInfo(contractFQN); @@ -243,10 +245,10 @@ export async function getContractInfo( sourceName, contractName, buildInfo, - deployedBytecode + deployedBytecode, ); - if (contractInformation === null) { + if (contractInformation === undefined || contractInformation === null) { throw new ZkSyncVerifyPluginError(NO_MATCHING_CONTRACT); } } else { diff --git a/packages/hardhat-zksync-verify/src/utils.ts b/packages/hardhat-zksync-verify/src/utils.ts index dfedf2f11..8faf825e0 100644 --- a/packages/hardhat-zksync-verify/src/utils.ts +++ b/packages/hardhat-zksync-verify/src/utils.ts @@ -1,15 +1,15 @@ import axios from 'axios'; import * as zk from 'zksync-ethers'; +import chalk from 'chalk'; import { VerificationStatusResponse } from './zksync-block-explorer/verification-status-response'; import { checkVerificationStatusService } from './zksync-block-explorer/service'; import { ZkSyncVerifyPluginError } from './errors'; import { PENDING_CONTRACT_INFORMATION_MESSAGE, WRONG_CONSTRUCTOR_ARGUMENTS } from './constants'; -import chalk from 'chalk'; export function handleAxiosError(error: any): never { if (axios.isAxiosError(error)) { throw new Error( - `Axios error (code: ${error.code}) during the contract verification request\n Reason: ${error.response?.data}` + `Axios error (code: ${error.code}) during the contract verification request\n Reason: ${error.response?.data}`, ); } else { throw new ZkSyncVerifyPluginError(`Failed to send contract verification request\n Reason: ${error}`); @@ -42,7 +42,7 @@ export async function executeVeificationWithRetry( requestId: number, verifyURL: string, maxRetries = 5, - delayInMs = 1500 + delayInMs = 1500, ): Promise<VerificationStatusResponse | undefined> { let retries = 0; @@ -68,7 +68,7 @@ export async function retrieveContractBytecode(address: string, hreNetwork: any) if (deployedBytecode.length === 0) { throw new ZkSyncVerifyPluginError( `The address ${address} has no bytecode. Is the contract deployed to this network? - The selected network is ${hreNetwork.name}.` + The selected network is ${hreNetwork.name}.`, ); } return deployedBytecode; @@ -82,11 +82,11 @@ export function removeMultipleSubstringOccurrences(inputString: string, stringTo for (const line of lines) { if (line.trim().includes(stringToRemove)) { if (!firstIdentifierFound) { - output += line + '\n'; + output += `${line}\n`; firstIdentifierFound = true; } } else { - output += line + '\n'; + output += `${line}\n`; } } @@ -97,5 +97,10 @@ export function parseWrongConstructorArgumentsError(string: string): string { // extract the values of the "types" and "values" keys from the string const data = JSON.parse(string.split('count=')[1].split(', value=')[0]); - return `The number of constructor arguments you provided (${data['values']}) does not match the number of constructor arguments the contract has been deployed with (${data['types']}).`; + return `The number of constructor arguments you provided (${data.values}) does not match the number of constructor arguments the contract has been deployed with (${data.types}).`; +} + +export async function extractModule(constructorArgsModulePath: string) { + const constructorArguments = (await import(constructorArgsModulePath)).default; + return constructorArguments; } diff --git a/packages/hardhat-zksync-verify/src/zksync-block-explorer/service.ts b/packages/hardhat-zksync-verify/src/zksync-block-explorer/service.ts index 826608a18..d90e4a169 100644 --- a/packages/hardhat-zksync-verify/src/zksync-block-explorer/service.ts +++ b/packages/hardhat-zksync-verify/src/zksync-block-explorer/service.ts @@ -1,8 +1,8 @@ import axios from 'axios'; import { handleAxiosError } from '../utils'; -import { VerificationStatusResponse } from './verification-status-response'; import { ZkSyncVerifyPluginError } from '../errors'; +import { VerificationStatusResponse } from './verification-status-response'; import { ZkSyncBlockExplorerVerifyRequest } from './verify-contract-request'; export class ZkSyncBlockExplorerResponse { @@ -21,12 +21,12 @@ export class ZkSyncBlockExplorerResponse { export async function checkVerificationStatusService( requestId: number, - verifyURL: string + verifyURL: string, ): Promise<VerificationStatusResponse> { let verificationStatusResponse; try { - let data = await axios.get(verifyURL + `/${requestId}`); + const data = await axios.get(`${verifyURL}/${requestId}`); verificationStatusResponse = new VerificationStatusResponse(data); return verificationStatusResponse; @@ -37,7 +37,7 @@ export async function checkVerificationStatusService( export async function verifyContractRequest( req: ZkSyncBlockExplorerVerifyRequest, - verifyURL: string + verifyURL: string, ): Promise<ZkSyncBlockExplorerResponse> { let data; try { @@ -57,7 +57,7 @@ export async function verifyContractRequest( export async function getSupportedCompilerVersions(verifyURL: string | undefined): Promise<string[]> { try { - const response = await axios.get(verifyURL + '/solc_versions'); + const response = await axios.get(`${verifyURL}/solc_versions`); return response.data; } catch (error) { handleAxiosError(error); diff --git a/packages/hardhat-zksync-verify/src/zksync-block-explorer/verification-status-response.ts b/packages/hardhat-zksync-verify/src/zksync-block-explorer/verification-status-response.ts index baefed80d..f0ec1fa1e 100644 --- a/packages/hardhat-zksync-verify/src/zksync-block-explorer/verification-status-response.ts +++ b/packages/hardhat-zksync-verify/src/zksync-block-explorer/verification-status-response.ts @@ -1,14 +1,14 @@ enum VerificationStatusEnum { - successful = 'successful', - failed = 'failed', - queued = 'queued', - inProgress = 'in_progress', + SUCCESSFUL = 'successful', + FAILED = 'failed', + QUEUED = 'queued', + IN_PROGRESS = 'in_progress', } export class VerificationStatusResponse { public readonly status: VerificationStatusEnum; public readonly error: string | undefined; - public readonly compilationErrors: Array<string> | undefined; + public readonly compilationErrors: string[] | undefined; constructor(response: any) { this.status = response.data.status; @@ -35,18 +35,18 @@ export class VerificationStatusResponse { } public isPending() { - return this.status === VerificationStatusEnum.inProgress; + return this.status === VerificationStatusEnum.IN_PROGRESS; } public isVerificationFailure() { - return this.status === VerificationStatusEnum.failed; + return this.status === VerificationStatusEnum.FAILED; } public isQueued() { - return this.status === VerificationStatusEnum.queued; + return this.status === VerificationStatusEnum.QUEUED; } public isVerificationSuccess() { - return this.status === VerificationStatusEnum.successful; + return this.status === VerificationStatusEnum.SUCCESSFUL; } } diff --git a/packages/hardhat-zksync-verify/test/fixture-projects/localGreeter/args.js b/packages/hardhat-zksync-verify/test/fixture-projects/localGreeter/args.js new file mode 100644 index 000000000..ee3507708 --- /dev/null +++ b/packages/hardhat-zksync-verify/test/fixture-projects/localGreeter/args.js @@ -0,0 +1,3 @@ +module.exports = { + "name": "localGreeter", +} \ No newline at end of file diff --git a/packages/hardhat-zksync-verify/test/fixture-projects/localGreeter/contracts/Contract.sol b/packages/hardhat-zksync-verify/test/fixture-projects/localGreeter/contracts/Contract.sol new file mode 100644 index 000000000..efcb9fa85 --- /dev/null +++ b/packages/hardhat-zksync-verify/test/fixture-projects/localGreeter/contracts/Contract.sol @@ -0,0 +1,5 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.16; + +contract Contract { +} \ No newline at end of file diff --git a/packages/hardhat-zksync-verify/test/helpers.ts b/packages/hardhat-zksync-verify/test/helpers.ts index 899a4ab39..f527b6805 100644 --- a/packages/hardhat-zksync-verify/test/helpers.ts +++ b/packages/hardhat-zksync-verify/test/helpers.ts @@ -15,7 +15,7 @@ export function useEnvironment(fixtureProjectName: string, networkName = 'hardha process.env.HARDHAT_NETWORK = networkName; this.env = require('hardhat'); - this.env.run(TASK_CLEAN); + const _ = this.env.run(TASK_CLEAN); }); afterEach('Resetting hardhat', function () { diff --git a/packages/hardhat-zksync-verify/test/tests/plugin.test.ts b/packages/hardhat-zksync-verify/test/tests/plugin.test.ts new file mode 100644 index 000000000..f93cc1d5e --- /dev/null +++ b/packages/hardhat-zksync-verify/test/tests/plugin.test.ts @@ -0,0 +1,319 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { Artifacts, ResolvedFile } from 'hardhat/types'; +import { fail } from 'assert'; +import path from 'path'; +import { + checkContractName, + checkVerificationStatus, + flattenContractFile, + getLibraries, + getSolidityStandardJsonInput, + inferContractArtifacts, +} from '../../src/plugin'; +import { Bytecode } from '../../src/solc/bytecode'; +import * as bytecodes from '../../src/solc/bytecode'; +import { useEnvironment } from '../helpers'; +import { NO_MATCHING_CONTRACT } from '../../src/constants'; +import { ContractInformation } from '../../src/solc/types'; +import { VerificationStatusResponse } from '../../src/zksync-block-explorer/verification-status-response'; + +describe('Plugin', () => { + const artifacts: Artifacts = { + readArtifact: sinon.stub().resolves({}), + readArtifactSync: sinon.stub().returns({}), + artifactExists: sinon.stub().resolves(true), + getAllFullyQualifiedNames: sinon.stub().resolves([]), + getBuildInfo: sinon.stub().resolves({ solcVersion: '0.8.0' }), + getBuildInfoSync: sinon.stub().returns({ solcVersion: '0.8.0' }), + getArtifactPaths: sinon.stub().resolves([]), + getDebugFilePaths: sinon.stub().resolves([]), + getBuildInfoPaths: sinon.stub().resolves([]), + saveArtifactAndDebugFile: sinon.stub().resolves(), + saveBuildInfo: sinon.stub().resolves(), + formArtifactPathFromFullyQualifiedName: sinon.stub().returns(''), + }; + + beforeEach(() => { + sinon.restore(); + }); + + function extractMatchingContractInformation(contractInformation: any): Promise<ContractInformation> { + return contractInformation; + } + + const bytecode: Bytecode = new Bytecode('0x1234567890'); + + describe('inferContractArtifacts', () => { + it('should throw ZkSyncVerifyPluginError when no matching contract is found', async () => { + sinon.stub(bytecodes, 'extractMatchingContractInformation').returns( + extractMatchingContractInformation({ + contractName: 'Contract', + sourceName: 'contracts/Contract.sol', + compilerInput: { + language: 'Solidity', + sources: { + 'contracts/Contract.sol': { + content: 'contract Contract {}', + }, + }, + settings: { + optimizer: { + enabled: true, + }, + outputSelection: { + '*': { + '*': ['evm'], + }, + }, + }, + }, + contractOutput: { + abi: [], + metadata: { + zk_version: 'v0.1.0', + solc_metadata: '0x1234567890', + optimizer_settings: '0x1234567890', + }, + evm: { + bytecode: { + linkReferences: {}, + object: '0x1234567890', + opcodes: '0x1234567890', + sourceMap: '0x1234567890', + }, + deployedBytecode: { + linkReferences: {}, + object: '0x1234567890', + opcodes: '0x1234567890', + sourceMap: '0x1234567890', + }, + methodIdentifiers: {}, + }, + }, + solcVersion: '0.8.0', + }), + ); + + try { + await inferContractArtifacts(artifacts, [], bytecode); + fail('Expected an error to be thrown'); + } catch (error: any) { + expect(error.message).to.equal(NO_MATCHING_CONTRACT); + } + }); + it('should return the contract information when a matching contract is found', async () => { + sinon.stub(bytecodes, 'extractMatchingContractInformation').returns( + extractMatchingContractInformation({ + contractName: 'Contract', + sourceName: 'contracts/Contract.sol', + compilerInput: { + language: 'Solidity', + sources: { + 'contracts/Contract.sol': { + content: 'contract Contract {}', + }, + }, + settings: { + optimizer: { + enabled: true, + }, + outputSelection: { + '*': { + '*': ['evm'], + }, + }, + }, + }, + contractOutput: { + abi: [], + metadata: { + zk_version: 'v0.1.0', + solc_metadata: '0x1234567890', + optimizer_settings: '0x1234567890', + }, + evm: { + bytecode: { + linkReferences: {}, + object: '0x1234567890', + opcodes: '0x1234567890', + sourceMap: '0x1234567890', + }, + deployedBytecode: { + linkReferences: {}, + object: '0x1234567890', + opcodes: '0x1234567890', + sourceMap: '0x1234567890', + }, + methodIdentifiers: {}, + }, + }, + solcVersion: '0.8.0', + }), + ); + + artifacts.getAllFullyQualifiedNames = sinon.stub().resolves(['contracts/Contract.sol:Contract']); + + const matchingCompilerVersions = ['0.8.0']; + + const contractInformation: ContractInformation = await inferContractArtifacts( + artifacts, + matchingCompilerVersions, + bytecode, + ); + + expect(contractInformation.contractName).to.equal('Contract'); + expect(contractInformation.sourceName).to.equal('contracts/Contract.sol'); + expect(contractInformation.contractOutput.evm.bytecode.object).to.equal('0x1234567890'); + }); + }); + + describe('flattenContractFile', async function () { + useEnvironment('localGreeter'); + + it('should return the flattened source code', async function () { + const filePath = 'contracts/Contract.sol'; + + const flattenedSourceCode = await flattenContractFile(this.env, filePath); + + expect(flattenedSourceCode).to.includes('contract Contract {'); + }); + }); + + describe('checkContractName', async function () { + it('should throw ZkSyncVerifyPluginError when contractFQN is not a valid fully qualified name', async function () { + artifacts.artifactExists = sinon.stub().resolves(false); + artifacts.getAllFullyQualifiedNames = sinon.stub().resolves(['contracts/Contract.sol:Contract']); + const contractFQN = 'invalid_contract_name'; + + try { + await checkContractName(artifacts, contractFQN); + fail('Expected an error to be thrown'); + } catch (error: any) { + expect(error.message).to + .equal(`A valid fully qualified name was expected. Fully qualified names look like this: "contracts/AContract.sol:TheContract" +Instead, this name was received: ${contractFQN}`); + } + }); + + it('should throw ZkSyncVerifyPluginError when the contract does not exist', async function () { + artifacts.artifactExists = sinon.stub().resolves(false); + const contractFQN = 'contracts/Contract.sol:Contract'; + + try { + await checkContractName(artifacts, contractFQN); + fail('Expected an error to be thrown'); + } catch (error: any) { + expect(error.message).to.equal( + 'The contract contracts/Contract.sol:Contract is not present in your project.', + ); + } + }); + + it('should not throw an error when the contract exists', async function () { + artifacts.artifactExists = sinon.stub().resolves(true); + const contractFQN = 'contracts/Contract.sol:Contract'; + + try { + await checkContractName(artifacts, contractFQN); + } catch (error: any) { + fail('Expected no error to be thrown'); + } + }); + }); + + describe('getSolidityStandardJsonInput', async function () { + useEnvironment('localGreeter'); + it('should return the Solidity standard JSON input', async function () { + const resolvedFiles: ResolvedFile[] = [ + { + sourceName: 'contracts/Contract.sol', + absolutePath: 'contracts/Contract.sol', + lastModificationDate: new Date(), + contentHash: '0x1234567890', + getVersionedName: () => 'contracts/Contract.sol', + content: { + rawContent: 'contract Contract {}', + imports: [], + versionPragmas: ['0.8.0'], + }, + }, + ]; + + const solidityStandardJsonInput = getSolidityStandardJsonInput(this.env, resolvedFiles); + + expect(solidityStandardJsonInput.language).to.equal('Solidity'); + expect(solidityStandardJsonInput.sources['contracts/Contract.sol'].content).to.equal( + 'contract Contract {}', + ); + expect(solidityStandardJsonInput.settings.optimizer.enabled).to.equal(true); + expect(solidityStandardJsonInput.settings.areLibrariesMissing).to.equal(false); + }); + }); + + describe('getLibraries', async function () { + it('should throw ZkSyncVerifyPluginError when importing the libraries module fails', async function () { + const librariesModule = '../args.js'; + + try { + await getLibraries(librariesModule); + fail('Expected an error to be thrown'); + } catch (error: any) { + expect(error.message).to.includes( + `Importing the module for the libraries dictionary failed. Reason: Cannot find module '${path.resolve( + process.cwd(), + librariesModule, + )}'`, + ); + } + }); + + it('should return the libraries object when importing the libraries module succeeds', async function () { + const librariesModule = '../localGreeter/args.js'; + + const libraries = await getLibraries(librariesModule); + + expect(libraries).to.deep.equal({ + name: 'localGreeter', + }); + }); + }); + + describe('checkVerificationStatus', async function () { + useEnvironment('localGreeter'); + + it('should throw ZkSyncVerifyPluginError when backend verification error exists', async function () { + sinon.stub(VerificationStatusResponse.prototype, 'errorExists').returns(true); + + const args = { + verificationId: 123, + }; + + try { + await checkVerificationStatus(args, this.env); + fail('Expected an error to be thrown'); + } catch (error: any) { + expect(error.message).to.equal( + 'Backend verification error: Deployed bytecode is not equal to generated one from given source', + ); + } + }); + + it.skip('should log a success message and return true when verification is successful', async function () { + sinon.stub(VerificationStatusResponse.prototype, 'errorExists').returns(false); + + const args = { + verificationId: 123, + }; + + const consoleInfoSpy = sinon.spy(console, 'info'); + const result = await checkVerificationStatus(args, this.env); + + sinon.assert.calledWith( + consoleInfoSpy, + '\u001b[32mContract successfully verified on zkSync block explorer!\u001b[39m', + ); + expect(result).to.equal(true); + }); + }); +}); diff --git a/packages/hardhat-zksync-verify/test/tests/solc/bytecode.test.ts b/packages/hardhat-zksync-verify/test/tests/solc/bytecode.test.ts new file mode 100644 index 000000000..5391f5bd6 --- /dev/null +++ b/packages/hardhat-zksync-verify/test/tests/solc/bytecode.test.ts @@ -0,0 +1,149 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { Bytecode, compareBytecode, extractMatchingContractInformation } from '../../../src/solc/bytecode'; +import * as bytecodes from '../../../src/solc/bytecode'; + +describe('compareBytecode', () => { + beforeEach(() => { + sinon.restore(); + }); + + it('should return null when the lengths of deployed and runtime executable sections are different', async () => { + const deployedBytecode: Bytecode = new Bytecode('deployedBytecode'); + const runtimeBytecodeSymbols = { + object: 'runtimeBytecode', + }; + + const result = await compareBytecode(deployedBytecode, runtimeBytecodeSymbols as any); + + expect(result).to.equal(null); + }); + + it('should return null when the normalized bytecode does not match the reference bytecode', async () => { + const deployedBytecode: Bytecode = new Bytecode('deployedBytecode'); + const runtimeBytecodeSymbols = { + object: 'runtimeBytecode', + }; + + // Stub the normalizeBytecode function to return different normalized bytecodes + const normalizeBytecodeStub = sinon.stub().resolves({ + normalizedBytecode: 'differentNormalizedBytecode', + }); + sinon.replace( + // Replace the import of normalizeBytecode with the stub + bytecodes, + 'normalizeBytecode', + normalizeBytecodeStub, + ); + + const result = await compareBytecode(deployedBytecode, runtimeBytecodeSymbols as any); + + expect(result).to.equal(null); + }); + + it('should return the normalized bytecode when it matches the reference bytecode', async () => { + const deployedBytecode: Bytecode = new Bytecode('deployedBytecode'); + const runtimeBytecodeSymbols = { + object: 'deployedBytecode', + }; + + const result = await compareBytecode(deployedBytecode, runtimeBytecodeSymbols as any); + + expect(result).to.deep.equal({ + normalizedBytecode: 'deployedBytecode', + }); + }); +}); + +describe('extractMatchingContractInformation', () => { + it("should return null when the contract does not have 'evm' property", async () => { + const sourceName = 'sourceName'; + const contractName = 'contractName'; + const buildInfo = { + output: { + contracts: { + [sourceName]: { + [contractName]: {}, + }, + }, + }, + }; + const deployedBytecode: Bytecode = new Bytecode('deployedBytecode'); + + const result = await extractMatchingContractInformation( + sourceName, + contractName, + buildInfo as any, + deployedBytecode, + ); + + expect(result).to.equal(null); + }); + + it('should return null when the runtime bytecode symbols are null', async () => { + const sourceName = 'sourceName'; + const contractName = 'contractName'; + const buildInfo = { + output: { + contracts: { + [sourceName]: { + [contractName]: { + evm: { + bytecode: null, + }, + }, + }, + }, + }, + }; + const deployedBytecode: Bytecode = new Bytecode('deployedBytecode'); + + const result = await extractMatchingContractInformation( + sourceName, + contractName, + buildInfo as any, + deployedBytecode, + ); + + expect(result).to.equal(null); + }); + + it('should return the contract information when the bytecode is matched', async () => { + const sourceName = 'sourceName'; + const contractName = 'contractName'; + const buildInfo = { + input: 'compilerInput', + output: { + contracts: { + [sourceName]: { + [contractName]: { + evm: { + bytecode: { + object: 'deployedBytecode', + }, + }, + }, + }, + }, + }, + solcVersion: 'solcVersion', + }; + const deployedBytecode: Bytecode = new Bytecode('deployedBytecode'); + + const result = await bytecodes.extractMatchingContractInformation( + sourceName, + contractName, + buildInfo as any, + deployedBytecode, + ); + + expect(result).to.deep.equal({ + normalizedBytecode: 'deployedBytecode', + compilerInput: 'compilerInput', + contractOutput: buildInfo.output.contracts[sourceName][contractName], + solcVersion: 'solcVersion', + sourceName, + contractName, + }); + }); +}); diff --git a/packages/hardhat-zksync-verify/test/tests/task-actions.test.ts b/packages/hardhat-zksync-verify/test/tests/task-actions.test.ts new file mode 100644 index 000000000..15530ef71 --- /dev/null +++ b/packages/hardhat-zksync-verify/test/tests/task-actions.test.ts @@ -0,0 +1,682 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { fail } from 'assert'; +import { TASK_COMPILE, TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH } from 'hardhat/builtin-tasks/task-names'; +import { + getCompilerVersions, + getConstructorArguments, + getContractInfo, + verify, + verifyContract, +} from '../../src/task-actions'; +import { + NO_MATCHING_CONTRACT, + NO_VERIFIABLE_ADDRESS_ERROR, + TASK_CHECK_VERIFICATION_STATUS, + TASK_VERIFY_GET_COMPILER_VERSIONS, + TASK_VERIFY_GET_CONSTRUCTOR_ARGUMENTS, + TASK_VERIFY_GET_CONTRACT_INFORMATION, + TASK_VERIFY_VERIFY, +} from '../../src/constants'; +import * as utils from '../../src/utils'; +import * as metadata from '../../src/solc/metadata'; +import * as service from '../../src/zksync-block-explorer/service'; +import * as plugin from '../../src/plugin'; +import * as bytecode from '../../src/solc/bytecode'; + +describe('verifyContract', async function () { + it('should call runSuper if zksync is false', async function () { + const runSuperStub = sinon.stub().resolves(0); + const hre = { + network: { + zksync: false, + }, + run: sinon.stub(), + }; + + await verifyContract({}, hre as any, runSuperStub as any); + expect(runSuperStub.calledOnce).to.equal(true); + }); + + it('should throw an error if the address is invalid', async function () { + const runSuperStub = sinon.stub().resolves(0); + const hre = { + network: { + zksync: true, + }, + run: sinon.stub(), + }; + + try { + await verifyContract({ address: 'invalid_address' }, hre as any, runSuperStub as any); + fail('Expected an error to be thrown'); + } catch (error: any) { + expect(error.message).to.equal('invalid_address is an invalid address.'); + } + }); + + it('should call runSuper if zksync is false', async function () { + const runSuperStub = sinon.stub().resolves(0); + const hre = { + network: { + zksync: false, + }, + run: sinon.stub(), + }; + + await verifyContract({}, hre as any, runSuperStub as any); + expect(runSuperStub.calledOnce).to.equal(true); + }); + + it('should throw an error if the address is invalid', async function () { + const runSuperStub = sinon.stub().resolves(0); + const hre = { + network: { + zksync: true, + }, + run: sinon.stub(), + }; + + try { + await verifyContract({ address: 'invalid_address' }, hre as any, runSuperStub as any); + fail('Expected an error to be thrown'); + } catch (error: any) { + expect(error.message).to.equal('invalid_address is an invalid address.'); + } + }); + + it('should call run with the correct arguments if zksync is true and address is valid', async function () { + sinon.stub(utils, 'retrieveContractBytecode').resolves('0x1234567890'); + sinon.stub(metadata, 'inferSolcVersion').resolves('0.8.0'); + sinon.stub(service, 'getSupportedCompilerVersions').resolves(['0.8.0']); + sinon.stub(plugin, 'getSolidityStandardJsonInput').resolves({ + language: 'Solidity', + sources: { + 'contracts/Contract.sol': { + content: 'contract Contract {}', + }, + }, + }); + sinon.stub(service, 'verifyContractRequest').resolves({ + status: 200, + message: '1', + isOk: () => true, + }); + + const runSuperStub = sinon.stub().resolves(0); + const hre = { + network: { + config: { + url: 'http://localhost:3000', + }, + zksync: true, + verifyURL: 'http://localhost:3000/verify', + }, + run: sinon + .stub() + .onThirdCall() + .resolves({ + contractName: 'Contract', + sourceName: 'contracts/Contract.sol', + compilerInput: { + language: 'Solidity', + sources: { + 'contracts/Contract.sol': { + content: 'contract Contract {}', + }, + }, + settings: { + optimizer: { + enabled: true, + }, + outputSelection: { + '*': { + '*': ['evm'], + }, + }, + }, + }, + contractOutput: { + abi: [], + metadata: { + zk_version: '0.1.0', + solc_metadata: '0x1234567890', + optimizer_settings: '0x1234567890', + }, + evm: { + bytecode: { + linkReferences: {}, + object: '0x1234567890', + opcodes: '0x1234567890', + sourceMap: '0x1234567890', + }, + deployedBytecode: { + linkReferences: {}, + object: '0x1234567890', + opcodes: '0x1234567890', + sourceMap: '0x1234567890', + }, + methodIdentifiers: {}, + }, + }, + solcVersion: '0.8.0', + }) + .onCall(3) + .resolves({ + getResolvedFiles: sinon.stub().resolves(), + }), + }; + + const args = { + address: '0x1234567890123456789012345678901234567890', + constructorArguments: [], + contract: 'contract', + libraries: 'libraries', + noCompile: false, + }; + + await verifyContract(args, hre as any, runSuperStub as any); + expect(runSuperStub.calledOnce).to.equal(false); + expect(hre.run.firstCall.args[0]).to.equal(TASK_VERIFY_GET_COMPILER_VERSIONS); + expect(hre.run.secondCall.args[0]).to.equal(TASK_COMPILE); + expect(hre.run.thirdCall.args[0]).to.equal(TASK_VERIFY_GET_CONTRACT_INFORMATION); + expect(hre.run.getCall(3).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH); + expect(hre.run.getCall(4).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); + }); +}); +describe('getCompilerVersions', async function () { + it('should call runSuper if zksync is false', async function () { + const runSuperStub = sinon.stub().resolves([]); + const hre = { + network: { + zksync: false, + }, + config: { + solidity: { + compilers: [], + }, + }, + run: sinon.stub(), + }; + + const result = await getCompilerVersions({}, hre as any, runSuperStub as any); + expect(result).to.deep.equal([]); + expect(runSuperStub.calledOnce).to.equal(true); + }); + + it('should return compiler versions if zksync is true', async function () { + const runSuperStub = sinon.stub().resolves([]); + const hre = { + network: { + zksync: true, + }, + config: { + solidity: { + compilers: [{ version: '0.8.0' }, { version: '0.7.0' }], + overrides: { + 'contracts/Contract.sol': { version: '0.6.0' }, + }, + }, + }, + run: sinon.stub(), + }; + + const result = await getCompilerVersions({}, hre as any, runSuperStub as any); + expect(result).to.deep.equal(['0.8.0', '0.7.0', '0.6.0']); + expect(runSuperStub.called).to.equal(false); + }); +}); + +describe('verify', async function () { + afterEach(() => { + sinon.restore(); + }); + + it('should call runSuper if zksync is false', async function () { + const runSuperStub = sinon.stub().resolves(0); + const hre = { + network: { + zksync: false, + verifyURL: 'http://localhost:3000/verify', + }, + run: sinon.stub().resolves({}), + }; + + await verify( + { + address: '0x1234567890', + constructorArgs: '', + contract: '', + constructorArgsParams: [], + libraries: '', + noCompile: false, + }, + hre as any, + runSuperStub as any, + ); + + expect(runSuperStub.calledOnce).to.equal(true); + }); + + it('should throw an error if address is undefined', async function () { + sinon.stub(plugin, 'getLibraries').resolves({}); + const runSuperStub = sinon.stub().resolves(0); + const hre = { + network: { + zksync: true, + verifyURL: 'http://localhost:3000/verify', + }, + run: sinon.stub().resolves({}), + }; + + try { + await verify( + { + address: undefined as any, + constructorArgs: '', + contract: '', + constructorArgsParams: [], + libraries: '', + noCompile: false, + }, + hre as any, + runSuperStub as any, + ); + fail('Expected an error to be thrown'); + } catch (error: any) { + expect(error.message).to.equal(NO_VERIFIABLE_ADDRESS_ERROR); + } + }); + + it('should call run with the correct arguments', async function () { + sinon.stub(plugin, 'getLibraries').resolves({}); + const runSuperStub = sinon.stub().resolves(0); + const hre = { + network: { + zksync: true, + verifyURL: 'http://localhost:3000/verify', + }, + run: sinon.stub().resolves({}), + }; + + await verify( + { + address: '0x1234567890', + constructorArgs: '', + contract: 'Contract', + constructorArgsParams: [], + libraries: '', + noCompile: false, + }, + hre as any, + runSuperStub as any, + ); + + expect(runSuperStub.calledOnce).to.equal(false); + expect(hre.run.firstCall.args[0]).to.equal(TASK_VERIFY_GET_CONSTRUCTOR_ARGUMENTS); + expect(hre.run.secondCall.args[0]).to.equal(TASK_VERIFY_VERIFY); + expect(hre.run.secondCall.args[1]).to.deep.equal({ + address: '0x1234567890', + constructorArguments: {}, + contract: 'Contract', + libraries: {}, + noCompile: false, + }); + }); +}); +describe('getConstructorArguments', async function () { + afterEach(() => { + sinon.restore(); + }); + + it('should call runSuper if zksync is false', async function () { + const args = {}; + const hre = { + network: { + zksync: false, + }, + run: sinon.stub().resolves(), + }; + const runSuperStub = sinon.stub().resolves(); + + await getConstructorArguments(args, hre as any, runSuperStub as any); + + expect(runSuperStub.calledOnce).to.equal(true); + }); + + it('should return constructorArgsParams if constructorArgsModule is not a string', async function () { + const args = { + constructorArgsModule: 123, + constructorArgsParams: [1, 2, 3], + }; + const hre = { + network: { + zksync: true, + }, + }; + const runSuperStub = sinon.stub().resolves(); + + const result = await getConstructorArguments(args, hre as any, runSuperStub as any); + + expect(result).to.deep.equal([1, 2, 3]); + }); + + it('should import constructorArgsModule and return constructorArguments if zksync is true and constructorArgsModule is a string', async function () { + const args = { + constructorArgsModule: 'path/to/module', + }; + const hre = { + network: { + zksync: true, + }, + }; + const runSuperStub = sinon.stub().resolves(); + const importStub = sinon.stub(utils, 'extractModule').resolves('0x1234567890'); + + const result = await getConstructorArguments(args, hre as any, runSuperStub as any); + + expect(importStub.calledOnce).to.equal(true); + expect(importStub.firstCall.args[0]).to.includes('path/to/module'); + expect(result).to.deep.equal('0x1234567890'); + }); + + it('should throw an error if importing constructorArgsModule fails', async function () { + const args = { + constructorArgsModule: 'path/to/module', + }; + const hre = { + network: { + zksync: true, + }, + }; + + const runSuperStub = sinon.stub().resolves(); + sinon.stub(utils, 'extractModule').throws(new Error('Import error')); + + try { + await getConstructorArguments(args, hre as any, runSuperStub as any); + fail('Expected an error to be thrown'); + } catch (error: any) { + expect(error.message).to.includes('Importing the module for the constructor arguments list failed.'); + } + }); +}); + +describe('getContractInfo', async function () { + afterEach(() => { + sinon.restore(); + }); + + it('should call runSuper if zksync is false', async function () { + const args = { + contractFQN: 'Contract', + deployedBytecode: '0x1234567890', + matchingCompilerVersions: [], + libraries: {}, + }; + const hre = { + network: { + zksync: false, + }, + }; + const runSuperStub = sinon.stub().resolves({}); + + await getContractInfo(args, hre as any, runSuperStub as any); + + expect(runSuperStub.calledOnce).to.equal(true); + expect(runSuperStub.firstCall.args[0]).to.deep.equal(args); + }); + + it('should throw an error if contractFQN is undefined', async function () { + const args = { + contractFQN: undefined, + deployedBytecode: '0x1234567890', + matchingCompilerVersions: [], + libraries: {}, + }; + const hre = { + artifacts: sinon.stub().resolves({}), + network: { + zksync: true, + }, + }; + const runSuperStub = sinon.stub().resolves({}); + sinon.stub(plugin, 'inferContractArtifacts').throws(new Error('contractFQN is undefined')); + + try { + await getContractInfo(args, hre as any, runSuperStub as any); + fail('Expected an error to be thrown'); + } catch (error: any) { + expect(error.message).to.equal('contractFQN is undefined'); + } + }); + + it('should throw an error if no matching contract is found', async function () { + const args = { + contractFQN: 'contracts/Contract2.sol:Contract2', + deployedBytecode: '0x1234567890', + matchingCompilerVersions: [], + libraries: {}, + }; + const hre = { + artifacts: { + getAllFullyQualifiedNames: sinon.stub().resolves(['contracts/Contract.sol:Contract']), + getBuildInfo: sinon.stub().resolves({ + output: { + contracts: { + 'contracts/Contract.sol': { + Contract: { + evm: { + bytecode: { + object: '0x1234567890', + }, + }, + }, + }, + }, + }, + solcVersion: '0.8.0', + }), + }, + network: { + zksync: true, + }, + }; + const runSuperStub = sinon.stub().resolves({}); + sinon.stub(bytecode, 'extractMatchingContractInformation').resolves(); + + try { + await getContractInfo(args, hre as any, runSuperStub as any); + fail('Expected an error to be thrown'); + } catch (error: any) { + expect(error.message).to.equal(NO_MATCHING_CONTRACT); + } + }); + + it('should return contract information if contractFQN is defined and matching contract is found', async function () { + const args = { + contractFQN: 'contracts/Contract.sol:Contract', + deployedBytecode: '0x1234567890', + matchingCompilerVersions: [], + libraries: {}, + }; + const hre = { + artifacts: { + getAllFullyQualifiedNames: sinon.stub().resolves(['contracts/Contract.sol:Contract']), + getBuildInfo: sinon.stub().resolves({ + output: { + contracts: { + 'contracts/Contract.sol': { + Contract: { + evm: { + bytecode: { + object: '0x1234567890', + }, + }, + }, + }, + }, + }, + solcVersion: '0.8.0', + }), + }, + network: { + zksync: true, + }, + }; + + const contractInformation = { + contractName: 'Contract', + sourceName: 'contracts/Contract.sol', + compilerInput: { + language: 'Solidity', + sources: { + 'contracts/Contract.sol': { + content: 'contract Contract {}', + }, + }, + settings: { + optimizer: { + enabled: true, + }, + outputSelection: { + '*': { + '*': ['evm'], + }, + }, + }, + }, + contractOutput: { + abi: [], + metadata: { + zk_version: 'v0.1.0', + solc_metadata: '0x1234567890', + optimizer_settings: '0x1234567890', + }, + evm: { + bytecode: { + linkReferences: {}, + object: '0x1234567890', + opcodes: '0x1234567890', + sourceMap: '0x1234567890', + }, + deployedBytecode: { + linkReferences: {}, + object: '0x1234567890', + opcodes: '0x1234567890', + sourceMap: '0x1234567890', + }, + methodIdentifiers: {}, + }, + }, + solcVersion: '0.8.0', + }; + + const runSuperStub = sinon.stub().resolves({}); + const extractMatchingContractInformationStub = sinon + .stub(bytecode, 'extractMatchingContractInformation') + .resolves(contractInformation); + + const result = await getContractInfo(args, hre as any, runSuperStub as any); + + expect(result).to.deep.equal(contractInformation); + expect(runSuperStub.called).to.equal(false); + expect(extractMatchingContractInformationStub.calledOnce).to.equal(true); + expect(extractMatchingContractInformationStub.firstCall.args[0]).to.equal('contracts/Contract.sol'); + expect(extractMatchingContractInformationStub.firstCall.args[1]).to.equal('Contract'); + expect(extractMatchingContractInformationStub.firstCall.args[3]).to.equal(args.deployedBytecode); + }); + + it('should return contract information if contractFQN is undefined and matching contract is found', async function () { + const args = { + contractFQN: undefined, + deployedBytecode: '0x1234567890', + matchingCompilerVersions: [], + libraries: {}, + }; + + const hre = { + artifacts: { + getAllFullyQualifiedNames: sinon.stub().resolves(['contracts/Contract.sol:Contract']), + getBuildInfo: sinon.stub().resolves({ + output: { + contracts: { + 'contracts/Contract.sol': { + Contract: { + evm: { + bytecode: { + object: '0x1234567890', + }, + }, + }, + }, + }, + }, + solcVersion: '0.8.0', + }), + }, + network: { + zksync: true, + }, + }; + + const contractInformation = { + contractName: 'Contract', + sourceName: 'contracts/Contract.sol', + compilerInput: { + language: 'Solidity', + sources: { + 'contracts/Contract.sol': { + content: 'contract Contract {}', + }, + }, + settings: { + optimizer: { + enabled: true, + }, + outputSelection: { + '*': { + '*': ['evm'], + }, + }, + }, + }, + contractOutput: { + abi: [], + metadata: { + zk_version: '0.1.0', + solc_metadata: '0x1234567890', + optimizer_settings: '0x1234567890', + }, + evm: { + bytecode: { + linkReferences: {}, + object: '0x1234567890', + opcodes: '0x1234567890', + sourceMap: '0x1234567890', + }, + deployedBytecode: { + linkReferences: {}, + object: '0x1234567890', + opcodes: '0x1234567890', + sourceMap: '0x1234567890', + }, + methodIdentifiers: {}, + }, + }, + solcVersion: '0.8.0', + }; + const runSuperStub = sinon.stub().resolves({}); + const inferContractArtifactsStub = sinon.stub(plugin, 'inferContractArtifacts').resolves(contractInformation); + + const result = await getContractInfo(args, hre as any, runSuperStub as any); + + expect(result).to.deep.equal(contractInformation); + expect(runSuperStub.called).to.equal(false); + expect(hre.artifacts.getBuildInfo.called).to.equal(false); + expect(inferContractArtifactsStub.calledOnce).to.equal(true); + expect(inferContractArtifactsStub.firstCall.args[0]).to.equal(hre.artifacts); + expect(inferContractArtifactsStub.firstCall.args[1]).to.equal(args.matchingCompilerVersions); + expect(inferContractArtifactsStub.firstCall.args[2]).to.equal(args.deployedBytecode); + }); +}); diff --git a/packages/hardhat-zksync-verify/test/tests.ts b/packages/hardhat-zksync-verify/test/tests/tests.ts similarity index 84% rename from packages/hardhat-zksync-verify/test/tests.ts rename to packages/hardhat-zksync-verify/test/tests/tests.ts index e1f384823..ace4794e4 100644 --- a/packages/hardhat-zksync-verify/test/tests.ts +++ b/packages/hardhat-zksync-verify/test/tests/tests.ts @@ -1,9 +1,7 @@ import { assert } from 'chai'; -import { useEnvironment } from './helpers'; +import { useEnvironment } from '../helpers'; describe('verify plugin', async function () { - const sourceName: string = 'contracts/Greeter.sol'; - const contractName: string = 'Greeter'; const testnetVerifyURL = 'https://explorer.sepolia.era.zksync.dev/contract_verification'; describe('Testnet verifyURL extraction from config', async function () { diff --git a/packages/hardhat-zksync-verify/test/tests/utils.test.ts b/packages/hardhat-zksync-verify/test/tests/utils.test.ts new file mode 100644 index 000000000..82a314d3d --- /dev/null +++ b/packages/hardhat-zksync-verify/test/tests/utils.test.ts @@ -0,0 +1,272 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import axion from 'axios'; +import { + delay, + encodeArguments, + executeVeificationWithRetry, + handleAxiosError, + parseWrongConstructorArgumentsError, + removeMultipleSubstringOccurrences, +} from '../../src/utils'; +import * as service from '../../src/zksync-block-explorer/service'; + +describe('executeVeificationWithRetry', () => { + let checkVerificationStatusServiceStub: sinon.SinonStub; + + beforeEach(() => { + sinon.restore(); + }); + + it('should return the verification status response when verification is successful', async () => { + const requestId = 123; + const verifyURL = 'https://example.com/verify'; + const response = { + isVerificationSuccess: sinon.stub().returns(true), + isVerificationFailure: sinon.stub().returns(false), + }; + + checkVerificationStatusServiceStub = sinon + .stub(service, 'checkVerificationStatusService') + .resolves(response as any); + + const result = await executeVeificationWithRetry(requestId, verifyURL); + + expect(result).to.equal(response); + expect(checkVerificationStatusServiceStub.calledOnceWith(requestId, verifyURL)).to.equal(true); + }); + + it('should return the verification status response when verification is failed', async () => { + const requestId = 123; + const verifyURL = 'https://example.com/verify'; + const response = { + isVerificationSuccess: sinon.stub().returns(false), + isVerificationFailure: sinon.stub().returns(true), + }; + + checkVerificationStatusServiceStub = sinon + .stub(service, 'checkVerificationStatusService') + .resolves(response as any); + + const result = await executeVeificationWithRetry(requestId, verifyURL); + + expect(result).to.equal(response); + expect(checkVerificationStatusServiceStub.calledOnceWith(requestId, verifyURL)).to.equal(true); + }); + + it('should return undefined when max retries exceeded', async () => { + const requestId = 123; + const verifyURL = 'https://example.com/verify'; + const maxRetries = 2; + const delayInMs = 100; + + const response = { + isVerificationSuccess: sinon.stub().returns(false), + isVerificationFailure: sinon.stub().returns(false), + }; + + checkVerificationStatusServiceStub = sinon + .stub(service, 'checkVerificationStatusService') + .resolves(response as any); + + const result = await executeVeificationWithRetry(requestId, verifyURL, maxRetries, delayInMs); + + expect(result).to.equal(undefined); + expect(checkVerificationStatusServiceStub.callCount).to.equal(maxRetries + 1); + expect(checkVerificationStatusServiceStub.calledWith(requestId, verifyURL)).to.equal(true); + }); +}); + +describe('handleAxiosError', () => { + beforeEach(() => { + sinon.restore(); + }); + + it('should throw an error with the Axios error details', () => { + const error = { + code: 'SOME_CODE', + response: { + data: 'Some error message', + }, + }; + + sinon.stub(axion, 'isAxiosError').returns(true); + + expect(() => handleAxiosError(error)).to.throw( + `Axios error (code: SOME_CODE) during the contract verification request\n Reason: ${error.response?.data}`, + ); + }); + + it('should throw a ZkSyncVerifyPluginError with the error message', () => { + const error = 'Some error message'; + + expect(() => handleAxiosError(error)).to.throw( + `Failed to send contract verification request\n Reason: ${error}`, + ); + }); +}); + +describe('delay', () => { + it('should delay for the specified amount of time', async () => { + const ms = 1000; + const startTime = Date.now(); + await delay(ms); + const endTime = Date.now(); + const elapsedTime = endTime - startTime; + expect(elapsedTime).to.be.at.least(ms); + }); +}); + +describe('encodeArguments', () => { + it('should encode constructor arguments correctly', async () => { + const abi = [ + { + type: 'constructor', + inputs: [ + { type: 'string', name: 'name' }, + { type: 'uint256', name: 'age' }, + ], + }, + ]; + const constructorArgs = ['John Doe', 25]; + const encodedData = + '0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001900000000000000000000000000000000000000000000000000000000000000084a6f686e20446f65000000000000000000000000000000000000000000000000'; + + const result = await encodeArguments(abi, constructorArgs); + + expect(result).to.equal(encodedData); + }); + + it('should throw an error when constructor arguments are incorrect', async () => { + const abi = [ + { + type: 'constructor', + inputs: [ + { type: 'string', name: 'name' }, + { type: 'uint256', name: 'age' }, + ], + }, + ]; + const constructorArgs = ['John Doe', '25', '43']; + + try { + await encodeArguments(abi, constructorArgs); + // Fail the test if no error is thrown + expect.fail('Expected an error to be thrown'); + } catch (error: any) { + expect(error.message).to.equal( + 'The number of constructor arguments you provided (3) does not match the number of constructor arguments the contract has been deployed with (2).', + ); + } + }); +}); + +/* describe("retrieveContractBytecode", () => { + let providerSendStub: sinon.SinonStub; + + beforeEach(() => { + sinon.restore(); + }); + + it("should return the deployed bytecode when the address has bytecode", async () => { + const address = "0x1234567890abcdef"; + const hreNetwork = { + config: { + url: "https://example.com", + }, + name: "test-network", + }; + const bytecodeString = "0xabcdef1234567890"; + const providerResponse = `0x${bytecodeString}`; + + providerSendStub = sinon.stub().resolves(providerResponse); + sinon.stub(zk, "Provider").resolves({ + send: providerSendStub, + }); + + const result = await retrieveContractBytecode(address, hreNetwork); + + expect(result).to.equal(bytecodeString); + expect(providerSendStub.calledOnceWith("eth_getCode", [address, "latest"])).to.be.true; + }); + + it("should throw an error when the address has no bytecode", async () => { + const address = "0x1234567890abcdef"; + const hreNetwork = { + config: { + url: "https://example.com", + }, + name: "test-network", + }; + const bytecodeString = ""; + + providerSendStub = sinon.stub().resolves(bytecodeString); + sinon.stub(zk, "Provider").resolves({ + send: providerSendStub, + }); + + try { + await retrieveContractBytecode(address, hreNetwork); + // Fail the test if no error is thrown + expect.fail("Expected an error to be thrown"); + } catch (error: any) { + expect(error.message).to.equal( + `The address ${address} has no bytecode. Is the contract deployed to this network?\n The selected network is ${hreNetwork.name}.` + ); + } + }); +});*/ + +describe('removeMultipleSubstringOccurrences', () => { + it('should remove all occurrences of the specified substring', () => { + const inputString = 'Hello, World!\n Hello, World! \nHello, World!'; + const stringToRemove = 'Hello, World!'; + const expectedOutput = 'Hello, World!'; + + const result = removeMultipleSubstringOccurrences(inputString, stringToRemove); + + expect(result).to.equal(expectedOutput); + }); + + it('should handle empty input string', () => { + const inputString = ''; + const stringToRemove = 'Hello, '; + const expectedOutput = ''; + + const result = removeMultipleSubstringOccurrences(inputString, stringToRemove); + + expect(result).to.equal(expectedOutput); + }); + + it('should handle empty string to remove', () => { + const inputString = 'Hello, World!'; + const stringToRemove = ''; + const expectedOutput = 'Hello, World!'; + + const result = removeMultipleSubstringOccurrences(inputString, stringToRemove); + + expect(result).to.equal(expectedOutput); + }); + + it('should handle no occurrences of the substring', () => { + const inputString = 'Hello, World!'; + const stringToRemove = 'Foo, '; + const expectedOutput = 'Hello, World!'; + + const result = removeMultipleSubstringOccurrences(inputString, stringToRemove); + + expect(result).to.equal(expectedOutput); + }); +}); + +describe('parseWrongConstructorArgumentsError', () => { + it('should return the correct error message', () => { + const inputString = 'Error: count=2, value=5, types=[string, uint256]'; + const expectedOutput = + 'The number of constructor arguments you provided (undefined) does not match the number of constructor arguments the contract has been deployed with (undefined).'; + + const result = parseWrongConstructorArgumentsError(inputString); + + expect(result).to.equal(expectedOutput); + }); +}); diff --git a/packages/hardhat-zksync-verify/test/tests/zksync-block-explorer/service.test.ts b/packages/hardhat-zksync-verify/test/tests/zksync-block-explorer/service.test.ts new file mode 100644 index 000000000..c51029596 --- /dev/null +++ b/packages/hardhat-zksync-verify/test/tests/zksync-block-explorer/service.test.ts @@ -0,0 +1,164 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import axios from 'axios'; +import { + checkVerificationStatusService, + verifyContractRequest, + getSupportedCompilerVersions, + ZkSyncBlockExplorerResponse, +} from '../../../src/zksync-block-explorer/service'; +import { VerificationStatusResponse } from '../../../src/zksync-block-explorer/verification-status-response'; +import { ZkSyncBlockExplorerVerifyRequest } from '../../../src/zksync-block-explorer/verify-contract-request'; + +describe('ZkSyncBlockExplorer Service', () => { + describe('checkVerificationStatusService', () => { + afterEach(() => { + sinon.restore(); + }); + + it('should return the verification status response', async () => { + const requestId = 123; + const verifyURL = 'https://example.com/verify'; + + const response = { + status: 200, + data: { + status: 'successful', + message: 'Verification successful', + error: undefined, + compilationErrors: undefined, + }, + }; + + sinon.stub(axios, 'get').resolves(response); + + const result = await checkVerificationStatusService(requestId, verifyURL); + + expect(result).to.be.instanceOf(VerificationStatusResponse); + expect(result.status).to.equal(response.data.status); + expect(result.isVerificationSuccess()).to.equal(true); + }); + + it('should handle axios error', async () => { + const requestId = 123; + const verifyURL = 'https://example.com/verify'; + + const error = new Error('Network error'); + + sinon.stub(axios, 'get').rejects(error); + + try { + await checkVerificationStatusService(requestId, verifyURL); + } catch (err: any) { + expect(err.message).to.equal(err.message); + } + }); + }); + + describe('verifyContractRequest', () => { + afterEach(() => { + sinon.restore(); + }); + + it('should return the ZkSyncBlockExplorerResponse when verification is successful', async () => { + const req: ZkSyncBlockExplorerVerifyRequest = { + codeFormat: 'solidity-standard-json-input', + compilerSolcVersion: '0.8.0', + compilerZksolcVersion: '0.1.0', + contractName: 'MyContract', + constructorArguments: '[]', + contractAddress: '0x123456', + optimizationUsed: true, + sourceCode: 'pragma solidity ^0.8.0; contract MyContract {}', + }; + const verifyURL = 'https://example.com/verify'; + + const response = { + status: 200, + data: 'Verification successful', + }; + + sinon.stub(axios, 'post').resolves(response); + + const result = await verifyContractRequest(req, verifyURL); + + expect(result).to.be.instanceOf(ZkSyncBlockExplorerResponse); + expect(result.status).to.equal(response.status); + expect(result.message).to.equal(response.data); + }); + + it('should throw ZkSyncVerifyPluginError when verification fails', async () => { + const req: ZkSyncBlockExplorerVerifyRequest = { + codeFormat: 'solidity-standard-json-input', + compilerSolcVersion: '0.8.0', + compilerZksolcVersion: '0.1.0', + contractName: 'MyContract', + constructorArguments: '[]', + contractAddress: '0x123456', + optimizationUsed: true, + sourceCode: 'pragma solidity ^0.8.0; contract MyContract {}', + }; + const verifyURL = 'https://example.com/verify'; + + const response = { + status: 400, + data: 'Verification failed', + }; + + sinon.stub(axios, 'post').resolves(response); + + try { + await verifyContractRequest(req, verifyURL); + expect.fail('Expected ZkSyncVerifyPluginError to be thrown'); + } catch (error: any) { + expect(error.message).to.includes( + 'Failed to send contract verification request\n Reason: ZkSyncVerifyPluginError: Verification failed', + ); + } + }); + + it('should handle axios error', async () => { + const req: ZkSyncBlockExplorerVerifyRequest = { + codeFormat: 'solidity-standard-json-input', + compilerSolcVersion: '0.8.0', + compilerZksolcVersion: '0.1.0', + contractName: 'MyContract', + constructorArguments: '[]', + contractAddress: '0x123456', + optimizationUsed: true, + sourceCode: 'pragma solidity ^0.8.0; contract MyContract {}', + }; + const verifyURL = 'https://example.com/verify'; + + const error = new Error('Network error'); + + sinon.stub(axios, 'post').rejects(error); + + try { + await verifyContractRequest(req, verifyURL); + } catch (err: any) { + expect(err.message).to.equal(err.message); + } + }); + }); + + describe('getSupportedCompilerVersions', () => { + afterEach(() => { + sinon.restore(); + }); + + it('should return the list of supported compiler versions', async () => { + const verifyURL = 'https://example.com/verify'; + + const response = { + data: ['0.7.0', '0.8.0', '0.8.1'], + }; + + sinon.stub(axios, 'get').resolves(response); + + const result = await getSupportedCompilerVersions(verifyURL); + + expect(result).to.deep.equal(response.data); + }); + }); +}); diff --git a/packages/hardhat-zksync-vyper/.eslintrc.js b/packages/hardhat-zksync-vyper/.eslintrc.js index 56ca0b4bd..b0d17045a 100644 --- a/packages/hardhat-zksync-vyper/.eslintrc.js +++ b/packages/hardhat-zksync-vyper/.eslintrc.js @@ -4,4 +4,18 @@ module.exports = { project: `${__dirname}/eslint-tsconfig.json`, sourceType: "module", }, + rules: { + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "property", + "format": ["camelCase", "PascalCase", "snake_case"], + "leadingUnderscore": "allow", + "filter": { + "regex": "^[a-zA-Z0-9-_]+$", + "match": false + } + }, + ], + }, }; diff --git a/packages/hardhat-zksync-vyper/CHANGELOG.md b/packages/hardhat-zksync-vyper/CHANGELOG.md index 0e1233520..6df3b9a66 100644 --- a/packages/hardhat-zksync-vyper/CHANGELOG.md +++ b/packages/hardhat-zksync-vyper/CHANGELOG.md @@ -1,5 +1,13 @@ # @matterlabs/hardhat-zksync-vyper +## [1.0.5](https://github.com/matter-labs/hardhat-zksync/compare/@matterlabs/hardhat-zksync-vyper@1.0.4...@matterlabs/hardhat-zksync-vyper-v1.0.5) (2023-12-22) + + +### Fixes + +* **docs:** update readme files ([#612](https://github.com/matter-labs/hardhat-zksync/issues/612)) ([682338e](https://github.com/matter-labs/hardhat-zksync/commit/682338e60f52021206325ff6eeec2c394a118642)) +* **hardhat-zksync-vyper:** Fixed windows compile paths ([#479](https://github.com/matter-labs/hardhat-zksync/issues/479)) ([4859b29](https://github.com/matter-labs/hardhat-zksync/commit/4859b293ad53ca608df277ddb349dae6d1237394)) + ## 1.0.4 ### Patch Changes diff --git a/packages/hardhat-zksync-vyper/README.md b/packages/hardhat-zksync-vyper/README.md index f13b939bd..5a9206094 100644 --- a/packages/hardhat-zksync-vyper/README.md +++ b/packages/hardhat-zksync-vyper/README.md @@ -1,7 +1,119 @@ -# hardhat-zksync-vyper +# hardhat-zksync-vyper 🚀 [Hardhat](https://hardhat.org/) plugin that adds zkSync-specific capabilities to vyper. -Check out the documentation page for the [zksync vyper plugin](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-vyper.html) for more detailed explanation on how to use hardhat-zksync-vyper. +![Era Logo](https://github.com/matter-labs/era-contracts/raw/main/eraLogo.svg) +## 📥 Installation +To install **hardhat-zksync-vyper** plugin, run: + +`npm install -D @matterlabs/hardhat-zksync-vyper` + +or + +`yarn add -D @matterlabs/hardhat-zksync-vyper @nomiclabs/hardhat-vyper` + +## 🔧 Setup + +**Scaffold a new project** + +Use the zkSync Era cli to set up a project. + +`npx zksync-cli@latest create project greeter-vyper-example --template hardhat_vyper` +`cd greeter-vyper-example` + + +Update the hardhat.config.ts file with: + +`import "@matterlabs/hardhat-zksync-vyper";` + +``` +const config: HardhatUserConfig = { + zkvyper: { + version: "latest", // Uses latest available in https://github.com/matter-labs/zkvyper-bin/ + settings: { + // compilerPath: "zkvyper", // optional field with the path to the `zkvyper` binary. + libraries: {}, // optional. References to non-inlinable libraries + }, + }, + ... +``` + +| 🔧 properties | 📄 Description | +|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------| +| version | zkvyper compiler version. Default value is latest | +| compilerSource | Indicates the compiler source and can be either binary. (A docker option is no longer recommended). | +| compilerPath | Optional field with the path to the zkvyper binary. By default, the binary in $PATH is used. | +| libraries | Define any non-inlinable libraries your contracts use as dependencies here | + +Learn more about [compiling libraries](https://era.zksync.io/docs/tools/hardhat/compiling-libraries.html). + +**Create a simple vyper contract** + +The zkSync Era cli generates a contracts folder which includes a Greeter.sol contract. + +- Delete Greeter.sol from the contracts/ directory. +- Add the equivalent Greeter.vy Vyper contract: + +``` +# @version ^0.3.3 + +greeting: constant(String[100]) = "Hello, World!" + +@external +@view +def greet() -> String[100]: + return self.greeting +``` + +**Compile the contract** + +`yarn hardhat compile` + +**Create deployment script** + +First update the use-greeter.ts script, supplied by the CLI in the deploy/ directory by importing contract from correct location. + +`import * as ContractArtifact from "../artifacts-zk/contracts/Greeter.vy/Greeter.json";` + +**Add private key to environment variables** + +Remove example from the .env.example file and add your private key to <WALLET-PRIVATE-KEY>. + +Deploy the contract + +`yarn hardhat deploy-zksync --script deploy-greeter.ts` + +**Output** + +You should see something like this: + +``` +Running deploy function for the Greeter contract +The deployment is projected to cost 0.000135806 ETH +constructor args:0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000094869207468657265210000000000000000000000000000000000000000000000 +Greeter was deployed to 0x7CDF8A4334fafE21B8dCCe70487d6CBC00183c0d +``` + +## 📝 Documentation + +In addition to the [hardhat-zksync-vyper](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-vyper.html), zkSync's Era [website](https://era.zksync.io/docs/) offers a variety of resources including: + +[Guides to get started](https://era.zksync.io/docs/dev/building-on-zksync/hello-world.html): Learn how to start building on zkSync Era.\ +[Hardhat zkSync Era plugins](https://era.zksync.io/docs/tools/hardhat/getting-started.html): Overview and guides for all Hardhat zkSync Era plugins.\ +[Hyperscaling](https://era.zksync.io/docs/reference/concepts/hyperscaling.html#what-are-hyperchains): Deep dive into hyperscaling on zkSync Era. + +## 🤝 Contributing + +Contributions are always welcome! Feel free to open any issue or send a pull request. + +Go to [CONTRIBUTING.md](https://github.com/matter-labs/hardhat-zksync/blob/main/.github/CONTRIBUTING.md) to learn about steps and best practices for contributing to zkSync hardhat tooling base repository. + + +## 🙌 Feedback, help and news + +[zkSync Era Discord server](https://join.zksync.dev/): for questions and feedback.\ +[Follow zkSync Era on Twitter](https://twitter.com/zksync) + +## Happy building! 👷‍♀️👷‍♂️ \ No newline at end of file diff --git a/packages/hardhat-zksync-vyper/package.json b/packages/hardhat-zksync-vyper/package.json index e28dfec75..c6aef89c5 100644 --- a/packages/hardhat-zksync-vyper/package.json +++ b/packages/hardhat-zksync-vyper/package.json @@ -1,6 +1,6 @@ { "name": "@matterlabs/hardhat-zksync-vyper", - "version": "1.0.4", + "version": "1.0.5", "description": "Hardhat plugin to compile Vyper smart contracts for the zkSync network", "repository": "github:matter-labs/hardhat-zksync", "homepage": "https://github.com/matter-labs/hardhat-zksync/tree/main/packages/hardhat-zksync-vyper", @@ -38,6 +38,9 @@ "chalk": "4.1.2", "dockerode": "^4.0.0", "fs-extra": "^11.1.1", + "sinon": "^9.0.0", + "chai": "^4.3.6", + "undici": "^5.14.0", "semver": "^7.5.4" }, "devDependencies": { @@ -52,24 +55,22 @@ "@types/fs-extra": "^5.1.0", "@typescript-eslint/eslint-plugin": "6.13.1", "@typescript-eslint/parser": "6.13.1", - "chai": "^4.3.6", "eslint": "^8.54.0", "eslint-config-prettier": "9.0.0", "eslint-plugin-import": "2.29.0", "eslint-plugin-no-only-tests": "3.1.0", "eslint-plugin-prettier": "5.0.1", - "hardhat": "^2.19.2", + "hardhat": "^2.19.4", "mocha": "^9.2.1", "prettier": "3.1.0", "rimraf": "^3.0.2", - "sinon": "^15.0.1", "ts-node": "^10.6.0", "typescript": "^5.1.6", "c8": "^8.0.1" }, "peerDependencies": { "@nomiclabs/hardhat-vyper": "^3.0.5", - "hardhat": "^2.19.2" + "hardhat": "^2.19.4" }, "prettier": { "tabWidth": 4, diff --git a/packages/hardhat-zksync-vyper/src/artifacts.ts b/packages/hardhat-zksync-vyper/src/artifacts.ts index 9e54d79ac..4d2aafdd1 100644 --- a/packages/hardhat-zksync-vyper/src/artifacts.ts +++ b/packages/hardhat-zksync-vyper/src/artifacts.ts @@ -1,24 +1,25 @@ import { Artifacts } from 'hardhat/internal/artifacts'; import { Artifact } from 'hardhat/types'; -import { FactoryDeps } from './types'; import { VyperOutput, ContractOutput } from '@nomiclabs/hardhat-vyper/dist/src/types'; import { getArtifactFromVyperOutput } from '@nomiclabs/hardhat-vyper/dist/src/util'; +import { FactoryDeps } from './types'; import { zeroxlify } from './utils'; const ZK_ARTIFACT_FORMAT_VERSION = 'hh-zkvyper-artifact-1'; export class ZkArtifacts extends Artifacts { - compilerOutput?: VyperOutput; - forwarderOutput?: ContractOutput; + public compilerOutput?: VyperOutput; + public forwarderOutput?: ContractOutput; - override async saveArtifactAndDebugFile(artifact: Artifact, pathToBuildInfo?: string) { + public override async saveArtifactAndDebugFile(artifact: Artifact, pathToBuildInfo?: string) { + /* eslint-disable */ if (this.forwarderOutput != null) { const forwarderArtifact = { ...getArtifactFromVyperOutput(".__VYPER_FORWARDER_CONTRACT", this.forwarderOutput), contractName: "__VYPER_FORWARDER_CONTRACT", _format: ZK_ARTIFACT_FORMAT_VERSION, }; - + /* eslint-disable */ await super.saveArtifactAndDebugFile(forwarderArtifact, pathToBuildInfo); this.forwarderOutput = undefined; @@ -30,6 +31,7 @@ export class ZkArtifacts extends Artifacts { if (artifact.sourceName.endsWith('.vy') || artifact.sourceName.endsWith('.v.py')) { let factoryDeps: FactoryDeps = {}; + /* eslint-disable */ // @ts-ignore let entries: Array<[string, string]> = Object.entries(this.compilerOutput[artifact.sourceName]?.factory_deps ?? {}); for (const [hash, dependency] of entries) { @@ -45,6 +47,7 @@ export class ZkArtifacts extends Artifacts { // @ts-ignore factoryDeps }, pathToBuildInfo); + /* eslint-disable */ } return await super.saveArtifactAndDebugFile(artifact, pathToBuildInfo); diff --git a/packages/hardhat-zksync-vyper/src/compile/binary.ts b/packages/hardhat-zksync-vyper/src/compile/binary.ts index e7d11af65..75ff0974f 100644 --- a/packages/hardhat-zksync-vyper/src/compile/binary.ts +++ b/packages/hardhat-zksync-vyper/src/compile/binary.ts @@ -1,11 +1,17 @@ import { exec } from 'child_process'; import { CompilerOptions, ZkVyperConfig } from '../types'; -export async function compileWithBinary(paths: CompilerOptions, config: ZkVyperConfig, vyperPath: string): Promise<any> { +export async function compileWithBinary( + paths: CompilerOptions, + config: ZkVyperConfig, + vyperPath: string, +): Promise<any> { const optimizationMode = config.settings.optimizer?.mode; const output: string = await new Promise((resolve, reject) => { exec( - `${paths.compilerPath} ${optimizationMode ? '-O ' + optimizationMode : ''} -f combined_json ${paths.inputPaths.join(' ')} --vyper ${vyperPath}`, + `${paths.compilerPath} ${ + optimizationMode ? `-O ${optimizationMode}` : '' + } -f combined_json ${paths.inputPaths.join(' ')} --vyper ${vyperPath}`, { maxBuffer: 1024 * 1024 * 500, }, @@ -14,7 +20,7 @@ export async function compileWithBinary(paths: CompilerOptions, config: ZkVyperC return reject(err); } resolve(stdout); - } + }, ); }); diff --git a/packages/hardhat-zksync-vyper/src/compile/docker.ts b/packages/hardhat-zksync-vyper/src/compile/docker.ts index 181380210..42726e861 100644 --- a/packages/hardhat-zksync-vyper/src/compile/docker.ts +++ b/packages/hardhat-zksync-vyper/src/compile/docker.ts @@ -7,18 +7,18 @@ import { Image, ImageDoesntExistError, } from '@nomiclabs/hardhat-docker'; -import { CompilerOptions, ZkVyperConfig } from '../types'; import Docker, { ContainerCreateOptions } from 'dockerode'; -import { ZkSyncVyperPluginError } from '../errors'; import { Writable } from 'stream'; import path from 'path'; import chalk from 'chalk'; +import { ZkSyncVyperPluginError } from '../errors'; +import { CompilerOptions, ZkVyperConfig } from '../types'; async function runZkVyperContainer(docker: Docker, image: Image, paths: CompilerOptions, config: ZkVyperConfig) { const relativeSourcesPath = path.relative(process.cwd(), paths.sourcesPath!); - - const optimizationMode = config.settings.optimizer?.mode; - + + const _optimizationMode = config.settings.optimizer?.mode; + const command = ['zkvyper']; // Commented out because it's not supported by latest zkvyper image. // if (optimizationMode) { @@ -44,7 +44,7 @@ async function runZkVyperContainer(docker: Docker, image: Image, paths: Compiler let output = Buffer.from(''); let chunk = Buffer.from(''); const stream = new Writable({ - write: function (incoming: Buffer, _encoding, next) { + write(incoming: Buffer, _encoding, next) { // Please refer to the 'Stream format' chapter at // https://docs.docker.com/engine/api/v1.37/#operation/ContainerAttach // to understand the details of this implementation. @@ -96,7 +96,7 @@ export async function validateDockerIsInstalled() { if (!(await HardhatDocker.isInstalled())) { throw new ZkSyncVyperPluginError( 'Docker Desktop is not installed.\n' + - 'Please install it by following the instructions on https://www.docker.com/get-started' + 'Please install it by following the instructions on https://www.docker.com/get-started', ); } } @@ -132,10 +132,10 @@ async function checkForImageUpdates(docker: HardhatDocker, image: Image) { } export async function compileWithDocker( - paths: CompilerOptions, - docker: HardhatDocker, - image: Image, - config: ZkVyperConfig + paths: CompilerOptions, + docker: HardhatDocker, + image: Image, + config: ZkVyperConfig, ) { // @ts-ignore const dockerInstance: Docker = docker._docker; @@ -149,12 +149,15 @@ async function handleCommonErrors<T>(promise: Promise<T>): Promise<T> { if (error instanceof DockerNotRunningError || error instanceof DockerBadGatewayError) { throw new ZkSyncVyperPluginError( 'Docker Desktop is not running.\nPlease open it and wait until it finishes booting.', - error + error, ); } if (error instanceof DockerHubConnectionError) { - throw new ZkSyncVyperPluginError('Error connecting to Docker Hub.\nPlease check your internet connection.', error); + throw new ZkSyncVyperPluginError( + 'Error connecting to Docker Hub.\nPlease check your internet connection.', + error, + ); } if (error instanceof DockerServerError) { @@ -164,7 +167,7 @@ async function handleCommonErrors<T>(promise: Promise<T>): Promise<T> { if (error instanceof ImageDoesntExistError) { throw new ZkSyncVyperPluginError( `Docker image ${HardhatDocker.imageToRepoTag(error.image)} doesn't exist.\n` + - 'Make sure you chose a valid zkvyper version.' + 'Make sure you chose a valid zkvyper version.', ); } diff --git a/packages/hardhat-zksync-vyper/src/compile/downloader.ts b/packages/hardhat-zksync-vyper/src/compile/downloader.ts index 58f05021f..0948bc399 100644 --- a/packages/hardhat-zksync-vyper/src/compile/downloader.ts +++ b/packages/hardhat-zksync-vyper/src/compile/downloader.ts @@ -1,24 +1,24 @@ -import path from "path"; -import fsExtra from "fs-extra"; -import chalk from "chalk"; +import path from 'path'; +import fsExtra from 'fs-extra'; +import chalk from 'chalk'; import { spawnSync } from 'child_process'; -import { download, getLatestRelease, getZkvyperUrl, isURL, isVersionInRange, saveDataToFile } from "../utils"; -import { - COMPILER_BINARY_CORRUPTION_ERROR, - COMPILER_VERSION_INFO_FILE_DOWNLOAD_ERROR, - COMPILER_VERSION_INFO_FILE_NOT_FOUND_ERROR, - COMPILER_VERSION_RANGE_ERROR, - COMPILER_VERSION_WARNING, - DEFAULT_COMPILER_VERSION_INFO_CACHE_PERIOD, - DEFAULT_TIMEOUT_MILISECONDS, - USER_AGENT, - ZKVYPER_BIN_OWNER, - ZKVYPER_BIN_REPOSITORY, - ZKVYPER_BIN_REPOSITORY_NAME, - ZKVYPER_COMPILER_VERSION_MIN_VERSION, -} from "../constants"; -import { ZkSyncVyperPluginError } from "../errors"; +import { download, getLatestRelease, getZkvyperUrl, isURL, isVersionInRange, saveDataToFile } from '../utils'; +import { + COMPILER_BINARY_CORRUPTION_ERROR, + COMPILER_VERSION_INFO_FILE_DOWNLOAD_ERROR, + COMPILER_VERSION_INFO_FILE_NOT_FOUND_ERROR, + COMPILER_VERSION_RANGE_ERROR, + COMPILER_VERSION_WARNING, + DEFAULT_COMPILER_VERSION_INFO_CACHE_PERIOD, + DEFAULT_TIMEOUT_MILISECONDS, + USER_AGENT, + ZKVYPER_BIN_OWNER, + ZKVYPER_BIN_REPOSITORY, + ZKVYPER_BIN_REPOSITORY_NAME, + ZKVYPER_COMPILER_VERSION_MIN_VERSION, +} from '../constants'; +import { ZkSyncVyperPluginError } from '../errors'; export interface CompilerVersionInfo { latest: string; @@ -29,7 +29,6 @@ export interface CompilerVersionInfo { * This class is responsible for downloading the zkvyper binary. */ export class ZkVyperCompilerDownloader { - public static async getDownloaderWithVersionValidated( version: string, configCompilerPath: string, @@ -37,7 +36,10 @@ export class ZkVyperCompilerDownloader { ): Promise<ZkVyperCompilerDownloader> { if (!ZkVyperCompilerDownloader._instance) { let compilerVersionInfo = await ZkVyperCompilerDownloader._getCompilerVersionInfo(compilersDir); - if (compilerVersionInfo === undefined || (await ZkVyperCompilerDownloader._shouldDownloadCompilerVersionInfo(compilersDir))) { + if ( + compilerVersionInfo === undefined || + (await ZkVyperCompilerDownloader._shouldDownloadCompilerVersionInfo(compilersDir)) + ) { try { await ZkVyperCompilerDownloader._downloadCompilerVersionInfo(compilersDir); } catch (e: any) { @@ -45,20 +47,26 @@ export class ZkVyperCompilerDownloader { } compilerVersionInfo = await ZkVyperCompilerDownloader._getCompilerVersionInfo(compilersDir); } - + if (compilerVersionInfo === undefined) { throw new ZkSyncVyperPluginError(COMPILER_VERSION_INFO_FILE_NOT_FOUND_ERROR); } - + if (version === 'latest' || version === compilerVersionInfo.latest) { version = compilerVersionInfo.latest; } else if (!isVersionInRange(version, compilerVersionInfo)) { - throw new ZkSyncVyperPluginError(COMPILER_VERSION_RANGE_ERROR(version, compilerVersionInfo.minVersion, compilerVersionInfo.latest)); + throw new ZkSyncVyperPluginError( + COMPILER_VERSION_RANGE_ERROR(version, compilerVersionInfo.minVersion, compilerVersionInfo.latest), + ); } else { console.info(chalk.yellow(COMPILER_VERSION_WARNING(version, compilerVersionInfo.latest))); - }; + } - ZkVyperCompilerDownloader._instance = new ZkVyperCompilerDownloader(version, configCompilerPath, compilersDir); + ZkVyperCompilerDownloader._instance = new ZkVyperCompilerDownloader( + version, + configCompilerPath, + compilersDir, + ); } return ZkVyperCompilerDownloader._instance; @@ -67,14 +75,14 @@ export class ZkVyperCompilerDownloader { private static _instance: ZkVyperCompilerDownloader; public static compilerVersionInfoCachePeriodMs = DEFAULT_COMPILER_VERSION_INFO_CACHE_PERIOD; - /** + /** * Use `getDownloaderWithVersionValidated` to create an instance of this class. */ private constructor( private _version: string, private readonly _configCompilerPath: string, private readonly _compilersDirectory: string, - ) { } + ) {} public getVersion(): string { return this._version; @@ -88,12 +96,12 @@ export class ZkVyperCompilerDownloader { return path.join(this._compilersDirectory, 'zkvyper', `zkvyper-v${this._version}`); } - public async isCompilerDownloaded(): Promise<boolean> { + public async isCompilerDownloaded(): Promise<boolean> { if (this._configCompilerPath) { await this._verifyCompiler(); return true; } - + const compilerPath = this.getCompilerPath(); return fsExtra.pathExists(compilerPath); } @@ -117,11 +125,14 @@ export class ZkVyperCompilerDownloader { public async downloadCompiler(): Promise<void> { let compilerVersionInfo = await ZkVyperCompilerDownloader._getCompilerVersionInfo(this._compilersDirectory); - if (compilerVersionInfo === undefined || (await ZkVyperCompilerDownloader._shouldDownloadCompilerVersionInfo(this._compilersDirectory))) { + if ( + compilerVersionInfo === undefined || + (await ZkVyperCompilerDownloader._shouldDownloadCompilerVersionInfo(this._compilersDirectory)) + ) { try { await ZkVyperCompilerDownloader._downloadCompilerVersionInfo(this._compilersDirectory); } catch (e: any) { - throw new ZkSyncVyperPluginError(COMPILER_VERSION_INFO_FILE_DOWNLOAD_ERROR) + throw new ZkSyncVyperPluginError(COMPILER_VERSION_INFO_FILE_DOWNLOAD_ERROR); } compilerVersionInfo = await ZkVyperCompilerDownloader._getCompilerVersionInfo(this._compilersDirectory); @@ -131,7 +142,9 @@ export class ZkVyperCompilerDownloader { throw new ZkSyncVyperPluginError(COMPILER_VERSION_INFO_FILE_NOT_FOUND_ERROR); } if (!isVersionInRange(this._version, compilerVersionInfo)) { - throw new ZkSyncVyperPluginError(COMPILER_VERSION_RANGE_ERROR(this._version, compilerVersionInfo.minVersion, compilerVersionInfo.latest)); + throw new ZkSyncVyperPluginError( + COMPILER_VERSION_RANGE_ERROR(this._version, compilerVersionInfo.minVersion, compilerVersionInfo.latest), + ); } try { @@ -151,8 +164,8 @@ export class ZkVyperCompilerDownloader { const releaseToSave = { latest: latestRelease, - minVersion: ZKVYPER_COMPILER_VERSION_MIN_VERSION - } + minVersion: ZKVYPER_COMPILER_VERSION_MIN_VERSION, + }; const savePath = this._getCompilerVersionInfoPath(compilersDir); await saveDataToFile(releaseToSave, savePath); } @@ -194,7 +207,6 @@ export class ZkVyperCompilerDownloader { if (!(await fsExtra.pathExists(compilerVersionInfoPath))) { return undefined; } - return await this._readCompilerVersionInfo(compilerVersionInfoPath); } @@ -212,8 +224,8 @@ export class ZkVyperCompilerDownloader { .match(/\d+\.\d+\.\d+/) ?.toString(); - if (versionOutput.status !== 0 || version == null) { + if (versionOutput.status !== 0 || version === null) { throw new ZkSyncVyperPluginError(COMPILER_BINARY_CORRUPTION_ERROR(compilerPath)); } } -} \ No newline at end of file +} diff --git a/packages/hardhat-zksync-vyper/src/compile/index.ts b/packages/hardhat-zksync-vyper/src/compile/index.ts index f7a79a943..3a5904d51 100644 --- a/packages/hardhat-zksync-vyper/src/compile/index.ts +++ b/packages/hardhat-zksync-vyper/src/compile/index.ts @@ -1,6 +1,7 @@ +import { HardhatDocker, Image } from '@nomiclabs/hardhat-docker'; import { ZkVyperConfig, CompilerOptions, CompilerOutput } from '../types'; +import { ZkSyncVyperPluginError } from '../errors'; import { compileWithBinary } from './binary'; -import { HardhatDocker, Image } from '@nomiclabs/hardhat-docker'; import { validateDockerIsInstalled, createDocker, @@ -8,36 +9,36 @@ import { dockerImage, compileWithDocker, } from './docker'; -import { ZkSyncVyperPluginError } from '../errors'; export async function compile( zkvyperConfig: ZkVyperConfig, inputPaths: string[], sourcesPath: string, rootPath: string, - vyperPath?: string + vyperPath?: string, ) { let compiler: ICompiler; - if (zkvyperConfig.compilerSource == 'binary') { - if (vyperPath == null) { + if (zkvyperConfig.compilerSource === 'binary') { + if (vyperPath === null) { throw new ZkSyncVyperPluginError('vyper executable is not specified'); } - compiler = new BinaryCompiler(vyperPath); - } else if (zkvyperConfig.compilerSource == 'docker') { + compiler = new BinaryCompiler(vyperPath!); + } else if (zkvyperConfig.compilerSource === 'docker') { compiler = await DockerCompiler.initialize(zkvyperConfig); } else { throw new ZkSyncVyperPluginError(`Incorrect compiler source: ${zkvyperConfig.compilerSource}`); } - let output = await compiler.compile({ - inputPaths, - sourcesPath, - compilerPath: zkvyperConfig.settings.compilerPath, - }, - zkvyperConfig + const output = await compiler.compile( + { + inputPaths, + sourcesPath, + compilerPath: zkvyperConfig.settings.compilerPath, + }, + zkvyperConfig, ); - if(process.platform !== 'win32') { + if (process.platform !== 'win32') { return output; } @@ -45,12 +46,12 @@ export async function compile( } function getWindowsOutput(output: CompilerOutput, path: string) { - let { version, ...contracts } = output; - let changedOutput = {} as CompilerOutput; + const { version, ...contracts } = output; + const changedOutput = {} as CompilerOutput; - let specificPath = path.replaceAll('\\', '/'); - for(let [sourceName, output] of Object.entries(contracts)) { - sourceName = sourceName.replace(`//?/${specificPath}/`, ''); + const specificPath = path.replaceAll('\\', '/'); + for (const [originalSourceName, _output] of Object.entries(contracts)) { + const sourceName = originalSourceName.replace(`//?/${specificPath}/`, ''); changedOutput[sourceName] = output; } @@ -66,12 +67,15 @@ export class BinaryCompiler implements ICompiler { constructor(public vyperPath: string) {} public async compile(paths: CompilerOptions, config: ZkVyperConfig) { - return await compileWithBinary(paths, config, this.vyperPath); + return compileWithBinary(paths, config, this.vyperPath); } } export class DockerCompiler implements ICompiler { - protected constructor(public dockerImage: Image, public docker: HardhatDocker) {} + protected constructor( + public dockerCompilerImage: Image, + public docker: HardhatDocker, + ) {} public static async initialize(config: ZkVyperConfig): Promise<ICompiler> { await validateDockerIsInstalled(); @@ -84,6 +88,6 @@ export class DockerCompiler implements ICompiler { } public async compile(paths: CompilerOptions, config: ZkVyperConfig) { - return await compileWithDocker(paths, this.docker, this.dockerImage, config); + return compileWithDocker(paths, this.docker, this.dockerCompilerImage, config); } } diff --git a/packages/hardhat-zksync-vyper/src/constants.ts b/packages/hardhat-zksync-vyper/src/constants.ts index 4844f61cc..4f7a142cc 100644 --- a/packages/hardhat-zksync-vyper/src/constants.ts +++ b/packages/hardhat-zksync-vyper/src/constants.ts @@ -8,7 +8,8 @@ export const TASK_COMPILE_VYPER_CHECK_ERRORS = 'compile:vyper:check-errors'; export const TASK_COMPILE_VYPER_LOG_COMPILATION_ERRORS = 'compile:vyper:log:compilation-errors'; // User agent of MacOSX Chrome 120.0.0.0 -export const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; +export const USER_AGENT = + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; export const defaultZkVyperConfig: ZkVyperConfig = { version: 'latest', @@ -22,26 +23,28 @@ export const defaultZkVyperConfig: ZkVyperConfig = { }, }; -export const UNSUPPORTED_VYPER_VERSIONS = [ - '0.3.4', - '0.3.5', - '0.3.6', - '0.3.7', -]; +export const UNSUPPORTED_VYPER_VERSIONS = ['0.3.4', '0.3.5', '0.3.6', '0.3.7']; -export const ZKVYPER_COMPILER_VERSION_MIN_VERSION = "1.3.9"; +export const ZKVYPER_COMPILER_VERSION_MIN_VERSION = '1.3.9'; export const ZKVYPER_BIN_OWNER = 'matter-labs'; export const ZKVYPER_BIN_REPOSITORY_NAME = 'zkvyper-bin'; export const DEFAULT_COMPILER_VERSION_INFO_CACHE_PERIOD = 24 * 60 * 60 * 1000; // 24 hours -export const COMPILER_VERSION_INFO_FILE_NOT_FOUND_ERROR = 'Could not find zkvyper compiler version info file. Please check your internet connection and try again.'; -export const COMPILER_VERSION_INFO_FILE_DOWNLOAD_ERROR = 'Could not download zkvyper compiler version info file. Please check your internet connection and try again.'; - -export const COMPILER_VERSION_RANGE_ERROR = (version: string, minVersion: string, latestVersion: string) => `The zkvyper compiler version (${version}) in the hardhat config file is not within the allowed range. Please use versions ${minVersion} to ${latestVersion}.`; -export const COMPILER_VERSION_WARNING = (version: string, latestVersion: string) => `The zkvyper compiler version in your Hardhat config file (${version}) is not the latest. We recommend using the latest version ${latestVersion}.`; -export const COMPILER_BINARY_CORRUPTION_ERROR = (compilerPath: string) => `The zkvyper binary at path ${compilerPath} is corrupted. Please delete it and try again.`; -export const COMPILING_INFO_MESSAGE = (zksolcVersion: string, solcVersion: string) => `Compiling contracts for zkSync Era with zkvyper v${zksolcVersion} and vyper v${solcVersion}`; - -export const VYPER_VERSION_ERROR = 'Vyper versions 0.3.4 to 0.3.7 are not supported by zkvyper. Please use vyper 0.3.3 or >=0.3.8 in your hardhat.config file instead.' \ No newline at end of file +export const COMPILER_VERSION_INFO_FILE_NOT_FOUND_ERROR = + 'Could not find zkvyper compiler version info file. Please check your internet connection and try again.'; +export const COMPILER_VERSION_INFO_FILE_DOWNLOAD_ERROR = + 'Could not download zkvyper compiler version info file. Please check your internet connection and try again.'; + +export const COMPILER_VERSION_RANGE_ERROR = (version: string, minVersion: string, latestVersion: string) => + `The zkvyper compiler version (${version}) in the hardhat config file is not within the allowed range. Please use versions ${minVersion} to ${latestVersion}.`; +export const COMPILER_VERSION_WARNING = (version: string, latestVersion: string) => + `The zkvyper compiler version in your Hardhat config file (${version}) is not the latest. We recommend using the latest version ${latestVersion}.`; +export const COMPILER_BINARY_CORRUPTION_ERROR = (compilerPath: string) => + `The zkvyper binary at path ${compilerPath} is corrupted. Please delete it and try again.`; +export const COMPILING_INFO_MESSAGE = (zksolcVersion: string, solcVersion: string) => + `Compiling contracts for zkSync Era with zkvyper v${zksolcVersion} and vyper v${solcVersion}`; + +export const VYPER_VERSION_ERROR = + 'Vyper versions 0.3.4 to 0.3.7 are not supported by zkvyper. Please use vyper 0.3.3 or >=0.3.8 in your hardhat.config file instead.'; diff --git a/packages/hardhat-zksync-vyper/src/errors.ts b/packages/hardhat-zksync-vyper/src/errors.ts index 771ce3b41..6f7edaed4 100644 --- a/packages/hardhat-zksync-vyper/src/errors.ts +++ b/packages/hardhat-zksync-vyper/src/errors.ts @@ -5,4 +5,4 @@ export class ZkSyncVyperPluginError extends HardhatPluginError { constructor(message: string, parentError?: Error) { super(PLUGIN_NAME, message, parentError); } -} \ No newline at end of file +} diff --git a/packages/hardhat-zksync-vyper/src/index.ts b/packages/hardhat-zksync-vyper/src/index.ts index fa5c905d9..46e84002f 100644 --- a/packages/hardhat-zksync-vyper/src/index.ts +++ b/packages/hardhat-zksync-vyper/src/index.ts @@ -12,18 +12,26 @@ import { extendEnvironment, extendConfig, subtask, types } from 'hardhat/interna import { getCompilersDir } from 'hardhat/internal/util/global-dir'; import { Mutex } from 'hardhat/internal/vendor/await-semaphore'; import './type-extensions'; +import chalk from 'chalk'; +import { CompilationJob } from 'hardhat/types'; import { ZkArtifacts } from './artifacts'; import { compile } from './compile'; import { checkSupportedVyperVersions, pluralize } from './utils'; -import chalk from 'chalk'; -import { CompilationJob } from 'hardhat/types'; -import { COMPILING_INFO_MESSAGE, defaultZkVyperConfig, TASK_COMPILE_VYPER_CHECK_ERRORS, TASK_COMPILE_VYPER_LOG_COMPILATION_ERRORS } from './constants'; +import { + COMPILING_INFO_MESSAGE, + defaultZkVyperConfig, + TASK_COMPILE_VYPER_CHECK_ERRORS, + TASK_COMPILE_VYPER_LOG_COMPILATION_ERRORS, +} from './constants'; import { ZkVyperCompilerDownloader } from './compile/downloader'; extendConfig((config, userConfig) => { config.zkvyper = { ...defaultZkVyperConfig, ...userConfig?.zkvyper }; config.zkvyper.settings = { ...defaultZkVyperConfig.settings, ...userConfig?.zkvyper?.settings }; - config.zkvyper.settings.optimizer = { ...defaultZkVyperConfig.settings.optimizer, ...userConfig?.zkvyper?.settings?.optimizer }; + config.zkvyper.settings.optimizer = { + ...defaultZkVyperConfig.settings.optimizer, + ...userConfig?.zkvyper?.settings?.optimizer, + }; }); extendEnvironment((hre) => { @@ -34,12 +42,12 @@ extendEnvironment((hre) => { let artifactsPath = hre.config.paths.artifacts; if (!artifactsPath.endsWith('-zk')) { - artifactsPath = artifactsPath + '-zk'; + artifactsPath = `${artifactsPath}-zk`; } let cachePath = hre.config.paths.cache; if (!cachePath.endsWith('-zk')) { - cachePath = cachePath + '-zk'; + cachePath = `${cachePath}-zk`; } // Forcibly update the artifacts object. @@ -50,7 +58,7 @@ extendEnvironment((hre) => { }); // If there're no .sol files to compile - that's ok. -subtask(TASK_COMPILE_SOLIDITY_LOG_NOTHING_TO_COMPILE, async () => { }); +subtask(TASK_COMPILE_SOLIDITY_LOG_NOTHING_TO_COMPILE, async () => {}); subtask(TASK_COMPILE_VYPER_RUN_BINARY, async (args: { inputPaths: string[]; vyperPath: string }, hre, runSuper) => { if (hre.network.zksync !== true) { @@ -62,7 +70,7 @@ subtask(TASK_COMPILE_VYPER_RUN_BINARY, async (args: { inputPaths: string[]; vype args.inputPaths, hre.config.paths.sources, hre.config.paths.root, - args.vyperPath + args.vyperPath, ); await hre.run(TASK_COMPILE_VYPER_CHECK_ERRORS, { output: compilerOutput, quiet: true }); @@ -105,15 +113,15 @@ subtask(TASK_COMPILE_VYPER_GET_BUILD, async (args: { vyperVersion: string }, hre const zkvyperDownloader = await ZkVyperCompilerDownloader.getDownloaderWithVersionValidated( hre.config.zkvyper.version, hre.config.zkvyper.settings.compilerPath ?? '', - compilersCache + compilersCache, ); const isZksolcDownloaded = await zkvyperDownloader.isCompilerDownloaded(); if (!isZksolcDownloaded) { await zkvyperDownloader.downloadCompiler(); } - hre.config.zkvyper.settings.compilerPath = await zkvyperDownloader.getCompilerPath(); - hre.config.zkvyper.version = await zkvyperDownloader.getVersion(); + hre.config.zkvyper.settings.compilerPath = zkvyperDownloader.getCompilerPath(); + hre.config.zkvyper.version = zkvyperDownloader.getVersion(); }); console.info(chalk.yellow(COMPILING_INFO_MESSAGE(hre.config.zkvyper.version, args.vyperVersion))); @@ -133,7 +141,7 @@ subtask( } hre.network.solcCompilationsNum = count; - } + }, ); subtask( @@ -147,79 +155,64 @@ subtask( if (args.quiet) return; - if (hre.network.solcCompilationsNum != 0 && vyperCompilationsNum != 0) { + if (hre.network.solcCompilationsNum !== 0 && vyperCompilationsNum !== 0) { console.info( chalk.green( - `Successfully compiled ${hre.network.solcCompilationsNum??0} Solidity ${pluralize( + `Successfully compiled ${hre.network.solcCompilationsNum ?? 0} Solidity ${pluralize( hre.network.solcCompilationsNum, - 'file' - )} and ${vyperCompilationsNum} Vyper ${pluralize(vyperCompilationsNum, 'file')}` - ) + 'file', + )} and ${vyperCompilationsNum} Vyper ${pluralize(vyperCompilationsNum, 'file')}`, + ), ); - } else if (hre.network.solcCompilationsNum == 0 && vyperCompilationsNum != 0) { + } else if (hre.network.solcCompilationsNum === 0 && vyperCompilationsNum !== 0) { console.info( chalk.green( - `Successfully compiled ${vyperCompilationsNum} Vyper ${pluralize(vyperCompilationsNum, 'file')}` - ) + `Successfully compiled ${vyperCompilationsNum} Vyper ${pluralize(vyperCompilationsNum, 'file')}`, + ), ); - } else if (hre.network.solcCompilationsNum != 0 && vyperCompilationsNum == 0) { + } else if (hre.network.solcCompilationsNum !== 0 && vyperCompilationsNum === 0) { console.info( chalk.yellow( - `Warning: You imported '@matterlabs/hardhat-zksync-vyper', but there are no .vy files to compile!\nPlease check if any files are missing or if the import is redundant.` - ) + `Warning: You imported '@matterlabs/hardhat-zksync-vyper', but there are no .vy files to compile!\nPlease check if any files are missing or if the import is redundant.`, + ), ); } else { console.info(chalk.green(`Nothing to compile`)); } - } + }, ); -subtask(TASK_COMPILE_VYPER_LOG_DOWNLOAD_COMPILER_START) - .setAction( - async ({ - quiet, - isDownloaded, - vyperVersion, - }: { - quiet: boolean; - isDownloaded: boolean; - vyperVersion: string; - }) => { - if (isDownloaded || quiet) return; - - console.info(chalk.yellow(`Downloading vyper ${vyperVersion}`)); - } - ); +subtask(TASK_COMPILE_VYPER_LOG_DOWNLOAD_COMPILER_START).setAction( + async ({ quiet, isDownloaded, vyperVersion }: { quiet: boolean; isDownloaded: boolean; vyperVersion: string }) => { + if (isDownloaded || quiet) return; + + console.info(chalk.yellow(`Downloading vyper ${vyperVersion}`)); + }, +); subtask(TASK_COMPILE_VYPER_CHECK_ERRORS) - .addParam("output", undefined, undefined, types.any) - .addParam("quiet", undefined, undefined, types.boolean) - .setAction( - async ({ output, quiet }: { output: any; quiet: boolean }, { run }) => { - await run(TASK_COMPILE_VYPER_LOG_COMPILATION_ERRORS, { - output, - quiet, - }); - } - ); + .addParam('output', undefined, undefined, types.any) + .addParam('quiet', undefined, undefined, types.boolean) + .setAction(async ({ output, quiet }: { output: any; quiet: boolean }, { run }) => { + await run(TASK_COMPILE_VYPER_LOG_COMPILATION_ERRORS, { + output, + quiet, + }); + }); subtask(TASK_COMPILE_VYPER_LOG_COMPILATION_ERRORS) - .addParam("output", undefined, undefined, types.any) - .addParam("quiet", undefined, undefined, types.boolean) + .addParam('output', undefined, undefined, types.any) + .addParam('quiet', undefined, undefined, types.boolean) .setAction(async ({ output }: { output: any; quiet: boolean }) => { // Iterate over contracts for (const contractPath in output) { - if (contractPath !== "version" && contractPath !== "zk_version") { + if (contractPath !== 'version' && contractPath !== 'zk_version') { const contract = output[contractPath]; - + if (contract.warnings && Array.isArray(contract.warnings) && contract.warnings.length > 0) { // Iterate over warnings for (const warning of contract.warnings) { - console.warn( - (warning.message as string).replace(/^\w+:/, (t) => - chalk.yellow.bold(t) - ) - ); + console.warn((warning.message as string).replace(/^\w+:/, (t) => chalk.yellow.bold(t))); } } } diff --git a/packages/hardhat-zksync-vyper/src/types.ts b/packages/hardhat-zksync-vyper/src/types.ts index 967813d71..49112d6c6 100644 --- a/packages/hardhat-zksync-vyper/src/types.ts +++ b/packages/hardhat-zksync-vyper/src/types.ts @@ -48,4 +48,4 @@ export interface CompilerOptions { export interface CompilerOutput { [fqn: string]: any; version: string; -} \ No newline at end of file +} diff --git a/packages/hardhat-zksync-vyper/src/utils.ts b/packages/hardhat-zksync-vyper/src/utils.ts index 015571d96..09c4b2fa9 100644 --- a/packages/hardhat-zksync-vyper/src/utils.ts +++ b/packages/hardhat-zksync-vyper/src/utils.ts @@ -1,9 +1,9 @@ import semver from 'semver'; -import fs from "fs"; -import path from "path"; -import util from "util"; -import fse from "fs-extra"; -import type { Dispatcher } from "undici"; +import fs from 'fs'; +import path from 'path'; +import util from 'util'; +import fse from 'fs-extra'; +import type { Dispatcher } from 'undici'; import { MultiVyperConfig } from '@nomiclabs/hardhat-vyper/dist/src/types'; @@ -11,7 +11,7 @@ import { CompilerVersionInfo } from './compile/downloader'; import { DEFAULT_TIMEOUT_MILISECONDS, UNSUPPORTED_VYPER_VERSIONS, VYPER_VERSION_ERROR } from './constants'; import { ZkSyncVyperPluginError } from './errors'; -const TEMP_FILE_PREFIX = "tmp-"; +const TEMP_FILE_PREFIX = 'tmp-'; export function zeroxlify(hex: string): string { hex = hex.toLowerCase(); @@ -23,8 +23,8 @@ export function getZkvyperUrl(repo: string, version: string, isRelease: boolean const platform = { darwin: 'macosx', linux: 'linux', win32: 'windows' }[process.platform]; // @ts-ignore const toolchain = { linux: '-musl', win32: '-gnu', darwin: '' }[process.platform]; - const arch = process.arch == 'x64' ? 'amd64' : process.arch; - const ext = process.platform == 'win32' ? '.exe' : ''; + const arch = process.arch === 'x64' ? 'amd64' : process.arch; + const ext = process.platform === 'win32' ? '.exe' : ''; if (isRelease) { return `${repo}/releases/download/v${version}/zkvyper-${platform}-${arch}${toolchain}-v${version}${ext}`; @@ -85,23 +85,23 @@ export async function download( userAgent: string, version: string, timeoutMillis = 10000, - extraHeaders: { [name: string]: string } = {} + extraHeaders: { [name: string]: string } = {}, ) { - const { pipeline } = await import("stream"); - const { getGlobalDispatcher, request } = await import("undici"); + const { pipeline } = await import('stream'); + const { getGlobalDispatcher, request } = await import('undici'); const streamPipeline = util.promisify(pipeline); - let dispatcher: Dispatcher = getGlobalDispatcher(); + const dispatcher: Dispatcher = getGlobalDispatcher(); // Fetch the url const response = await request(url, { dispatcher, headersTimeout: timeoutMillis, maxRedirections: 10, - method: "GET", + method: 'GET', headers: { ...extraHeaders, - "User-Agent": `${userAgent} ${version}`, + 'User-Agent': `${userAgent} ${version}`, }, }); @@ -122,18 +122,23 @@ export async function download( ); } -export async function getLatestRelease(owner: string, repo: string, userAgent: string, timeout: number = DEFAULT_TIMEOUT_MILISECONDS): Promise<any> { - let url = `https://github.com/${owner}/${repo}/releases/latest`; - let redirectUrlPattern = `https://github.com/${owner}/${repo}/releases/tag/v` +export async function getLatestRelease( + owner: string, + repo: string, + userAgent: string, + timeout: number = DEFAULT_TIMEOUT_MILISECONDS, +): Promise<any> { + const url = `https://github.com/${owner}/${repo}/releases/latest`; + const redirectUrlPattern = `https://github.com/${owner}/${repo}/releases/tag/v`; - const { request } = await import("undici"); + const { request } = await import('undici'); const response = await request(url, { headersTimeout: timeout, maxRedirections: 0, - method: "GET", + method: 'GET', headers: { - "User-Agent": `${userAgent}`, + 'User-Agent': `${userAgent}`, }, }); @@ -161,4 +166,4 @@ export async function getLatestRelease(owner: string, repo: string, userAgent: s export async function saveDataToFile(data: any, targetPath: string) { await fse.ensureDir(path.dirname(targetPath)); await fse.writeJSON(targetPath, data, { spaces: 2 }); -} \ No newline at end of file +} diff --git a/packages/hardhat-zksync-vyper/test/common.config.ts b/packages/hardhat-zksync-vyper/test/common.config.ts index 67d2f834f..15b003017 100644 --- a/packages/hardhat-zksync-vyper/test/common.config.ts +++ b/packages/hardhat-zksync-vyper/test/common.config.ts @@ -11,6 +11,10 @@ const config: HardhatUserConfig = { hardhat: { zksync: true, }, + otherNetwork: { + zksync: false, + url: 'http://0.0.0.0:3050', + }, }, vyper: { version: '0.3.3', diff --git a/packages/hardhat-zksync-vyper/test/fixture-projects/nothing-to-compile/contracts/Greeter.vyx b/packages/hardhat-zksync-vyper/test/fixture-projects/nothing-to-compile/contracts/Greeter.vyx new file mode 100644 index 000000000..458857e40 --- /dev/null +++ b/packages/hardhat-zksync-vyper/test/fixture-projects/nothing-to-compile/contracts/Greeter.vyx @@ -0,0 +1,13 @@ +# @version ^0.3.3 +# vim: ft=python + +greeting: String[100] + +@external +def __init__(): + self.greeting = "Hello World" + +@external +@view +def greet() -> String[100]: + return self.greeting diff --git a/packages/hardhat-zksync-vyper/test/fixture-projects/nothing-to-compile/hardhat.config.js b/packages/hardhat-zksync-vyper/test/fixture-projects/nothing-to-compile/hardhat.config.js new file mode 100644 index 000000000..12575417f --- /dev/null +++ b/packages/hardhat-zksync-vyper/test/fixture-projects/nothing-to-compile/hardhat.config.js @@ -0,0 +1 @@ +module.exports = require('../../common.config'); diff --git a/packages/hardhat-zksync-vyper/test/fixture-projects/simple-docker-no-image/contracts/Greeter.vy b/packages/hardhat-zksync-vyper/test/fixture-projects/simple-docker-no-image/contracts/Greeter.vy new file mode 100644 index 000000000..458857e40 --- /dev/null +++ b/packages/hardhat-zksync-vyper/test/fixture-projects/simple-docker-no-image/contracts/Greeter.vy @@ -0,0 +1,13 @@ +# @version ^0.3.3 +# vim: ft=python + +greeting: String[100] + +@external +def __init__(): + self.greeting = "Hello World" + +@external +@view +def greet() -> String[100]: + return self.greeting diff --git a/packages/hardhat-zksync-vyper/test/fixture-projects/simple-docker-no-image/hardhat.config.ts b/packages/hardhat-zksync-vyper/test/fixture-projects/simple-docker-no-image/hardhat.config.ts new file mode 100644 index 000000000..b5cc28ba5 --- /dev/null +++ b/packages/hardhat-zksync-vyper/test/fixture-projects/simple-docker-no-image/hardhat.config.ts @@ -0,0 +1,36 @@ +import '@nomiclabs/hardhat-vyper'; +import '../../../src/index'; +import { HardhatUserConfig } from 'hardhat/config'; + +const config: HardhatUserConfig = { + zkvyper: { + version: 'latest', + compilerSource: 'docker', + }, + networks: { + hardhat: { + zksync: true, + }, + otherNetwork: { + zksync: false, + url: 'http://0.0.0.0:3050', + }, + // ethNetwork: { + // url: 'http://0.0.0.0:8545', + // }, + // zkSyncNetwork2:{ + // url: 'http://0.0.0.0:3050', + // ethNetwork: 'ethNetwork', + // zksync: true, + // accounts: ["0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110","0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110"] + // } + }, + vyper: { + version: '0.3.3', + }, + solidity: { + version: '0.8.17', + }, +}; + +export default config; diff --git a/packages/hardhat-zksync-vyper/test/fixture-projects/simple-docker/contracts/Greeter.vy b/packages/hardhat-zksync-vyper/test/fixture-projects/simple-docker/contracts/Greeter.vy new file mode 100644 index 000000000..458857e40 --- /dev/null +++ b/packages/hardhat-zksync-vyper/test/fixture-projects/simple-docker/contracts/Greeter.vy @@ -0,0 +1,13 @@ +# @version ^0.3.3 +# vim: ft=python + +greeting: String[100] + +@external +def __init__(): + self.greeting = "Hello World" + +@external +@view +def greet() -> String[100]: + return self.greeting diff --git a/packages/hardhat-zksync-vyper/test/fixture-projects/simple-docker/hardhat.config.ts b/packages/hardhat-zksync-vyper/test/fixture-projects/simple-docker/hardhat.config.ts new file mode 100644 index 000000000..9cb351012 --- /dev/null +++ b/packages/hardhat-zksync-vyper/test/fixture-projects/simple-docker/hardhat.config.ts @@ -0,0 +1,42 @@ +import '@nomiclabs/hardhat-vyper'; +import '../../../src/index'; +import { HardhatUserConfig } from 'hardhat/config'; + +const config: HardhatUserConfig = { + zkvyper: { + version: 'latest', + compilerSource: 'docker', + settings: { + experimental: { + dockerImage: 'matterlabs/zkvyper', + tag: 'latest', + }, + }, + }, + networks: { + hardhat: { + zksync: true, + }, + otherNetwork: { + zksync: false, + url: 'http://0.0.0.0:3050', + }, + // ethNetwork: { + // url: 'http://0.0.0.0:8545', + // }, + // zkSyncNetwork2:{ + // url: 'http://0.0.0.0:3050', + // ethNetwork: 'ethNetwork', + // zksync: true, + // accounts: ["0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110","0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110"] + // } + }, + vyper: { + version: '0.3.3', + }, + solidity: { + version: '0.8.17', + }, +}; + +export default config; diff --git a/packages/hardhat-zksync-vyper/test/fixture-projects/unsupported-vyper/contracts/Greeter.vy b/packages/hardhat-zksync-vyper/test/fixture-projects/unsupported-vyper/contracts/Greeter.vy new file mode 100644 index 000000000..bf6ade4f3 --- /dev/null +++ b/packages/hardhat-zksync-vyper/test/fixture-projects/unsupported-vyper/contracts/Greeter.vy @@ -0,0 +1,13 @@ +# @version ^0.3.11 +# vim: ft=python + +greeting: String[100] + +@external +def __init__(): + self.greeting = "Hello World" + +@external +@view +def greet() -> String[100]: + return self.greeting diff --git a/packages/hardhat-zksync-vyper/test/fixture-projects/unsupported-vyper/hardhat.config.ts b/packages/hardhat-zksync-vyper/test/fixture-projects/unsupported-vyper/hardhat.config.ts new file mode 100644 index 000000000..5613ceb24 --- /dev/null +++ b/packages/hardhat-zksync-vyper/test/fixture-projects/unsupported-vyper/hardhat.config.ts @@ -0,0 +1,37 @@ +import '@nomiclabs/hardhat-vyper'; +import '../../../src/index'; +import { HardhatUserConfig } from 'hardhat/config'; +import '../../../src/type-extensions'; + +const config: HardhatUserConfig = { + zkvyper: { + version: 'latest', + compilerSource: 'binary', + }, + networks: { + hardhat: { + zksync: true, + }, + otherNetwork: { + zksync: false, + url: 'http://0.0.0.0:3050', + }, + // ethNetwork: { + // url: 'http://0.0.0.0:8545', + // }, + // zkSyncNetwork2:{ + // url: 'http://0.0.0.0:3050', + // ethNetwork: 'ethNetwork', + // zksync: true, + // accounts: ["0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110","0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110"] + // } + }, + vyper: { + version: '0.3.7', + }, + solidity: { + version: '0.8.17', + }, +}; + +export default config; diff --git a/packages/hardhat-zksync-vyper/test/fixture-projects/unsupported-zkvyper/contracts/Greeter.vy b/packages/hardhat-zksync-vyper/test/fixture-projects/unsupported-zkvyper/contracts/Greeter.vy new file mode 100644 index 000000000..cfee0ca24 --- /dev/null +++ b/packages/hardhat-zksync-vyper/test/fixture-projects/unsupported-zkvyper/contracts/Greeter.vy @@ -0,0 +1,13 @@ +# @version ^0.3.9 +# vim: ft=python + +greeting: String[100] + +@external +def __init__(): + self.greeting = "Hello World" + +@external +@view +def greet() -> String[100]: + return self.greeting diff --git a/packages/hardhat-zksync-vyper/test/fixture-projects/unsupported-zkvyper/hardhat.config.ts b/packages/hardhat-zksync-vyper/test/fixture-projects/unsupported-zkvyper/hardhat.config.ts new file mode 100644 index 000000000..6b56be47f --- /dev/null +++ b/packages/hardhat-zksync-vyper/test/fixture-projects/unsupported-zkvyper/hardhat.config.ts @@ -0,0 +1,37 @@ +import '@nomiclabs/hardhat-vyper'; +import '../../../src/index'; +import { HardhatUserConfig } from 'hardhat/config'; +import '../../../src/type-extensions'; + +const config: HardhatUserConfig = { + zkvyper: { + version: '111.1.77', + compilerSource: 'binary', + }, + networks: { + hardhat: { + zksync: true, + }, + otherNetwork: { + zksync: false, + url: 'http://0.0.0.0:3050', + }, + // ethNetwork: { + // url: 'http://0.0.0.0:8545', + // }, + // zkSyncNetwork2:{ + // url: 'http://0.0.0.0:3050', + // ethNetwork: 'ethNetwork', + // zksync: true, + // accounts: ["0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110","0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110"] + // } + }, + vyper: { + version: '0.3.9', + }, + solidity: { + version: '0.8.17', + }, +}; + +export default config; diff --git a/packages/hardhat-zksync-vyper/test/helpers.ts b/packages/hardhat-zksync-vyper/test/helpers.ts index 899a4ab39..f1b9240fc 100644 --- a/packages/hardhat-zksync-vyper/test/helpers.ts +++ b/packages/hardhat-zksync-vyper/test/helpers.ts @@ -9,13 +9,14 @@ declare module 'mocha' { } } -export function useEnvironment(fixtureProjectName: string, networkName = 'hardhat') { +export function useEnvironment(fixtureProjectName: string, networkName = 'hardhat', manualyLoadEnv: boolean = false) { beforeEach('Loading hardhat environment', function () { process.chdir(path.join(__dirname, 'fixture-projects', fixtureProjectName)); process.env.HARDHAT_NETWORK = networkName; - - this.env = require('hardhat'); - this.env.run(TASK_CLEAN); + if (!manualyLoadEnv) { + this.env = require('hardhat'); + const _ = this.env.run(TASK_CLEAN); + } }); afterEach('Resetting hardhat', function () { diff --git a/packages/hardhat-zksync-vyper/test/tests.ts b/packages/hardhat-zksync-vyper/test/tests.ts index 68ae076bc..cece563ec 100644 --- a/packages/hardhat-zksync-vyper/test/tests.ts +++ b/packages/hardhat-zksync-vyper/test/tests.ts @@ -1,9 +1,9 @@ import { assert, expect } from 'chai'; import { TASK_COMPILE } from 'hardhat/builtin-tasks/task-names'; -import { ZkSyncArtifact } from '../src/types'; -import { useEnvironment } from './helpers'; import { spy } from 'sinon'; import chalk from 'chalk'; +import { ZkSyncArtifact } from '../src/types'; +import { useEnvironment } from './helpers'; describe('zkvyper plugin', async function () { describe('Simple', async function () { @@ -21,6 +21,86 @@ describe('zkvyper plugin', async function () { }); }); + describe('Simple on otherNetwork', async function () { + useEnvironment('simple', 'otherNetwork'); + + it('Should successfully compile a simple contract on otherNetwork', async function () { + await this.env.run(TASK_COMPILE); + + const artifact = this.env.artifacts.readArtifactSync('Greeter') as ZkSyncArtifact; + + assert.deepEqual(artifact.factoryDeps, undefined, 'Contract should not have factoryDeps'); + }); + }); + + describe('Simple using docker without image', async function () { + useEnvironment('simple-docker-no-image'); + + it('Should fail to compile a simple contract', async function () { + try { + await this.env.run(TASK_COMPILE); + const artifact = this.env.artifacts.readArtifactSync('Greeter') as ZkSyncArtifact; + + assert.equal(artifact.contractName, 'Greeter'); + } catch (e: any) { + expect(e.message).to.include('Docker source was chosen but no image was specified'); + } + }); + }); + + describe('Simple using docker', async function () { + useEnvironment('simple-docker'); + + it('Should compile a simple contract', async function () { + await this.env.run(TASK_COMPILE); + const artifact = this.env.artifacts.readArtifactSync('Greeter') as ZkSyncArtifact; + + assert.equal(artifact.contractName, 'Greeter'); + }); + }); + + describe('Should not download', async function () { + useEnvironment('unsupported-vyper', 'hardhat', true); + + it('Should not download vyper compiler', async function () { + try { + const hh = require('hardhat'); + await hh.run(TASK_COMPILE); + } catch (e: any) { + expect(e.message).to.include('Vyper versions 0.3.4 to 0.3.7 are not supported by zkvyper'); + } + }); + }); + + describe('Should not download', async function () { + useEnvironment('unsupported-zkvyper', 'hardhat', true); + + it('Should not download zkvyper compiler', async function () { + try { + const hh = require('hardhat'); + await hh.run(TASK_COMPILE); + } catch (e: any) { + console.info(e.message); + expect(e.message).to.include('Please use versions 1.3.9 to 1.3.14'); + } + }); + }); + + describe('Compiling nothing', async function () { + useEnvironment('nothing-to-compile'); + + it('Should not compile anything', async function () { + let isError = false; + try { + await this.env.run(TASK_COMPILE); + } catch (e: any) { + isError = true; + console.info(e); + } + assert(isError === false); + }); + }); + describe('Factory', async function () { useEnvironment('factory'); @@ -28,7 +108,7 @@ describe('zkvyper plugin', async function () { await this.env.run(TASK_COMPILE); const factoryArtifact = this.env.artifacts.readArtifactSync( - 'contracts/CreateForwarder.vy:CreateForwarder' + 'contracts/CreateForwarder.vy:CreateForwarder', ) as ZkSyncArtifact; const depArtifact = this.env.artifacts.readArtifactSync('contracts/DeployMe.vy:DeployMe') as ZkSyncArtifact; @@ -46,7 +126,7 @@ describe('zkvyper plugin', async function () { assert.equal( '.__VYPER_FORWARDER_CONTRACT:__VYPER_FORWARDER_CONTRACT', factoryArtifact.factoryDeps[depHash], - 'No required dependency in the artifact' + 'No required dependency in the artifact', ); // For the dependency contract should be no further dependencies. @@ -54,7 +134,7 @@ describe('zkvyper plugin', async function () { // Check that the forwarder artifact was saved correctly. const forwarderArtifact = this.env.artifacts.readArtifactSync( - '.__VYPER_FORWARDER_CONTRACT:__VYPER_FORWARDER_CONTRACT' + '.__VYPER_FORWARDER_CONTRACT:__VYPER_FORWARDER_CONTRACT', ) as ZkSyncArtifact; assert.equal(forwarderArtifact.contractName, '__VYPER_FORWARDER_CONTRACT'); }); @@ -64,11 +144,11 @@ describe('zkvyper plugin', async function () { useEnvironment('output'); it('Should successfully compile both solidity and vyper contracts and match their log outputs', async function () { - let consoleSpy = spy(console, 'info'); + const consoleSpy = spy(console, 'info'); await this.env.run(TASK_COMPILE); expect( - consoleSpy.calledWith(chalk.green('Successfully compiled 3 Solidity files and 1 Vyper file')) + consoleSpy.calledWith(chalk.green('Successfully compiled 3 Solidity files and 1 Vyper file')), ).to.equal(true); consoleSpy.restore(); }); diff --git a/scripts/create-release-from-tags/index.js b/scripts/create-release-from-tags/index.js deleted file mode 100644 index e357abe3c..000000000 --- a/scripts/create-release-from-tags/index.js +++ /dev/null @@ -1,187 +0,0 @@ -import { Command } from 'commander'; -import spawn from '@npmcli/promise-spawn'; -import getPackages from 'get-monorepo-packages'; -import path from 'path'; -import fs from 'fs'; - -/** - * - * @returns list of tags based on package name - * @example ["@matterlabs/hardhat-zksync-solc@0.3.15", "@matterlabs/hardhat-zksync-solc@0.3.16"] - */ -const getCurrentGitTagsForPackage = async (packageName) => { - const { stdout, stderr, code } = await spawn('git', [ - 'tag', - '--points-at', - 'HEAD', - '--column', - ]); - - if (code !== 0) { - throw new Error(stderr.toString()); - } - - return parseRawTags(stdout.toString(), packageName); -}; - -const getChangelogPath = (packageName) => { - const result = getPackages('.').find((p) => p.package.name.includes(packageName)); - if (!result) { - throw new Error(`could not find package with name: ${packageName}.`); - } - - let changelogPath; - for (const fileName of ['CHANGELOG.MD', 'CHANGELOG.md']) { - const myPath = path.join(result.location, fileName); - const pathExists = fs.existsSync(myPath); - if (pathExists) { - changelogPath = myPath; - break; - } - } - - if (!changelogPath) { - console.log(`could not find changelog path for ${result.location}`); - } - - return changelogPath; -}; - -/** - * - * @returns list of tags - * @example ["@matterlabs/hardhat-zksync-solc@0.3.16", "@matterlabs/hardhat-zksync-deploy@0.6.3"] - */ -const createGithubRelease = async (tag, releaseNotes) => { - let args = ['release', 'create', tag, '--title', tag, '--notes', releaseNotes || '']; - - if(tag.includes("alpha") || tag.includes("beta")) { - args.push('--prerelease'); - } - - const { stderr, code } = await spawn('gh', args); - - if (code !== 0) { - throw new Error(stderr.toString()); - } -}; - -/** - * - * @param rawTag - ex. "@matterlabs/hardhat-zksync-solc@0.3.16" - */ -const extractPartsFromTag = (rawTag) => { - const [name, version] = rawTag.split(/@(\d.*)/); - if (!name || !version) { - return undefined; - } - - return { - name, - versionNumber: version?.replace('\n', ''), - raw: rawTag, - }; -}; - -/** - * Removes nullish values. - */ -const exists = (value) => { - return value != null && value !== undefined; -}; - -/** - * - * @param rawTags - string delimited list of tags (e.g. `@matterlabs/hardhat-zksync-solc@0.3.16 @matterlabs/hardhat-zksync-deploy@0.6.3`) - */ -const parseRawTags = (rawTags, packageName) => { - let tags = rawTags.trim().split(' ').map(extractPartsFromTag).filter(exists); - - if (packageName) { - tags = tags.filter((tag) => tag.name.includes(packageName)); - } - - return tags; -}; - -/** - * - * @returns the release notes that correspond to a given tag. - */ -export const parseReleaseNotes = ( - changelogText, - versionNumber -) => { - const h2tag = /(##\s.*\d.*)/gi; - let begin; - let end; - - changelogText.split('\n').forEach((line, idx) => { - if (begin && end) { - return; - } - if (line.includes(versionNumber)) { - begin = idx + 1; - } else if (begin && h2tag.test(line)) { - end = idx - 1; - } - }); - - const result = changelogText.split('\n').filter((_, idx) => { - return idx >= begin && idx <= (end ?? Infinity); - }); - - return result.join('\n'); -}; - -const getReleaseNotes = (tag) => { - const { name, versionNumber } = tag; - const changelogPath = getChangelogPath(name); - if (!changelogPath) { - console.log(`no changelog path for ${name}... skipping.`); - return; - } - - const changelogText = fs.readFileSync(changelogPath, { encoding: 'utf8' }); - const releaseNotes = parseReleaseNotes(changelogText, versionNumber); - if (!releaseNotes) { - console.log( - `Could not find release notes for tags ${tag.raw} in ${changelogPath}.` - ); - }; - - return releaseNotes; -}; - -const createGithubReleaseFromTag = async (tag) => { - const notes = getReleaseNotes(tag); - if (notes) { - console.log( - `\n ---> Outputting release titled: ${tag.raw} with notes: \n ${notes}` - ) - } - - await createGithubRelease(tag.raw, notes); -}; - -const createReleaseFromTags = async (tags) => { - console.log('Processing tags:', tags, '\n'); - - for (const tag of tags) { - await createGithubReleaseFromTag(tag); - } -}; - -export async function main() { - const program = new Command(); - - program - .name('create-github-release-from-tags') - .option('-p, --package <package>', 'Package name to create release for') - .action(async (cmd) => { - const tags = await getCurrentGitTagsForPackage(cmd.package); - return createReleaseFromTags(tags); - }); - - await program.parseAsync(process.argv); -} diff --git a/scripts/create-release-from-tags/run.js b/scripts/create-release-from-tags/run.js deleted file mode 100644 index 24501cb9e..000000000 --- a/scripts/create-release-from-tags/run.js +++ /dev/null @@ -1,8 +0,0 @@ -import { main } from './index.js'; - -main() - .then(() => process.exit(0)) - .catch((err) => { - console.error('Error: ', err); - process.exit(1); - }); diff --git a/yarn.lock b/yarn.lock index 54e65e1ed..7c1bbcd43 100644 --- a/yarn.lock +++ b/yarn.lock @@ -771,70 +771,118 @@ read-yaml-file "^1.1.0" "@matterlabs/hardhat-zksync-chai-matchers@link:packages/hardhat-zksync-chai-matchers": - version "1.2.0" + version "1.2.1" + dependencies: + "@matterlabs/hardhat-zksync-deploy" "1.1.2" + "@matterlabs/hardhat-zksync-solc" "1.0.6" + chai "^4.3.7" + chai-as-promised "^7.1.1" + ethers "^6.7.1" + hardhat "^2.19.4" + ordinal "1.0.3" + zksync-ethers "^6.0.0" "@matterlabs/hardhat-zksync-deploy@link:packages/hardhat-zksync-deploy": - version "1.1.1" + version "1.1.2" dependencies: "@matterlabs/hardhat-zksync-solc" "^1.0.4" + chai "^4.3.6" chalk "4.1.2" ts-morph "^21.0.1" "@matterlabs/hardhat-zksync-ethers@link:packages/hardhat-zksync-ethers": - version "0.0.1-beta.1" + version "0.0.1-beta.2" dependencies: + "@matterlabs/hardhat-zksync-deploy" "1.1.2" + "@matterlabs/hardhat-zksync-solc" "1.0.6" + chai "^4.2.0" chalk "5.3.0" + hardhat "^2.19.4" "@matterlabs/hardhat-zksync-node@link:packages/hardhat-zksync-node": - version "1.0.0" + version "1.0.1" dependencies: "@matterlabs/hardhat-zksync-solc" "^1.0.5" axios "^1.6.2" + chai "^4.3.6" chalk "4.1.2" fs-extra "^11.1.1" + proxyquire "^2.1.3" + sinon "^16.0.0" + sinon-chai "^3.7.0" + undici "^5.14.0" "@matterlabs/hardhat-zksync-solc@link:packages/hardhat-zksync-solc": - version "1.0.5" + version "1.0.6" dependencies: "@nomiclabs/hardhat-docker" "^2.0.0" + chai "^4.3.6" chalk "4.1.2" dockerode "^4.0.0" fs-extra "^11.1.1" proper-lockfile "^4.1.2" semver "^7.5.1" + sinon "^16.0.0" + sinon-chai "^3.7.0" + undici "^5.14.0" "@matterlabs/hardhat-zksync-upgradable@link:packages/hardhat-zksync-upgradable": - version "1.2.0" + version "1.2.1" dependencies: "@matterlabs/hardhat-zksync-deploy" "^1.1.0" "@matterlabs/hardhat-zksync-solc" "^1.0.3" "@openzeppelin/upgrades-core" "^1.31.0" + chalk "4.1.2" + compare-versions "^6.0.0" + ethereumjs-util "^6.2.1" + ethers "^6.7.1" + fs-extra "^7.0.1" + hardhat "^2.19.4" + proper-lockfile "^4.1.1" + solidity-ast "^0.4.51" + zksync-ethers "^6.0.0" "@matterlabs/hardhat-zksync-verify-vyper@link:packages/hardhat-zksync-verify-vyper": - version "0.0.1-alpha.5" + version "0.0.1-alpha.6" dependencies: + "@ethersproject/abi" "^5.1.2" + "@ethersproject/address" "5.7.0" "@matterlabs/hardhat-zksync-vyper" "^1.0.0" + "@nomiclabs/hardhat-vyper" "^3.0.5" axios "^1.6.2" + chai "^4.3.6" chalk "4.1.2" zksync-ethers "^6.0.0" "@matterlabs/hardhat-zksync-verify@link:packages/hardhat-zksync-verify": - version "1.2.1" + version "1.2.2" dependencies: + "@ethersproject/abi" "^5.1.2" + "@ethersproject/address" "5.7.0" "@matterlabs/hardhat-zksync-solc" "^1.0.3" "@nomicfoundation/hardhat-verify" "^2.0.0" + "@openzeppelin/contracts" "^4.9.2" axios "^1.6.2" + cbor "^8.1.0" + chai "^4.3.6" chalk "4.1.2" + debug "^4.1.1" + hardhat "^2.19.4" + sinon "^16.0.0" + sinon-chai "^3.7.0" zksync-ethers "^6.0.0" "@matterlabs/hardhat-zksync-vyper@link:packages/hardhat-zksync-vyper": - version "1.0.4" + version "1.0.5" dependencies: "@nomiclabs/hardhat-docker" "^2.0.0" + chai "^4.3.6" chalk "4.1.2" dockerode "^4.0.0" fs-extra "^11.1.1" semver "^7.5.4" + sinon "^9.0.0" + undici "^5.14.0" "@matterlabs/zksync-contracts@^0.6.1": version "0.6.1" @@ -4236,10 +4284,10 @@ hard-rejection@^2.1.0: resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== -hardhat@^2.19.2: - version "2.19.2" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.19.2.tgz#815819e4efd234941d495decb718b358d572e2c8" - integrity sha512-CRU3+0Cc8Qh9UpxKd8cLADDPes7ZDtKj4dTK+ERtLBomEzhRPLWklJn4VKOwjre9/k8GNd/e9DYxpfuzcxbXPQ== +hardhat@^2.19.4: + version "2.19.4" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.19.4.tgz#5112c30295d8be2e18e55d847373c50483ed1902" + integrity sha512-fTQJpqSt3Xo9Mn/WrdblNGAfcANM6XC3tAEi6YogB4s02DmTf93A8QsGb8uR0KR8TFcpcS8lgiW4ugAIYpnbrQ== dependencies: "@ethersproject/abi" "^5.1.2" "@metamask/eth-sig-util" "^4.0.0" @@ -5674,7 +5722,7 @@ optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" -ordinal@^1.0.3: +ordinal@1.0.3, ordinal@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/ordinal/-/ordinal-1.0.3.tgz#1a3c7726a61728112f50944ad7c35c06ae3a0d4d" integrity sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==