DEV Community

Cover image for ICON Network and JavaScript: How to interact with the ICON Blockchain with JS (node.js)
FidelVe for Espanicon

Posted on

ICON Network and JavaScript: How to interact with the ICON Blockchain with JS (node.js)

The ICON Network is a decentralized blockchain project from South Korea with the goal of becoming a hub for interconnecting many other blockchains with its BTP technology, it currently hosts many Dapps (Decentralized Apps) ranging from NFT games and platforms ( like gangstabet, Craft Network, Project Nebula, etc) to DeFi platforms (like Balanced Network, OMM Finance and Optimus Finance) and even a fully decentralized casino running entirely on smart contracts (ICONBet).

This is the first article in a series dedicated to show how to interact with the ICON Network using nodejs. While the article is directed to people that have no prior knowledge of how to interact with smarts contracts on ICON or any other blockchain project, it is necessary to have a prior knowledge of coding with JavaScript (specially asynchronous programming with JavaScript) and nodejs (knowing how to use modules is necessary).

Understanding the JSON RPC API

All your interactions with the ICON Network and the different smart contracts in the chain are going to be done via a JSON RPC API.

The JSON RPC API is a remote procedure call encoded in JSON, to put it in simple terms, you are going to be sending data in a predefined schema or format to a smart contract in the ICON Network and if all goes well you'll be receiving a reply with the data you asked (if things don't go well you'll still receive an error reply too).

This is the description of the ICON JSON RPC API V3 and this is how a common call and response would look like.

// Request
{
    "jsonrpc": "2.0",
    "method": "$STRING1",
    "id": $INT,
    "params": {
        "$KEY1": "$VALUE1",
        "$KEY2": {
            "method": "$STRING2",
            "params": {
                "$KEY3": "$VALUE3"
            }
        }
    }
}

// Response - success
{
    "jsonrpc": "2.0",
    "id": $INT,
    "result": "$STRING"
    // or
    "result": {
      "$KEY1": "$VALUE1",
      "$KEY2": "$VALUE2"
    }
}

// Response - error
{
    "jsonrpc": "2.0",
    "id": $INT1,
    "error": {
        "code": $INT2,
        "message": "$STRING"
    }
}
Enter fullscreen mode Exit fullscreen mode

The communication to the ICON Network is done from your code (any script or Dapp you build) to a citizen node (which is basically a full node with no governance participation in the network, a silent listener of the chain) an then to an active governance node (called Public Representatives or P-Reps) that is in charge to act up or process the interactions and/or transactions you are going to be making and then it propagates those changes to the entire network. The following diagram shows these interaction.

Talking with the ICON Network via the JSON RPC API Image taken from the "ICON Deconstructed Part III" article on Medium

Things to know before talking to the chain, the https module and JS promises

As shown in the previous image there are several SDK in different languages that you can use but for the purpose of this article and because we are going to start with simple things we will be doing the communication directly via HTTPS requests.

Now that we know what is the language that we will be using to communicate to the ICON Network (JSON RPC API), there is one important piece of code that we need to write which will become our main module to talk to the ICON Network and that is an HTTPS request wrapped in a promise to allow for asynchronous interactions with the network.

Assuming that you already have nodejs installed in your computer, go and create a folder and then run npm init to create a new project. The folder structure in the project is going to look like this:

dev.to-article/
├── httpsRequest.js
├── index.js
└── package.json

0 directories, 3 files
Enter fullscreen mode Exit fullscreen mode

We are going to start working on the httpsRequest.js file. The content is pretty straightforward we import the https module, create an async function wrap the https request inside this function and execute it using await to get the response from the network and then at the end we export the httpsRequest async function as a module.

// httpsRequest.js
// This module is a https request wrapped in a promise design to be used
// to interact with the ICON Blockchain
//
// Imports
const https = require("https");

/**
 * async https request wrapped in a promise
 * @param {Object} param - params for the http request
 * @param {string} param.hostname
 * @param {string} param.ip
 * @param {number} param.port
 * @param {number} param.timeout
 * @param {string} param.path
 */
