diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ee95deaf804..f05eac79fd8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: - run: yarn install --ignore-scripts - run: npx ts-node scripts/init.ts - run: yarn build:cjs - - uses: actions/cache/save@v3 + - uses: actions/cache/save@v4 with: path: ./ key: web3-${{ matrix.node }}-${{github.sha}} @@ -39,7 +39,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 18 - - uses: actions/cache/restore@v3 + - uses: actions/cache/restore@v4 with: path: ./ key: web3-18-${{github.sha}} @@ -52,7 +52,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 18 - - uses: actions/cache/restore@v3 + - uses: actions/cache/restore@v4 with: path: ./ key: web3-18-${{github.sha}} @@ -65,12 +65,63 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 18 - - uses: actions/cache/restore@v3 + - uses: actions/cache/restore@v4 with: path: ./ key: web3-18-${{github.sha}} - run: npx ts-node scripts/init.ts + - name: Restore eslint caches + uses: actions/cache/restore@v4 + with: + path: | + packages/web3/.eslintcache + packages/web3-core/.eslintcache + packages/web3-eth/.eslintcache + packages/web3-eth-abi/.eslintcache + packages/web3-eth-accounts/.eslintcache + packages/web3-eth-contract/.eslintcache + packages/web3-eth-ens/.eslintcache + packages/web3-eth-iban/.eslintcache + packages/web3-eth-personal/.eslintcache + packages/web3-net/.eslintcache + packages/web3-providers-http/.eslintcache + packages/web3-providers-ws/.eslintcache + packages/web3-rpc-methods/.eslintcache + packages/web3-types/.eslintcache + packages/web3-utils/.eslintcache + packages/web3-validator/.eslintcache + tools/web3-plugin-example/.eslintcache + key: ${{ runner.os }}-eslintcache + - run: yarn lint + - run: gh cache delete "${{ runner.os }}-eslintcache" + if: github.event_name == 'push' && github.ref == 'refs/heads/4.x' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Save eslint caches + if: github.event_name == 'push' && github.ref == 'refs/heads/4.x' + uses: actions/cache/save@v4 + with: + path: | + packages/web3/.eslintcache + packages/web3-core/.eslintcache + packages/web3-eth/.eslintcache + packages/web3-eth-abi/.eslintcache + packages/web3-eth-accounts/.eslintcache + packages/web3-eth-contract/.eslintcache + packages/web3-eth-ens/.eslintcache + packages/web3-eth-iban/.eslintcache + packages/web3-eth-personal/.eslintcache + packages/web3-net/.eslintcache + packages/web3-providers-http/.eslintcache + packages/web3-providers-ws/.eslintcache + packages/web3-rpc-methods/.eslintcache + packages/web3-types/.eslintcache + packages/web3-utils/.eslintcache + packages/web3-validator/.eslintcache + tools/web3-plugin-example/.eslintcache + key: ${{ runner.os }}-eslintcache build-web: name: Build Web @@ -80,13 +131,13 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 18 - - uses: actions/cache/restore@v3 + - uses: actions/cache/restore@v4 with: path: ./ key: web3-18-${{github.sha}} - name: Restore default branch stats if: github.event_name != 'push' - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 with: path: packages/web3/dist/4.x.json key: web3-bundle-stats-4x-${{github.event.pull_request.base.sha}} @@ -101,7 +152,7 @@ jobs: current-stats-json-path: "packages/web3/dist/${{ github.ref_name }}.json" base-stats-json-path: "packages/web3/dist/4.x.json" - name: Cache default branch stats - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 if: github.event_name == 'push' && github.ref == 'refs/heads/4.x' with: path: packages/web3/dist/${{ github.ref_name }}.json @@ -120,7 +171,7 @@ jobs: with: architecture: x64 node-version: ${{ matrix.node }} - - uses: actions/cache/restore@v3 + - uses: actions/cache/restore@v4 with: path: ./ key: web3-${{ matrix.node }}-${{github.sha}} @@ -142,7 +193,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: 18 - - uses: actions/cache/restore@v3 + - uses: actions/cache/restore@v4 with: path: ./ key: web3-18-${{github.sha}} @@ -165,7 +216,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 18 - - uses: actions/cache/restore@v3 + - uses: actions/cache/restore@v4 with: path: ./ key: web3-18-${{github.sha}} @@ -186,7 +237,7 @@ jobs: node-version: 18 - uses: browser-actions/setup-firefox@latest if: matrix.browser == 'firefox' - - uses: actions/cache/restore@v3 + - uses: actions/cache/restore@v4 with: path: ./ key: web3-18-${{github.sha}} @@ -212,7 +263,7 @@ jobs: with: cache: yarn node-version: '18' - - uses: actions/cache/restore@v3 + - uses: actions/cache/restore@v4 with: path: ./ key: web3-18-${{github.sha}} @@ -235,14 +286,14 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 18 - - uses: actions/cache/restore@v3 + - uses: actions/cache/restore@v4 with: path: ./ key: web3-18-${{github.sha}} # @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@5.1.0 - name: Restore main branch benchmark data - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 with: path: web3-benchmark-main.json key: ${{ runner.os }}-web3-benchmark-main.json @@ -279,8 +330,12 @@ jobs: # Enable alert commit comment alert-threshold: '200%' comment-always: false + - run: gh cache delete "${{ runner.os }}-web3-benchmark-main.json" + if: github.event_name == 'push' && github.ref == 'refs/heads/4.x' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Save main branch benchmark data - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 if: github.event_name == 'push' && github.ref == 'refs/heads/4.x' with: path: web3-benchmark-main.json diff --git a/.gitignore b/.gitignore index 45c80f83fbd..c22337c0c9a 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,5 @@ packages/web3/.in3/ # benchmark results benchmark-data.txt + +.eslintcache diff --git a/CHANGELOG.md b/CHANGELOG.md index 34609aba1c0..b756bba2604 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2120,7 +2120,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-eth - Ensure provider.supportsSubscriptions exists before watching by subscription (#6440) -- Fixed param sent to `checkRevertBeforeSending` in `sendSignedTransaction` +- Fixed param sent to `checkRevertBeforeSending` in `sendSignedTransaction` - Fixed `defaultTransactionBuilder` for value issue (#6509) #### web3-eth-abi @@ -2129,7 +2129,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-eth-accounts -- Fixed `recover` function, `v` will be normalized to value 0,1 (#6344) +- Fixed `recover` function, `v` will be normalized to value 0,1 (#6344) #### web3-providers-http @@ -2252,7 +2252,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-eth-contract -- Will populate `data` for transactions in contract for metamask provider instead of `input` (#6534) +- Will populate `data` for transactions in contract for metamask provider instead of `input` (#6534) ## [4.3.0] @@ -2260,19 +2260,18 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-core -- Web3config `contractDataInputFill` has been defaulted to `data`, instead of `input`. (#6622) +- Web3config `contractDataInputFill` has been defaulted to `data`, instead of `input`. (#6622) #### web3-eth-contracts -- By default, contracts will fill `data` instead of `input` within method calls (#6622) +- By default, contracts will fill `data` instead of `input` within method calls (#6622) ### Added - #### web3-utils - `SocketProvider` now contains public function `getPendingRequestQueueSize`, `getSentRequestsQueueSize` and `clearQueues` (#6479) -- Added `safeDisconnect` as a `SocketProvider` method to disconnect only when request queue size and send request queue size is 0 (#6479) +- Added `safeDisconnect` as a `SocketProvider` method to disconnect only when request queue size and send request queue size is 0 (#6479) - Add `isContractInitOptions` method (#6555) #### web3 @@ -2305,7 +2304,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-eth-contract -- Allow the `deploy` function to accept parameters, even when no ABI was provided to the `Contract`(#6635) +- Allow the `deploy` function to accept parameters, even when no ABI was provided to the `Contract`(#6635) #### web3 @@ -2324,8 +2323,8 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-eth-contract -- 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) +- 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) #### web3-utils @@ -2347,7 +2346,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f - Adds missing exported type `AbiItem` from 1.x to v4 for compatabiltiy (#6678) -### Changed +### Changed #### web3 @@ -2359,7 +2358,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3 -- Added EIP-6963 utility function `requestEIP6963Providers` for multi provider discovery +- Added EIP-6963 utility function `requestEIP6963Providers` for multi provider discovery #### web3-eth @@ -2393,16 +2392,138 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-eth-ens -- Added function getText and getName in ENS and resolver classes (#6914) +- Added function getText and getName in ENS and resolver classes (#6914) ### fixed #### web3-validator -- Multi-dimensional arrays(with a fix length) are now handled properly when parsing ABIs (#6798) +- Multi-dimensional arrays(with a fix length) are now handled properly when parsing ABIs (#6798) #### web3-utils -- fixed erroneous parsing of big numbers in the `toNumber(...)` function (#6880) +- fixed erroneous parsing of big numbers in the `toNumber(...)` function (#6880) + +## [4.8.0] + +### Changed + +#### web3-eth-abi + +- Dependencies updated + +#### web3-eth-accounts + +- Dependencies updated + +### Fixed + +#### web3-eth-contract + +- Fix an issue with smart contract function overloading (#6922) + +#### web3-utils + +- fixed toHex incorrectly hexing Uint8Arrays and Buffer (#6957) +- fixed isUint8Array not returning true for Buffer (#6957) + +### Added + +#### web3-eth-contract + +- Added a console warning in case of an ambiguous call to a solidity method with parameter overloading (#6942) +- Added contract.deploy(...).decodeData(...) and contract.decodeMethodData(...) that decode data based on the ABI (#6950) + +#### web3-eth + +- method `getBlock` now includes properties of eip 4844, 4895, 4788 when returning block (#6933) +- update type `withdrawalsSchema`, `blockSchema` and `blockHeaderSchema` schemas to include properties of eip 4844, 4895, 4788 (#6933) + +#### web3-types + +- Added `signature` to type `AbiFunctionFragment` (#6922) +- update type `Withdrawals`, `block` and `BlockHeaderOutput` to include properties of eip 4844, 4895, 4788 (#6933) + +## [4.9.0] + +### Added + +#### web3 + +- Updated type `Web3EthInterface.accounts` to includes `privateKeyToAccount`,`privateKeyToAddress`,and `privateKeyToPublicKey` (#6762) + +#### web3-core + +- `defaultReturnFormat` was added to the configuration options. (#6947) + +#### web3-errors + +- Added `InvalidIntegerError` error for fromWei and toWei (#7052) + +#### web3-eth + +- `defaultReturnFormat` was added to all methods that have `ReturnType` param. (#6947) +- `getTransactionFromOrToAttr`, `waitForTransactionReceipt`, `trySendTransaction`, `SendTxHelper` was exported (#7000) + +#### web3-eth-contract + +- `defaultReturnFormat` was added to all methods that have `ReturnType` param. (#6947) + +#### web3-eth-ens + +- `defaultReturnFormat` was added to all methods that have `ReturnType` param. (#6947) + +#### web3-net + +- `defaultReturnFormat` was added to all methods that have `ReturnType` param. (#6947) + +#### web3-types + +- Added `signature` to type `AbiFunctionFragment` (#6922) +- update type `Withdrawals`, `block` and `BlockHeaderOutput` to include properties of eip 4844, 4895, 4788 (#6933) + +#### web3-utils + +- `toWei` add warning when using large numbers or large decimals that may cause precision loss (#6908) +- `toWei` and `fromWei` now supports integers as a unit. (#7053) + +### Fixed + +#### web3-eth + +- Fixed issue with simple transactions, Within `checkRevertBeforeSending` if there is no data set in transaction, set gas to be `21000` (#7043) + +#### web3-utils + +- `toWei` support numbers in scientific notation (#6908) +- `toWei` and `fromWei` trims according to ether unit successfuly (#7044) + +#### web3-validator + +- The JSON schema conversion process now correctly assigns an id when the `abi.name` is not available, for example, in the case of public mappings. (#6981) +- `browser` entry point that was pointing to an non-existing bundle file was removed from `package.json` (#7015) + +#### web3-core + +- Set a try catch block if processesingError fails (#7022) + +### Changed + +#### web3-core + +- Interface `RequestManagerMiddleware` was changed (#7003) + +#### web3-eth + +- Added parameter `customTransactionReceiptSchema` into methods `emitConfirmation`, `waitForTransactionReceipt`, `watchTransactionByPolling`, `watchTransactionBySubscription`, `watchTransactionForConfirmations` (#7000) +- Changed functionality: For networks that returns `baseFeePerGas===0x0` fill `maxPriorityFeePerGas` and `maxFeePerGas` by `getGasPrice` method (#7050) + +#### web3-eth-abi + +- Dependencies updated + +#### web3-rpc-methods + +- Change `estimateGas` method to add possibility pass Transaction type (#7000) ## [Unreleased] \ No newline at end of file diff --git a/FUNDING.json b/FUNDING.json new file mode 100644 index 00000000000..a11415df3a8 --- /dev/null +++ b/FUNDING.json @@ -0,0 +1,7 @@ +{ + "drips": { + "ethereum": { + "ownedBy": "0x689f1278469c6146d835CB99BbfF818fB62989FE" + } + } +} diff --git a/docs/docs/guides/glossary/index.md b/docs/docs/glossary/index.md similarity index 99% rename from docs/docs/guides/glossary/index.md rename to docs/docs/glossary/index.md index e8962c28966..39294de39ed 100644 --- a/docs/docs/guides/glossary/index.md +++ b/docs/docs/glossary/index.md @@ -1,5 +1,5 @@ --- -sidebar_position: 14 +sidebar_position: 18 sidebar_label: '📖 Glossary' title: Glossary --- diff --git a/docs/docs/glossary/json_interface.md b/docs/docs/glossary/json_interface.md deleted file mode 100644 index fe6384e060d..00000000000 --- a/docs/docs/glossary/json_interface.md +++ /dev/null @@ -1,89 +0,0 @@ ---- -title: JSON Interface ---- - -The JSON interface is a `JSON` object describing the [Application Binary Interface (ABI)](https://docs.soliditylang.org/en/develop/abi-spec.html) for an Ethereum smart contract. - -Using this JSON interface, web3.js is able to create a JavaScript object representing the smart contract , its methods and events using the `web3.eth.Contract` object. - -### Specification - -#### Functions - -- `type`: `"function"`, `"constructor"` (can be omitted, defaulting to `"function"`; `"fallback"` also possible but not relevant in web3.js); -- `name`: the name of the function (only present for function types); -- `constant`: `true` if function is specified to not modify the blockchain state; -- `payable`: `true` if function accepts ether, defaults to false; -- `stateMutability`: a `string` with one of the following values: `"pure"` (specified to not read blockchain state), `"view"` (same as constant above), `"non-payable"` and `"payable"` (same as payable above); -- `inputs`: an `Array of objects`, each of which contains: - -- `name`: the name of the parameter; - -- `type`: the canonical type of the parameter. -- `outputs`: an `Array of objects`, same as inputs, can be omitted if no outputs exist. - -#### Events - -- `type`: always `"event"` -- `name`: the name of the event; -- `inputs`: an `Array of objects`, each of which contains: - - - `name`: the name of the parameter; - - - `type`: the canonical type of the parameter. - - - `indexed`: `true` if the field is part of the log’s topics, false if it is one of the log’s data segment. - - - `anonymous`: `true` if the event was declared as anonymous. - -#### Example - -```solidity title='Solidity Contract' -pragma solidity ^0.8.4; - -contract Test { - uint256 a; - address d = 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF; - - constructor(uint256 testInt) { - a = testInt; - } - - event Event(uint256 indexed b, bytes32 c); - - event Event2(uint256 indexed b, bytes32 c); - - function foo(uint256 b, bytes32 c) public returns (address) { - emit Event(b, c); - return d; - } -} - -``` - -```json title='Resulting JSON ABI' -[ - { - "type": "constructor", - "stateMutability": "nonpayable", - "inputs": [{"internalType":"uint256","name":"testInt","type":"uint256"}], - }, - { - "type": "event", - "name": "Event", - "inputs": [{"indexed":true,"internalType":"uint256","name":"b","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"c","type":"bytes32"}], - "anonymous": false, - }, - { - "type": "event", - "name": "Event2", - "inputs": [{"indexed":true,"internalType":"uint256","name":"b","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"c","type":"bytes32"}], - "anonymous": false, - }, - { - "type": "function", - "name": "foo", - "stateMutability": "nonpayable", - "inputs": [{"internalType":"uint256","name":"b","type":"uint256"},{"internalType":"bytes32","name":"c","type":"bytes32"}], - "outputs": [{"internalType":"address","name":"","type":"address"}], - } -] -``` diff --git a/docs/docs/guides/advanced/_category_.yml b/docs/docs/guides/advanced/_category_.yml index 2abe6bbdc6a..3131878f18e 100644 --- a/docs/docs/guides/advanced/_category_.yml +++ b/docs/docs/guides/advanced/_category_.yml @@ -2,4 +2,4 @@ label: '🧠 Advanced' collapsible: true collapsed: true link: null -position: 12 \ No newline at end of file +position: 15 \ No newline at end of file diff --git a/docs/docs/guides/ens/index.md b/docs/docs/guides/ens/index.md index 78de32fc4fb..a55f9662799 100644 --- a/docs/docs/guides/ens/index.md +++ b/docs/docs/guides/ens/index.md @@ -6,6 +6,8 @@ title: Mastering the Web3 ENS package # Using web3.js ENS Package + + In this tutorial, we'll explore how to use the web3.js ENS (Ethereum Name Service) package. The Ethereum Name Service (ENS) is a decentralized domain system built on the Ethereum blockchain. It serves as a distributed, secure, and human-readable naming system designed to map Ethereum addresses, smart contracts, and various other services to easily understandable names. ## Installing web3.js @@ -115,4 +117,4 @@ console.log(result); ## Conclusion -In this tutorial, we've covered how to use the web3.js ENS package to interact with Ethereum Name Service. You should now be able to perform various ENS-related operations using web3.js version 4. For more details visit web3.js ENS [documentation](/libdocs/ENS) section. \ No newline at end of file +In this tutorial, we've covered how to use the web3.js ENS package to interact with Ethereum Name Service. You should now be able to perform various ENS-related operations using web3.js version 4. For more details visit web3.js ENS [documentation](/libdocs/ENS) section. diff --git a/docs/docs/guides/events_subscriptions/_category_.yml b/docs/docs/guides/events_subscriptions/_category_.yml index 528836749db..2f13d5222b4 100644 --- a/docs/docs/guides/events_subscriptions/_category_.yml +++ b/docs/docs/guides/events_subscriptions/_category_.yml @@ -2,4 +2,4 @@ label: '🔔 Events subscription' collapsible: true collapsed: true link: null -position: 6 \ No newline at end of file +position: 7 \ No newline at end of file diff --git a/docs/docs/guides/events_subscriptions/custom_subscriptions.md b/docs/docs/guides/events_subscriptions/custom_subscriptions.md index 6f5826fe902..c57def91b7b 100644 --- a/docs/docs/guides/events_subscriptions/custom_subscriptions.md +++ b/docs/docs/guides/events_subscriptions/custom_subscriptions.md @@ -1,5 +1,5 @@ --- -sidebar_position: 3 +sidebar_position: 2 sidebar_label: 'Custom Subscriptions' --- @@ -8,7 +8,7 @@ sidebar_label: 'Custom Subscriptions' You can extend the `Web3Subscription` class to create custom subscriptions. This way you can subscribe to custom events emitted by the provider. :::note -This guide is most likely for advanced users who are connecting to a node that provides additional custom subscriptions. For normal users, the standard subscriptions are supported out of the box as you can find in [Supported Subscriptions](/guides/events_subscriptions/supported_subscriptions). +This guide is most likely for advanced users who are connecting to a node that provides additional custom subscriptions. For normal users, the standard subscriptions are supported out of the box as you can find in [Supported Subscriptions](/guides/events_subscriptions/index.md). ::: :::important diff --git a/docs/docs/guides/events_subscriptions/index.md b/docs/docs/guides/events_subscriptions/index.md index 7bd2f60b958..bd4ed1a3387 100644 --- a/docs/docs/guides/events_subscriptions/index.md +++ b/docs/docs/guides/events_subscriptions/index.md @@ -1,17 +1,173 @@ --- sidebar_position: 1 -sidebar_label: 'Introduction' +sidebar_label: 'Mastering Events Subcriptions' --- # Events Subscription + -A standard Ethereum node like [Geth supports subscribing to specific events](https://geth.ethereum.org/docs/interacting-with-geth/rpc/pubsub#supported-subscriptions). Additionally, there are some Ethereum nodes that provide additional custom subscriptions. As you can find in [Supported Subscriptions](/guides/events_subscriptions/supported_subscriptions) guide, web3.js enables you to subscribe to the standard events out of the box. And it also provides you with the capability to subscribe to custom subscriptions as you can find in the [Custom Subscriptions](/guides/events_subscriptions/custom_subscriptions) guide. +## Subscribing to smart contracts events + +```ts +import { Web3 } from "web3"; + +// set a provider - MUST be a WebSocket(WSS) provider +const web3 = new Web3("wss://ethereum-rpc.publicnode.com"); + +async function subscribe() { + // create a new contract object, providing the ABI and address + const contract = new web3.eth.Contract(abi, address); + + // subscribe to the smart contract event + const subscription = contract.events.EventName(); + + // new value every time the event is emitted + subscription.on("data", console.log); +} + +// function to unsubscribe from a subscription +async function unsubscribe(subscription) { + await subscription.unsubscribe(); +} + +subscribe(); +unsubscribe(subscription); +``` + + +## Subscribing to node events + +A standard Ethereum node like [Geth supports subscribing to specific events](https://geth.ethereum.org/docs/interacting-with-geth/rpc/pubsub#supported-subscriptions). Additionally, there are some Ethereum nodes that provide additional custom subscriptions. As you can find in this guide, web3.js enables you to subscribe to the standard events out of the box. And it also provides you with the capability to subscribe to custom subscriptions as you can find in the [Custom Subscriptions](/guides/events_subscriptions/custom_subscriptions) guide. :::important If you are the developer who provides custom subscriptions to users. We encourage you to develop a web3.js Plugin after you go through the [Custom Subscription](#custom-subscription) section below. You can find how to develop a plugin at [web3.js Plugin Developer Guide](/guides/web3_plugin_guide/plugin_authors) ::: -## Here are the guides for events subscription -- [Supported Subscriptions Guide](/guides/events_subscriptions/supported_subscriptions) -- [Custom Subscriptions Guide](/guides/events_subscriptions/custom_subscriptions) +- `on("data")` - Fires on each incoming log with the log object as argument. +```ts + subcription.on("data", (data) => console.log(data)); +``` + +- `on("changed")` - Fires on each log which was removed from the blockchain. The log will have the additional property "removed: true". +```ts + subcription.on("changed", (changed) => console.log(changed)); +``` + +- `on("error")` - Fires when an error in the subscription occurs. +```ts + subcription.on("error", (error) => console.log(error)); +``` + +- `on("connected")` - Fires once after the subscription successfully connected. Returns the subscription id. +```ts + subcription.on("connected", (connected) => console.log(connected)); +``` +### Logs + +- `logs`: implemented in the class [`LogsSubscription`](/api/web3-eth/class/LogsSubscription) + +```ts +import { Web3 } from "web3"; + +const web3 = new Web3("wss://ethereum-rpc.publicnode.com"); + +async function subscribe() { + //create subcription + const subcription = await web3.eth.subscribe("logs"); + + //print logs of the latest mined block + subcription.on("data", (data) => console.log(data)); +} + +// function to unsubscribe from a subscription +async function unsubscribe(subscription) { + await subscription.unsubscribe(); +} + +subscribe(); +unsubscribe(subscription); +``` + +### Pending Transactions + +- `newPendingTransactions`: implemented in the class [`NewPendingTransactionsSubscription`](/api/web3-eth/class/NewPendingTransactionsSubscription). +- `pendingTransactions`: same as `newPendingTransactions`. + +```ts +import { Web3 } from "web3"; + +const web3 = new Web3("wss://ethereum-rpc.publicnode.com"); + +async function subscribe() { + //create subcription + const subcription = await web3.eth.subscribe("pendingTransactions"); //or ("newPendingTransactions") + + //print tx hashs of pending transactions + subcription.on("data", (data) => console.log(data)); +} + +// function to unsubscribe from a subscription +async function unsubscribe(subscription) { + await subscription.unsubscribe(); +} + +subscribe(); +unsubscribe(subscription); +``` + +### Block headers + +- `newBlockHeaders`: implemented in the class [`NewHeadsSubscription`](/api/web3-eth/class/NewHeadsSubscription). +- `newHeads` same as `newBlockHeaders`. + +```ts +import { Web3 } from "web3"; + +const web3 = new Web3("wss://ethereum-rpc.publicnode.com"); + +async function subscribe() { + //create subcription + const subcription = await web3.eth.subscribe("newBlockHeaders"); //or ("newHeads") + + //print block header everytime a block is mined + subcription.on("data", (data) => console.log(data)); +} + +// function to unsubscribe from a subscription +async function unsubscribe(subscription) { + await subscription.unsubscribe(); +} + +subscribe(); +unsubscribe(subscription); +``` + +### Syncing + +- `syncing`: implemented in the class [`SyncingSubscription`](/api/web3-eth/class/SyncingSubscription) + +```ts +import { Web3 } from "web3"; + +const web3 = new Web3("wss://ethereum-rpc.publicnode.com"); + +async function subscribe() { + //create subcription + const subcription = await web3.eth.subscribe("syncing"); + + //this will return `true` when the node is syncing + //when it’s finished syncing will return `false`, for the `changed` event. + subcription.on("data", (data) => console.log(data)); +} + +// function to unsubscribe from a subscription +async function unsubscribe(subscription) { + await subscription.unsubscribe(); +} + +subscribe(); +unsubscribe(subscription); +``` + + diff --git a/docs/docs/guides/events_subscriptions/supported_subscriptions.md b/docs/docs/guides/events_subscriptions/supported_subscriptions.md deleted file mode 100644 index bb8b704dd62..00000000000 --- a/docs/docs/guides/events_subscriptions/supported_subscriptions.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -sidebar_position: 2 -sidebar_label: 'Supported Subscriptions' ---- - -# Supported Subscriptions - -web3.js supports the standard Ethereum subscriptions out of the box. And they are the ones registered inside [registeredSubscriptions](/api/web3-eth#registeredSubscriptions) object. Here are a list of them: - -- `logs`: implemented in the class [`LogsSubscription`](/api/web3-eth/class/LogsSubscription). -- `newBlockHeaders`: implemented in the class [`NewHeadsSubscription`](/api/web3-eth/class/NewHeadsSubscription). -- `newHeads` same as `newBlockHeaders`. -- `newPendingTransactions`: implemented in the class [`NewPendingTransactionsSubscription`](/api/web3-eth/class/NewPendingTransactionsSubscription). -- `pendingTransactions`: same as `newPendingTransactions`. -- `syncing`: implemented in the class [`SyncingSubscription`](/api/web3-eth/class/SyncingSubscription) diff --git a/docs/docs/guides/feedback/index.md b/docs/docs/guides/feedback/index.md index f096d471236..0118436ec91 100644 --- a/docs/docs/guides/feedback/index.md +++ b/docs/docs/guides/feedback/index.md @@ -1,5 +1,5 @@ --- -sidebar_position: 15 +sidebar_position: 18 sidebar_label: '🗣️ Feedback' --- diff --git a/docs/docs/guides/getting_started/introduction.md b/docs/docs/guides/getting_started/introduction.md index 30a031a5f1f..ab9a303fc84 100644 --- a/docs/docs/guides/getting_started/introduction.md +++ b/docs/docs/guides/getting_started/introduction.md @@ -55,4 +55,12 @@ Web3.js is modular, consisting of several packages, each serving specific functi - **Web3 Errors:** Web3 Errors has error codes and common error classes that are used by other Web3 packages. -- **Web3 RPC Methods:** This is for advanced uses for building more lightweight applications. It has functions for making RPC requests to Ethereum using a given provider. \ No newline at end of file +- **Web3 RPC Methods:** This is for advanced uses for building more lightweight applications. It has functions for making RPC requests to Ethereum using a given provider. + +## Advantages over other libraries + +- **Extensive Documentation and Community**: Being one of the earliest Ethereum libraries, Web3.js benefits from extensive documentation and a large, active community. Web3.js is widely adopted and has been thoroughly tested in various production environments and is compatible with a broad range of other tools and services in the Ethereum ecosystem. + +- **Modular Design**: Web3.js is designed to be modular, meaning it allows developers to use specific packages according to their needs. This may lead to smaller bundle sizes and faster load times for web applications. + +- **Active Development and Support**: Web3.js sees regular updates and active development. This support is crucial for developers needing assurance that the library they're using will keep pace with the evolving Ethereum landscape. diff --git a/docs/docs/guides/getting_started/metamask.md b/docs/docs/guides/getting_started/metamask.md index 714360b9665..e40db544ac6 100644 --- a/docs/docs/guides/getting_started/metamask.md +++ b/docs/docs/guides/getting_started/metamask.md @@ -5,6 +5,9 @@ sidebar_label: Connecting to Metamask # Connecting to Metamask + + + ## React app After creating your react app `npx create-react-app app-name`, and installing web3 `npm i web3` you can go to `src/app.js`, you can clean up the code and import `{Web3}`. It should look like this: @@ -186,4 +189,4 @@ export default App; ``` - \ No newline at end of file + diff --git a/docs/docs/guides/hardhat_tutorial/_category_.yml b/docs/docs/guides/hardhat_tutorial/_category_.yml index dfd158abb00..191ad0faa29 100644 --- a/docs/docs/guides/hardhat_tutorial/_category_.yml +++ b/docs/docs/guides/hardhat_tutorial/_category_.yml @@ -2,4 +2,4 @@ label: '⛑️ Hardhat Tutorial' collapsible: true collapsed: true link: null -position: 2 +position: 3 diff --git a/docs/docs/guides/migration_from_other_libs/_category_.yml b/docs/docs/guides/migration_from_other_libs/_category_.yml index dc10487c990..665581166bc 100644 --- a/docs/docs/guides/migration_from_other_libs/_category_.yml +++ b/docs/docs/guides/migration_from_other_libs/_category_.yml @@ -2,4 +2,4 @@ label: '🔄 Migration Guides' collapsible: true collapsed: true link: null -position: 11 \ No newline at end of file +position: 12 \ No newline at end of file diff --git a/docs/docs/guides/resources/resources.md b/docs/docs/guides/resources_and_troubleshooting/index.md similarity index 52% rename from docs/docs/guides/resources/resources.md rename to docs/docs/guides/resources_and_troubleshooting/index.md index 0975f4ad3f2..6426b99d8df 100644 --- a/docs/docs/guides/resources/resources.md +++ b/docs/docs/guides/resources_and_troubleshooting/index.md @@ -1,17 +1,54 @@ --- -sidebar_position: 16 -sidebar_label: '📚 Resources' +sidebar_position: 17 +sidebar_label: '📚 Resources & Troubleshooting' --- -# Resources +# Resources & Troubleshooting -## [Web3.js v4 course](https://www.youtube.com/watch?v=3ZO_t-Kyr1g&list=PLPn3rQCo3XrP4LbQcOyyHQR8McV7w3HZT) +## Troubleshooting + +### ReferenceError: Can't find variable: BigInt using React + +Occasionally, users encounter errors in web3.js due to external dependencies, which may seem challenging to resolve within the web3.js framework alone. + +**Resolution Steps:** + +1. Install `rn-nodeify` as a development dependency: +```bash +yarn add --dev rn-nodeify +``` + +2. Add the `big-integer` package: +```bash +yarn add big-integer +``` + +3. Create a file named `shim.js` at the root of your project and include the following polyfill: +```ts +if (typeof BigInt === 'undefined') { + global.BigInt = require('big-integer'); +} +``` + +4. Import shim.js at the top of your App.js: +```ts +// Make sure you use `import` and not `require`! +import './shim.js' +``` + +Additional Info: + +[Facebook/React-native Issue #28492](https://github.com/facebook/react-native/issues/28492#issuecomment-824698934) + +## Resources + +### [Web3.js v4 course](https://www.youtube.com/watch?v=3ZO_t-Kyr1g&list=PLPn3rQCo3XrP4LbQcOyyHQR8McV7w3HZT) This comprehensive 14-part video course from ChainSafe equips you with the skills to conquer the blockchain using web3.js v4. Unlock the potential of web3.js v4 and build cutting-edge dApps. This course caters to all skill levels. [![Web3.js v4 course](https://img.youtube.com/vi/3ZO_t-Kyr1g/0.jpg)](https://www.youtube.com/watch?v=3ZO_t-Kyr1g&list=PLPn3rQCo3XrP4LbQcOyyHQR8McV7w3HZT) -## [Web3.js series](https://www.youtube.com/watch?v=BQ_bDH91S4k&list=PLPn3rQCo3XrNf__8irs4-MjMt4fJqW2I_) +### [Web3.js series](https://www.youtube.com/watch?v=BQ_bDH91S4k&list=PLPn3rQCo3XrNf__8irs4-MjMt4fJqW2I_) This series of 3 videos takes you on a journey through web3.js. Whether you're a complete beginner or want to refine your skills, these videos have something for you: @@ -23,6 +60,6 @@ This series of 3 videos takes you on a journey through web3.js. Whether you're a [![Web3.js series](https://img.youtube.com/vi/BQ_bDH91S4k/0.jpg)](https://www.youtube.com/watch?v=BQ_bDH91S4k&list=PLPn3rQCo3XrNf__8irs4-MjMt4fJqW2I_) -## Hackathons +## Hackathons and Bounties You'll find the latest hackathons opportunities by following [web3js](https://twitter.com/web3_js) on X. diff --git a/docs/docs/guides/smart_contracts/_category_.yml b/docs/docs/guides/smart_contracts/_category_.yml index 6bf6e7694db..fcbba9574a5 100644 --- a/docs/docs/guides/smart_contracts/_category_.yml +++ b/docs/docs/guides/smart_contracts/_category_.yml @@ -2,4 +2,4 @@ label: '📜 Smart Contracts' collapsible: true collapsed: true link: null -position: 4 \ No newline at end of file +position: 6 \ No newline at end of file diff --git a/docs/docs/guides/wagmi_usage/_category_.yml b/docs/docs/guides/wagmi_usage/_category_.yml index a314e00849b..90b55da4312 100644 --- a/docs/docs/guides/wagmi_usage/_category_.yml +++ b/docs/docs/guides/wagmi_usage/_category_.yml @@ -2,4 +2,4 @@ label: '🔄 Wagmi usage' collapsible: true collapsed: true link: null -position: 11 +position: 13 diff --git a/docs/docs/guides/wallet/_category_.yml b/docs/docs/guides/wallet/_category_.yml index d96e556347b..6b079975e01 100644 --- a/docs/docs/guides/wallet/_category_.yml +++ b/docs/docs/guides/wallet/_category_.yml @@ -2,4 +2,4 @@ label: '🔑 Wallet and Accounts ' collapsible: true collapsed: true link: null -position: 3 \ No newline at end of file +position: 5 \ No newline at end of file diff --git a/docs/docs/guides/web3_config/_category_.yml b/docs/docs/guides/web3_config/_category_.yml index 4c1d5ddbd90..d1fd0583848 100644 --- a/docs/docs/guides/web3_config/_category_.yml +++ b/docs/docs/guides/web3_config/_category_.yml @@ -2,4 +2,4 @@ label: '⚙️ Web3 config' collapsible: true collapsed: true link: null -position: 9 \ No newline at end of file +position: 16 \ No newline at end of file diff --git a/docs/docs/guides/web3_config/index.md b/docs/docs/guides/web3_config/index.md index ea061762968..f6d041b6b15 100644 --- a/docs/docs/guides/web3_config/index.md +++ b/docs/docs/guides/web3_config/index.md @@ -27,6 +27,7 @@ There is list of configuration params that can be set for modifying behavior of - [defaultHardfork](/api/web3-core/class/Web3Config#defaultHardfork) - [defaultCommon](/api/web3-core/class/Web3Config#defaultCommon) - [defaultTransactionType](/api/web3-core/class/Web3Config#defaultTransactionType) +- [defaultReturnFormat](/api/web3-core/class/Web3Config#defaultReturnFormat) ## Global level Config @@ -198,4 +199,46 @@ console.log(web3.getContextObject().config) transactionPollingTimeout: 750000, ... */ -``` \ No newline at end of file +``` + + +### defaultReturnFormat +The `defaultReturnFormat` allows users to specify the format in which certain types of data should be returned by default. It is a configuration parameter that can be set at the global level and affects how data is returned across the entire library. +```ts +import { Web3, FMT_NUMBER, FMT_BYTES } from 'web3'; + +web3.defaultReturnFormat = { + number: FMT_NUMBER.BIGINT, + bytes: FMT_BYTES.HEX, +}; + +``` +:::info +The `defaultReturnFormat` can be configured both globally and at the package level: +```ts +import { Web3Eth, FMT_NUMBER, FMT_BYTES } from 'web3-eth'; + +const eth = new Web3Eth() +eth.defaultReturnFormat = { + number: FMT_NUMBER.BIGINT, + bytes: FMT_BYTES.HEX, +}; + +``` +::: +#### All available choices for numeric data: +```ts +export enum FMT_NUMBER { + NUMBER = 'NUMBER_NUMBER', + HEX = 'NUMBER_HEX', + STR = 'NUMBER_STR', + BIGINT = 'NUMBER_BIGINT', +} +``` +#### All available choices for bytes data: +```ts +export enum FMT_BYTES { + HEX = 'BYTES_HEX', + UINT8ARRAY = 'BYTES_UINT8ARRAY', +} +``` diff --git a/docs/docs/guides/web3_eth/_category_.yml b/docs/docs/guides/web3_eth/_category_.yml index f7e09e00d19..197604038d5 100644 --- a/docs/docs/guides/web3_eth/_category_.yml +++ b/docs/docs/guides/web3_eth/_category_.yml @@ -1,5 +1,5 @@ -label: '📦 Web3Eth module' +label: '📦 Web3 Eth module' collapsible: true collapsed: true link: null -position: 7 \ No newline at end of file +position: 8 \ No newline at end of file diff --git a/docs/docs/guides/web3_eth/eth.md b/docs/docs/guides/web3_eth/eth.md index cce7370e9a0..5db4eef6919 100644 --- a/docs/docs/guides/web3_eth/eth.md +++ b/docs/docs/guides/web3_eth/eth.md @@ -8,6 +8,8 @@ import TabItem from '@theme/TabItem'; # Getting Started with `eth` Package + + ## Introduction The `web3-eth` package provides a set of powerful functionalities to interact with the Ethereum blockchain and smart contracts. In this tutorial, we will guide you through the basics of using the `web3-eth` package of web3.js version 4. We will be using TypeScript throughout the examples. diff --git a/docs/docs/guides/web3_modal_guide/_category_.yml b/docs/docs/guides/web3_modal_guide/_category_.yml new file mode 100644 index 00000000000..6744910f3b6 --- /dev/null +++ b/docs/docs/guides/web3_modal_guide/_category_.yml @@ -0,0 +1,5 @@ +label: '📱 WalletConnect Tutorial' +collapsible: true +collapsed: true +link: null +position: 14 \ No newline at end of file diff --git a/docs/docs/guides/web3_modal_guide/index.mdx b/docs/docs/guides/web3_modal_guide/index.mdx new file mode 100644 index 00000000000..a1bef97eb66 --- /dev/null +++ b/docs/docs/guides/web3_modal_guide/index.mdx @@ -0,0 +1,130 @@ +--- +sidebar_position: 1 +sidebar_label: 'Getting started' +--- + +# Getting Started + +Welcome to the Web3modal📱 Guide. + +The Web3Modal SDK allows you to easily connect your Web3 app with wallets. It provides a simple and intuitive interface for requesting actions such as signing transactions and interacting with smart contracts on the blockchain. + +In this guide, you will learn how to set up Walletconnect with web3js. + + + +## Preview +:::info +Switch your browsers if the preview doesn't load. +::: + + + + +## Installation + +```bash +npm install web3modal-web3js web3js +``` + +## Implementation + +```typescript + +import { createWeb3Modal, defaultConfig } from 'web3modal-web3/react' + +// 1. Get projectId, Your Project ID can be obtained from walletconnect.com +const projectId = 'YOUR_PROJECT_ID' + +// 2. Set chains +const mainnet = { + chainId: 1, + name: 'Ethereum', + currency: 'ETH', + explorerUrl: 'https://etherscan.io', + rpcUrl: 'https://cloudflare-eth.com' +} + +// 3. Create a metadata object +const metadata = { + name: 'My Website', + description: 'My Website description', + url: 'https://mywebsite.com', // origin must match your domain & subdomain + icons: ['https://avatars.mywebsite.com/'] +} + +// 4. Create web3 config +const web3Config = defaultConfig({ + /*Required*/ + metadata, + + /*Optional*/ + enableEIP6963: true, // true by default + enableInjected: true, // true by default + enableCoinbase: true, // true by default + rpcUrl: '...', // used for the Coinbase SDK + defaultChainId: 1, // used for the Coinbase SDK +}) + +// 5. Create a Web3Modal instance +createWeb3Modal({ + ethersConfig, + chains: [mainnet], + projectId, + enableAnalytics: true // Optional - defaults to your Cloud configuration +}) + +export default function App() { + return +} +``` + +## Triggering the modal + +```Typescript + +export default function ConnectButton() { + return +} + +``` + +## Smart Contract Interaction + + + +Web3js can help us interact with wallets and smart contracts: + +```Typescript +import Web3 from 'web3'; +import { ERC20ABI } from './contracts/ERC20'; + +const USDTAddress = '0xdac17f958d2ee523a2206206994597c13d831ec7'; + +function Components() { + const { isConnected } = useWeb3ModalAccount() + const { walletProvider } = useWeb3ModalProvider() + const [USDTBalance, setUSDTBalance] = useState(0); + const [smartContractName, setSmartContractName] = useState(''); + + async function getContractInfo() { + if (!isConnected) throw Error('not connected'); + const web3 = new Web3({ + provider: walletProvider, + config: { defaultNetworkId: chainId }, + }); + const contract = new web3.eth.Contract(ERC20ABI, USDTAddress); + const balance = await contract.methods.balanceOf(address).call(); + const name = (await contract.methods.name().call()) as string; + setUSDTBalance(Number(balance)); + setSmartContractName(name); + } + + return <>

