Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Libraries

Ethers.js

Ethers.js is a library which abstracts away many of the complexities associated with interacting with EVM blockchains.

Here we will run through the eth_call example in two different ways, this time using ethers.js.

First, we need to install ethers into our project.

Installation

Simply install ethers using node package manager (npm):

npm install ethers

Setup

Create a new script named ‘ethers_call.ts’ and open it.

We begin by importing ethers into our script. It is easiest to import everything from ethers to begin with. This is done with the line:

import { ethers } from "ethers";

From here, we need to connect to the RPC node (ethers still needs to know where to get the information we need from). This introduces the idea of a provider. A Provider is a read-only connection to the blockchain. Ethers also has another object, the Signer, which uses an RPC node along with a private key to allow write access to the blockchain. We will cover this later.

We create a provider instance with the line:

const provider = new ethers.JsonRpcProvider("https://rpc.adiri.tel");

This establishes the connection to the RPC node and gives us an object on which we can call ethers functions.

Simple use

A simple function to start is just getting the block number. If we write the script:

import { ethers } from "ethers";

// Set up the provider to connect to an Ethereum node
const provider = new ethers.JsonRpcProvider("https://rpc.adiri.tel");

async function fetchData() {
  console.log(await provider.getBlockNumber());
}
fetchData();

and run it, we should print the current block number to the console. Note it prints a decimal integer value, rather than the hexadecimal we got when calling the node using HTTP requests. Ethers handles the conversion automatically.

Using call()

Our eth_call example becomes slightly simpler too:

import { ethers } from "ethers";

// Set up the provider to connect to an Ethereum node
const provider = new ethers.JsonRpcProvider("https://rpc.adiri.tel");

async function fetchData() {
  const result = await provider.call({
    to: "0xc9593289e95938FC4170B3Fc51dbcCa0A46b4486",
    data: "0x06fdde03",
  });

  // Log the result
  console.log("Response Data:", result);
}

fetchData();

This, when run should give the response:

0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b54656c636f696e20415544000000000000000000000000000000000000000000 as before.

Directly calling functions

There is, however, an alternative approach to getting the name of the token, or calling any other read function on a given contract. This involves creating an ethers Contract instance, which requires a contract address, a provider and the contract Application Binary Interface (ABI). We have the first two, and the third is available here. The ABI provides ethers with important context relating to a contract’s functionality and how to interact with it.

Handling the ABI

In your project’s root folder, create a file named ‘ABI.json’ and copy and paste the ABI from the documentation link. The ABI is produced by the solidity compiler and is derived from the solidity smart contract code.

We can then import this information to our script using the built in fs (File System) and path modules:

const abiPath = path.join(__dirname, 'ABI.json');
const abiJson = fs.readFileSync(abiPath, 'utf8');
const abi = JSON.parse(abiJson);

In the above 3 lines we:

  1. create the string representing the path to the ABI.json file
  2. Read the data stored within the ABI.json file
  3. convert the read data to JSON format for use with ethers.

Creating the Contract Object

We now have all the components required to create an ethers Contract object which we can do in the following way:

// Create the contract instance with the provider
  const contract = new ethers.Contract(
    "0xc9593289e95938FC4170B3Fc51dbcCa0A46b4486",
    abi,
    provider
  );

Here we have provided the contract address, ABI and provider to create a new instance of a Contract object which we save to the variable named contract. More information can be found in the relevant section of the documentation.

All that is left is for us to use the contract. We can do this easily by calling the function names as they appear on the smart contract. In our case, we know the token inherits from the ERC 20 token standard. It will therefore have a getter function named name() (this is the one we have been calling up to now). To get the name of the token we write:

const name = await contract.name();

Building the Script

Putting all of these elements together might give us something like:

import { ethers } from "ethers";
import fs from "fs";
import path from "path";

const provider = new ethers.JsonRpcProvider("https://rpc.adiri.tel");

async function fetchData() {
  const abiPath = path.join(__dirname, "ABI.json"); // Adjust the path as necessary
  const abiJson = fs.readFileSync(abiPath, "utf8");
  const abi = JSON.parse(abiJson);

  const contract = new ethers.Contract(
    "0xc9593289e95938FC4170B3Fc51dbcCa0A46b4486",
    abi,
    provider
  );

  const name = await contract.name();
  console.log("Contract Name:", name);
}

fetchData();

Feel free to overwrite the previous code from the ‘Using call()’ section.

Running the Script

We run this with:

npx tsc

which compiles our typescript scripts to JavaScript, followed by:

node ethers_call.js

Response

The script should return:

Contract Name: Telcoin AUD

Note the string has been automatically converted from hexadecimal.

Reading Decimals

It is also very easy to call other functions on the contract - we just change the function name. For example, we can now easily query how many decimals this contract uses, by replacing:

const name = await contract.name();

with:

const name = await contract.decimals();

recompiling and rerunning the script gives:

Contract Name: 6n

Showing the contract uses 6 decimals. This is correct.

Conclusion

Hopefully this series of tutorials has demonstrated how to query a blockchain, and the advantages of using libraries to gather data rather than raw HTTP requests. This should serve as useful context as you continue to learn about Telcoin Network!