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

event-system.mddocs/

0

# Event System

1

2

The Web3 event system provides robust event emission and handling capabilities with type-safe event listeners, Promise-Event hybrids, and comprehensive lifecycle management. It serves as the foundation for all asynchronous communication within the Web3 ecosystem.

3

4

## Capabilities

5

6

### Web3EventEmitter Class

7

8

Core event emitter implementation with type-safe event handling and listener management.

9

10

```typescript { .api }

11

/**

12

* Web3 event emitter with type-safe event handling and listener management

13

* @template T - Event map type defining available events and their data types

14

*/

15

class Web3EventEmitter<T extends Web3EventMap = Web3EventMap> implements Web3Emitter<T> {

16

constructor();

17

18

// Event listener management

19

on<K extends Web3EventKey<T>>(eventName: K, fn: Web3EventCallback<T[K]>): void;

20

once<K extends Web3EventKey<T>>(eventName: K, fn: Web3EventCallback<T[K]>): void;

21

off<K extends Web3EventKey<T>>(eventName: K, fn: Web3EventCallback<T[K]>): void;

22

23

// Event emission

24

emit<K extends Web3EventKey<T>>(eventName: K, params: T[K]): void;

25

26

// Listener inspection

27

listenerCount<K extends Web3EventKey<T>>(eventName: K): number;

28

listeners<K extends Web3EventKey<T>>(eventName: K): Function[];

29

eventNames(): (string | symbol)[];

30

31

// Management

32

removeAllListeners(): EventEmitter;

33

setMaxListenerWarningThreshold(maxListenersWarningThreshold: number): void;

34

getMaxListeners(): number;

35

}

36

```

37

38

**Usage Examples:**

39

40

```typescript

41

import { Web3EventEmitter } from "web3-core";

42

43

// Define event map for type safety

44

interface MyEvents {

45

dataReceived: { data: string; timestamp: number };

46

error: { message: string; code: number };

47

connected: { address: string };

48

disconnected: undefined;

49

}

50

51

// Create typed event emitter

52

const emitter = new Web3EventEmitter<MyEvents>();

53

54

// Add event listeners with type safety

55

emitter.on("dataReceived", ({ data, timestamp }) => {

56

console.log(`Received data: ${data} at ${new Date(timestamp)}`);

57

});

58

59

emitter.on("error", ({ message, code }) => {

60

console.error(`Error ${code}: ${message}`);

61

});

62

63

emitter.on("connected", ({ address }) => {

64

console.log(`Connected to ${address}`);

65

});

66

67

emitter.on("disconnected", () => {

68

console.log("Disconnected");

69

});

70

71

// Emit events

72

emitter.emit("dataReceived", { data: "Hello World", timestamp: Date.now() });

73

emitter.emit("error", { message: "Connection failed", code: 500 });

74

emitter.emit("connected", { address: "127.0.0.1:8080" });

75

emitter.emit("disconnected", undefined);

76

77

// One-time listener

78

emitter.once("connected", ({ address }) => {

79

console.log(`First connection to ${address}`);

80

});

81

82

// Remove specific listener

83

const errorHandler = ({ message, code }: { message: string; code: number }) => {

84

console.error(`Error: ${message}`);

85

};

86

emitter.on("error", errorHandler);

87

emitter.off("error", errorHandler);

88

89

// Inspect listeners

90

console.log("Error listeners:", emitter.listenerCount("error"));

91

console.log("All event names:", emitter.eventNames());

92

```

93

94

### Web3PromiEvent Class

95

96

Promise-Event hybrid that combines Promise functionality with event emission for long-running operations.

97

98

