Decoding & encoding data

Depending on where data like transactions and blocks is processed further, it might be necessary to convert the data to a human- or a machine-readable format.

On this page, you’ll learn:

  • How blocks are encoded and decoded

  • How transactions are encoded and decoded

  • How the different formats of blocks and transactions look like

Connecting a node with the API client

Blocks and transactions can be decoded and encoded conveniently with the @liskhq/api-client library of the Lisk SDK.

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 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.

Install the lisk-client package like this:

npm i @liskhq/lisk-client

Then, create a new file api-client.js with a function getClient() that returns an instance of the API client when executed.

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

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

module.exports = { getClient };

Export the function getClient(), so it can be used to get data from the blockchain in the following sections.

Decoding & encoding blocks and their included transactions

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

getBlockById.js
const { getClient } = require('./api-client');
const assert = require("assert");

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) => {
	// Returns the block
	client.invoke("chain_getBlockByID", {
		id: blockId
	}).then(block => {
		// Decoded block as Object
		const blockObject = client.block.fromJSON(block);
		// Decoded block as JSON
		const jsonBlock = client.block.toJSON(blockObject);
		// Encoded block as Buffer
		const encodedBlock = client.block.encode(blockObject);
		// Decoded block as Object
		const decodedBlock = client.block.decode(encodedBlock);
		// Encoded block as Hex String
		const blockHexString = encodedBlock.toString('hex');
		console.log("Block: ", block);
		console.log("Block(Object): ", blockObject);
		console.log("Block(JSON): ", jsonBlock)
		console.log("Block(Buffer): ", encodedBlock);
		console.log("Block(Hex String): ", blockHexString);
		console.log("blockObject = decodedBlock? - ", assert.deepStrictEqual(blockObject,decodedBlock));
		process.exit(0);
	}).catch(err => {
		console.log("Error: ", err);
		process.exit(1);
	});
});

To encode or decode a specific block, execute the script with the block ID as the parameter:

node getBlockById.js 6533da00cec82c6d22b1c12f6cb4b6178fbfd9530e35d1018fcf4585085ec9c6

This will display the different block formats in the console, as described below.

Block formats

