Jan 30, 2023

Building on eCash: Creating a Demo App Using the Chronik Indexer
Building on eCash: Creating a Demo App Using the Chronik Indexer
Building on eCash: Creating a Demo App Using the Chronik Indexer

Building on eCash: Creating a Demo App Using the Chronik Indexer

Building on eCash: Creating a Demo App Using the Chronik Indexer

Joey, lead dev, Cashtab

Last month, I wrote about available tools and libraries for developers looking to get started with eCash. This month, let’s put together a quick demo app using the chronik indexer.

Here are some basic bash command prompt instructions. You will need nodejs to follow the example here.

First, we create the directory for our app, initialize an npm module, install chronik, and create our first script.

{% c-block language="bash"%}
$ mkdir node-chronik-testdrive
$ cd chronik-node-testdrive
$ npm init
$ npm i chronik-client
$ touch getDetailsFromTxid.js
{% c-block-end %}

Next, let's write a function that queries {% c-line %}chronik{% c-line-end %}  for information about a transaction.

{% c-block language="js" %}
/*getDetailsFromTxid.js*/
const { ChronikClient } = require('chronik-client');
const chronik = new ChronikClient('https://chronik.fabien.cash');
async function getTxDetails(txid) {
   let txDetails;
   try {
       txDetails = await chronik.tx(txid);
       console.log(txDetails);
   } catch (err) {
       console.log(`Error in chronik.tx(${txid})`);
       console.log(err);
   }
}
getTxDetails(process.env.TXID);
{% c-block-end %}

Now we can run the script from the command line by entering a txid as an env variable.

{% c-block language="bash"%}
$ TXID=3c8f39cbfb663312010fe9279e99d147c0a676416ebd131df746081f41fdce8c node getDetailsFromTxid.js
$ {
 txid: '3c8f39cbfb663312010fe9279e99d147c0a676416ebd131df746081f41fdce8c',
 version: 2,
 inputs: [
   {
     prevOut: [Object],
     inputScript: '48304502210097277ddff25681cc5e303de359e7812364b7b2adc179081e9652fede69d97b5b02200f029c01eae95787ae6e9cf477999e5e3db081f631184933b2e16dd0e5414f484121020cba405a7e2396485b94d70a21dbdba29fb6a61970ed3a210dc30bf879f1059d',
     outputScript: '76a914e0a3c5d6dc80ee3a2e084dca41a6ac9a4bf3f2e288ac',
     value: '428143644042',
     sequenceNo: 4294967295,
     slpBurn: undefined,
     slpToken: undefined
   }
 ],
 outputs: [
   {
     value: '10848865900',
     outputScript: '76a9145511fb10a3de1ade2ecde41176139b1425e1b21788ac',
     slpToken: undefined,
     spentBy: [Object]
   },
   {
     value: '417294777712',
     outputScript: '76a914e0a3c5d6dc80ee3a2e084dca41a6ac9a4bf3f2e288ac',
     slpToken: undefined,
     spentBy: [Object]
   }
 ],
 lockTime: 0,
 slpTxData: undefined,
 slpErrorMsg: undefined,
 block: {
   height: 776279,
   hash: '00000000000000000639809bb892ddd9f739ab18bf6a734ee07299965affac1a',
   timestamp: '1674602795'
 },
 timeFirstSeen: '1674602114',
 size: 226,
 isCoinbase: false,
 network: 'XEC'
}
{% c-block-end %}

Great! Now we can use this script to get tx details for any given txid. 

What else can we do with chronik?

To build a basic wallet app, you need to know how much eCash is available to spend at the wallet's address. Spendable eCash is stored in objects called "utxos," or "unspent transaction outputs." Let’s write a simple script to get utxos from a wallet:

{% c-block language="bash"%}
$ touch getUtxosFromAddress.js
{% c-block-end %}

{% c-block language="js" %}
/*getUtxosFromAddress.js*/
const { ChronikClient } = require('chronik-client');
const chronik = new ChronikClient('https://chronik.fabien.cash');

async function getUtxos(address) {
   /*Note: chronik requires a hash160, but this function accepts an address input
   We'll need some way of converting an address to a hash160*/
   let hash160 = someFunctionOfAddress(address);
   let utxos;

   try {
       utxos = await chronik.script('p2pkh', hash160).utxos();
       console.log(utxos[0].utxos);
   } catch (err) {
       console.log(`Error in chronik.utxos(${hash160})`);
       console.log(err);
   }
}
getUtxos(process.env.ADDRESS);
{% c-block-end %}
While we are used to thinking of wallets and addresses, chronik accesses information about an address at a lower level. So, for a typical wallet address, we’ll need to first get the hash160 before we can see its utxo set. Let's write a helper function to get the hash160 from an eCash address.

{% c-block language="bash"%}
$ npm i ecashaddrjs bs58
$ touch helperFunctions.js
{% c-block-end %}