```typescript { .api }

99

/**

100

* Promise-like object that also emits events during execution

101

* @template ResolveType - Type of the final resolved value

102

* @template EventMap - Event map for intermediate events during execution

103

*/

104

class Web3PromiEvent<ResolveType, EventMap extends Web3EventMap = Web3EventMap>

105

extends Web3EventEmitter<EventMap>

106

implements Promise<ResolveType> {

107

108

constructor(executor: PromiseExecutor<ResolveType>);

109

110

// Promise interface

111

then<TResult1 = ResolveType, TResult2 = never>(

112

onfulfilled?: ((value: ResolveType) => TResult1 | PromiseLike<TResult1>) | null,

113

onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null

114

): Promise<TResult1 | TResult2>;

115

116

catch<TResult = never>(

117

onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null

118

): Promise<ResolveType | TResult>;

119

120

finally(onfinally?: (() => void) | null): Promise<ResolveType>;

121

122

// Enhanced event methods that return this for chaining

123

on<K extends Web3EventKey<EventMap>>(eventName: K, fn: Web3EventCallback<EventMap[K]>): this;

124

once<K extends Web3EventKey<EventMap>>(eventName: K, fn: Web3EventCallback<EventMap[K]>): this;

125

126

// Promise symbol

127

readonly [Symbol.toStringTag]: 'Promise';

128

}

129

130

/**

131

* Promise executor function type

132

* @template T - Type of resolved value

133

*/

134

type PromiseExecutor<T> = (

135

resolve: (value: T) => void,

136

reject: (reason: unknown) => void

137

) => void;

138

```

139

140

**Usage Examples:**

141

142

```typescript

143

import { Web3PromiEvent } from "web3-core";

144

145

// Define event map for transaction process

146

interface TransactionEvents {

147

sending: { txHash: string };

148

sent: { txHash: string };

149

transactionHash: { txHash: string };

150

confirmation: { confirmationNumber: number; receipt: any };

151

}

152

153

// Create PromiEvent for transaction sending

154

function sendTransaction(txData: any): Web3PromiEvent<any, TransactionEvents> {

155

return new Web3PromiEvent((resolve, reject) => {

156

// Simulate transaction process

157

setTimeout(() => {

158

const txHash = "0x1234567890abcdef...";

159

160

// Emit sending event

161

promiEvent.emit("sending", { txHash });

162

163

setTimeout(() => {

164

// Emit sent event

165

promiEvent.emit("sent", { txHash });

166

promiEvent.emit("transactionHash", { txHash });

167

168

// Simulate confirmations

169

let confirmationCount = 0;

170

const confirmationInterval = setInterval(() => {

171

confirmationCount++;

172

promiEvent.emit("confirmation", {

173

confirmationNumber: confirmationCount,

174

receipt: { txHash, status: 1, blockNumber: 18000000 + confirmationCount }

175

});

176

177

if (confirmationCount >= 3) {

178

clearInterval(confirmationInterval);

179

resolve({ txHash, confirmations: confirmationCount });

180

}

181

}, 1000);

182

}, 2000);

183

}, 1000);

184

});

185

}

186

187

// Use PromiEvent - can be used as both Promise and EventEmitter

188

const txPromiEvent = sendTransaction({ to: "0x742d35Cc6634C0532925a3b8D0d3", value: "1000000000000000000" });

189

190

// Listen to events during execution

191

txPromiEvent

192

.on("sending", ({ txHash }) => {

193

console.log("Transaction sending:", txHash);

194

})

195

.on("sent", ({ txHash }) => {

196

console.log("Transaction sent:", txHash);

197

})

198

.on("transactionHash", ({ txHash }) => {

199

console.log("Transaction hash received:", txHash);

200

})

201

.on("confirmation", ({ confirmationNumber, receipt }) => {

202

console.log(`Confirmation ${confirmationNumber}:`, receipt.blockNumber);

203

});

204

205

// Use as Promise

206

txPromiEvent

207

.then((result) => {

208

console.log("Transaction completed:", result);

209

})

210

.catch((error) => {

211

console.error("Transaction failed:", error);

212

});

213

214

// Or with async/await

215

try {

216

const result = await txPromiEvent;

217

console.log("Transaction successful:", result.txHash);

218

} catch (error) {

219

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

220

}

221

```

222

223

### Event Type System

224

225

Type definitions for creating type-safe event maps and handlers.

226

227