Block
{
  header: {
    version: 2,
    timestamp: 1679317980,
    height: 3596,
    previousBlockID: 'a7306072a98c7e758491a0fb842152dfeb90a29a53f9c07ee2e6a4169cd182fa',
    stateRoot: 'fd832267b28b176311f5996479f197940dc1de65b1c545ddb711eba2d2bdbc8b',
    assetRoot: '28a8603d4ed4d0a9bb722c0ffb92f2609963d4616c858dca6c64e4b74af98bbf',
    eventRoot: 'f83bd4c1aac2b07263c49d292f63c7c7031d2139d7eab2d0a2127ee17526dc0a',
    transactionRoot: '98c6520c40514db11da9aa0ba293665b8810d554374f21bdfcf78e7c486c5e8c',
    validatorsHash: '458f294af772546633809db0842bdbf8e3b4070e76ecbd1d926f4e630c1f2778',
    aggregateCommit: { height: 3450, aggregationBits: '', certificateSignature: '' },
    generatorAddress: 'lskz4bzo8zkfnzrs2f3zb3yfk2vz9px4bhk5vjeha',
    maxHeightPrevoted: 3528,
    maxHeightGenerated: 3462,
    impliesMaxPrevotes: true,
    signature: 'c1585c85cde44fdb6c64f9f95ca45445e902282579c6299c7752ae33ea464b2e41cd654756886877732b4a90180513b52e8816c71eeb59aa2b9208fc0949210a',
    id: '6533da00cec82c6d22b1c12f6cb4b6178fbfd9530e35d1018fcf4585085ec9c6'
  },
  transactions: [
    {
      module: 'token',
      command: 'transfer',
      params: '0a0803000008000000001080c2d72f1a1488c0ee8a4f8fa0e498770c70749584f179938ffa220c48656c6c6f20576f726c6421',
      nonce: '2',
      fee: '176000',
      senderPublicKey: 'ec10255d3e78b2977f04e59ea9afd3e9a2ce9a6b44619ef9f6c47c29695b1df3',
      signatures: [Array],
      id: '6ffb10cf563b809618a079f07791f2699be6989935e2582d5e5f4cc7afd2b4f9'
    }
  ],
  assets: [
    { module: 'random', data: '0a10690fbf905c9d4e0e970173195c7187fe' }
  ]
}
Decoded Block(JSON)
{
  header: {
    version: 2,
    timestamp: 1679317980,
    height: 3596,
    previousBlockID: 'a7306072a98c7e758491a0fb842152dfeb90a29a53f9c07ee2e6a4169cd182fa',
    stateRoot: 'fd832267b28b176311f5996479f197940dc1de65b1c545ddb711eba2d2bdbc8b',
    assetRoot: '28a8603d4ed4d0a9bb722c0ffb92f2609963d4616c858dca6c64e4b74af98bbf',
    eventRoot: 'f83bd4c1aac2b07263c49d292f63c7c7031d2139d7eab2d0a2127ee17526dc0a',
    transactionRoot: '98c6520c40514db11da9aa0ba293665b8810d554374f21bdfcf78e7c486c5e8c',
    validatorsHash: '458f294af772546633809db0842bdbf8e3b4070e76ecbd1d926f4e630c1f2778',
    aggregateCommit: { height: 3450, aggregationBits: '', certificateSignature: '' },
    generatorAddress: 'lskz4bzo8zkfnzrs2f3zb3yfk2vz9px4bhk5vjeha',
    maxHeightPrevoted: 3528,
    maxHeightGenerated: 3462,
    impliesMaxPrevotes: true,
    signature: 'c1585c85cde44fdb6c64f9f95ca45445e902282579c6299c7752ae33ea464b2e41cd654756886877732b4a90180513b52e8816c71eeb59aa2b9208fc0949210a',
    id: '6533da00cec82c6d22b1c12f6cb4b6178fbfd9530e35d1018fcf4585085ec9c6'
  },
  transactions: [
    {
      module: 'token',
      command: 'transfer',
      params: [Object],
      nonce: '2',
      fee: '176000',
      senderPublicKey: 'ec10255d3e78b2977f04e59ea9afd3e9a2ce9a6b44619ef9f6c47c29695b1df3',
      signatures: [Array],
      id: '6ffb10cf563b809618a079f07791f2699be6989935e2582d5e5f4cc7afd2b4f9'
    }
  ],
  assets: [ { module: 'random', data: [Object] } ]
}
Decoded Block(Object)
{
  header: {
    version: 2,
    timestamp: 1679317980,
    height: 3596,
    previousBlockID: <Buffer a7 30 60 72 a9 8c 7e 75 84 91 a0 fb 84 21 52 df eb 90 a2 9a 53 f9 c0 7e e2 e6 a4 16 9c d1 82 fa>,
    stateRoot: <Buffer fd 83 22 67 b2 8b 17 63 11 f5 99 64 79 f1 97 94 0d c1 de 65 b1 c5 45 dd b7 11 eb a2 d2 bd bc 8b>,
    assetRoot: <Buffer 28 a8 60 3d 4e d4 d0 a9 bb 72 2c 0f fb 92 f2 60 99 63 d4 61 6c 85 8d ca 6c 64 e4 b7 4a f9 8b bf>,
    eventRoot: <Buffer f8 3b d4 c1 aa c2 b0 72 63 c4 9d 29 2f 63 c7 c7 03 1d 21 39 d7 ea b2 d0 a2 12 7e e1 75 26 dc 0a>,
    transactionRoot: <Buffer 98 c6 52 0c 40 51 4d b1 1d a9 aa 0b a2 93 66 5b 88 10 d5 54 37 4f 21 bd fc f7 8e 7c 48 6c 5e 8c>,
    validatorsHash: <Buffer 45 8f 29 4a f7 72 54 66 33 80 9d b0 84 2b db f8 e3 b4 07 0e 76 ec bd 1d 92 6f 4e 63 0c 1f 27 78>,
    aggregateCommit: {
      height: 3450,
      aggregationBits: <Buffer >,
      certificateSignature: <Buffer >
    },
    generatorAddress: <Buffer 02 4c 06 3c 14 f1 c1 2d e3 c8 01 91 1f 53 02 03 48 14 9b b4>,
    maxHeightPrevoted: 3528,
    maxHeightGenerated: 3462,
    impliesMaxPrevotes: true,
    signature: <Buffer c1 58 5c 85 cd e4 4f db 6c 64 f9 f9 5c a4 54 45 e9 02 28 25 79 c6 29 9c 77 52 ae 33 ea 46 4b 2e 41 cd 65 47 56 88 68 77 73 2b 4a 90 18 05 13 b5 2e 88 ... 14 more bytes>,
    id: <Buffer 65 33 da 00 ce c8 2c 6d 22 b1 c1 2f 6c b4 b6 17 8f bf d9 53 0e 35 d1 01 8f cf 45 85 08 5e c9 c6>
  },
  transactions: [
    {
      module: 'token',
      command: 'transfer',
      params: [Object],
      nonce: 2n,
      fee: 176000n,
      senderPublicKey: <Buffer ec 10 25 5d 3e 78 b2 97 7f 04 e5 9e a9 af d3 e9 a2 ce 9a 6b 44 61 9e f9 f6 c4 7c 29 69 5b 1d f3>,
      signatures: [Array],
      id: <Buffer 6f fb 10 cf 56 3b 80 96 18 a0 79 f0 77 91 f2 69 9b e6 98 99 35 e2 58 2d 5e 5f 4c c7 af d2 b4 f9>
    }
  ],
  assets: [ { module: 'random', data: [Object] } ]
}
Encoded Block(Buffer)
<Buffer 0a c0 02 08 02 10 dc b7 e1 a0 06 18 8c 1c 22 20 a7 30 60 72 a9 8c 7e 75 84 91 a0 fb 84 21 52 df eb 90 a2 9a 53 f9 c0 7e e2 e6 a4 16 9c d1 82 fa 2a 14 ... 482 more bytes>
Encoded Block(Hex String)
"0ac002080210dcb7e1a006188c1c2220a7306072a98c7e758491a0fb842152dfeb90a29a53f9c07ee2e6a4169cd182fa2a14024c063c14f1c12de3c801911f53020348149bb4322098c6520c40514db11da9aa0ba293665b8810d554374f21bdfcf78e7c486c5e8c3a2028a8603d4ed4d0a9bb722c0ffb92f2609963d4616c858dca6c64e4b74af98bbf4220f83bd4c1aac2b07263c49d292f63c7c7031d2139d7eab2d0a2127ee17526dc0a4a20fd832267b28b176311f5996479f197940dc1de65b1c545ddb711eba2d2bdbc8b50c81b58861b60016a20458f294af772546633809db0842bdbf8e3b4070e76ecbd1d926f4e630c1f2778720708fa1a12001a007a40c1585c85cde44fdb6c64f9f95ca45445e902282579c6299c7752ae33ea464b2e41cd654756886877732b4a90180513b52e8816c71eeb59aa2b9208fc0949210a12b0010a05746f6b656e12087472616e7366657218022080df0a2a20ec10255d3e78b2977f04e59ea9afd3e9a2ce9a6b44619ef9f6c47c29695b1df332330a0803000008000000001080c2d72f1a1488c0ee8a4f8fa0e498770c70749584f179938ffa220c48656c6c6f20576f726c64213a40866c4a96e8d9dd27100520759e1974cea8db52bf0df4b7660b5a87c6e4cd4488c6c71ac6f6ebbd57188a355a331d10f318f814eae63fb966e4f8cea25530480f1a1c0a0672616e646f6d12120a10690fbf905c9d4e0e970173195c7187fe"

