Retrieving on-chain events

On this page, you’ll learn about how events can be subscribed to or retrieved from a Lisk blockchain.

Specifically, you’ll learn:

  • The difference between RPC events and blockchain events

  • How subscribe to RPC events

  • How retrieve blockchain events

The difference between RPC events and blockchain events

The Lisk blockchain generates various events that emit on-chain data. The emitted events can either be RPC Events or Blockchain Events. The aforementioned events are different in terms of when and how they are generated and also how these events can be accessed.

Lisk makes RPC events accessible to everyone by offering a range of RPC-based events. These events can be subscribed to by anyone in the network. The subscribers can then get real-time information about the subscribed event, for example, the generation of a new block on the network, the addition of a new transaction in the transaction pool, etc. RPC events offered by Lisk can be subscribed to directly by any plugin, external service, or application.

The other type of event emitted by Lisk is blockchain events. Such events cannot be directly subscribed to with the RPC protocol. Blockchain events are usually emitted at a modular level, during block execution and they reside in the event log. The event log keeps the blockchain events emitted from the latest set of blocks. Blockchain events can have information about a successful or failed transaction, among other information. For more information on events and events root, see LIP 65.

The number of events stored by the event log depends on the configuration of the node. By default, a node is configured to store the blockchain events from the latest 300 blocks in the event log. Due to this, events from only those 300 blocks can be retrieved, by default. This can be changed by updating the keepEventsForHeights property in the node configuration.

To store the blockchain events on a node forever, set keepEventsForHeights to 0 in the node configuration.

Subscribing to an RPC event

The API client simplifies the RPC event subscription process not only for a blockchain client but also for external plugins and services. With the API client, RPC-based interfaces i.e. IPC and WS can be used to subscribe to RPC events.

An API client can be imported into any JS client application as shown in the following snippets.

To conveniently communicate with a blockchain node, use the apiClient included in the @liskhq/lisk-client and the lisk-sdk packages.
  • WS API client example

  • IPC API client example

const { apiClient } = require('@liskhq/lisk-client');
let clientCache;
const nodeAPIURL = 'ws://localhost:7887/rpc-ws';

const getClient = async () => {
    if (!clientCache) {
        clientCache = await apiClient.createWSClient(nodeAPIURL);
    }
    return clientCache;
};

getClient().then((client) => {
    client.subscribe('network_newBlock', ( data ) => {
    console.log('new block: ',data);
    });
});
const { apiClient } = require('@liskhq/lisk-client');
let clientCache;

const getClient = async () => {
    if (!clientCache) {
        clientCache = await apiClient.createIPCClient('~/.lisk/my-app');
    }
    return clientCache;
};

getClient().then((client) => {
    client.subscribe('network_newBlock', ( data ) => {
    console.log('new block: ',data);
    });
});

Retrieving a blockchain event

With each block execution, various events are generated and stored in the event log. For example, modules such as Reward emit the rewardMinted event after each block execution.

Different from the RPC events, blockchain events cannot be directly subscribed to. Instead, blockchain events are requested for a particular block. The block is defined by the block height which is provided as a parameter in the request.

To fetch such an event, we can use the chain_getEvents endpoint, as shown below:

fetchBlockchainEvent.js
const { apiClient } = require('@liskhq/lisk-client');
const { codec } = require('@liskhq/lisk-codec');
let clientCache;
const nodeAPIURL = 'ws://127.0.0.1:7887/rpc-ws'

const getClient = async () => {
    if (!clientCache) {
        clientCache = await apiClient.createWSClient(nodeAPIURL);
    }
    return clientCache;
};

const rewardMintedDataSchema = {
    $id: '/reward/events/rewardMintedData',
    type: 'object',
    required: ['amount', 'reduction'],
    properties: {
        amount: {
            dataType: 'uint64',
            fieldNumber: 1,
        },
        reduction: {
            dataType: 'uint32',
            fieldNumber: 2,
        },
    },
};

getClient().then((client) => {
    // Returns the encoded event based on the 'height' passed
    client.invoke("chain_getEvents", {
        height: 60
    }).then(res => {
        console.log("Event: ", res);
        // Decode the aforementioned event's data by passing relevant schema and the encoded 'data'
        const parsedData = codec.decode(rewardMintedDataSchema, Buffer.from(res[0]['data'], 'hex'));
        console.log(parsedData);
    });
});

Once an event is retrieved from the event log, its data property can be decoded by using the codec.decode() function. This function takes in the encoded data and the relevant schema as arguments.

The codec.decode() function is available inside the @liskhq/lisk-codec package.

A detailed example of emitting a blockchain event, fetching it, and decoding it is available in the Hello World blockchain example.

Response
Make sure your blockchain client is running before executing the script
$ node fetchBlockchainEvent.js
Event:  [
  {
    data: '08001000',
    index: 0,
    module: 'reward',
    name: 'rewardMinted',
    topics: [ '03', 'aa84845c4bc4e75802921fc315a01576c75ade73' ],
    height: 60
  }
]
Decoded data: { amount: 0n, reduction: 0 }