```typescript { .api }

228

/**

229

* Base event map type - maps event names to their data types

230

*/

231

type Web3EventMap = Record<string, unknown>;

232

233

/**

234

* Event key type - ensures key exists in event map

235

* @template T - Event map type

236

*/

237

type Web3EventKey<T extends Web3EventMap> = string & keyof T;

238

239

/**

240

* Event callback function type

241

* @template T - Event data type

242

*/

243

type Web3EventCallback<T> = (params: T) => void | Promise<void>;

244

245

/**

246

* Event emitter interface definition

247

* @template T - Event map type

248

*/

249

interface Web3Emitter<T extends Web3EventMap> {

250

on<K extends Web3EventKey<T>>(eventName: K, fn: Web3EventCallback<T[K]>): void;

251

once<K extends Web3EventKey<T>>(eventName: K, fn: Web3EventCallback<T[K]>): void;

252

off<K extends Web3EventKey<T>>(eventName: K, fn: Web3EventCallback<T[K]>): void;

253

emit<K extends Web3EventKey<T>>(eventName: K, params: T[K]): void;

254

}

255

```

256

257

**Usage Examples:**

258

259

```typescript

260

// Define complex event map

261

interface BlockchainEvents {

262

blockReceived: {

263

blockNumber: number;

264

blockHash: string;

265

timestamp: number;

266

transactions: string[]

267

};

268

transactionPending: {

269

txHash: string;

270

from: string;

271

to: string;

272

value: string

273

};

274

error: {

275

code: number;

276

message: string;

277

details?: any

278

};

279

connected: {

280

provider: string;

281

networkId: number

282

};

283

disconnected: undefined;

284

}

285

286

// Create typed emitter

287

class BlockchainMonitor extends Web3EventEmitter<BlockchainEvents> {

288

constructor() {

289

super();

290

this.setMaxListenerWarningThreshold(50); // Higher threshold for blockchain events

291

}

292

293

startMonitoring() {

294

// Simulate blockchain monitoring

295

setInterval(() => {

296

this.emit("blockReceived", {

297

blockNumber: Math.floor(Math.random() * 1000000),

298

blockHash: `0x${Math.random().toString(16).substr(2, 64)}`,

299

timestamp: Date.now(),

300

transactions: [`0x${Math.random().toString(16).substr(2, 64)}`]

301

});

302

}, 12000); // Every 12 seconds

303

}

304

305

reportError(code: number, message: string, details?: any) {

306

this.emit("error", { code, message, details });

307

}

308

}

309

310

// Use typed monitor

311

const monitor = new BlockchainMonitor();

312

313

monitor.on("blockReceived", ({ blockNumber, blockHash, timestamp, transactions }) => {

314

console.log(`Block ${blockNumber} (${blockHash}) received at ${new Date(timestamp)}`);

315

console.log(`Contains ${transactions.length} transactions`);

316

});

317

318

monitor.on("error", ({ code, message, details }) => {

319

console.error(`Blockchain error ${code}: ${message}`, details);

320

});

321

322

monitor.startMonitoring();

323

```

324

325

### Advanced Event Patterns

326

327

Common patterns and best practices for event-driven Web3 applications.

328

329

```typescript { .api }

330

/**

331

* Event listener management utilities

332

*/

333

listenerCount<K extends Web3EventKey<T>>(eventName: K): number;

334

listeners<K extends Web3EventKey<T>>(eventName: K): Function[];

335

eventNames(): (string | symbol)[];

336

removeAllListeners(): EventEmitter;

337

setMaxListenerWarningThreshold(maxListenersWarningThreshold: number): void;

338

getMaxListeners(): number;

339

```

340

341

**Usage Examples:**

342

343

