Decoding & encoding data

The API of a blockchain node typically returns all requested transactions and blocks encoded. To be readable, they need to be decoded into JS or JSON compatible objects.

Blocks and transactions can be decoded conveniently with the apiClient library of the Lisk SDK.

Connecting a node with the API client

Before the API client can be used, it needs to connect to a blockchain node. This can be achieved either via a WebSocket (local or remote), or an IPC (only on the local server) connection.

In most scenarios, it is beneficial to have a local node running (for example, the Hello World application) to which the client can send the API requests. The RPC options in the config should be enabled and configured accordingly, see RPC configuration of a node.

The apiClient is also included in the NPM package @liskhq/lisk-client.
const { apiClient } = require('@liskhq/lisk-client');
let clientCache;
const nodeAPIURL = 'ws://localhost:8080/ws';

const getClient = async () => {
	if (!clientCache) {
        // Connect to the specified blockchain node
		clientCache = await apiClient.createWSClient(nodeAPIURL);
	}
	return clientCache;
};

Decoding & encoding blocks and their included transactions

The following script shows how to decode & encode a block by a given ID.

getBlockById.js:
const { apiClient } = require('@liskhq/lisk-client');
let clientCache;
const nodeAPIURL = 'ws://localhost:8080/ws';

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

if (process.argv.length < 3) {
	console.error("Please provide the block ID to be decoded.")
	process.exit(1);
}
const blockId = process.argv[2];


getClient().then((client) => {
	client.invoke("app:getBlockByID", {
		id: blockId
	}).then(res => {
		const decodedBlock = client.block.decode(res);
		const blockJSON = client.block.toJSON(decodedBlock);
		const blockObject = client.block.fromJSON(blockJSON);
		const encodedBlockObject = client.block.encode(blockObject);
		const encodedBlockAsHexString = encodedBlockObject.toString('hex');
		console.log("Encoded block: ", res)
		console.log("Decoded block: ", decodedBlock);
		console.log("Block as JSON compatible object: ", blockJSON);
		if (blockJSON.payload && blockJSON.payload.length > 0) {
			console.log(blockJSON.payload[0].asset);
		}
		console.log("Block from JSON to decoded block: ", blockObject);
		console.log("Encoded block object: ", encodedBlockObject);
		console.log("Encoded block as hex string: ", encodedBlockAsHexString)
		console.log("res = encodedBlockAsHexString? - ", (res == encodedBlockAsHexString));
		process.exit(0);
	}).catch(err => {
		console.log("Error: ", err);
		process.exit(1);
	});
});

Assuming you wish to decode a block with the ID 52303f33684f5ffae4d307747db36d86bde6e14d76958e197cb82eeab5ca6f92, then execute the script with the block ID as the parameter:

node getBlockById.js 52303f33684f5ffae4d307747db36d86bde6e14d76958e197cb82eeab5ca6f92

The decoded block JSON will look similar to the following, as shown below:

Block and transaction payload as JSON
{
  header: {
    version: 2,
    timestamp: 1641301752,
    height: 3445,
    previousBlockID: 'ac4d1c2af42f7c596c65e172d5ac68cd47e7c3d539979b648b0778910181cb7a',
    transactionRoot: 'eb3dc80f04a469b9c6224c2e69c3b22b01a81e8986cf42037dc5b8ff768f0e8c',
    generatorPublicKey: 'e8a9c5bb058377aee7ba833fe9f5cf4de3bd02fd9ec6fe749b00542d93b44ca0',
    reward: '500000000',
    signature: '3a0bdd57baa6a79d80c6004dcfa96420398fcb2da9fe75f5883ce9f7767692c290dd1f3f70509d0f5c62970567dc496fe7491f5c88f15a4b83067d7bd7440204',
    asset: {
      maxHeightPreviouslyForged: 3406,
      maxHeightPrevoted: 3356,
      seedReveal: '0445aab446d7bcfc533a40bad1056986'
    },
    id: '1dd06d95755984741260be1a7b07c512ddb8a1a1fb5c422d7f5b14995f8a85aa'
  },
  payload: [
    {
      moduleID: 2,
      assetID: 0,
      nonce: '2',
      fee: '141000',
      senderPublicKey: '5133af7944acf5278b0310a11c06134f80ab4546d77d1b0e027c8430a7d2bb92',
      signatures: [Array],
      id: '2dd42458cd3255bb2db9f19a32519e9dd7705d273683114822fbcfd85a1cea00',
      asset: [Object]
    }
  ]
}
{
  amount: '100000000',
  recipientAddress: 'ed86183c22f8399f7aa28f7d2c2f1680224f7281',
  data: ''
}

Decoding & encoding transactions

The transaction ID is returned every time a transaction is sent to the node.

Write a simple script that decodes the transaction. Create a new file getTxById.js:

Example for decoding a transaction by a given ID
const { apiClient } = require('@liskhq/lisk-client');
let clientCache;
const nodeAPIURL = 'ws://localhost:8080/ws';

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

if (process.argv.length < 3) {
	console.error("Please provide the transaction ID to be decoded.")
	process.exit(1);
}
const txId = process.argv[2];