async function httpsRequest(params, data = false) {
  const promisifiedQuery = new Promise((resolve, reject) => {
    const query = https.request(params, res => {
      // Print status code on console
      console.log("Status Code: " + res.statusCode);

      // Process chunked data
      let rawData = "";
      res.on("data", chunk => {
        rawData += chunk;
      });

      // when request completed, pass the data to the 'resolve' callback
      res.on("end", () => {
        let data = JSON.parse(rawData);
        resolve(data);
      });

      // if error, print on console
      res.on("error", err => {
        console.log("Got error: ", +err.message);
      });
    });
    // If request timeout destroy request
    query.on("timeout", () => {
      console.log("timeout. destroying query");
      query.destroy();
    });
    // Handle query error
    query.on("error", err => {
      console.log("error running query, passing error to callback reject");
      reject(err);
    });
    if (data != false) {
      // If data param is passed into function we write the data
      query.write(data);
    }
    // end request
    query.end();
  });
  // wait for the response and return it
  try {
    return await promisifiedQuery;
  } catch (err) {
    console.log("error while running promisifiedQuery");
    console.log(err);
  }
}
module.exports = httpsRequest;
Enter fullscreen mode Exit fullscreen mode

Talking to the governance smart contract on the ICON Network

On the index.js file we are going to start by importing the httpsRequest.js module and then start creating the JSON RPC formatted request we will be sending to the ICON network.

// index.js
const httpsRequest = require('./httpsRequest.js');

// These are the params of the https request
const PARAMS = {
  hostname: 'api.icon.geometry.io', // This is a citizen node in the network
  path: '/api/v3',
  method: 'POST' // all https request to the network are POST requests
};

// This is the JSON RPC formatted call we are sending to the network
const DATA = JSON.stringify({
  jsonrpc: '2.0',
  method: 'icx_call',
  id: 1,
  params: {
    to: 'cx0000000000000000000000000000000000000000',
    dataType: 'call',
    data: {
      method: 'getPReps',
      params: {
        startRanking: '0x1',
        endRanking: '0x16'
      }
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

In this specific example we are interacting with the governance smart contract in the ICON Network (cx000...) and calling the method getPReps, this will allow us to get a list of the P-Reps (node validators) in the network, also since there are more than 100 P-Reps in the network we are passing a startRanking and endRanking values to get only the top 22 P-Reps by delegation in the network.

The final code in the index.js file should look like this:

// index.js
const httpsRequest = require("./httpsRequest.js");

// These are the params of the https request
const PARAMS = {
  hostname: "api.icon.geometry.io", // This is a citizen node in the network
  path: "/api/v3",
  method: "POST" // all https request to the network are POST requests
};

// This is the JSON RPC formatted call we are sending to the network
const DATA = JSON.stringify({
  jsonrpc: "2.0",
  method: "icx_call",
  id: 1,
  params: {
    to: "cx0000000000000000000000000000000000000000",
    dataType: "call",
    data: {
      method: "getPReps",
      params: {
        startRanking: "0x1",
        endRanking: "0x16"
      }
    }
  }
});

// Run async httpsRequest and print response on console
(async () => {
  try {
    let response = await httpsRequest(PARAMS, DATA);
    console.log(response);
  } catch (err) {
    console.log("Error running httpsRequest");
    console.log(err);
  }
})();
Enter fullscreen mode Exit fullscreen mode

After running node index.js we are going to get a reply like this from the smart contract:

dev.to-article$ node index.js 
Status Code: 200
{
  jsonrpc: '2.0',
  result: {
    blockHeight: '0x2b924c6',
    preps: [
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object]
    ],
    startRanking: '0x1',
    totalDelegated: '0x1205ef103fffce01d3dd1ff',
    totalStake: '0x13f0b214efa073bac2d0d53'
  },
  id: 1
}
Enter fullscreen mode Exit fullscreen mode

Congratulations! now you know how to interact with smart contracts on the ICON Network!, I invite you to join the community by visiting the forum and telegram channel for developers and keep learning so that you can also build amazing projects in the ICON Ecosystem, we have a decentralized funding program called CPS (Contribution Proposal System) that anyone can use to finance and build projects on the ICON Network so if you have a great idea please come and build with us!.

If you like this article please leave a comment and share it on social media, more articles will be coming explaining things about interacting with smart contracts on the ICON Network.

Thanks for reading!.

Discussion (0)