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

subscription-management.mddocs/

0

# Subscription Management

1

2

The Web3 subscription management system provides real-time WebSocket connectivity for blockchain events with automatic reconnection, event handling, and comprehensive subscription lifecycle management. It enables applications to receive live updates for blockchain changes.

3

4

## Capabilities

5

6

### Web3SubscriptionManager Class

7

8

Central manager for handling multiple WebSocket subscriptions with lifecycle management and event coordination.

9

10

```typescript { .api }

11

/**

12

* Manages WebSocket subscriptions to blockchain events with lifecycle management

13

* @template API - JSON-RPC API specification type

14

* @template RegisteredSubs - Registered subscription constructor types

15

*/

16

class Web3SubscriptionManager<

17

API extends Web3APISpec = Web3APISpec,

18

RegisteredSubs extends {[key: string]: Web3SubscriptionConstructor} = {[key: string]: Web3SubscriptionConstructor}

19

> {

20

constructor(

21

requestManager: Web3RequestManager<API>,

22

registeredSubscriptions: RegisteredSubs,

23

tolerateUnlinkedSubscription?: boolean

24

);

25

26

// Core properties

27

readonly requestManager: Web3RequestManager<API>;

28

readonly registeredSubscriptions: RegisteredSubs;

29

readonly subscriptions: Map<string, InstanceType<RegisteredSubs[keyof RegisteredSubs]>>;

30

31

// Subscription management

32

subscribe<T extends keyof RegisteredSubs>(

33

name: T,

34

args?: ConstructorParameters<RegisteredSubs[T]>[0],

35

returnFormat?: DataFormat

36

): Promise<InstanceType<RegisteredSubs[T]>>;

37

38

addSubscription(

39

sub: InstanceType<RegisteredSubs[keyof RegisteredSubs]>

40

): Promise<string>;

41

42

removeSubscription(

43

sub: InstanceType<RegisteredSubs[keyof RegisteredSubs]>

44

): Promise<string>;

45

46

unsubscribe(condition?: ShouldUnsubscribeCondition): Promise<string[]>;

47

48

clear(): void;

49

50

// Capability detection

51

supportsSubscriptions(): boolean;

52

}

53

```

54

55

**Usage Examples:**

56

57

```typescript

58

import { Web3SubscriptionManager, Web3RequestManager } from "web3-core";

59

import { EthExecutionAPI } from "web3-types";

60

61

// Define subscription types

62

interface MySubscriptions {

63

newBlockHeaders: typeof NewBlockHeadersSubscription;

64

pendingTransactions: typeof PendingTransactionsSubscription;

65

logs: typeof LogsSubscription;

66

}

67

68

// Create WebSocket request manager

69

const wsRequestManager = new Web3RequestManager<EthExecutionAPI>(

70

"wss://eth-mainnet.ws.alchemyapi.io/v2/your-api-key"

71

);

72

73

// Create subscription manager

74

const subscriptionManager = new Web3SubscriptionManager(

75

wsRequestManager,

76

{

77

newBlockHeaders: NewBlockHeadersSubscription,

78

pendingTransactions: PendingTransactionsSubscription,

79

logs: LogsSubscription

80

} as MySubscriptions

81

);

82

83

// Check subscription support

84

if (subscriptionManager.supportsSubscriptions()) {

85

console.log("WebSocket provider supports subscriptions");

86

87

// Subscribe to new block headers

88

const blockSubscription = await subscriptionManager.subscribe("newBlockHeaders");

89

90

blockSubscription.on("data", (blockHeader) => {

91

console.log("New block:", blockHeader.number, blockHeader.hash);

92

});

93

94

blockSubscription.on("error", (error) => {

95

console.error("Block subscription error:", error);

96

});

97

} else {

98

console.log("Current provider does not support subscriptions");

99

}

100

```

101

102

### Web3Subscription Base Class

103

104

Abstract base class for implementing specific subscription types with event handling and lifecycle management.

