diff --git a/docs/docs/guides/web3_migration_guide/providers_migration_guide.md b/docs/docs/guides/web3_migration_guide/providers_migration_guide.md index 7d58b9f6f85..7cc50878c15 100644 --- a/docs/docs/guides/web3_migration_guide/providers_migration_guide.md +++ b/docs/docs/guides/web3_migration_guide/providers_migration_guide.md @@ -4,85 +4,18 @@ sidebar_position: 2 sidebar_label: web3.providers --- -There are multiple ways to set the provider. +For full description about the providers, their priorities and their types, you can check [web3.js Providers Guide](/docs/guides/web3_providers_guide/). -```ts title='Setting a provider' -web3.setProvider(myProvider); -web3.eth.setProvider(myProvider); -web3.Contract.setProvider(myProvider); -contractInstance.setProvider(myProvider); -``` - -The key rule for setting provider is as follows: - -1. Any provider set on the higher level will be applied to all lower levels. e.g. Any provider set using `web3.setProvider` will also be applied to `web3.eth` object. -2. For contracts `web3.Contract.setProvider` can be used to set provider for **all instances** of contracts created by `web3.eth.Contract`. - -:::tip -A provider can be either type `string` or [`SupportedProviders`](/api/web3-core#SupportedProviders). -::: - -## Examples - -### Local Geth Node - -```ts -const Web3 = require('web3'); -const web3 = new Web3('http://localhost:8545'); -// or -const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); - -// change provider -web3.setProvider('ws://localhost:8546'); -// or -web3.setProvider(new Web3.providers.WebsocketProvider('ws://localhost:8546')); - -// Using the IPC provider in node.js -const net = require('net'); -const web3 = new Web3('/Users/myuser/Library/Ethereum/geth.ipc', net); // mac os path -// or -const web3 = new Web3( - new Web3.providers.IpcProvider('/Users/myuser/Library/Ethereum/geth.ipc', net), -); // mac os path -// on windows the path is: "\\\\.\\pipe\\geth.ipc" -// on linux the path is: "/users/myuser/.ethereum/geth.ipc" -``` +### Provider Options Changes -### Remote Node Provider - -```ts -// Using a remote node provider, like Alchemy (https://www.alchemyapi.io/supernode), is simple. -const Web3 = require('web3'); -const web3 = new Web3('https://eth-mainnet.alchemyapi.io/v2/your-api-key'); -``` - -### Injected providers - -The Injected provider should be in compliance with [EIP1193](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md). - -The web3.js 4.x Provider specifications are defined in [web3 base provider](https://github.com/ChainSafe/web3.js/blob/4.x/packages/web3-types/src/web3_base_provider.ts) for Injected Providers. - -```ts -const Web3 = require('web3'); -// Using an EIP1193 provider like MetaMask can be injected - -if (window.ethereum) { - // Check if ethereum object exists - await window.ethereum.request(); - window.web3 = new Web3(window.ethereum); // inject provider -} -``` - -### Provider Options - -There are differences in the objects that could be passed in the Provider constructors. +There are differences in the objects that could be passed in the Provider constructors between version 1.x and 4.x. Below, you will find the difference for every Provider object type. #### HttpProvider In 1.x, options passed in the constructor should be of type [`HttpProviderOptions`](https://github.com/web3/web3.js/blob/1.x/packages/web3-core-helpers/types/index.d.ts#L173). The `HttpProviderOptions` interface consists of: ```ts -export interface HttpProviderOptions { +interface HttpProviderOptions { keepAlive?: boolean; timeout?: number; headers?: HttpHeader[]; @@ -90,13 +23,13 @@ export interface HttpProviderOptions { agent?: HttpAgent; } -export interface HttpAgent { +interface HttpAgent { http?: http.Agent; https?: https.Agent; baseUrl?: string; } -export interface HttpHeader { +interface HttpHeader { name: string; value: string; } @@ -147,12 +80,12 @@ let httpOptions = { }; ``` -#### WebsocketProvider +#### WebSocketProvider In 1.x, options passed in the constructor should be of type [`WebsocketProviderOptions`](https://github.com/web3/web3.js/blob/1.x/packages/web3-core-helpers/types/index.d.ts#L192). The `WebsocketProviderOptions` interface consists of: ```ts -export interface WebsocketProviderOptions { +interface WebsocketProviderOptions { host?: string; timeout?: number; reconnectDelay?: number; @@ -164,7 +97,7 @@ export interface WebsocketProviderOptions { reconnect?: ReconnectOptions; } -export interface ReconnectOptions { +interface ReconnectOptions { auto?: boolean; delay?: number; maxAttempts?: number; @@ -172,22 +105,21 @@ export interface ReconnectOptions { } ``` -In 4.x, the options object is of type `ClientRequestArgs` or of `ClientOptions`. See -Regarding `RequestInit` see [here](https://microsoft.github.io/PowerBI-JavaScript/interfaces/_node_modules__types_node_http_d_._http_.clientrequestargs.html) for `ClientRequestArgs` and [here](https://github.com/websockets/ws) for `ClientOptions`. - -In 4.x a second option parameter can be given regarding reconnecting. +In 4.x, the options object is of type `ClientRequestArgs` or of `ClientOptions`. See [here](https://microsoft.github.io/PowerBI-JavaScript/interfaces/_node_modules__types_node_http_d_._http_.clientrequestargs.html) for `ClientRequestArgs` and [here](https://github.com/websockets/ws) for `ClientOptions`. -The interface: +In 4.x a second option parameter can be given regarding auto-reconnecting, delay and max tries attempts. And here is its type: ```ts -export type ReconnectOptions = { - autoReconnect: boolean; - delay: number; - maxAttempts: number; +type ReconnectOptions = { + autoReconnect: boolean; // default: `true` + delay: number; // default: `5000` + maxAttempts: number; // default: `5` }; ``` -For example: +##### Options examples + +Below is an example for the passed options for each version: ```ts // in 1.x @@ -232,3 +164,23 @@ const reconnectOptions: ReconnectOptions = { maxAttempts: 5, }; ``` + +##### Error message for reconnect attempts + +The error in, version 1.x, was an Error object that contains the message: +`'Maximum number of reconnect attempts reached!'` + +However, the error, in version 4.x, is just an error message (not wrapped in an Error object). And the error message will contain the value of the variable `maxAttempts` as follows: + +`` `Max connection attempts exceeded (${maxAttempts})` `` + +And here is how to catch the error, in version 4.x, if max attempts reached when there is auto reconnecting: + +```ts +provider.on('error', errorMessage => { + if (errorMessage.startsWith('Max connection attempts exceeded')) { + // the `errorMessage` will be `Max connection attempts exceeded (${maxAttempts})` + // the `maxAttempts` is equal to the provided value by the user, or the default value `5`. + } +}); +``` diff --git a/docs/docs/guides/web3_plugin_guide/index.md b/docs/docs/guides/web3_plugin_guide/index.md index c85745adbf3..32d0f8c15b4 100644 --- a/docs/docs/guides/web3_plugin_guide/index.md +++ b/docs/docs/guides/web3_plugin_guide/index.md @@ -1,5 +1,5 @@ --- -sidebar_position: 1 +sidebar_position: 2 sidebar_label: 'web3 Plugins' --- diff --git a/docs/docs/guides/web3_providers_guide/events_listening.md b/docs/docs/guides/web3_providers_guide/events_listening.md new file mode 100644 index 00000000000..8c85bb00e2f --- /dev/null +++ b/docs/docs/guides/web3_providers_guide/events_listening.md @@ -0,0 +1,68 @@ +--- +sidebar_position: 0 +sidebar_label: 'Providers Events Listening' +--- + +# Providers Events Listening + +Some providers are, by design, always connected. Therefor, they can communicate changes with the user through events. Actually, among the 3 providers, `HttpProvider` is the only one that does not support event. And the other 2: +[WebSocketProvider](/api/web3-providers-ws/class/WebSocketProvider) and [IpcProvider](/api/web3-providers-ipc/class/IpcProvider) enable the user to listen to emitted events. + +Actually, the events can be categorized as follows ([according to EIP 1193](https://eips.ethereum.org/EIPS/eip-1193#rationale)): + +- Communicate arbitrary messages: `message` +- Changes to the Provider’s ability to make RPC requests; + - `connect` + - `disconnect` +- Common Client and/or Wallet state changes that any non-trivial application must handle: + - `chainChanged` + - `accountsChanged` + +Below a sample code for listening and remove listening to EIP 1193 events: + +```ts +import { Web3 } from `web3` + +const web3 = new Web3(/* PROVIDER*/); + +web3.provider.on('message',()=>{ + // ... +}) + +web3.provider.on('connect',()=>{ + // ... +}) + +web3.provider.on('disconnect',()=>{ + // ... +}) + +web3.provider.on('accountsChanged',()=>{ + // ... +}) + +web3.provider.on('chainChanged',()=>{ + // ... +}) + +// it is possible to catch errors that could happen in the underlying connection Socket with the `error` event +// and it is also used to catch the error when max connection attempts exceeded +// as in section: /docs/guides/web3_providers_guide/#error-message +web3.provider.on('error',()=>{ + // ... +} + +// ... + +// for every event above `once` could be used to register to the event only once +web3.provider.once('SUPPORTED_EVENT_NAME',()=>{ + // ... +}) + +// And to unregister a listener `removeListener` could be called +web3.provider.removeListener('SUPPORTED_EVENT_NAME',()=>{ + // ... +}) +``` + +However, the underlying `SocketConnection` of both `WebSocketProvider` and `IpcProvider` could be accessed. This enables the user to access any special properties of the used Socket. As well as, registering to the custom server events directly. Actually the Socket used at `WebSocketProvider` is [isomorphic-ws](https://github.com/heineiuo/isomorphic-ws). And the Socket used at `IpcProvider` is [net.Server](https://nodejs.org/api/net.html#class-netserver) diff --git a/docs/docs/guides/web3_providers_guide/index.md b/docs/docs/guides/web3_providers_guide/index.md new file mode 100644 index 00000000000..c8d5f8ffe5b --- /dev/null +++ b/docs/docs/guides/web3_providers_guide/index.md @@ -0,0 +1,206 @@ +--- +sidebar_position: 1 +sidebar_label: 'Providers' +--- + +# web3.js Providers Guide + +Connecting to a chain happens through a provider. You can pass the provider to the constructor as in the following example: + +```ts +import { Web3 } from `web3` + +const web3 = new Web3(/* PROVIDER*/); + +// calling any method that interact with the network would involve using the early passed provider. +await web3.eth.sendTransaction({ + from, + to, + value, +}); +``` + +The created Web3 instance will use the passed provider to interact with the blockchain network. This interaction happen when sending a request and receiving the response, and when possibly listen to provider events (if the provider support this). + +## Providers Types + +Actually, the provider could be any of the following: + +- An instance of [HttpProvider](/api/web3-providers-http/class/HttpProvider) +- An instance of [WebSocketProvider](/api/web3-providers-ws/class/WebSocketProvider) +- An instance of [IpcProvider](/api/web3-providers-ipc/class/IpcProvider) +- A string containing string url for `http`/`https`, `ws`/`wss`, or `ipc` protocol. And when a string is passed, an instance of the compatible class above will be created accordingly. ex. WebSocketProvider instance will be created for string containing `ws` or `ws`. And you access this instance by calling `web3.provider` to read the provider and possibly register an event listener. +- Any provider object that adhere to [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193). And it has been tested with Ganache provider, Hardhat provider, and Incubed (IN3) as a provider. + +For both [WebSocketProvider](/api/web3-providers-ws/class/WebSocketProvider) and [IpcProvider](/api/web3-providers-ipc/class/IpcProvider) the user can listen to emitted events. More on this is at [Providers Events Listening](events_listening). + +:::tip +The passed provider can be either type `string` or one of the [`SupportedProviders`](/api/web3-core#SupportedProviders). And if it is passed as a string, then internally the compatible provider object will be created and used. +::: + +## Providers Priorities + +There are multiple ways to set the provider. + +```ts title='Setting a provider' +web3.setProvider(myProvider); +web3.eth.setProvider(myProvider); +web3.Contract.setProvider(myProvider); +contractInstance.setProvider(myProvider); +``` + +The key rule for setting provider is as follows: + +1. Any provider set on the higher level will be applied to all lower levels. e.g. Any provider set using `web3.setProvider` will also be applied to `web3.eth` object. +2. For contracts `web3.Contract.setProvider` can be used to set provider for **all instances** of contracts created by `web3.eth.Contract`. + +--- + +## Examples + +### Local Geth Node + +```ts +const Web3 = require('web3'); +const web3 = new Web3('http://localhost:8545'); +// or +const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); + +// change provider +web3.setProvider('ws://localhost:8546'); +// or +web3.setProvider(new Web3.providers.WebsocketProvider('ws://localhost:8546')); + +// Using the IPC provider in node.js +const net = require('net'); +const web3 = new Web3('/Users/myuser/Library/Ethereum/geth.ipc', net); // mac os path +// or +const web3 = new Web3( + new Web3.providers.IpcProvider('/Users/myuser/Library/Ethereum/geth.ipc', net), +); // mac os path +// on windows the path is: "\\\\.\\pipe\\geth.ipc" +// on linux the path is: "/users/myuser/.ethereum/geth.ipc" +``` + +### Remote Node Provider + +```ts +// Using a remote node provider, like Alchemy (https://www.alchemyapi.io/supernode), is simple. +const Web3 = require('web3'); +const web3 = new Web3('https://eth-mainnet.alchemyapi.io/v2/your-api-key'); +``` + +### Injected providers + +As stated above, the injected provider should be in compliance with [EIP-1193](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md). And it is tested with Ganache provider, Hardhat provider, and Incubed (IN3) as a provider. + +The web3.js 4.x Provider specifications are defined in [web3 base provider](https://github.com/ChainSafe/web3.js/blob/4.x/packages/web3-types/src/web3_base_provider.ts) for Injected Providers. + +```ts +const Web3 = require('web3'); +// Using an EIP1193 provider like MetaMask can be injected + +if (window.ethereum) { + // Check if ethereum object exists + await window.ethereum.request(); + window.web3 = new Web3(window.ethereum); // inject provider +} +``` + +### Provider Options + +There are differences in the objects that could be passed in the Provider constructors. + +#### HttpProvider + +The options is of type `HttpProviderOptions`, which is an object with a single key named `providerOptions` and its value is an object of type `RequestInit`. +Regarding `RequestInit` see [microsoft's github](https://microsoft.github.io/PowerBI-JavaScript/interfaces/_node_modules_typedoc_node_modules_typescript_lib_lib_dom_d_.requestinit.html). + +For example: + +```ts +const httpOptions = { + providerOptions: { + body: undefined, + cache: 'force-cache', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + integrity: 'foo', + keepalive: true, + method: 'GET', + mode: 'same-origin', + redirect: 'error', + referrer: 'foo', + referrerPolicy: 'same-origin', + signal: undefined, + window: undefined, + } as RequestInit, +}; +``` + +#### WebSocketProvider + +The options object is of type `ClientRequestArgs` or of `ClientOptions`. See [here](https://microsoft.github.io/PowerBI-JavaScript/interfaces/_node_modules__types_node_http_d_._http_.clientrequestargs.html) for `ClientRequestArgs` and [here](https://github.com/websockets/ws) for `ClientOptions`. + +The second option parameter can be given regarding reconnecting. And here is its type: + +```ts +type ReconnectOptions = { + autoReconnect: boolean; + delay: number; + maxAttempts: number; +}; +``` + +:::info +Here is how to catch the error if max attempts reached when the auto reconnecting: + +```ts +provider.on('error', errorMessage => { + if (errorMessage.startsWith('Max connection attempts exceeded')) { + // the `errorMessage` will be `Max connection attempts exceeded (${maxAttempts})` + // the `maxAttempts` is equal to the provided value by the user or the default `5`. + } +}); +``` + +::: + +##### Options example + +Below is an example for the passed options: + +```ts +let clientOptions: ClientOptions = { + // Useful for credentialed urls, e.g: ws://username:password@localhost:8546 + headers: { + authorization: 'Basic username:password', + }, + maxPayload: 100000000, +}; + +const reconnectOptions: ReconnectOptions = { + autoReconnect: true, + delay: 5000, + maxAttempts: 5, +}; +``` + +##### Error message for reconnect attempts + +The error message (not wrapped in an Error object) for the max reconnect attempts, will contain the value of the variable `maxAttempts` as follows: + +`` `Max connection attempts exceeded (${maxAttempts})` `` + +And here is how to catch the error, if max attempts reached when there is auto reconnecting: + +```ts +provider.on('error', errorMessage => { + if (errorMessage.startsWith('Max connection attempts exceeded')) { + // the `errorMessage` will be `Max connection attempts exceeded (${maxAttempts})` + // the `maxAttempts` is equal to the provided value by the user or the default `5`. + } +}); +``` diff --git a/docs/docs/guides/web3_tree_shaking_support_guide/index.md b/docs/docs/guides/web3_tree_shaking_support_guide/index.md index 64835249d00..893af66f7fb 100644 --- a/docs/docs/guides/web3_tree_shaking_support_guide/index.md +++ b/docs/docs/guides/web3_tree_shaking_support_guide/index.md @@ -1,5 +1,5 @@ --- -sidebar_position: 2 +sidebar_position: 3 sidebar_label: web3 Tree Shaking Guide --- diff --git a/packages/web3-providers-ipc/src/index.ts b/packages/web3-providers-ipc/src/index.ts index 67a3afb353f..7b4d8cae1f7 100644 --- a/packages/web3-providers-ipc/src/index.ts +++ b/packages/web3-providers-ipc/src/index.ts @@ -27,9 +27,6 @@ import { } from 'web3-types'; import { existsSync } from 'fs'; -// todo had to ignore, introduce error in doc generation,see why/better solution -/** @ignore */ - export default class IpcProvider extends SocketProvider< Buffer | string, CloseEvent, @@ -103,6 +100,7 @@ export default class IpcProvider exte this._socketConnection?.removeAllListeners('end'); this._socketConnection?.removeAllListeners('close'); this._socketConnection?.removeAllListeners('data'); + // note: we intentionally keep the error event listener to be able to emit it in case an error happens when closing the connection } protected _onCloseEvent(event: CloseEvent): void { diff --git a/packages/web3-providers-ws/src/index.ts b/packages/web3-providers-ws/src/index.ts index 8740ba4ea2a..e5352aba929 100644 --- a/packages/web3-providers-ws/src/index.ts +++ b/packages/web3-providers-ws/src/index.ts @@ -28,14 +28,14 @@ import { isNullish, SocketProvider } from 'web3-utils'; import { ConnectionNotOpenError } from 'web3-errors'; export { ClientRequestArgs } from 'http'; -// todo had to ignore, introduce error in doc generation,see why/better solution -/** @ignore */ + export { ClientOptions } from 'isomorphic-ws'; export default class WebSocketProvider< API extends Web3APISpec = EthExecutionAPI, > extends SocketProvider { protected readonly _providerOptions?: ClientOptions | ClientRequestArgs; + protected _socketConnection?: WebSocket; // eslint-disable-next-line class-methods-use-this @@ -59,6 +59,7 @@ export default class WebSocketProvider< } return 'disconnected'; } + protected _openSocketConnection() { this._socketConnection = new WebSocket( this._socketPath, diff --git a/packages/web3-utils/src/socket_provider.ts b/packages/web3-utils/src/socket_provider.ts index 7c5fcda3287..827228c549b 100644 --- a/packages/web3-utils/src/socket_provider.ts +++ b/packages/web3-utils/src/socket_provider.ts @@ -201,7 +201,7 @@ export abstract class SocketProvider< /** * Removes a listener for the specified event type. * @param type - The event type to remove the listener for - * @param callback - The callback to be exetuted + * @param callback - The callback to be executed */ public removeListener(type: EventType, callback: Web3ProviderEventCallback): void { this._eventEmitter.removeListener(type, callback);