{% c-block language="js" %}
/*helperFunctions.js*/
const ecashaddr = require('ecashaddrjs');
const bs58 = require('bs58');
module.exports = {
   addressToHash160: function (address) {
       try {
       /* decode address hash */
       const { hash } = ecashaddr.decode(address);
       /* encode the address hash to legacy format (bitcoin) */
       const legacyAdress = bs58.encode(hash);
       /* convert legacy to hash160 */
       const hash160 = Buffer.from(bs58.decode(legacyAdress)).toString(
           'hex',
       );
       return hash160;
   } catch (err) {
       console.log('Error converting address to hash160');
       throw err;
   }
   },
};
{% c-block-end %}

Now we can go back and include the helper function {% c-line %}addressToHash160{% c-line-end %} in getUtxosFromAddress.js

{% c-block language="js" %}
/* getUtxosFromAddress.js */
const { ChronikClient } = require('chronik-client');
const chronik = new ChronikClient('https://chronik.fabien.cash');
const { addressToHash160 } = require('./helperFunctions');
async function getUtxos(address) {
 /* Convert address to hash160 */
 const hash160 = addressToHash160(address);
 let utxos;
 try {
     utxos = await chronik.script('p2pkh', hash160).utxos();
     console.log(utxos[0].utxos);
 } catch (err) {
     console.log(`Error in chronik.utxos(${hash160})`);
     console.log(err);
 }
}
getUtxos(process.env.ADDRESS);
{% c-block-end %}

...so now, we can get the utxo set of an eCash address by running this script from the command line.

{% c-block language="bash"%}
$ ADDRESS=ecash:qzqk7ephyf66s8829ywcjv52fuut35q77u8hqcpvdq node getUtxosFromAddress.js
$ [
 {
   outpoint: {
     txid: '3e3cc96ae606653159a6a4920915742a3a3591609a269d623c2bda26339ed9aa',
     outIdx: 1
   },
   blockHeight: -1,
   isCoinbase: false,
   value: '546',
   slpMeta: {
     tokenType: 'FUNGIBLE',
     txType: 'SEND',
     tokenId: '98183238638ecb4ddc365056e22de0e8a05448c1e6084bae247fae5a74ad4f48',
     groupTokenId: undefined
   },
   slpToken: { amount: '1234', isMintBaton: false },
   network: 'XEC'
 },
 {
   outpoint: {
     txid: '816d32c855e40c4221482eb85390a72ba0906360197c297a787125e6979e674e',
     outIdx: 0
   },
   blockHeight: -1,
   isCoinbase: false,
   value: '1200000',
   slpMeta: undefined,
   slpToken: undefined,
   network: 'XEC'
 }
]
{% c-block-end %}

Great! What about tx history?

{% c-block language="bash"%}
$ touch getTxHistoryFromAddress.js
{% c-block-end %}

{% c-block language="js" %}
/* getTxHistoryFromAddress.js */
const { ChronikClient } = require('chronik-client');
const chronik = new ChronikClient('https://chronik.fabien.cash');
const { addressToHash160 } = require('./helperFunctions');
async function getTxHistory(address) {
 const hash160 = addressToHash160(address)
   let history;
   try {
       history = await chronik
           .script('p2pkh', hash160)
           .history(/*page=*/ 0, /*page_size=*/ 10);
       console.log(history);
   } catch (err) {
       console.log(`Error in chronik.script('p2pkh', ${hash160}).history()`);
       console.log(err);
   }
}
getTxHistory(process.env.ADDRESS);
{% c-block-end %}

{% c-block language="bash"%}
$ ADDRESS=ecash:qzqk7ephyf66s8829ywcjv52fuut35q77u8hqcpvdq node getTxHistoryFromAddress.js
$ {
 txs: [
   {
     txid: '3e3cc96ae606653159a6a4920915742a3a3591609a269d623c2bda26339ed9aa',
     version: 2,
     inputs: [Array],
     outputs: [Array],
     lockTime: 0,
     slpTxData: [Object],
     slpErrorMsg: undefined,
     block: undefined,
     timeFirstSeen: '1674687524',
     size: 481,
     isCoinbase: false,
     network: 'XEC'
   },
   {
     txid: '816d32c855e40c4221482eb85390a72ba0906360197c297a787125e6979e674e',
     version: 2,
     inputs: [Array],
     outputs: [Array],
     lockTime: 0,
     slpTxData: undefined,
     slpErrorMsg: undefined,
     block: undefined,
     timeFirstSeen: '1674687499',
     size: 226,
     isCoinbase: false,
     network: 'XEC'
   }
 ],
 numPages: 1
}
{% c-block-end %}

The final app is available here. You could also check out Cashtab's source code to see how to use chronik in a React app. Or check out the eCash development telegram chat if you have any questions about using these tools to build on eCash.













You may also like
You may also like
You may also like