105

106

```typescript { .api }

107

/**

108

* Abstract base class for WebSocket subscriptions with event emission

109

* @template EventMap - Event type mapping for this subscription

110

* @template ArgsType - Constructor arguments type

111

* @template API - JSON-RPC API specification type

112

*/

113

abstract class Web3Subscription<

114

EventMap extends {[key: string]: unknown} = {[key: string]: unknown},

115

ArgsType = unknown,

116

API extends Web3APISpec = Web3APISpec

117

> extends Web3EventEmitter<EventMap & {

118

data: any;

119

changed: any;

120

error: Error;

121

connected: string;

122

disconnected: string | Error;

123

}> {

124

constructor(

125

args: ArgsType,

126

options: {

127

subscriptionManager: Web3SubscriptionManager<API, any>;

128

returnFormat?: DataFormat;

129

} | {

130

requestManager: Web3RequestManager<API>;

131

returnFormat?: DataFormat;

132

}

133

);

134

135

// Core properties

136

readonly id?: HexString;

137

readonly args: ArgsType;

138

lastBlock?: BlockOutput;

139

readonly returnFormat: DataFormat;

140

141

// Subscription lifecycle

142

subscribe(): Promise<string>;

143

unsubscribe(): Promise<void>;

144

resubscribe(): Promise<void>;

145

146

// Internal methods (implement in subclasses)

147

protected abstract sendSubscriptionRequest(): Promise<string>;

148

protected abstract sendUnsubscribeRequest(): Promise<void>;

149

protected abstract processSubscriptionData(

150

data: JsonRpcSubscriptionResult | JsonRpcSubscriptionResultOld<Log> | JsonRpcNotification<Log>

151

): void;

152

protected abstract formatSubscriptionResult(data: any): any;

153

}

154

```

155

156

**Usage Examples:**

157

158

```typescript

159

// Example custom subscription implementation

160

class CustomEventSubscription extends Web3Subscription<{

161

data: CustomEvent;

162

error: Error;

163

}> {

164

constructor(

165

args: { address: string; topics: string[] },

166

options: { subscriptionManager: Web3SubscriptionManager }

167

) {

168

super(args, options);

169

}

170

171

protected async sendSubscriptionRequest(): Promise<string> {

172

return this.requestManager.send({

173

method: "eth_subscribe",

174

params: ["logs", {

175

address: this.args.address,

176

topics: this.args.topics

177

}]

178

});

179

}

180

181

protected async sendUnsubscribeRequest(): Promise<void> {

182

if (this.id) {

183

await this.requestManager.send({

184

method: "eth_unsubscribe",

185

params: [this.id]

186

});

187

}

188

}

189

190

protected processSubscriptionData(data: JsonRpcSubscriptionResult): void {

191

const formattedData = this.formatSubscriptionResult(data.params.result);

192

this.emit("data", formattedData);

193

}

194

195

protected formatSubscriptionResult(data: any): CustomEvent {

196

// Format the raw data into CustomEvent format

197

return {

198

address: data.address,

199

topics: data.topics,

200

data: data.data,

201

blockNumber: data.blockNumber,

202

transactionHash: data.transactionHash

203

};

204

}

205

}

206

207

// Use custom subscription

208

const customSub = new CustomEventSubscription(

209

{

210

address: "0x742d35Cc6634C0532925a3b8D0d3",

211

topics: ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]

212

},

213

{ subscriptionManager }

214

);

215

216

await customSub.subscribe();

217

218

customSub.on("data", (event) => {

219

console.log("Custom event received:", event);

220

});

221

```

222

223

### Subscription Lifecycle Management

224

225

Methods for managing subscription states and handling connection events.

226

227

