or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

api-initialization.mdblockchain-queries.mdevents-metadata.mdindex.mdnetwork-providers.mdrpc-operations.mdtransaction-submission.md

events-metadata.mddocs/

0

# Events and Metadata

1

2

Access runtime events, errors, and metadata for dynamic API behavior. The @polkadot/api provides comprehensive access to blockchain runtime information that enables dynamic interface generation and event monitoring.

3

4

## Capabilities

5

6

### Runtime Events

7

8

Access to all runtime events emitted by the blockchain.

9

10

```typescript { .api }

11

interface DecoratedEvents<ApiType> {

12

[section: string]: {

13

[event: string]: EventCreator;

14

};

15

}

16

17

interface EventCreator {

18

/**

19

* Create event instance

20

* @param data - Event data parameters

21

* @returns Event instance

22

*/

23

(...data: any[]): Event;

24

25

/**

26

* Check if event matches this type

27

* @param event - Event to check

28

* @returns Type guard result

29

*/

30

is(event: Event): boolean;

31

32

/** Event metadata */

33

readonly meta: EventMetadataLatest;

34

35

/** Event section name */

36

readonly section: string;

37

38

/** Event method name */

39

readonly method: string;

40

}

41

42

interface Event extends Codec {

43

/** Event section (pallet name) */

44

readonly section: string;

45

46

/** Event method name */

47

readonly method: string;

48

49

/** Event data parameters */

50

readonly data: Codec[];

51

52

/** Event metadata */

53

readonly meta: EventMetadataLatest;

54

55

/** Event index */

56

readonly index: Bytes;

57

}

58

```

59

60

### Runtime Errors

61

62

Access to all runtime errors that can be returned by extrinsics.

63

64

```typescript { .api }

65

interface DecoratedErrors<ApiType> {

66

[section: string]: {

67

[error: string]: ErrorCreator;

68

};

69

}

70

71

interface ErrorCreator {

72

/**

73

* Check if error matches this type

74

* @param error - Error to check

75

* @returns Type guard result

76

*/

77

is(error: RegistryError): boolean;

78

79

/** Error metadata */

80

readonly meta: ErrorMetadataLatest;

81

82

/** Error section name */

83

readonly section: string;

84

85

/** Error name */

86

readonly name: string;

87

}

88

89

interface RegistryError extends Codec {

90

/** Error section (pallet name) */

91

readonly section: string;

92

93

/** Error name */

94

readonly name: string;

95

96

/** Error documentation */

97

readonly docs: string[];

98

99

/** Error index */

100

readonly index: number;

101

}

102

```

103

104

### Runtime Metadata

105

106

Complete runtime metadata providing type and interface information.

107

108

```typescript { .api }

109

interface Metadata extends Codec {

110

/** Metadata version */

111

readonly version: number;

112

113

/** Runtime metadata */

114

readonly asLatest: MetadataLatest;

115

116

/** Convert to JSON */

117

toJSON(): any;

118

119

/** Convert to human-readable format */

120

toHuman(): any;

121

}

122

123

interface MetadataLatest {

124

/** Lookup registry for types */

125

readonly lookup: PortableRegistry;

126

127

/** Pallet metadata */

128

readonly pallets: Vec<PalletMetadata>;

129

130

/** Extrinsic metadata */

131

readonly extrinsic: ExtrinsicMetadata;

132

133

/** Runtime API metadata */

134

readonly apis: Vec<RuntimeApiMetadata>;

135

136

/** Outer enums (Event, Call, Error) */

137

readonly outerEnums: MetadataOuterEnums;

138

139

/** Runtime type information */

140

readonly type: SiLookupTypeId;

141

}

142

143

interface PalletMetadata {

144

/** Pallet name */

145

readonly name: string;

146

147

/** Storage metadata */

148

readonly storage?: PalletStorageMetadata;

149

150

/** Call metadata */

151

readonly calls?: PalletCallMetadata;

152

153

/** Event metadata */

154

readonly events?: PalletEventMetadata;

155

156

/** Constants metadata */

157

readonly constants: Vec<PalletConstantMetadata>;

158

159

/** Error metadata */

160

readonly errors?: PalletErrorMetadata;

161

162

/** Pallet index */

163

readonly index: number;

164

}

165

```

166

167

### Event Records

168

169

Structure for events emitted during block execution.

170

171

```typescript { .api }

172

interface EventRecord extends Codec {

173

/** Execution phase when event was emitted */

174

readonly phase: Phase;

175

176

/** The actual event */

177

readonly event: Event;

178

179

/** Event topics for filtering */

180

readonly topics: Vec<Hash>;

181

}

182

183

interface Phase extends Enum {

184

/** Emitted during block initialization */

185

readonly isApplyExtrinsic: boolean;

186

187

/** Emitted during extrinsic execution */

188

readonly asApplyExtrinsic: number;

189

190

/** Emitted during block finalization */

191

readonly isFinalization: boolean;

192

193

/** Emitted during block initialization */

194

readonly isInitialization: boolean;

195

}

196

```

