diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fb73ef0c5d7..e4a843c88cd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: - run: yarn prebuild - run: yarn build:cjs - run: tar -czf /tmp/web3-${{ matrix.node }}.js.tar.gz --exclude="./.git" ./ - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: web3-${{ matrix.node }}.js.tar.gz path: /tmp/web3-${{ matrix.node }}.js.tar.gz @@ -42,7 +42,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: ${{ matrix.node }} - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: web3-${{ matrix.node }}.js.tar.gz path: /tmp @@ -59,7 +59,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: ${{ matrix.node }} - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: web3-${{ matrix.node }}.js.tar.gz path: /tmp @@ -73,7 +73,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: 18 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: web3-18.js.tar.gz path: /tmp @@ -88,7 +88,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: 18 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: web3-18.js.tar.gz path: /tmp @@ -128,7 +128,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: ${{ matrix.node }} - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: web3-${{ matrix.node }}.js.tar.gz path: /tmp @@ -158,7 +158,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: ${{ matrix.node }} - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: web3-${{ matrix.node }}.js.tar.gz path: /tmp @@ -183,7 +183,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: ${{ matrix.node }} - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: web3-${{ matrix.node }}.js.tar.gz path: /tmp @@ -206,7 +206,7 @@ jobs: node-version: ${{ matrix.node }} - uses: browser-actions/setup-firefox@latest if: matrix.browser == 'firefox' - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: web3-${{ matrix.node }}.js.tar.gz path: /tmp @@ -230,9 +230,72 @@ jobs: - uses: actions/setup-node@v3 with: node-version: ${{ matrix.node }} - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: web3-${{ matrix.node }}.js.tar.gz path: /tmp - run: tar -xf /tmp/web3-${{ matrix.node }}.js.tar.gz -C ./ - run: yarn build:docs + + benchmark: + name: Benchmark Tests + needs: build + runs-on: ubuntu-latest + strategy: + matrix: + node: [ 18 ] + steps: + - uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node }} + - uses: actions/download-artifact@v4 + with: + name: web3-${{ matrix.node }}.js.tar.gz + path: /tmp + - run: tar -xf /tmp/web3-${{ matrix.node }}.js.tar.gz -C ./ + # @octokit/core not supported on node 16, so I can't add it to the package.json + - run: npm install --no-package-lock --no-save --force @octokit/core + - name: Restore main branch benchmark data + uses: actions/cache/restore@v3 + with: + path: web3-benchmark-main.json + key: ${{ runner.os }}-web3-benchmark-main.json + - run: yarn test:benchmark + - name: Compare benchmark result and make comment + uses: benchmark-action/github-action-benchmark@v1 + with: + # What benchmark tool the output.txt came from + tool: 'benchmarkjs' + # Where the output from the benchmark tool is stored + output-file-path: benchmark-data.txt + # Where the previous data file is stored + external-data-json-path: web3-benchmark-main.json + # Workflow will fail when an alert happens + fail-on-alert: false + # GitHub API token to make a commit comment + github-token: ${{ secrets.GITHUB_TOKEN }} + # Enable alert commit comment + comment-always: true + save-data-file: false + # copy comment from commit to Pull Request + - run: node scripts/copyCommitCommentToPrComment.js ${{ secrets.GITHUB_TOKEN }} ${{github.event.pull_request.head.sha}} ${{github.event.number}} + - name: Compare benchmark result and fail if threshold is reached + uses: benchmark-action/github-action-benchmark@v1 + with: + # What benchmark tool the output.txt came from + tool: 'benchmarkjs' + # Where the output from the benchmark tool is stored + output-file-path: benchmark-data.txt + # Where the previous data file is stored + external-data-json-path: web3-benchmark-main.json + # Workflow will fail when an alert happens + fail-on-alert: true + # Enable alert commit comment + alert-threshold: '100%' + comment-always: false + - name: Save main branch benchmark data + uses: actions/cache/save@v3 + if: github.event_name == 'push' && github.ref == 'refs/heads/4.x' + with: + path: web3-benchmark-main.json + key: ${{ runner.os }}-web3-benchmark-main.json diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml index c1092c4e194..a8782bfa276 100644 --- a/.github/workflows/deploy-docs.yaml +++ b/.github/workflows/deploy-docs.yaml @@ -1,5 +1,5 @@ name: Docs CloudFlare Deploy -on: +on: push: branches-ignore: - "1.x" @@ -25,4 +25,4 @@ jobs: accountId: 2238a825c5aca59233eab1f221f7aefb projectName: web3-js-docs directory: ./docs/build - gitHubToken: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + gitHubToken: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index f85d4f36b67..45c80f83fbd 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,6 @@ tmp/ # Incubed (in3) nodelist packages/web3/.in3/ + +# benchmark results +benchmark-data.txt diff --git a/package.json b/package.json index 2a767bf4d23..e0642f62074 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "test:coverage:integration": "lerna run test:coverage:integration --stream --parallel", "test:unit": "lerna run test:unit --stream --parallel && jest --config=./scripts/jest.config.js", "test:integration": "lerna run test:integration --stream", + "test:benchmark": "lerna run test:benchmark", "test:integration:stress": "lerna run test:integration:stress --stream", "test:e2e:ganache:http": "./scripts/test-runner.sh ganache http", "test:e2e:ganache:ws": "./scripts/test-runner.sh ganache ws", @@ -93,7 +94,7 @@ "test:blackbox:infura:http": "yarn pre-blackbox && ./scripts/verdaccio.sh startBackgroundAndPublish && lerna run test:blackbox:infura:http --stream && yarn post-blackbox", "test:blackbox:infura:ws": "yarn pre-blackbox && ./scripts/verdaccio.sh startBackgroundAndPublish && lerna run test:blackbox:infura:ws --stream && yarn post-blackbox", "test:manual:long-connection-ws": "node packages/web3/test/stress/long_ws_tests/nodejs_test/long_connection_ws.js", - "test:stress":"yarn test:stress:geth:ws && yarn test:stress:geth:http && yarn test:stress:geth:ipc && yarn test:e2e:stress:geth:ws:chrome", + "test:stress": "yarn test:stress:geth:ws && yarn test:stress:geth:http && yarn test:stress:geth:ipc && yarn test:e2e:stress:geth:ws:chrome", "husky:install": "husky install", "husky:uninstall": "husky uninstall", "postinstall": "yarn build", diff --git a/packages/web3-eth-contract/src/contract.ts b/packages/web3-eth-contract/src/contract.ts index 7bce531344d..6b1a12295dd 100644 --- a/packages/web3-eth-contract/src/contract.ts +++ b/packages/web3-eth-contract/src/contract.ts @@ -83,7 +83,7 @@ import { ContractAbiWithSignature, ContractOptions, } from 'web3-types'; -import { format, isDataFormat, keccak256, toChecksumAddress } from 'web3-utils'; +import { format, isDataFormat, keccak256, toChecksumAddress , isContractInitOptions } from 'web3-utils'; import { isNullish, validator, @@ -106,7 +106,6 @@ import { getEstimateGasParams, getEthTxCallParams, getSendTxParams, - isContractInitOptions, isWeb3ContractContext, } from './utils.js'; diff --git a/packages/web3-eth-contract/src/index.ts b/packages/web3-eth-contract/src/index.ts index 22937531980..09052f859a1 100644 --- a/packages/web3-eth-contract/src/index.ts +++ b/packages/web3-eth-contract/src/index.ts @@ -47,5 +47,6 @@ export * from './encoding.js'; export * from './contract.js'; export * from './log_subscription.js'; export * from './types.js'; +export * from './utils.js'; export default Contract; diff --git a/packages/web3-eth-contract/src/utils.ts b/packages/web3-eth-contract/src/utils.ts index f3d45528ede..d2afe72a7b2 100644 --- a/packages/web3-eth-contract/src/utils.ts +++ b/packages/web3-eth-contract/src/utils.ts @@ -48,7 +48,6 @@ const dataInputEncodeMethodHelper = ( if (isNullish(tx.input) && isNullish(tx.data)) { tx[dataInputFill as 'data' | 'input'] = encodeMethodABI(abi, params); } - return { data: tx.data as HexString, input: tx.input as HexString }; }; @@ -164,11 +163,11 @@ export const getEstimateGasParams = ({ return txParams as TransactionWithSenderAPI; }; -export { isContractInitOptions } from 'web3-utils'; - export const isWeb3ContractContext = (options: unknown): options is Web3ContractContext => - typeof options === 'object' && !isNullish(options) && - Object.keys(options).length !== 0 && !isContractInitOptions(options); + typeof options === 'object' && + !isNullish(options) && + Object.keys(options).length !== 0 && + !isContractInitOptions(options); export const getCreateAccessListParams = ({ abi, diff --git a/packages/web3/package.json b/packages/web3/package.json index e09c3dd5ecd..b26832d9f67 100644 --- a/packages/web3/package.json +++ b/packages/web3/package.json @@ -60,13 +60,16 @@ "test:blackbox:geth:ws": "./scripts/black_box_test.sh geth ws", "test:blackbox:infura:http": "./scripts/black_box_test.sh infura http", "test:blackbox:infura:ws": "./scripts/black_box_test.sh infura ws", - "test:e2e:chrome:stress": "npx cypress run --headless --browser chrome" + "test:e2e:chrome:stress": "npx cypress run --headless --browser chrome", + "test:benchmark": "npx ts-node --preferTsExts ../web3/test/benchmark/benchmark.ts" }, "devDependencies": { "@truffle/hdwallet-provider": "^2.0.12", + "@types/benchmark": "^2.1.5", "@types/jest": "^28.1.6", "@typescript-eslint/eslint-plugin": "^5.30.7", "@typescript-eslint/parser": "^5.30.7", + "benchmark": "^2.1.4", "eslint": "^8.20.0", "eslint-config-base-web3": "0.1.0", "eslint-config-prettier": "^8.5.0", diff --git a/packages/web3/test/benchmark/abi.ts b/packages/web3/test/benchmark/abi.ts new file mode 100644 index 00000000000..36ff4a00404 --- /dev/null +++ b/packages/web3/test/benchmark/abi.ts @@ -0,0 +1,28 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ +// eslint-disable-next-line import/no-extraneous-dependencies +import { decodeParameters, encodeParameters } from 'web3-eth-abi'; + +export const abiDecode = async () => { + return decodeParameters( + ['uint256', 'string'], + '0x000000000000000000000000000000000000000000000000000000008bd02b7b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000748656c6c6f212500000000000000000000000000000000000000000000000000', + ); +}; +export const abiEncode = async () => { + return encodeParameters(['uint256', 'string'], ['2345675643', 'Hello!%']); +}; diff --git a/packages/web3/test/benchmark/benchmark.ts b/packages/web3/test/benchmark/benchmark.ts new file mode 100644 index 00000000000..2d0c6eb6006 --- /dev/null +++ b/packages/web3/test/benchmark/benchmark.ts @@ -0,0 +1,47 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ +import Benchmark from 'benchmark'; +import fs from 'fs'; +import path from 'path'; + +import { processingTx } from './processingTx'; +import { + processingContractDeploy, + processingContractMethodSend, + processingContractMethodCall, +} from './processingContract'; +import { abiEncode, abiDecode } from './abi'; +import { sign, verify } from './wallet'; + +const suite = new Benchmark.Suite(); +const results: any[] = []; +suite + .add('processingTx', processingTx) + .add('processingContractDeploy', processingContractDeploy) + .add('processingContractMethodSend', processingContractMethodSend) + .add('processingContractMethodCall', processingContractMethodCall) + .add('abiEncode', abiEncode) + .add('abiDecode', abiDecode) + .add('sign', sign) + .add('verify', verify) + .on('cycle', (event: any) => { + results.push(String(event.target)); + }) + .run({ async: true }) + .on('complete', () => { + fs.writeFileSync(path.join('..', '..', 'benchmark-data.txt'), results.join('\n')); + }); diff --git a/packages/web3/test/benchmark/helpers.ts b/packages/web3/test/benchmark/helpers.ts new file mode 100644 index 00000000000..d70b6dc484f --- /dev/null +++ b/packages/web3/test/benchmark/helpers.ts @@ -0,0 +1,23 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ +// eslint-disable-next-line import/no-extraneous-dependencies +import { Web3Context } from 'web3-core'; + +export const context = new Web3Context({ + // eslint-disable-next-line @typescript-eslint/no-empty-function + request: () => {}, +}); diff --git a/packages/web3/test/benchmark/processingContract.ts b/packages/web3/test/benchmark/processingContract.ts new file mode 100644 index 00000000000..2f1966cdf04 --- /dev/null +++ b/packages/web3/test/benchmark/processingContract.ts @@ -0,0 +1,56 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ +import { AbiFunctionFragment, ContractOptions } from 'web3-types'; +// eslint-disable-next-line +import { getSendTxParams, getEthTxCallParams } from 'web3-eth-contract'; +import { BasicAbi, BasicBytecode } from '../shared_fixtures/build/Basic'; +import accounts from '../shared_fixtures/accounts.json'; + +const abiConstructor = BasicAbi.find(j => j.type === 'constructor') as AbiFunctionFragment; +const abiSendMethod = BasicAbi.find( + j => j.type === 'function' && j.name === 'firesMultiValueEvent', +) as AbiFunctionFragment; +const abiCallMethod = BasicAbi.find( + j => j.type === 'function' && j.name === 'getBoolValue', +) as AbiFunctionFragment; + +export const processingContractDeploy = async () => { + return getSendTxParams({ + abi: abiConstructor, + params: [1123, 'asdasd'], + options: { dataInputFill: 'data', data: BasicBytecode }, + contractOptions: { from: accounts[0].address } as ContractOptions, + }); +}; + +export const processingContractMethodSend = async () => { + return getSendTxParams({ + abi: abiSendMethod, + params: ['asdasd', 1123, true], + options: { dataInputFill: 'data', to: accounts[1].address }, + contractOptions: { from: accounts[0].address } as ContractOptions, + }); +}; + +export const processingContractMethodCall = async () => { + return getEthTxCallParams({ + abi: abiCallMethod, + params: [], + options: { dataInputFill: 'data', to: accounts[1].address }, + contractOptions: { from: accounts[0].address } as ContractOptions, + }); +}; diff --git a/packages/web3/test/benchmark/processingTx.ts b/packages/web3/test/benchmark/processingTx.ts new file mode 100644 index 00000000000..ac748e64726 --- /dev/null +++ b/packages/web3/test/benchmark/processingTx.ts @@ -0,0 +1,38 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ +// eslint-disable-next-line import/no-extraneous-dependencies +import { prepareTransactionForSigning } from 'web3-eth'; +import accounts from '../shared_fixtures/accounts.json'; +import { context } from './helpers'; + +export const processingTx = async () => { + return prepareTransactionForSigning( + { + from: accounts[0].address, + to: accounts[1].address, + value: BigInt(1), + nonce: 0, + chainId: 0, + gas: BigInt(100000), + gasPrice: BigInt(10000), + }, + context, + undefined, + false, + false, + ); +}; diff --git a/packages/web3/test/benchmark/wallet.ts b/packages/web3/test/benchmark/wallet.ts new file mode 100644 index 00000000000..d2237da407e --- /dev/null +++ b/packages/web3/test/benchmark/wallet.ts @@ -0,0 +1,42 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ +// eslint-disable-next-line import/no-extraneous-dependencies +import { Wallet, create, Web3Account, recover } from 'web3-eth-accounts'; +import { Web3AccountProvider } from 'web3-types'; +import { context } from './helpers'; + +const wallet = new Wallet(context.accountProvider as Web3AccountProvider); +const acc = create(); +wallet.add(acc); + +export const sign = async () => { + return (wallet.get(acc.address) as Web3Account).sign('Some data'); +}; + +const signedData = { + message: 'Some data', + messageHash: '0x1da44b586eb0729ff70a73c326926f6ed5a25f5b056e7f47fbc6e58d86871655', + v: '0x1c', + r: '0x297a764ab50bf53381d19ce1dc2b7dd4c456c91e38722ebced5b9f5bb54648f3', + s: '0x41f216d7482608ca295651a7fa4f66a5ee5336d06a9d5b344f8a9160e1e9c2aa', + signature: + '0x297a764ab50bf53381d19ce1dc2b7dd4c456c91e38722ebced5b9f5bb54648f341f216d7482608ca295651a7fa4f66a5ee5336d06a9d5b344f8a9160e1e9c2aa1c', +}; + +export const verify = async () => { + return recover(signedData.message, signedData.v, signedData.r, signedData.s); +}; diff --git a/scripts/copyCommitCommentToPrComment.js b/scripts/copyCommitCommentToPrComment.js new file mode 100755 index 00000000000..2571c0eea03 --- /dev/null +++ b/scripts/copyCommitCommentToPrComment.js @@ -0,0 +1,88 @@ +#!/usr/bin/env node + +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ +const { Octokit } = require('@octokit/core'); +const token = process.argv[2]; +const commitSha = process.argv[3]; +const prNumber = process.argv[4]; + +const owner = 'web3'; +const repo = 'web3.js'; + +const octokit = new Octokit({ + auth: token, +}); + +const run = async () => { + const list = await octokit.request( + `GET /repos/${owner}/${repo}/commits/${commitSha}/comments`, + { + owner, + repo, + commit_sha: commitSha, + headers: { + 'X-GitHub-Api-Version': '2022-11-28', + }, + }, + ); + // get latest commit comment body + let body = list.data[list.data.length - 1].body; + // remove
tag + body = body.replace('
', '').replace('
', ''); + // find if there is already a comment in PR + const comments = await octokit.request( + `GET /repos/${owner}/${repo}/issues/${prNumber}/comments`, + { + owner, + repo, + issue_number: prNumber, + headers: { + 'X-GitHub-Api-Version': '2022-11-28', + }, + }, + ); + const benchMarkComment = comments.data.find(c => c.body.includes(`# Benchmark`)); + if (benchMarkComment) { + // update benchMarkComment + await octokit.request( + `PATCH /repos/${owner}/${repo}/issues/comments/${benchMarkComment.id}`, + { + owner, + repo, + comment_id: benchMarkComment.id, + body, + headers: { + 'X-GitHub-Api-Version': '2022-11-28', + }, + }, + ); + } else { + // create new comment in PR + await octokit.request(`POST /repos/${owner}/${repo}/issues/${prNumber}/comments`, { + owner, + repo, + issue_number: prNumber, + body, + headers: { + 'X-GitHub-Api-Version': '2022-11-28', + }, + }); + } +}; + +run().catch(console.error);