Decoding & encoding transactions

Create a new file getTxById.js and paste the following script, which decodes & encodes a given transaction.

getTxById.js
const { getClient } = require('./api-client');
const assert = require("assert");

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) => {
	// Returns the transaction
	client.invoke("chain_getTransactionByID", {
		id: txId
	}).then(tx => {
		// Decoded transaction as Object
		const txObject = client.transaction.fromJSON(tx);
		// Decoded transaction as JSON
		const jsonTx = client.transaction.toJSON(txObject);
		// Encoded transaction as Buffer
		const encodedTx = client.transaction.encode(txObject);
		// Encoded transaction as Hex String
		const txHexString = encodedTx.toString('hex');
		// Decodes transaction from Hex String to Object
		const decodedTx = client.transaction.decode(txHexString);
		console.log("Transaction: ", tx)
		console.log("Decoded transaction(Object): ", txObject);
		console.log("Decoded transaction(JSON): ", jsonTx)
		console.log("Encoded transaction(Buffer): ", encodedTx);
		console.log("Encoded transaction(Hex String): ", txHexString);
		const checkEqual = !assert.deepStrictEqual(txObject, decodedTx);
		console.log("txObject = decodedTx? - ", checkEqual);
		process.exit(0);
	}).catch(err => {
		console.log("Error: ", err);
		process.exit(1);
	});
});

