Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Commit e2a64f8

Browse files
Update the Plugin documentation (#6165)
* exported few more basic classes from web3 package * update plugin developer guid * update plugin users guid * updated CHANGELOG.md of the web3 package
1 parent 5d37d01 commit e2a64f8

File tree

4 files changed

+96
-164
lines changed

4 files changed

+96
-164
lines changed

docs/docs/guides/web3_plugin_guide/plugin_authors.md

Lines changed: 64 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,26 @@ To provide type safety and IntelliSense for your plugin users, please refer to t
1313

1414
## Plugin Dependencies
1515

16-
At the minimum, your plugin should depend on the `4.x` version of `web3-core`. This will allow your plugin class to extend the provided `Web3PluginBase` abstract class. However, `web3-core` shouldn't be listed as a regular dependency, instead it should be listed in your plugin's `package.json` as a [peer dependency](https://nodejs.org/en/blog/npm/peer-dependencies/):
16+
At the minimum, your plugin should depend on the `4.x` version of `web3`. This will allow your plugin class to extend the provided `Web3PluginBase` abstract class. However, `web3` shouldn't be listed as a regular dependency, instead it should be listed in your plugin's `package.json` as a [peer dependency](https://nodejs.org/en/blog/npm/peer-dependencies/):
1717

1818
```json
1919
{
2020
"name": "web3-plugin-custom-rpc-methods",
2121
"version": "0.1.0",
2222
"peerDependencies": {
23-
"web3-core": ">= 4.0.1-alpha.0 < 5"
23+
"web3": ">= 4.0.1 < 5"
2424
}
2525
}
2626
```
2727

28-
When your users install your plugin, this will allow the package manager to make use of the user installed `web3-core` if available and if the version satisfies the version constraints instead of installing it's own version of `web3-core`.
28+
When your users install your plugin, this will allow the package manager to make use of the user installed `web3` if available and if the version satisfies the version constraints instead of installing it's own version of `web3`.
2929

3030
## Extending `Web3PluginBase`
3131

3232
Your plugin class should `extend` the `Web3PluginBase` abstract class. This class `extends` [Web3Context](/api/web3-core/class/Web3Context) and when the user registers your plugin with a class, your plugin's `Web3Context` will point to the module's `Web3Context` giving your plugin access to things such as user configured [requestManager](/api/web3-core/class/Web3Context#requestManager) and [accountProvider](/api/web3-core/class/Web3Context#accountProvider).
3333

3434
```typescript
35-
import { Web3PluginBase } from 'web3-core';
35+
import { Web3PluginBase } from 'web3';
3636

3737
export class CustomRpcMethodsPlugin extends Web3PluginBase { ... }
3838
```
@@ -42,20 +42,20 @@ export class CustomRpcMethodsPlugin extends Web3PluginBase { ... }
4242
In addition to `Web3PluginBase`, you can choose to extend `Web3EthPluginBase` which will provide the [Ethereum JSON RPC API interface](/api/web3-types#EthExecutionAPI), which packages such as `Web3Eth` use, as a generic to your plugin's `requestManager`, giving it type support for the [Ethereum JSON RPC spec](https://ethereum.github.io/execution-apis/api-documentation/). This would be the recommended approach if your plugin makes Ethereum JSON RPC calls directly to a provider using web3's provided `requestManager`.
4343

4444
```typescript
45-
import { Web3EthPluginBase } from 'web3-core';
45+
import { Web3EthPluginBase } from 'web3';
4646

4747
export class CustomRpcMethodsPlugin extends Web3EthPluginBase { ... }
4848
```
4949

5050
### `pluginNamespace`
5151

52-
After extending the `Web3PluginBase` class, your plugin will need a `public` `pluginNamespace` property that configures how your plugin will be accessed on the class your plugin was registered with. In the following example, the `pluginNamespace` is set to `customRpcMethods`, so when the user registers the plugin they will access your plugin as follows:
52+
After extending the `Web3PluginBase` class, your plugin will need a `public` `pluginNamespace` property that configures how your plugin will be accessed on the class, which your plugin was registered with. In the following example, the `pluginNamespace` is set to `customRpcMethods`, so when the user registers the plugin they will access your plugin as follows:
5353

5454
The following represents your plugin code:
5555

5656
```typescript
5757
// custom_rpc_methods_plugin.ts
58-
import { Web3PluginBase } from 'web3-core';
58+
import { Web3PluginBase } from 'web3';
5959

6060
export class CustomRpcMethodsPlugin extends Web3PluginBase {
6161
public pluginNamespace = 'customRpcMethods';
@@ -70,7 +70,7 @@ The following represents the plugin user's code:
7070

7171
```typescript
7272
// registering_a_plugin.ts
73-
import { Web3Context } from 'web3-core';
73+
import { Web3Context } from 'web3';
7474

7575
import { CustomRpcMethodsPlugin } from './custom_rpc_methods_plugin';
7676

@@ -85,7 +85,7 @@ await web3Context.customRpcMethods.someMethod();
8585
Below is an example of `CustomRpcMethodsPlugin` making use of `this.requestManager` which will have access to an Ethereum provider if one was configured by the user. In the event that no `provider` was set by the user, the below code will throw a [ProviderError](/api/web3-errors/class/ProviderError) if `customRpcMethod` was to be called:
8686

8787
```typescript
88-
import { Web3PluginBase } from 'web3-core';
88+
import { Web3PluginBase } from 'web3';
8989

9090
export class CustomRpcMethodsPlugin extends Web3PluginBase {
9191
public pluginNamespace = 'customRpcMethods';
@@ -103,7 +103,7 @@ Below depicts a plugin user's code that does not configure an Ethereum provider,
103103

104104
```typescript
105105
// registering_a_plugin.ts
106-
import { Web3Context } from 'web3-core';
106+
import { Web3Context } from 'web3';
107107

108108
import { CustomRpcMethodsPlugin } from './custom_rpc_methods_plugin';
109109

@@ -126,7 +126,7 @@ ProviderError: Provider not available. Use `.setProvider` or `.provider=` to ini
126126
If needed, you can provide an API type (that follows the [Web3ApiSpec](/api/web3-types#Web3APISpec) pattern) as a generic to `Web3PluginBase` that will add type hinting to the `requestManager` when developing your plugin. In the below code, this is the `CustomRpcApi` type that's being passed as `Web3PluginBase<CustomRpcApi>`
127127

128128
```typescript
129-
import { Web3PluginBase } from 'web3-core';
129+
import { Web3PluginBase } from 'web3';
130130

131131
type CustomRpcApi = {
132132
custom_rpc_method_with_parameters: (parameter1: string, parameter2: number) => string;
@@ -153,11 +153,7 @@ There currently exists [an issue](https://github.com/web3/web3.js/issues/5492) w
153153
A workaround for this issue is available, below is an example of it:
154154

155155
```typescript
156-
import { Web3Context, Web3PluginBase } from 'web3-core';
157-
import { ContractAbi } from 'web3-eth-abi';
158-
import Contract from 'web3-eth-contract';
159-
import { Address, DataFormat, DEFAULT_RETURN_FORMAT } from 'web3-types';
160-
import { format } from 'web3-utils';
156+
import { Contract, ContractAbi, Web3Context, Web3PluginBase, types, utils } from 'web3';
161157

162158
import { ERC20TokenAbi } from './ERC20Token';
163159

@@ -166,7 +162,7 @@ export class ContractMethodWrappersPlugin extends Web3PluginBase {
166162

167163
private readonly _contract: Contract<typeof ERC20TokenAbi>;
168164

169-
public constructor(abi: ContractAbi, address: Address) {
165+
public constructor(abi: ContractAbi, address: types.Address) {
170166
super();
171167
this._contract = new Contract(abi, address);
172168
}
@@ -185,14 +181,14 @@ export class ContractMethodWrappersPlugin extends Web3PluginBase {
185181
this._contract.link(parentContext);
186182
}
187183

188-
public async getFormattedBalance<ReturnFormat extends DataFormat>(
189-
address: Address,
184+
public async getFormattedBalance<ReturnFormat extends types.DataFormat>(
185+
address: types.Address,
190186
returnFormat?: ReturnFormat,
191187
) {
192-
return format(
188+
return utils.format(
193189
{ eth: 'unit' },
194190
await this._contract.methods.balanceOf(address).call(),
195-
returnFormat ?? DEFAULT_RETURN_FORMAT,
191+
returnFormat ?? types.DEFAULT_RETURN_FORMAT,
196192
);
197193
}
198194
}
@@ -214,22 +210,17 @@ public link(parentContext: Web3Context) {
214210

215211
## Setting Up Module Augmentation
216212

217-
In order to provide type safety and IntelliSense for your plugin when it's registered by the user, you must [augment](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation) the `Web3Context` module. In simpler terms, you will be making TypeScript aware that you are modifying the interface of `Web3Context`, and any class that extends it, to include the interface of your plugin (i.e. your plugin's added methods, properties, etc.). A good tutorial that further explains the topic can be found [here](https://www.digitalocean.com/community/tutorials/typescript-module-augmentation).
213+
To ensure type safety and enable IntelliSense for your plugin (which still needs to be registered by the user), you must augment the `Web3Context` class inside the `web3` module. In simpler terms, this is to modify the `Web3Context` class, and any inheriting classes, to make your plugin's functionality accessible from within. As a result, your plugin object will be accessible within a namespace of your choice, which will be available within any `Web3Context` object.
218214

219-
### A Quick Disclaimer
215+
For a general understanding of Module Augmentation, you can refer to [this tutorial](https://www.digitalocean.com/community/tutorials/typescript-module-augmentation).
220216

221-
The `registerPlugin` method exists on the `Web3Context` class, so any class that `extends Web3Context` has the ability to add your plugin's additional functionality to its interface. By augmenting `Web3Context` to include your plugin's interface, you're essentially providing a blanket augmentation that adds your plugin's interface to **all** Web3 modules that extend `Web3Context` (i.e. `web3`, `web3-eth`, `web3-eth-contract`, etc.).
217+
### Module Augmentation
222218

223-
:::warning
224-
By augmenting `Web3Context` (and by extension all class interfaces that extend it), your plugin's interface will show up in things like IntelliSense for **all** Web3 modules that extend `Web3Context`, even if your plugin isn't registered - This is something worth making your users aware of, as they'll only be able to use your plugin if they register it with a Web3 class instance using `.registerPlugin`
225-
226-
For context, here is an example of your plugin's interface showing up in IntelliSense even though your plugin hasn't been registered (the code in this example is further explained in the subsequent sections):
219+
When registering a plugin, you're adding additional methods and/or classes to the module's interface and TypeScript needs a little help understanding what's going to be available within the module after the plugin is registered.
227220

228221
```typescript
229222
// custom_rpc_methods_plugin.ts
230-
import { Web3PluginBase } from 'web3-core';
231-
232-
import { Web3Context } from './reexported_web3_context';
223+
import { Web3PluginBase } from 'web3';
233224

234225
export class CustomRpcMethodsPlugin extends Web3PluginBase {
235226
public pluginNamespace = 'customRpcMethods';
@@ -240,148 +231,70 @@ export class CustomRpcMethodsPlugin extends Web3PluginBase {
240231
}
241232

242233
// Module Augmentation
243-
declare module './reexported_web3_context' {
244-
interface Web3Context {
245-
customRpcMethods: CustomRpcMethodsPlugin;
246-
}
247-
}
248-
249-
export { Web3Context };
250-
```
251-
252-
The following represent what your plugin users would see:
253-
254-
![web3 context augmentation](./assets/web3_context_augmentation.png 'web3Context augmentation')
255-
256-
The above screenshot shows IntelliSense thinking `.customRpcMethods.someMethod` is available to call on the instance of `Web3`, even though the plugin user hasn't registered `CustomRpcMethodsPlugin` - running this code would result in an error.
257-
:::
258-
259-
### Re-exporting Web3Context
260-
261-
Currently TypeScript's module augmentation only supports named exports, so the first step in augmenting `Web3Context` is to re-export it as a named export. To do this you're going to create a `reexported_web3_context.ts` file (the name of this file can be whatever you prefer, but for the sake of this guide, it's going to be assumed it's named `reexported_web3_context.ts` and is located within the same directory as the `custom_rpc_methods_plugin.ts` file). The file contents should be as follows:
262-
263-
```typescript
264-
// reexported_web3_context.ts
265-
import { Web3Context } from 'web3-core';
266-
267-
export { Web3Context };
268-
```
269-
270-
### Re-declaring the Module
271-
272-
Now you're going to tell TypeScript that you're interested in re-defining a module's (in this case `reexported_web3_context`) interface. In simpler terms, TypeScript is already aware of what methods and classes exist for each web3.js module, but when registering a plugin, you're adding additional methods and/or classes to the module's interface and TypeScript needs a little help understanding what's going to be available within the module after the plugin is registered.
273-
274-
```typescript
275-
// custom_rpc_methods_plugin.ts
276-
import { Web3PluginBase } from 'web3-core';
277-
278-
// Here the re-exported Web3Context from
279-
// the previous section is being imported
280-
import { Web3Context } from './reexported_web3_context';
281-
282-
export class CustomRpcMethodsPlugin extends Web3PluginBase {
283-
public pluginNamespace = 'customRpcMethods';
284-
285-
public someMethod() {
286-
return 'someValue';
287-
}
288-
}
289-
290-
// Here is the declaration to TypeScript that you are
291-
// augmenting the imported module (i.e. ./reexported_web3_context)
292-
declare module './reexported_web3_context' {...}
293-
```
294-
295-
### Adding Your Plugin's Interface
296-
297-
Now that TypeScript is aware that the interface of the `reexport_web3_context` module is going to be augmented, you can add your plugin's interface. In this case, you're adding the interface of `CustomRpcMethodsPlugin` to the interface of `Web3Context` which is what the **plugin-user** is going to be calling `.registerPlugin` on:
298-
299-
```typescript
300-
// custom_rpc_methods_plugin.ts
301-
import { Web3PluginBase } from 'web3-core';
302-
303-
import { Web3Context } from './reexport_web3_context';
304-
305-
export class CustomRpcMethodsPlugin extends Web3PluginBase {
306-
public pluginNamespace = 'customRpcMethods';
307-
308-
public someMethod() {
309-
return 'someValue';
310-
}
311-
}
312-
313-
declare module './reexported_web3_context.ts' {
234+
declare module 'web3' {
314235
// Here is where you're adding your plugin's
315-
// interface to the interface of Web3Context
236+
// class inside Web3Context class
316237
interface Web3Context {
317238
customRpcMethods: CustomRpcMethodsPlugin;
318239
}
319240
}
320241
```
321242

322-
:::info
323-
The property name (i.e. `pluginNamespace`), `customRpcMethods` in
243+
### Important points to consider
324244

325-
```typescript
326-
{
327-
customRpcMethods: CustomRpcMethodsPlugin;
328-
}
329-
```
245+
1. By augmenting `Web3Context` (and, by extension, all the classes that extend it), your plugin's interface will show up in things like IntelliSense for **all** Web3 modules that extend `Web3Context`, even if your plugin isn't registered.
246+
This is something worth making your users aware of, as they'll only be able to use your plugin if they register it with a Web3 class instance using `.registerPlugin`.
330247

331-
**MUST** be the same as the `pluginNamespace` set by the plugin.
248+
:::warning
332249

333-
```typescript
334-
import { Web3PluginBase } from 'web3-core';
250+
The following represent what your **plugin users** would see, when they use the plugin `CustomRpcMethodsPlugin`, without calling `.registerPlugin`:
335251

336-
export class CustomRpcMethodsPlugin extends Web3PluginBase {
337-
public pluginNamespace = 'customRpcMethods';
252+
![web3 context augmentation](./assets/web3_context_augmentation.png 'Web3Context augmentation')
338253

339-
...
340-
}
341-
```
254+
The above screenshot shows IntelliSense thinking `.customRpcMethods.someMethod` is available to call on the instance of `Web3`, regardless if the plugin user registered or did not register `CustomRpcMethodsPlugin`.
255+
But, the user who does not call `.registerPlugin`, before accessing your plugin, would face an error. And you need to make it clear for them that they need to call `.registerPlugin`, before they can access any plugin functionality.
342256

343-
This is because `.registerPlugin` will use the `pluginNamespace` property provided by the plugin as the property name when it registers the plugin with the class instance you call `.registerPlugin` on:
257+
:::
344258

345-
```typescript
346-
const web3 = new Web3('http://127.0.0.1:8545');
347-
web3.registerPlugin(new CustomRpcMethodsPlugin());
348-
// Now customRpcMethods (i.e. the pluginNamespace) is available
349-
// on the instance of Web3
350-
web3.customRpcMethods;
351-
```
259+
2. The `registerPlugin` method exists on the `Web3Context` class, so any class that `extends Web3Context` has the ability to add your plugin's additional functionality to its interface. So, by augmenting `Web3Context` to include your plugin's interface, you're essentially providing a blanket augmentation that adds your plugin's interface to **all** Web3 modules that extend `Web3Context` (i.e. `web3`, `web3-eth`, `web3-eth-contract`, etc.).
352260

353-
:::
261+
3. The value of the `pluginNamespace`, that we used `customRpcMethods` for it in our sample code, **MUST** have the exact same name at 2 places: The first place is in the augmentation. And the second is the value of the public `pluginNamespace` inside your plugin class.
354262

355-
### Exporting The Augmented Web3Context
263+
So, for example, kindly notice using `customRpcMethods` in the next 2 snippets:
356264

357-
Lastly, you just need to export the augmented `Web3Context` by adding the following after the module re-declaration:
265+
Module Augmentation:
358266

359-
```typescript
360-
export { Web3Context };
361-
```
267+
```typescript
268+
// code written by the plugin **developer**
362269

363-
The full code example is as follows:
270+
declare module 'web3' {
271+
// Here is where you're adding your plugin inside Web3Context
272+
interface Web3Context {
273+
customRpcMethods: CustomRpcMethodsPlugin;
274+
}
275+
}
276+
```
364277

365-
```typescript
366-
// custom_rpc_methods_plugin.ts
367-
import { Web3PluginBase } from 'web3-core';
278+
Your the plugin class:
368279

369-
import { Web3Context } from './custom_rpc_methods_plugin';
280+
```typescript
281+
// code written by the plugin **developer**
370282
371-
export class CustomRpcMethodsPlugin extends Web3PluginBase {
372-
public pluginNamespace = 'customRpcMethods';
283+
export class CustomRpcMethodsPlugin extends Web3PluginBase {
284+
public pluginNamespace = 'customRpcMethods';
373285
374-
public someMethod() {
375-
return 'someValue';
376-
}
377-
}
286+
...
287+
}
288+
```
378289

379-
declare module './custom_rpc_methods_plugin.ts' {
380-
interface Web3Context {
381-
customRpcMethods: CustomRpcMethodsPlugin;
382-
}
383-
}
290+
This is because `.registerPlugin` will use the `pluginNamespace` property provided by the plugin as the property name when it registers the plugin with the class instance that the **plugin user** will call `.registerPlugin` on:
384291

385-
// Here is where you are exporting your augmented Web3Context
386-
export { Web3Context };
387-
```
292+
```typescript
293+
// code written by the plugin **user**
294+
295+
const web3 = new Web3('http://127.0.0.1:8545');
296+
web3.registerPlugin(new CustomRpcMethodsPlugin());
297+
// Now customRpcMethods (i.e. the pluginNamespace) is available
298+
// on the instance of Web3
299+
web3.customRpcMethods;
300+
```

0 commit comments

Comments
 (0)