```typescript { .api }

228

/**

229

* Subscribe to blockchain events

230

* @returns Promise resolving to subscription ID

231

*/

232

subscribe(): Promise<string>;

233

234

/**

235

* Unsubscribe from blockchain events

236

* @returns Promise resolving when unsubscribed

237

*/

238

unsubscribe(): Promise<void>;

239

240

/**

241

* Resubscribe to blockchain events (useful after connection loss)

242

* @returns Promise resolving when resubscribed

243

*/

244

resubscribe(): Promise<void>;

245

246

/**

247

* Add existing subscription instance to manager

248

* @param sub - Subscription instance to add

249

* @returns Promise resolving to subscription ID

250

*/

251

addSubscription(sub: InstanceType<RegisteredSubs[keyof RegisteredSubs]>): Promise<string>;

252

253

/**

254

* Remove subscription instance from manager

255

* @param sub - Subscription instance to remove

256

* @returns Promise resolving to removed subscription ID

257

*/

258

removeSubscription(sub: InstanceType<RegisteredSubs[keyof RegisteredSubs]>): Promise<string>;

259

260

/**

261

* Condition function type for selective unsubscription

262

*/

263

type ShouldUnsubscribeCondition = (subscription: Web3Subscription) => boolean;

264

265

/**

266

* Unsubscribe multiple subscriptions based on condition

267

* @param condition - Optional filter function for selective unsubscription

268

* @returns Promise resolving to array of unsubscribed subscription IDs

269

*/

270

unsubscribe(condition?: ShouldUnsubscribeCondition): Promise<string[]>;

271

272

/**

273

* Clear all subscriptions without sending unsubscribe requests

274

*/

275

clear(): void;

276

```

277

278

**Usage Examples:**

279

280

```typescript

281

// Basic subscription lifecycle

282

const blockSub = await subscriptionManager.subscribe("newBlockHeaders");

283

console.log("Subscribed with ID:", blockSub.id);

284

285

// Later unsubscribe

286

await blockSub.unsubscribe();

287

console.log("Unsubscribed");

288

289

// Resubscribe after connection issues

290

await blockSub.resubscribe();

291

console.log("Resubscribed with new ID:", blockSub.id);

292

293

// Selective unsubscription

294

await subscriptionManager.unsubscribe((sub) => {

295

// Unsubscribe all subscriptions older than 1 hour

296

const oneHourAgo = Date.now() - 60 * 60 * 1000;

297

return sub.createdAt < oneHourAgo;

298

});

299

300

// Clear all subscriptions (emergency cleanup)

301

subscriptionManager.clear();

302

```

303

304

### Subscription Events

305

306

Event system for handling subscription data, errors, and connection states.

307

308

```typescript { .api }

309

/**

310

* Standard subscription events

311

*/

312

interface SubscriptionEventMap {

313

/**

314

* Emitted when new data is received

315

*/

316

data: any;

317

318

/**

319

* Emitted when subscription data changes

320

*/

321

changed: any;

322

323

/**

324

* Emitted when subscription encounters an error

325

*/

326

error: Error;

327

328

/**

329

* Emitted when subscription successfully connects

330

*/

331

connected: string;

332

333

/**

334

* Emitted when subscription disconnects

335

*/

336

disconnected: string | Error;

337

}

338

```

339

340

**Usage Examples:**

341

342

```typescript

343

// Handle all subscription events

344

const subscription = await subscriptionManager.subscribe("newBlockHeaders");

345

346

subscription.on("data", (blockHeader) => {

347

console.log("New block header:", {

348

number: blockHeader.number,

349

hash: blockHeader.hash,

350

timestamp: blockHeader.timestamp,

351

parentHash: blockHeader.parentHash

352

});

353

});

354

355

subscription.on("changed", (changedData) => {

356

console.log("Subscription data changed:", changedData);

357

});

358

359

subscription.on("error", (error) => {

360

console.error("Subscription error:", error.message);

361

362

// Attempt to resubscribe on error

363

setTimeout(async () => {

364

try {

365

await subscription.resubscribe();

366

console.log("Successfully resubscribed after error");

367

} catch (resubError) {

368

console.error("Failed to resubscribe:", resubError);

369

}

370

}, 5000);

371

});

372

373

subscription.on("connected", (subscriptionId) => {

374

console.log("Subscription connected with ID:", subscriptionId);

375

});

376

377

subscription.on("disconnected", (reason) => {

378

if (reason instanceof Error) {

379

console.error("Subscription disconnected due to error:", reason.message);

380

} else {

381

console.log("Subscription disconnected with ID:", reason);

382

}

383

});

384

```

