or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

batch-processing.mdconfiguration-management.mdcontext-management.mdevent-system.mdindex.mdprovider-integration.mdrequest-management.mdsubscription-management.md

provider-integration.mddocs/

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

```