197

198

### Registry and Type Information

199

200

Type registry providing runtime type definitions.

201

202

```typescript { .api }

203

interface Registry {

204

/** Chain properties */

205

readonly chainDecimals: number[];

206

readonly chainSS58: number;

207

readonly chainTokens: string[];

208

209

/**

210

* Create type instance

211

* @param type - Type name or definition

212

* @param value - Initial value

213

* @returns Type instance

214

*/

215

createType<T = Codec>(type: string, value?: any): T;

216

217

/**

218

* Get type definition

219

* @param type - Type name

220

* @returns Type definition

221

*/

222

getDefinition(type: string): string;

223

224

/**

225

* Check if type exists

226

* @param type - Type name

227

* @returns Existence check

228

*/

229

hasType(type: string): boolean;

230

231

/**

232

* Register custom types

233

* @param types - Type definitions

234

*/

235

register(types: RegistryTypes): void;

236

237

/**

238

* Set chain properties

239

* @param properties - Chain properties

240

*/

241

setChainProperties(properties: ChainProperties): void;

242

243

/**

244

* Set metadata

245

* @param metadata - Runtime metadata

246

*/

247

setMetadata(metadata: Metadata): void;

248

249

/**

250

* Find call by index

251

* @param callIndex - Call index bytes

252

* @returns Call function

253

*/

254

findMetaCall(callIndex: Uint8Array): CallFunction;

255

256

/**

257

* Find error by index

258

* @param errorIndex - Error index bytes

259

* @returns Registry error

260

*/

261

findMetaError(errorIndex: Uint8Array): RegistryError;

262

}

263

```

264

265

## Usage Examples

266

267

### Event Monitoring

268

269

```typescript

270

import { ApiPromise } from "@polkadot/api";

271

272

const api = await ApiPromise.create();

273

274

// Subscribe to all system events

275

const unsubscribe = await api.query.system.events((events) => {

276

console.log(`Received ${events.length} events:`);

277

278

events.forEach((record, index) => {

279

const { event, phase } = record;

280

const types = event.typeDef;

281

282

console.log(`\t${index}: ${event.section}.${event.method}${

283

phase.isApplyExtrinsic ? ` (extrinsic ${phase.asApplyExtrinsic})` : ''

284

}`);

285

286

// Show event data

287

event.data.forEach((data, index) => {

288

console.log(`\t\t${types[index].type}: ${data.toString()}`);

289

});

290

});

291

});

292

```

293

294

### Specific Event Filtering

295

296

```typescript

297

import { ApiPromise } from "@polkadot/api";

298

299

const api = await ApiPromise.create();

300

301

// Filter for balance transfer events

302

const unsubscribe = await api.query.system.events((events) => {

303

events.forEach(({ event, phase }) => {

304

// Check if event is a balance transfer

305

if (api.events.balances.Transfer.is(event)) {

306

const [from, to, amount] = event.data;

307

console.log(`Transfer: ${from} -> ${to}: ${amount.toHuman()}`);

308

}

309

310

// Check for transaction fees

311

if (api.events.balances.Withdraw.is(event)) {

312

const [account, amount] = event.data;

313

console.log(`Fee paid by ${account}: ${amount.toHuman()}`);

314

}

315

316

// Check for failed extrinsics

317

if (api.events.system.ExtrinsicFailed.is(event)) {

318

const [dispatchError, dispatchInfo] = event.data;

319

console.log('Extrinsic failed:', dispatchError.toString());

320

}

321

});

322

});

323

```

324

325

### Error Handling

326

327

```typescript

328

import { ApiPromise } from "@polkadot/api";

329

330

const api = await ApiPromise.create();

331

332

// Handle transaction with error checking

333

const transfer = api.tx.balances.transferAllowDeath(

334

'1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg',

335

1000000000000

336

);

337

338

const unsubscribe = await transfer.signAndSend(sender, ({ status, events, dispatchError }) => {

339

if (status.isInBlock) {

340

console.log(`Transaction included in block ${status.asInBlock}`);

341

342

if (dispatchError) {

343

if (dispatchError.isModule) {

344

// Module error - can decode using metadata

345

const decoded = api.registry.findMetaError(dispatchError.asModule);

346

const { docs, name, section } = decoded;

347

348

console.log(`Error: ${section}.${name}: ${docs.join(' ')}`);

349

350

// Check specific error types

351

if (api.errors.balances.InsufficientBalance.is(dispatchError)) {

352

console.log('Insufficient balance for transfer');

353

}

354

} else {

355

// Other error types

356

console.log('Error:', dispatchError.toString());

357

}

358

}

359

360

// Check events for additional context

361

events.forEach(({ event }) => {

362

if (api.events.system.ExtrinsicFailed.is(event)) {

363

console.log('Extrinsic failed event emitted');

364

} else if (api.events.system.ExtrinsicSuccess.is(event)) {

365

console.log('Extrinsic succeeded');

366

}

367

});

368

}

369

});

370

```