Balance: {USDTBalance} smartContractName: {smartContractName}

+} + +``` + +:::info +- To learn how to set up Web3modal with vue, click [here](/guides/web3_modal_guide/vue). +::: \ No newline at end of file diff --git a/docs/docs/guides/web3_modal_guide/vue.md b/docs/docs/guides/web3_modal_guide/vue.md new file mode 100644 index 00000000000..a113d519197 --- /dev/null +++ b/docs/docs/guides/web3_modal_guide/vue.md @@ -0,0 +1,122 @@ +--- +sidebar_position: 1 +sidebar_label: 'Web3Modal with Vue' +--- + +# Web3Modal with Vue and web3js + +## Live code editor + + + + + +## Installation + +For this guide we will be creating a new project will need to install dependancies. We will be using vite to locally host the app, React and web3modal-web3js + +```bash +npm install web3modal-web3js react react-dom +npm install --save-dev vite @vitejs/plugin-react +``` + +## Implementation + +Within the root of the project create `index.html` +```html + + + + + + React Web3 example + + +
+ + + +``` + +Now we will add the Web3modal code within `src/Web3modal.tsx` +```typescript + +import { createWeb3Modal, defaultConfig } from 'web3modal-web3/react' + +// 1. Get projectId, Your Project ID can be obtained from walletconnect.com +const projectId = 'YOUR_PROJECT_ID' + +// 2. Set chains +const mainnet = { + chainId: 1, + name: 'Ethereum', + currency: 'ETH', + explorerUrl: 'https://etherscan.io', + rpcUrl: 'https://cloudflare-eth.com' +} + +// 3. Create a metadata object +const metadata = { + name: 'My Website', + description: 'My Website description', + url: 'https://mywebsite.com', // origin must match your domain & subdomain + icons: ['https://avatars.mywebsite.com/'] +} + +// 4. Create web3 config +const web3Config = defaultConfig({ + /*Required*/ + metadata, + + /*Optional*/ + enableEIP6963: true, // true by default + enableInjected: true, // true by default + enableCoinbase: true, // true by default + rpcUrl: '...', // used for the Coinbase SDK + defaultChainId: 1, // used for the Coinbase SDK +}) + +// 5. Create a Web3Modal instance +createWeb3Modal({ + ethersConfig, + chains: [mainnet], + projectId, + enableAnalytics: true // Optional - defaults to your Cloud configuration +}) + +export default function App() { + return +} +``` + +Set up vite configs within root `vite.config.js` +```javascript +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [react()] +}) +``` + +And finally add react to the app `src/main.tsx` +```typescript +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.js' + +ReactDOM.createRoot(document.getElementById('app')!).render( + + + +) +``` + +You are finished and have successfully created Web3modal with Vue! + +:::info +- For additional information take a look into the interactive code editor above. +- You can view different examples of setting up walletconnect with web3.js [here](https://github.com/ChainSafe/web3modal/tree/add-examples/examples/vue-web3) +- Learn more about Web3modal [here](https://docs.walletconnect.com/web3modal/about) +::: + diff --git a/docs/docs/guides/web3_plugin_guide/_category_.yml b/docs/docs/guides/web3_plugin_guide/_category_.yml index afd1de17e96..42237f7ea38 100644 --- a/docs/docs/guides/web3_plugin_guide/_category_.yml +++ b/docs/docs/guides/web3_plugin_guide/_category_.yml @@ -1,5 +1,5 @@ -label: '🧩🛠️ Web3 Plugin' +label: '🛠️ Web3 Plugin 🧩' collapsible: true collapsed: true link: null -position: 13 \ No newline at end of file +position: 2 \ No newline at end of file diff --git a/docs/docs/guides/web3_plugin_guide/index.md b/docs/docs/guides/web3_plugin_guide/index.md deleted file mode 100644 index 365ca7b82f1..00000000000 --- a/docs/docs/guides/web3_plugin_guide/index.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -sidebar_position: 1 -sidebar_label: 'Introduction' ---- - -# Introduction - -Welcome to the web3.js Plugins Guide, an exciting new feature introduced in web3.js v4! In addition to the core web3.js libraries, plugins bring specialized functionalities tailored for end-users (functionalities that you, as a developer, can create). These enhancements may involve creating wrappers for specific contracts, adding extra features to RPC methods, or extending the capabilities of web3.js methods. Dive in and explore this innovative addition to web3.js v4! - -- [Plugin Developer Guide (For Creators)](/guides/web3_plugin_guide/plugin_authors) -- [Plugin User Guide (Usage)](/guides/web3_plugin_guide/plugin_users) -- [Plugin List](https://web3js.org/plugins) - - -## Plugin Showcase - -### Chainlink Plugin -- [`npm i @chainsafe/web3-plugin-chainlink`](https://www.npmjs.com/package/@chainsafe/web3-plugin-chainlink) -- **Description**: A Web3.js 4.x Plugin for Interacting With Chainlink Smart Contracts -- **Author**: ChainSafe Systems - -### Tokens Plugin -- [`npm i @chainsafe/web3-plugin-tokens`](https://www.npmjs.com/package/@chainsafe/web3-plugin-tokens) -- **Description**: Plugin to extend web3.js with additional methods to interact with common token interfaces (ERC20, ERC721, ERC1155...) -- **Author**: Peter Grassberger & ChainSafe - -### Craftsman Plugin -- [`npm i web3-plugin-craftsman`](https://www.npmjs.com/package/web3-plugin-craftsman) -- **Description**: web3.js plugin allowing instantiation of contract objects directly from Solidity source code -- **Author**: Muhammad-Altabba - -### Optimism Plugin -- [`npm i @eth-optimism/web3.js-plugin`](https://www.npmjs.com/package/@eth-optimism/web3.js-plugin) -- **Description**: Web3.js plugin for OP-Chain gas estimation -- **Author**: Unknown - -### Near Protocol Plugin -- [`npm i @conx3/web3-plugin-near`](https://npmjs.com/package/@conx3/web3-plugin-near) -- **Description**: web3.js plugin for Near Protocol -- **Author**: Muhammad Altabba - -### Aurora Engine Plugin -- [`npm i @conx3/web3-plugin-aurora`](https://www.npmjs.com/package/@conx3/web3-plugin-aurora) -- **Description**: web3.js plugin for Aurora Engine, an EVM running atop NEAR protocol -- **Author**: Muhammad Altabba - -### Superfluid Plugin -- [`npm i web3-plugin-superfluid`](https://www.npmjs.com/package/web3-plugin-superfluid) -- **Description**: Superfluid Web3.js Plugin -- **Author**: Salman Dabbakuti diff --git a/docs/docs/guides/web3_plugin_guide/index.mdx b/docs/docs/guides/web3_plugin_guide/index.mdx new file mode 100644 index 00000000000..ef1942c53fc --- /dev/null +++ b/docs/docs/guides/web3_plugin_guide/index.mdx @@ -0,0 +1,293 @@ +--- +sidebar_position: 1 +sidebar_label: 'Getting started' +--- + +# Getting Started + + + +Welcome to the Web3 Plugins🧩 Guide, a new feature introduced in web3.js v4. In addition to the core web3.js libraries, plugins bring specialized functionalities tailored for end-users (functionalities, that you, as a developer, can create). These enhancements may involve creating wrappers for specific contracts, adding extra features to RPC methods, adding any external libraries, logic, extending the capabilities of web3.js methods, etc... + +In this guide, you will learn the basics to get started building web3 plugins, setting up providers, importing, and using different web3.js packages. + +- [Plugin Developer Guide (For Creators)](/guides/web3_plugin_guide/plugin_authors) +- [Plugin User Guide (Usage)](/guides/web3_plugin_guide/plugin_users) + +- You can find all the web3 plugins🧩 [here](https://web3js.org/plugins) + +- To list your web3 plugin🧩 into the web3js.org/plugins page, you can submit a PR [here](https://github.com/web3/web3js-landing/blob/main/src/pluginList.ts) + +## Create a plugin + +```javascript +//1. import the `Web3PluginBase` module +import { Web3PluginBase } from "web3"; + +//2. Create a Class extending the `Web3Pluginbase` +class MyPlugin extends Web3PluginBase { + + //3. Add a name to the plugin + pluginNamespace = "pluginExample"; + + //4. Create any methods with your desired functionality + async doSomething(){ + console.log("Hello web3!"); + //send transactions + //initialize contracts + //deploy or interact with contracts + //add your own library, logic or functionality + //much more... + } +} + +module.exports = MyPlugin; +``` + +## Use a plugin + +```javascript +//1. import the plugin and web3 module +import { Web3 } from "web3"; +import MyPlugin from "./plugin"; + +//2. Initialize the web3 instance +const web3 = new Web3("https://eth.llamarpc.com"); + +//3. Register plugin to add the current Web3Context +web3.registerPlugin(new MyPlugin()); + +//4. Use the plugin methods +web3.pluginExample.doSomething(); +//--> Hello web3! +``` + +## Using web3 packages on the plugin + +### Use Eth module + +```js +import { FMT_NUMBER, Web3PluginBase, eth } from 'web3'; + +class MyPlugin extends Web3PluginBase { + pluginNamespace = 'pluginExample'; + + async getChainId() { + //`this` is the web3context used when you register the plugin in the usage + return await eth.getChainId(this, { number: FMT_NUMBER.NUMBER }); + } + + async getBlockNumber() { + return await eth.getBlockNumber(this, { number: FMT_NUMBER.NUMBER }); + } + + //more web3.eth. methods... +} + +export default MyPlugin; +``` + +### Use Utils + +```js +import { Web3PluginBase, utils } from 'web3'; + +class MyPlugin extends Web3PluginBase { + pluginNamespace = 'pluginExample'; + + weiToEth(value) { + //`this` is the web3context used when you register the plugin in the usage + return utils.fromWei(value, 'ether'); + } + + //more web3.eth. methods... +} + +export default MyPlugin; + +``` + + +### Use Accounts + + +```js +import { Web3PluginBase, eth } from 'web3'; + +class MyPlugin extends Web3PluginBase { + pluginNamespace = 'pluginExample'; + + async createAccount() { + const account = eth.accounts.create(); + console.log("account:", account); + /* + account: { + address: '0x59E797F2F66AffA9A419a6BC2ED4742b7cBc2570', + privateKey: '0x52a81fc3a7fd6ce9644147c9fb5bfbe1f708f37b4611b3c3310eb9a6dc85ccf4', + signTransaction: [Function: signTransaction], + sign: [Function: sign], + encrypt: [Function: encrypt] + } + */ + } +} + +export default MyPlugin; +``` + +### Use Wallet + + +```js +import { Web3PluginBase, eth } from 'web3'; + +class MyPlugin extends Web3PluginBase { + pluginNamespace = 'pluginExample'; + + async createWallet() { + //1. Create a random account + const accounts = eth.accounts.create(); + //2. Add the account to the wallet + const wallet = this.wallet.add(accounts); + //by creating the wallet, web3.js will use this account to sign TXs under the hood + console.log(wallet); + /* + Wallet(1) [ + { + address: '0x233725561B1430bE2C24Ce9EEabe63E4B46EC9E3', + privateKey: '0x6856adf06dd803e0354450ccf251f829a2c9ef1177ce371f8835bbfb56cd0898', + signTransaction: [Function: signTransaction], + sign: [Function: sign], + encrypt: [Function: encrypt] + }, + _accountProvider: { + create: [Function: createWithContext], + privateKeyToAccount: [Function: privateKeyToAccountWithContext], + decrypt: [Function: decryptWithContext] + }, + _addressMap: Map(1) { '0x233725561b1430be2c24ce9eeabe63e4b46ec9e3' => 0 }, + _defaultKeyName: 'web3js_wallet' + ] + */ + } +} + +export default MyPlugin; +``` + + +### Use Contract + + +```js +import { Web3PluginBase, Contract } from 'web3'; + +class MyPlugin extends Web3PluginBase { + pluginNamespace = 'pluginExample'; + + async interactWithContract() { + //1. Initialize contract + const myContract = new Contract(ABI, ADDRESS); + + //2. Interact with reading functions + const response = myContract.methods.doSomething().call(); + + //3. Interact with writing functions + //You must initialize a wallet to be able to send the TX from wallet[0].address + const txReceipt = myContract.methods.doSomething().send({from: wallet[0].address}) + } +} + +export default MyPlugin; +``` + +### Use ENS + +```js +import { Web3PluginBase } from "web3"; +import { ENS } from "web3-eth-ens"; + +class MyPlugin extends Web3PluginBase { + pluginNamespace = "pluginExample"; + + async getAddressENS() { + const ens = new ENS(undefined, this); //link to current web3Context + return ens.getAddress("ethereum.eth"); + } + +} + +``` +:::info +More ENS methods [here](https://docs.web3js.org/libdocs/ENS#methods) +::: + + +## Web3 requestManager (custom RPC) + +```js +import { Web3PluginBase } from 'web3'; + +class MyPlugin extends Web3PluginBase { + pluginNamespace = 'pluginExample'; + + async customRPC() { + return await this.requestManager.send({ + method: "custom_RPC_call", + params: [], + }); + } + + async getNonce() { + return await this.requestManager.send({ + jsonrpc: "2.0", + method: "eth_getTransactionCount", + params: ["0xEA9eEca67682Cd9c6Ce3DdD1681049D7A897289F", "latest"], + }); + } + + async getBlockNumber() { + return await this.requestManager.send({ + jsonrpc: "2.0", + method: "eth_blockNumber", + params: [], + }); + } + +} +``` + +:::info +All the Ethereum JSON-RPC API [here](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactioncount) +::: + +## Web3 Config Params + +```js +import { Web3PluginBase } from 'web3'; + +class MyPlugin extends Web3PluginBase { + pluginNamespace = 'pluginExample'; + + async configParams() { + this.config.handleRevert = true; + this.config.defaultTransactionType = 0x1; + //more params... + } +} +``` +:::info +All web3 config params [here](https://docs.web3js.org/guides/web3_config/) +::: + + +## Videos + +### Create your first plugin in 20 minutes! + + + + +### Chainlink plugin: web3.js, viem, ethers vs web3 plugin🧩 + + diff --git a/docs/docs/guides/web3_providers_guide/_category_.yml b/docs/docs/guides/web3_providers_guide/_category_.yml index 3b85fe4849e..f426dbedf9a 100644 --- a/docs/docs/guides/web3_providers_guide/_category_.yml +++ b/docs/docs/guides/web3_providers_guide/_category_.yml @@ -2,4 +2,4 @@ label: '🔌 Providers' collapsible: true collapsed: true link: null -position: 2 \ No newline at end of file +position: 4 \ No newline at end of file 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 52530284ed2..b375a63974f 100644 --- a/docs/docs/guides/web3_upgrade_guide/1.x/index.md +++ b/docs/docs/guides/web3_upgrade_guide/1.x/index.md @@ -79,6 +79,9 @@ It will not have: - `givenProvider` default value is `undefined` instead of `null` - `currentProvider` default value is `undefined` instead of `null` (if web3 is instantiated without a provider) +:::warning +In version 4.x, all numbers return as BigInt instead of string or number, which constitutes a breaking change for users accustomed to handling numbers as string or number in their code. For instance, web3.eth.getBalance will now return BigInt instead of string or number. If you wish to retain numbers as number or string, you can refer to [this guide](/guides/web3_config/#defaultreturnformat) on how to set returning types in web3js 4.x. +::: ### Web3 BatchRequest ```ts diff --git a/docs/docs/guides/web3_upgrade_guide/_category_.yml b/docs/docs/guides/web3_upgrade_guide/_category_.yml index e3022cfe8fd..aeab3125b26 100644 --- a/docs/docs/guides/web3_upgrade_guide/_category_.yml +++ b/docs/docs/guides/web3_upgrade_guide/_category_.yml @@ -2,4 +2,4 @@ label: '⬆️ Upgrading' collapsible: true collapsed: true link: null -position: 10 \ No newline at end of file +position: 11 \ No newline at end of file diff --git a/docs/docs/guides/web3_utils_module/_category_.yml b/docs/docs/guides/web3_utils_module/_category_.yml new file mode 100644 index 00000000000..95a9529cfce --- /dev/null +++ b/docs/docs/guides/web3_utils_module/_category_.yml @@ -0,0 +1,5 @@ +label: '📦 Web3 Utils module' +collapsible: true +collapsed: true +link: null +position: 10 \ No newline at end of file diff --git a/docs/docs/guides/web3_utils_module/mastering_web3-utils.md b/docs/docs/guides/web3_utils_module/mastering_web3-utils.md new file mode 100644 index 00000000000..c1ea5b833f7 --- /dev/null +++ b/docs/docs/guides/web3_utils_module/mastering_web3-utils.md @@ -0,0 +1,242 @@ +# Mastering Utility Functions + +## Introduction + +In this guide, you'll learn about the different functions of the web3 utils package, it contains the methods to know how to generate random bytes in different formats, how to perform conversion between Hex values and numbers, hashing functions, addresses, packing padding and in the last part you will see how to compare block numbers. + +## Installation + +Install only the web3 package +```bash +npm i web3-utils +``` + +Or you can install the web3 library as well and then access web3.utils + +```bash +npm i web3 +``` + +## Imports + +There are three different ways to import utils package. + +### Import the entire web3 library + +```js +// import web3 module +import { Web3 } from "web3"; + +// no need to initialize a provider +Web3.utils.toHex("web3"); +//=> 0x77656233 + +// initializing a provider +const web3 = new Web3("https:// eth.llamarpc.com"); + +// access the utils package +web3.utils.toHex("web3"); +//=> 0x77656233 +``` + + +### Import utils module + +```js +// import utils module +import { utils } from "web3"; + +// access the utils package +utils.toWei("1", "ether") +``` + +### Import specific methods + +```js +// import toWei and toHex functions +import { toWei, toHex } from"web3-utils"; + +// usage +toWei("1", "ether") +toHex("") +``` + +## Methods example + +### Random Hex and Bytes + +```js +// Random bytes in hex format and array format + +console.log(web3.utils.randomBytes(32)); +/* => array format +Uint8Array(32) [ + 251, 70, 124, 65, 203, 180, 92, 234, + 210, 236, 72, 154, 83, 219, 171, 223, + 212, 136, 117, 140, 67, 117, 86, 81, + 234, 245, 148, 186, 175, 83, 98, 78 +] +*/ + +console.log(web3.utils.randomHex(32)); +/* => hex string format +0x594386dc9b2e150979416f9b2a093e01f84a37c4f8db5fc1b0d9b1dc83a12c1f +*/ + +``` +:::info +If you don't give any arguments then both of these functions will have a default value as 32. +::: + +### Conversions - Ethereum Denominations + +We've got two different functions to perform conversions between Ethereum denominations. + +```js +console.log(web3.utils.fromWei("1", "ether")); +// 0.000000000000000001 + +console.log(web3.utils.toWei("1", "ether")); +// 1_000_000_000_000_000_000 +``` + +### Conversions to Hex Values + +```js +// most versatile one +console.log(web3.utils.toHex(10)); +// 0xa + +console.log(web3.utils.toHex(true)); +// 0x01 + +console.log(web3.utils.numberToHex(10)); +// 0xa + +console.log(web3.utils.fromDecimal(10)); +// 0xa + +const arr = new Uint8Array([1, 2, 3, 4]); + +console.log(web3.utils.toHex(arr)); +// 0x7b2230223a312c2231223a322c2232223a332c2233223a347d + +console.log(web3.utils.bytesToHex(arr)); +// 0x01020304 +``` + +### Conversions UTF and ASCII + +```js +console.log(web3.utils.utf8ToHex("😊")); +// 0xf09f988a + +console.log(web3.utils.fromUtf8("😊")); +// 0xf09f988a + +console.log(web3.utils.asciiToHex("😊")); +// 0xd83dde0a + +console.log(web3.utils.toUtf8("0xf09f988a")); +// 😊 + +console.log(web3.utils.hexToUtf8("0xf09f988a")); +// 😊 + +console.log(web3.utils.hexToString("0xf09f988a")); +// 😊 + +// emojis are not ASCII character, that's why it won't work +console.log(web3.utils.toAscii("0x4869")); +// Hi + +console.log(web3.utils.hexToAscii("0x4869")); +// Hi +``` + +### Conversions - Numbers and Bigint + +```js +console.log(web3.utils.toNumber("0xa")); +// 10 (number) + +console.log(web3.utils.hexToNumber("0xa")); +// 10 (number) + +console.log(web3.utils.toDecimal("0xa")); +// 10 (number) + +console.log(web3.utils.hexToNumberString("0xa")); +// 10 (string) + +console.log(web3.utils.toBigInt("0xa")); +// 10n (bigint) +``` + +### Hashing Functions + +```js +// both will return undefined if an empty string is passed as an argument +console.log(web3.utils.sha3("hello web3")); +// 0x6c171485a0138b7b0a49d72b570e1d9c589d42a79ae57329d90671d1ac702d74 + +console.log(web3.utils.soliditySha3({ type: "string", value: "hello web3" })); +// 0x6c171485a0138b7b0a49d72b570e1d9c589d42a79ae57329d90671d1ac702d74 +``` + +### Addresses + +```js +// isAddress() is deprecated so we can use toCheckSumAddress() +// to see if the hex string we are passing is a correct ethereum address + +// passing an address with all characters lowercase +console.log(web3.utils.toChecksumAddress("0xa3286628134bad128faeef82f44e99aa64085c94")); +// 0xA3286628134baD128faeef82F44e99AA64085C94 + +// passing an wrong address +console.log(web3.utils.toChecksumAddress("0xa3286628134bad128faeef82f44e99aa64085c9")); +// InvalidAddressError: Invalid value given "0xa286628134bad128faeef82f44e99aa64085c94". Error: invalid ethereum address. +``` + +### Packing and Padding + +```js +// same as abi.encodePacked() in solidity (must be strings) +// converts everything to hex and packs everything without padding +console.log(web3.utils.encodePacked("1", "1", "1")); +// 0x313131 + + +// it will convert the number `10` to hex('a') and add 0s until it's 32 characters long +// the third argument will be the one that will fill/pad the whole hex string, in this case is '0' +console.log(web3.utils.padRight(10, 32, 0)); +// 0xa0000000000000000000000000000000 + +console.log(web3.utils.rightPad(10, 32, 0)); +// 0xa0000000000000000000000000000000 + +console.log(web3.utils.padLeft(10, 32, 0)); +// 0x0000000000000000000000000000000a + +console.log(web3.utils.leftPad(10, 32, 0)); +// 0x0000000000000000000000000000000a +``` + +### Compare Block Numbers + +```js +// accepts numbers and formats as well +console.log(web3.utils.compareBlockNumbers("pending", "latest")); +// 1 + +console.log(web3.utils.compareBlockNumbers("latest", "pending")); +// -1 + +console.log(web3.utils.compareBlockNumbers("latest", "latest")); +// 0 + +console.log(web3.utils.compareBlockNumbers(2, 2)); +// 0 +``` + diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 29c2c66284c..3cf7c9a8443 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -132,7 +132,7 @@ const config = { position: 'left', }, { - to: '/glossary/json_interface', + to: 'glossary', activeBasePath: '/glossary/', label: 'Glossary', position: 'left', diff --git a/packages/web3-core/CHANGELOG.md b/packages/web3-core/CHANGELOG.md index 39d574bcd43..0346e3f74a9 100644 --- a/packages/web3-core/CHANGELOG.md +++ b/packages/web3-core/CHANGELOG.md @@ -207,4 +207,18 @@ Documentation: - Web3config `contractDataInputFill` has been defaulted to `data`, istead of `input`. (#6622) +## [4.4.0] + +### Added + +- `defaultReturnFormat` was added to the configuration options. (#6947) + +### Changed + +- Interface `RequestManagerMiddleware` was changed (#7003) + +### Fixed + +- Set a try catch block if processesingError fails (#7022) + ## [Unreleased] \ No newline at end of file diff --git a/packages/web3-core/package.json b/packages/web3-core/package.json index 6552a6d8bf4..865aafd0db6 100644 --- a/packages/web3-core/package.json +++ b/packages/web3-core/package.json @@ -1,6 +1,6 @@ { "name": "web3-core", - "version": "4.3.2", + "version": "4.4.0", "description": "Web3 core tools for sub-packages. This is an internal package.", "main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", @@ -42,14 +42,14 @@ "test:integration": "jest --config=./test/integration/jest.config.js --passWithNoTests" }, "dependencies": { - "web3-errors": "^1.1.4", + "web3-errors": "^1.2.0", + "web3-eth-accounts": "^4.1.2", "web3-eth-iban": "^4.0.7", - "web3-eth-accounts": "^4.1.0", "web3-providers-http": "^4.1.0", "web3-providers-ws": "^4.0.7", - "web3-types": "^1.3.1", - "web3-utils": "^4.1.0", - "web3-validator": "^2.0.3" + "web3-types": "^1.6.0", + "web3-utils": "^4.3.0", + "web3-validator": "^2.0.6" }, "optionalDependencies": { "web3-providers-ipc": "^4.0.7" diff --git a/packages/web3-core/src/types.ts b/packages/web3-core/src/types.ts index 3f4e66d1f52..b2b655b351a 100644 --- a/packages/web3-core/src/types.ts +++ b/packages/web3-core/src/types.ts @@ -15,11 +15,16 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -import { HexString, Transaction } from 'web3-types'; +import { + HexString, + JsonRpcPayload, + JsonRpcResponse, + Transaction, + Web3APIMethod, + Web3APIReturnType, +} from 'web3-types'; -export type TransactionTypeParser = ( - transaction: Transaction, -) => HexString | undefined; +export type TransactionTypeParser = (transaction: Transaction) => HexString | undefined; export interface Method { name: string; @@ -30,3 +35,18 @@ export interface ExtensionObject { property?: string; methods: Method[]; } + +export interface RequestManagerMiddleware { + processRequest( + request: JsonRpcPayload, + options?: { [key: string]: unknown }, + ): Promise>; + + processResponse< + AnotherMethod extends Web3APIMethod, + ResponseType = Web3APIReturnType, + >( + response: JsonRpcResponse, + options?: { [key: string]: unknown }, + ): Promise>; +} diff --git a/packages/web3-core/src/web3_config.ts b/packages/web3-core/src/web3_config.ts index 772653cfdeb..3c335292c4a 100644 --- a/packages/web3-core/src/web3_config.ts +++ b/packages/web3-core/src/web3_config.ts @@ -15,7 +15,14 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -import { Numbers, HexString, BlockNumberOrTag, Common } from 'web3-types'; +import { + Numbers, + HexString, + BlockNumberOrTag, + Common, + DEFAULT_RETURN_FORMAT, + DataFormat, +} from 'web3-types'; import { ConfigHardforkMismatchError, ConfigChainMismatchError } from 'web3-errors'; import { isNullish, toHex } from 'web3-utils'; import { TransactionTypeParser } from './types.js'; @@ -52,6 +59,7 @@ export interface Web3ConfigOptions { }; transactionBuilder?: TransactionBuilder; transactionTypeParser?: TransactionTypeParser; + defaultReturnFormat: DataFormat; } type ConfigEvent = P extends unknown @@ -93,6 +101,7 @@ export abstract class Web3Config }, transactionBuilder: undefined, transactionTypeParser: undefined, + defaultReturnFormat: DEFAULT_RETURN_FORMAT, }; public constructor(options?: Partial) { @@ -348,6 +357,15 @@ export abstract class Web3Config this.config.maxListenersWarningThreshold = val; } + public get defaultReturnFormat() { + return this.config.defaultReturnFormat; + } + public set defaultReturnFormat(val) { + this._triggerConfigChange('defaultReturnFormat', val); + + this.config.defaultReturnFormat = val; + } + public get defaultNetworkId() { return this.config.defaultNetworkId; } diff --git a/packages/web3-core/src/web3_context.ts b/packages/web3-core/src/web3_context.ts index 76afcaf8875..b50f8ba30f7 100644 --- a/packages/web3-core/src/web3_context.ts +++ b/packages/web3-core/src/web3_context.ts @@ -25,7 +25,7 @@ import { isNullish } from 'web3-utils'; import { BaseTransaction, TransactionFactory } from 'web3-eth-accounts'; import { isSupportedProvider } from './utils.js'; // eslint-disable-next-line import/no-cycle -import { ExtensionObject } from './types.js'; +import { ExtensionObject, RequestManagerMiddleware } from './types.js'; import { Web3BatchRequest } from './web3_batch_request.js'; // eslint-disable-next-line import/no-cycle import { Web3Config, Web3ConfigEvent, Web3ConfigOptions } from './web3_config.js'; @@ -65,6 +65,7 @@ export type Web3ContextInitOptions< registeredSubscriptions?: RegisteredSubs; accountProvider?: Web3AccountProvider; wallet?: Web3BaseWallet; + requestManagerMiddleware?: RequestManagerMiddleware; }; // eslint-disable-next-line no-use-before-define @@ -129,6 +130,7 @@ export class Web3Context< registeredSubscriptions, accountProvider, wallet, + requestManagerMiddleware } = providerOrContext as Web3ContextInitOptions; this.setConfig(config ?? {}); @@ -138,6 +140,7 @@ export class Web3Context< new Web3RequestManager( provider, config?.enableExperimentalFeatures?.useSubscriptionWhenCheckingBlockTimeout, + requestManagerMiddleware ); if (subscriptionManager) { @@ -352,6 +355,11 @@ export class Web3Context< this.provider = provider; return true; } + + public setRequestManagerMiddleware(requestManagerMiddleware: RequestManagerMiddleware){ + this.requestManager.setMiddleware(requestManagerMiddleware); + } + /** * Will return the {@link Web3BatchRequest} constructor. */ diff --git a/packages/web3-core/src/web3_request_manager.ts b/packages/web3-core/src/web3_request_manager.ts index 3c6cabd0182..1cbe9377988 100644 --- a/packages/web3-core/src/web3_request_manager.ts +++ b/packages/web3-core/src/web3_request_manager.ts @@ -52,6 +52,7 @@ import { isWeb3Provider, } from './utils.js'; import { Web3EventEmitter } from './web3_event_emitter.js'; +import { RequestManagerMiddleware } from './types.js'; export enum Web3RequestManagerEvent { PROVIDER_CHANGED = 'PROVIDER_CHANGED', @@ -73,9 +74,12 @@ export class Web3RequestManager< }> { private _provider?: SupportedProviders; private readonly useRpcCallSpecification?: boolean; + public middleware?: RequestManagerMiddleware; + public constructor( provider?: SupportedProviders | string, useRpcCallSpecification?: boolean, + requestManagerMiddleware?: RequestManagerMiddleware, ) { super(); @@ -83,6 +87,8 @@ export class Web3RequestManager< this.setProvider(provider); } this.useRpcCallSpecification = useRpcCallSpecification; + + if (!isNullish(requestManagerMiddleware)) this.middleware = requestManagerMiddleware; } /** @@ -142,6 +148,10 @@ export class Web3RequestManager< return true; } + public setMiddleware(requestManagerMiddleware: RequestManagerMiddleware) { + this.middleware = requestManagerMiddleware; + } + /** * * Will execute a request @@ -155,7 +165,12 @@ export class Web3RequestManager< Method extends Web3APIMethod, ResponseType = Web3APIReturnType, >(request: Web3APIRequest): Promise { - const response = await this._sendRequest(request); + const requestObj = { ...request }; + + let response = await this._sendRequest(requestObj); + + if (!isNullish(this.middleware)) response = await this.middleware.processResponse(response); + if (jsonRpc.isResponseWithResult(response)) { return response.result; } @@ -188,10 +203,15 @@ export class Web3RequestManager< ); } - const payload = jsonRpc.isBatchRequest(request) - ? jsonRpc.toBatchPayload(request) - : jsonRpc.toPayload(request); + let payload = ( + jsonRpc.isBatchRequest(request) + ? jsonRpc.toBatchPayload(request) + : jsonRpc.toPayload(request) + ) as JsonRpcPayload; + if (!isNullish(this.middleware)) { + payload = await this.middleware.processRequest(payload); + } if (isWeb3Provider(provider)) { let response; @@ -228,7 +248,7 @@ export class Web3RequestManager< // TODO: This could be deprecated and removed. if (isLegacyRequestProvider(provider)) { return new Promise>((resolve, reject) => { - const rejectWithError = (err: unknown) => + const rejectWithError = (err: unknown) => { reject( this._processJsonRpcResponse( payload, @@ -239,6 +259,8 @@ export class Web3RequestManager< }, ), ); + }; + const resolveWithResponse = (response: JsonRpcResponse) => resolve( this._processJsonRpcResponse(payload, response, { @@ -268,7 +290,20 @@ export class Web3RequestManager< const responsePromise = result as unknown as Promise< JsonRpcResponse >; - responsePromise.then(resolveWithResponse).catch(rejectWithError); + responsePromise.then(resolveWithResponse).catch(error => { + try { + // Attempt to process the error response + const processedError = this._processJsonRpcResponse( + payload, + error as JsonRpcResponse, + { legacy: true, error: true }, + ); + reject(processedError); + } catch (processingError) { + // Catch any errors that occur during the error processing + reject(processingError); + } + }); } }); } @@ -425,10 +460,10 @@ export class Web3RequestManager< } else if ((response as unknown) instanceof Error) { error = response as unknown as JsonRpcError; } - + // This message means that there was an error while executing the code of the smart contract // However, more processing will happen at a higher level to decode the error data, - // according to the Error ABI, if it was available as of EIP-838. + // according to the Error ABI, if it was available as of EIP-838. if (error?.message.includes('revert')) throw new ContractExecutionError(error); return false; diff --git a/packages/web3-core/test/unit/__snapshots__/web3_context.test.ts.snap b/packages/web3-core/test/unit/__snapshots__/web3_context.test.ts.snap index 7e9121c85d2..e01ba14a467 100644 --- a/packages/web3-core/test/unit/__snapshots__/web3_context.test.ts.snap +++ b/packages/web3-core/test/unit/__snapshots__/web3_context.test.ts.snap @@ -13,6 +13,10 @@ exports[`Web3Context getContextObject should return correct context object 1`] = "defaultHardfork": "london", "defaultMaxPriorityFeePerGas": "0x9502f900", "defaultNetworkId": undefined, + "defaultReturnFormat": { + "bytes": "BYTES_HEX", + "number": "NUMBER_BIGINT", + }, "defaultTransactionType": "0x2", "enableExperimentalFeatures": { "useRpcCallSpecification": false, diff --git a/packages/web3-core/test/unit/web3_config.test.ts b/packages/web3-core/test/unit/web3_config.test.ts index 32a5fc17ddc..0391eec851a 100644 --- a/packages/web3-core/test/unit/web3_config.test.ts +++ b/packages/web3-core/test/unit/web3_config.test.ts @@ -16,6 +16,7 @@ along with web3.js. If not, see . */ import { toHex } from 'web3-utils'; +import { DEFAULT_RETURN_FORMAT } from 'web3-types'; import { Web3Config, Web3ConfigEvent } from '../../src/web3_config'; class MyConfigObject extends Web3Config {} @@ -44,6 +45,7 @@ const defaultConfig = { transactionConfirmationPollingInterval: undefined, defaultTransactionType: '0x2', defaultMaxPriorityFeePerGas: toHex(2500000000), + defaultReturnFormat: DEFAULT_RETURN_FORMAT, }; const setValue = { string: 'newValue', diff --git a/packages/web3-core/test/unit/web3_context.test.ts b/packages/web3-core/test/unit/web3_context.test.ts index 3c433f7259a..c235a06cb83 100644 --- a/packages/web3-core/test/unit/web3_context.test.ts +++ b/packages/web3-core/test/unit/web3_context.test.ts @@ -18,8 +18,16 @@ along with web3.js. If not, see . // eslint-disable-next-line max-classes-per-file import { ExistingPluginNamespaceError } from 'web3-errors'; import HttpProvider from 'web3-providers-http'; +import { + EthExecutionAPI, + JsonRpcPayload, + JsonRpcResponse, + Web3APIMethod, + Web3APIReturnType, +} from 'web3-types'; import { Web3Context, Web3PluginBase } from '../../src/web3_context'; import { Web3RequestManager } from '../../src/web3_request_manager'; +import { RequestManagerMiddleware } from '../../src/types'; // eslint-disable-next-line @typescript-eslint/ban-types class Context1 extends Web3Context<{}> {} @@ -63,6 +71,27 @@ describe('Web3Context', () => { expect(context.currentProvider).toBeInstanceOf(HttpProvider); }); + + it('should set middleware for the request manager', () => { + const context = new Web3Context('http://test.com'); + + const middleware: RequestManagerMiddleware = { + processRequest: jest.fn( + async (request: JsonRpcPayload) => request, + ), + processResponse: jest.fn( + async < + Method extends Web3APIMethod, + ResponseType = Web3APIReturnType, + >( + response: JsonRpcResponse, + ) => response, + ), + }; + + context.setRequestManagerMiddleware(middleware); + expect(context.requestManager.middleware).toEqual(middleware); + }); }); describe('getContextObject', () => { @@ -80,7 +109,9 @@ describe('Web3Context', () => { ).find(s => s.description === 'shapeMode'); if (symbolForShapeMode) { // eslint-disable-next-line @typescript-eslint/no-explicit-any - delete (context.getContextObject().requestManager as any)._emitter[symbolForShapeMode]; + delete (context.getContextObject().requestManager as any)._emitter[ + symbolForShapeMode + ]; } // eslint-disable-next-line @typescript-eslint/no-explicit-any (context.getContextObject().requestManager as any)._emitter = { diff --git a/packages/web3-core/test/unit/web3_middleware_request_manager.test.ts b/packages/web3-core/test/unit/web3_middleware_request_manager.test.ts new file mode 100644 index 00000000000..7b971883613 --- /dev/null +++ b/packages/web3-core/test/unit/web3_middleware_request_manager.test.ts @@ -0,0 +1,177 @@ +/* +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 { + EthExecutionAPI, + JsonRpcPayload, + JsonRpcRequest, + JsonRpcResponse, + Web3APIMethod, + Web3APIReturnType, +} from 'web3-types'; +import { jsonRpc } from 'web3-utils'; +import { RequestManagerMiddleware } from '../../src/types'; +import { Web3RequestManager } from '../../src/web3_request_manager'; + +class Web3Middleware implements RequestManagerMiddleware { + // eslint-disable-next-line class-methods-use-this + public async processRequest( + request: JsonRpcPayload, + ): Promise> { + // Implement the processRequest logic here + + let requestObj = { ...request }; + if ( + (requestObj as JsonRpcRequest).method === 'eth_call' && + Array.isArray((requestObj as JsonRpcRequest).params) + ) { + (requestObj as JsonRpcRequest) = { + ...(requestObj as JsonRpcRequest), + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + params: [...((requestObj as JsonRpcRequest).params ?? []), '0x0', '0x1'], + }; + } + + return Promise.resolve(requestObj as JsonRpcPayload); + } + + // eslint-disable-next-line class-methods-use-this + public async processResponse< + Method extends Web3APIMethod, + ResponseType = Web3APIReturnType, + >(response: JsonRpcResponse): Promise> { + let responseObj = { ...response }; + if (!jsonRpc.isBatchResponse(responseObj) && responseObj.id === 1) { + responseObj = { + ...responseObj, + result: '0x6a756e616964' as any, + }; + } + + return Promise.resolve(responseObj); + } +} + +describe('Request Manager Middleware', () => { + let requestManagerMiddleware: RequestManagerMiddleware; + + beforeAll(() => { + requestManagerMiddleware = { + processRequest: jest.fn( + async (request: JsonRpcPayload) => request, + ), + processResponse: jest.fn( + async < + Method extends Web3APIMethod, + ResponseType = Web3APIReturnType, + >( + response: JsonRpcResponse, + ) => response, + ), + }; + }); + + it('should set requestManagerMiddleware via constructor', () => { + const web3RequestManager1: Web3RequestManager = new Web3RequestManager( + undefined, + true, + requestManagerMiddleware, + ); + + expect(web3RequestManager1.middleware).toBeDefined(); + expect(web3RequestManager1.middleware).toEqual(requestManagerMiddleware); + }); + + it('should set requestManagerMiddleware via set method', () => { + const middleware2: RequestManagerMiddleware = + new Web3Middleware(); + const web3RequestManager2: Web3RequestManager = new Web3RequestManager( + 'http://localhost:8181', + ); + web3RequestManager2.setMiddleware(middleware2); + + expect(web3RequestManager2.middleware).toBeDefined(); + expect(web3RequestManager2.middleware).toEqual(middleware2); + }); + + it('should call processRequest and processResponse functions of requestManagerMiddleware', async () => { + const web3RequestManager3 = new Web3RequestManager( + 'http://localhost:8080', + true, + requestManagerMiddleware, + ); + + const expectedResponse: JsonRpcResponse = { + jsonrpc: '2.0', + id: 1, + result: '0x0', + }; + + jest.spyOn(web3RequestManager3.provider as any, 'request').mockResolvedValue( + expectedResponse, + ); + + const request = { + id: 1, + method: 'eth_call', + params: [], + }; + + await web3RequestManager3.send(request); + + expect(requestManagerMiddleware.processRequest).toHaveBeenCalledWith({ + jsonrpc: '2.0', + ...request, + }); + expect(requestManagerMiddleware.processResponse).toHaveBeenCalled(); + }); + + it('should allow modification of request and response', async () => { + const middleware3: RequestManagerMiddleware = + new Web3Middleware(); + + const web3RequestManager3 = new Web3RequestManager( + 'http://localhost:8080', + true, + middleware3, + ); + + const expectedResponse: JsonRpcResponse = { + jsonrpc: '2.0', + id: 1, + result: '0x0', + }; + + const mockSendRequest = jest.spyOn(web3RequestManager3.provider as any, 'request'); + mockSendRequest.mockResolvedValue(expectedResponse); + + const request = { + id: 1, + method: 'eth_call', + params: ['0x3'], + }; + + const response = await web3RequestManager3.send(request); + expect(response).toBe('0x6a756e616964'); + + expect(mockSendRequest).toHaveBeenCalledWith({ + ...request, + jsonrpc: '2.0', + params: [...request.params, '0x0', '0x1'], + }); + }); +}); diff --git a/packages/web3-core/test/unit/web3_request_manager.test.ts b/packages/web3-core/test/unit/web3_request_manager.test.ts index 375e27a5d1e..2db8c010fd8 100644 --- a/packages/web3-core/test/unit/web3_request_manager.test.ts +++ b/packages/web3-core/test/unit/web3_request_manager.test.ts @@ -280,6 +280,7 @@ describe('Web3RequestManager', () => { expect(myProvider.request).toHaveBeenCalledTimes(1); expect(await pr).toBe('test'); }); + it('Got a "nullish" response from provider', async () => { const manager = new Web3RequestManager(undefined, undefined); const myProvider = { @@ -1134,6 +1135,38 @@ describe('Web3RequestManager', () => { expect(myProvider.request).toHaveBeenCalledWith(payload, expect.any(Function)); }); + it('should error in isPromise', async () => { + const manager = new Web3RequestManager(); + const myProvider = { + request: jest + .fn() + .mockImplementation(async () => Promise.reject(errorResponse)), + } as any; + + jest.spyOn(manager, 'provider', 'get').mockReturnValue(myProvider); + + await expect(manager.sendBatch(request)).rejects.toEqual(errorResponse); + expect(myProvider.request).toHaveBeenCalledTimes(1); + expect(myProvider.request).toHaveBeenCalledWith(payload, expect.any(Function)); + }); + + it('should catch error and process json response in isPromise', async () => { + const manager = new Web3RequestManager(); + const myProvider = { + request: jest + .fn() + .mockImplementation(async () => Promise.reject(errorResponse)), + } as any; + jest.spyOn(manager as any, '_processJsonRpcResponse').mockImplementation(() => { + return errorResponse; + }); + jest.spyOn(manager, 'provider', 'get').mockReturnValue(myProvider); + + await expect(manager.sendBatch(request)).rejects.toEqual(errorResponse); + expect(myProvider.request).toHaveBeenCalledTimes(1); + expect(myProvider.request).toHaveBeenCalledWith(payload, expect.any(Function)); + }); + it('should pass request to provider and reject if provider returns error', async () => { const manager = new Web3RequestManager(); const myProvider = { diff --git a/packages/web3-errors/CHANGELOG.md b/packages/web3-errors/CHANGELOG.md index 06946bb65df..7f9b132283b 100644 --- a/packages/web3-errors/CHANGELOG.md +++ b/packages/web3-errors/CHANGELOG.md @@ -166,4 +166,10 @@ Documentation: - Fixed grammar and spelling in `transactionTimeoutHint` (#6559) +## [1.2.0] + +### Added + +- Added `InvalidIntegerError` error for fromWei and toWei (#7052) + ## [Unreleased] \ No newline at end of file diff --git a/packages/web3-errors/package.json b/packages/web3-errors/package.json index 89338006efd..1b8587c81b3 100644 --- a/packages/web3-errors/package.json +++ b/packages/web3-errors/package.json @@ -1,6 +1,6 @@ { "name": "web3-errors", - "version": "1.1.4", + "version": "1.2.0", "description": "This package has web3 error classes", "main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", @@ -41,7 +41,7 @@ "test:integration": "jest --config=./test/integration/jest.config.js --passWithNoTests" }, "dependencies": { - "web3-types": "^1.3.1" + "web3-types": "^1.6.0" }, "devDependencies": { "@types/jest": "^28.1.6", diff --git a/packages/web3-errors/src/error_codes.ts b/packages/web3-errors/src/error_codes.ts index 1db99b30ea7..16c95ad81cb 100644 --- a/packages/web3-errors/src/error_codes.ts +++ b/packages/web3-errors/src/error_codes.ts @@ -155,6 +155,7 @@ export const ERR_INVALID_LARGE_VALUE = 1011; export const ERR_INVALID_BLOCK = 1012; export const ERR_INVALID_TYPE_ABI = 1013; export const ERR_INVALID_NIBBLE_WIDTH = 1014; +export const ERR_INVALID_INTEGER = 1015; // Validation error codes export const ERR_VALIDATION = 1100; diff --git a/packages/web3-errors/src/errors/utils_errors.ts b/packages/web3-errors/src/errors/utils_errors.ts index b93ff964f38..ca213958b9a 100644 --- a/packages/web3-errors/src/errors/utils_errors.ts +++ b/packages/web3-errors/src/errors/utils_errors.ts @@ -31,6 +31,7 @@ import { ERR_INVALID_TYPE, ERR_INVALID_TYPE_ABI, ERR_INVALID_UNIT, + ERR_INVALID_INTEGER, ERR_INVALID_UNSIGNED_INTEGER, } from '../error_codes.js'; import { InvalidValueError } from '../web3_error_base.js'; @@ -75,6 +76,15 @@ export class InvalidUnitError extends InvalidValueError { } } +export class InvalidIntegerError extends InvalidValueError { + public code = ERR_INVALID_INTEGER; + + public constructor(value: unknown) { + super(value, 'not a valid unit. Must be a positive integer'); + + } +} + export class HexProcessingError extends InvalidValueError { public code = ERR_INVALID_HEX; diff --git a/packages/web3-eth-abi/CHANGELOG.md b/packages/web3-eth-abi/CHANGELOG.md index 6fa52af5287..4a7f1fe8add 100644 --- a/packages/web3-eth-abi/CHANGELOG.md +++ b/packages/web3-eth-abi/CHANGELOG.md @@ -164,4 +164,16 @@ Documentation: - Fixed an issue with detecting Uint8Array (#6486) -## [Unreleased] +## [4.2.1] + +### Changed + +- Dependencies updated + +## [4.2.2] + +### Changed + +- Dependencies updated + +## [Unreleased] \ No newline at end of file diff --git a/packages/web3-eth-abi/package.json b/packages/web3-eth-abi/package.json index 47802dc53c1..4333ba616bb 100644 --- a/packages/web3-eth-abi/package.json +++ b/packages/web3-eth-abi/package.json @@ -1,6 +1,6 @@ { "name": "web3-eth-abi", - "version": "4.2.0", + "version": "4.2.2", "description": "Web3 module encode and decode EVM in/output.", "main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", @@ -43,10 +43,10 @@ }, "dependencies": { "abitype": "0.7.1", - "web3-errors": "^1.1.4", - "web3-types": "^1.3.1", - "web3-utils": "^4.1.1", - "web3-validator": "^2.0.4" + "web3-errors": "^1.2.0", + "web3-types": "^1.6.0", + "web3-utils": "^4.3.0", + "web3-validator": "^2.0.6" }, "devDependencies": { "@humeris/espresso-shot": "^4.0.0", diff --git a/packages/web3-eth-accounts/CHANGELOG.md b/packages/web3-eth-accounts/CHANGELOG.md index 05682b29954..30514ec972d 100644 --- a/packages/web3-eth-accounts/CHANGELOG.md +++ b/packages/web3-eth-accounts/CHANGELOG.md @@ -156,5 +156,10 @@ Documentation: - Send Transaction config used to be ignored if the passed `common` did not have a `copy()` and the `chainId` was not provided (#6663) - Fixed an issue with detecting Uint8Array (#6486) -## [Unreleased] +## [4.1.2] +### Changed + +- Dependencies updated + +## [Unreleased] \ No newline at end of file diff --git a/packages/web3-eth-accounts/package.json b/packages/web3-eth-accounts/package.json index 1906d06b599..dbd0a9fc881 100644 --- a/packages/web3-eth-accounts/package.json +++ b/packages/web3-eth-accounts/package.json @@ -1,6 +1,6 @@ { "name": "web3-eth-accounts", - "version": "4.1.1", + "version": "4.1.2", "description": "Package for managing Ethereum accounts and signing", "main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", @@ -62,8 +62,8 @@ "crc-32": "^1.2.2", "ethereum-cryptography": "^2.0.0", "web3-errors": "^1.1.4", - "web3-types": "^1.3.1", - "web3-utils": "^4.1.1", - "web3-validator": "^2.0.4" + "web3-types": "^1.6.0", + "web3-utils": "^4.2.3", + "web3-validator": "^2.0.5" } } diff --git a/packages/web3-eth-accounts/test/config/setup.js b/packages/web3-eth-accounts/test/config/setup.js index b3c35155474..36be82ea918 100644 --- a/packages/web3-eth-accounts/test/config/setup.js +++ b/packages/web3-eth-accounts/test/config/setup.js @@ -23,6 +23,6 @@ require('jest-extended'); process.env.NODE_ENV = 'test'; -const jestTimeout = 10000; +const jestTimeout = 15000; jest.setTimeout(jestTimeout); diff --git a/packages/web3-eth-contract/CHANGELOG.md b/packages/web3-eth-contract/CHANGELOG.md index 3aa8afa88e8..8e275470925 100644 --- a/packages/web3-eth-contract/CHANGELOG.md +++ b/packages/web3-eth-contract/CHANGELOG.md @@ -328,7 +328,6 @@ Documentation: - Added `dataInputFill` as a ContractInitOption, allowing users to have the choice using property `data`, `input` or `both` for contract methods to be sent to the RPC provider. (#6355) - Added to `Web3Config` property `contractDataInputFill` allowing users to have the choice using property `data`, `input` or `both` for contract methods to be sent to the RPC provider when creating contracts. (#6377) - ## [4.1.1] ### Changed @@ -345,24 +344,24 @@ Documentation: ### Fixed -- Will populate `data` for transactions in contract for metamask provider instead of `input` (#6534) +- Will populate `data` for transactions in contract for metamask provider instead of `input` (#6534) ## [4.1.4] ### Changed -- By default, contracts will fill `data` instead of `input` within method calls (#6622) +- By default, contracts will fill `data` instead of `input` within method calls (#6622) ## [4.2.0] ### Changed -- Allow the `deploy` function to accept parameters, even when no ABI was provided to the `Contract`(#6635) +- 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) -- Fixed: The Contract is not using the context wallet passed if context was passed at constructor. (#6661) +- 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) ## [4.3.0] @@ -370,13 +369,21 @@ Documentation: - Types `ContractDeploySend`, `ContractMethodSend`, `Web3PromiEvent` was exported (#6883) -## [Unreleased] +## [4.4.0] ### Fixed -- Fix an issue with smart contract function overloading (#6922) +- Fix an issue with smart contract function overloading (#6922) + +### Added + +- Added a console warning in case of an ambiguous call to a solidity method with parameter overloading (#6942) +- Added contract.deploy(...).decodeData(...) and contract.decodeMethodData(...) that decode data based on the ABI (#6950) + +## [4.5.0] ### Added -- Added a console warning in case of an ambiguous call to a solidity method with parameter overloading (#6942) -- Added contract.deploy(...).decodeData(...) and contract.decodeMethodData(...) that decode data based on the ABI (#6950) +- `defaultReturnFormat` was added to all methods that have `ReturnType` param. (#6947) + +## [Unreleased] \ No newline at end of file diff --git a/packages/web3-eth-contract/package.json b/packages/web3-eth-contract/package.json index 0ccf9536c56..74edee8f90b 100644 --- a/packages/web3-eth-contract/package.json +++ b/packages/web3-eth-contract/package.json @@ -1,6 +1,6 @@ { "name": "web3-eth-contract", - "version": "4.3.0", + "version": "4.5.0", "description": "Web3 module to interact with Ethereum smart contracts.", "main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", @@ -45,13 +45,13 @@ "test:e2e:firefox": "npx cypress run --headless --browser firefox --env grep='ignore',invert=true" }, "dependencies": { - "web3-core": "^4.3.2", - "web3-errors": "^1.1.4", - "web3-eth": "^4.5.0", - "web3-eth-abi": "^4.2.0", - "web3-types": "^1.5.0", - "web3-utils": "^4.2.2", - "web3-validator": "^2.0.5" + "web3-core": "^4.4.0", + "web3-errors": "^1.2.0", + "web3-eth": "^4.7.0", + "web3-eth-abi": "^4.2.2", + "web3-types": "^1.6.0", + "web3-utils": "^4.3.0", + "web3-validator": "^2.0.6" }, "devDependencies": { "@humeris/espresso-shot": "^4.0.0", @@ -68,7 +68,7 @@ "prettier": "^2.7.1", "ts-jest": "^29.1.1", "typescript": "^4.7.4", - "web3-eth-accounts": "^4.1.1", + "web3-eth-accounts": "^4.1.2", "web3-providers-ws": "^4.0.7" } } diff --git a/packages/web3-eth-contract/src/contract.ts b/packages/web3-eth-contract/src/contract.ts index 7ebff70a136..c4be775b31f 100644 --- a/packages/web3-eth-contract/src/contract.ts +++ b/packages/web3-eth-contract/src/contract.ts @@ -163,13 +163,13 @@ export type ContractMethodsInterface = { } & { [key: string]: ContractBoundMethod }; export type ContractMethodSend = Web3PromiEvent< - FormatType, - SendTransactionEvents + FormatType, + SendTransactionEvents >; export type ContractDeploySend = Web3PromiEvent< // eslint-disable-next-line no-use-before-define Contract, - SendTransactionEvents + SendTransactionEvents >; /** @@ -334,24 +334,24 @@ const contractSubscriptions = { * Decodes the given ABI-encoded data, revealing both the method name and the parameters used in the smart contract call. * This function reverses the encoding process happens at the method `encodeABI`. * It's particularly useful for debugging and understanding the interactions with and between smart contracts. - * + * * #### Parameters - * + * * - `data` **HexString**: The string of ABI-encoded data that needs to be decoded. This should include the method signature and the encoded parameters. - * + * * #### Returns - * + * * - **Object**: This object combines both the decoded parameters and the method name in a readable format. Specifically, the returned object contains: * - `__method__` **String**: The name of the contract method, reconstructed from the ABI. * - `__length__` **Number**: The number of parameters decoded. * - Additional properties representing each parameter by name, as well as their position and values. - * + * * #### Example - * + * * Given an ABI-encoded string from a transaction, you can decode this data to identify the method called and the parameters passed. * Here's a simplified example: - * - * + * + * * ```typescript * const GreeterAbi = [ * { @@ -368,11 +368,11 @@ const contractSubscriptions = { * }, * ]; * const contract = new Contract(GreeterAbi); // Initialize with your contract's ABI - * + * * // The ABI-encoded data string for "setGreeting('Hello World')" * const encodedData = * '0xa41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000'; - * + * * try { * const decoded = contract.decodeMethodData(encodedData); * console.log(decoded.__method__); // Outputs: "setGreeting(string)" @@ -388,7 +388,7 @@ const contractSubscriptions = { * console.error(error); * } * ``` - * + * * ### createAccessList * This will create an access list a method execution will access when executed in the EVM. @@ -513,7 +513,11 @@ export class Contract ); public constructor( jsonInterface: Abi, - addressOrOptionsOrContext?: Address | ContractInitOptions | Web3ContractContext | Web3Context, + addressOrOptionsOrContext?: + | Address + | ContractInitOptions + | Web3ContractContext + | Web3Context, optionsOrContextOrReturnFormat?: | ContractInitOptions | Web3ContractContext @@ -539,14 +543,20 @@ export class Contract } let provider; - if (typeof addressOrOptionsOrContext === 'object' && 'provider' in addressOrOptionsOrContext) { + if ( + typeof addressOrOptionsOrContext === 'object' && + 'provider' in addressOrOptionsOrContext + ) { provider = addressOrOptionsOrContext.provider; } else if ( typeof optionsOrContextOrReturnFormat === 'object' && 'provider' in optionsOrContextOrReturnFormat ) { provider = optionsOrContextOrReturnFormat.provider; - } else if (typeof contextOrReturnFormat === 'object' && 'provider' in contextOrReturnFormat) { + } else if ( + typeof contextOrReturnFormat === 'object' && + 'provider' in contextOrReturnFormat + ) { provider = contextOrReturnFormat.provider; } else { provider = Contract.givenProvider; @@ -583,13 +593,17 @@ export class Contract ? contextOrReturnFormat : isDataFormat(optionsOrContextOrReturnFormat) ? optionsOrContextOrReturnFormat - : returnFormat ?? DEFAULT_RETURN_FORMAT; + : returnFormat ?? this.defaultReturnFormat; const address = typeof addressOrOptionsOrContext === 'string' ? addressOrOptionsOrContext : undefined; this.config.contractDataInputFill = (options as ContractInitOptions)?.dataInputFill ?? this.config.contractDataInputFill; this._parseAndSetJsonInterface(jsonInterface, returnDataFormat); + if (this.defaultReturnFormat !== returnDataFormat) { + this.defaultReturnFormat = returnDataFormat; + } + if (!isNullish(address)) { this._parseAndSetAddress(address, returnDataFormat); } @@ -872,7 +886,7 @@ export class Contract }, estimateGas: async ( options?: PayableCallOptions, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) => { const modifiedOptions = { ...options }; return this._contractMethodEstimateGas({ @@ -887,7 +901,11 @@ export class Contract encodeMethodABI( abi as AbiFunctionFragment, args as unknown[], - format({ format: 'bytes' }, deployData as Bytes, DEFAULT_RETURN_FORMAT), + format( + { format: 'bytes' }, + deployData as Bytes, + this.defaultReturnFormat as typeof DEFAULT_RETURN_FORMAT, + ), ), decodeData: (data: HexString) => ({ ...decodeMethodParams( @@ -979,7 +997,7 @@ export class Contract ? param1 : isDataFormat(param2) ? param2 - : param3 ?? DEFAULT_RETURN_FORMAT; + : param3 ?? this.defaultReturnFormat; const abi = eventName === 'allEvents' || eventName === ALL_EVENTS @@ -1018,7 +1036,8 @@ export class Contract if (Array.isArray(filter[key])) { return (filter[key] as Numbers[]).some( (v: Numbers) => - String(log.returnValues[key]).toUpperCase() === String(v).toUpperCase(), + String(log.returnValues[key]).toUpperCase() === + String(v).toUpperCase(), ); } @@ -1028,7 +1047,10 @@ export class Contract if (hashedIndexedString === String(log.returnValues[key])) return true; } - return String(log.returnValues[key]).toUpperCase() === String(filter[key]).toUpperCase(); + return ( + String(log.returnValues[key]).toUpperCase() === + String(filter[key]).toUpperCase() + ); }); }); } @@ -1036,7 +1058,10 @@ export class Contract return decodedLogs; } - private _parseAndSetAddress(value?: Address, returnFormat: DataFormat = DEFAULT_RETURN_FORMAT) { + private _parseAndSetAddress( + value?: Address, + returnFormat: DataFormat = this.defaultReturnFormat, + ) { this._address = value ? toChecksumAddress(format({ format: 'address' }, value, returnFormat)) : value; @@ -1059,7 +1084,7 @@ export class Contract private _parseAndSetJsonInterface( abis: ContractAbi, - returnFormat: DataFormat = DEFAULT_RETURN_FORMAT, + returnFormat: DataFormat = this.defaultReturnFormat, ) { this._functions = {}; this._methods = {} as ContractMethodsInterface; @@ -1068,7 +1093,9 @@ export class Contract let result: ContractAbi = []; const functionsAbi = abis.filter(abi => abi.type !== 'error'); - const errorsAbi = abis.filter(abi => isAbiErrorFragment(abi)) as unknown as AbiErrorFragment[]; + const errorsAbi = abis.filter(abi => + isAbiErrorFragment(abi), + ) as unknown as AbiErrorFragment[]; for (const a of functionsAbi) { const abi: Mutable = { @@ -1084,7 +1111,9 @@ export class Contract // make constant and payable backwards compatible abi.constant = - abi.stateMutability === 'view' ?? abi.stateMutability === 'pure' ?? abi.constant; + abi.stateMutability === 'view' ?? + abi.stateMutability === 'pure' ?? + abi.constant; abi.payable = abi.stateMutability === 'payable' ?? abi.payable; this._overloadedMethodAbis.set(abi.name, [ @@ -1092,10 +1121,10 @@ export class Contract abi, ]); const abiFragment = this._overloadedMethodAbis.get(abi.name) ?? []; - const contractMethod = this._createContractMethod( - abiFragment, - errorsAbi, - ); + const contractMethod = this._createContractMethod< + typeof abiFragment, + AbiErrorFragment + >(abiFragment, errorsAbi); const exactContractMethod = this._createContractMethod< typeof abiFragment, @@ -1108,7 +1137,8 @@ export class Contract }; // We don't know a particular type of the Abi method so can't type check - this._methods[abi.name as keyof ContractMethodsInterface] = contractMethod as never; + this._methods[abi.name as keyof ContractMethodsInterface] = + contractMethod as never; // We don't know a particular type of the Abi method so can't type check this._methods[methodName as keyof ContractMethodsInterface] = @@ -1184,7 +1214,10 @@ export class Contract for (const _abi of arrayOfAbis) { try { abiParams = this._getAbiParams(_abi, params); - validator.validate(_abi.inputs as unknown as ValidationSchemaInput, abiParams); + validator.validate( + _abi.inputs as unknown as ValidationSchemaInput, + abiParams, + ); applicableMethodAbi.push(_abi); } catch (e) { errors.push(e as Web3ValidationErrorObject); @@ -1200,9 +1233,9 @@ export class Contract } compatible methods: ${JSON.stringify( applicableMethodAbi.map( m => - `${(m as { methodNameWithInputs: string }).methodNameWithInputs} (signature: ${ - (m as { signature: string }).signature - })`, + `${ + (m as { methodNameWithInputs: string }).methodNameWithInputs + } (signature: ${(m as { signature: string }).signature})`, ), )} \n\tThe first one will be used: ${ (methodAbi as { methodNameWithInputs: string }).methodNameWithInputs @@ -1224,14 +1257,22 @@ export class Contract call: async ( options?: PayableCallOptions | NonPayableCallOptions, block?: BlockNumberOrTag, - ) => this._contractMethodCall(methodAbi, abiParams, internalErrorsAbis, options, block), + ) => + this._contractMethodCall( + methodAbi, + abiParams, + internalErrorsAbis, + options, + block, + ), send: (options?: PayableTxOptions | NonPayableTxOptions): ContractMethodSend => this._contractMethodSend(methodAbi, abiParams, internalErrorsAbis, options), estimateGas: async ( options?: PayableCallOptions | NonPayableCallOptions, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this + .defaultReturnFormat as unknown as ReturnFormat, ) => this._contractMethodEstimateGas({ abi: methodAbi, @@ -1289,7 +1330,12 @@ export class Contract }, }); try { - const result = await call(this, tx, block, DEFAULT_RETURN_FORMAT); + const result = await call( + this, + tx, + block, + this.defaultReturnFormat as typeof DEFAULT_RETURN_FORMAT, + ); return decodeMethodReturn(abi, result); } catch (error: unknown) { if (error instanceof ContractExecutionError) { @@ -1320,7 +1366,7 @@ export class Contract }); try { - return createAccessList(this, tx, block, DEFAULT_RETURN_FORMAT); + return createAccessList(this, tx, block, this.defaultReturnFormat); } catch (error: unknown) { if (error instanceof ContractExecutionError) { // this will parse the error data by trying to decode the ABI error inputs according to EIP-838 @@ -1350,7 +1396,7 @@ export class Contract contractOptions: modifiedContractOptions, }); - const transactionToSend = sendTransaction(this, tx, DEFAULT_RETURN_FORMAT, { + const transactionToSend = sendTransaction(this, tx, this.defaultReturnFormat, { // TODO Should make this configurable by the user checkRevertBeforeSending: false, contractAbi: this._jsonInterface, @@ -1380,10 +1426,10 @@ export class Contract const tx = getSendTxParams({ abi, params, - options: { ...options, dataInputFill: this.config.contractDataInputFill }, + options: { ...options, dataInputFill: this.contractDataInputFill }, contractOptions: modifiedContractOptions, }); - return sendTransaction(this, tx, DEFAULT_RETURN_FORMAT, { + return sendTransaction(this, tx, this.defaultReturnFormat, { transactionResolver: receipt => { if (receipt.status === BigInt(0)) { throw new Web3ContractError("code couldn't be stored", receipt); @@ -1423,16 +1469,20 @@ export class Contract options: { ...options, dataInputFill: this.config.contractDataInputFill }, contractOptions: contractOptions ?? this.options, }); - return estimateGas(this, tx, BlockTags.LATEST, returnFormat); + return estimateGas(this, tx, BlockTags.LATEST, returnFormat ?? this.defaultReturnFormat); } // eslint-disable-next-line class-methods-use-this private _createContractEvent( abi: AbiEventFragment & { signature: HexString }, - returnFormat: DataFormat = DEFAULT_RETURN_FORMAT, + returnFormat: DataFormat = this.defaultReturnFormat, ): ContractBoundEvent { return (...params: unknown[]) => { - const { topics, fromBlock } = encodeEventABI(this.options, abi, params[0] as EventParameters); + const { topics, fromBlock } = encodeEventABI( + this.options, + abi, + params[0] as EventParameters, + ); const sub = new LogsSubscription( { address: this.options.address, @@ -1442,7 +1492,10 @@ export class Contract }, { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - subscriptionManager: this.subscriptionManager as Web3SubscriptionManager, + subscriptionManager: this.subscriptionManager as Web3SubscriptionManager< + unknown, + any + >, returnFormat, }, ); @@ -1455,7 +1508,10 @@ export class Contract } }) .catch((error: Error) => { - sub.emit('error', new SubscriptionError('Failed to get past events.', error)); + sub.emit( + 'error', + new SubscriptionError('Failed to get past events.', error), + ); }); } this.subscriptionManager?.addSubscription(sub).catch((error: Error) => { diff --git a/packages/web3-eth-contract/src/log_subscription.ts b/packages/web3-eth-contract/src/log_subscription.ts index 6ace1e17478..3cb1a527941 100644 --- a/packages/web3-eth-contract/src/log_subscription.ts +++ b/packages/web3-eth-contract/src/log_subscription.ts @@ -101,7 +101,7 @@ export class LogsSubscription extends Web3Subscription< public readonly topics?: (Topic | Topic[] | null)[]; /** - * The {@doclink glossary/json_interface | JSON Interface} of the event. + * The {@doclink glossary#json-interface-abi | JSON Interface} of the event. */ public readonly abi: AbiEventFragment & { signature: HexString }; diff --git a/packages/web3-eth-contract/test/integration/contract_deploy.test.ts b/packages/web3-eth-contract/test/integration/contract_deploy.test.ts index 1d7277fe881..b8036c123a3 100644 --- a/packages/web3-eth-contract/test/integration/contract_deploy.test.ts +++ b/packages/web3-eth-contract/test/integration/contract_deploy.test.ts @@ -15,6 +15,7 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ import { Web3Eth } from 'web3-eth'; +import { FMT_BYTES, FMT_NUMBER } from 'web3-types'; import { Contract } from '../../src'; import { sleep } from '../shared_fixtures/utils'; import { ERC721TokenAbi, ERC721TokenBytecode } from '../shared_fixtures/build/ERC721Token'; @@ -30,7 +31,8 @@ import { sendFewSampleTxs, closeOpenConnection, getSystemTestBackend, - BACKEND + BACKEND, + mapFormatToType, } from '../fixtures/system_test_utils'; describe('contract', () => { @@ -109,8 +111,33 @@ describe('contract', () => { from: acc.address, gas: '1000000', }); + expect(typeof estimatedGas).toBe('bigint'); expect(Number(estimatedGas)).toBeGreaterThan(0); }); + it.each(Object.values(FMT_NUMBER))( + 'should return estimated gas of contract constructor %p with correct type', + async format => { + const returnFormat = { number: format as FMT_NUMBER, bytes: FMT_BYTES.HEX }; + + const estimatedGas = await new Contract( + GreeterAbi, + { + provider: getSystemTestProvider(), + }, + returnFormat, + ) + .deploy({ + data: GreeterBytecode, + arguments: ['My Greeting'], + }) + .estimateGas({ + from: acc.address, + gas: '1000000', + }); + expect(typeof estimatedGas).toBe(mapFormatToType[format as string]); + expect(Number(estimatedGas)).toBeGreaterThan(0); + }, + ); it('should return estimated gas of contract constructor without arguments', async () => { const estimatedGas = await new Contract(ERC721TokenAbi, undefined, { provider: getSystemTestProvider(), @@ -256,7 +283,7 @@ describe('contract', () => { }); it('should fail with errors on "intrinsic gas too low" OOG', async () => { - if (getSystemTestBackend() !== BACKEND.HARDHAT){ + if (getSystemTestBackend() !== BACKEND.HARDHAT) { // eslint-disable-next-line jest/no-conditional-expect await expect( contract.deploy(deployOptions).send({ ...sendOptions, gas: '100' }), @@ -265,7 +292,9 @@ describe('contract', () => { // eslint-disable-next-line jest/no-conditional-expect await expect( contract.deploy(deployOptions).send({ ...sendOptions, gas: '100' }), - ).rejects.toThrow('Returned error: Transaction requires at least 109656 gas but got 100'); + ).rejects.toThrow( + 'Returned error: Transaction requires at least 109656 gas but got 100', + ); } }); @@ -283,7 +312,7 @@ describe('contract', () => { it('should fail with errors on revert', async () => { const revert = new Contract(DeployRevertAbi); revert.provider = getSystemTestProvider(); - if (getSystemTestBackend() !== BACKEND.HARDHAT){ + if (getSystemTestBackend() !== BACKEND.HARDHAT) { // eslint-disable-next-line jest/no-conditional-expect await expect( revert @@ -293,15 +322,17 @@ describe('contract', () => { .send(sendOptions), ).rejects.toThrow("code couldn't be stored"); } else { - // eslint-disable-next-line jest/no-conditional-expect - await expect( - revert - .deploy({ - data: DeployRevertBytecode, - }) - .send(sendOptions), - ).rejects.toThrow("Error happened while trying to execute a function inside a smart contract"); - } + // eslint-disable-next-line jest/no-conditional-expect + await expect( + revert + .deploy({ + data: DeployRevertBytecode, + }) + .send(sendOptions), + ).rejects.toThrow( + 'Error happened while trying to execute a function inside a smart contract', + ); + } }); }); }); diff --git a/packages/web3-eth-ens/CHANGELOG.md b/packages/web3-eth-ens/CHANGELOG.md index c0212f7f121..c9be2f93561 100644 --- a/packages/web3-eth-ens/CHANGELOG.md +++ b/packages/web3-eth-ens/CHANGELOG.md @@ -153,4 +153,10 @@ Documentation: - Added function getText and getName in ENS and resolver classes (#6914) +## [4.3.0] + +### Added + +- `defaultReturnFormat` was added to all methods that have `ReturnType` param. (#6947) + ## [Unreleased] \ No newline at end of file diff --git a/packages/web3-eth-ens/package.json b/packages/web3-eth-ens/package.json index 994f4427741..4c1865ca983 100644 --- a/packages/web3-eth-ens/package.json +++ b/packages/web3-eth-ens/package.json @@ -1,6 +1,6 @@ { "name": "web3-eth-ens", - "version": "4.2.0", + "version": "4.3.0", "description": "This package has ENS functions for interacting with Ethereum Name Service.", "main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", @@ -59,13 +59,13 @@ }, "dependencies": { "@adraffy/ens-normalize": "^1.8.8", - "web3-core": "^4.3.2", - "web3-errors": "^1.1.4", - "web3-eth": "^4.5.0", - "web3-eth-contract": "^4.3.0", - "web3-net": "^4.0.7", - "web3-types": "^1.5.0", - "web3-utils": "^4.2.2", - "web3-validator": "^2.0.5" + "web3-core": "^4.4.0", + "web3-errors": "^1.2.0", + "web3-eth": "^4.7.0", + "web3-eth-contract": "^4.5.0", + "web3-net": "^4.1.0", + "web3-types": "^1.6.0", + "web3-utils": "^4.3.0", + "web3-validator": "^2.0.6" } } diff --git a/packages/web3-eth-ens/src/ens.ts b/packages/web3-eth-ens/src/ens.ts index 88d6ae58c83..254a33bc842 100644 --- a/packages/web3-eth-ens/src/ens.ts +++ b/packages/web3-eth-ens/src/ens.ts @@ -16,13 +16,16 @@ along with web3.js. If not, see . */ import { Web3Context, Web3ContextObject } from 'web3-core'; -import { ENSNetworkNotSyncedError, ENSUnsupportedNetworkError, RevertInstructionError } from 'web3-errors'; +import { + ENSNetworkNotSyncedError, + ENSUnsupportedNetworkError, + RevertInstructionError, +} from 'web3-errors'; import { isSyncing } from 'web3-eth'; import { Contract } from 'web3-eth-contract'; import { getId } from 'web3-net'; import { Address, - DEFAULT_RETURN_FORMAT, EthExecutionAPI, FMT_NUMBER, PayableCallOptions, @@ -37,25 +40,25 @@ import { Resolver } from './resolver.js'; /** * This class is designed to interact with the ENS system on the Ethereum blockchain. -* For using ENS package, first install Web3 package using: `npm i web3` or `yarn add web3` based on your package manager, after that ENS features can be used as mentioned in following snippet. -* ```ts -* -* import { Web3 } from 'web3'; -* -* const web3 = new Web3('https://127.0.0.1:4545'); -* -* console.log(await web3.eth.ens.getAddress('ethereum.eth')) -* ``` -* For using individual package install `web3-eth-ens` packages using: `npm i web3-eth-ens` or `yarn add web3-eth-ens`. This is more efficient approach for building lightweight applications. -* -* ```ts -*import { ENS } from 'web3-eth-ens'; -* -* const ens = new ENS(undefined,'https://127.0.0.1:4545'); -* -* console.log(await ens.getAddress('vitalik.eth')); -* ``` -*/ + * For using ENS package, first install Web3 package using: `npm i web3` or `yarn add web3` based on your package manager, after that ENS features can be used as mentioned in following snippet. + * ```ts + * + * import { Web3 } from 'web3'; + * + * const web3 = new Web3('https://127.0.0.1:4545'); + * + * console.log(await web3.eth.ens.getAddress('ethereum.eth')) + * ``` + * For using individual package install `web3-eth-ens` packages using: `npm i web3-eth-ens` or `yarn add web3-eth-ens`. This is more efficient approach for building lightweight applications. + * + * ```ts + *import { ENS } from 'web3-eth-ens'; + * + * const ens = new ENS(undefined,'https://127.0.0.1:4545'); + * + * console.log(await ens.getAddress('vitalik.eth')); + * ``` + */ export class ENS extends Web3Context { /** * The registryAddress property can be used to define a custom registry address when you are connected to an unknown chain. It defaults to the main registry address. @@ -175,7 +178,6 @@ export class ENS extends Web3Context { return this._resolver.getText(ENSName, key); } - /** * Resolves the name of an ENS node. * @param ENSName - The node to resolve @@ -246,7 +248,7 @@ export class ENS extends Web3Context { return this._detectedAddress; } const networkType = await getId(this, { - ...DEFAULT_RETURN_FORMAT, + ...this.defaultReturnFormat, number: FMT_NUMBER.HEX, }); // get the network from provider const addr = registryAddresses[networkIds[networkType]]; @@ -292,10 +294,10 @@ export class ENS extends Web3Context { * const receipt = await ens.setAddress('web3js.eth','0xe2597eb05cf9a87eb1309e86750c903ec38e527e'); *``` */ - public async setAddress( + public async setAddress( name: string, address: Address, - txConfig: PayableCallOptions + txConfig: PayableCallOptions, ): Promise { return this._resolver.setAddress(name, address, txConfig); } diff --git a/packages/web3-eth-ens/test/integration/ens.events.test.ts b/packages/web3-eth-ens/test/integration/ens.events.test.ts index 8ad10d08bda..4e199597c24 100644 --- a/packages/web3-eth-ens/test/integration/ens.events.test.ts +++ b/packages/web3-eth-ens/test/integration/ens.events.test.ts @@ -18,9 +18,8 @@ along with web3.js. If not, see . /* eslint-disable @typescript-eslint/no-unused-vars */ import { Contract, PayableTxOptions } from 'web3-eth-contract'; import { sha3 } from 'web3-utils'; -import { getBlock } from 'web3-eth'; -import { Address, Bytes, DEFAULT_RETURN_FORMAT } from 'web3-types'; +import { Address, Bytes } from 'web3-types'; // eslint-disable-next-line import/no-extraneous-dependencies import { IpcProvider } from 'web3-providers-ipc'; import { ENS } from '../../src'; @@ -75,7 +74,7 @@ describeIf(isSocket)('ens events', () => { const acc2 = await createTempAccount(); accountOne = acc2.address; - sendOptions = { from: defaultAccount, gas: '10000000' }; + sendOptions = { from: defaultAccount, type: '0x1' }; const Registry = new Contract(ENSRegistryAbi, undefined, { provider: getSystemTestProvider(), @@ -120,15 +119,6 @@ describeIf(isSocket)('ens events', () => { else provider = new ENS.providers.HttpProvider(clientUrl); ens = new ENS(registry.options.address, provider); - - const block = await getBlock(ens, 'latest', false, DEFAULT_RETURN_FORMAT); - const gas = block.gasLimit.toString(); - - // Increase gas for contract calls - sendOptions = { - ...sendOptions, - gas, - }; }); afterAll(async () => { diff --git a/packages/web3-eth-ens/test/integration/ens.test.ts b/packages/web3-eth-ens/test/integration/ens.test.ts index 5683451b66f..bde73e72fc7 100644 --- a/packages/web3-eth-ens/test/integration/ens.test.ts +++ b/packages/web3-eth-ens/test/integration/ens.test.ts @@ -16,9 +16,8 @@ along with web3.js. If not, see . */ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { getBlock } from 'web3-eth'; import { Contract, PayableTxOptions } from 'web3-eth-contract'; -import { Address, Bytes, DEFAULT_RETURN_FORMAT } from 'web3-types'; +import { Address, Bytes } from 'web3-types'; import { sha3, toChecksumAddress } from 'web3-utils'; // eslint-disable-next-line import/no-extraneous-dependencies import { IpcProvider } from 'web3-providers-ipc'; @@ -76,7 +75,7 @@ describe('ens', () => { const acc2 = await createTempAccount(); accountOne = acc2.address; - sendOptions = { from: defaultAccount, gas: '10000000' }; + sendOptions = { from: defaultAccount, type: '0x1' }; const Registry = new Contract(ENSRegistryAbi, undefined, { provider: getSystemTestProvider(), @@ -121,15 +120,6 @@ describe('ens', () => { else provider = new ENS.providers.HttpProvider(clientUrl); ens = new ENS(registry.options.address, provider); - - const block = await getBlock(ens, 'latest', false, DEFAULT_RETURN_FORMAT); - const gas = block.gasLimit.toString(); - - // Increase gas for contract calls - sendOptions = { - ...sendOptions, - gas, - }; }); afterAll(async () => { diff --git a/packages/web3-eth-ens/test/integration/resolver.test.ts b/packages/web3-eth-ens/test/integration/resolver.test.ts index 587ce2e7b29..f6b2e648991 100644 --- a/packages/web3-eth-ens/test/integration/resolver.test.ts +++ b/packages/web3-eth-ens/test/integration/resolver.test.ts @@ -15,12 +15,10 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import Web3Eth from 'web3-eth'; import { Contract, PayableTxOptions } from 'web3-eth-contract'; import { sha3 } from 'web3-utils'; -import { Address, Bytes, DEFAULT_RETURN_FORMAT } from 'web3-types'; +import { Address, Bytes } from 'web3-types'; // eslint-disable-next-line import/no-extraneous-dependencies import { IpcProvider } from 'web3-providers-ipc'; import { ENS } from '../../src'; @@ -59,8 +57,6 @@ describe('ens', () => { const node = namehash('resolver'); const label = sha3('resolver') as string; - let web3Eth: Web3Eth; - let ens: ENS; let defaultAccount: string; let accountOne: string; @@ -78,7 +74,7 @@ describe('ens', () => { const acc2 = await createTempAccount(); accountOne = acc2.address; - sendOptions = { from: defaultAccount, gas: '10000000' }; + sendOptions = { from: defaultAccount, type: '0x1' }; const Registry = new Contract(ENSRegistryAbi, undefined, { provider: getSystemTestProvider(), @@ -123,16 +119,6 @@ describe('ens', () => { else provider = new ENS.providers.HttpProvider(clientUrl); ens = new ENS(registry.options.address, provider); - - web3Eth = new Web3Eth(provider); - const block = await web3Eth.getBlock('latest', false, DEFAULT_RETURN_FORMAT); - const gas = block.gasLimit.toString(); - - // Increase gas for contract calls - sendOptions = { - ...sendOptions, - gas, - }; }); afterAll(async () => { diff --git a/packages/web3-eth-ens/test/integration/setup.js b/packages/web3-eth-ens/test/integration/setup.js index 5be1bccf7cc..59b1f904213 100644 --- a/packages/web3-eth-ens/test/integration/setup.js +++ b/packages/web3-eth-ens/test/integration/setup.js @@ -19,6 +19,6 @@ along with web3.js. If not, see . // eslint-disable-next-line @typescript-eslint/no-require-imports require('../config/setup'); -const jestTimeout = 15000; +const jestTimeout = 30000; jest.setTimeout(jestTimeout); diff --git a/packages/web3-eth-iban/package.json b/packages/web3-eth-iban/package.json index 8a92af70ea6..81e65c298b1 100644 --- a/packages/web3-eth-iban/package.json +++ b/packages/web3-eth-iban/package.json @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", diff --git a/packages/web3-eth-personal/package.json b/packages/web3-eth-personal/package.json index 4be584d822a..8cb5ada7a04 100644 --- a/packages/web3-eth-personal/package.json +++ b/packages/web3-eth-personal/package.json @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", diff --git a/packages/web3-eth/CHANGELOG.md b/packages/web3-eth/CHANGELOG.md index 90ad4805704..3d2d36b17c0 100644 --- a/packages/web3-eth/CHANGELOG.md +++ b/packages/web3-eth/CHANGELOG.md @@ -200,7 +200,7 @@ Documentation: ### Fixed - Ensure provider.supportsSubscriptions exists before watching by subscription (#6440) -- Fixed param sent to `checkRevertBeforeSending` in `sendSignedTransaction` +- Fixed param sent to `checkRevertBeforeSending` in `sendSignedTransaction` - Fixed `defaultTransactionBuilder` for value issue (#6509) ### Added @@ -225,4 +225,27 @@ Documentation: - Added `eth.getMaxPriorityFeePerGas` method (#6748) +## [4.6.0] + +### Added + +- method `getBlock` now includes properties of eip 4844, 4895, 4788 when returning block (#6933) +- update type `withdrawalsSchema`, `blockSchema` and `blockHeaderSchema` schemas to include properties of eip 4844, 4895, 4788 (#6933) + +## [4.7.0] + +### Added + +- `defaultReturnFormat` was added to all methods that have `ReturnType` param. (#6947) +- `getTransactionFromOrToAttr`, `waitForTransactionReceipt`, `trySendTransaction`, `SendTxHelper` was exported (#7000) + +### Changed + +- Added parameter `customTransactionReceiptSchema` into methods `emitConfirmation`, `waitForTransactionReceipt`, `watchTransactionByPolling`, `watchTransactionBySubscription`, `watchTransactionForConfirmations` (#7000) +- Changed functionality: For networks that returns `baseFeePerGas===0x0` fill `maxPriorityFeePerGas` and `maxFeePerGas` by `getGasPrice` method (#7050) + +### Fixed + +- Fixed issue with simple transactions, Within `checkRevertBeforeSending` if there is no data set in transaction, set gas to be `21000` (#7043) + ## [Unreleased] \ No newline at end of file diff --git a/packages/web3-eth/package.json b/packages/web3-eth/package.json index 7b53c9df5ab..47c188743b2 100644 --- a/packages/web3-eth/package.json +++ b/packages/web3-eth/package.json @@ -1,6 +1,6 @@ { "name": "web3-eth", - "version": "4.5.0", + "version": "4.7.0", "description": "Web3 module to interact with the Ethereum blockchain and smart contracts.", "main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", @@ -63,15 +63,15 @@ }, "dependencies": { "setimmediate": "^1.0.5", - "web3-core": "^4.3.2", - "web3-errors": "^1.1.4", - "web3-eth-abi": "^4.2.0", - "web3-eth-accounts": "^4.1.1", - "web3-net": "^4.0.7", + "web3-core": "^4.4.0", + "web3-errors": "^1.2.0", + "web3-eth-abi": "^4.2.2", + "web3-eth-accounts": "^4.1.2", + "web3-net": "^4.1.0", "web3-providers-ws": "^4.0.7", - "web3-rpc-methods": "^1.2.0", - "web3-types": "^1.5.0", - "web3-utils": "^4.2.1", - "web3-validator": "^2.0.4" + "web3-rpc-methods": "^1.3.0", + "web3-types": "^1.6.0", + "web3-utils": "^4.3.0", + "web3-validator": "^2.0.6" } } diff --git a/packages/web3-eth/src/index.ts b/packages/web3-eth/src/index.ts index d1f1d326832..5e1c4297ffa 100644 --- a/packages/web3-eth/src/index.ts +++ b/packages/web3-eth/src/index.ts @@ -63,6 +63,9 @@ export * from './utils/format_transaction.js'; export * from './utils/prepare_transaction_for_signing.js'; export * from './web3_subscriptions.js'; export { detectTransactionType } from './utils/detect_transaction_type.js'; -export { transactionBuilder } from './utils/transaction_builder.js'; +export { transactionBuilder, getTransactionFromOrToAttr } from './utils/transaction_builder.js'; +export { waitForTransactionReceipt } from './utils/wait_for_transaction_receipt.js'; +export { trySendTransaction } from './utils/try_send_transaction.js'; +export { SendTxHelper } from './utils/send_tx_helper.js'; export default Web3Eth; diff --git a/packages/web3-eth/src/rpc_method_wrappers.ts b/packages/web3-eth/src/rpc_method_wrappers.ts index 1a2cac1ffd8..20e832be892 100644 --- a/packages/web3-eth/src/rpc_method_wrappers.ts +++ b/packages/web3-eth/src/rpc_method_wrappers.ts @@ -122,7 +122,11 @@ export async function getHashRate( ) { const response = await ethRpcMethods.getHashRate(web3Context.requestManager); - return format({ format: 'uint' }, response as Numbers, returnFormat); + return format( + { format: 'uint' }, + response as Numbers, + returnFormat ?? web3Context.defaultReturnFormat, + ); } /** @@ -135,7 +139,11 @@ export async function getGasPrice( ) { const response = await ethRpcMethods.getGasPrice(web3Context.requestManager); - return format({ format: 'uint' }, response as Numbers, returnFormat); + return format( + { format: 'uint' }, + response as Numbers, + returnFormat ?? web3Context.defaultReturnFormat, + ); } /** @@ -148,7 +156,11 @@ export async function getMaxPriorityFeePerGas( ) { const response = await ethRpcMethods.getMaxPriorityFeePerGas(web3Context.requestManager); - return format({ format: 'uint' }, response as Numbers, returnFormat); + return format( + { format: 'uint' }, + response as Numbers, + returnFormat ?? web3Context.defaultReturnFormat, + ); } /** * View additional documentations here: {@link Web3Eth.getBlockNumber} @@ -160,7 +172,11 @@ export async function getBlockNumber( ) { const response = await ethRpcMethods.getBlockNumber(web3Context.requestManager); - return format({ format: 'uint' }, response as Numbers, returnFormat); + return format( + { format: 'uint' }, + response as Numbers, + returnFormat ?? web3Context.defaultReturnFormat, + ); } /** @@ -181,7 +197,11 @@ export async function getBalance( address, blockNumberFormatted, ); - return format({ format: 'uint' }, response as Numbers, returnFormat); + return format( + { format: 'uint' }, + response as Numbers, + returnFormat ?? web3Context.defaultReturnFormat, + ); } /** @@ -205,7 +225,11 @@ export async function getStorageAt( storageSlotFormatted, blockNumberFormatted, ); - return format({ format: 'bytes' }, response as Bytes, returnFormat); + return format( + { format: 'bytes' }, + response as Bytes, + returnFormat ?? web3Context.defaultReturnFormat, + ); } /** @@ -226,7 +250,11 @@ export async function getCode( address, blockNumberFormatted, ); - return format({ format: 'bytes' }, response as Bytes, returnFormat); + return format( + { format: 'bytes' }, + response as Bytes, + returnFormat ?? web3Context.defaultReturnFormat, + ); } /** @@ -257,7 +285,11 @@ export async function getBlock( hydrated, ); } - return format(blockSchema, response as unknown as Block, returnFormat); + return format( + blockSchema, + response as unknown as Block, + returnFormat ?? web3Context.defaultReturnFormat, + ); } /** @@ -286,7 +318,11 @@ export async function getBlockTransactionCount( ); } - return format({ format: 'uint' }, response as Numbers, returnFormat); + return format( + { format: 'uint' }, + response as Numbers, + returnFormat ?? web3Context.defaultReturnFormat, + ); } /** @@ -315,7 +351,11 @@ export async function getBlockUncleCount( ); } - return format({ format: 'uint' }, response as Numbers, returnFormat); + return format( + { format: 'uint' }, + response as Numbers, + returnFormat ?? web3Context.defaultReturnFormat, + ); } /** @@ -349,7 +389,11 @@ export async function getUncle( ); } - return format(blockSchema, response as unknown as Block, returnFormat); + return format( + blockSchema, + response as unknown as Block, + returnFormat ?? web3Context.defaultReturnFormat, + ); } /** @@ -359,7 +403,7 @@ export async function getUncle( export async function getTransaction( web3Context: Web3Context, transactionHash: Bytes, - returnFormat: ReturnFormat, + returnFormat: ReturnFormat = web3Context.defaultReturnFormat as ReturnFormat, ) { const transactionHashFormatted = format( { format: 'bytes32' }, @@ -373,7 +417,9 @@ export async function getTransaction( return isNullish(response) ? response - : formatTransaction(response, returnFormat, { fillInputAndData: true }); + : formatTransaction(response, returnFormat, { + fillInputAndData: true, + }); } /** @@ -387,9 +433,13 @@ export async function getPendingTransactions( const response = await ethRpcMethods.getPendingTransactions(web3Context.requestManager); return response.map(transaction => - formatTransaction(transaction as unknown as Transaction, returnFormat, { - fillInputAndData: true, - }), + formatTransaction( + transaction as unknown as Transaction, + returnFormat ?? web3Context.defaultReturnFormat, + { + fillInputAndData: true, + }, + ), ); } @@ -426,7 +476,9 @@ export async function getTransactionFromBlock( return isNullish(response) ? response - : formatTransaction(response, returnFormat, { fillInputAndData: true }); + : formatTransaction(response, returnFormat ?? web3Context.defaultReturnFormat, { + fillInputAndData: true, + }); } /** @@ -453,7 +505,7 @@ export async function getTransactionReceipt( : (format( transactionReceiptSchema, response as unknown as TransactionReceipt, - returnFormat, + returnFormat ?? web3Context.defaultReturnFormat, ) as TransactionReceipt); } @@ -476,7 +528,11 @@ export async function getTransactionCount( blockNumberFormatted, ); - return format({ format: 'uint' }, response as Numbers, returnFormat); + return format( + { format: 'uint' }, + response as Numbers, + returnFormat ?? web3Context.defaultReturnFormat, + ); } /** @@ -519,7 +575,7 @@ export function sendTransaction< }, ETH_DATA_FORMAT, ); - + try { transactionFormatted = await sendTxHelper.populateGasPrice({ transaction, @@ -548,7 +604,7 @@ export function sendTransaction< const transactionHashFormatted = format( { format: 'bytes32' }, transactionHash as Bytes, - returnFormat, + returnFormat ?? web3Context.defaultReturnFormat, ); sendTxHelper.emitSent(transactionFormatted); sendTxHelper.emitTransactionHash( @@ -558,11 +614,15 @@ export function sendTransaction< const transactionReceipt = await waitForTransactionReceipt( web3Context, transactionHash, - returnFormat, + returnFormat ?? web3Context.defaultReturnFormat, ); const transactionReceiptFormatted = sendTxHelper.getReceiptWithEvents( - format(transactionReceiptSchema, transactionReceipt, returnFormat), + format( + transactionReceiptSchema, + transactionReceipt, + returnFormat ?? web3Context.defaultReturnFormat, + ), ); sendTxHelper.emitReceipt(transactionReceiptFormatted); @@ -661,7 +721,7 @@ export function sendSignedTransaction< const transactionHashFormatted = format( { format: 'bytes32' }, transactionHash as Bytes, - returnFormat, + returnFormat ?? web3Context.defaultReturnFormat, ); sendTxHelper.emitTransactionHash( @@ -671,11 +731,15 @@ export function sendSignedTransaction< const transactionReceipt = await waitForTransactionReceipt( web3Context, transactionHash, - returnFormat, + returnFormat ?? web3Context.defaultReturnFormat, ); const transactionReceiptFormatted = sendTxHelper.getReceiptWithEvents( - format(transactionReceiptSchema, transactionReceipt, returnFormat), + format( + transactionReceiptSchema, + transactionReceipt, + returnFormat ?? web3Context.defaultReturnFormat, + ), ); sendTxHelper.emitReceipt(transactionReceiptFormatted); @@ -715,7 +779,7 @@ export async function sign( web3Context: Web3Context, message: Bytes, addressOrIndex: Address | number, - returnFormat: ReturnFormat, + returnFormat: ReturnFormat = web3Context.defaultReturnFormat as ReturnFormat, ) { const messageFormatted = format({ format: 'bytes' }, message, DEFAULT_RETURN_FORMAT); if (web3Context.wallet?.get(addressOrIndex)) { @@ -747,7 +811,7 @@ export async function sign( export async function signTransaction( web3Context: Web3Context, transaction: Transaction, - returnFormat: ReturnFormat, + returnFormat: ReturnFormat = web3Context.defaultReturnFormat as ReturnFormat, ) { const response = await ethRpcMethods.signTransaction( web3Context.requestManager, @@ -781,7 +845,7 @@ export async function call( web3Context: Web3Context, transaction: TransactionCall, blockNumber: BlockNumberOrTag = web3Context.defaultBlock, - returnFormat: ReturnFormat, + returnFormat: ReturnFormat = web3Context.defaultReturnFormat as ReturnFormat, ) { const blockNumberFormatted = isBlockTag(blockNumber as string) ? (blockNumber as BlockTag) @@ -818,7 +882,11 @@ export async function estimateGas( blockNumberFormatted, ); - return format({ format: 'uint' }, response as Numbers, returnFormat); + return format( + { format: 'uint' }, + response as Numbers, + returnFormat ?? web3Context.defaultReturnFormat, + ); } // TODO - Add input formatting to filter @@ -853,7 +921,11 @@ export async function getLogs( return res; } - return format(logSchema, res as unknown as Log, returnFormat); + return format( + logSchema, + res as unknown as Log, + returnFormat ?? web3Context.defaultReturnFormat, + ); }); return result; @@ -873,7 +945,7 @@ export async function getChainId( { format: 'uint' }, // Response is number in hex formatted string response as unknown as number, - returnFormat, + returnFormat ?? web3Context.defaultReturnFormat, ); } @@ -903,7 +975,11 @@ export async function getProof( blockNumberFormatted, ); - return format(accountSchema, response as unknown as AccountObject, returnFormat); + return format( + accountSchema, + response as unknown as AccountObject, + returnFormat ?? web3Context.defaultReturnFormat, + ); } // TODO Throwing an error with Geth, but not Infura @@ -943,7 +1019,11 @@ export async function getFeeHistory( rewardPercentilesFormatted, ); - return format(feeHistorySchema, response as unknown as FeeHistory, returnFormat); + return format( + feeHistorySchema, + response as unknown as FeeHistory, + returnFormat ?? web3Context.defaultReturnFormat, + ); } /** @@ -966,7 +1046,11 @@ export async function createAccessList( blockNumberFormatted, )) as unknown as AccessListResult; - return format(accessListResultSchema, response, returnFormat); + return format( + accessListResultSchema, + response, + returnFormat ?? web3Context.defaultReturnFormat, + ); } /** @@ -987,5 +1071,5 @@ export async function signTypedData( useLegacy, ); - return format({ format: 'bytes' }, response, returnFormat); + return format({ format: 'bytes' }, response, returnFormat ?? web3Context.defaultReturnFormat); } diff --git a/packages/web3-eth/src/utils/get_revert_reason.ts b/packages/web3-eth/src/utils/get_revert_reason.ts index ae4fc32dbfa..6edb2ec52c3 100644 --- a/packages/web3-eth/src/utils/get_revert_reason.ts +++ b/packages/web3-eth/src/utils/get_revert_reason.ts @@ -16,7 +16,12 @@ along with web3.js. If not, see . */ import { Web3Context } from 'web3-core'; -import { ContractExecutionError, Eip838ExecutionError, InvalidResponseError , MultipleErrors } from 'web3-errors'; +import { + ContractExecutionError, + Eip838ExecutionError, + InvalidResponseError, + MultipleErrors, +} from 'web3-errors'; import { decodeContractErrorData, isAbiErrorFragment } from 'web3-eth-abi'; import { AbiErrorFragment, @@ -80,7 +85,7 @@ export async function getRevertReason< web3Context: Web3Context, transaction: TransactionCall, contractAbi?: ContractAbi, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = web3Context.defaultReturnFormat as ReturnFormat, ): Promise { try { await call(web3Context, transaction, web3Context.defaultBlock, returnFormat); diff --git a/packages/web3-eth/src/utils/get_transaction_gas_pricing.ts b/packages/web3-eth/src/utils/get_transaction_gas_pricing.ts index e5bbcdedfd4..95e0a3c411f 100644 --- a/packages/web3-eth/src/utils/get_transaction_gas_pricing.ts +++ b/packages/web3-eth/src/utils/get_transaction_gas_pricing.ts @@ -38,14 +38,17 @@ async function getEip1559GasPricing( web3Context: Web3Context, returnFormat: ReturnFormat, ): Promise> { - const block = await getBlock(web3Context, web3Context.defaultBlock, false, returnFormat); - + const block = await getBlock(web3Context, web3Context.defaultBlock, false, ETH_DATA_FORMAT); if (isNullish(block.baseFeePerGas)) throw new Eip1559NotSupportedError(); - if (!isNullish(transaction.gasPrice)) { + let gasPrice: Numbers | undefined; + if (isNullish(transaction.gasPrice) && BigInt(block.baseFeePerGas) === BigInt(0)) { + gasPrice = await getGasPrice(web3Context, returnFormat); + } + if (!isNullish(transaction.gasPrice) || !isNullish(gasPrice)) { const convertedTransactionGasPrice = format( { format: 'uint' }, - transaction.gasPrice as Numbers, + transaction.gasPrice ?? gasPrice, returnFormat, ); diff --git a/packages/web3-eth/src/utils/send_tx_helper.ts b/packages/web3-eth/src/utils/send_tx_helper.ts index 2e3385d5d1c..0037e1051fa 100644 --- a/packages/web3-eth/src/utils/send_tx_helper.ts +++ b/packages/web3-eth/src/utils/send_tx_helper.ts @@ -33,7 +33,7 @@ import { ContractAbiWithSignature, } from 'web3-types'; import { Web3Context, Web3EventEmitter, Web3PromiEvent } from 'web3-core'; -import { isNullish } from 'web3-validator'; +import { isNullish, JsonSchema } from 'web3-validator'; import { ContractExecutionError, InvalidResponseError, @@ -122,7 +122,14 @@ export class SendTxHelper< public async checkRevertBeforeSending(tx: TransactionCall) { if (this.options.checkRevertBeforeSending !== false) { - const reason = await getRevertReason(this.web3Context, tx, this.options.contractAbi); + let formatTx = tx; + if (isNullish(tx.data) && isNullish(tx.input) && isNullish(tx.gas)) { // eth.call runs into error if data isnt filled and gas is not defined, its a simple transaction so we fill it with 21000 + formatTx = { + ...tx, + gas: 21000 + } + } + const reason = await getRevertReason(this.web3Context, formatTx, this.options.contractAbi); if (reason !== undefined) { throw await getTransactionError( this.web3Context, @@ -257,9 +264,11 @@ export class SendTxHelper< public emitConfirmation({ receipt, transactionHash, + customTransactionReceiptSchema, }: { receipt: ResolveType; transactionHash: TransactionHash; + customTransactionReceiptSchema?: JsonSchema; }) { if (this.promiEvent.listenerCount('confirmation') > 0) { watchTransactionForConfirmations< @@ -272,6 +281,7 @@ export class SendTxHelper< receipt as unknown as TransactionReceipt, transactionHash, this.returnFormat, + customTransactionReceiptSchema, ); } } diff --git a/packages/web3-eth/src/utils/transaction_builder.ts b/packages/web3-eth/src/utils/transaction_builder.ts index 13edd253330..0d459017b4a 100644 --- a/packages/web3-eth/src/utils/transaction_builder.ts +++ b/packages/web3-eth/src/utils/transaction_builder.ts @@ -29,7 +29,6 @@ import { Web3NetAPI, Numbers, DataFormat, - DEFAULT_RETURN_FORMAT, FormatType, ETH_DATA_FORMAT, } from 'web3-types'; @@ -99,7 +98,7 @@ export const getTransactionFromOrToAttr = ( export const getTransactionNonce = async ( web3Context: Web3Context, address?: Address, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = web3Context.defaultReturnFormat as ReturnFormat, ) => { if (isNullish(address)) { // TODO if (web3.eth.accounts.wallet) use address from local wallet @@ -133,7 +132,7 @@ export async function defaultTransactionBuilder(option let populatedTransaction = format( transactionSchema, options.transaction, - DEFAULT_RETURN_FORMAT, + options.web3Context.defaultReturnFormat, ) as InternalTransaction; if (isNullish(populatedTransaction.from)) { diff --git a/packages/web3-eth/src/utils/wait_for_transaction_receipt.ts b/packages/web3-eth/src/utils/wait_for_transaction_receipt.ts index d335683391e..e422b6307a2 100644 --- a/packages/web3-eth/src/utils/wait_for_transaction_receipt.ts +++ b/packages/web3-eth/src/utils/wait_for_transaction_receipt.ts @@ -30,19 +30,30 @@ export async function waitForTransactionReceipt web3Context: Web3Context, transactionHash: Bytes, returnFormat: ReturnFormat, + customGetTransactionReceipt?: ( + web3Context: Web3Context, + transactionHash: Bytes, + returnFormat: ReturnFormat, + ) => Promise, ): Promise { - const pollingInterval = web3Context.transactionReceiptPollingInterval ?? web3Context.transactionPollingInterval; - const [awaitableTransactionReceipt, IntervalId] = pollTillDefinedAndReturnIntervalId(async () => { - try { - return getTransactionReceipt(web3Context, transactionHash, returnFormat); - } catch (error) { - console.warn('An error happen while trying to get the transaction receipt', error); - return undefined; - } - }, pollingInterval); + const [awaitableTransactionReceipt, IntervalId] = pollTillDefinedAndReturnIntervalId( + async () => { + try { + return (customGetTransactionReceipt ?? getTransactionReceipt)( + web3Context, + transactionHash, + returnFormat, + ); + } catch (error) { + console.warn('An error happen while trying to get the transaction receipt', error); + return undefined; + } + }, + pollingInterval, + ); const [timeoutId, rejectOnTimeout] = rejectIfTimeout( web3Context.transactionPollingTimeout, @@ -65,10 +76,8 @@ export async function waitForTransactionReceipt rejectOnBlockTimeout, // this will throw an error on Transaction Block Timeout ]); } finally { - if(timeoutId) - clearTimeout(timeoutId); - if(IntervalId) - clearInterval(IntervalId); + if (timeoutId) clearTimeout(timeoutId); + if (IntervalId) clearInterval(IntervalId); blockTimeoutResourceCleaner.clean(); } } diff --git a/packages/web3-eth/src/utils/watch_transaction_by_polling.ts b/packages/web3-eth/src/utils/watch_transaction_by_polling.ts index 6be3c572742..34e01581422 100644 --- a/packages/web3-eth/src/utils/watch_transaction_by_polling.ts +++ b/packages/web3-eth/src/utils/watch_transaction_by_polling.ts @@ -20,6 +20,7 @@ import { format, numberToHex } from 'web3-utils'; import { ethRpcMethods } from 'web3-rpc-methods'; import { DataFormat } from 'web3-types'; +import { JsonSchema } from 'web3-validator'; import { SendSignedTransactionEvents, SendTransactionEvents } from '../types.js'; import { transactionReceiptSchema } from '../schemas.js'; @@ -30,6 +31,7 @@ export type Web3PromiEventEventTypeBase = export type WaitProps = { web3Context: Web3Context; transactionReceipt: TransactionReceipt; + customTransactionReceiptSchema?: JsonSchema; transactionPromiEvent: Web3PromiEvent>; returnFormat: ReturnFormat; }; @@ -46,6 +48,7 @@ export const watchTransactionByPolling = < web3Context, transactionReceipt, transactionPromiEvent, + customTransactionReceiptSchema, returnFormat, }: WaitProps) => { // Having a transactionReceipt means that the transaction has already been included @@ -67,7 +70,11 @@ export const watchTransactionByPolling = < transactionPromiEvent.emit('confirmation', { confirmations: format({ format: 'uint' }, confirmations, returnFormat), - receipt: format(transactionReceiptSchema, transactionReceipt, returnFormat), + receipt: format( + customTransactionReceiptSchema ?? transactionReceiptSchema, + transactionReceipt, + returnFormat, + ), latestBlockHash: format( { format: 'bytes32' }, nextBlock.hash as Bytes, diff --git a/packages/web3-eth/src/utils/watch_transaction_by_subscription.ts b/packages/web3-eth/src/utils/watch_transaction_by_subscription.ts index d68c39da95d..e7e68adc529 100644 --- a/packages/web3-eth/src/utils/watch_transaction_by_subscription.ts +++ b/packages/web3-eth/src/utils/watch_transaction_by_subscription.ts @@ -21,7 +21,6 @@ import { DataFormat } from 'web3-types'; import { NewHeadsSubscription } from '../web3_subscriptions.js'; import { transactionReceiptSchema } from '../schemas.js'; import { WaitProps, watchTransactionByPolling } from './watch_transaction_by_polling.js'; - /** * This function watches a Transaction by subscribing to new heads. * It is used by `watchTransactionForConfirmations`, in case the provider supports subscription. @@ -33,6 +32,7 @@ export const watchTransactionBySubscription = < web3Context, transactionReceipt, transactionPromiEvent, + customTransactionReceiptSchema, returnFormat, }: WaitProps) => { // The following variable will stay true except if the data arrived, @@ -66,7 +66,11 @@ export const watchTransactionBySubscription = < confirmations as Numbers, returnFormat, ), - receipt: format(transactionReceiptSchema, transactionReceipt, returnFormat), + receipt: format( + customTransactionReceiptSchema ?? transactionReceiptSchema, + transactionReceipt, + returnFormat, + ), latestBlockHash: format( { format: 'bytes32' }, newBlockHeader.parentHash as Bytes, @@ -85,6 +89,7 @@ export const watchTransactionBySubscription = < web3Context, transactionReceipt, transactionPromiEvent, + customTransactionReceiptSchema, returnFormat, }); }); @@ -94,6 +99,7 @@ export const watchTransactionBySubscription = < watchTransactionByPolling({ web3Context, transactionReceipt, + customTransactionReceiptSchema, transactionPromiEvent, returnFormat, }); diff --git a/packages/web3-eth/src/utils/watch_transaction_for_confirmations.ts b/packages/web3-eth/src/utils/watch_transaction_for_confirmations.ts index 60489d59c50..01ca39f8894 100644 --- a/packages/web3-eth/src/utils/watch_transaction_for_confirmations.ts +++ b/packages/web3-eth/src/utils/watch_transaction_for_confirmations.ts @@ -17,7 +17,7 @@ along with web3.js. If not, see . import { Bytes, EthExecutionAPI, Web3BaseProvider, TransactionReceipt } from 'web3-types'; import { Web3Context, Web3PromiEvent } from 'web3-core'; import { format } from 'web3-utils'; -import { isNullish } from 'web3-validator'; +import { isNullish, JsonSchema } from 'web3-validator'; import { TransactionMissingReceiptOrBlockHashError, @@ -41,6 +41,7 @@ export function watchTransactionForConfirmations< transactionReceipt: TransactionReceipt, transactionHash: Bytes, returnFormat: ReturnFormat, + customTransactionReceiptSchema?: JsonSchema, ) { if (isNullish(transactionReceipt) || isNullish(transactionReceipt.blockHash)) throw new TransactionMissingReceiptOrBlockHashError({ @@ -55,7 +56,11 @@ export function watchTransactionForConfirmations< // As we have the receipt, it's the first confirmation that tx is accepted. transactionPromiEvent.emit('confirmation', { confirmations: format({ format: 'uint' }, 1, returnFormat), - receipt: format(transactionReceiptSchema, transactionReceipt, returnFormat), + receipt: format( + customTransactionReceiptSchema ?? transactionReceiptSchema, + transactionReceipt, + returnFormat, + ), latestBlockHash: format({ format: 'bytes32' }, transactionReceipt.blockHash, returnFormat), }); @@ -66,6 +71,7 @@ export function watchTransactionForConfirmations< web3Context, transactionReceipt, transactionPromiEvent, + customTransactionReceiptSchema, returnFormat, }); } else { @@ -73,6 +79,7 @@ export function watchTransactionForConfirmations< web3Context, transactionReceipt, transactionPromiEvent, + customTransactionReceiptSchema, returnFormat, }); } diff --git a/packages/web3-eth/src/web3_eth.ts b/packages/web3-eth/src/web3_eth.ts index 3c1165b0e3c..959a7d5eddc 100644 --- a/packages/web3-eth/src/web3_eth.ts +++ b/packages/web3-eth/src/web3_eth.ts @@ -201,7 +201,8 @@ export class Web3Eth extends Web3Context( - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = (this.defaultReturnFormat ?? + DEFAULT_RETURN_FORMAT) as ReturnFormat, ) { return this.getHashRate(returnFormat); } @@ -219,7 +220,7 @@ export class Web3Eth extends Web3Context( - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.getHashRate(this, returnFormat); } @@ -237,7 +238,7 @@ export class Web3Eth extends Web3Context( - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.getGasPrice(this, returnFormat); } @@ -256,7 +257,7 @@ export class Web3Eth extends Web3Context(returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat) { + >(returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat) { return rpcMethodsWrappers.getMaxPriorityFeePerGas(this, returnFormat); } @@ -300,7 +301,10 @@ export class Web3Eth extends Web3Context(); + gasPrice = await this.getGasPrice<{ + number: FMT_NUMBER.BIGINT; + bytes: FMT_BYTES.HEX; + }>(); } catch (error) { // do nothing } @@ -361,7 +365,7 @@ export class Web3Eth extends Web3Context( - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.getBlockNumber(this, returnFormat); } @@ -385,7 +389,7 @@ export class Web3Eth extends Web3Context( address: Address, blockNumber: BlockNumberOrTag = this.defaultBlock, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.getBalance(this, address, blockNumber, returnFormat); } @@ -421,9 +425,15 @@ export class Web3Eth extends Web3Context( address: Address, blockNumber: BlockNumberOrTag = this.defaultBlock, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.getCode(this, address, blockNumber, returnFormat); } @@ -527,7 +537,7 @@ export class Web3Eth extends Web3Context( block: HexString32Bytes | BlockNumberOrTag = this.defaultBlock, hydrated = false, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.getBlock(this, block, hydrated, returnFormat); } @@ -552,7 +562,7 @@ export class Web3Eth extends Web3Context( block: HexString32Bytes | BlockNumberOrTag = this.defaultBlock, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.getBlockTransactionCount(this, block, returnFormat); } @@ -575,7 +585,7 @@ export class Web3Eth extends Web3Context( block: HexString32Bytes | BlockNumberOrTag = this.defaultBlock, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.getBlockUncleCount(this, block, returnFormat); } @@ -646,7 +656,7 @@ export class Web3Eth extends Web3Context( block: HexString32Bytes | BlockNumberOrTag = this.defaultBlock, uncleIndex: Numbers, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.getUncle(this, block, uncleIndex, returnFormat); } @@ -701,9 +711,13 @@ export class Web3Eth extends Web3Context( transactionHash: Bytes, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { - const response = await rpcMethodsWrappers.getTransaction(this, transactionHash, returnFormat); + const response = await rpcMethodsWrappers.getTransaction( + this, + transactionHash, + returnFormat, + ); if (!response) throw new TransactionNotFound(); @@ -794,7 +808,7 @@ export class Web3Eth extends Web3Context(returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat) { + >(returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat) { return rpcMethodsWrappers.getPendingTransactions(this, returnFormat); } @@ -853,9 +867,14 @@ export class Web3Eth extends Web3Context( block: HexString32Bytes | BlockNumberOrTag = this.defaultBlock, transactionIndex: Numbers, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { - return rpcMethodsWrappers.getTransactionFromBlock(this, block, transactionIndex, returnFormat); + return rpcMethodsWrappers.getTransactionFromBlock( + this, + block, + transactionIndex, + returnFormat, + ); } /** @@ -904,7 +923,10 @@ export class Web3Eth extends Web3Context(transactionHash: Bytes, returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat) { + >( + transactionHash: Bytes, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, + ) { const response = await rpcMethodsWrappers.getTransactionReceipt( this, transactionHash, @@ -934,10 +956,12 @@ export class Web3Eth extends Web3Context 1 * ``` */ - public async getTransactionCount( + public async getTransactionCount< + ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT, + >( address: Address, blockNumber: BlockNumberOrTag = this.defaultBlock, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.getTransactionCount(this, address, blockNumber, returnFormat); } @@ -1054,7 +1078,7 @@ export class Web3Eth extends Web3Context( transaction: Bytes, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, options?: SendTransactionOptions, ) { return rpcMethodsWrappers.sendSignedTransaction(this, transaction, returnFormat, options); @@ -1182,7 +1206,7 @@ export class Web3Eth extends Web3Context( message: Bytes, addressOrIndex: Address | number, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.sign(this, message, addressOrIndex, returnFormat); } @@ -1240,7 +1264,7 @@ export class Web3Eth extends Web3Context( transaction: Transaction, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.signTransaction(this, transaction, returnFormat); } @@ -1259,7 +1283,7 @@ export class Web3Eth extends Web3Context( transaction: TransactionCall, blockNumber: BlockNumberOrTag = this.defaultBlock, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.call(this, transaction, blockNumber, returnFormat); } @@ -1293,7 +1317,7 @@ export class Web3Eth extends Web3Context( transaction: Transaction, blockNumber: BlockNumberOrTag = this.defaultBlock, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.estimateGas(this, transaction, blockNumber, returnFormat); } @@ -1344,7 +1368,7 @@ export class Web3Eth extends Web3Context( filter: Filter, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.getLogs(this, filter, returnFormat); } @@ -1427,7 +1451,7 @@ export class Web3Eth extends Web3Context( - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.getChainId(this, returnFormat); } @@ -1520,7 +1544,7 @@ export class Web3Eth extends Web3Context( transaction: TransactionForAccessList, blockNumber: BlockNumberOrTag = this.defaultBlock, - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.createAccessList(this, transaction, blockNumber, returnFormat); } @@ -1653,7 +1677,8 @@ export class Web3Eth extends Web3Context( name: T, args?: ConstructorParameters[0], - returnFormat: ReturnType = DEFAULT_RETURN_FORMAT as ReturnType, + returnFormat: ReturnType = (this.defaultReturnFormat ?? + DEFAULT_RETURN_FORMAT) as ReturnType, ): Promise> { const subscription = await this.subscriptionManager?.subscribe(name, args, returnFormat); if ( diff --git a/packages/web3-eth/test/integration/format.test.ts b/packages/web3-eth/test/integration/format.test.ts new file mode 100644 index 00000000000..b9962c065ff --- /dev/null +++ b/packages/web3-eth/test/integration/format.test.ts @@ -0,0 +1,112 @@ +/* +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 { SupportedProviders, FMT_BYTES, FMT_NUMBER } from 'web3-types'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { Contract } from 'web3-eth-contract'; +import { numberToHex } from 'web3-utils'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { Web3Eth } from '../../src'; + +import { + closeOpenConnection, + getSystemTestProvider, + createNewAccount, + createTempAccount, + mapFormatToType, +} from '../fixtures/system_test_utils'; +import { BasicAbi, BasicBytecode } from '../shared_fixtures/build/Basic'; + +describe('format', () => { + let web3Eth: Web3Eth; + let clientUrl: string | SupportedProviders; + let contractDeployed: Contract; + let contract: Contract; + let deployOptions: Record; + let sendOptions: Record; + let tempAcc: { address: string; privateKey: string }; + beforeAll(async () => { + clientUrl = getSystemTestProvider(); + web3Eth = new Web3Eth({ + provider: clientUrl, + config: { + transactionPollingTimeout: 2000, + }, + }); + contract = new Contract(BasicAbi, undefined, { + provider: clientUrl, + }); + + deployOptions = { + data: BasicBytecode, + arguments: [10, 'string init value'], + }; + tempAcc = await createTempAccount(); + sendOptions = { from: tempAcc.address, gas: '1000000' }; + + contractDeployed = await contract.deploy(deployOptions).send(sendOptions); + }); + + afterAll(async () => { + await closeOpenConnection(web3Eth); + await closeOpenConnection(contract); + }); + + describe('methods', () => { + it.each(Object.values(FMT_NUMBER))('getBlockNumber', async format => { + web3Eth.defaultReturnFormat = { number: format as FMT_NUMBER, bytes: FMT_BYTES.HEX }; + const res = await web3Eth.getBlockNumber(); + expect(typeof res).toBe(mapFormatToType[format as string]); + expect(parseInt(String(res), 16)).toBeGreaterThan(0); + }); + + it.each(Object.values(FMT_NUMBER))('getGasPrice', async format => { + web3Eth.defaultReturnFormat = { number: format as FMT_NUMBER, bytes: FMT_BYTES.HEX }; + const res = await web3Eth.getGasPrice(); + expect(typeof res).toBe(mapFormatToType[format as string]); + expect(parseInt(String(res), 16)).toBeGreaterThan(0); + }); + + it.each(Object.values(FMT_NUMBER))('getBalance', async format => { + web3Eth.defaultReturnFormat = { number: format as FMT_NUMBER, bytes: FMT_BYTES.HEX }; + const value = '0xa'; + const newAccount = await createNewAccount(); + await web3Eth.sendTransaction({ + to: newAccount.address, + value, + from: tempAcc.address, + }); + const res = await web3Eth.getBalance(newAccount.address); + expect(typeof res).toBe(mapFormatToType[format as string]); + expect(numberToHex(res)).toBe(value); + }); + + it.each(Object.values(FMT_BYTES))('getCode', async format => { + web3Eth.defaultReturnFormat = { number: FMT_NUMBER.BIGINT, bytes: format }; + const code = await web3Eth.getCode(contractDeployed?.options?.address as string); + expect(code).toBeDefined(); + expect(typeof code).toBe(mapFormatToType[format as string]); + }); + + it.each(Object.values(FMT_NUMBER))('getChainId', async format => { + web3Eth.defaultReturnFormat = { number: format as FMT_NUMBER, bytes: FMT_BYTES.HEX }; + const res = await web3Eth.getChainId(); + expect(typeof res).toBe(mapFormatToType[format as string]); + expect(Number(res)).toBeGreaterThan(0); + }); + }); +}); diff --git a/packages/web3-eth/test/integration/helper.ts b/packages/web3-eth/test/integration/helper.ts index 46063294702..4a12b288055 100644 --- a/packages/web3-eth/test/integration/helper.ts +++ b/packages/web3-eth/test/integration/helper.ts @@ -14,13 +14,7 @@ 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 { - AbiEventFragment, - Block, - TransactionInfo, - TransactionReceipt, - FMT_NUMBER, -} from 'web3-types'; +import { AbiEventFragment, Block, TransactionInfo, TransactionReceipt } from 'web3-types'; // eslint-disable-next-line import/no-extraneous-dependencies import Web3 from 'web3'; import { BasicAbi } from '../shared_fixtures/build/Basic'; @@ -130,12 +124,6 @@ export const validateReceipt = (r: TransactionReceipt) => { expect(Number(r.gasUsed)).toBeGreaterThan(0); }; -export const mapFormatToType: { [key: string]: string } = { - [FMT_NUMBER.NUMBER]: 'number', - [FMT_NUMBER.HEX]: 'string', - [FMT_NUMBER.STR]: 'string', - [FMT_NUMBER.BIGINT]: 'bigint', -}; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion export const eventAbi: AbiEventFragment = BasicAbi.find((e: any) => { return e.name === 'StringEvent' && (e as AbiEventFragment).type === 'event'; diff --git a/packages/web3-eth/test/integration/rpc.test.ts b/packages/web3-eth/test/integration/rpc.test.ts index d9ca6e5676b..eba6792a2f8 100644 --- a/packages/web3-eth/test/integration/rpc.test.ts +++ b/packages/web3-eth/test/integration/rpc.test.ts @@ -26,7 +26,6 @@ import { // eslint-disable-next-line import/no-extraneous-dependencies import { Contract, decodeEventABI } from 'web3-eth-contract'; import { hexToNumber, hexToString, numberToHex, getStorageSlotNumForLongString } from 'web3-utils'; -// eslint-disable-next-line import/no-extraneous-dependencies import { Web3Eth } from '../../src'; import { @@ -37,16 +36,11 @@ import { itIf, createTempAccount, describeIf, - BACKEND + mapFormatToType, + BACKEND, } from '../fixtures/system_test_utils'; import { BasicAbi, BasicBytecode } from '../shared_fixtures/build/Basic'; -import { - eventAbi, - mapFormatToType, - sendFewTxes, - validateReceipt, - validateTransaction, -} from './helper'; +import { eventAbi, sendFewTxes, validateReceipt, validateTransaction } from './helper'; describe('rpc', () => { let web3Eth: Web3Eth; @@ -86,11 +80,14 @@ describe('rpc', () => { }); describe('methods', () => { - itIf(!['geth', 'hardhat'].includes(getSystemTestBackend()))('getProtocolVersion', async () => { - const version = await web3Eth.getProtocolVersion(); - // eslint-disable-next-line jest/no-standalone-expect - expect(parseInt(version, 16)).toBeGreaterThan(0); - }); + itIf(!['geth', 'hardhat'].includes(getSystemTestBackend()))( + 'getProtocolVersion', + async () => { + const version = await web3Eth.getProtocolVersion(); + // eslint-disable-next-line jest/no-standalone-expect + expect(parseInt(version, 16)).toBeGreaterThan(0); + }, + ); // TODO:in beta, test eth_syncing during sync mode with return obj having ( startingblock, currentBlock, heighestBlock ) it('isSyncing', async () => { @@ -113,20 +110,20 @@ describe('rpc', () => { expect(isMining).toBe(true); }); - describeIf(getSystemTestBackend() !== BACKEND.HARDHAT)('getHashRate', () => { + describeIf(getSystemTestBackend() !== BACKEND.HARDHAT)('getHashRate', () => { it.each(Object.values(FMT_NUMBER))('getHashRate', async format => { const hashRate = await web3Eth.getHashRate({ number: format as FMT_NUMBER, bytes: FMT_BYTES.HEX, }); // eslint-disable-next-line jest/no-standalone-expect - expect(typeof hashRate).toBe(mapFormatToType[format as string]); + expect(typeof hashRate).toBe(mapFormatToType[format as string]); }); - }) + }); it('getAccounts', async () => { // hardhat does not have support importrawkey, so we can't add new accounts rather just check the default 20 accounts - if (getSystemTestBackend() !== BACKEND.HARDHAT) { + if (getSystemTestBackend() !== BACKEND.HARDHAT) { const account = await createNewAccount({ unlock: true }); const accList = await web3Eth.getAccounts(); const accListLowerCase = accList.map((add: string) => add.toLowerCase()); diff --git a/packages/web3-eth/test/integration/web3_eth/send_transaction.test.ts b/packages/web3-eth/test/integration/web3_eth/send_transaction.test.ts index 9018381bd6a..9b0eda360dc 100644 --- a/packages/web3-eth/test/integration/web3_eth/send_transaction.test.ts +++ b/packages/web3-eth/test/integration/web3_eth/send_transaction.test.ts @@ -90,7 +90,9 @@ describe('Web3Eth.sendTransaction', () => { expect(response.status).toBe(BigInt(1)); expect(response.events).toBeUndefined(); - const minedTransactionData = await web3EthWithWallet.getTransaction(response.transactionHash); + const minedTransactionData = await web3EthWithWallet.getTransaction( + response.transactionHash, + ); expect(minedTransactionData).toMatchObject({ from: tempAcc.address, @@ -119,7 +121,9 @@ describe('Web3Eth.sendTransaction', () => { expect(response.status).toBe(BigInt(1)); expect(response.events).toBeUndefined(); - const minedTransactionData = await web3EthWithWallet.getTransaction(response.transactionHash); + const minedTransactionData = await web3EthWithWallet.getTransaction( + response.transactionHash, + ); expect(minedTransactionData).toMatchObject({ from: tempAcc.address, @@ -152,7 +156,9 @@ describe('Web3Eth.sendTransaction', () => { expect(response.status).toBe(BigInt(1)); expect(response.events).toBeUndefined(); - const minedTransactionData = await web3EthWithWallet.getTransaction(response.transactionHash); + const minedTransactionData = await web3EthWithWallet.getTransaction( + response.transactionHash, + ); expect(minedTransactionData).toMatchObject({ from: tempAcc.address, @@ -534,9 +540,12 @@ describe('Web3Eth.sendTransaction', () => { from: tempAcc.address, data: SimpleRevertDeploymentData, }; - simpleRevertDeployTransaction.gas = await web3Eth.estimateGas(simpleRevertDeployTransaction); - simpleRevertContractAddress = (await web3Eth.sendTransaction(simpleRevertDeployTransaction)) - .contractAddress as Address; + simpleRevertDeployTransaction.gas = await web3Eth.estimateGas( + simpleRevertDeployTransaction, + ); + simpleRevertContractAddress = ( + await web3Eth.sendTransaction(simpleRevertDeployTransaction) + ).contractAddress as Address; }); it('Should throw TransactionRevertInstructionError because gas too low', async () => { @@ -578,7 +587,9 @@ describe('Web3Eth.sendTransaction', () => { const transaction: Transaction = { from: tempAcc.address, to: '0x0000000000000000000000000000000000000000', - value: BigInt('99999999999999999999999999999999999999999999999999999999999999999'), + value: BigInt( + '99999999999999999999999999999999999999999999999999999999999999999', + ), }; const expectedThrownError = { @@ -587,7 +598,9 @@ describe('Web3Eth.sendTransaction', () => { code: 402, reason: getSystemTestBackend() === BACKEND.GETH - ? expect.stringContaining('err: insufficient funds for gas * price + value: address') + ? expect.stringContaining( + 'err: insufficient funds for gas * price + value: address', + ) : 'VM Exception while processing transaction: insufficient balance', }; diff --git a/packages/web3-eth/test/unit/rpc_method_wrappers/send_signed_transaction.test.ts b/packages/web3-eth/test/unit/rpc_method_wrappers/send_signed_transaction.test.ts index 4a741c46646..c5a461c7372 100644 --- a/packages/web3-eth/test/unit/rpc_method_wrappers/send_signed_transaction.test.ts +++ b/packages/web3-eth/test/unit/rpc_method_wrappers/send_signed_transaction.test.ts @@ -53,7 +53,7 @@ describe('sendTransaction', () => { WaitForTransactionReceipt.waitForTransactionReceipt as jest.Mock ).mockResolvedValueOnce(expectedTransactionReceipt); - const checkRevertBeforeSendingSpy = jest.fn().mockImplementation((transaction) => { + const checkRevertBeforeSendingSpy = jest.fn().mockImplementation(transaction => { expect(transaction).toBeDefined(); // verify signature part is removed before sending to revert check function @@ -78,7 +78,6 @@ describe('sendTransaction', () => { ); expect(checkRevertBeforeSendingSpy).toHaveBeenCalledTimes(1); - }, ); @@ -300,6 +299,7 @@ describe('sendTransaction', () => { formattedTransactionReceipt, expectedTransactionHash, DEFAULT_RETURN_FORMAT, + undefined, ); }, ); diff --git a/packages/web3-eth/test/unit/rpc_method_wrappers/send_transaction.test.ts b/packages/web3-eth/test/unit/rpc_method_wrappers/send_transaction.test.ts index 3be8182cd33..e9b60f97e74 100644 --- a/packages/web3-eth/test/unit/rpc_method_wrappers/send_transaction.test.ts +++ b/packages/web3-eth/test/unit/rpc_method_wrappers/send_transaction.test.ts @@ -299,6 +299,7 @@ describe('sendTransaction', () => { formattedTransactionReceipt, expectedTransactionHash, DEFAULT_RETURN_FORMAT, + undefined, ); }, ); diff --git a/packages/web3-eth/test/unit/send_tx_helper.test.ts b/packages/web3-eth/test/unit/send_tx_helper.test.ts index 55a482395b8..6b40496f5a6 100644 --- a/packages/web3-eth/test/unit/send_tx_helper.test.ts +++ b/packages/web3-eth/test/unit/send_tx_helper.test.ts @@ -21,6 +21,7 @@ import { JsonRpcResponse, TransactionReceipt, Web3BaseWalletAccount, + TransactionCall } from 'web3-types'; import { Web3Context, Web3EventMap, Web3PromiEvent } from 'web3-core'; import { @@ -151,6 +152,28 @@ describe('sendTxHelper class', () => { expect(f).toHaveBeenCalledWith(error); promiEvent.off('error', f); }); + + it('add gas to simple transaction in checkRevertBeforeSending', async () => { + const _sendTxHelper = new SendTxHelper({ + web3Context, + promiEvent: promiEvent as PromiEvent, + options: { + checkRevertBeforeSending: true, + }, + returnFormat: DEFAULT_RETURN_FORMAT, + }); + + const tx = {from:"0x"} as TransactionCall + + await _sendTxHelper.checkRevertBeforeSending(tx); + + const expectedTx = { + ...tx, + gas: 21000, + }; + expect(utils.getRevertReason).toHaveBeenCalledWith(web3Context, expectedTx, undefined); + + }); it('emit handleError with handleRevert', async () => { const error = new ContractExecutionError({ code: 1, message: 'error' }); web3Context.handleRevert = true; diff --git a/packages/web3-eth/test/unit/utils/get_transaction_gas_pricing.test.ts b/packages/web3-eth/test/unit/utils/get_transaction_gas_pricing.test.ts new file mode 100644 index 00000000000..30d3e5beaf9 --- /dev/null +++ b/packages/web3-eth/test/unit/utils/get_transaction_gas_pricing.test.ts @@ -0,0 +1,58 @@ +/* +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 } from 'web3-core'; + +import { FMT_BYTES, FMT_NUMBER } from 'web3-types'; +import * as RpcMethodWrappers from '../../../src/rpc_method_wrappers'; +import { getTransactionGasPricing } from '../../../src/utils/get_transaction_gas_pricing'; + +describe('getTransactionGasPricing', () => { + const web3Context = new Web3Context(); + + it('should use the call rpc wrapper', async () => { + const gasPrice = '0x123456'; + const gasPriceSpy = jest + .spyOn(RpcMethodWrappers, 'getGasPrice') + .mockImplementation(async () => gasPrice); + const getBlockSpy = jest + .spyOn(RpcMethodWrappers, 'getBlock') + // @ts-expect-error only for testing purposes + .mockImplementation(async () => ({ + baseFeePerGas: '0x0', + })); + + const transaction = { + from: '0x4fec0a51024b13030d26e70904b066c6d41157a5', + to: '0x36361143b7e2c676f8ccd67743a89d26437f0529', + data: '0x819f48fe', + type: '0x2', + }; + + const res = await getTransactionGasPricing(transaction, web3Context, { + number: FMT_NUMBER.HEX, + bytes: FMT_BYTES.HEX, + }); + + expect(getBlockSpy).toHaveBeenCalled(); + expect(gasPriceSpy).toHaveBeenCalled(); + expect(res).toEqual({ + gasPrice: undefined, + maxPriorityFeePerGas: gasPrice, + maxFeePerGas: gasPrice, + }); + }); +}); diff --git a/packages/web3-net/CHANGELOG.md b/packages/web3-net/CHANGELOG.md index dd6227ecdd8..5e7ea9073b3 100644 --- a/packages/web3-net/CHANGELOG.md +++ b/packages/web3-net/CHANGELOG.md @@ -141,4 +141,10 @@ Documentation: - Dependencies updated +## [4.1.0] + +### Added + +- `defaultReturnFormat` was added to all methods that have `ReturnType` param. (#6947) + ## [Unreleased] \ No newline at end of file diff --git a/packages/web3-net/package.json b/packages/web3-net/package.json index 1397aa44c7a..952457f72be 100644 --- a/packages/web3-net/package.json +++ b/packages/web3-net/package.json @@ -1,6 +1,6 @@ { "name": "web3-net", - "version": "4.0.7", + "version": "4.1.0", "description": "Web3 module to interact with the Ethereum nodes networking properties.", "main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", @@ -56,9 +56,9 @@ "typescript": "^4.7.4" }, "dependencies": { - "web3-core": "^4.3.0", - "web3-rpc-methods": "^1.1.3", - "web3-types": "^1.3.0", - "web3-utils": "^4.0.7" + "web3-core": "^4.4.0", + "web3-rpc-methods": "^1.3.0", + "web3-types": "^1.6.0", + "web3-utils": "^4.3.0" } } diff --git a/packages/web3-net/src/net.ts b/packages/web3-net/src/net.ts index eb84e567bb5..ac376e86c5a 100644 --- a/packages/web3-net/src/net.ts +++ b/packages/web3-net/src/net.ts @@ -21,24 +21,24 @@ import * as rpcMethodsWrappers from './rpc_method_wrappers.js'; /** * Net class allows you to interact with an Ethereum node’s network properties. -* For using Net package, first install Web3 package using: `npm i web3` or `yarn add web3` based on your package manager, after that Net features can be used. -* ```ts -* -* import { Web3 } from 'web3'; -* const web3 = new Web3('https://mainnet.infura.io/v3/'); -* -* console.log(await web3.eth.net.getId()); -* -* ``` -* For using individual package install `web3-net` packages using: `npm i web3-net` or `yarn add web3-net`. -* -* ```ts -* import {Net} from 'web3-net'; -* -* const net = new Net('https://mainnet.infura.io/v3/'); -* console.log(await net.getId()); -* ``` -*/ + * For using Net package, first install Web3 package using: `npm i web3` or `yarn add web3` based on your package manager, after that Net features can be used. + * ```ts + * + * import { Web3 } from 'web3'; + * const web3 = new Web3('https://mainnet.infura.io/v3/'); + * + * console.log(await web3.eth.net.getId()); + * + * ``` + * For using individual package install `web3-net` packages using: `npm i web3-net` or `yarn add web3-net`. + * + * ```ts + * import {Net} from 'web3-net'; + * + * const net = new Net('https://mainnet.infura.io/v3/'); + * console.log(await net.getId()); + * ``` + */ export class Net extends Web3Context { /** * Gets the current network ID @@ -53,7 +53,7 @@ export class Net extends Web3Context { * ``` */ public async getId( - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.getId(this, returnFormat); } @@ -71,7 +71,7 @@ export class Net extends Web3Context { * ``` */ public async getPeerCount( - returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, ) { return rpcMethodsWrappers.getPeerCount(this, returnFormat); } diff --git a/packages/web3-providers-http/package.json b/packages/web3-providers-http/package.json index f8b1eed8821..75cf3529945 100644 --- a/packages/web3-providers-http/package.json +++ b/packages/web3-providers-http/package.json @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", diff --git a/packages/web3-providers-ipc/package.json b/packages/web3-providers-ipc/package.json index e9a6db2d25b..395156d6598 100644 --- a/packages/web3-providers-ipc/package.json +++ b/packages/web3-providers-ipc/package.json @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", diff --git a/packages/web3-providers-ws/package.json b/packages/web3-providers-ws/package.json index 2df7997c08c..b3612afaa23 100644 --- a/packages/web3-providers-ws/package.json +++ b/packages/web3-providers-ws/package.json @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", diff --git a/packages/web3-rpc-methods/CHANGELOG.md b/packages/web3-rpc-methods/CHANGELOG.md index c2385435cac..050b6451539 100644 --- a/packages/web3-rpc-methods/CHANGELOG.md +++ b/packages/web3-rpc-methods/CHANGELOG.md @@ -138,4 +138,10 @@ Documentation: - Added `getMaxPriorityFeePerGas` method (#6748) +## [1.3.0] + +### Changed + +- Change `estimateGas` method to add possibility pass Transaction type (#7000) + ## [Unreleased] \ No newline at end of file diff --git a/packages/web3-rpc-methods/package.json b/packages/web3-rpc-methods/package.json index 9a6605d1939..3a05060f2c2 100644 --- a/packages/web3-rpc-methods/package.json +++ b/packages/web3-rpc-methods/package.json @@ -1,6 +1,6 @@ { "name": "web3-rpc-methods", - "version": "1.2.0", + "version": "1.3.0", "description": "Ethereum RPC methods for Web3 4.x.x", "main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", @@ -56,8 +56,8 @@ "typescript": "^4.7.4" }, "dependencies": { - "web3-core": "^4.3.2", - "web3-types": "^1.5.0", - "web3-validator": "^2.0.4" + "web3-core": "^4.4.0", + "web3-types": "^1.6.0", + "web3-validator": "^2.0.6" } } diff --git a/packages/web3-rpc-methods/src/eth_rpc_methods.ts b/packages/web3-rpc-methods/src/eth_rpc_methods.ts index b33857d7d80..f86240ddccb 100644 --- a/packages/web3-rpc-methods/src/eth_rpc_methods.ts +++ b/packages/web3-rpc-methods/src/eth_rpc_methods.ts @@ -264,9 +264,9 @@ export async function call( } // TODO Not sure how to best validate Partial -export async function estimateGas( +export async function estimateGas( requestManager: Web3RequestManager, - transaction: Partial, + transaction: Partial, blockNumber: BlockNumberOrTag, ) { validator.validate(['blockNumberOrTag'], [blockNumber]); diff --git a/packages/web3-types/CHANGELOG.md b/packages/web3-types/CHANGELOG.md index 7d1e3042b96..638d9616377 100644 --- a/packages/web3-types/CHANGELOG.md +++ b/packages/web3-types/CHANGELOG.md @@ -189,8 +189,9 @@ Documentation: - Type `FeeData` to be filled by `await web3.eth.calculateFeeData()` to be used with EIP-1559 transactions (#6795) -## [Unreleased] +## [1.6.0] ### Added - Added `signature` to type `AbiFunctionFragment` (#6922) +- update type `Withdrawals`, `block` and `BlockHeaderOutput` to include properties of eip 4844, 4895, 4788 (#6933) diff --git a/packages/web3-types/package.json b/packages/web3-types/package.json index a254eb19f19..8cec2cee0b0 100644 --- a/packages/web3-types/package.json +++ b/packages/web3-types/package.json @@ -1,6 +1,6 @@ { "name": "web3-types", - "version": "1.5.0", + "version": "1.6.0", "description": "Provide the common data structures and interfaces for web3 modules.", "main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", @@ -30,7 +30,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", diff --git a/packages/web3-types/src/data_format_types.ts b/packages/web3-types/src/data_format_types.ts index f0ffe3bc8aa..46c1f5352b8 100644 --- a/packages/web3-types/src/data_format_types.ts +++ b/packages/web3-types/src/data_format_types.ts @@ -46,7 +46,10 @@ export type DataFormat = { readonly bytes: FMT_BYTES; }; -export const DEFAULT_RETURN_FORMAT = { number: FMT_NUMBER.BIGINT, bytes: FMT_BYTES.HEX } as const; +export const DEFAULT_RETURN_FORMAT = { + number: FMT_NUMBER.BIGINT, + bytes: FMT_BYTES.HEX, +} as const; export const ETH_DATA_FORMAT = { number: FMT_NUMBER.HEX, bytes: FMT_BYTES.HEX } as const; export type FormatType = number extends Extract diff --git a/packages/web3-types/src/eth_contract_types.ts b/packages/web3-types/src/eth_contract_types.ts index fb34328ce21..00eba77e5ed 100644 --- a/packages/web3-types/src/eth_contract_types.ts +++ b/packages/web3-types/src/eth_contract_types.ts @@ -114,7 +114,7 @@ export interface ContractOptions { 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. + * The {@doclink glossary#json-interface-abi | 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. * diff --git a/packages/web3-utils/CHANGELOG.md b/packages/web3-utils/CHANGELOG.md index c0ab0335bed..5fcf44ccec5 100644 --- a/packages/web3-utils/CHANGELOG.md +++ b/packages/web3-utils/CHANGELOG.md @@ -174,7 +174,7 @@ Documentation: ### Added - `SocketProvider` now contains public function `getPendingRequestQueueSize`, `getSentRequestsQueueSize` and `clearQueues` (#6479) -- Added `safeDisconnect` as a `SocketProvider` method to disconnect only when request queue size and send request queue size is 0 (#6479) +- Added `safeDisconnect` as a `SocketProvider` method to disconnect only when request queue size and send request queue size is 0 (#6479) - Add `isContractInitOptions` method (#6555) ### Fixed @@ -203,11 +203,29 @@ Documentation: ### Fixed -- fixed erroneous parsing of big numbers in the `toNumber(...)` function (#6880) +- fixed erroneous parsing of big numbers in the `toNumber(...)` function (#6880) -## [Unreleased] +## [4.2.3] + +### Changed + +- Method `format` was changed. Now it has default value `DEFAULT_RETURN_FORMAT` for `returnFormat` parameter (#6947) ### Fixed - fixed toHex incorrectly hexing Uint8Arrays and Buffer (#6957) -- fixed isUint8Array not returning true for Buffer \ No newline at end of file +- fixed isUint8Array not returning true for Buffer (#6957) + +## [4.3.0] + +### Added + +- `toWei` add warning when using large numbers or large decimals that may cause precision loss (#6908) +- `toWei` and `fromWei` now supports integers as a unit. (#7053) + +### Fixed + +- `toWei` support numbers in scientific notation (#6908) +- `toWei` and `fromWei` trims according to ether unit successfuly (#7044) + +## [Unreleased] \ No newline at end of file diff --git a/packages/web3-utils/package.json b/packages/web3-utils/package.json index 33be7953d0f..c07e9b85821 100644 --- a/packages/web3-utils/package.json +++ b/packages/web3-utils/package.json @@ -1,7 +1,7 @@ { "name": "web3-utils", "sideEffects": false, - "version": "4.2.2", + "version": "4.3.0", "description": "Collection of utility functions used in web3.js.", "main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", @@ -31,7 +31,7 @@ "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", @@ -65,8 +65,8 @@ "dependencies": { "ethereum-cryptography": "^2.0.0", "eventemitter3": "^5.0.1", - "web3-errors": "^1.1.4", - "web3-types": "^1.5.0", - "web3-validator": "^2.0.5" + "web3-errors": "^1.2.0", + "web3-types": "^1.6.0", + "web3-validator": "^2.0.6" } } diff --git a/packages/web3-utils/src/converters.ts b/packages/web3-utils/src/converters.ts index a8aef7699e7..6649bef1481 100644 --- a/packages/web3-utils/src/converters.ts +++ b/packages/web3-utils/src/converters.ts @@ -32,6 +32,7 @@ import { utils, utils as validatorUtils, validator, + bigintPower, } from 'web3-validator'; import { @@ -41,6 +42,7 @@ import { InvalidBytesError, InvalidNumberError, InvalidUnitError, + InvalidIntegerError, } from 'web3-errors'; import { isUint8Array } from './uint8array.js'; @@ -77,6 +79,8 @@ export const ethUnitMap = { tether: BigInt('1000000000000000000000000000000'), }; +const PrecisionLossWarning = 'Warning: Using type `number` with values that are large or contain many decimals may cause loss of precision, it is recommended to use type `string` or `BigInt` when using conversion methods'; + export type EtherUnits = keyof typeof ethUnitMap; /** * Convert a value from bytes to Uint8Array @@ -415,7 +419,8 @@ export const toHex = ( */ export const toNumber = (value: Numbers): number | bigint => { if (typeof value === 'number') { - if (value > 1e+20) { + if (value > 1e+20) { + console.warn(PrecisionLossWarning) // JavaScript converts numbers >= 10^21 to scientific notation when coerced to strings, // leading to potential parsing errors and incorrect representations. // For instance, String(10000000000000000000000) yields '1e+22'. @@ -489,13 +494,22 @@ export const toBigInt = (value: unknown): bigint => { * > 0.000000001 * ``` */ -export const fromWei = (number: Numbers, unit: EtherUnits): string => { - const denomination = ethUnitMap[unit]; +export const fromWei = (number: Numbers, unit: EtherUnits | number): string => { + let denomination; + if (typeof unit === 'string') { + denomination = ethUnitMap[unit]; - if (!denomination) { - throw new InvalidUnitError(unit); + if (!denomination) { + throw new InvalidUnitError(unit); + } + } else { + if (unit < 0 || !Number.isInteger(unit)) { + throw new InvalidIntegerError(unit); + } + denomination = bigintPower(BigInt(10),BigInt(unit)); } + // value in wei would always be integer // 13456789, 1234 const value = String(toNumber(number)); @@ -529,8 +543,9 @@ export const fromWei = (number: Numbers, unit: EtherUnits): string => { if (fraction === '') { return integer; } + const updatedValue = `${integer}.${fraction}`; - return `${integer}.${fraction}`; + return updatedValue.slice(0, integer.length + numberOfZerosInDenomination + 1); }; /** @@ -547,19 +562,47 @@ export const fromWei = (number: Numbers, unit: EtherUnits): string => { * ``` */ // todo in 1.x unit defaults to 'ether' -export const toWei = (number: Numbers, unit: EtherUnits): string => { +export const toWei = (number: Numbers, unit: EtherUnits | number): string => { validator.validate(['number'], [number]); - const denomination = ethUnitMap[unit]; + let denomination; + if (typeof unit === 'string') { + denomination = ethUnitMap[unit]; + if (!denomination) { + throw new InvalidUnitError(unit); + } + } else { + if (unit < 0 || !Number.isInteger(unit)) { + throw new InvalidIntegerError(unit); + } + + denomination = bigintPower(BigInt(10),BigInt(unit)); + } - if (!denomination) { - throw new InvalidUnitError(unit); + let parsedNumber = number; + if (typeof parsedNumber === 'number') { + if (parsedNumber < 1e-15) { + console.warn(PrecisionLossWarning); + } + if (parsedNumber > 1e20) { + console.warn(PrecisionLossWarning); + + parsedNumber = BigInt(parsedNumber); + } else { + // in case there is a decimal point, we need to convert it to string + parsedNumber = parsedNumber.toLocaleString('fullwide', { + useGrouping: false, + maximumFractionDigits: 20, + }); + } } // if value is decimal e.g. 24.56 extract `integer` and `fraction` part // to avoid `fraction` to be null use `concat` with empty string const [integer, fraction] = String( - typeof number === 'string' && !isHexStrict(number) ? number : toNumber(number), + typeof parsedNumber === 'string' && !isHexStrict(parsedNumber) + ? parsedNumber + : toNumber(parsedNumber), ) .split('.') .concat(''); @@ -572,19 +615,14 @@ export const toWei = (number: Numbers, unit: EtherUnits): string => { // 2456 * 1000000 -> 2456000000 const updatedValue = value * denomination; - // count number of zeros in denomination - const numberOfZerosInDenomination = denomination.toString().length - 1; - - // check which either `fraction` or `denomination` have lower number of zeros - const decimals = Math.min(fraction.length, numberOfZerosInDenomination); - + // check if whole number was passed in + const decimals = fraction.length; if (decimals === 0) { return updatedValue.toString(); } - // Add zeros to make length equal to required decimal points - // If string is larger than decimal points required then remove last zeros - return updatedValue.toString().padStart(decimals, '0').slice(0, -decimals); + // trim the value to remove extra zeros + return updatedValue.toString().slice(0, -decimals); }; /** diff --git a/packages/web3-utils/src/formatter.ts b/packages/web3-utils/src/formatter.ts index 1901e1fde21..42062add5c6 100644 --- a/packages/web3-utils/src/formatter.ts +++ b/packages/web3-utils/src/formatter.ts @@ -15,7 +15,14 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ import { FormatterError } from 'web3-errors'; -import { Bytes, DataFormat, FMT_BYTES, FMT_NUMBER, FormatType } from 'web3-types'; +import { + Bytes, + DataFormat, + FMT_BYTES, + FMT_NUMBER, + FormatType, + DEFAULT_RETURN_FORMAT, +} from 'web3-types'; import { isNullish, isObject, JsonSchema, utils, ValidationSchemaInput } from 'web3-validator'; import { bytesToUint8Array, bytesToHex, numberToHex, toBigInt } from './converters.js'; import { mergeDeep } from './objects.js'; @@ -138,6 +145,115 @@ export const convertScalarValue = (value: unknown, ethType: string, format: Data return value; }; + +const convertArray = ({ + value, + schemaProp, + schema, + object, + key, + dataPath, + format, + oneOfPath = [], +}: { + value: unknown; + schemaProp: JsonSchema; + schema: JsonSchema; + object: Record; + key: string; + dataPath: string[]; + format: DataFormat; + oneOfPath: [string, number][]; +}) => { + // If value is an array + if (Array.isArray(value)) { + let _schemaProp = schemaProp; + + // TODO This is a naive approach to solving the issue of + // a schema using oneOf. This chunk of code was intended to handle + // BlockSchema.transactions + // TODO BlockSchema.transactions are not being formatted + if (schemaProp?.oneOf !== undefined) { + // The following code is basically saying: + // if the schema specifies oneOf, then we are to loop + // over each possible schema and check if they type of the schema + // matches the type of value[0], and if so we use the oneOfSchemaProp + // as the schema for formatting + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + schemaProp.oneOf.forEach((oneOfSchemaProp: JsonSchema, index: number) => { + if ( + !Array.isArray(schemaProp?.items) && + ((typeof value[0] === 'object' && + (oneOfSchemaProp?.items as JsonSchema)?.type === 'object') || + (typeof value[0] === 'string' && + (oneOfSchemaProp?.items as JsonSchema)?.type !== 'object')) + ) { + _schemaProp = oneOfSchemaProp; + oneOfPath.push([key, index]); + } + }); + } + + if (isNullish(_schemaProp?.items)) { + // Can not find schema for array item, delete that item + // eslint-disable-next-line no-param-reassign + delete object[key]; + dataPath.pop(); + + return true; + } + + // If schema for array items is a single type + if (isObject(_schemaProp.items) && !isNullish(_schemaProp.items.format)) { + for (let i = 0; i < value.length; i += 1) { + // eslint-disable-next-line no-param-reassign + (object[key] as unknown[])[i] = convertScalarValue( + value[i], + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + _schemaProp?.items?.format, + format, + ); + } + + dataPath.pop(); + return true; + } + + // If schema for array items is an object + if (!Array.isArray(_schemaProp?.items) && _schemaProp?.items?.type === 'object') { + for (const arrObject of value) { + // eslint-disable-next-line no-use-before-define + convert( + arrObject as Record | unknown[], + schema, + dataPath, + format, + oneOfPath, + ); + } + + dataPath.pop(); + return true; + } + + // If schema for array is a tuple + if (Array.isArray(_schemaProp?.items)) { + for (let i = 0; i < value.length; i += 1) { + // eslint-disable-next-line no-param-reassign + (object[key] as unknown[])[i] = convertScalarValue( + value[i], + _schemaProp.items[i].format as string, + format, + ); + } + + dataPath.pop(); + return true; + } + } + return false; +}; + /** * Converts the data to the specified format * @param data - data to convert @@ -160,112 +276,62 @@ export const convert = ( } const object = data as Record; + // case when schema is array and `items` is object + if ( + Array.isArray(object) && + schema?.type === 'array' && + (schema?.items as JsonSchema)?.type === 'object' + ) { + convertArray({ + value: object, + schemaProp: schema, + schema, + object, + key: '', + dataPath, + format, + oneOfPath, + }); + } else { + for (const [key, value] of Object.entries(object)) { + dataPath.push(key); + const schemaProp = findSchemaByDataPath(schema, dataPath, oneOfPath); - for (const [key, value] of Object.entries(object)) { - dataPath.push(key); - const schemaProp = findSchemaByDataPath(schema, dataPath, oneOfPath); - - // If value is a scaler value - if (isNullish(schemaProp)) { - delete object[key]; - dataPath.pop(); - - continue; - } - - // If value is an object, recurse into it - if (isObject(value)) { - convert(value, schema, dataPath, format); - dataPath.pop(); - continue; - } - - // If value is an array - if (Array.isArray(value)) { - let _schemaProp = schemaProp; - - // TODO This is a naive approach to solving the issue of - // a schema using oneOf. This chunk of code was intended to handle - // BlockSchema.transactions - // TODO BlockSchema.transactions are not being formatted - if (schemaProp?.oneOf !== undefined) { - // The following code is basically saying: - // if the schema specifies oneOf, then we are to loop - // over each possible schema and check if they type of the schema - // matches the type of value[0], and if so we use the oneOfSchemaProp - // as the schema for formatting - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call - schemaProp.oneOf.forEach((oneOfSchemaProp: JsonSchema, index: number) => { - if ( - !Array.isArray(schemaProp?.items) && - ((typeof value[0] === 'object' && - (oneOfSchemaProp?.items as JsonSchema)?.type === 'object') || - (typeof value[0] === 'string' && - (oneOfSchemaProp?.items as JsonSchema)?.type !== 'object')) - ) { - _schemaProp = oneOfSchemaProp; - oneOfPath.push([key, index]); - } - }); - } - - if (isNullish(_schemaProp?.items)) { - // Can not find schema for array item, delete that item + // If value is a scaler value + if (isNullish(schemaProp)) { delete object[key]; dataPath.pop(); continue; } - // If schema for array items is a single type - if (isObject(_schemaProp.items) && !isNullish(_schemaProp.items.format)) { - for (let i = 0; i < value.length; i += 1) { - (object[key] as unknown[])[i] = convertScalarValue( - value[i], - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - _schemaProp?.items?.format, - format, - ); - } - + // If value is an object, recurse into it + if (isObject(value)) { + convert(value, schema, dataPath, format); dataPath.pop(); continue; } - // If schema for array items is an object - if (!Array.isArray(_schemaProp?.items) && _schemaProp?.items?.type === 'object') { - for (const arrObject of value) { - convert( - arrObject as Record | unknown[], - schema, - dataPath, - format, - oneOfPath, - ); - } - - dataPath.pop(); + // If value is an array + if ( + convertArray({ + value, + schemaProp, + schema, + object, + key, + dataPath, + format, + oneOfPath, + }) + ) { continue; } - // If schema for array is a tuple - if (Array.isArray(_schemaProp?.items)) { - for (let i = 0; i < value.length; i += 1) { - (object[key] as unknown[])[i] = convertScalarValue( - value[i], - _schemaProp.items[i].format as string, - format, - ); - } + object[key] = convertScalarValue(value, schemaProp.format as string, format); - dataPath.pop(); - continue; - } + dataPath.pop(); } - - object[key] = convertScalarValue(value, schemaProp.format as string, format); - - dataPath.pop(); } return object; @@ -277,7 +343,7 @@ export const format = < >( schema: ValidationSchemaInput | JsonSchema, data: DataType, - returnFormat: ReturnType, + returnFormat: ReturnType = DEFAULT_RETURN_FORMAT as ReturnType, ): FormatType => { let dataToParse: Record | unknown[] | unknown; diff --git a/packages/web3-utils/test/fixtures/converters.ts b/packages/web3-utils/test/fixtures/converters.ts index 8cf2c3d9e8d..9f0324b68ad 100644 --- a/packages/web3-utils/test/fixtures/converters.ts +++ b/packages/web3-utils/test/fixtures/converters.ts @@ -256,7 +256,7 @@ export const toHexInvalidData: [any, string][] = [ [undefined, 'Invalid value given "undefined". Error: can not be converted to hex.'], ]; -const conversionBaseData: [[Numbers, EtherUnits], string][] = [ +const conversionBaseData: [[Numbers, EtherUnits | number], string][] = [ [[0, 'wei'], '0'], [[123, 'wei'], '123'], [['123', 'wei'], '123'], @@ -290,19 +290,66 @@ const conversionBaseData: [[Numbers, EtherUnits], string][] = [ [['178373938391829348', 'ether'], '0.178373938391829348'], [['879123456788877661', 'gwei'], '879123456.788877661'], [['879123456788877661', 'tether'], '0.000000000000879123456788877661'], + [['1', 0], '1'], + [['1', 1], '0.1'], + [['1', 2], '0.01'], + [['1', 3], '0.001'], + [['1', 4], '0.0001'], + [['1', 5], '0.00001'], + [['1', 6], '0.000001'], + [['1', 7], '0.0000001'], + [['1', 8], '0.00000001'], + [['1', 9], '0.000000001'], + [['1', 10], '0.0000000001'], + [[1, 18], '0.000000000000000001'], + [[100, 2], '1'], + [['100', 2], '1'], + [['1000', 3], '1'], + [['10000', 4], '1'], + [['100000', 5], '1'], + [['1000000', 6], '1'], + [['10000000', 7], '1'], + [['100000000', 8], '1'], + [['1000000000', 9], '1'], + [['10000000000', 10], '1'], + [['100000000000', 11], '1'], + [['1000000000000', 12], '1'], + [['10000000000000', 13], '1'], + [['1000000000000000000', 18], '1'], ]; -export const fromWeiValidData: [[Numbers, EtherUnits], string][] = [ +export const fromWeiValidData: [[Numbers, EtherUnits | number], Numbers][] = [ ...conversionBaseData, [['0xff', 'wei'], '255'], [[1e+22, 'ether'], '10000'], [[19999999999999991611392, 'ether'], '19999.999999999991611392'], [[1.9999999999999991611392e+22, 'ether'], '19999.999999999991611392'], + [['1000000', 'ether'], '0.000000000001'], + [['1123456789123456789', 'ether'], '1.123456789123456789'], + [['1123', 'kwei'], '1.123'], + [['1234100' ,'kwei'], '1234.1'], + [['3308685546611893', 'ether'], '0.003308685546611893'] ]; -export const toWeiValidData: [[Numbers, EtherUnits], string][] = [ +export const toWeiValidData: [[Numbers, EtherUnits | number], Numbers][] = [ ...conversionBaseData, [['255', 'wei'], '0xFF'], + [['100000000000', 'ether'], 0.0000001], + [['1000000000', 'ether'], 0.000000001], + [['1000000', 'ether'], 0.000000000001], + [['1123456789123456789', 'ether'], '1.123456789123456789123'], + [['1123', 'kwei'], '1.12345'], + [['1234100' ,'kwei'], '1234.1'], + [['3308685546611893', 'ether'], '0.0033086855466118933'], + [['1123', 'kwei'], 1.12345], + +]; + +export const toWeiValidDataWarnings: [[Numbers, EtherUnits], string][] = [ + [[0.0000000000000000000001, 'ether'], 'Warning: Using type `number` with values that are large or contain many decimals may cause loss of precision, it is recommended to use type `string` or `BigInt` when using conversion methods'], + [[0.0000000000000000000001, 'ether'], 'Warning: Using type `number` with values that are large or contain many decimals may cause loss of precision, it is recommended to use type `string` or `BigInt` when using conversion methods'], + [[1999999000000009900000, 'kwei'], 'Warning: Using type `number` with values that are large or contain many decimals may cause loss of precision, it is recommended to use type `string` or `BigInt` when using conversion methods'], + ]; export const fromWeiInvalidData: [[any, any], string][] = [ @@ -315,6 +362,8 @@ export const fromWeiInvalidData: [[any, any], string][] = [ [[{}, 'kwei'], 'Invalid value given "{}". Error: can not parse as number data'], [['data', 'kwei'], 'Invalid value given "data". Error: can not parse as number data.'], [['1234', 'uwei'], 'Invalid value given "uwei". Error: invalid unit.'], + [['1234', -1], 'Invalid value given "-1". Error: not a valid unit. Must be a positive integer.'], + [['1234', 3.3], 'Invalid value given "3.3". Error: not a valid unit. Must be a positive integer.'] ]; export const toWeiInvalidData: [[any, any], string][] = [ diff --git a/packages/web3-utils/test/unit/converters.test.ts b/packages/web3-utils/test/unit/converters.test.ts index cb6fa58caa0..940590f06e7 100644 --- a/packages/web3-utils/test/unit/converters.test.ts +++ b/packages/web3-utils/test/unit/converters.test.ts @@ -63,6 +63,7 @@ import { toHexInvalidData, toWeiInvalidData, toWeiValidData, + toWeiValidDataWarnings, utf8ToHexInvalidData, utf8ToHexValidData, toCheckSumValidData, @@ -365,6 +366,19 @@ describe('converters', () => { expect(() => toWei(input[0], input[1])).toThrow(output); }); }); + describe('test console warnings', () => { + beforeEach(() => { + jest.spyOn(console, 'warn').mockImplementation(() => { + // do nothing + }); + }); + it.each(toWeiValidDataWarnings)('%s', (input, output) => { + toWei(input[0], input[1]); + // expect(() => toWei(input[0], input[1])).toThrow(output); + expect(console.warn).toHaveBeenCalledWith(output) + }); + + }) }); describe('toChecksumAddress', () => { describe('valid cases', () => { diff --git a/packages/web3-utils/test/unit/formatter.test.ts b/packages/web3-utils/test/unit/formatter.test.ts index b58f5459584..bed8b55de8e 100644 --- a/packages/web3-utils/test/unit/formatter.test.ts +++ b/packages/web3-utils/test/unit/formatter.test.ts @@ -519,6 +519,115 @@ describe('formatter', () => { ).toEqual(result); }); + it('should format array of objects', () => { + const schema = { + type: 'array', + items: { + type: 'object', + properties: { + prop1: { + format: 'uint', + }, + prop2: { + format: 'bytes', + }, + }, + }, + }; + + const data = [ + { prop1: 10, prop2: new Uint8Array(hexToBytes('FF')) }, + { prop1: 10, prop2: new Uint8Array(hexToBytes('FF')) }, + ]; + + const result = [ + { prop1: '0xa', prop2: '0xff' }, + { prop1: '0xa', prop2: '0xff' }, + ]; + + expect( + format(schema, data, { number: FMT_NUMBER.HEX, bytes: FMT_BYTES.HEX }), + ).toEqual(result); + }); + + it('should format array of objects with oneOf', () => { + const schema = { + type: 'array', + items: { + type: 'object', + properties: { + prop1: { + oneOf: [{ format: 'address' }, { type: 'string' }], + }, + prop2: { + format: 'bytes', + }, + }, + }, + }; + + const data = [ + { + prop1: '0x7ed0e85b8e1e925600b4373e6d108f34ab38a401', + prop2: new Uint8Array(hexToBytes('FF')), + }, + { prop1: 'some string', prop2: new Uint8Array(hexToBytes('FF')) }, + ]; + + const result = [ + { prop1: '0x7ed0e85b8e1e925600b4373e6d108f34ab38a401', prop2: '0xff' }, + { prop1: 'some string', prop2: '0xff' }, + ]; + + expect( + format(schema, data, { number: FMT_NUMBER.HEX, bytes: FMT_BYTES.HEX }), + ).toEqual(result); + }); + + it('should format array of different objects', () => { + const schema = { + type: 'array', + items: [ + { + type: 'object', + properties: { + prop1: { + format: 'uint', + }, + prop2: { + format: 'bytes', + }, + }, + }, + { + type: 'object', + properties: { + prop1: { + format: 'string', + }, + prop2: { + format: 'uint', + }, + }, + }, + ], + }; + + const data = [ + { prop1: 10, prop2: new Uint8Array(hexToBytes('FF')) }, + { prop1: 'test', prop2: 123 }, + ]; + + const result = [ + { prop1: 10, prop2: '0xff' }, + { prop1: 'test', prop2: 123 }, + ]; + + expect( + format(schema, data, { number: FMT_NUMBER.NUMBER, bytes: FMT_BYTES.HEX }), + ).toEqual(result); + }); + it('should format array values with object type', () => { const schema = { type: 'object', diff --git a/packages/web3-utils/test/unit/promise_helpers.test.ts b/packages/web3-utils/test/unit/promise_helpers.test.ts index b18f4f1a5b0..7492c51fc24 100644 --- a/packages/web3-utils/test/unit/promise_helpers.test.ts +++ b/packages/web3-utils/test/unit/promise_helpers.test.ts @@ -147,20 +147,26 @@ describe('promise helpers', () => { }); it('should return interval id if not resolved in specific time', async () => { - let counter = 0; + // eslint-disable-next-line @typescript-eslint/require-await const asyncHelper = async () => { if (counter <= 3000000) { counter += 1; return undefined; } - return "result"; + return 'result'; }; const testError = new Error('Test P2 Error'); - const [neverResolvePromise, intervalId] = pollTillDefinedAndReturnIntervalId(asyncHelper, 100); - const promiCheck = Promise.race([neverResolvePromise, rejectIfTimeout(500,testError)[1]]); + const [neverResolvePromise, intervalId] = pollTillDefinedAndReturnIntervalId( + asyncHelper, + 100, + ); + const promiCheck = Promise.race([ + neverResolvePromise, + rejectIfTimeout(500, testError)[1], + ]); await expect(promiCheck).rejects.toThrow(testError); expect(intervalId).toBeDefined(); @@ -188,6 +194,7 @@ describe('promise helpers', () => { it('reject if later throws', async () => { const dummyError = new Error('error'); let counter = 0; + // eslint-disable-next-line @typescript-eslint/require-await const asyncHelper = async () => { if (counter === 0) { counter += 1; diff --git a/packages/web3-validator/CHANGELOG.md b/packages/web3-validator/CHANGELOG.md index 07ba353112d..da4cc2c0136 100644 --- a/packages/web3-validator/CHANGELOG.md +++ b/packages/web3-validator/CHANGELOG.md @@ -168,8 +168,11 @@ Documentation: - Multi-dimensional arrays(with a fix length) are now handled properly when parsing ABIs (#6798) -## [Unreleased] +## [2.0.6] ### Fixed -- Nodejs Buffer is not recognized as valid bytes value +- The JSON schema conversion process now correctly assigns an id when the `abi.name` is not available, for example, in the case of public mappings. (#6981) +- `browser` entry point that was pointing to an non-existing bundle file was removed from `package.json` (#7015) + +## [Unreleased] \ No newline at end of file diff --git a/packages/web3-validator/package.json b/packages/web3-validator/package.json index f32cf8d44e4..4a12b604981 100644 --- a/packages/web3-validator/package.json +++ b/packages/web3-validator/package.json @@ -1,6 +1,6 @@ { "name": "web3-validator", - "version": "2.0.5", + "version": "2.0.6", "description": "JSON-Schema compatible validator for web3", "main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", @@ -11,7 +11,6 @@ "require": "./lib/commonjs/index.js" } }, - "browser": "./dist/web3-validator.min.js", "repository": "https://github.com/ChainSafe/web3.js", "author": "ChainSafe Systems", "license": "LGPL-3.0", @@ -33,7 +32,7 @@ "build:types": "tsc --build tsconfig.types.json", "build:web": "npx webpack", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", @@ -47,8 +46,8 @@ "dependencies": { "ethereum-cryptography": "^2.0.0", "util": "^0.12.5", - "web3-errors": "^1.1.4", - "web3-types": "^1.5.0", + "web3-errors": "^1.2.0", + "web3-types": "^1.6.0", "zod": "^3.21.4" }, "devDependencies": { diff --git a/packages/web3-validator/src/utils.ts b/packages/web3-validator/src/utils.ts index 36a65e3f122..6f9fd6f7f55 100644 --- a/packages/web3-validator/src/utils.ts +++ b/packages/web3-validator/src/utils.ts @@ -141,7 +141,7 @@ export const abiSchemaToJsonSchema = ( // e.g. {name: 'a', type: 'uint'} if (isAbiParameterSchema(abi)) { abiType = abi.type; - abiName = abi.name; + abiName = abi.name || `${level}/${index}`; abiComponents = abi.components as FullValidationSchema; // If its short form string value e.g. ['uint'] } else if (typeof abi === 'string') { diff --git a/packages/web3-validator/src/validation/numbers.ts b/packages/web3-validator/src/validation/numbers.ts index 276fb84dd1e..bfaa7372148 100644 --- a/packages/web3-validator/src/validation/numbers.ts +++ b/packages/web3-validator/src/validation/numbers.ts @@ -28,6 +28,10 @@ export const isBigInt = (value: ValidInputTypes): boolean => typeof value === 'b // you can find more at: https://github.com/babel/babel/issues/13109 and https://github.com/web3/web3.js/issues/6187 /** @internal */ export const bigintPower = (base: bigint, expo: bigint) => { + // edge case + if (expo === BigInt(0)) { + return BigInt(1); + } let res = base; for (let index = 1; index < expo; index += 1) { res *= base; diff --git a/packages/web3-validator/test/fixtures/abi_to_json_schema.ts b/packages/web3-validator/test/fixtures/abi_to_json_schema.ts index 35ed35bce6f..e9e4391700c 100644 --- a/packages/web3-validator/test/fixtures/abi_to_json_schema.ts +++ b/packages/web3-validator/test/fixtures/abi_to_json_schema.ts @@ -157,6 +157,40 @@ const abiToJsonSchemaCases: AbiToJsonSchemaCase[] = [ }, }, + // this is for public mappings case where the abi has no name + { + title: 'multiple params of different types without name', + abi: { + fullSchema: [ + { name: '', type: 'uint' }, + { name: '', type: 'int' }, + ], + shortSchema: ['uint', 'int'], + data: [12, -1], + }, + json: { + fullSchema: { + type: 'array', + items: [ + { $id: '/0/0', format: 'uint', required: true }, + { $id: '/0/1', format: 'int', required: true }, + ], + minItems: 2, + maxItems: 2, + }, + shortSchema: { + type: 'array', + items: [ + { $id: '/0/0', format: 'uint', required: true }, + { $id: '/0/1', format: 'int', required: true }, + ], + minItems: 2, + maxItems: 2, + }, + data: [12, -1], + }, + }, + { title: 'single param array', abi: { diff --git a/packages/web3/CHANGELOG.md b/packages/web3/CHANGELOG.md index d46d48112b7..896d0d884f0 100644 --- a/packages/web3/CHANGELOG.md +++ b/packages/web3/CHANGELOG.md @@ -220,4 +220,52 @@ Documentation: - fixed erroneous parsing of big numbers in the `toNumber(...)` function (#6880) +## [4.8.0] + +### Changed + +#### web3-eth-abi + +- Dependencies updated + +#### web3-eth-accounts + +- Dependencies updated + +### Fixed + +#### web3-eth-contract + +- Fix an issue with smart contract function overloading (#6922) + +#### web3-utils + +- fixed toHex incorrectly hexing Uint8Arrays and Buffer (#6957) +- fixed isUint8Array not returning true for Buffer (#6957) + + +### Added + +#### web3-eth-contract + +- Added a console warning in case of an ambiguous call to a solidity method with parameter overloading (#6942) +- Added contract.deploy(...).decodeData(...) and contract.decodeMethodData(...) that decode data based on the ABI (#6950) + +#### web3-eth + +- method `getBlock` now includes properties of eip 4844, 4895, 4788 when returning block (#6933) +- update type `withdrawalsSchema`, `blockSchema` and `blockHeaderSchema` schemas to include properties of eip 4844, 4895, 4788 (#6933) + + +#### web3-types + +- Added `signature` to type `AbiFunctionFragment` (#6922) +- update type `Withdrawals`, `block` and `BlockHeaderOutput` to include properties of eip 4844, 4895, 4788 (#6933) + +## [4.9.0] + +### Added + +- Updated type `Web3EthInterface.accounts` to includes `privateKeyToAccount`,`privateKeyToAddress`,and `privateKeyToPublicKey` (#6762) + ## [Unreleased] \ No newline at end of file diff --git a/packages/web3/package.json b/packages/web3/package.json index 5d20596abe8..668d77e26ff 100644 --- a/packages/web3/package.json +++ b/packages/web3/package.json @@ -1,6 +1,6 @@ { "name": "web3", - "version": "4.7.0", + "version": "4.9.0", "description": "Ethereum JavaScript API", "main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", @@ -40,7 +40,7 @@ "build:web": "npx webpack", "build:web:analyze": "npx webpack --config ./webpack.analyze.js", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", @@ -86,21 +86,21 @@ "web3-providers-ipc": "^4.0.7" }, "dependencies": { - "web3-core": "^4.3.2", - "web3-errors": "^1.1.4", - "web3-eth": "^4.5.0", - "web3-eth-abi": "^4.2.0", - "web3-eth-accounts": "^4.1.1", - "web3-eth-contract": "^4.3.0", - "web3-eth-ens": "^4.2.0", + "web3-core": "^4.4.0", + "web3-errors": "^1.2.0", + "web3-eth": "^4.7.0", + "web3-eth-abi": "^4.2.2", + "web3-eth-accounts": "^4.1.2", + "web3-eth-contract": "^4.5.0", + "web3-eth-ens": "^4.3.0", "web3-eth-iban": "^4.0.7", "web3-eth-personal": "^4.0.8", - "web3-net": "^4.0.7", + "web3-net": "^4.1.0", "web3-providers-http": "^4.1.0", "web3-providers-ws": "^4.0.7", - "web3-rpc-methods": "^1.2.0", - "web3-types": "^1.5.0", - "web3-utils": "^4.2.2", - "web3-validator": "^2.0.5" + "web3-rpc-methods": "^1.3.0", + "web3-types": "^1.6.0", + "web3-utils": "^4.3.0", + "web3-validator": "^2.0.6" } } diff --git a/packages/web3/src/types.ts b/packages/web3/src/types.ts index fb14e6b2e1a..ef9bcc379cf 100644 --- a/packages/web3/src/types.ts +++ b/packages/web3/src/types.ts @@ -90,6 +90,9 @@ export interface Web3EthInterface extends Eth { options?: Record, ) => Promise; wallet: Wallet; + privateKeyToAddress: (privateKey: Bytes) => string; + privateKeyToPublicKey: (privateKey: Bytes, isCompressed: boolean) => string; + parseAndValidatePrivateKey: (data: Bytes, ignoreLength?: boolean) => Uint8Array; }; personal: Personal; } diff --git a/packages/web3/src/version.ts b/packages/web3/src/version.ts index c6e779f738c..4ab9622dba7 100644 --- a/packages/web3/src/version.ts +++ b/packages/web3/src/version.ts @@ -1 +1 @@ -/* eslint-disable header/header */ export const Web3PkgInfo = { version: '4.7.0' }; +/* eslint-disable header/header */ export const Web3PkgInfo = { version: '4.9.0' }; diff --git a/packages/web3/src/web3.ts b/packages/web3/src/web3.ts index 2953e67d364..0fda2c10521 100644 --- a/packages/web3/src/web3.ts +++ b/packages/web3/src/web3.ts @@ -20,7 +20,7 @@ import { Web3ContextInitOptions, Web3ContextObject, Web3SubscriptionConstructor, - isSupportedProvider + isSupportedProvider, } from 'web3-core'; import { Web3Eth, RegisteredSubscription, registeredSubscriptions } from 'web3-eth'; import Contract from 'web3-eth-contract'; @@ -37,7 +37,6 @@ import { EthExecutionAPI, SupportedProviders, DataFormat, - DEFAULT_RETURN_FORMAT } from 'web3-types'; import { InvalidMethodParamsError } from 'web3-errors'; import abi from './abi.js'; @@ -122,32 +121,38 @@ export class Web3< class ContractBuilder extends Contract { public constructor(jsonInterface: Abi); - public constructor(jsonInterface: Abi, + public constructor( + jsonInterface: Abi, addressOrOptionsOrContext?: Address | ContractInitOptions | Web3Context, - ); + ); public constructor( jsonInterface: Abi, addressOrOptionsOrContext?: Address | ContractInitOptions | Web3Context, optionsOrContextOrReturnFormat?: ContractInitOptions | Web3Context | DataFormat, ); - public constructor(jsonInterface: Abi, + public constructor( + jsonInterface: Abi, addressOrOptionsOrContext?: Address | ContractInitOptions, optionsOrContextOrReturnFormat?: ContractInitOptions, contextOrReturnFormat?: Web3Context | DataFormat, - ); - public constructor(jsonInterface: Abi, + ); + public constructor( + jsonInterface: Abi, addressOrOptionsOrContext?: Address | ContractInitOptions, optionsOrContextOrReturnFormat?: ContractInitOptions, contextOrReturnFormat?: Web3Context | DataFormat, - ); - public constructor(jsonInterface: Abi, + ); + public constructor( + jsonInterface: Abi, addressOrOptionsOrContext?: Address | ContractInitOptions, optionsOrContextOrReturnFormat?: ContractInitOptions, contextOrReturnFormat?: Web3Context | DataFormat, - returnFormat?: DataFormat - ) - { - if (isContractInitOptions(addressOrOptionsOrContext) && isContractInitOptions(optionsOrContextOrReturnFormat)) { + returnFormat?: DataFormat, + ) { + if ( + isContractInitOptions(addressOrOptionsOrContext) && + isContractInitOptions(optionsOrContextOrReturnFormat) + ) { throw new InvalidMethodParamsError( 'Should not provide options at both 2nd and 3rd parameters', ); @@ -155,22 +160,26 @@ export class Web3< let address: string | undefined; let options: object = {}; let context: Web3ContextObject; - let dataFormat: DataFormat = DEFAULT_RETURN_FORMAT; + let dataFormat: DataFormat | undefined; // add validation so its not a breaking change - if (!isNullish(addressOrOptionsOrContext) && typeof addressOrOptionsOrContext !== 'object' && typeof addressOrOptionsOrContext !== 'string') { + if ( + !isNullish(addressOrOptionsOrContext) && + typeof addressOrOptionsOrContext !== 'object' && + typeof addressOrOptionsOrContext !== 'string' + ) { throw new InvalidMethodParamsError(); } if (typeof addressOrOptionsOrContext === 'string') { address = addressOrOptionsOrContext; } - if (isContractInitOptions(addressOrOptionsOrContext)){ + if (isContractInitOptions(addressOrOptionsOrContext)) { options = addressOrOptionsOrContext as object; } else if (isContractInitOptions(optionsOrContextOrReturnFormat)) { options = optionsOrContextOrReturnFormat as object; } else { - options = {} + options = {}; } if (addressOrOptionsOrContext instanceof Web3Context) { @@ -183,7 +192,7 @@ export class Web3< context = self.getContextObject() as Web3ContextObject; } - if (returnFormat){ + if (returnFormat) { dataFormat = returnFormat; } else if (isDataFormat(optionsOrContextOrReturnFormat)) { dataFormat = optionsOrContextOrReturnFormat as DataFormat; @@ -191,7 +200,7 @@ export class Web3< dataFormat = contextOrReturnFormat; } - super(jsonInterface,address, options, context, dataFormat) + super(jsonInterface, address, options, context, dataFormat); super.subscribeToContextEvents(self); } } diff --git a/packages/web3/test/integration/external-providers/hardhat.test.ts b/packages/web3/test/integration/external-providers/hardhat.test.ts index 989424199fb..6a4fb6ff28b 100644 --- a/packages/web3/test/integration/external-providers/hardhat.test.ts +++ b/packages/web3/test/integration/external-providers/hardhat.test.ts @@ -18,11 +18,16 @@ along with web3.js. If not, see . // eslint-disable-next-line import/no-extraneous-dependencies import hardhat from 'hardhat'; -import { performBasicRpcCalls } from './helper'; +import { performBasicRpcCalls, failErrorCalls} from './helper'; describe('compatibility with `hardhat` provider', () => { it('should initialize Web3, get accounts & block number and send a transaction', async () => { // use the hardhat provider for web3.js - await performBasicRpcCalls(hardhat.network.provider); + await expect(performBasicRpcCalls(hardhat.network.provider)).resolves.not.toThrow(); }); + it('should throw on error calls', async () => { + const result = failErrorCalls(hardhat.network.provider); + await expect(result).rejects.toThrow(); + + }) }); diff --git a/packages/web3/test/integration/external-providers/helper.ts b/packages/web3/test/integration/external-providers/helper.ts index c1f88dd86b0..d88b1a7c90f 100644 --- a/packages/web3/test/integration/external-providers/helper.ts +++ b/packages/web3/test/integration/external-providers/helper.ts @@ -16,7 +16,9 @@ along with web3.js. If not, see . */ import { SupportedProviders } from 'web3-types'; +import { Contract } from 'web3-eth-contract'; import Web3 from '../../../src/index'; +import { BasicAbi, BasicBytecode } from '../../shared_fixtures/build/Basic'; /** * Performs basic RPC calls (like `eth_accounts`, `eth_blockNumber` and `eth_sendTransaction`) @@ -36,7 +38,7 @@ export async function performBasicRpcCalls(provider: SupportedProviders) { to: accounts[1], from: accounts[0], value: '1', - gas: 21000 + gas: 21000, }); expect(tx.status).toBe(BigInt(1)); @@ -46,3 +48,27 @@ export async function performBasicRpcCalls(provider: SupportedProviders) { // After sending a transaction, the blocknumber is supposed to be greater than or equal the block number before sending the transaction expect(blockNumber1).toBeGreaterThanOrEqual(blockNumber0); } + +export async function failErrorCalls(provider: SupportedProviders) { + let contract: Contract; + const web3 = new Web3(provider); + + contract = new web3.eth.Contract(BasicAbi, undefined, { + provider, + }); + + let deployOptions: Record; + + // eslint-disable-next-line prefer-const + deployOptions = { + data: BasicBytecode, + arguments: [10, 'string init value'], + }; + const accounts = await web3.eth.getAccounts(); + + const sendOptions = { from: accounts[0], gas: '1000000' }; + + contract = await contract.deploy(deployOptions).send(sendOptions); + + await contract.methods.reverts().send({ from: accounts[0] }); +} diff --git a/packages/web3/test/integration/web3.format.test.ts b/packages/web3/test/integration/web3.format.test.ts new file mode 100644 index 00000000000..5496a9efce1 --- /dev/null +++ b/packages/web3/test/integration/web3.format.test.ts @@ -0,0 +1,107 @@ +/* +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 { SupportedProviders } from 'web3-types'; +import { numberToHex } from 'web3-utils'; +import { Web3, Contract, FMT_BYTES, FMT_NUMBER } from '../../src'; + +import { + closeOpenConnection, + getSystemTestProvider, + createNewAccount, + createTempAccount, + mapFormatToType, +} from '../shared_fixtures/system_tests_utils'; +import { BasicAbi, BasicBytecode } from '../shared_fixtures/build/Basic'; + +describe('format', () => { + let web3: Web3; + let clientUrl: string | SupportedProviders; + let contractDeployed: Contract; + let contract: Contract; + let deployOptions: Record; + let sendOptions: Record; + let tempAcc: { address: string; privateKey: string }; + beforeAll(async () => { + clientUrl = getSystemTestProvider(); + web3 = new Web3({ + provider: clientUrl, + config: { + transactionPollingTimeout: 2000, + }, + }); + contract = new web3.eth.Contract(BasicAbi); + + deployOptions = { + data: BasicBytecode, + arguments: [10, 'string init value'], + }; + tempAcc = await createTempAccount(); + sendOptions = { from: tempAcc.address, gas: '1000000' }; + + contractDeployed = await contract.deploy(deployOptions).send(sendOptions); + }); + + afterAll(async () => { + await closeOpenConnection(web3); + }); + + describe('methods', () => { + it.each(Object.values(FMT_NUMBER))('getBlockNumber', async format => { + web3.defaultReturnFormat = { number: format as FMT_NUMBER, bytes: FMT_BYTES.HEX }; + const res = await web3.eth.getBlockNumber(); + expect(typeof res).toBe(mapFormatToType[format as string]); + expect(parseInt(String(res), 16)).toBeGreaterThan(0); + }); + + it.each(Object.values(FMT_NUMBER))('getGasPrice', async format => { + web3.defaultReturnFormat = { number: format as FMT_NUMBER, bytes: FMT_BYTES.HEX }; + const res = await web3.eth.getGasPrice(); + expect(typeof res).toBe(mapFormatToType[format as string]); + expect(parseInt(String(res), 16)).toBeGreaterThan(0); + }); + + it.each(Object.values(FMT_NUMBER))('getBalance', async format => { + web3.defaultReturnFormat = { number: format as FMT_NUMBER, bytes: FMT_BYTES.HEX }; + const value = '0xa'; + const newAccount = await createNewAccount(); + await web3.eth.sendTransaction({ + to: newAccount.address, + value, + from: tempAcc.address, + }); + const res = await web3.eth.getBalance(newAccount.address); + expect(typeof res).toBe(mapFormatToType[format as string]); + expect(numberToHex(res)).toBe(value); + }); + + it.each(Object.values(FMT_BYTES))('getCode', async format => { + web3.defaultReturnFormat = { number: FMT_NUMBER.BIGINT, bytes: format }; + const code = await web3.eth.getCode(contractDeployed?.options?.address as string); + expect(code).toBeDefined(); + expect(typeof code).toBe(mapFormatToType[format as string]); + }); + + it.each(Object.values(FMT_NUMBER))('getChainId', async format => { + web3.defaultReturnFormat = { number: format as FMT_NUMBER, bytes: FMT_BYTES.HEX }; + + const res = await web3.eth.getChainId(); + expect(typeof res).toBe(mapFormatToType[format as string]); + expect(Number(res)).toBeGreaterThan(0); + }); + }); +}); diff --git a/scripts/system_tests_utils.ts b/scripts/system_tests_utils.ts index a502c44825e..da58fc19d91 100644 --- a/scripts/system_tests_utils.ts +++ b/scripts/system_tests_utils.ts @@ -48,6 +48,8 @@ import { SupportedProviders, Web3APISpec, Web3EthExecutionAPI, + FMT_NUMBER, + FMT_BYTES, } from 'web3-types'; // eslint-disable-next-line import/no-extraneous-dependencies import { Personal } from 'web3-eth-personal'; @@ -504,3 +506,12 @@ export const objectBigintToString = (obj: object): object => // eslint-disable-next-line @typescript-eslint/no-unsafe-return JSON.stringify(obj, (_, value) => (typeof value === 'bigint' ? value.toString() : value)), ); + +export const mapFormatToType: { [key: string]: string } = { + [FMT_NUMBER.NUMBER]: 'number', + [FMT_NUMBER.HEX]: 'string', + [FMT_NUMBER.STR]: 'string', + [FMT_NUMBER.BIGINT]: 'bigint', + [FMT_BYTES.HEX]: 'string', + [FMT_BYTES.UINT8ARRAY]: 'object', +}; diff --git a/tools/eslint-config-base-web3/package.json b/tools/eslint-config-base-web3/package.json index d8487f9f237..0d645c8697f 100644 --- a/tools/eslint-config-base-web3/package.json +++ b/tools/eslint-config-base-web3/package.json @@ -28,7 +28,7 @@ }, "main": "ts.js", "scripts": { - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .js,.ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'" }, diff --git a/tools/web3-packagetemplate/package.json b/tools/web3-packagetemplate/package.json index a329ede3dfe..dab16c29ee0 100644 --- a/tools/web3-packagetemplate/package.json +++ b/tools/web3-packagetemplate/package.json @@ -22,7 +22,7 @@ "build:esm": "tsc --build tsconfig.esm.json", "build:types": "tsc --build tsconfig.types.json", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", diff --git a/tools/web3-plugin-example/package.json b/tools/web3-plugin-example/package.json index 8f3a5d6a5f5..3832f5ef30d 100644 --- a/tools/web3-plugin-example/package.json +++ b/tools/web3-plugin-example/package.json @@ -23,7 +23,7 @@ "prebuild": "yarn clean", "build": "tsc --build", "build:check": "node -e \"require('./lib')\"", - "lint": "eslint --ext .js,.ts .", + "lint": "eslint --cache --cache-strategy content --ext .ts .", "lint:fix": "eslint --fix --ext .js,.ts .", "format": "prettier --write '**/*'", "test": "jest --config=./test/unit/jest.config.js", diff --git a/tools/web3-plugin-example/src/custom_rpc_methods.ts b/tools/web3-plugin-example/src/custom_rpc_methods.ts index 101816a9c27..ed1c2fbee23 100644 --- a/tools/web3-plugin-example/src/custom_rpc_methods.ts +++ b/tools/web3-plugin-example/src/custom_rpc_methods.ts @@ -17,6 +17,8 @@ along with web3.js. If not, see . import { Web3PluginBase } from 'web3-core'; // eslint-disable-next-line require-extensions/require-extensions import { Web3Context } from './reexported_web3_context'; +// eslint-disable-next-line require-extensions/require-extensions +import { Web3Middleware } from './middleware'; type CustomRpcApi = { custom_rpc_method: () => string; @@ -25,6 +27,24 @@ type CustomRpcApi = { export class CustomRpcMethodsPlugin extends Web3PluginBase { public pluginNamespace = 'customRpcMethods'; + public web3Middleware: Web3Middleware | undefined; + + public constructor(testMiddleware = false) { + super(); + + if (testMiddleware) { + this.web3Middleware = new Web3Middleware(); + } + } + + public link(parentContext: Web3Context): void { + + if (this.web3Middleware) + parentContext.requestManager.setMiddleware(this.web3Middleware); + + super.link(parentContext); + } + public async customRpcMethod() { return this.requestManager.send({ @@ -39,6 +59,7 @@ export class CustomRpcMethodsPlugin extends Web3PluginBase { params: [parameter1, parameter2], }); } + } // Module Augmentation diff --git a/tools/web3-plugin-example/src/middleware.ts b/tools/web3-plugin-example/src/middleware.ts new file mode 100644 index 00000000000..bf16bc619f8 --- /dev/null +++ b/tools/web3-plugin-example/src/middleware.ts @@ -0,0 +1,76 @@ +/* +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 { RequestManagerMiddleware } from 'web3-core'; +import { + Web3APIMethod, + Web3APIReturnType, + JsonRpcResponse, + JsonRpcPayload, + JsonRpcRequest, +} from 'web3-types'; +import { jsonRpc } from 'web3-utils'; + +export class Web3Middleware implements RequestManagerMiddleware { + // eslint-disable-next-line class-methods-use-this + public async processRequest( + request: JsonRpcPayload, + ): Promise> { + // add your custom logic here for processing requests + let reqObj = { ...request } as JsonRpcPayload; + if (Array.isArray(reqObj)) { + reqObj = reqObj.map((req: JsonRpcRequest) => { + if (req.method === 'eth_call' && Array.isArray(req.params)) { + return { + ...req, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + params: [...req.params, '0x0', '0x1'], + }; + } + return req; + }); + } else if ( + (reqObj ).method === 'eth_call' && + Array.isArray((reqObj ).params) + ) { + (reqObj ) = { + ...(reqObj ), + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + params: [...((reqObj ).params ?? []), '0x0', '0x1'], + }; + } + + return Promise.resolve(reqObj as JsonRpcPayload); + } + + // eslint-disable-next-line class-methods-use-this + public async processResponse< + Method extends Web3APIMethod, + ResponseType = Web3APIReturnType, + >(response: JsonRpcResponse): Promise> { + // add your custom logic here for processing responses, following is just a demo + let resObj = { ...response }; + if (!jsonRpc.isBatchResponse(resObj) && resObj.id === 1) { + resObj = { + ...resObj, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + result: '0x6a756e616964' as any, + }; + } + + return Promise.resolve(resObj); + } +} diff --git a/tools/web3-plugin-example/test/unit/middleware.test.ts b/tools/web3-plugin-example/test/unit/middleware.test.ts new file mode 100644 index 00000000000..78ce5d7df8b --- /dev/null +++ b/tools/web3-plugin-example/test/unit/middleware.test.ts @@ -0,0 +1,68 @@ +/* +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 Web3, { TransactionCall } from 'web3'; +import { jsonRpc } from 'web3-utils'; +import { CustomRpcMethodsPlugin } from '../../src/custom_rpc_methods'; + +describe('CustomRpcMethodsPlugin Middleware', () => { + it('should modify request and response using middleware plugin', async () => { + const web3 = new Web3('http://127.0.0.1:8545'); + const plugin = new CustomRpcMethodsPlugin(true); + + // Test mocks and spy - code block start + const expectedResponse = { + jsonrpc: '2.0', + id: 1, + result: '0x6a756e616964', + }; + + jsonRpc.setRequestIdStart(0); + + const mockRequest = jest.spyOn(web3.provider as any, 'request'); + mockRequest.mockResolvedValue(expectedResponse); + // Test mocks and spy - code block end + + web3.registerPlugin(plugin); + + const transaction: TransactionCall = { + from: '0xee815C0a7cD0Ab35273Bc5943a3c6839a680Eaf0', + to: '0xe3342ae375e9B02F7D5513a1BB2276438D193e15', + type: '0x0', + data: '0x', + nonce: '0x4', + chain: 'mainnet', + hardfork: 'berlin', + chainId: '0x1', + }; + const result = await web3.eth.call(transaction); + expect(result).toBe('0x6a756e616964'); // result modified by response processor , so its 0x6a756e616964 instead of 0x0 + + const expectedCall = { + jsonrpc: '2.0', + id: 1, + method: 'eth_call', + params: [ + { ...transaction }, + 'latest', + '0x0', // added by middleware by request processor + '0x1', // added by middleware by request processor + ], + }; + expect(mockRequest).toHaveBeenCalledWith(expectedCall); + }); +});