385

386

### Provider Capability Detection

387

388

Methods for detecting WebSocket subscription support in providers.

389

390

```typescript { .api }

391

/**

392

* Check if current provider supports subscriptions

393

* @returns Boolean indicating subscription support

394

*/

395

supportsSubscriptions(): boolean;

396

```

397

398

**Usage Examples:**

399

400

```typescript

401

// Check provider capabilities before subscribing

402

if (subscriptionManager.supportsSubscriptions()) {

403

// Provider supports subscriptions - proceed with WebSocket subscriptions

404

const logsSubscription = await subscriptionManager.subscribe("logs", {

405

address: "0x742d35Cc6634C0532925a3b8D0d3",

406

topics: ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]

407

});

408

409

logsSubscription.on("data", (log) => {

410

console.log("Token transfer:", log);

411

});

412

} else {

413

// Fall back to polling mechanism

414

console.log("Provider doesn't support subscriptions, using polling...");

415

416

const pollForBlocks = async () => {

417

try {

418

const latestBlock = await subscriptionManager.requestManager.send({

419

method: "eth_getBlockByNumber",

420

params: ["latest", false]

421

});

422

console.log("Latest block (polling):", latestBlock.number);

423

} catch (error) {

424

console.error("Polling error:", error);

425

}

426

};

427

428

// Poll every 12 seconds (average block time)

429

setInterval(pollForBlocks, 12000);

430

}

431

```

432

433

### Subscription Constructor Types

434

435

Type definitions for subscription constructors and registration.

436

437

```typescript { .api }

438

/**

439

* Constructor type for Web3 subscription classes

440

*/

441

type Web3SubscriptionConstructor<

442

EventMap = {[key: string]: unknown},

443

ArgsType = unknown

444

> = new (

445

args: ArgsType,

446

options: {

447

subscriptionManager?: Web3SubscriptionManager;

448

requestManager?: Web3RequestManager;

449

returnFormat?: DataFormat;

450

}

451

) => Web3Subscription<EventMap, ArgsType>;

452

```

453

454

**Usage Examples:**

455

456

```typescript

457

// Define subscription registry with proper typing

458

interface EthereumSubscriptions {

459

newBlockHeaders: Web3SubscriptionConstructor<

460

{ data: BlockHeader },

461

undefined

462

>;

463

logs: Web3SubscriptionConstructor<

464

{ data: Log },

465

{ address?: string; topics?: string[] }

466

>;

467

pendingTransactions: Web3SubscriptionConstructor<

468

{ data: string },

469

undefined

470

>;

471

syncing: Web3SubscriptionConstructor<

472

{ data: SyncingStatus },

473

undefined

474

>;

475

}

476

477

// Create typed subscription manager

478

const typedSubscriptionManager = new Web3SubscriptionManager<

479

EthExecutionAPI,

480

EthereumSubscriptions

481

>(

482

wsRequestManager,

483

{

484

newBlockHeaders: NewBlockHeadersSubscription,

485

logs: LogsSubscription,

486

pendingTransactions: PendingTransactionsSubscription,

487

syncing: SyncingSubscription

488

}

489

);

490

491

// TypeScript will provide proper type checking and autocomplete

492

const logsub = await typedSubscriptionManager.subscribe("logs", {

493

address: "0x742d35Cc6634C0532925a3b8D0d3",

494

topics: ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]

495

});

496

```