0
# Provider Integration
1
2
The Web3 provider integration system provides comprehensive support for various blockchain providers with type guards, capability detection, and seamless integration patterns. It abstracts provider differences and enables consistent blockchain communication across different provider types.
3
4
## Capabilities
5
6
### Provider Type Guards
7
8
Utility functions for detecting and validating different provider types with TypeScript type narrowing.
9
10
```typescript { .api }
11
/**
12
* Check if provider is a Web3 provider instance
13
* @template API - JSON-RPC API specification type
14
* @param provider - Provider to check
15
* @returns Type guard indicating if provider is Web3BaseProvider
16
*/
17
function isWeb3Provider<API extends Web3APISpec = Web3APISpec>(
18
provider: unknown
19
): provider is Web3BaseProvider<API>;
20
21
/**
22
* Check if provider is a MetaMask provider instance
23
* @template API - JSON-RPC API specification type
24
* @param provider - Provider to check
25
* @returns Type guard indicating if provider is MetaMaskProvider
26
*/
27
function isMetaMaskProvider<API extends Web3APISpec = Web3APISpec>(
28
provider: unknown
29
): provider is MetaMaskProvider<API>;
30
31
/**
32
* Check if provider is a legacy request provider
33
* @param provider - Provider to check
34
* @returns Type guard indicating if provider is LegacyRequestProvider
35
*/
36
function isLegacyRequestProvider(provider: unknown): provider is LegacyRequestProvider;
37
38
/**
39
* Check if provider is an EIP-1193 compliant provider
40
* @template API - JSON-RPC API specification type
41
* @param provider - Provider to check
42
* @returns Type guard indicating if provider is EIP1193Provider
43
*/
44
function isEIP1193Provider<API extends Web3APISpec = Web3APISpec>(
45
provider: unknown
46
): provider is EIP1193Provider<API>;
47
48
/**
49
* Check if provider is a legacy send provider
50
* @param provider - Provider to check
51
* @returns Type guard indicating if provider is LegacySendProvider
52
*/
53
function isLegacySendProvider(provider: unknown): provider is LegacySendProvider;
54
55
/**
56
* Check if provider is a legacy sendAsync provider
57
* @param provider - Provider to check
58
* @returns Type guard indicating if provider is LegacySendAsyncProvider
59
*/
60
function isLegacySendAsyncProvider(provider: unknown): provider is LegacySendAsyncProvider;
61
62
/**
63
* Check if provider is any supported provider type
64
* @template API - JSON-RPC API specification type
65
* @param provider - Provider to check
66
* @returns Type guard indicating if provider is SupportedProviders
67
*/
68
function isSupportedProvider<API extends Web3APISpec = Web3APISpec>(
69
provider: unknown
70
): provider is SupportedProviders<API>;
71
```
72
73
**Usage Examples:**
74
75
```typescript
76
import {
77
isWeb3Provider,
78
isMetaMaskProvider,
79
isEIP1193Provider,
80
isSupportedProvider,
81
isLegacyRequestProvider
82
} from "web3-core";
83
84
// Type-safe provider detection
85
function handleProvider(provider: unknown) {
86
if (isMetaMaskProvider(provider)) {
87
// TypeScript knows this is MetaMaskProvider
88
console.log("MetaMask detected");
89
console.log("Chain ID:", provider.chainId);
90
console.log("Selected address:", provider.selectedAddress);
91
92
// MetaMask-specific methods available
93
provider.request({ method: "eth_requestAccounts", params: [] });
94
95
} else if (isEIP1193Provider(provider)) {
96
// TypeScript knows this is EIP1193Provider
97
console.log("EIP-1193 provider detected");
98
99
// Standard EIP-1193 interface
100
provider.request({ method: "eth_blockNumber", params: [] });
101
102
} else if (isWeb3Provider(provider)) {
103
// TypeScript knows this is Web3BaseProvider
104
console.log("Web3 provider detected");
105
106
// Web3.js provider interface
107
provider.request({ method: "eth_gasPrice", params: [] }, (error, result) => {
108
if (error) console.error(error);
109
else console.log("Gas price:", result);
110
});
111
112
} else if (isLegacyRequestProvider(provider)) {
113
// TypeScript knows this is LegacyRequestProvider
114
console.log("Legacy request provider detected");
115
116
// Legacy interface
117
provider.request("eth_blockNumber", [], (error, result) => {
118
console.log("Block number:", result);
119
});
120
121
} else if (isSupportedProvider(provider)) {
122
// Any other supported provider type
123
console.log("Supported provider detected");
124
125
} else {
126
console.error("Unsupported provider type");
127
}
128
}
129
130
// Browser provider detection
131
if (typeof window !== 'undefined' && window.ethereum) {
132
handleProvider(window.ethereum);
133
} else {
134
console.log("No browser provider found");
135
}
136
```
137
138
### Subscription Support Detection
139
140
Functions for detecting WebSocket subscription capabilities in providers.
141
142
```typescript { .api }
143
/**
144
* Check if provider supports subscriptions (WebSocket functionality)
145
* @template API - JSON-RPC API specification type
146
* @param provider - Provider to check
147
* @returns Boolean indicating subscription support
148
*/
149
function isSupportSubscriptions<API extends Web3APISpec = Web3APISpec>(
150
provider: unknown
151
): boolean;
152
```
153
154
**Usage Examples:**
155
156
```typescript
157
import { isSupportSubscriptions } from "web3-core";
158
159
// Check subscription support before creating subscription manager
160
function setupBlockchainConnection(provider: unknown) {
161
if (!isSupportedProvider(provider)) {
162
throw new Error("Unsupported provider");
163
}
164
165
const requestManager = new Web3RequestManager(provider);
166
167
if (isSupportSubscriptions(provider)) {
168
console.log("Provider supports subscriptions - setting up WebSocket features");
169
170
// Create subscription manager for real-time updates
171
const subscriptionManager = new Web3SubscriptionManager(
172
requestManager,
173
{
174
newBlockHeaders: NewBlockHeadersSubscription,
175
logs: LogsSubscription,
176
pendingTransactions: PendingTransactionsSubscription
177
}
178
);
179
180
// Subscribe to new blocks
181
subscriptionManager.subscribe("newBlockHeaders").then(subscription => {
182
subscription.on("data", (blockHeader) => {
183
console.log("New block:", blockHeader.number);
184
});
185
});
186
187
return { requestManager, subscriptionManager };
188
189
} else {
190
console.log("Provider doesn't support subscriptions - using polling");
191
192
// Fall back to polling for updates
193
const pollForBlocks = async () => {
194
const blockNumber = await requestManager.send({
195
method: "eth_blockNumber",
196
params: []
197
});
198
console.log("Latest block (polling):", parseInt(blockNumber, 16));
199
};
200
201
setInterval(pollForBlocks, 12000); // Poll every 12 seconds
202
203
return { requestManager };
204
}
205
}
206
207
// Usage with different provider types
208
const httpProvider = "https://eth-mainnet.g.alchemy.com/v2/your-api-key";
209
const wsProvider = "wss://eth-mainnet.ws.alchemyapi.io/v2/your-api-key";
210
211
console.log("HTTP supports subscriptions:", isSupportSubscriptions(httpProvider)); // false
212
console.log("WebSocket supports subscriptions:", isSupportSubscriptions(wsProvider)); // true
213
214
setupBlockchainConnection(wsProvider);
215
```
216
217
### Provider Configuration Patterns
218
219
Common patterns for configuring and working with different provider types.
220
221
**Usage Examples:**
222
223
```typescript
224
// Pattern: Multi-provider setup with fallback
225
function createResilientConnection() {
226
const providers = [
227
"wss://eth-mainnet.ws.alchemyapi.io/v2/your-api-key", // Primary WebSocket
228
"https://eth-mainnet.g.alchemy.com/v2/your-api-key", // Fallback HTTP
229
"https://cloudflare-eth.com", // Public fallback
230
];
231
232
for (const provider of providers) {
233
try {
234
if (isSupportedProvider(provider)) {
235
const requestManager = new Web3RequestManager(provider);
236
237
console.log(`Connected to ${provider}`);
238
console.log(`Subscription support: ${isSupportSubscriptions(provider)}`);
239
240
return {
241
requestManager,
242
hasSubscriptions: isSupportSubscriptions(provider),
243
providerType: typeof provider === 'string' ? 'url' : 'object'
244
};
245
}
246
} catch (error) {
247
console.warn(`Failed to connect to ${provider}:`, error);
248
}
249
}
250
251
throw new Error("No working provider found");
252
}
253
254
// Pattern: Browser provider detection and setup
255
function setupBrowserProvider() {
256
// Check for injected providers
257
if (typeof window === 'undefined') {
258
throw new Error("Not in browser environment");
259
}
260
261
const providers = [
262
{ name: "MetaMask", provider: window.ethereum },
263
{ name: "WalletConnect", provider: (window as any).WalletConnect },
264
{ name: "Coinbase", provider: (window as any).coinbaseWalletExtension }
265
];
266
267
for (const { name, provider } of providers) {
268
if (provider && isSupportedProvider(provider)) {
269
console.log(`${name} provider detected`);
270
271
if (isMetaMaskProvider(provider)) {
272
// MetaMask-specific setup
273
console.log("MetaMask chain ID:", provider.chainId);
274
console.log("MetaMask accounts:", provider.selectedAddress);
275
276
// Listen to MetaMask events
277
provider.on("accountsChanged", (accounts: string[]) => {
278
console.log("Accounts changed:", accounts);
279
});
280
281
provider.on("chainChanged", (chainId: string) => {
282
console.log("Chain changed:", chainId);
283
});
284
285
} else if (isEIP1193Provider(provider)) {
286
// Standard EIP-1193 setup
287
console.log("EIP-1193 provider setup");
288
}
289
290
return new Web3RequestManager(provider);
291
}
292
}
293
294
throw new Error("No supported browser provider found");
295
}
296
297
// Pattern: Provider feature detection
298
function analyzeProvider(provider: unknown) {
299
const analysis = {
300
isSupported: isSupportedProvider(provider),
301
type: 'unknown' as string,
302
features: {
303
subscriptions: false,
304
accounts: false,
305
events: false,
306
batch: false
307
}
308
};
309
310
if (!analysis.isSupported) {
311
return analysis;
312
}
313
314
// Determine provider type
315
if (isMetaMaskProvider(provider)) {
316
analysis.type = 'MetaMask';
317
analysis.features.accounts = true;
318
analysis.features.events = true;
319
} else if (isEIP1193Provider(provider)) {
320
analysis.type = 'EIP-1193';
321
analysis.features.accounts = true;
322
} else if (isWeb3Provider(provider)) {
323
analysis.type = 'Web3';
324
analysis.features.batch = true;
325
} else if (isLegacyRequestProvider(provider)) {
326
analysis.type = 'Legacy Request';
327
} else if (isLegacySendProvider(provider)) {
328
analysis.type = 'Legacy Send';
329
} else if (isLegacySendAsyncProvider(provider)) {
330
analysis.type = 'Legacy SendAsync';
331
}
332
333
// Check subscription support
334
analysis.features.subscriptions = isSupportSubscriptions(provider);
335
336
return analysis;
337
}
338
339
// Usage
340
const analysis = analyzeProvider(window.ethereum);
341
console.log("Provider analysis:", analysis);
342
```
343
344
### Custom Provider Integration
345
346
Patterns for integrating custom or specialized providers.
347
348
**Usage Examples:**
349
350
```typescript
351
// Pattern: Custom provider wrapper
352
class CustomProviderWrapper {
353
constructor(private baseProvider: any) {}
354
355
async request(args: { method: string; params?: any[] }) {
356
// Add authentication headers
357
const headers = {
358
'Authorization': `Bearer ${this.getAuthToken()}`,
359
'X-API-Key': 'your-api-key'
360
};
361
362
// Custom request logic
363
try {
364
const result = await this.baseProvider.request({
365
...args,
366
headers
367
});
368
369
// Custom response processing
370
return this.processResponse(result);
371
} catch (error) {
372
// Custom error handling
373
throw this.processError(error);
374
}
375
}
376
377
private getAuthToken(): string {
378
// Token management logic
379
return localStorage.getItem('auth-token') || '';
380
}
381
382
private processResponse(response: any): any {
383
// Custom response transformation
384
return response;
385
}
386
387
private processError(error: any): Error {
388
// Custom error transformation
389
return new Error(`Custom provider error: ${error.message}`);
390
}
391
}
392
393
// Use custom provider
394
const customProvider = new CustomProviderWrapper(window.ethereum);
395
396
// Verify it's supported
397
if (isSupportedProvider(customProvider)) {
398
const requestManager = new Web3RequestManager(customProvider);
399
} else {
400
console.error("Custom provider is not compatible");
401
}
402
403
// Pattern: Provider proxy for caching
404
class CachingProviderProxy {
405
private cache = new Map<string, { result: any; timestamp: number }>();
406
private cacheTimeout = 30000; // 30 seconds
407
408
constructor(private provider: any) {}
409
410
async request(args: { method: string; params?: any[] }) {
411
const cacheKey = JSON.stringify(args);
412
const cached = this.cache.get(cacheKey);
413
414
// Return cached result if still valid
415
if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
416
console.log("Cache hit for:", args.method);
417
return cached.result;
418
}
419
420
// Make actual request
421
const result = await this.provider.request(args);
422
423
// Cache the result
424
this.cache.set(cacheKey, {
425
result,
426
timestamp: Date.now()
427
});
428
429
return result;
430
}
431
}
432
433
// Pattern: Provider load balancer
434
class LoadBalancingProvider {
435
private currentIndex = 0;
436
437
constructor(private providers: any[]) {}
438
439
async request(args: { method: string; params?: any[] }) {
440
const maxAttempts = this.providers.length;
441
442
for (let attempt = 0; attempt < maxAttempts; attempt++) {
443
const provider = this.providers[this.currentIndex];
444
this.currentIndex = (this.currentIndex + 1) % this.providers.length;
445
446
try {
447
return await provider.request(args);
448
} catch (error) {
449
console.warn(`Provider ${this.currentIndex} failed:`, error);
450
451
if (attempt === maxAttempts - 1) {
452
throw new Error("All providers failed");
453
}
454
}
455
}
456
}
457
}
458
459
// Create load-balanced provider
460
const loadBalancer = new LoadBalancingProvider([
461
"https://eth-mainnet.g.alchemy.com/v2/your-api-key",
462
"https://mainnet.infura.io/v3/your-project-id",
463
"https://cloudflare-eth.com"
464
]);
465
466
if (isSupportedProvider(loadBalancer)) {
467
const requestManager = new Web3RequestManager(loadBalancer);
468
}
469
```
470
471
### Provider Events and Lifecycle
472
473
Handling provider events and managing provider lifecycle changes.
474
475
**Usage Examples:**
476
477
```typescript
478
// Pattern: Provider event handling
479
function setupProviderEvents(provider: unknown) {
480
if (isMetaMaskProvider(provider)) {
481
// MetaMask events
482
provider.on("accountsChanged", (accounts: string[]) => {
483
console.log("Accounts changed:", accounts);
484
if (accounts.length === 0) {
485
console.log("User disconnected");
486
}
487
});
488
489
provider.on("chainChanged", (chainId: string) => {
490
console.log("Chain changed to:", parseInt(chainId, 16));
491
// Reload application or update context
492
window.location.reload();
493
});
494
495
provider.on("disconnect", (error: { code: number; message: string }) => {
496
console.error("Provider disconnected:", error);
497
});
498
499
} else if (isEIP1193Provider(provider)) {
500
// Standard EIP-1193 events
501
provider.on("accountsChanged", (accounts: string[]) => {
502
console.log("EIP-1193 accounts changed:", accounts);
503
});
504
505
provider.on("chainChanged", (chainId: string) => {
506
console.log("EIP-1193 chain changed:", chainId);
507
});
508
}
509
}
510
511
// Pattern: Provider health monitoring
512
class ProviderHealthMonitor {
513
private isHealthy = true;
514
private lastCheck = Date.now();
515
private checkInterval = 30000; // 30 seconds
516
517
constructor(private provider: any) {
518
this.startMonitoring();
519
}
520
521
private startMonitoring() {
522
setInterval(async () => {
523
try {
524
await this.provider.request({
525
method: "eth_blockNumber",
526
params: []
527
});
528
529
if (!this.isHealthy) {
530
console.log("Provider recovered");
531
this.isHealthy = true;
532
}
533
534
this.lastCheck = Date.now();
535
} catch (error) {
536
if (this.isHealthy) {
537
console.error("Provider health check failed:", error);
538
this.isHealthy = false;
539
}
540
}
541
}, this.checkInterval);
542
}
543
544
getHealthStatus() {
545
return {
546
isHealthy: this.isHealthy,
547
lastCheck: this.lastCheck,
548
timeSinceLastCheck: Date.now() - this.lastCheck
549
};
550
}
551
}
552
553
// Use health monitor
554
const healthMonitor = new ProviderHealthMonitor(provider);
555
console.log("Provider health:", healthMonitor.getHealthStatus());
556
```