371

372

### Metadata Exploration

373

374

```typescript

375

import { ApiPromise } from "@polkadot/api";

376

377

const api = await ApiPromise.create();

378

379

// Get runtime metadata

380

const metadata = api.runtimeMetadata;

381

console.log(`Metadata version: ${metadata.version}`);

382

383

// Explore pallets

384

metadata.asLatest.pallets.forEach((pallet) => {

385

console.log(`\nPallet: ${pallet.name} (index: ${pallet.index})`);

386

387

// Storage items

388

if (pallet.storage) {

389

console.log(' Storage:');

390

pallet.storage.items.forEach((item) => {

391

console.log(` ${item.name}: ${item.type}`);

392

});

393

}

394

395

// Calls (extrinsics)

396

if (pallet.calls) {

397

console.log(' Calls:');

398

const calls = api.registry.lookup.getTypeDef(pallet.calls.type);

399

if (calls.type === 'Enum') {

400

calls.sub.forEach((call) => {

401

console.log(` ${call.name}`);

402

});

403

}

404

}

405

406

// Events

407

if (pallet.events) {

408

console.log(' Events:');

409

const events = api.registry.lookup.getTypeDef(pallet.events.type);

410

if (events.type === 'Enum') {

411

events.sub.forEach((event) => {

412

console.log(` ${event.name}`);

413

});

414

}

415

}

416

417

// Constants

418

if (pallet.constants.length > 0) {

419

console.log(' Constants:');

420

pallet.constants.forEach((constant) => {

421

const value = api.consts[pallet.name.toString()][constant.name.toString()];

422

console.log(` ${constant.name}: ${value?.toHuman() || 'N/A'}`);

423

});

424

}

425

});

426

```

427

428

### Type Registry Usage

429

430

```typescript

431

import { ApiPromise } from "@polkadot/api";

432

433

const api = await ApiPromise.create();

434

435

// Access registry

436

const registry = api.registry;

437

438

// Check chain properties

439

console.log('Chain decimals:', registry.chainDecimals);

440

console.log('Chain tokens:', registry.chainTokens);

441

console.log('SS58 format:', registry.chainSS58);

442

443

// Create custom types

444

const accountId = registry.createType('AccountId', '1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg');

445

console.log('Account ID:', accountId.toString());

446

447

const balance = registry.createType('Balance', 1000000000000);

448

console.log('Balance:', balance.toHuman());

449

450

// Type definitions

451

const hasAccountId = registry.hasType('AccountId');

452

console.log('Has AccountId type:', hasAccountId);

453

454

const accountIdDef = registry.getDefinition('AccountId');

455

console.log('AccountId definition:', accountIdDef);

456

```

457

458

### Custom Event Handling

459

460

```typescript

461

import { ApiPromise } from "@polkadot/api";

462

463

const api = await ApiPromise.create();

464

465

// Create custom event handlers

466

class EventHandler {

467

constructor(private api: ApiPromise) {}

468

469

handleBalanceEvents(events: EventRecord[]) {

470

events.forEach(({ event }) => {

471

if (this.api.events.balances.Transfer.is(event)) {

472

this.onTransfer(event.data);

473

} else if (this.api.events.balances.Deposit.is(event)) {

474

this.onDeposit(event.data);

475

} else if (this.api.events.balances.Withdraw.is(event)) {

476

this.onWithdraw(event.data);

477

}

478

});

479

}

480

481

private onTransfer([from, to, amount]: any[]) {

482

console.log(`πŸ’Έ Transfer: ${from} -> ${to}: ${amount.toHuman()}`);

483

}

484

485

private onDeposit([account, amount]: any[]) {

486

console.log(`πŸ’° Deposit: ${account}: ${amount.toHuman()}`);

487

}

488

489

private onWithdraw([account, amount]: any[]) {

490

console.log(`πŸ’³ Withdraw: ${account}: ${amount.toHuman()}`);

491

}

492

493

handleStakingEvents(events: EventRecord[]) {

494

events.forEach(({ event }) => {

495

if (this.api.events.staking.Bonded?.is(event)) {

496

const [stash, amount] = event.data;

497

console.log(`πŸ”— Bonded: ${stash}: ${amount.toHuman()}`);

498

} else if (this.api.events.staking.Unbonded?.is(event)) {

499

const [stash, amount] = event.data;

500

console.log(`πŸ”“ Unbonded: ${stash}: ${amount.toHuman()}`);

501

} else if (this.api.events.staking.Rewarded?.is(event)) {

502

const [stash, amount] = event.data;

503

console.log(`🎁 Reward: ${stash}: ${amount.toHuman()}`);

504

}

505

});

506

}

507

}

508

509

// Usage

510

const eventHandler = new EventHandler(api);

511

512

const unsubscribe = await api.query.system.events((events) => {

513

eventHandler.handleBalanceEvents(events);

514

eventHandler.handleStakingEvents(events);

515

});

516

```

