Ethereum JSON-RPC Middleware for json-rpc-engine
raw JSON →eth-json-rpc-middleware provides a collection of Ethereum-specific middleware functions designed to integrate with `@metamask/json-rpc-engine`. It facilitates common RPC patterns like block tracking, transaction handling, and EIP-standard implementations (e.g., EIP-5792, EIP-7715). Maintained by MetaMask, this library is actively developed, evidenced by its frequent major releases and updates to support the latest Ethereum specifications. The current stable version is v20.0.0, which notably introduced separate CommonJS and ESM distributions. Its key differentiators include tight integration within the MetaMask ecosystem and robust support for advanced wallet and RPC interactions, making it a foundational component for building dApps and Ethereum tooling.
Common errors
error ERR_PACKAGE_PATH_NOT_EXPORTED ↓
import { createSomeMiddleware } from 'eth-json-rpc-middleware';). error TypeError: createFetchMiddleware is not a function ↓
createFetchMiddleware is called with an object containing an rpcService property, which itself has a provider.request method, as required since v18.0.0. error Error: PollingBlockTracker - encountered an error while attempting to update latest block ↓
getLatestBlock might reject on errors now, which could be an upstream issue with your RPC provider or network configuration. Warnings
breaking Version 20.0.0 introduced separate CommonJS and ESM distributions using the `exports` field in `package.json`. This breaks previously valid direct imports (e.g., from `dist/`) and may require updating `require()` statements to named imports. ↓
breaking The `createFetchMiddleware` signature changed significantly in v18.0.0. It no longer accepts `fetch`, `btoa`, `rpcUrl`, or `originHttpHeaderKey` directly. Instead, it now strictly requires an `rpcService` object, typically sourced from `@metamask/network-controller`. ↓
breaking Version 17.0.0 updated support for EIP-5792 to version 2.0.0, introducing changes like the `atomicRequired` property, making `from` optional in `SendCallsStruct`, and modifying error codes. This may require updates to dApps implementing EIP-5792. ↓
gotcha Prior to v16.0.1, `fetch` middleware could treat non-standard JSON-RPC error responses (those with an `error` field but unexpected additional properties) as successful. This could lead to incorrect error handling or silent failures. ↓
gotcha Changes in `PollingBlockTracker.getLatestBlock()` behavior in v19.0.1 and v17.0.1 (regarding `useCache` and error rejection vs. hanging) could affect dApps relying on specific block tracking update mechanisms. ↓
Install
npm install eth-json-rpc-middleware yarn add eth-json-rpc-middleware pnpm add eth-json-rpc-middleware Imports
- createFetchMiddleware wrong
const createFetchMiddleware = require('eth-json-rpc-middleware').createFetchMiddleware;correctimport { createFetchMiddleware } from 'eth-json-rpc-middleware'; - createBlockRefMiddleware wrong
const createBlockRefMiddleware = require('eth-json-rpc-middleware/dist/block-ref');correctimport { createBlockRefMiddleware } from 'eth-json-rpc-middleware'; - createWalletMiddleware
import { createWalletMiddleware } from 'eth-json-rpc-middleware'; - createChainIdMiddleware
import { createChainIdMiddleware } from 'eth-json-rpc-middleware';
Quickstart
import { JsonRpcEngine } from 'json-rpc-engine';
import { createFetchMiddleware, createBlockRefMiddleware, createChainIdMiddleware } from 'eth-json-rpc-middleware';
// Mock RPC service for createFetchMiddleware (required since v18.0.0)
const mockRpcService = {
provider: {
request: async ({ method, params }) => {
console.log(`Mock RPC request: ${method} ${JSON.stringify(params)}`);
// In a real application, this would forward to an actual RPC endpoint
// using a provider from @metamask/network-controller or similar.
if (method === 'eth_chainId') return '0x1'; // Mainnet
if (method === 'eth_blockNumber') return '0x' + (123456789).toString(16);
if (method === 'eth_sendRawTransaction') return '0xmockTxHash';
return null; // Handle other methods as needed
},
},
// Other properties like 'currentChainId' might be expected by some versions/integrations
};
async function setupAndUseRpcEngine() {
const engine = new JsonRpcEngine();
// Add middleware
engine.add(createBlockRefMiddleware());
engine.add(createChainIdMiddleware('0x1')); // Example: force chainId to Ethereum Mainnet
engine.add(createFetchMiddleware({ rpcService: mockRpcService }));
// Example request
try {
const response = await engine.handle({ id: 1, jsonrpc: '2.0', method: 'eth_blockNumber' });
console.log('eth_blockNumber response:', response.result);
const chainIdResponse = await engine.handle({ id: 2, jsonrpc: '2.0', method: 'eth_chainId' });
console.log('eth_chainId response:', chainIdResponse.result);
// Example of a request that might pass through the fetch middleware
const sendTxResponse = await engine.handle({
id: 3,
jsonrpc: '2.0',
method: 'eth_sendRawTransaction',
params: ['0xmockrawtransactiondata'],
});
console.log('eth_sendRawTransaction response:', sendTxResponse.result);
} catch (error) {
console.error('RPC Error:', error);
}
}
setupAndUseRpcEngine();