0
# Middleware
1
2
Request and response processing middleware for customizing Web3 behavior including validation, formatting, signing, filtering, and gas price strategies with composable middleware stack management.
3
4
## Capabilities
5
6
### Base Middleware Classes
7
8
Core middleware infrastructure and base classes.
9
10
```python { .api }
11
class Web3Middleware:
12
def __init__(self, w3: Union[Web3, AsyncWeb3]):
13
"""
14
Initialize middleware with Web3 instance.
15
16
Parameters:
17
- w3: Web3 or AsyncWeb3 instance
18
"""
19
20
def wrap_make_request(self, make_request: MakeRequestFn) -> MakeRequestFn:
21
"""
22
Wrap request function with middleware logic.
23
24
Parameters:
25
- make_request: Original request function
26
27
Returns:
28
Wrapped request function
29
"""
30
31
async def async_wrap_make_request(
32
self,
33
make_request: AsyncMakeRequestFn
34
) -> AsyncMakeRequestFn:
35
"""
36
Wrap async request function with middleware logic.
37
38
Parameters:
39
- make_request: Original async request function
40
41
Returns:
42
Wrapped async request function
43
"""
44
45
Middleware = Callable[[Union[Web3, AsyncWeb3]], Web3Middleware]
46
```
47
48
### Built-in Middleware
49
50
Pre-built middleware for common functionality.
51
52
```python { .api }
53
class AttributeDictMiddleware(Web3Middleware):
54
"""Convert response objects to attribute dictionaries."""
55
56
class BufferedGasEstimateMiddleware(Web3Middleware):
57
"""Buffer gas estimates to avoid repeated calls."""
58
59
class LocalFilterMiddleware(Web3Middleware):
60
"""Handle event filters locally instead of on node."""
61
62
class GasPriceStrategyMiddleware(Web3Middleware):
63
"""Apply gas price strategy to transactions."""
64
65
class ENSNameToAddressMiddleware(Web3Middleware):
66
"""Resolve ENS names to addresses in transactions."""
67
68
class ExtraDataToPOAMiddleware(Web3Middleware):
69
"""Handle Proof of Authority consensus extra data."""
70
71
class PythonicMiddleware(Web3Middleware):
72
"""Convert responses to Pythonic formats."""
73
74
class ValidationMiddleware(Web3Middleware):
75
"""Validate request parameters."""
76
```
77
78
### Middleware Builders
79
80
Factory functions for creating configurable middleware.
81
82
```python { .api }
83
def FormattingMiddlewareBuilder() -> Middleware:
84
"""
85
Create response formatting middleware.
86
87
Returns:
88
Configured formatting middleware
89
"""
90
91
def SignAndSendRawMiddlewareBuilder(
92
private_key_or_account: Union[PrivateKey, LocalAccount]
93
) -> Middleware:
94
"""
95
Create transaction signing middleware.
96
97
Parameters:
98
- private_key_or_account: Private key or account for signing
99
100
Returns:
101
Configured signing middleware
102
"""
103
104
def StalecheckMiddlewareBuilder(
105
allowable_delay: int = 60
106
) -> Middleware:
107
"""
108
Create stale block check middleware.
109
110
Parameters:
111
- allowable_delay: Maximum block age in seconds
112
113
Returns:
114
Configured stalecheck middleware
115
"""
116
```
117
118
### Middleware Management
119
120
Functions for combining and managing middleware stacks.
121
122
```python { .api }
123
def combine_middleware(
124
middleware: Sequence[Middleware],
125
w3: Web3,
126
provider_request_fn: MakeRequestFn
127
) -> Callable[..., RPCResponse]:
128
"""
129
Combine middleware into request processing pipeline.
130
131
Parameters:
132
- middleware: List of middleware functions
133
- w3: Web3 instance
134
- provider_request_fn: Provider's request function
135
136
Returns:
137
Combined request processor
138
"""
139
140
async def async_combine_middleware(
141
middleware: Sequence[Middleware],
142
async_w3: AsyncWeb3,
143
provider_request_fn: AsyncMakeRequestFn
144
) -> Callable[..., Coroutine[Any, Any, RPCResponse]]:
145
"""
146
Combine async middleware into request processing pipeline.
147
148
Parameters:
149
- middleware: List of middleware functions
150
- async_w3: AsyncWeb3 instance
151
- provider_request_fn: Provider's async request function
152
153
Returns:
154
Combined async request processor
155
"""
156
```
157
158
### Middleware Onion
159
160
Middleware stack management interface.
161
162
```python { .api }
163
class MiddlewareOnion:
164
def add(self, middleware: Middleware, name: Optional[str] = None) -> None:
165
"""
166
Add middleware to the top of stack.
167
168
Parameters:
169
- middleware: Middleware function
170
- name: Optional middleware name
171
"""
172
173
def inject(
174
self,
175
middleware: Middleware,
176
name: Optional[str] = None,
177
layer: Optional[int] = None
178
) -> None:
179
"""
180
Inject middleware at specific layer.
181
182
Parameters:
183
- middleware: Middleware function
184
- name: Optional middleware name
185
- layer: Stack position (0 = top)
186
"""
187
188
def remove(self, name: str) -> None:
189
"""
190
Remove middleware by name.
191
192
Parameters:
193
- name: Middleware name to remove
194
"""
195
196
def replace(self, name: str, middleware: Middleware) -> None:
197
"""
198
Replace existing middleware.
199
200
Parameters:
201
- name: Name of middleware to replace
202
- middleware: New middleware function
203
"""
204
205
def clear(self) -> None:
206
"""Remove all middleware from stack."""
207
```
208
209
## Types
210
211
Middleware-related type definitions.
212
213
```python { .api }
214
MakeRequestFn = Callable[[RPCEndpoint, Any], RPCResponse]
215
AsyncMakeRequestFn = Callable[[RPCEndpoint, Any], Coroutine[Any, Any, RPCResponse]]
216
217
class RPCResponse(TypedDict):
218
id: int
219
jsonrpc: str
220
result: Any
221
error: NotRequired[Dict[str, Any]]
222
223
RPCEndpoint = NewType('RPCEndpoint', str)
224
```
225
226
## Usage Examples
227
228
### Basic Middleware Usage
229
230
```python
231
from web3 import Web3
232
from web3.middleware import geth_poa_middleware
233
234
w3 = Web3(Web3.HTTPProvider('http://localhost:8545'))
235
236
# Add Proof of Authority middleware
237
w3.middleware_onion.inject(geth_poa_middleware, layer=0)
238
239
# Verify middleware is active
240
print(f"Middleware stack: {[name for name, _ in w3.middleware_onion]}")
241
```
242
243
### Custom Middleware
244
245
```python
246
from web3 import Web3
247
from web3.types import RPCEndpoint, RPCResponse
248
249
def request_logging_middleware(make_request, w3):
250
"""Log all RPC requests."""
251
def middleware(method: RPCEndpoint, params):
252
print(f"Making request: {method} with params: {params}")
253
response = make_request(method, params)
254
print(f"Response: {response}")
255
return response
256
return middleware
257
258
w3 = Web3(Web3.HTTPProvider('http://localhost:8545'))
259
w3.middleware_onion.add(request_logging_middleware, 'logging')
260
261
# All requests will now be logged
262
balance = w3.eth.get_balance('0x742d35Cc6635C0532925a3b8D5c0d9E3C4B3c8')
263
```
264
265
### Transaction Signing Middleware
266
267
```python
268
from web3 import Web3
269
from web3.middleware import SignAndSendRawMiddlewareBuilder
270
from eth_account import Account
271
272
# Create account for signing
273
account = Account.create()
274
w3 = Web3(Web3.HTTPProvider('http://localhost:8545'))
275
276
# Add signing middleware
277
signing_middleware = SignAndSendRawMiddlewareBuilder(account)
278
w3.middleware_onion.add(signing_middleware, 'signing')
279
280
# Transactions will be automatically signed
281
tx_hash = w3.eth.send_transaction({
282
'from': account.address,
283
'to': '0x742d35Cc6635C0532925a3b8D5c0d9E3C4B3c8',
284
'value': w3.to_wei(1, 'ether'),
285
'gas': 21000
286
})
287
288
print(f"Signed transaction hash: {tx_hash.hex()}")
289
```
290
291
### Gas Price Strategy Middleware
292
293
```python
294
from web3 import Web3
295
from web3.gas_strategies.time_based import construct_time_based_gas_price_strategy
296
297
w3 = Web3(Web3.HTTPProvider('http://localhost:8545'))
298
299
# Create time-based gas price strategy
300
gas_strategy = construct_time_based_gas_price_strategy(
301
max_wait_seconds=60,
302
sample_size=5
303
)
304
305
# Set gas price strategy
306
w3.eth.set_gas_price_strategy(gas_strategy)
307
308
# Gas prices will be determined by strategy
309
transaction = {
310
'from': w3.eth.accounts[0],
311
'to': '0x742d35Cc6635C0532925a3b8D5c0d9E3C4B3c8',
312
'value': w3.to_wei(1, 'ether'),
313
'gas': 21000
314
# gasPrice automatically set by strategy
315
}
316
317
tx_hash = w3.eth.send_transaction(transaction)
318
```
319
320
### Stale Block Check Middleware
321
322
```python
323
from web3 import Web3
324
from web3.middleware import StalecheckMiddlewareBuilder
325
326
w3 = Web3(Web3.HTTPProvider('http://localhost:8545'))
327
328
# Add stale block check (fail if block is >60 seconds old)
329
stalecheck_middleware = StalecheckMiddlewareBuilder(allowable_delay=60)
330
w3.middleware_onion.add(stalecheck_middleware, 'stalecheck')
331
332
# Requests will fail if connected to stale node
333
try:
334
balance = w3.eth.get_balance('0x742d35Cc6635C0532925a3b8D5c0d9E3C4B3c8')
335
print(f"Balance: {balance}")
336
except Exception as e:
337
print(f"Node appears to be stale: {e}")
338
```
339
340
### Response Formatting Middleware
341
342
```python
343
from web3 import Web3
344
from web3.middleware import FormattingMiddlewareBuilder
345
346
w3 = Web3(Web3.HTTPProvider('http://localhost:8545'))
347
348
# Add response formatting
349
formatting_middleware = FormattingMiddlewareBuilder()
350
w3.middleware_onion.add(formatting_middleware, 'formatting')
351
352
# Responses will be formatted for Python consumption
353
block = w3.eth.get_block('latest')
354
print(f"Block timestamp: {block.timestamp}") # Properly formatted
355
```
356
357
### Middleware Stack Management
358
359
```python
360
from web3 import Web3
361
362
w3 = Web3(Web3.HTTPProvider('http://localhost:8545'))
363
364
# View current middleware stack
365
print("Current middleware:")
366
for name, middleware in w3.middleware_onion:
367
print(f" {name}: {middleware}")
368
369
# Remove specific middleware
370
w3.middleware_onion.remove('validation')
371
372
# Replace middleware
373
new_validation = lambda make_request, w3: make_request
374
w3.middleware_onion.replace('validation', new_validation)
375
376
# Clear all middleware (be careful!)
377
# w3.middleware_onion.clear()
378
```
379
380
### Async Middleware
381
382
```python
383
import asyncio
384
from web3 import AsyncWeb3
385
386
async def async_logging_middleware(make_request, async_w3):
387
"""Async logging middleware."""
388
async def middleware(method, params):
389
print(f"Async request: {method}")
390
response = await make_request(method, params)
391
print(f"Async response received")
392
return response
393
return middleware
394
395
async def main():
396
w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider('http://localhost:8545'))
397
w3.middleware_onion.add(async_logging_middleware, 'async_logging')
398
399
# Middleware will process async requests
400
balance = await w3.eth.get_balance('0x742d35Cc6635C0532925a3b8D5c0d9E3C4B3c8')
401
print(f"Balance: {balance}")
402
403
asyncio.run(main())
404
```
405
406
### Error Handling Middleware
407
408
```python
409
from web3 import Web3
410
from web3.exceptions import Web3Exception
411
412
def error_handling_middleware(make_request, w3):
413
"""Add custom error handling."""
414
def middleware(method, params):
415
try:
416
return make_request(method, params)
417
except Exception as e:
418
print(f"Request failed: {method} - {e}")
419
# Could implement retry logic, fallback providers, etc.
420
raise Web3Exception(f"Enhanced error info: {e}")
421
return middleware
422
423
w3 = Web3(Web3.HTTPProvider('http://localhost:8545'))
424
w3.middleware_onion.add(error_handling_middleware, 'error_handling')
425
```