Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation for catching provider events #5886

Merged
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,78 +4,11 @@ sidebar_position: 2
sidebar_label: web3.providers
---

There are multiple ways to set the provider.
Muhammad-Altabba marked this conversation as resolved.
Show resolved Hide resolved
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
### Provider Options Changes

```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

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

Expand Down Expand Up @@ -147,7 +80,7 @@ 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:

Expand All @@ -172,12 +105,9 @@ 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 = {
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/guides/web3_plugin_guide/index.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 1
sidebar_position: 2
sidebar_label: 'web3 Plugins'
---

Expand Down
61 changes: 61 additions & 0 deletions docs/docs/guides/web3_providers_guide/events_listening.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
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',()=>{
// ...
})

avkos marked this conversation as resolved.
Show resolved Hide resolved
// ...

// 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)
173 changes: 173 additions & 0 deletions docs/docs/guides/web3_providers_guide/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
---
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:
luu-alex marked this conversation as resolved.
Show resolved Hide resolved

- 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
Muhammad-Altabba marked this conversation as resolved.
Show resolved Hide resolved

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
export type ReconnectOptions = {
autoReconnect: boolean;
delay: number;
maxAttempts: number;
};
```

For example:

```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,
Muhammad-Altabba marked this conversation as resolved.
Show resolved Hide resolved
};
```
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 2
sidebar_position: 3
sidebar_label: web3 Tree Shaking Guide
---

Expand Down
7 changes: 4 additions & 3 deletions packages/web3-providers-ipc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<API extends Web3APISpec = EthExecutionAPI> extends SocketProvider<
Buffer | string,
CloseEvent,
Expand All @@ -38,6 +35,9 @@ export default class IpcProvider<API extends Web3APISpec = EthExecutionAPI> exte
> {
// Message handlers. Due to bounding of `this` and removing the listeners we have to keep it's reference.
protected _socketConnection?: Socket;
public get SocketConnection() {
Muhammad-Altabba marked this conversation as resolved.
Show resolved Hide resolved
return this._socketConnection;
}

public getStatus(): Web3ProviderStatus {
if (this._socketConnection?.connecting) {
Expand Down Expand Up @@ -103,6 +103,7 @@ export default class IpcProvider<API extends Web3APISpec = EthExecutionAPI> 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 {
Expand Down
2 changes: 2 additions & 0 deletions packages/web3-providers-ipc/test/unit/ipc_provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

import { Socket } from 'net';
import * as fs from 'fs';
import { ConnectionError, InvalidClientError } from 'web3-errors';
import IpcProvider from '../../src/index';
Expand All @@ -34,6 +35,7 @@ describe('IpcProvider', () => {
it('should construct the instance of the provider', () => {
const provider = new IpcProvider(socketPath);
expect(provider).toBeInstanceOf(IpcProvider);
expect(provider.SocketConnection).toBeInstanceOf(Socket);
});

it('should try to connect', () => {
Expand Down
Loading