getClient().then((client) => {
	client.invoke("app:getTransactionByID", {
		id: txId
	}).then(res => {
		const decodedTx = client.transaction.decode(res);
		const txJSON = client.transaction.toJSON(decodedTx);
		const txObject = client.transaction.fromJSON(txJSON);
		const encodedTxObject = client.transaction.encode(txObject)
		const encodedTxAsHexString = encodedTxObject.toString('hex')
		console.log("Encoded tx: ", res)
		console.log("Decoded tx: ", decodedTx);
		console.log("Tx as JSON compatible object: ", txJSON);
		console.log("Tx from JSON to decoded object: ", txObject);
		console.log("Encoded tx object: ", encodedTxObject);
		console.log("Encoded tx as hex string: ", encodedTxAsHexString)
		console.log("res = encodedTxAsHexString? - ", (res == encodedTxAsHexString));
		process.exit(0);
	});
});

Assuming you wish to display a transaction with the ID 130227fa63ac60edbbacb6dae709cf9304ab0181ef7ea28105764f6240d012f2 in the different existing formats, then execute the script with the transaction ID as the parameter:

node getTxById.js 130227fa63ac60edbbacb6dae709cf9304ab0181ef7ea28105764f6240d012f2
Example output
Decoded tx
{
moduleID: 2,
assetID: 0,
nonce: 4n,
fee: 1000000n,
senderPublicKey: <Buffer 51 33 af 79 44 ac f5 27 8b 03 10 a1 1c 06 13 4f 80 ab 45 46 d7 7d 1b 0e 02 7c 84 30 a7 d2 bb 92>,
asset: {
amount: 800000000n,
recipientAddress: <Buffer 9b d8 2e 63 7d 30 65 33 b1 e1 ad 66 e1 9c a0 04 7f aa 1a 6a>,
data: 'Happy birthday!'
},
signatures: [
<Buffer 98 a9 ee 2c de 83 54 d0 14 cf e6 36 7d 43 0b e2 71 3e 10 2f 37 d9 2a b9 1f 03 db 78 04 07 e5 bf 6d 81 8a 45 c2 1c 9f 55 18 63 8d fc 3c 53 65 fc 2d 49 ... 14 more bytes>
],
id: <Buffer 17 a0 90 44 15 f7 ad 9a 0e 30 5a e8 a4 34 53 24 3a 84 e2 af e1 76 08 3a 0a b3 3f b1 2e 3b bc 25>
}
Transaction as JSON compatible object
{
moduleID: 2,
assetID: 0,
nonce: '4',
fee: '1000000',
senderPublicKey: '5133af7944acf5278b0310a11c06134f80ab4546d77d1b0e027c8430a7d2bb92',
signatures: [
'98a9ee2cde8354d014cfe6367d430be2713e102f37d92ab91f03db780407e5bf6d818a45c21c9f5518638dfc3c5365fc2d497b928e0b9d6337988df46a663a02'
],
asset: {
amount: '800000000',
recipientAddress: '9bd82e637d306533b1e1ad66e19ca0047faa1a6a',
data: 'Happy birthday!'
},
id: '17a0904415f7ad9a0e305ae8a43453243a84e2afe176083a0ab33fb12e3bbc25'
}
Decoded transaction object
{
moduleID: 2,
assetID: 0,
nonce: 4n,
fee: 1000000n,
senderPublicKey: <Buffer 51 33 af 79 44 ac f5 27 8b 03 10 a1 1c 06 13 4f 80 ab 45 46 d7 7d 1b 0e 02 7c 84 30 a7 d2 bb 92>,
signatures: [
<Buffer 98 a9 ee 2c de 83 54 d0 14 cf e6 36 7d 43 0b e2 71 3e 10 2f 37 d9 2a b9 1f 03 db 78 04 07 e5 bf 6d 81 8a 45 c2 1c 9f 55 18 63 8d fc 3c 53 65 fc 2d 49 ... 14 more bytes>
],
asset: {
amount: 800000000n,
recipientAddress: <Buffer 9b d8 2e 63 7d 30 65 33 b1 e1 ad 66 e1 9c a0 04 7f aa 1a 6a>,
data: 'Happy birthday!'
},
id: <Buffer 17 a0 90 44 15 f7 ad 9a 0e 30 5a e8 a4 34 53 24 3a 84 e2 af e1 76 08 3a 0a b3 3f b1 2e 3b bc 25>
}
Encoded transaction as Buffer
<Buffer 08 02 10 00 18 04 20 c0 84 3d 2a 20 51 33 af 79 44 ac f5 27 8b 03 10 a1 1c 06 13 4f 80 ab 45 46 d7 7d 1b 0e 02 7c 84 30 a7 d2 bb 92 32 2d 08 80 90 bc ... 107 more bytes>
Encoded tx as hex string
'08021000180420c0843d2a205133af7944acf5278b0310a11c06134f80ab4546d77d1b0e027c8430a7d2bb92322d088090bcfd0212149bd82e637d306533b1e1ad66e19ca0047faa1a6a1a0f4861707079206269727468646179213a4098a9ee2cde8354d014cfe6367d430be2713e102f37d92ab91f03db780407e5bf6d818a45c21c9f5518638dfc3c5365fc2d497b928e0b9d6337988df46a663a02'