From 59fc43846f0558c4d02b67876edbf88d3a162127 Mon Sep 17 00:00:00 2001 From: Muhammad Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Wed, 13 Dec 2023 16:29:16 +0100 Subject: [PATCH 1/8] fix: The Contract is not using the context wallet passed if context was passed at constructor (#6661) * fix Contract not using context wallet passed at constructor * add integration test * update CHANGELOG.md --- packages/web3-eth-contract/CHANGELOG.md | 1 + packages/web3-eth-contract/src/contract.ts | 9 ++ .../web3/test/integration/contract.test.ts | 95 +++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 packages/web3/test/integration/contract.test.ts diff --git a/packages/web3-eth-contract/CHANGELOG.md b/packages/web3-eth-contract/CHANGELOG.md index 159712d744a..ef88ed80d13 100644 --- a/packages/web3-eth-contract/CHANGELOG.md +++ b/packages/web3-eth-contract/CHANGELOG.md @@ -358,3 +358,4 @@ Documentation: ### Fixed - Fix and error that happen when trying to get past events by calling `contract.getPastEvents` or `contract.events.allEvents()`, if there is no matching events. (#6647) +- Fixed: The Contract is not using the context wallet passed if context was passed at constructor. (#6661) diff --git a/packages/web3-eth-contract/src/contract.ts b/packages/web3-eth-contract/src/contract.ts index eb60285a52d..ac965ac6ef0 100644 --- a/packages/web3-eth-contract/src/contract.ts +++ b/packages/web3-eth-contract/src/contract.ts @@ -483,6 +483,15 @@ export class Contract provider, registeredSubscriptions: contractSubscriptions, }); + + // Init protected properties + if ((contractContext as Web3Context)?.wallet) { + this._wallet = (contractContext as Web3Context).wallet; + } + if ((contractContext as Web3Context)?.accountProvider) { + this._accountProvider = (contractContext as Web3Context).accountProvider; + } + if ( !isNullish(options) && !isNullish(options.data) && diff --git a/packages/web3/test/integration/contract.test.ts b/packages/web3/test/integration/contract.test.ts new file mode 100644 index 00000000000..3b07f56c009 --- /dev/null +++ b/packages/web3/test/integration/contract.test.ts @@ -0,0 +1,95 @@ +/* +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 * as Web3Eth from 'web3-eth'; +import { Web3, Contract } from '../../src/index'; + +import { + ERC20TokenAbi, + // eslint-disable-next-line import/no-relative-packages +} from '../shared_fixtures/contracts/ERC20Token'; + +jest.mock('web3-eth'); + +describe('Contract', () => { + describe('Contract use the the context wallet', () => { + it('should work when created as web.eth.Contract', async () => { + const web3 = new Web3('https://rpc2.sepolia.org'); + const contract = new web3.eth.Contract( + ERC20TokenAbi, + '0x7af963cF6D228E564e2A0aA0DdBF06210B38615D', + ); + + // could be add wallet also as: + // const account = web3.eth.accounts.wallet.add('Private Key').get(0); + const account = web3.eth.accounts.create(); + + expect(contract.wallet).toBeDefined(); + + const sendTransactionSpy = jest + .spyOn(Web3Eth, 'sendTransaction') + .mockImplementation((_objInstance, tx) => { + expect(tx.from).toStrictEqual(account.address); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return { on: jest.fn() } as any; + }); + + await contract.methods.transfer(account.address, 100).send({ from: account?.address }); + + expect(sendTransactionSpy).toHaveBeenLastCalledWith( + expect.any(Object), + expect.objectContaining({ + from: account.address, + }), + expect.any(Object), + expect.any(Object), + ); + }); + it('should work when passed to constructor as Contract(..., web3Context)', async () => { + const web3 = new Web3('https://rpc2.sepolia.org'); + const contract = new Contract( + ERC20TokenAbi, + '0x7af963cF6D228E564e2A0aA0DdBF06210B38615D', + web3, + ); + + // could be add wallet also as: + // const account = web3.eth.accounts.wallet.add('Private Key').get(0); + const account = web3.eth.accounts.create(); + + expect(contract.wallet).toBeDefined(); + + const sendTransactionSpy = jest + .spyOn(Web3Eth, 'sendTransaction') + .mockImplementation((_objInstance, tx) => { + expect(tx.from).toStrictEqual(account.address); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return { on: jest.fn() } as any; + }); + + await contract.methods.transfer(account.address, 100).send({ from: account?.address }); + + expect(sendTransactionSpy).toHaveBeenLastCalledWith( + expect.any(Object), + expect.objectContaining({ + from: account.address, + }), + expect.any(Object), + expect.any(Object), + ); + }); + }); +}); From e1080d9169dcbbf6ff406ab8532e2a6e8aab6fff Mon Sep 17 00:00:00 2001 From: Junaid <86780488+jdevcs@users.noreply.github.com> Date: Wed, 13 Dec 2023 22:22:40 +0500 Subject: [PATCH 2/8] 4.x docs - improvements (#6656) * utils docs export * default to abi page * updated doc exports * subs doc --- docs/docusaurus.config.js | 2 +- packages/web3-eth/src/web3_eth.ts | 179 +++++++++++++----- packages/web3-utils/src/random.ts | 4 + .../web3-utils/src/string_manipulation.ts | 6 +- packages/web3-utils/src/uuid.ts | 4 + packages/web3-utils/src/validation.ts | 4 + scripts/docshelper/functionsdoc.config.js | 4 + 7 files changed, 154 insertions(+), 49 deletions(-) diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 14a572e952b..3091f2d4a7c 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -121,7 +121,7 @@ const config = { position: 'left', }, { - to: '/libdocs/Accounts', + to: '/libdocs/ABI', activeBasePath: '/libdocs/', label: 'Documentation', position: 'left', diff --git a/packages/web3-eth/src/web3_eth.ts b/packages/web3-eth/src/web3_eth.ts index c78b8b420cf..42ca62e7b54 100644 --- a/packages/web3-eth/src/web3_eth.ts +++ b/packages/web3-eth/src/web3_eth.ts @@ -1579,53 +1579,138 @@ export class Web3Eth extends Web3Context console.log(data)); - * logSubscription.on('error', (error: any) => console.log(error)); - * - * ``` - * - * @example **Subscribe to new block headers** - * ```ts - * // Subscribe to `newBlockHeaders` - * const newBlocksSubscription = await web3.eth.subscribe('newBlockHeaders'); - * - * newBlocksSubscription.on('data', async blockhead => { - * console.log('New block header: ', blockhead); - * - * // You do not need the next line, if you like to keep notified for every new block - * await newBlocksSubscription.unsubscribe(); - * console.log('Unsubscribed from new block headers.'); - * }); - * newBlocksSubscription.on('error', error => - * console.log('Error when subscribing to New block header: ', error), - * ); - * ``` - */ + * Lets you subscribe to specific events in the blockchain. + * + * @param name - The subscription you want to subscribe to. + * @param args - Optional additional parameters, depending on the subscription type. + * @returns A subscription object of type {@link RegisteredSubscription}. The object contains: + * - subscription.id: The subscription id, used to identify and unsubscribing the subscription. + * - subscription.subscribe(): Can be used to re-subscribe with the same parameters. + * - subscription.unsubscribe(): Unsubscribes the subscription and returns TRUE in the callback if successful. + * - subscription.args: The subscription arguments, used when re-subscribing. + * + * + * You can use the subscription object to listen on: + * + * - on("data") - Fires on each incoming log with the log object as argument. + * - on("changed") - Fires on each log which was removed from the blockchain. The log will have the additional property "removed: true". + * - on("error") - Fires when an error in the subscription occurs. + * - on("connected") - Fires once after the subscription successfully connected. Returns the subscription id. + * + * @example **Subscribe to Smart Contract events** + * ```ts + * // Subscribe to `logs` + * const logSubscription = web3.eth.subscribe('logs', { + * address: '0x1234567890123456789012345678901234567890', + * topics: ['0x033456732123ffff2342342dd12342434324234234fd234fd23fd4f23d4234'] + * }); + * logSubscription.on('data', (data: any) => console.log(data)); + * logSubscription.on('error', (error: any) => console.log(error)); + * + * ``` + * + * @example **Subscribe to new block headers** + * ```ts + * // Subscribe to `newBlockHeaders` + * const newBlocksSubscription = await web3.eth.subscribe('newBlockHeaders'); + * + * newBlocksSubscription.on('data', async blockhead => { + * console.log('New block header: ', blockhead); + * + * // You do not need the next line, if you like to keep notified for every new block + * await newBlocksSubscription.unsubscribe(); + * console.log('Unsubscribed from new block headers.'); + * }); + * newBlocksSubscription.on('error', error => + * console.log('Error when subscribing to New block header: ', error), + * ); + * ``` + * + * ### subscribe('pendingTransactions') + * + * Subscribes to incoming pending transactions. + * You can subscribe to pending transactions by calling web3.eth.subscribe('pendingTransactions'). + * + * ```ts + * (await web3.eth.subscribe('pendingTransactions')).on('data', console.log); + * ``` + * + * ### subscribe('newHeads') + * ( same as subscribe('newBlockHeaders')) + * Subscribes to incoming block headers. This can be used as timer to check for changes on the blockchain. + * + * The structure of a returned block header is {@link BlockHeaderOutput}: + * + * ```ts + * (await web3.eth.subscribe('newHeads')).on( // 'newBlockHeaders' would work as well + * 'data', + * console.log + * ); + * >{ + * parentHash: '0x9e746a1d906b299def98c75b06f714d62dacadd567c7515d76eeaa8c8074c738', + * sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', + * miner: '0x0000000000000000000000000000000000000000', + * stateRoot: '0xe0f04b04861ecfa95e82a9310d6a7ef7aef8d7417f5209c182582bfb98a8e307', + * transactionsRoot: '0x31ab4ea571a9e10d3a19aaed07d190595b1dfa34e03960c04293fec565dea536', + * logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + * difficulty: 2n, + * number: 21n, + * gasLimit: 11738125n, + * gasUsed: 830006n, + * timestamp: 1678797237n, + * extraData: '0xd883010b02846765746888676f312e32302e31856c696e757800000000000000e0a6e93cf40e2e71a72e493272210c3f43738ccc7e7d7b14ffd51833797d896c09117e8dc4fbcbc969bd21b42e5af3e276a911524038c001b2109b63b8e0352601', + * nonce: 0n + * } + * ``` + * + * ### subscribe('syncing') + * Subscribe to syncing events. This will return `true` when the node is syncing and when it’s finished syncing will return `false`, for the `changed` event. + * + * ```ts + * (await web3.eth.subscribe('syncing')).on('changed', console.log); + * > `true` // when syncing + * + * (await web3.eth.subscribe('syncing')).on('data', console.log); + * > { + * startingBlock: 0, + * currentBlock: 0, + * highestBlock: 0, + * pulledStates: 0, + * knownStates: 0 + * } + * ``` + * + * ### subscribe('logs', options) + * Subscribes to incoming logs, filtered by the given options. If a valid numerical fromBlock options property is set, web3.js will retrieve logs beginning from this point, backfilling the response as necessary. + * + * options: You can subscribe to logs matching a given filter object, which can take the following parameters: + * - `fromBlock`: (optional, default: 'latest') Integer block number, or `'latest'` for the last mined block or `'pending'`, `'earliest'` for not yet mined transactions. + * - `address`: (optional) Contract address or a list of addresses from which logs should originate. + * - `topics`: (optional) Array of 32 Bytes DATA topics. Topics are order-dependent. Each topic can also be an array of DATA with `or` options. + * + * ```ts + * (await web3.eth.subscribe('logs', { + * address: '0xdac17f958d2ee523a2206206994597c13d831ec7', + * })).on('data', console.log); + * + * > { + * removed: false, + * logIndex: 119n, + * transactionIndex: 58n, + * transactionHash: '0x61533efa77937360215069d5d6cb0be09a22af9721e6dc3df59d957833ed8870', + * blockHash: '0xe32bb97084479d32247f66f8b46d00af2fbc3c2db2bc6e5843fe2e4d1ca9b099', + * blockNumber: 18771966n, + * address: '0xdac17f958d2ee523a2206206994597c13d831ec7', + * data: '0x00000000000000000000000000000000000000000000000000000000d88b2e40', + * topics: [ + * '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', + * '0x0000000000000000000000002fb2457f6ec1865dc0d4e7300c696b69c2a1b989', + * '0x00000000000000000000000027fd43babfbe83a81d14665b1a6fb8030a60c9b4' + * ] + * } + *``` + */ + public async subscribe< T extends keyof RegisteredSubscription, ReturnType extends DataFormat = DataFormat, diff --git a/packages/web3-utils/src/random.ts b/packages/web3-utils/src/random.ts index 2898c93acf3..705a6ecee61 100644 --- a/packages/web3-utils/src/random.ts +++ b/packages/web3-utils/src/random.ts @@ -15,6 +15,10 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ +/** + * @module Utils + */ + import { getRandomBytesSync } from 'ethereum-cryptography/random.js'; import { bytesToHex } from './converters.js'; diff --git a/packages/web3-utils/src/string_manipulation.ts b/packages/web3-utils/src/string_manipulation.ts index b7d0371d0b1..88a0a9a5534 100644 --- a/packages/web3-utils/src/string_manipulation.ts +++ b/packages/web3-utils/src/string_manipulation.ts @@ -15,6 +15,10 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ +/** + * @module Utils + */ + import { Numbers } from 'web3-types'; import { NibbleWidthError } from 'web3-errors'; import { isHexStrict, validator, utils as validatorUtils, bigintPower } from 'web3-validator'; @@ -134,7 +138,7 @@ export const toTwosComplement = (value: Numbers, nibbleWidth = 64): string => { * * @example * ```ts - * console.log(web3.utils.fromTwosComplement(''0x0000000000000000000000000000000d', 32')); + * console.log(web3.utils.fromTwosComplement('0x0000000000000000000000000000000d', 32')); * > 13 * * console.log(web3.utils.fromTwosComplement('0x00000000000000000020000000000000', 32)); diff --git a/packages/web3-utils/src/uuid.ts b/packages/web3-utils/src/uuid.ts index 369cdc86c60..08b9152b71c 100644 --- a/packages/web3-utils/src/uuid.ts +++ b/packages/web3-utils/src/uuid.ts @@ -15,6 +15,10 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ +/** + * @module Utils + */ + import { bytesToHex } from './converters.js'; import { randomBytes } from './random.js'; diff --git a/packages/web3-utils/src/validation.ts b/packages/web3-utils/src/validation.ts index f0b25c35f04..76f1de97742 100644 --- a/packages/web3-utils/src/validation.ts +++ b/packages/web3-utils/src/validation.ts @@ -15,6 +15,10 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ +/** + * @module Utils + */ + import { InvalidBlockError } from 'web3-errors'; import { checkAddressCheckSum as checkAddressCheckSumValidator, diff --git a/scripts/docshelper/functionsdoc.config.js b/scripts/docshelper/functionsdoc.config.js index 1c606fbf540..2d9b1e8b4f4 100644 --- a/scripts/docshelper/functionsdoc.config.js +++ b/scripts/docshelper/functionsdoc.config.js @@ -9,6 +9,10 @@ module.exports = { //utils "./packages/web3-utils/src/converters.ts", "./packages/web3-utils/src/hash.ts", + "./packages/web3-utils/src/validation.ts", + "./packages/web3-utils/src/random.ts", + "./packages/web3-utils/src/string_manipulation.ts", + "./packages/web3-utils/src/uuid.ts", //ABI "./packages/web3-eth-abi/src/api/functions_api.ts", "./packages/web3-eth-abi/src/eip_712.ts", From e442fd20f5467236bd01e275ed68741e1d415c0d Mon Sep 17 00:00:00 2001 From: Santiago Trujillo Zuluaga Date: Thu, 14 Dec 2023 05:38:27 +0000 Subject: [PATCH 3/8] corrected ESM import statement (#6658) --- docs/docs/guides/web3_upgrade_guide/1.x/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/guides/web3_upgrade_guide/1.x/index.md b/docs/docs/guides/web3_upgrade_guide/1.x/index.md index 034f7f2d024..c7dc961d20b 100644 --- a/docs/docs/guides/web3_upgrade_guide/1.x/index.md +++ b/docs/docs/guides/web3_upgrade_guide/1.x/index.md @@ -43,9 +43,9 @@ const web3 = new Web3(); attributes={{className: "typescript-tab"}}> ```typescript -const { Web3 } = require('web3'); - import { Web3 } from 'web3'; + +const web3 = new Web3(); ``` From f7d9349d92163254146cd61b1e38a78edaab2d24 Mon Sep 17 00:00:00 2001 From: Muhammad Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Thu, 14 Dec 2023 17:06:13 +0100 Subject: [PATCH 4/8] Write smart contracts Guide (#6628) * write smart contracts guide * fix links in docs * restore a code comment * corrected import statement for require * added tip: discord support * added js code samples, reviewed/edited code samples and changed ABI explanation * edited category structure for user clarity * added note on web3-eth-contract import for better app performance --------- Co-authored-by: your_username Co-authored-by: Alex --- .../guides/smart_contracts/_category_.yml | 5 + ...ng_and_interacting_with_smart_contracts.md | 26 +- docs/docs/guides/smart_contracts/index.md | 492 +++++++++++++++++- packages/web3-types/src/eth_contract_types.ts | 19 +- 4 files changed, 527 insertions(+), 15 deletions(-) create mode 100644 docs/docs/guides/smart_contracts/_category_.yml diff --git a/docs/docs/guides/smart_contracts/_category_.yml b/docs/docs/guides/smart_contracts/_category_.yml new file mode 100644 index 00000000000..d234faa131b --- /dev/null +++ b/docs/docs/guides/smart_contracts/_category_.yml @@ -0,0 +1,5 @@ +label: 'Smart Contracts' +collapsible: true +collapsed: true +link: null +position: 4 \ No newline at end of file diff --git a/docs/docs/guides/smart_contracts/deploying_and_interacting_with_smart_contracts.md b/docs/docs/guides/smart_contracts/deploying_and_interacting_with_smart_contracts.md index 526ca45a058..1a2916345ce 100644 --- a/docs/docs/guides/smart_contracts/deploying_and_interacting_with_smart_contracts.md +++ b/docs/docs/guides/smart_contracts/deploying_and_interacting_with_smart_contracts.md @@ -1,6 +1,6 @@ --- sidebar_position: 1 -sidebar_label: 'Deploying and Interacting with Smart Contracts' +sidebar_label: 'Tutorial: Deploying and Interacting with Smart Contracts' --- import Tabs from '@theme/Tabs'; @@ -24,6 +24,12 @@ Here is a high-level overview of the steps we will be taking in this tutorial: 6. Deploy the smart contract to the Ganache network using web3.js. 7. Interact with the smart contract using web3.js. +:::tip +📝 **Community support:** +If you encounter any issues while following this guide or have questions, don't hesitate to seek assistance. Our friendly community is ready to help you out! +Join our [Discord](https://discord.gg/F4NUfaCC) server and head to the **#web3js-general** channel to connect with other developers and get the support you need. +::: + ## Step 1: Setting up the Environment Before we start writing and deploying our contract, we need to set up our environment. For that, we need to install the following: @@ -108,9 +114,9 @@ Next, create a new file called `compile.js` in your project directory and add th // This code will compile smart contract and generate its ABI and bytecode // Alternatively, you can use something like `npm i solc && npx solcjs MyContract.sol --bin --abi` -import solc from 'solc'; -import path from 'path'; -import fs from 'fs'; +const solc = require( 'solc'); +const path = require('path'); +const fs = require('fs'); const fileName = 'MyContract.sol'; const contractName = 'MyContract'; @@ -340,20 +346,20 @@ async function deploy() { const defaultAccount = providersAccounts[0]; console.log('deployer account:', defaultAccount); - const deployedContract = myContract.deploy({ + const contractDeployer = myContract.deploy({ data: '0x' + bytecode, arguments: [1], }); // optionally, estimate the gas that will be used for development and log it - const gas = await deployedContract.estimateGas({ + const gas = await contractDeployer.estimateGas({ from: defaultAccount, }); console.log('estimated gas:', gas); try { // Deploy the contract to the Ganache network - const tx = await deployedContract.send({ + const tx = await contractDeployer.send({ from: defaultAccount, gas, gasPrice: 10000000000, @@ -396,18 +402,18 @@ async function deploy(): Promise { const defaultAccount: string = providersAccounts[0]; console.log('deployer account:', defaultAccount); - const deployedContract: any = myContract.deploy({ + const contractDeployer: any = myContract.deploy({ data: '0x' + bytecode, arguments: [1], }); - const gas: number = await deployedContract.estimateGas({ + const gas: number = await contractDeployer.estimateGas({ from: defaultAccount, }); console.log('estimated gas:', gas); try { - const tx: any = await deployedContract.send({ + const tx: any = await contractDeployer.send({ from: defaultAccount, gas, gasPrice: 10000000000, diff --git a/docs/docs/guides/smart_contracts/index.md b/docs/docs/guides/smart_contracts/index.md index d87d1ebaa7b..52abc382011 100644 --- a/docs/docs/guides/smart_contracts/index.md +++ b/docs/docs/guides/smart_contracts/index.md @@ -1,5 +1,493 @@ --- -sidebar_position: 4 +sidebar_position: 2 +sidebar_label: 'Mastering Smart Contracts' --- -# Smart Contracts +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Mastering Smart Contracts + +:::info +This guide expects you to have some basic knowledge. If you are just starting, it is recommended to first check out this [Tutorial: Deploying and Interacting with Smart Contracts](./deploying_and_interacting_with_smart_contracts). +::: + +## Contract class + +The `Contract` class is the main object exported by the `web3-eth-contract` package. It is also available in the `web3` package. + +### Importing the Contract Class + +To use the `Contract` class, you'll need to import it from one of two packages: directly from the `web3-eth-contract` package or from the `web3` package. + +Here's an example of importing from each: + + + + +```javascript +// Importing from web3-eth-contract package +const { Contract } = require("web3-eth-contract"); +const contract = new Contract(...); + +// Importing from the main web3 package +const { Contract } = require("web3"); +const contract = new Contract(...); + +// Importing from the main web3 package from inside `web3.eth` namespace +const { Web3 } = require("web3"); +const web3 = new Web3("http://127.0.0.1:8545"); +const contract = new web3.eth.Contract(...); + +// to set the provider for the contract instance: +contract.setProvider('http://127.0.0.1:7545'); +``` + + + + + + +```ts +// Importing from web3-eth-contract package +import { Contract } from "web3-eth-contract"; +const contract = new Contract(...); + +// Importing from the main web3 package +import { Contract } from 'web3'; +const contract = new Contract(...); + +// Importing from the main web3 package from inside `web3.eth` namespace +import { Web3 } from 'web3'; +const web3 = new Web3("http://127.0.0.1:8545"); +const contract = new web3.eth.Contract(...); + +// to set the provider for the contract instance: +contract.setProvider('http://127.0.0.1:7545'); +``` + + + + + +### `Contract` vs `web3.eth.Contract` + +There is a way to create a contract object, other than the ones listed above. That is by accessing an instance of `Web3` object. And this instance of `Web3` is usually called `web3`. + +Actually, the `web3.eth.Contract` is typically how you access the class through a web3 instance that already has a provider setup and that may already has customized configurations. + +:::tip +1. Importing the `web3-eth-contract` module can be beneficial if you want to reduce the size of your application instead of importing the whole `Web3` module. + +2. Note the difference between `Web3.eth.Contract` and `web3instance.eth.Contract` (that is usually named `web3.eth.Contract`) is that the `Web3` (with capital `W`) is used to access the objects organized in namespaces. While `web3instance`, that is usually named `web3`, is to access the properties of this instance. +::: + +Examples: + + + + +```javascript +const { Contract } = require("web3-eth-contract"); + +// instantiating Contract directly with provider URL from Contract package +// alternatively, you can instantiate the Contract without a provider and set it later using contract.setProvider() +const abi = [{...}]; +const address = '0x...'; +const contract = new Contract(abi, address { provider: "http://127.0.0.1:8545" }); + +// the provider can be set like this if not provided at the constructor: +contract.setProvider('http://127.0.0.1:7545'); + +// using Contract from a web3 instance +const web3 = new Web3('http://localhost:8545'); +const contract = new web3.eth.Contract(abi, address); +// no need to pass the provider to this contract instance. +// because it will have the same provider of the web3 instance. +``` + + + + + + +```ts +import { Contract } from "web3-eth-contract"; + +// instantiating Contract directly with provider URL from Contract package +// alternatively, you can instantiate the Contract without a provider and set it later using contract.setProvider() +const abi = [{...}]; +const address = '0x...'; +const contract = new Contract(abi, address { provider: "http://127.0.0.1:8545" }); + +// the provider can be set like this if not provided at the constructor: +contract.setProvider('http://127.0.0.1:7545'); + +// using Contract from a web3 instance +const web3 = new Web3('http://localhost:8545'); +const contract = new web3.eth.Contract(abi, address); +// no need to pass the provider to this contract instance. +// because it will have the same provider of the web3 instance. +``` + + + + + +### Constructor Parameters + +When you instantiate a `Contract`, you primarily provide one or two parameters, and sometimes 3 parameters: + +1. **ABI (Application Binary Interface):** The ABI tells the `Contract` how to format calls and transactions so that the contract can understand them. + +:::tip +If you do not know how to get the contract ABI, we recommend you to check the Step 4 at the [Deploying and Interacting with Smart Contracts](./deploying_and_interacting_with_smart_contracts#step-4-compile-the-solidity-code-using-the-solidity-compiler-and-get-its-abi-and-bytecode) tutorial. And to look into the guide: [Infer Contract Types from JSON Artifact](./infer_contract_types_guide/). +::: + +2. (optional) **Contract Address:** The Ethereum address at which your contract is deployed. If the contract is not deployed yet, do not pass a second parameter or pass `undefined` to it. + + +3. (optional) **Contract Options:** you can provide contract options as a third parameter. + +```ts +const abi = [{...}]; /* obtained ABI as an array */; +const address = '0x...'; // Deployed address of the contract + +const myContract = new Contract(abi, address, { + defaultGasPrice: '20000000000', // default gas price in wei, 20 gwei in this case + defaultGas: 5000000, // provide the gas limit for transactions + //...other optional properties +}); +``` + +### Contract Properties and Methods + +The `Contract` class comes equipped with a range of properties and methods for contract interaction. We encourage you to check them at the [Contract API Documentation section](/api/web3-eth-contract/class/Contract) + +#### Properties include + +- **config**: The set of configurations for the contract instance that is defaults to the same values as the `web3` object instance. But, it allows for using a different configurations for a specific contract instance. So, in most cases, you would use `web3.eth.Contract` and keep the configurations of the parent context (from the `web3` instance). Except if there is something you need to handle differently for only a specific contract instance. + + Here is an example on how to set a value of a specific config variable on a contract instance: + + + + + +```javascript +const {Web3} = require('web3'); + +// Set up a connection to a testnet or Ethereum network +const web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:8545')); //or new Web3('http://127.0.0.1:8545') + +// Create a new contract object using the ABI and bytecode +const abi = [{...}] +const myContract = new web3.eth.Contract(abi); +console.log(myContract.config.handleRevert); //false + +// This will set `handleRevert` to `true` only on `myContract` instance: +myContract.handleRevert = true; // same as: myContract.config.handleRevert +console.log(myContract.config.handleRevert); //true + +``` + + + + +```ts +import {Web3} from 'web3'; + +// Set up a connection to a testnet or Ethereum network +const web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:8545')); //or new Web3('http://127.0.0.1:8545') + +// Create a new contract object using the ABI and bytecode +const abi = [{...}] +const myContract = new web3.eth.Contract(abi); +console.log(myContract.config.handleRevert); //false + +// This will set `handleRevert` to `true` only on `myContract` instance: +myContract.handleRevert = true; // same as: myContract.config.handleRevert +console.log(myContract.config.handleRevert); //true + +``` + + + + + +More on the `config` properties in the [API documentation](/api/web3/namespace/core/#Web3ConfigOptions) + + +- **options**: The set of options for the contract instance. + This options can be passed as the third parameter to the constructor. And can be accessed also later with contractInstance.options. + +```ts +myContract.options = { + address: '0x1234567890123456789012345678901234567891', + from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe', + gasPrice: '10000000000000', + gas: 1000000 +} + +// If the smart contract is not deployed yet, the property `address` will be filled automatically after deployment succeed. +// If the smart contract is already deployed, you can set the `address`: +myContract.options.address = '0x1234567890123456789012345678901234567891'; +// this is the same as the second parameter in the constructor: +// new Contract(abi, `address`); + +// set default from address +myContract.options.from = '0x1234567890123456789012345678901234567891'; +// set default gas price in wei +myContract.options.gasPrice = '20000000000000'; +// set the gas limit +myContract.options.gas = 5000000; + +// you can also use this to update the ABI of the contract +myContract.options.jsonInterface = [{...}]; // ABI +// this is the same as the first parameter in the Contract constructor: +// new Contract(`abi`, address) +``` + +- **methods**: An object mapping your contract's methods for easy calling. + This property provide a strongly typed methods depending on the passed ABI. And here is how to use it: + +```ts +// note that the bellow METHOD_NAME and METHOD_PARAMETERS are +// according to the early provided ABI. +// And TypeScript intellisense will help you with. + +// to call a method by sending a transaction +contract.methods.METHOD_NAME(METHOD_PARAMETERS).send(); +// you need to specify the account (from) that will be used to sign and send the transaction +contract.methods.METHOD_NAME(METHOD_PARAMETERS).send({from: "0x..."}); + +// to call a view or pure method that does not send a transaction +contract.methods.METHOD_NAME(METHOD_PARAMETERS).call(); +``` + +- **events**: An object mapping your contract's events, allowing you to subscribe to them. + + And here is an example on how to use it: + +```ts +//If you want to filter events, create `options`: +const options: ContractEventOptions = { + // the following means all events where `myNumber` is `12` or `13` + filter: myNumber: [12,13]; + // you can specify the block from where you like to start + // listing to events + fromBlock: 'earliest'; + + // You can also manually set the topics for the event filter. + // If given the filter property and event signature, + // (topic[0]) will not be set automatically. + // Each topic can also be a nested array of topics that behaves + // as `or` operation between the given nested topics. + topics?: ['0x617cf8a4400dd7963ed519ebe655a16e8da1282bb8fea36a21f634af912f54ab']; +} + +// if you would like to not filter, don't pass `options`. +const event = await myContract.events.MyEvent(options); + +event.on('data', (data) => { + console.log(data) +}); +event.on('error', (err: Error) => { + console.log(err); +}); +``` + +To subscribe all events use the special `allEvents`: + +```ts +const event = await myContract.events.allEvents(options); +``` + +#### Methods include + +- **deploy**: For deploying a new contract instance. + +```ts +// this will give you the accounts from the connected provider +// For example, if you are using MetaMask, it will be the account available. +const providersAccounts = await web3.eth.getAccounts(); +const defaultAccount = providersAccounts[0]; +console.log('deployer account:', defaultAccount); + +// NOTE: If you want to manually unlock an account with a private key, you can use wallet.add(privateKey). +// however, exercise caution and ensure the security of your private keys. + +// this is how to obtain the deployer function, +// so you can estimate its needed gas and deploy it. +const contractDeployer = myContract.deploy({ + data: bytecode, // prefix the bytecode with '0x' if it is note already + arguments: [1], // provide the parameters in an array; in this case, it's the number `1`. +}); + +// optionally, estimate the gas that will be used for development and log it +const gas = await contractDeployer.estimateGas({ + from: defaultAccount, +}); +console.log('estimated gas:', gas); + +// Deploy the contract to the Ganache network +const tx = await contractDeployer.send({ + from: defaultAccount, + gas, + gasPrice: 10000000000, +}); +console.log('Contract deployed at address: ' + tx.options.address); +``` + +:::tip +If you do not know how to get the contract bytecode, we recommend you to check the Step 4 at the [Deploying and Interacting with Smart Contracts](./deploying_and_interacting_with_smart_contracts#step-4-compile-the-solidity-code-using-the-solidity-compiler-and-get-its-abi-and-bytecode) tutorial. +::: + +- **getPastEvents**: Gets past events for this contract. It differs from `events` properties that it returns the past events as an array, rather than allowing to subscribe to them like when using `events` properties. More on the [API documentation](/api/web3-eth-contract/class/Contract#getPastEvents) + + +- **setProvider**: This allows you to set a specific provider for a contract instance. As highlighted early in this guide, this is especially handy if you are importing the `Contract` object from `web3-eth-contract` and then you will need to set the provider while there is no `web3` context to read the provider from. + + + + + +```javascript +// Importing from web3-eth-contract package +const { Contract } = require('web3-eth-contract'); +const contract = new Contract(...); + +// to set the provider for the contract instance +contract.setProvider('yourProvider'); +``` + + + + +```ts +// Importing from web3-eth-contract package +import { Contract } from 'web3-eth-contract'; +const contract = new Contract(...); + +// to set the provider for the contract instance +contract.setProvider('yourProvider'); +``` + + + + + +## ABI and Bytecode + +### ABI +The ABI is the Application Binary Interface ( ABI ) of a smart contract. Which defines the methods and variables that are available in a smart contract and which we can use to interact with that smart contract. + + For example, for the following solidity code: + +```js +// SPDX-License-Identifier: MIT +pragma solidity 0.8.0; + +contract MyContract { + uint256 public myNumber; + + constructor(uint256 _myNumber) { + myNumber = _myNumber; + } + + function setMyNumber(uint256 _myNumber) public { + myNumber = _myNumber; + } +} +``` +Its ABI would be: +```ts +const abi = [ + { + inputs: [{ internalType: 'uint256', name: '_myNumber', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [], + name: 'myNumber', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: '_myNumber', type: 'uint256' }], + name: 'setMyNumber', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const +``` + +### Bytecode +Bytecode results from the compilation of a Solidity code. Bytecode is generally compact numeric codes, constants, and other pieces of information. Where each instruction step is an operation which is referred to as “opcodes,” which are typically one-byte (eight-bits) long. This is why they’re called “bytecode”—one-byte opcodes. +And this bytecode is usually represented as a long hex string similar to the following; +```ts +const bytecode = '0x60806040523480156100115760006000fd5b50604051610224380380610224...' +``` + +:::info +And as mentioned in the tips inside previous sections: +If you do not know how to get the contract ABI and bytecode, we recommend you to check the Step 4 at the [Deploying and Interacting with Smart Contracts](./deploying_and_interacting_with_smart_contracts#step-4-compile-the-solidity-code-using-the-solidity-compiler-and-get-its-abi-and-bytecode) tutorial. +::: + +### Do I always need the contract ByteCode? +The short answer is yes, only if you need to deploy the smart contract yourself. And below is more elaboration on this. + +Basically, with every Contract instance, there are 2 cases. First case is when you want to deploy a smart contract. And in this case, you will need to provide the bytecode of this smart contract. +```ts +import {Contract} from 'web3-eth-contract'; + +const myContract = new Contract(abi, undefined, options); +// if there is no options to be passed you can write: +const myContract = new Contract(abi); + +await myContract.deploy({ + data: '0x' + bytecode, + + // the smart contract constructor arguments in an array + arguments: [arg1, arg2], +}).send({ + from: someAccount, + ... +}); + +// the contract address will be filled automatically here after deployment: +myContract.options.address +``` + +And the other case, is when you want to interact with an already deployed smart contract. In this scenario, you will need to provide the address of the already deployed smart contract. + +```ts +import {Contract} from 'web3-eth-contract'; + +const myContract = new Contract(abi, smartContractAddress, options); +// if there is no options to be passed you can write: +const myContract = new Contract(abi, smartContractAddress); +``` + +### Do I always need the contract ABI? +The answer is yes, you need the ABI, especially if you want to enjoy TypeScript IntelliSense, and we strongly recommend doing so. The ABI informs your development environment about the contract's structure, allowing for improved code suggestions and type checking. + +If you choose not to provide the ABI, you won't be able to interact with the contract's methods correctly, and you'll miss out on IntelliSense support. + + +```ts +const myContract = new Contract(abi, address || undefined, options); +// remember that address can be empty if the contract is not deployed yet. +// or you can set the address to directly interact with the contract. +``` diff --git a/packages/web3-types/src/eth_contract_types.ts b/packages/web3-types/src/eth_contract_types.ts index 15d23b36470..fb34328ce21 100644 --- a/packages/web3-types/src/eth_contract_types.ts +++ b/packages/web3-types/src/eth_contract_types.ts @@ -83,32 +83,42 @@ export interface PayableCallOptions extends NonPayableCallOptions { } export type ContractAbiWithSignature = ReadonlyArray; + +/** + * Represents the options for a contract. + */ export interface ContractOptions { /** * The maximum gas provided for a transaction (gas limit). */ readonly gas?: Uint; + /** * The gas price in wei to use for transactions. */ readonly gasPrice?: Uint; + /** * The address transactions should be made from. */ readonly from?: Address; + /** * The byte code of the contract. Used when the contract gets {@link Contract.deploy | deployed} */ readonly input?: Bytes; + /** * The byte code of the contract. Used when the contract gets {@link Contract.deploy | deployed} */ readonly data?: Bytes; + /** * The {@doclink glossary/json_interface | json interface} object derived from the [ABI](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) of this contract. * * Re-setting this will regenerate the methods and events of the contract instance. * + * @example * ```ts * myContract.options.jsonInterface; * > [{ @@ -116,17 +126,17 @@ export interface ContractOptions { * "name":"foo", * "inputs": [{"name":"a","type":"uint256"}], * "outputs": [{"name":"b","type":"address"}], - * "signature": "0x...", + * "signature": "0x...", * },{ * "type":"event", * "name":"Event", * "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], - * "signature": "0x...", + * "signature": "0x...", * }] * * // Set a new ABI interface * // Note: the "signature" of every function and event's ABI is not needed to be provided when assigning. - * // It will be calculated and set automatically inside the setter. + * // It will be calculated and set automatically inside the setter. * myContract.options.jsonInterface = [...]; * ``` */ @@ -138,6 +148,7 @@ export interface ContractOptions { * * The address will be stored in lowercase. * + * @example * ```ts * myContract.options.address; * > '0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae' @@ -147,10 +158,12 @@ export interface ContractOptions { * ``` */ address?: Address; // All transactions generated by web3.js from this contract will contain this address as the "to". + /** * The max priority fee per gas to use for transactions. */ maxPriorityFeePerGas?: Uint; + /** * The max fee per gas to use for transactions. */ From fa4c72b33f5ec7a0ca8f79f74e788a1a79b9a5d7 Mon Sep 17 00:00:00 2001 From: Muhammad Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Fri, 15 Dec 2023 17:22:44 +0100 Subject: [PATCH 5/8] Allow the `deploy` function to accept parameters even when no ABI was provided to the Contract (#6635) * allow deploy to accept parameters even when no ABI * update CHANGELOG.md --- .../web3-eth-abi/src/api/parameters_api.ts | 22 +- packages/web3-eth-abi/src/coders/encode.ts | 86 ++- .../test/unit/encodeDecodeParams.test.ts | 13 +- packages/web3-eth-contract/CHANGELOG.md | 4 + packages/web3-eth-contract/src/contract.ts | 1 - packages/web3-eth-contract/src/encoding.ts | 17 +- .../contract_estimateGas_without_0x.test.ts | 2 - .../test/unit/contract.test.ts | 33 + packages/web3-types/src/eth_abi_types.ts | 21 +- packages/web3-utils/src/converters.ts | 6 +- yarn.lock | 595 +++++++++++++++++- 11 files changed, 760 insertions(+), 40 deletions(-) diff --git a/packages/web3-eth-abi/src/api/parameters_api.ts b/packages/web3-eth-abi/src/api/parameters_api.ts index 9aa590e64bc..d9cbd106673 100644 --- a/packages/web3-eth-abi/src/api/parameters_api.ts +++ b/packages/web3-eth-abi/src/api/parameters_api.ts @@ -23,26 +23,10 @@ along with web3.js. If not, see . import { AbiError } from 'web3-errors'; import { AbiInput, HexString } from 'web3-types'; import { decodeParameters as decodeParametersInternal } from '../coders/decode.js'; -import { encodeParameters as encodeParametersInternal } from '../coders/encode.js'; +import { encodeParameters } from '../coders/encode.js'; + +export { encodeParameters, inferTypesAndEncodeParameters } from '../coders/encode.js'; -/** - * Encodes a parameter based on its type to its ABI representation. - * @param abi - An array of {@link AbiInput}. See [Solidity's documentation](https://solidity.readthedocs.io/en/v0.5.3/abi-spec.html#json) for more details. - * @param params - The actual parameters to encode. - * @returns - The ABI encoded parameters - * @example - * ```ts - * const res = web3.eth.abi.encodeParameters( - * ["uint256", "string"], - * ["2345675643", "Hello!%"] - * ); - * - * console.log(res); - * > 0x000000000000000000000000000000000000000000000000000000008bd02b7b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000748656c6c6f212500000000000000000000000000000000000000000000000000 - * ``` - */ -export const encodeParameters = (abi: ReadonlyArray, params: unknown[]): string => - encodeParametersInternal(abi, params); /** * Encodes a parameter based on its type to its ABI representation. diff --git a/packages/web3-eth-abi/src/coders/encode.ts b/packages/web3-eth-abi/src/coders/encode.ts index d0d8260db16..791c86c827d 100644 --- a/packages/web3-eth-abi/src/coders/encode.ts +++ b/packages/web3-eth-abi/src/coders/encode.ts @@ -15,20 +15,100 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ import { AbiError } from 'web3-errors'; -import { AbiInput } from 'web3-types'; +import { AbiInput, AbiParameter } from 'web3-types'; +import { toHex } from 'web3-utils'; import { utils } from 'web3-validator'; import { encodeTuple } from './base/index.js'; import { toAbiParams } from './utils.js'; +/** + * @param params - The params to infer the ABI from + * @returns The inferred ABI + * @example + * ``` + * inferParamsAbi([1, -1, 'hello', '0x1234', ]) + * ``` + * > [{ type: 'int256' }, { type: 'uint256' }, { type: 'string' }, { type: 'bytes' }] + * ``` + */ +function inferParamsAbi(params: unknown[]): ReadonlyArray { + const abi: AbiParameter[] = []; + params.forEach(param => { + if (Array.isArray(param)) { + const inferredParams = inferParamsAbi(param); + abi.push({ + type: 'tuple', + components: inferredParams, + name: '', + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + } as AbiParameter); + } else { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + abi.push({ type: toHex(param as any, true) } as AbiParameter); + } + }); + return abi; +} + +/** + * Encodes a parameter based on its type to its ABI representation. + * @param abi - An array of {@link AbiInput}. See [Solidity's documentation](https://solidity.readthedocs.io/en/v0.5.3/abi-spec.html#json) for more details. + * @param params - The actual parameters to encode. + * @returns - The ABI encoded parameters + * @example + * ```ts + * const res = web3.eth.abi.encodeParameters( + * ["uint256", "string"], + * ["2345675643", "Hello!%"] + * ); + * + * console.log(res); + * > 0x000000000000000000000000000000000000000000000000000000008bd02b7b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000748656c6c6f212500000000000000000000000000000000000000000000000000 + * ``` + */ export function encodeParameters(abi: ReadonlyArray, params: unknown[]): string { - if (abi.length !== params.length) { + if (abi?.length !== params.length) { throw new AbiError('Invalid number of values received for given ABI', { - expected: abi.length, + expected: abi?.length, received: params.length, }); } + const abiParams = toAbiParams(abi); return utils.uint8ArrayToHexString( encodeTuple({ type: 'tuple', name: '', components: abiParams }, params).encoded, ); } + +/** + * Infer a smart contract method parameter type and then encode this parameter. + * @param params - The parameters to encode. + * @returns - The ABI encoded parameters + * + * @remarks + * This method is useful when you don't know the type of the parameters you want to encode. It will infer the type of the parameters and then encode them. + * However, it is not recommended to use this method when you know the type of the parameters you want to encode. In this case, use the {@link encodeParameters} method instead. + * The type inference is not perfect and can lead to unexpected results. Especially when you want to encode an array, uint that is not uint256 or bytes.... + * @example + * ```ts + * const res = web3.eth.abi.encodeParameters( + * ["2345675643", "Hello!%"] + * ); + * + * console.log(res); + * > 0x000000000000000000000000000000000000000000000000000000008bd02b7b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000748656c6c6f212500000000000000000000000000000000000000000000000000 + * ``` + */ +export function inferTypesAndEncodeParameters(params: unknown[]): string { + try { + const abiParams = inferParamsAbi(params); + return utils.uint8ArrayToHexString( + encodeTuple({ type: 'tuple', name: '', components: abiParams }, params).encoded, + ); + } catch (e) { + // throws If the inferred params type caused an error + throw new AbiError('Could not infer types from given params', { + params, + }); + } +} diff --git a/packages/web3-eth-abi/test/unit/encodeDecodeParams.test.ts b/packages/web3-eth-abi/test/unit/encodeDecodeParams.test.ts index 63916a598ab..184ed0fcf4a 100644 --- a/packages/web3-eth-abi/test/unit/encodeDecodeParams.test.ts +++ b/packages/web3-eth-abi/test/unit/encodeDecodeParams.test.ts @@ -16,7 +16,7 @@ along with web3.js. If not, see . */ import { AbiInput } from 'web3-types'; -import { decodeParameters, encodeParameters } from '../../src'; +import { decodeParameters, encodeParameters, inferTypesAndEncodeParameters } from '../../src'; import testsData from '../fixtures/abitestsdata.json'; import { deepEqualTolerateBigInt, removeKey } from './test_utils'; @@ -26,6 +26,17 @@ describe('encodeParameters decodeParameters tests should pass', () => { expect(encodedResult).toEqual(encoderTestObj.encoded); }); + it.each(testsData)(`unit test of encodeParameters - $name`, encoderTestObj => { + // skip for types that are not supported by infer-types + // the unsupported types are uint(other than 256), int(other than 256), bytes(that has a number like bytes1 or bytes2), and arrays + if (/((? { const decodedResult = decodeParameters( [decoderTestObj.type] as AbiInput[], diff --git a/packages/web3-eth-contract/CHANGELOG.md b/packages/web3-eth-contract/CHANGELOG.md index ef88ed80d13..583674ff7dd 100644 --- a/packages/web3-eth-contract/CHANGELOG.md +++ b/packages/web3-eth-contract/CHANGELOG.md @@ -355,6 +355,10 @@ Documentation: ## [Unreleased] +### Changed + +- Allow the `deploy` function to accept parameters, even when no ABI was provided to the `Contract`(#6635) + ### Fixed - Fix and error that happen when trying to get past events by calling `contract.getPastEvents` or `contract.events.allEvents()`, if there is no matching events. (#6647) diff --git a/packages/web3-eth-contract/src/contract.ts b/packages/web3-eth-contract/src/contract.ts index ac965ac6ef0..7bce531344d 100644 --- a/packages/web3-eth-contract/src/contract.ts +++ b/packages/web3-eth-contract/src/contract.ts @@ -743,7 +743,6 @@ export class Contract if (!abi) { abi = { type: 'constructor', - inputs: [], stateMutability: '', } as AbiConstructorFragment; } diff --git a/packages/web3-eth-contract/src/encoding.ts b/packages/web3-eth-contract/src/encoding.ts index aa06db45ae8..45965262f84 100644 --- a/packages/web3-eth-contract/src/encoding.ts +++ b/packages/web3-eth-contract/src/encoding.ts @@ -35,6 +35,7 @@ import { encodeFunctionSignature, encodeParameter, encodeParameters, + inferTypesAndEncodeParameters, isAbiConstructorFragment, jsonInterfaceMethodToString, } from 'web3-eth-abi'; @@ -120,16 +121,22 @@ export const encodeMethodABI = ( deployData?: HexString, ) => { const inputLength = Array.isArray(abi.inputs) ? abi.inputs.length : 0; - if (inputLength !== args.length) { + if (abi.inputs && inputLength !== args.length) { throw new Web3ContractError( `The number of arguments is not matching the methods required number. You need to pass ${inputLength} arguments.`, ); } - const params = encodeParameters(Array.isArray(abi.inputs) ? abi.inputs : [], args).replace( - '0x', - '', - ); + let params: string; + if (abi.inputs) { + params = encodeParameters(Array.isArray(abi.inputs) ? abi.inputs : [], args).replace( + '0x', + '', + ); + } else { + params = inferTypesAndEncodeParameters(args).replace('0x', ''); + } + if (isAbiConstructorFragment(abi)) { if (!deployData) diff --git a/packages/web3-eth-contract/test/integration/contract_estimateGas_without_0x.test.ts b/packages/web3-eth-contract/test/integration/contract_estimateGas_without_0x.test.ts index 91d484665eb..d74a901e265 100644 --- a/packages/web3-eth-contract/test/integration/contract_estimateGas_without_0x.test.ts +++ b/packages/web3-eth-contract/test/integration/contract_estimateGas_without_0x.test.ts @@ -56,8 +56,6 @@ describe('contract', () => { const myContract = contract.deploy({ data: '608060405234801561001057600080fd5b506040516101d93803806101d983398181016040528101906100329190610054565b806000819055505061009e565b60008151905061004e81610087565b92915050565b60006020828403121561006657600080fd5b60006100748482850161003f565b91505092915050565b6000819050919050565b6100908161007d565b811461009b57600080fd5b50565b61012c806100ad6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806323fd0e401460375780636ffd773c146051575b600080fd5b603d6069565b6040516048919060bf565b60405180910390f35b6067600480360381019060639190608c565b606f565b005b60005481565b8060008190555050565b60008135905060868160e2565b92915050565b600060208284031215609d57600080fd5b600060a9848285016079565b91505092915050565b60b98160d8565b82525050565b600060208201905060d2600083018460b2565b92915050565b6000819050919050565b60e98160d8565b811460f357600080fd5b5056fea2646970667358221220d28cf161457f7936995800eb9896635a02a559a0561bff6a09a40bfb81cd056564736f6c63430008000033', - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error arguments: [1], }); diff --git a/packages/web3-eth-contract/test/unit/contract.test.ts b/packages/web3-eth-contract/test/unit/contract.test.ts index f721ab8936c..e86d04cc4cd 100644 --- a/packages/web3-eth-contract/test/unit/contract.test.ts +++ b/packages/web3-eth-contract/test/unit/contract.test.ts @@ -263,6 +263,39 @@ describe('Contract', () => { sendTransactionSpy.mockClear(); }); + it('should deploy contract with input property with no ABI', async () => { + const input = `${GreeterBytecode}0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b4d79204772656574696e67000000000000000000000000000000000000000000`; + const contract = new Contract([]); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const sendTransactionSpy = jest + .spyOn(eth, 'sendTransaction') + .mockImplementation((_objInstance, tx) => { + expect(tx.to).toBeUndefined(); + expect(tx.gas).toStrictEqual(sendOptions.gas); + expect(tx.gasPrice).toBeUndefined(); + expect(tx.from).toStrictEqual(sendOptions.from); + expect(tx.input).toStrictEqual(input); // padded data + + const newContract = contract.clone(); + newContract.options.address = deployedAddr; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Promise.resolve(newContract) as any; + }); + + const deployedContract = await contract + .deploy({ + input: GreeterBytecode, + arguments: ['My Greeting'], + }) + .send(sendOptions); + + expect(deployedContract).toBeDefined(); + expect(deployedContract.options.address).toStrictEqual(deployedAddr); + sendTransactionSpy.mockClear(); + }); + it('should deploy contract with data property', async () => { const data = `${GreeterBytecode}0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b4d79204772656574696e67000000000000000000000000000000000000000000`; const contract = new Contract(GreeterAbi); diff --git a/packages/web3-types/src/eth_abi_types.ts b/packages/web3-types/src/eth_abi_types.ts index acd75e5eb30..0f461693602 100644 --- a/packages/web3-types/src/eth_abi_types.ts +++ b/packages/web3-types/src/eth_abi_types.ts @@ -326,12 +326,21 @@ export type ContractConstructor = { }; }['constructor']; -export type ContractConstructorArgs = { - [Abi in FilterAbis< - Abis, - AbiConstructorFragment & { type: 'constructor' } - > as 'constructor']: ContractMethodInputParameters; -}['constructor']; +export type ContractConstructorArgs = FilterAbis< + Abis, + AbiConstructorFragment & { + type: 'constructor'; + } +> extends never + ? any + : { + [Abi in FilterAbis< + Abis, + AbiConstructorFragment & { + type: 'constructor'; + } + > as 'constructor']: ContractMethodInputParameters; + }['constructor']; export type ContractMethod = { readonly Abi: Abi; diff --git a/packages/web3-utils/src/converters.ts b/packages/web3-utils/src/converters.ts index ef7f1307a77..a93eea80cf0 100644 --- a/packages/web3-utils/src/converters.ts +++ b/packages/web3-utils/src/converters.ts @@ -27,6 +27,7 @@ import { isHex, isHexStrict, isInt, + isUInt, isNullish, utils, utils as validatorUtils, @@ -372,9 +373,12 @@ export const toHex = ( if (isHexStrict(value)) { return returnType ? 'bytes' : value; } - if (isHex(value) && !isInt(value)) { + if (isHex(value) && !isInt(value) && !isUInt(value)) { return returnType ? 'bytes' : `0x${value}`; } + if (isHex(value) && !isInt(value) && isUInt(value)) { + return returnType ? 'uint' : numberToHex(value); + } if (!Number.isFinite(value)) { return returnType ? 'string' : utf8ToHex(value); diff --git a/yarn.lock b/yarn.lock index 86dca2bbc6b..18cbb781cec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -148,6 +148,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== +"@babel/helper-plugin-utils@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + "@babel/helper-simple-access@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" @@ -266,6 +271,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" + integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -744,6 +756,18 @@ jest-util "^28.1.3" slash "^3.0.0" +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + "@jest/core@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7" @@ -779,6 +803,40 @@ slash "^3.0.0" strip-ansi "^6.0.0" +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== + dependencies: + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + "@jest/environment@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-28.1.3.tgz#abed43a6b040a4c24fdcb69eab1f97589b2d663e" @@ -806,6 +864,13 @@ dependencies: jest-get-type "^28.0.2" +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + "@jest/expect@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72" @@ -814,6 +879,14 @@ expect "^28.1.3" jest-snapshot "^28.1.3" +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== + dependencies: + expect "^29.7.0" + jest-snapshot "^29.7.0" + "@jest/fake-timers@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.3.tgz#230255b3ad0a3d4978f1d06f70685baea91c640e" @@ -847,6 +920,16 @@ "@jest/expect" "^28.1.3" "@jest/types" "^28.1.3" +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" + "@jest/reporters@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.3.tgz#9adf6d265edafc5fc4a434cfb31e2df5a67a369a" @@ -878,6 +961,36 @@ terminal-link "^2.0.0" v8-to-istanbul "^9.0.1" +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^6.0.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + "@jest/schemas@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" @@ -917,6 +1030,15 @@ callsites "^3.0.0" graceful-fs "^4.2.9" +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" + callsites "^3.0.0" + graceful-fs "^4.2.9" + "@jest/test-result@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" @@ -936,6 +1058,16 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== + dependencies: + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + "@jest/test-sequencer@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz#9d0c283d906ac599c74bde464bc0d7e6a82886c3" @@ -946,6 +1078,16 @@ jest-haste-map "^28.1.3" slash "^3.0.0" +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== + dependencies: + "@jest/test-result" "^29.7.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + slash "^3.0.0" + "@jest/transform@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0" @@ -967,6 +1109,27 @@ slash "^3.0.0" write-file-atomic "^4.0.1" +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + "@jest/types@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" @@ -1074,6 +1237,14 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jridgewell/trace-mapping@^0.3.18": + version "0.3.20" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" + integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@lerna/child-process@6.6.2": version "6.6.2" resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-6.6.2.tgz#5d803c8dee81a4e013dc428292e77b365cba876c" @@ -3183,6 +3354,19 @@ babel-jest@^28.1.3: graceful-fs "^4.2.9" slash "^3.0.0" +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== + dependencies: + "@jest/transform" "^29.7.0" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.6.3" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" @@ -3204,6 +3388,16 @@ babel-plugin-jest-hoist@^28.1.3: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + babel-plugin-polyfill-corejs2@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz#e4c31d4c89b56f3cf85b92558954c66b54bd972d" @@ -3254,6 +3448,14 @@ babel-preset-jest@^28.1.3: babel-plugin-jest-hoist "^28.1.3" babel-preset-current-node-syntax "^1.0.0" +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== + dependencies: + babel-plugin-jest-hoist "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + backoff@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" @@ -4150,6 +4352,11 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -4227,6 +4434,19 @@ create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -4392,6 +4612,11 @@ dedent@0.7.0, dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== +dedent@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" + integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -4551,6 +4776,11 @@ diff-sequences@^28.1.1: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + diff@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" @@ -4671,6 +4901,11 @@ emittery@^0.10.2: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + emoji-regex@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.1.0.tgz#d50e383743c0f7a5945c47087295afc112e3cf66" @@ -5487,6 +5722,17 @@ expect@^28.0.0, expect@^28.1.3: jest-message-util "^28.1.3" jest-util "^28.1.3" +expect@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + express@^4.18.1: version "4.18.1" resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" @@ -5622,7 +5868,7 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -7254,6 +7500,17 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" +istanbul-lib-instrument@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz#71e87707e8041428732518c6fb5211761753fbdf" + integrity sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" @@ -7307,6 +7564,15 @@ jest-changed-files@^28.1.3: execa "^5.0.0" p-limit "^3.1.0" +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== + dependencies: + execa "^5.0.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + jest-circus@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.3.tgz#d14bd11cf8ee1a03d69902dc47b6bd4634ee00e4" @@ -7332,6 +7598,32 @@ jest-circus@^28.1.3: slash "^3.0.0" stack-utils "^2.0.3" +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^1.0.0" + is-generator-fn "^2.0.0" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + pretty-format "^29.7.0" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-cli@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2" @@ -7350,6 +7642,23 @@ jest-cli@^28.1.3: prompts "^2.0.1" yargs "^17.3.1" +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== + dependencies: + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + chalk "^4.0.0" + create-jest "^29.7.0" + exit "^0.1.2" + import-local "^3.0.2" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + yargs "^17.3.1" + jest-config@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.3.tgz#e315e1f73df3cac31447eed8b8740a477392ec60" @@ -7378,6 +7687,34 @@ jest-config@^28.1.3: slash "^3.0.0" strip-json-comments "^3.1.1" +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-json-comments "^3.1.1" + jest-diff@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" @@ -7398,6 +7735,16 @@ jest-diff@^28.0.0, jest-diff@^28.1.3: jest-get-type "^28.0.2" pretty-format "^28.1.3" +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + jest-docblock@^28.1.1: version "28.1.1" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8" @@ -7405,6 +7752,13 @@ jest-docblock@^28.1.1: dependencies: detect-newline "^3.0.0" +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== + dependencies: + detect-newline "^3.0.0" + jest-each@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.3.tgz#bdd1516edbe2b1f3569cfdad9acd543040028f81" @@ -7416,6 +7770,17 @@ jest-each@^28.1.3: jest-util "^28.1.3" pretty-format "^28.1.3" +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" + jest-environment-jsdom@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz#d206fa3551933c3fd519e5dfdb58a0f5139a837f" @@ -7442,6 +7807,18 @@ jest-environment-node@^28.1.3: jest-mock "^28.1.3" jest-util "^28.1.3" +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + jest-util "^29.7.0" + jest-extended@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/jest-extended/-/jest-extended-3.0.2.tgz#f74ae82bdf280536552752b502f57f71e7d2fcf1" @@ -7468,6 +7845,11 @@ jest-get-type@^28.0.0, jest-get-type@^28.0.2: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + jest-haste-map@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b" @@ -7487,6 +7869,25 @@ jest-haste-map@^28.1.3: optionalDependencies: fsevents "^2.3.2" +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== + dependencies: + "@jest/types" "^29.6.3" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + jest-jquery-matchers@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/jest-jquery-matchers/-/jest-jquery-matchers-2.1.1.tgz#74c174b60bd0be550d285bf3ae65ffc5e19a8d30" @@ -7500,6 +7901,14 @@ jest-leak-detector@^28.1.3: jest-get-type "^28.0.2" pretty-format "^28.1.3" +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== + dependencies: + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + jest-matcher-utils@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" @@ -7520,6 +7929,16 @@ jest-matcher-utils@^28.1.3: jest-get-type "^28.0.2" pretty-format "^28.1.3" +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + jest-message-util@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" @@ -7596,6 +8015,11 @@ jest-regex-util@^28.0.2: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== + jest-resolve-dependencies@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz#8c65d7583460df7275c6ea2791901fa975c1fe66" @@ -7604,6 +8028,14 @@ jest-resolve-dependencies@^28.1.3: jest-regex-util "^28.0.2" jest-snapshot "^28.1.3" +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== + dependencies: + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" + jest-resolve@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.3.tgz#cfb36100341ddbb061ec781426b3c31eb51aa0a8" @@ -7619,6 +8051,21 @@ jest-resolve@^28.1.3: resolve.exports "^1.1.0" slash "^3.0.0" +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-pnp-resolver "^1.2.2" + jest-util "^29.7.0" + jest-validate "^29.7.0" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + jest-runner@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.3.tgz#5eee25febd730b4713a2cdfd76bdd5557840f9a1" @@ -7646,6 +8093,33 @@ jest-runner@^28.1.3: p-limit "^3.1.0" source-map-support "0.5.13" +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== + dependencies: + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + jest-runtime@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.3.tgz#a57643458235aa53e8ec7821949e728960d0605f" @@ -7674,6 +8148,34 @@ jest-runtime@^28.1.3: slash "^3.0.0" strip-bom "^4.0.0" +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + strip-bom "^4.0.0" + jest-snapshot@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668" @@ -7703,6 +8205,32 @@ jest-snapshot@^28.1.3: pretty-format "^28.1.3" semver "^7.3.5" +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.7.0" + graceful-fs "^4.2.9" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + natural-compare "^1.4.0" + pretty-format "^29.7.0" + semver "^7.5.3" + jest-util@^28.0.0, jest-util@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" @@ -7739,6 +8267,18 @@ jest-validate@^28.1.3: leven "^3.1.0" pretty-format "^28.1.3" +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== + dependencies: + "@jest/types" "^29.6.3" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.6.3" + leven "^3.1.0" + pretty-format "^29.7.0" + jest-watcher@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4" @@ -7753,6 +8293,20 @@ jest-watcher@^28.1.3: jest-util "^28.1.3" string-length "^4.0.1" +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== + dependencies: + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.7.0" + string-length "^4.0.1" + jest-when@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/jest-when/-/jest-when-3.5.1.tgz#33ab6f923661cf878cd08fe9df64b507934603db" @@ -7776,6 +8330,16 @@ jest-worker@^28.1.3: merge-stream "^2.0.0" supports-color "^8.0.0" +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + jest@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.3.tgz#e9c6a7eecdebe3548ca2b18894a50f45b36dfc6b" @@ -7786,6 +8350,16 @@ jest@^28.1.3: import-local "^3.0.2" jest-cli "^28.1.3" +jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== + dependencies: + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" + import-local "^3.0.2" + jest-cli "^29.7.0" + jju@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" @@ -10076,6 +10650,11 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pure-rand@^6.0.0: + version "6.0.4" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.4.tgz#50b737f6a925468679bff00ad20eade53f37d5c7" + integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA== + q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -10425,6 +11004,11 @@ resolve.exports@^1.1.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== + resolve@1.17.0: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" @@ -10665,6 +11249,13 @@ semver@^7.3.8: dependencies: lru-cache "^6.0.0" +semver@^7.5.3, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + semver@~5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" @@ -12316,7 +12907,7 @@ write-file-atomic@^2.4.2: imurmurhash "^0.1.4" signal-exit "^3.0.2" -write-file-atomic@^4.0.1: +write-file-atomic@^4.0.1, write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== From c097b9aec99c33c48273f6566731606e79def131 Mon Sep 17 00:00:00 2001 From: Muhammad Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Fri, 15 Dec 2023 17:38:16 +0100 Subject: [PATCH 6/8] fix: upgrade prism-react-renderer from 2.2.0 to 2.3.0 (#6662) Snyk has created this PR to upgrade prism-react-renderer from 2.2.0 to 2.3.0. See this package in npm: See this project in Snyk: https://app.snyk.io/org/muhammad-altabba/project/97fdd18d-9bab-4c87-87eb-da3caee72033?utm_source=github&utm_medium=referral&page=upgrade-pr Co-authored-by: snyk-bot --- docs/package.json | 2 +- docs/yarn.lock | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/package.json b/docs/package.json index 7b259f68198..1c31693b477 100644 --- a/docs/package.json +++ b/docs/package.json @@ -22,7 +22,7 @@ "classnames": "^2.3.2", "clsx": "^2.0.0", "docusaurus-lunr-search": "^3.3.0", - "prism-react-renderer": "^2.2.0", + "prism-react-renderer": "^2.3.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/docs/yarn.lock b/docs/yarn.lock index 18fda4d981b..bb6b4936273 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -7206,7 +7206,7 @@ pretty-time@^1.1.0: resolved "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== -prism-react-renderer@^2.0.6, prism-react-renderer@^2.1.0, prism-react-renderer@^2.2.0: +prism-react-renderer@^2.0.6, prism-react-renderer@^2.1.0: version "2.2.0" resolved "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.2.0.tgz#f199b15716e0b8d0ccfd1986ff6fa226fb7ff2b1" integrity sha512-j4AN0VkEr72598+47xDvpzeYyeh/wPPRNTt9nJFZqIZUxwGKwYqYgt7RVigZ3ZICJWJWN84KEuMKPNyypyhNIw== @@ -7214,6 +7214,14 @@ prism-react-renderer@^2.0.6, prism-react-renderer@^2.1.0, prism-react-renderer@^ "@types/prismjs" "^1.26.0" clsx "^1.2.1" +prism-react-renderer@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-2.3.0.tgz#5f8f615af6af8201a0b734bd8c946df3d818ea54" + integrity sha512-UYRg2TkVIaI6tRVHC5OJ4/BxqPUxJkJvq/odLT/ykpt1zGYXooNperUxQcCvi87LyRnR4nCh81ceOA+e7nrydg== + dependencies: + "@types/prismjs" "^1.26.0" + clsx "^2.0.0" + prismjs@^1.29.0: version "1.29.0" resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" From 6b2dbe312edef9377b8258105b69ba44f6fcb2ba Mon Sep 17 00:00:00 2001 From: Oleksii Kosynskyi Date: Fri, 15 Dec 2023 22:53:12 -0500 Subject: [PATCH 7/8] Web3 v4 benchmarking (#6649) --- .github/workflows/build.yml | 83 ++++++++++++++--- .github/workflows/deploy-docs.yaml | 4 +- .gitignore | 3 + package.json | 3 +- packages/web3-eth-contract/src/contract.ts | 3 +- packages/web3-eth-contract/src/index.ts | 1 + packages/web3-eth-contract/src/utils.ts | 9 +- packages/web3/package.json | 5 +- packages/web3/test/benchmark/abi.ts | 28 ++++++ packages/web3/test/benchmark/benchmark.ts | 47 ++++++++++ packages/web3/test/benchmark/helpers.ts | 23 +++++ .../web3/test/benchmark/processingContract.ts | 56 ++++++++++++ packages/web3/test/benchmark/processingTx.ts | 38 ++++++++ packages/web3/test/benchmark/wallet.ts | 42 +++++++++ scripts/copyCommitCommentToPrComment.js | 88 +++++++++++++++++++ 15 files changed, 412 insertions(+), 21 deletions(-) create mode 100644 packages/web3/test/benchmark/abi.ts create mode 100644 packages/web3/test/benchmark/benchmark.ts create mode 100644 packages/web3/test/benchmark/helpers.ts create mode 100644 packages/web3/test/benchmark/processingContract.ts create mode 100644 packages/web3/test/benchmark/processingTx.ts create mode 100644 packages/web3/test/benchmark/wallet.ts create mode 100755 scripts/copyCommitCommentToPrComment.js 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); From 6c075dba3fefa27c833e5c71bf91319ac2466b0b Mon Sep 17 00:00:00 2001 From: Muhammad Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Wed, 3 Jan 2024 12:49:34 +0100 Subject: [PATCH 8/8] Increase Unit test coverage for web3-eth above 90% (#6663) * fix issues in Common config and add a test to accounts * add test cases * update CHAGELOG.md --- packages/web3-eth-accounts/CHANGELOG.md | 6 +- .../src/tx/baseTransaction.ts | 32 ++- .../utils/prepare_transaction_for_signing.ts | 2 +- .../prepare_transaction_for_signing.test.ts | 151 ++++++++++++- .../wait_for_transaction_receipt.test.ts | 198 +++++++++++------- .../watch_transaction_by_subscription.test.ts | 130 ++++++++++++ 6 files changed, 440 insertions(+), 79 deletions(-) create mode 100644 packages/web3-eth/test/unit/utils/watch_transaction_by_subscription.test.ts diff --git a/packages/web3-eth-accounts/CHANGELOG.md b/packages/web3-eth-accounts/CHANGELOG.md index 5e1e9bee8c1..337dd2292b0 100644 --- a/packages/web3-eth-accounts/CHANGELOG.md +++ b/packages/web3-eth-accounts/CHANGELOG.md @@ -149,4 +149,8 @@ Documentation: - Fixed `recover` function, `v` will be normalized to value 0,1 (#6344) -## [Unreleased] \ No newline at end of file +## [Unreleased] + +### Fixed + +- Send Transaction config used to be ignored if the passed `common` did not have a `copy()` and the `chainId` was not provided (#6663) diff --git a/packages/web3-eth-accounts/src/tx/baseTransaction.ts b/packages/web3-eth-accounts/src/tx/baseTransaction.ts index cb47d2bad22..3f8da6d4e41 100644 --- a/packages/web3-eth-accounts/src/tx/baseTransaction.ts +++ b/packages/web3-eth-accounts/src/tx/baseTransaction.ts @@ -15,7 +15,7 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -import { Numbers } from 'web3-types'; +import { Common as CommonType, Numbers } from 'web3-types'; import { bytesToHex } from 'web3-utils'; import { MAX_INTEGER, MAX_UINT64, SECP256K1_ORDER_DIV_2, secp256k1 } from './constants.js'; import { toUint8Array, uint8ArrayToBigInt, unpadUint8Array } from '../common/utils.js'; @@ -389,6 +389,8 @@ export abstract class BaseTransaction { * @param chainId - Chain ID from tx options (typed txs) or signature (legacy tx) */ protected _getCommon(common?: Common, chainId?: Numbers) { + // TODO: this function needs to be reviewed and the code to be more clean + // check issue https://github.com/web3/web3.js/issues/6666 // Chain ID provided if (chainId !== undefined) { const chainIdBigInt = uint8ArrayToBigInt(toUint8Array(chainId)); @@ -425,6 +427,34 @@ export abstract class BaseTransaction { if (common?.copy && typeof common?.copy === 'function') { return common.copy(); } + // TODO: Recheck this next block when working on https://github.com/web3/web3.js/issues/6666 + // This block is to handle when `chainId` was not passed and the `common` object does not have `copy()` + // If it was meant to be unsupported to process `common` in this case, an exception should be thrown instead of the following block + if (common) { + const hardfork = + typeof common.hardfork === 'function' + ? common.hardfork() + : // eslint-disable-next-line @typescript-eslint/unbound-method + (common.hardfork as unknown as string); + + return Common.custom( + { + name: 'custom-chain', + networkId: common.networkId + ? common.networkId() + : BigInt((common as unknown as CommonType).customChain?.networkId) ?? + undefined, + chainId: common.chainId + ? common.chainId() + : BigInt((common as unknown as CommonType).customChain?.chainId) ?? + undefined, + }, + { + baseChain: this.DEFAULT_CHAIN, + hardfork: hardfork || this.DEFAULT_HARDFORK, + }, + ); + } return new Common({ chain: this.DEFAULT_CHAIN, hardfork: this.DEFAULT_HARDFORK }); } diff --git a/packages/web3-eth/src/utils/prepare_transaction_for_signing.ts b/packages/web3-eth/src/utils/prepare_transaction_for_signing.ts index 607994d05a4..fe761ada1f6 100644 --- a/packages/web3-eth/src/utils/prepare_transaction_for_signing.ts +++ b/packages/web3-eth/src/utils/prepare_transaction_for_signing.ts @@ -68,7 +68,7 @@ const getEthereumjsTransactionOptions = ( if (!hasTransactionSigningOptions) { // if defaultcommon is specified, use that. if (web3Context.defaultCommon) { - common = web3Context.defaultCommon; + common = { ...web3Context.defaultCommon }; if (isNullish(common.hardfork)) common.hardfork = transaction.hardfork ?? web3Context.defaultHardfork; diff --git a/packages/web3-eth/test/unit/prepare_transaction_for_signing.test.ts b/packages/web3-eth/test/unit/prepare_transaction_for_signing.test.ts index d8d30ee0f65..d53785c2e36 100644 --- a/packages/web3-eth/test/unit/prepare_transaction_for_signing.test.ts +++ b/packages/web3-eth/test/unit/prepare_transaction_for_signing.test.ts @@ -16,7 +16,6 @@ along with web3.js. If not, see . */ import { - Common, EthExecutionAPI, HexString, Web3NetAPI, @@ -33,6 +32,7 @@ import { AccessListEIP2930Transaction, FeeMarketEIP1559Transaction, Transaction, + Hardfork, } from 'web3-eth-accounts'; import { prepareTransactionForSigning } from '../../src/utils/prepare_transaction_for_signing'; import { validTransactions } from '../fixtures/prepare_transaction_for_signing'; @@ -70,7 +70,7 @@ describe('prepareTransactionForSigning', () => { if (isNullish(tx.common)) { if (options.web3Context.defaultCommon) { - const common = options.web3Context.defaultCommon as unknown as Common; + const common = options.web3Context.defaultCommon; const chainId = common.customChain.chainId as string; const networkId = common.customChain.networkId as string; const name = common.customChain.name as string; @@ -102,6 +102,153 @@ describe('prepareTransactionForSigning', () => { expect(ethereumjsTx.common.chainName()).toBe('test'); }); }); + + it('should be able to read Hardfork from context.defaultHardfork', async () => { + const context = new Web3Context({ + provider: new HttpProvider('http://127.0.0.1'), + config: { defaultNetworkId: '0x9' }, + }); + context.defaultChain = 'mainnet'; + context.defaultHardfork = Hardfork.Istanbul; + + async function transactionBuilder(options: { + transaction: TransactionType; + web3Context: Web3Context; + privateKey?: HexString | Uint8Array; + fillGasPrice?: boolean; + fillGasLimit?: boolean; + }): Promise { + const tx = { ...options.transaction }; + return tx as unknown as ReturnType; + } + + context.transactionBuilder = transactionBuilder; + + const ethereumjsTx = await prepareTransactionForSigning( + { + chainId: 1458, + nonce: 1, + gasPrice: BigInt(20000000000), + gas: BigInt(21000), + to: '0xF0109fC8DF283027b6285cc889F5aA624EaC1F55', + from: '0x2c7536E3605D9C16a7a3D7b1898e529396a65c23', + value: '1000000000', + input: '', + networkId: 999, + }, + context, + ); + expect(ethereumjsTx.common.hardfork()).toBe(Hardfork.Istanbul); + expect(ethereumjsTx.common.networkId().toString()).toBe('999'); + }); + + it('should be able to read Hardfork from context.config.defaultHardfork and context.defaultCommon.hardfork', async () => { + const context = new Web3Context({ + provider: new HttpProvider('http://127.0.0.1'), + config: { defaultNetworkId: '0x9' }, + }); + context.defaultChain = 'mainnet'; + + // if the value here is different from the one in context.defaultCommon.hardfork + // Then an error will be thrown: + // "ConfigHardforkMismatchError: Web3Config hardfork doesnt match in defaultHardfork london and common.hardfork istanbul" + context.config.defaultHardfork = Hardfork.Istanbul; + context.defaultCommon = { + customChain: { + name: 'test', + networkId: 111, + chainId: 1458, + }, + hardfork: Hardfork.Istanbul, + baseChain: 'mainnet', + } as any; + + async function transactionBuilder(options: { + transaction: TransactionType; + web3Context: Web3Context; + privateKey?: HexString | Uint8Array; + fillGasPrice?: boolean; + fillGasLimit?: boolean; + }): Promise { + const tx = { ...options.transaction }; + return tx as unknown as ReturnType; + } + + context.transactionBuilder = transactionBuilder; + + const ethereumjsTx = await prepareTransactionForSigning( + { + chainId: 1458, + nonce: 1, + gasPrice: BigInt(20000000000), + gas: BigInt(21000), + to: '0xF0109fC8DF283027b6285cc889F5aA624EaC1F55', + from: '0x2c7536E3605D9C16a7a3D7b1898e529396a65c23', + value: '1000000000', + input: '', + }, + context, + ); + expect(ethereumjsTx.common.hardfork()).toBe(Hardfork.Istanbul); + expect(ethereumjsTx.common.networkId().toString()).toBe('111'); + }); + + it('should give priorities to tx.hardfork and tx.networkId over values from context', async () => { + const context = new Web3Context({ + provider: new HttpProvider('http://127.0.0.1'), + config: { defaultNetworkId: '0x9' }, + }); + context.defaultChain = 'mainnet'; + + // if the value here is different from the one in context.defaultCommon.hardfork + // Then an error will be thrown: + // "ConfigHardforkMismatchError: Web3Config hardfork doesnt match in defaultHardfork london and common.hardfork istanbul" + context.config.defaultHardfork = Hardfork.Istanbul; + context.defaultCommon = { + customChain: { + name: 'test', + networkId: 111, + chainId: 1458, + }, + hardfork: Hardfork.Istanbul, + baseChain: 'mainnet', + } as any; + + async function transactionBuilder(options: { + transaction: TransactionType; + web3Context: Web3Context; + privateKey?: HexString | Uint8Array; + fillGasPrice?: boolean; + fillGasLimit?: boolean; + }): Promise { + const tx = { ...options.transaction }; + return tx as unknown as ReturnType; + } + + context.transactionBuilder = transactionBuilder; + + // context.transactionBuilder = defaultTransactionBuilder; + + const ethereumjsTx = await prepareTransactionForSigning( + { + chainId: 1458, + nonce: 1, + gasPrice: BigInt(20000000000), + gas: BigInt(21000), + to: '0xF0109fC8DF283027b6285cc889F5aA624EaC1F55', + from: '0x2c7536E3605D9C16a7a3D7b1898e529396a65c23', + value: '1000000000', + input: '', + networkId: 999, + hardfork: Hardfork.Chainstart, + chain: 'mainnet', + }, + context, + ); + expect(ethereumjsTx.common.hardfork()).toBe(Hardfork.Chainstart); + expect(ethereumjsTx.common.networkId().toString()).toBe('999'); + }); + describe('should return an web3-utils/tx instance with expected properties', () => { it.each(validTransactions)( 'mockBlock: %s\nexpectedTransaction: %s\nexpectedPrivateKey: %s\nexpectedAddress: %s\nexpectedRlpEncodedTransaction: %s\nexpectedTransactionHash: %s\nexpectedMessageToSign: %s\nexpectedV: %s\nexpectedR: %s\nexpectedS: %s', diff --git a/packages/web3-eth/test/unit/utils/wait_for_transaction_receipt.test.ts b/packages/web3-eth/test/unit/utils/wait_for_transaction_receipt.test.ts index 8b70dcdd3b2..4a598a00fe7 100644 --- a/packages/web3-eth/test/unit/utils/wait_for_transaction_receipt.test.ts +++ b/packages/web3-eth/test/unit/utils/wait_for_transaction_receipt.test.ts @@ -25,102 +25,152 @@ describe('waitForTransactionReceipt unit test', () => { it(`waitForTransactionReceipt should throw error after block timeout`, async () => { let blockNum = 1; - web3Context = new Web3Context( - { - request: async (payload: any) => { - let response: { jsonrpc: string; id: any; result: string } | undefined; - - switch (payload.method) { - case 'eth_blockNumber': - blockNum += 50; - response = { - jsonrpc: '2.0', - id: payload.id, - result: `0x${blockNum.toString(16)}`, - }; - break; - - case 'eth_getTransactionReceipt': - response = undefined; - break; - - default: - throw new Error(`Unknown payload ${payload}`); - } - - return new Promise(resolve => { - resolve(response as any); - }); - }, - supportsSubscriptions: () => false, + web3Context = new Web3Context({ + request: async (payload: any) => { + let response: { jsonrpc: string; id: any; result: string } | undefined; + + switch (payload.method) { + case 'eth_blockNumber': + blockNum += 50; + response = { + jsonrpc: '2.0', + id: payload.id, + result: `0x${blockNum.toString(16)}`, + }; + break; + + case 'eth_getTransactionReceipt': + response = undefined; + break; + + default: + throw new Error(`Unknown payload ${payload}`); + } + + return new Promise(resolve => { + resolve(response as any); + }); }, - ); + supportsSubscriptions: () => false, + }); await expect(async () => waitForTransactionReceipt( web3Context, '0x0430b701e657e634a9d5480eae0387a473913ef29af8e60c38a3cee24494ed54', - DEFAULT_RETURN_FORMAT - ) + DEFAULT_RETURN_FORMAT, + ), ).rejects.toThrow(TransactionBlockTimeoutError); - }); - it(`waitForTransactionReceipt should resolve immediatly if receipt is avalible`, async () => { + it(`waitForTransactionReceipt should resolve immediately if receipt is available`, async () => { let blockNum = 1; const txHash = '0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5'; const blockHash = '0xa957d47df264a31badc3ae823e10ac1d444b098d9b73d204c40426e57f47e8c3'; - web3Context = new Web3Context( - { - request: async (payload: any) => { - const response = { - jsonrpc: '2.0', - id: payload.id, - result: {}, - }; - - switch (payload.method) { - case 'eth_blockNumber': - blockNum += 10; - response.result = `0x${blockNum.toString(16)}`; - break; - - case 'eth_getTransactionReceipt': - response.result = { - blockHash, - blockNumber: `0x1`, - cumulativeGasUsed: '0xa12515', - from: payload.from, - gasUsed: payload.gasLimit, - status: '0x1', - to: payload.to, - transactionHash: txHash, - transactionIndex: '0x66', - - }; - break; - - default: - throw new Error(`Unknown payload ${payload}`); - } - - return new Promise(resolve => { - resolve(response as any); - }); - }, - supportsSubscriptions: () => false, + web3Context = new Web3Context({ + request: async (payload: any) => { + const response = { + jsonrpc: '2.0', + id: payload.id, + result: {}, + }; + + switch (payload.method) { + case 'eth_blockNumber': + blockNum += 10; + response.result = `0x${blockNum.toString(16)}`; + break; + + case 'eth_getTransactionReceipt': + response.result = { + blockHash, + blockNumber: `0x1`, + cumulativeGasUsed: '0xa12515', + from: payload.from, + gasUsed: payload.gasLimit, + status: '0x1', + to: payload.to, + transactionHash: txHash, + transactionIndex: '0x66', + }; + break; + + default: + throw new Error(`Unknown payload ${payload}`); + } + + return new Promise(resolve => { + resolve(response as any); + }); }, + supportsSubscriptions: () => false, + }); + + const res = await waitForTransactionReceipt( + web3Context, + '0x0430b701e657e634a9d5480eae0387a473913ef29af8e60c38a3cee24494ed54', + DEFAULT_RETURN_FORMAT, ); + expect(res).toBeDefined(); + expect(res.transactionHash).toStrictEqual(txHash); + expect(res.blockHash).toStrictEqual(blockHash); + }); + + it(`waitForTransactionReceipt should resolve immediately if receipt is available - when subscription is enabled`, async () => { + let blockNum = 1; + const txHash = '0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5'; + const blockHash = '0xa957d47df264a31badc3ae823e10ac1d444b098d9b73d204c40426e57f47e8c3'; + + web3Context = new Web3Context({ + request: async (payload: any) => { + const response = { + jsonrpc: '2.0', + id: payload.id, + result: {}, + }; + + switch (payload.method) { + case 'eth_blockNumber': + blockNum += 10; + response.result = `0x${blockNum.toString(16)}`; + break; + + case 'eth_getTransactionReceipt': + response.result = { + blockHash, + blockNumber: `0x1`, + cumulativeGasUsed: '0xa12515', + from: payload.from, + gasUsed: payload.gasLimit, + status: '0x1', + to: payload.to, + transactionHash: txHash, + transactionIndex: '0x66', + }; + break; + + default: + throw new Error(`Unknown payload ${payload}`); + } + + return new Promise(resolve => { + resolve(response as any); + }); + }, + supportsSubscriptions: () => true, + }); + web3Context.enableExperimentalFeatures.useSubscriptionWhenCheckingBlockTimeout = true; + const res = await waitForTransactionReceipt( web3Context, '0x0430b701e657e634a9d5480eae0387a473913ef29af8e60c38a3cee24494ed54', - DEFAULT_RETURN_FORMAT + DEFAULT_RETURN_FORMAT, ); expect(res).toBeDefined(); expect(res.transactionHash).toStrictEqual(txHash); expect(res.blockHash).toStrictEqual(blockHash); }); -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/packages/web3-eth/test/unit/utils/watch_transaction_by_subscription.test.ts b/packages/web3-eth/test/unit/utils/watch_transaction_by_subscription.test.ts new file mode 100644 index 00000000000..d7f4169aeaf --- /dev/null +++ b/packages/web3-eth/test/unit/utils/watch_transaction_by_subscription.test.ts @@ -0,0 +1,130 @@ +/* +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 { Web3Context, Web3RequestManager } from 'web3-core'; +import { format } from 'web3-utils'; +import { DEFAULT_RETURN_FORMAT, JsonRpcResponseWithResult, Web3EthExecutionAPI } from 'web3-types'; +import { ethRpcMethods } from 'web3-rpc-methods'; +import { WebSocketProvider } from 'web3-providers-ws'; +import * as rpcMethodWrappers from '../../../src/rpc_method_wrappers'; +import * as WatchTransactionBySubscription from '../../../src/utils/watch_transaction_by_subscription'; +import { + expectedTransactionReceipt, + expectedTransactionHash, + testData, +} from '../rpc_method_wrappers/fixtures/send_signed_transaction'; +import { transactionReceiptSchema } from '../../../src/schemas'; +import { registeredSubscriptions } from '../../../src'; + +jest.mock('web3-rpc-methods'); +jest.mock('web3-providers-ws'); +jest.mock('../../../src/utils/watch_transaction_by_polling'); + +const testMessage = + 'Title: %s\ninputSignedTransaction: %s\nexpectedTransactionHash: %s\nexpectedTransactionReceipt: %s\n'; + +async function waitUntilCalled(mock: jest.Mock, timeout = 1000): Promise { + return new Promise((resolve, reject) => { + let timeoutId: NodeJS.Timeout | undefined; + const intervalId = setInterval(() => { + if (mock.mock.calls.length > 0) { + clearInterval(intervalId); + if (timeoutId) { + clearTimeout(timeoutId); + } + resolve(mock); + } + }, 100); + timeoutId = setTimeout(() => { + clearInterval(intervalId); + if (timeoutId) { + clearTimeout(timeoutId); + } + reject(new Error('timeout')); + }, timeout); + }); +} + +describe('watchTransactionBySubscription', () => { + describe('should revert to polling in cases where getting by subscription did not workout', () => { + let web3Context: Web3Context; + + beforeEach(() => { + jest.spyOn(Web3RequestManager.prototype, 'send').mockImplementation(async () => { + return {} as Promise; + }); + jest.spyOn(WebSocketProvider.prototype, 'request').mockImplementation(async () => { + return {} as Promise>; + }); + + (ethRpcMethods.sendRawTransaction as jest.Mock).mockResolvedValue( + expectedTransactionHash, + ); + (ethRpcMethods.getTransactionReceipt as jest.Mock).mockResolvedValue( + expectedTransactionHash, + ); + web3Context = new Web3Context({ + // dummy provider that does supports subscription + provider: new WebSocketProvider('ws://localhost:8546'), + registeredSubscriptions, + }); + (web3Context.provider as any).supportsSubscriptions = () => true; + }); + afterEach(() => { + // to clear the interval inside the subscription function: + web3Context.transactionConfirmationBlocks = 0; + }); + let counter = 0; + it.each(testData)( + `should call getBlockNumber if blockHeaderTimeout reached\n ${testMessage}`, + async (_, inputTransaction) => { + if (counter > 0) { + return; + } + counter += 1; + const formattedTransactionReceipt = format( + transactionReceiptSchema, + expectedTransactionReceipt, + DEFAULT_RETURN_FORMAT, + ); + + web3Context.enableExperimentalFeatures.useSubscriptionWhenCheckingBlockTimeout = + true; + // this will case the function to revert to polling: + web3Context.blockHeaderTimeout = 0; + + web3Context.transactionSendTimeout = 2; + + const promiEvent = rpcMethodWrappers.sendSignedTransaction( + web3Context, + inputTransaction, + DEFAULT_RETURN_FORMAT, + ); + // await promiEvent; + WatchTransactionBySubscription.watchTransactionBySubscription({ + web3Context, + transactionReceipt: formattedTransactionReceipt, + transactionPromiEvent: promiEvent, + returnFormat: DEFAULT_RETURN_FORMAT, + }); + await waitUntilCalled(ethRpcMethods.getBlockNumber as jest.Mock, 5000); + + await promiEvent; + }, + 60000, + ); + }); +});