```typescript

344

// Pattern: Event debouncing

345

class DebouncedEmitter extends Web3EventEmitter<{ dataChanged: string }> {

346

private debounceTimer?: NodeJS.Timeout;

347

348

emitDebounced(data: string) {

349

if (this.debounceTimer) {

350

clearTimeout(this.debounceTimer);

351

}

352

353

this.debounceTimer = setTimeout(() => {

354

this.emit("dataChanged", data);

355

}, 300); // 300ms debounce

356

}

357

}

358

359

// Pattern: Event aggregation

360

class EventAggregator extends Web3EventEmitter<{ batch: string[] }> {

361

private buffer: string[] = [];

362

private batchTimer?: NodeJS.Timeout;

363

364

add(item: string) {

365

this.buffer.push(item);

366

367

if (!this.batchTimer) {

368

this.batchTimer = setTimeout(() => {

369

this.emit("batch", [...this.buffer]);

370

this.buffer = [];

371

this.batchTimer = undefined;

372

}, 1000); // Batch every second

373

}

374

}

375

}

376

377

// Pattern: Error handling with retry

378

class ResilientEmitter extends Web3EventEmitter<{

379

data: any;

380

error: Error;

381

retry: { attempt: number; maxAttempts: number }

382

}> {

383

async emitWithRetry(data: any, maxAttempts = 3) {

384

for (let attempt = 1; attempt <= maxAttempts; attempt++) {

385

try {

386

this.emit("data", data);

387

return; // Success

388

} catch (error) {

389

this.emit("retry", { attempt, maxAttempts });

390

391

if (attempt === maxAttempts) {

392

this.emit("error", error as Error);

393

throw error;

394

}

395

396

// Wait before retry

397

await new Promise(resolve => setTimeout(resolve, 1000 * attempt));

398

}

399

}

400

}

401

}

402

403

// Pattern: Event metrics and monitoring

404

class MonitoredEmitter extends Web3EventEmitter<{ data: any; metrics: any }> {

405

private eventCounts = new Map<string, number>();

406

private startTime = Date.now();

407

408

emit<K extends string>(eventName: K, params: any): void {

409

// Track event metrics

410

const currentCount = this.eventCounts.get(eventName) || 0;

411

this.eventCounts.set(eventName, currentCount + 1);

412

413

// Emit metrics periodically

414

if (currentCount % 100 === 0) {

415

this.emit("metrics", {

416

eventName,

417

count: currentCount + 1,

418

rate: (currentCount + 1) / ((Date.now() - this.startTime) / 1000),

419

listeners: this.listenerCount(eventName as any)

420

});

421

}

422

423

super.emit(eventName as any, params);

424

}

425

426

getMetrics() {

427

const uptime = (Date.now() - this.startTime) / 1000;

428

const metrics: any = { uptime, events: {} };

429

430

for (const [eventName, count] of this.eventCounts) {

431

metrics.events[eventName] = {

432

count,

433

rate: count / uptime,

434

listeners: this.listenerCount(eventName as any)

435

};

436

}

437

438

return metrics;

439

}

440

}

441

442

// Usage of advanced patterns

443

const debouncedEmitter = new DebouncedEmitter();

444

debouncedEmitter.on("dataChanged", (data) => {

445

console.log("Debounced data:", data);

446

});

447

448

const aggregator = new EventAggregator();

449

aggregator.on("batch", (items) => {

450

console.log("Batch of items:", items);

451

});

452

453

const resilientEmitter = new ResilientEmitter();

454

resilientEmitter.on("retry", ({ attempt, maxAttempts }) => {

455

console.log(`Retry attempt ${attempt}/${maxAttempts}`);

456

});

457

458

const monitoredEmitter = new MonitoredEmitter();

459

monitoredEmitter.on("metrics", (metrics) => {

460

console.log("Event metrics:", metrics);

461

});

462

```

463

464

### Memory Management

465

466

Best practices for preventing memory leaks in event-driven applications.

467

468

```typescript

469

// ✅ Good: Remove listeners when done

470

const cleanup = () => {

471

emitter.removeAllListeners();

472

// Or remove specific listeners

473

emitter.off("data", specificHandler);

474

};

475

476

// ✅ Good: Use once() for one-time events

477

emitter.once("connected", () => {

478

console.log("Connected - this will only fire once");

479

});

480

481

// ✅ Good: Monitor listener counts

482

if (emitter.listenerCount("data") > 10) {

483

console.warn("Too many data listeners, possible memory leak");

484

}

485

486

// ✅ Good: Set appropriate max listener threshold

487

emitter.setMaxListenerWarningThreshold(20);

488

489

// ❌ Bad: Adding listeners in loops without cleanup

490

for (let i = 0; i < 100; i++) {

491

emitter.on("data", () => { /* handler */ }); // Memory leak!

492

}

493

```