To encode or decode a specific transaction, execute the script with the transaction ID as the parameter:

node getTxById.js 6ffb10cf563b809618a079f07791f2699be6989935e2582d5e5f4cc7afd2b4f9

This will display the different transaction formats in the console, as described below.

Transaction formats

Transaction
{
  module: 'token',
  command: 'transfer',
  params: '0a0803000008000000001080c2d72f1a1488c0ee8a4f8fa0e498770c70749584f179938ffa220c48656c6c6f20576f726c6421',
  nonce: '2',
  fee: '176000',
  senderPublicKey: 'ec10255d3e78b2977f04e59ea9afd3e9a2ce9a6b44619ef9f6c47c29695b1df3',
  signatures: [
    '866c4a96e8d9dd27100520759e1974cea8db52bf0df4b7660b5a87c6e4cd4488c6c71ac6f6ebbd57188a355a331d10f318f814eae63fb966e4f8cea25530480f'
  ],
  id: '6ffb10cf563b809618a079f07791f2699be6989935e2582d5e5f4cc7afd2b4f9'
}
Decoded Transaction(Object)
{
  module: 'token',
  command: 'transfer',
  params: {
    tokenID: <Buffer 03 00 00 08 00 00 00 00>,
    amount: 100000000n,
    recipientAddress: <Buffer 88 c0 ee 8a 4f 8f a0 e4 98 77 0c 70 74 95 84 f1 79 93 8f fa>,
    data: 'Hello World!'
  },
  nonce: 2n,
  fee: 176000n,
  senderPublicKey: <Buffer ec 10 25 5d 3e 78 b2 97 7f 04 e5 9e a9 af d3 e9 a2 ce 9a 6b 44 61 9e f9 f6 c4 7c 29 69 5b 1d f3>,
  signatures: [
    <Buffer 86 6c 4a 96 e8 d9 dd 27 10 05 20 75 9e 19 74 ce a8 db 52 bf 0d f4 b7 66 0b 5a 87 c6 e4 cd 44 88 c6 c7 1a c6 f6 eb bd 57 18 8a 35 5a 33 1d 10 f3 18 f8 ... 14 more bytes>
  ],
  id: <Buffer 6f fb 10 cf 56 3b 80 96 18 a0 79 f0 77 91 f2 69 9b e6 98 99 35 e2 58 2d 5e 5f 4c c7 af d2 b4 f9>
}
Decoded Transaction(JSON)
{
  module: 'token',
  command: 'transfer',
  params: {
    tokenID: '0300000800000000',
    amount: '100000000',
    recipientAddress: 'lskycz7hvr8yfu74bcwxy2n4mopfmjancgdvxq8xz',
    data: 'Hello World!'
  },
  nonce: '2',
  fee: '176000',
  senderPublicKey: 'ec10255d3e78b2977f04e59ea9afd3e9a2ce9a6b44619ef9f6c47c29695b1df3',
  signatures: [
    '866c4a96e8d9dd27100520759e1974cea8db52bf0df4b7660b5a87c6e4cd4488c6c71ac6f6ebbd57188a355a331d10f318f814eae63fb966e4f8cea25530480f'
  ],
  id: '6ffb10cf563b809618a079f07791f2699be6989935e2582d5e5f4cc7afd2b4f9'
}
Encoded Transaction(Buffer)
<Buffer 0a 05 74 6f 6b 65 6e 12 08 74 72 61 6e 73 66 65 72 18 02 20 80 df 0a 2a 20 ec 10 25 5d 3e 78 b2 97 7f 04 e5 9e a9 af d3 e9 a2 ce 9a 6b 44 61 9e f9 f6 ... 126 more bytes>
Encoded Transaction(Hex String)
'0a05746f6b656e12087472616e7366657218022080df0a2a20ec10255d3e78b2977f04e59ea9afd3e9a2ce9a6b44619ef9f6c47c29695b1df332330a0803000008000000001080c2d72f1a1488c0ee8a4f8fa0e498770c70749584f179938ffa220c48656c6c6f20576f726c64213a40866c4a96e8d9dd27100520759e1974cea8db52bf0df4b7660b5a87c6e4cd4488c6c71ac6f6ebbd57188a355a331d10f318f814eae63fb966e4f8cea25530480f'