Web3 ProviderEngine
raw JSON →Web3 ProviderEngine is a JavaScript library designed for composing custom Ethereum provider objects using a stack of middleware-like modules called 'sub-providers'. It enables fine-grained control over RPC requests, facilitating features like caching and custom transaction signing workflows. Originally developed for MetaMask, this package has been officially deprecated since version 17.0.0, released in March 2023. Development has largely ceased, and the current stable version is 17.0.1. Users are strongly advised to migrate to modern alternatives such as `@metamask/json-rpc-engine`, `@metamask/eth-json-rpc-middleware`, and `@metamask/eth-json-rpc-provider`. Its key differentiator was its highly modular architecture, allowing developers to assemble a customized provider pipeline for diverse DApp requirements, acting as a 'zero-client' that processes local requests and passes data lookups to a data source.
Common errors
error TypeError: ProviderEngine is not a constructor ↓
require syntax: const ProviderEngine = require('web3-provider-engine');. For ESM compatibility, use the import * as syntax: import * as ProviderEngine from 'web3-provider-engine';. error Error: Invalid JSON RPC response: {"jsonrpc":"2.0","error":{"code":-32003,"message":"project ID is required"}} ↓
rpcUrl configuration for RpcSubprovider, e.g., rpcUrl: 'https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID'. error Node.js version must be ^16.20 || ^18.16 || >=20 ↓
engines field. Warnings
breaking This package has been officially deprecated since version 17.0.0 (March 2023). It is no longer actively maintained. All users are strongly advised to migrate to the recommended `@metamask/json-rpc-engine` and related packages for continued support, security updates, and new features. ↓
breaking Version 17.0.0 increased the minimum required Node.js version to 16. Applications running on older Node.js versions (e.g., 14 or lower) will encounter runtime errors or fail to install. ↓
breaking Version 16.0.0 introduced a breaking change where the Infura provider (used via `RpcSubprovider`) now explicitly requires an API key for the Infura V3 API. Attempts to use Infura without a valid API key will result in RPC errors. ↓
gotcha Multiple security vulnerabilities related to the `request` dependency were addressed in versions 16.0.6 and 16.0.7 by replacing it with the patched `@cypress/request`. Older versions may be exposed to known vulnerabilities. ↓
gotcha A security vulnerability discovered in the `cross-fetch` dependency was patched in version 16.0.4. Applications using older versions could be at risk. ↓
Install
npm install web3-provider-engine yarn add web3-provider-engine pnpm add web3-provider-engine Imports
- ProviderEngine wrong
import { ProviderEngine } from 'web3-provider-engine';correctimport * as ProviderEngine from 'web3-provider-engine'; // Or for CommonJS: const ProviderEngine = require('web3-provider-engine'); - RpcSubprovider wrong
import { RpcSubprovider } from 'web3-provider-engine/subproviders/rpc';correctimport * as RpcSubprovider from 'web3-provider-engine/subproviders/rpc'; // Or for CommonJS: const RpcSubprovider = require('web3-provider-engine/subproviders/rpc.js'); - HookedWalletSubprovider wrong
import HookedWalletSubprovider from 'web3-provider-engine/subproviders/hooked-wallet';correctimport * as HookedWalletSubprovider from 'web3-provider-engine/subproviders/hooked-wallet'; // Or for CommonJS: const HookedWalletSubprovider = require('web3-provider-engine/subproviders/hooked-wallet.js');
Quickstart
const ProviderEngine = require('web3-provider-engine');
const CacheSubprovider = require('web3-provider-engine/subproviders/cache.js');
const FixtureSubprovider = require('web3-provider-engine/subproviders/fixture.js');
const FilterSubprovider = require('web3-provider-engine/subproviders/filters.js');
const VmSubprovider = require('web3-provider-engine/subproviders/vm.js');
const HookedWalletSubprovider = require('web3-provider-engine/subproviders/hooked-wallet.js');
const NonceSubprovider = require('web3-provider-engine/subproviders/nonce-tracker.js');
const RpcSubprovider = require('web3-provider-engine/subproviders/rpc.js');
const Web3 = require('web3'); // Ensure web3 is installed: npm install web3
// Create a new ProviderEngine instance
var engine = new ProviderEngine();
// Connect it to a Web3.js instance
var web3 = new Web3(engine);
// Add various subproviders to the engine stack:
// 1. Static results for common RPC methods
engine.addProvider(new FixtureSubprovider({
web3_clientVersion: 'ProviderEngine/v0.0.0/javascript',
net_listening: true,
eth_hashrate: '0x00',
eth_mining: false,
eth_syncing: true,
}));
// 2. Caching layer for RPC results
engine.addProvider(new CacheSubprovider());
// 3. Filter handling for log and block subscriptions
engine.addProvider(new FilterSubprovider());
// 4. Nonce tracking for transaction management
engine.addProvider(new NonceSubprovider());
// 5. Ethereum Virtual Machine (VM) for local execution
engine.addProvider(new VmSubprovider());
// 6. Hooked wallet for identity management and transaction signing
engine.addProvider(new HookedWalletSubprovider({
getAccounts: function(cb){ console.log('getAccounts called'); cb(null, ['0x9E75379dE05C552E5C9A367E9FfF1B9C7e5A83D0']); }, // Placeholder account
approveTransaction: function(cb){ console.log('approveTransaction called'); cb(null, true); }, // Auto-approve for demo
signTransaction: function(cb){ console.log('signTransaction called'); cb(null, '0xf86180808094000000000000000000000000000000000000000080801ba0483c6d860d843825a0a4c0384737d9953835032a76f23e74c1067e812d4d8cae6a053c8c7344933a3889151c76c0e5272a7281c7e949d212a4507119e075c12891'); }, // Placeholder signed tx
}));
// 7. RPC data source (Infura) - requires an API key since v16.0.0
engine.addProvider(new RpcSubprovider({
rpcUrl: 'https://mainnet.infura.io/v3/' + (process.env.INFURA_API_KEY ?? ''), // Replace with your Infura project ID
}));
// Set up event listeners
engine.on('block', function(block){
console.log('================================');
console.log('BLOCK CHANGED:', '#'+block.number.toString('hex'), '0x'+block.hash.toString('hex'));
console.log('================================');
});
// Handle network connectivity errors
engine.on('error', function(err){
console.error('ProviderEngine error:', err.stack);
});
// Start the ProviderEngine
engine.start();
// Example usage: Make a simple request using web3.js
web3.eth.getChainId().then(chainId => {
console.log('Successfully connected to Chain ID:', chainId);
}).catch(error => {
console.error('Failed to get Chain ID:', error);
});
// Keep the process alive for a short period to observe block events
// In a real application, you would manage engine lifecycle based on usage.
setTimeout(() => {
console.log('Stopping ProviderEngine after 30 seconds.');
engine.stop();
}, 30000);