517

518

### Runtime Version Monitoring

519

520

```typescript

521

import { ApiPromise } from "@polkadot/api";

522

523

const api = await ApiPromise.create();

524

525

// Monitor runtime version changes

526

let currentSpecVersion = api.runtimeVersion.specVersion.toNumber();

527

console.log(`Initial runtime version: ${currentSpecVersion}`);

528

529

const unsubscribe = await api.rpc.state.subscribeRuntimeVersion((version) => {

530

const newSpecVersion = version.specVersion.toNumber();

531

532

if (newSpecVersion !== currentSpecVersion) {

533

console.log(`πŸ”„ Runtime upgraded: ${currentSpecVersion} -> ${newSpecVersion}`);

534

console.log(`Implementation: ${version.implName}`);

535

console.log(`Spec name: ${version.specName}`);

536

537

currentSpecVersion = newSpecVersion;

538

539

// Metadata will be automatically updated

540

// You may want to refresh your application state here

541

}

542

});

543

544

// Access version information

545

const version = api.runtimeVersion;

546

console.log('Runtime info:');

547

console.log(` Spec name: ${version.specName}`);

548

console.log(` Impl name: ${version.implName}`);

549

console.log(` Spec version: ${version.specVersion}`);

550

console.log(` Impl version: ${version.implVersion}`);

551

console.log(` Transaction version: ${version.transactionVersion}`);

552

console.log(` State version: ${version.stateVersion}`);

553

```

554

555

### Event-Driven Application Logic

556

557

```typescript

558

import { ApiPromise } from "@polkadot/api";

559

560

const api = await ApiPromise.create();

561

562

// Application state manager based on events

563

class ChainStateManager {

564

private accounts = new Map<string, any>();

565

566

constructor(private api: ApiPromise) {

567

this.startEventListener();

568

}

569

570

private async startEventListener() {

571

const unsubscribe = await this.api.query.system.events((events) => {

572

events.forEach(({ event, phase }) => {

573

this.processEvent(event, phase);

574

});

575

});

576

}

577

578

private processEvent(event: Event, phase: Phase) {

579

// Process balance changes

580

if (this.api.events.balances.Transfer.is(event)) {

581

const [from, to, amount] = event.data;

582

this.updateAccountBalance(from.toString(), -amount.toBn());

583

this.updateAccountBalance(to.toString(), amount.toBn());

584

}

585

586

// Process staking events

587

if (this.api.events.staking?.Bonded?.is(event)) {

588

const [stash, amount] = event.data;

589

this.updateStakingInfo(stash.toString(), { bonded: amount.toBn() });

590

}

591

592

// Process democracy events

593

if (this.api.events.democracy?.Proposed?.is(event)) {

594

const [proposalIndex, deposit] = event.data;

595

console.log(`New proposal ${proposalIndex} with deposit ${deposit.toHuman()}`);

596

}

597

}

598

599

private updateAccountBalance(address: string, change: any) {

600

const account = this.accounts.get(address) || { balance: this.api.createType('Balance', 0) };

601

account.balance = account.balance.add(change);

602

this.accounts.set(address, account);

603

604

console.log(`Account ${address} balance: ${account.balance.toHuman()}`);

605

}

606

607

private updateStakingInfo(address: string, stakingInfo: any) {

608

const account = this.accounts.get(address) || {};

609

account.staking = { ...account.staking, ...stakingInfo };

610

this.accounts.set(address, account);

611

612

console.log(`Staking info for ${address}:`, account.staking);

613

}

614

615

getAccountInfo(address: string) {

616

return this.accounts.get(address);

617

}

618

619

getAllAccounts() {

620

return Array.from(this.accounts.entries());

621

}

622

}

623

624

// Usage

625

const stateManager = new ChainStateManager(api);

626

627

// Query account info after some time

628

setTimeout(() => {

629

const accountInfo = stateManager.getAccountInfo('1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg');

630

console.log('Account info:', accountInfo);

631

}, 10000);

632

```