diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f2e5ea09b0..6edd15446e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2458,7 +2458,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-errors -- Added `InvalidIntegerError` error for fromWei and toWei (#7052) +- Added `InvalidIntegerError` error for fromWei and toWei (#7052) #### web3-eth @@ -2484,8 +2484,8 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### 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) +- `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 @@ -2495,13 +2495,13 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-utils -- `toWei` support numbers in scientific notation (#6908) -- `toWei` and `fromWei` trims according to ether unit successfuly (#7044) +- `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) +- 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 @@ -2600,7 +2600,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-eth-accounts -- baseTransaction method updated (#7095) +- baseTransaction method updated (#7095) #### web3-providers-ws @@ -2612,7 +2612,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-rpc-providers - - Change request return type `Promise` to `Promise>` (#7102) +- Change request return type `Promise` to `Promise>` (#7102) ### Added @@ -2623,7 +2623,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-rpc-providers - - When error is returned with code 429, throw rate limit error (#7102) +- When error is returned with code 429, throw rate limit error (#7102) #### web3 @@ -2635,7 +2635,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-errors -- Fixed the undefined data in `Eip838ExecutionError` constructor (#6905) +- Fixed the undefined data in `Eip838ExecutionError` constructor (#6905) #### web3-eth @@ -2646,7 +2646,6 @@ If there are any bugs, improvements, optimizations or any new feature proposal f - Remove redundant constructor of contractBuilder (#7150) - ## [4.12.0] ### Fixed @@ -2657,7 +2656,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-eth-accounts -- Fix `TransactionFactory.registerTransactionType` not working, if there is a version mistatch between `web3-eth` and `web3-eth-accounts` by saving `extraTxTypes` at `globals`. (#7197) +- Fix `TransactionFactory.registerTransactionType` not working, if there is a version mistatch between `web3-eth` and `web3-eth-accounts` by saving `extraTxTypes` at `globals`. (#7197) ### Added @@ -2667,11 +2666,11 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-eth-contract -- Added `populateTransaction` to the `contract.deploy(...)` properties. (#7197) +- Added `populateTransaction` to the `contract.deploy(...)` properties. (#7197) #### web3-providers-http -- Added `statusCode` of response in ResponseError, `statusCode` is optional property in ResponseError. +- Added `statusCode` of response in ResponseError, `statusCode` is optional property in ResponseError. #### web3-rpc-providers @@ -2680,14 +2679,14 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-errors -- Added optional `statusCode` property of response in ResponseError. +- Added optional `statusCode` property of response in ResponseError. ### Changed #### web3-eth-contract - The returnred properties of `contract.deploy(...)` are structured with a newly created class named `DeployerMethodClass`. (#7197) -- Add a missed accepted type for the `abi` parameter, at `dataInputEncodeMethodHelper` and `getSendTxParams`. (#7197) +- Add a missed accepted type for the `abi` parameter, at `dataInputEncodeMethodHelper` and `getSendTxParams`. (#7197) ## [4.12.1] @@ -2695,6 +2694,11 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-eth-accounts -- Revert `TransactionFactory.registerTransactionType` if there is a version mistatch between `web3-eth` and `web3-eth-accounts` and fix nextjs problem. (#7216) +- Revert `TransactionFactory.registerTransactionType` if there is a version mistatch between `web3-eth` and `web3-eth-accounts` and fix nextjs problem. (#7216) + +#### web3 + +- `Web3.providers` namespace exports `type EIP6963ProviderResponse = Map`. Return type for the static `Web3.requestEIP6963Providers` is now `Promise`. (#7239) +- The callback function provided to the static `Web3.onNewProviderDiscovered` function expects a parameter of type `EIP6963ProvidersMapUpdateEvent` as opposed to `EIP6963AnnounceProviderEvent`. (#7242) ## [Unreleased] diff --git a/docs/docs/guides/advanced/custom_RPC.md b/docs/docs/guides/advanced/custom_RPC.md index 28ccfeeea63..605a9c61a3e 100644 --- a/docs/docs/guides/advanced/custom_RPC.md +++ b/docs/docs/guides/advanced/custom_RPC.md @@ -13,7 +13,7 @@ import TabItem from '@theme/TabItem'; Web3.js is a popular library for interacting with the Ethereum blockchain. It provides a set of APIs to interact with Ethereum nodes via JSON-RPC calls. For adding new JSON-RPC function calls to the library, you can do so using the plugin feature in web3.js 4.x. This allows you to extend the functionality of Web3.js and add support for new JSON-RPC methods. :::caution -In Web3.js 1.x, `web3.extend()` function could be used to add new JSON-RPC methods. `web3.extend()` is also available in Web3 v4.0.4+ with some breaking changes. However it is recommended to use Web3 Plugin feature for extending web3 functionality if you are developing new feature. +In Web3.js 1.x, `web3.extend()` function could be used to add new JSON-RPC methods. `web3.extend()` is also available in Web3 v4.0.4+ with some breaking changes. However it is recommended to use Web3 Plugin feature for extending web3 functionality if you are developing new feature. Read the ["Extending Web3.js"](/guides/advanced/extend) guide to learn more about the legacy `web3.extend()` method. ::: Following tutorial will guide you through the process of creating a custom plugin to extend the functionality of web3.js 4.x and add support for new RPC methods. @@ -30,16 +30,16 @@ This will give your plugin access to [requestManager](/api/web3-core/class/Web3C - + ```javascript const { Web3PluginBase } = require('web3'); //highlight-start class CustomRpcMethodsPlugin extends Web3PluginBase { - // step 1 - // ... + // step 1 + // ... } //highlight-end @@ -56,8 +56,8 @@ import { Web3PluginBase } from 'web3'; //highlight-start export default class CustomRpcMethodsPlugin extends Web3PluginBase { - // step 1 - // ... + // step 1 + // ... } //highlight-end ``` @@ -69,19 +69,18 @@ export default class CustomRpcMethodsPlugin extends Web3PluginBase { 2. After that add public `pluginNamespace` property. This will be used to access your plugin, as mentioned in step number 5 code example. - - + ```javascript const { Web3PluginBase } = require('web3'); class CustomRpcMethodsPlugin extends Web3PluginBase { -//highlight-start - pluginNamespace = 'customRpcMethods'; // step 2 -//highlight-end + //highlight-start + pluginNamespace = 'customRpcMethods'; // step 2 + //highlight-end } module.exports = CustomRpcMethodsPlugin; @@ -96,40 +95,39 @@ module.exports = CustomRpcMethodsPlugin; import { Web3PluginBase } from 'web3'; export default class CustomRpcMethodsPlugin extends Web3PluginBase { - //highlight-start - public pluginNamespace = 'customRpcMethods'; // step 2 -//highlight-end + //highlight-start + public pluginNamespace = 'customRpcMethods'; // step 2 + //highlight-end } ``` - ### Step 3: Creating Custom RPC Methods in the Plugin Class 3. Once plugin class is created using above mentioned steps, its very easy to add new RPC methods like: - + ```javascript const { Web3PluginBase } = require('web3'); class CustomRpcMethodsPlugin extends Web3PluginBase { - pluginNamespace = 'customRpcMethods'; + pluginNamespace = 'customRpcMethods'; //highlight-start - async customRpcMethod() { - // step 3 - return this.requestManager.send({ - // plugin has access to web3.js internal features like request manager - method: 'custom_rpc_method', - params: [], - }); - } + async customRpcMethod() { + // step 3 + return this.requestManager.send({ + // plugin has access to web3.js internal features like request manager + method: 'custom_rpc_method', + params: [], + }); + } //highlight-end } @@ -145,18 +143,18 @@ module.exports = CustomRpcMethodsPlugin; import { Web3PluginBase } from 'web3'; export default class CustomRpcMethodsPlugin extends Web3PluginBase { - public pluginNamespace = 'customRpcMethods'; - - //highlight-start - public async customRpcMethod() { - // step 3 - return this.requestManager.send({ - // plugin has access to web3.js internal features like request manager - method: 'custom_rpc_method', - params: [], - }); - } - //highlight-end + public pluginNamespace = 'customRpcMethods'; + + //highlight-start + public async customRpcMethod() { + // step 3 + return this.requestManager.send({ + // plugin has access to web3.js internal features like request manager + method: 'custom_rpc_method', + params: [], + }); + } + //highlight-end } ``` @@ -169,22 +167,22 @@ export default class CustomRpcMethodsPlugin extends Web3PluginBase { - + ```javascript const { Web3PluginBase } = require('web3'); class CustomRpcMethodsPlugin extends Web3PluginBase { - pluginNamespace = 'customRpcMethods'; - - async customRpcMethod() { - return this.requestManager.send({ - // plugin has access to web3.js internal features like request manager - method: 'custom_rpc_method', - params: [], - }); - } + pluginNamespace = 'customRpcMethods'; + + async customRpcMethod() { + return this.requestManager.send({ + // plugin has access to web3.js internal features like request manager + method: 'custom_rpc_method', + params: [], + }); + } } module.exports = CustomRpcMethodsPlugin; @@ -199,25 +197,25 @@ module.exports = CustomRpcMethodsPlugin; import { Web3PluginBase } from 'web3'; export default class CustomRpcMethodsPlugin extends Web3PluginBase { - public pluginNamespace = 'customRpcMethods'; - - public async customRpcMethod() { - return this.requestManager.send({ - // plugin has access to web3.js internal features like request manager - method: 'custom_rpc_method', - params: [], - }); - } + public pluginNamespace = 'customRpcMethods'; + + public async customRpcMethod() { + return this.requestManager.send({ + // plugin has access to web3.js internal features like request manager + method: 'custom_rpc_method', + params: [], + }); + } } //highlight-start // Module Augmentation declare module 'web3' { - // step 4 + // step 4 - interface Web3Context { - customRpcMethods: CustomRpcMethodsPlugin; - } + interface Web3Context { + customRpcMethods: CustomRpcMethodsPlugin; + } } //highlight-end ``` @@ -237,8 +235,8 @@ Once plugin is registered its custom methods will be available to use. - + ```javascript const { Web3 } = require('web3'); diff --git a/docs/docs/guides/advanced/extend.md b/docs/docs/guides/advanced/extend.md new file mode 100644 index 00000000000..b1301b2fc03 --- /dev/null +++ b/docs/docs/guides/advanced/extend.md @@ -0,0 +1,38 @@ +--- +sidebar_position: 2 +sidebar_label: Extending Web3.js +--- + +# Extending Web3.js + +Although the preferred way to add custom RPC methods to Web3.js is to [create a plugin](/guides/advanced/custom_RPC), Web3.js also exposes a [legacy `extend` method](/api/web3/class/Web3Context#extend) that can be used for the same purpose. Keep reading to learn how to use the legacy `extend` method to add a custom RPC method to an instance of Web3.js. + +## `ExtensionObject` + +The legacy `extend` method accepts a single parameter that should implement the [`ExtensionObject` interface](/api/web3/namespace/core/#ExtensionObject). An `ExtensionObject` consists of two properties: an optional `string` property named `property` and a required property named `methods` that is an array of objects that implement the [`Method` interface](/api/web3/namespace/core/#Method). The `Method` interface specifies two properties, both of which are required and both of which are strings: `name` and `call`. The `property` property of an `Extension` object can be used to specify the name of the Web3.js member property that will expose the custom RPC methods (if this parameter is omitted, the new RPC methods will be exposed by the "root" Web3.js object). Each element of the `methods` array from the `ExtensionObject` specifies a new custom RPC method - the `name` property is the name of the new function that will be used to call the custom RPC method and the `call` property is the actual RPC endpoint that should be invoked. The new function will accept parameters that will be passed along when invoking the RPC endpoint. + +Here is a complete example of using the legacy `extend` method: + +```js +import { Web3 } from 'web3'; + +const web3 = new Web3('https://eth.llamarpc.com'); + +async function main() { + web3.extend({ + property: 'BlockReceipts', + methods: [ + { + name: 'getBlockReceipts', + // https://www.quicknode.com/docs/ethereum/eth_getBlockReceipts + call: 'eth_getBlockReceipts', + }, + ], + }); + + const receipts = await web3.BlockReceipts.getBlockReceipts('latest'); + console.log(receipts); +} + +main(); +``` diff --git a/docs/docs/guides/advanced/tree_shaking.md b/docs/docs/guides/advanced/tree_shaking.md index 46f301e738c..46bd1a32505 100644 --- a/docs/docs/guides/advanced/tree_shaking.md +++ b/docs/docs/guides/advanced/tree_shaking.md @@ -1,5 +1,5 @@ --- -sidebar_position: 2 +sidebar_position: 3 sidebar_label: Tree Shaking Guide --- @@ -26,7 +26,7 @@ import TabItem from '@theme/TabItem'; :::note For further information about `sideEffects` see [webpack docs](https://webpack.js.org/guides/tree-shaking/) -::: +::: ## Step 3: Set tsconfig Module to ES2015 @@ -44,8 +44,8 @@ For further information about `sideEffects` see [webpack docs](https://webpack.j - + ```javascript const { Web3Eth } = require('web3-eth'); @@ -69,8 +69,8 @@ If you only need a few functions from `web3-utils`: - + ```javascript const { numberToHex, hexToNumber } = require('web3-utils'); diff --git a/docs/docs/guides/web3_upgrade_guide/1.x/web3_utils_migration_guide.md b/docs/docs/guides/web3_upgrade_guide/1.x/web3_utils_migration_guide.md index 0b977890e6e..cbc68f0eb14 100644 --- a/docs/docs/guides/web3_upgrade_guide/1.x/web3_utils_migration_guide.md +++ b/docs/docs/guides/web3_upgrade_guide/1.x/web3_utils_migration_guide.md @@ -17,6 +17,18 @@ import web3Utils from 'web3-utils'; import * as web3Utils from 'web3-utils'; ``` +## No `BN` property + +The `web3-utils` package no longer includes a `BN` property for using the [the `bn.js` package](https://github.com/indutny/bn.js/) for working with (big) numbers. In 4.x, [the native JavaScript `BigInt` type](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) is used. + +```ts +// 1.x +new Web3.utils.BN(1); + +// 4.x +BigInt(4); +``` + ## Unit conversion functions The `toWei` does not have an optional second parameter. You have to pass the source unit explicitly. @@ -31,20 +43,20 @@ web3.utils.toWei('0.1', 'ether'); ## Conversion to Hex -The `toHex` behave exactly the same in both v1.x and 4.x, except for a string that contains only numbers. In 1.x if a number was provided inside a string like `123` it used to be treated as a number. While in 4.x it will be treated as a string, except if it was prefixed with `0x`. For more clarity, check below: +The `toHex` behave exactly the same in both v1.x and 4.x, except for a string that contains only numbers. In 1.x if a number was provided inside a string like `123` it used to be treated as a number. While in 4.x it will be treated as a string, except if it was prefixed with `0x`. For more clarity, check below: ```ts // 1.x -new Web3().utils.toHex(0x1) // returns 0x1 -new Web3().utils.toHex('0x1') // returns 0x1 -new Web3().utils.toHex(1) // returns 0x1 -new Web3().utils.toHex('1') // returns 0x1 +new Web3().utils.toHex(0x1); // returns 0x1 +new Web3().utils.toHex('0x1'); // returns 0x1 +new Web3().utils.toHex(1); // returns 0x1 +new Web3().utils.toHex('1'); // returns 0x1 // 4.x -new Web3().utils.toHex(0x1) // returns 0x1 -new Web3().utils.toHex('0x1') // returns 0x1 -new Web3().utils.toHex(1) // returns 0x1 -new Web3().utils.toHex('1') // returns 0x31 +new Web3().utils.toHex(0x1); // returns 0x1 +new Web3().utils.toHex('0x1'); // returns 0x1 +new Web3().utils.toHex(1); // returns 0x1 +new Web3().utils.toHex('1'); // returns 0x31 ``` ## Validation functions @@ -80,6 +92,7 @@ isHex('-0x'); // in 1.x used to return `true`. But changed in 4.x to return `fal isHexStrict('-0x'); // in 1.x used to return `true`. But changed in 4.x to return `false` // `false` ``` + ## stripHexPrefix method In 1.x `stripHexPrefix` method is located in the `web3-utils` package, in 4.x this has been moved to `web3-eth-accounts` @@ -88,7 +101,6 @@ In 1.x `stripHexPrefix` method is located in the `web3-utils` package, in 4.x th import { stripHexPrefix } from 'web3-eth-accounts'; console.log(stripHexPrefix('0x123')); // "123" - ``` ## Other functions diff --git a/packages/web3-eth-abi/CHANGELOG.md b/packages/web3-eth-abi/CHANGELOG.md index 8410c9d9f95..c764ff78ff4 100644 --- a/packages/web3-eth-abi/CHANGELOG.md +++ b/packages/web3-eth-abi/CHANGELOG.md @@ -183,3 +183,7 @@ Documentation: - fix encodedata in EIP-712 (#7095) ## [Unreleased] + +### Added + +- Handle common cases for smart contract errors according to EIP 838: `0x4e487b71` which is the ‘selector’ for `Panic(uint256)` and `0x08c379a0` is the ‘selector’ of `Error(string)`. (7155) diff --git a/packages/web3-eth-abi/src/decode_contract_error_data.ts b/packages/web3-eth-abi/src/decode_contract_error_data.ts index 98eb02d4fd8..11566b79645 100644 --- a/packages/web3-eth-abi/src/decode_contract_error_data.ts +++ b/packages/web3-eth-abi/src/decode_contract_error_data.ts @@ -39,6 +39,36 @@ export const decodeContractErrorData = ( errorSignature = jsonInterfaceMethodToString(errorAbi); // decode abi.inputs according to EIP-838 errorArgs = decodeParameters([...errorAbi.inputs], error.data.substring(10)); + } else if (error.data.startsWith('0x08c379a0')) { + // If ABI was not provided, check for the 2 famous errors: 'Error(string)' or 'Panic(uint256)' + + errorName = 'Error'; + errorSignature = 'Error(string)'; + // decode abi.inputs according to EIP-838 + errorArgs = decodeParameters( + [ + { + name: 'message', + type: 'string', + }, + ], + error.data.substring(10), + ); + } else if (error.data.startsWith('0x4e487b71')) { + errorName = 'Panic'; + errorSignature = 'Panic(uint256)'; + // decode abi.inputs according to EIP-838 + errorArgs = decodeParameters( + [ + { + name: 'code', + type: 'uint256', + }, + ], + error.data.substring(10), + ); + } else { + console.error('No matching error abi found for error data', error.data); } } catch (err) { console.error(err); diff --git a/packages/web3-eth-abi/test/fixtures/data.ts b/packages/web3-eth-abi/test/fixtures/data.ts index 0fe90638f95..f8990a22e86 100644 --- a/packages/web3-eth-abi/test/fixtures/data.ts +++ b/packages/web3-eth-abi/test/fixtures/data.ts @@ -974,6 +974,40 @@ export const validDecodeContractErrorData: { input: any[]; output: any; }[] = [ + { + input: [ + [], + { + code: 12, + message: 'message', + data: '0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000155468697320697320612063616c6c207265766572740000000000000000000000', + }, + ], + output: { + errorName: 'Error', + errorSignature: 'Error(string)', + errorArgs: { + message: 'This is a call revert', + }, + }, + }, + { + input: [ + [], + { + code: 12, + message: 'message', + data: '0x4e487b71000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000', + }, + ], + output: { + errorName: 'Panic', + errorSignature: 'Panic(uint256)', + errorArgs: { + code: 42, + }, + }, + }, { input: [ [ diff --git a/packages/web3-eth-abi/test/unit/decodeContractErrorData.test.ts b/packages/web3-eth-abi/test/unit/decodeContractErrorData.test.ts index 3f6a624ef22..9e99add02f3 100644 --- a/packages/web3-eth-abi/test/unit/decodeContractErrorData.test.ts +++ b/packages/web3-eth-abi/test/unit/decodeContractErrorData.test.ts @@ -32,7 +32,13 @@ describe('decodeContractErrorData', () => { expect(err.errorName).toEqual(output.errorName); expect(err.errorSignature).toEqual(output.errorSignature); expect(err.errorArgs?.message).toEqual(output.errorArgs?.message); - expect(Number(err.errorArgs?.code)).toEqual(output.errorArgs?.code); + + // This ensures they are equal if one was provided + // It also skips if both are not provided + if (err.errorArgs?.code || output.errorArgs?.code) { + // eslint-disable-next-line jest/no-conditional-expect + expect(Number(err.errorArgs?.code)).toEqual(output.errorArgs?.code); + } expect(err.cause?.code).toEqual(output.cause?.code); }, ); diff --git a/packages/web3/src/web3_eip6963.ts b/packages/web3/src/web3_eip6963.ts index cf7f44a19be..2e9a2a391f8 100644 --- a/packages/web3/src/web3_eip6963.ts +++ b/packages/web3/src/web3_eip6963.ts @@ -34,6 +34,8 @@ export interface EIP6963ProviderDetail { provider: EIP1193Provider; } +export type EIP6963ProviderResponse = Map; + export interface EIP6963AnnounceProviderEvent extends CustomEvent { type: Eip6963EventName.eip6963announceProvider; detail: EIP6963ProviderDetail; @@ -43,15 +45,15 @@ export interface EIP6963RequestProviderEvent extends Event { type: Eip6963EventName.eip6963requestProvider; } -export const eip6963ProvidersMap: Map = new Map(); +export const eip6963ProvidersMap: EIP6963ProviderResponse = new Map(); export const web3ProvidersMapUpdated = 'web3:providersMapUpdated'; export interface EIP6963ProvidersMapUpdateEvent extends CustomEvent { type: string; - detail: Map; + detail: EIP6963ProviderResponse; } -export const requestEIP6963Providers = async () => +export const requestEIP6963Providers = async (): Promise => new Promise((resolve, reject) => { if (typeof window === 'undefined') { reject( @@ -80,7 +82,7 @@ export const requestEIP6963Providers = async () => }); export const onNewProviderDiscovered = ( - callback: (providerEvent: EIP6963AnnounceProviderEvent) => void, + callback: (providerEvent: EIP6963ProvidersMapUpdateEvent) => void, ) => { if (typeof window === 'undefined') { throw new Error(