Skip to main content
Create your own

Interacting with Smart Contracts using Node.js and Ethers.js

Welcome to Module 11. In the preceding modules, you successfully designed, built, and rigorously tested a complete SecurityToken smart contract. You've mastered the on-chain logic, from issuance and compliance to dividend distribution. Now, we shift our focus from the internal workings of the contract to its external interactions.

This lesson marks your first step into the world of DApp integration and off-chain automation. Our goal is to build a standalone Node.js script using ethers.js to connect to and read data from your deployed smart contract. Your background as a BI analyst provides a strong parallel: this process is analogous to writing a script to connect to a data warehouse via a driver, where you use connection strings and schemas to query specific data tables. Here, the blockchain is our data source, and ethers.js is our powerful client library.

By the end of this lesson, you will be able to bridge the gap between your on-chain contract and off-chain applications, a fundamental skill for any blockchain developer.

1. The Anatomy of an Off-Chain Connection

To communicate with a smart contract from an external application, you need three key pieces of information. Think of them as the coordinates, the language, and the communication line needed to talk to your contract on the Ethereum network.

  1. Contract Address: The unique address where your contract is deployed on the blockchain. This is like the server IP address and port for a database.
  2. ABI (Application Binary Interface): A JSON file that describes the contract's functions and events. The ABI tells your script what functions are available, what arguments they expect, and what they return. It's the equivalent of a database schema or an API specification (like an OpenAPI/Swagger file).
  3. Provider: Your connection to an Ethereum node. A node is a computer participating in the Ethereum network, and it's your gateway to reading blockchain data. Instead of running our own resource-intensive node, we'll use a node provider service like Alchemy.

The following flowchart illustrates how these components work together. Your script (the "Developer" part) uses ethers.js to combine the provider, address, and ABI to create a contract instance and then call its functions.

This flowchart shows the workflow for interacting with a smart contract using ethers.js. We will focus on the "Read Function" path in this lesson.

2. Gathering Your Connection Details

Before we write any code, let's gather the necessary pieces.

Provider: Your Alchemy Connection

First, you need a connection to an Ethereum network. We will use the Sepolia testnet.

How to Read Ethereum Smart Contracts with ethers.js (2024)

This guide from Diluk Angelo's blog provides a clear explanation of the infrastructure required for Web3 development.

Please read the first section, "Understanding Web3 Infrastructure". This will explain the role of JSON-RPC and node providers. Follow the steps to sign up for an Alchemy account and create a new app for the "Ethereum Sepolia" network. Once created, copy the HTTPS URL; you will need it shortly.

ABI and Contract Address: Your Contract's Blueprint

Next, we need the ABI and the address of the contract you deployed in previous modules. Since you've been working with Hardhat, it conveniently generates and saves the ABI for you every time you compile your contracts.

You can find the ABI in your Hardhat project directory under artifacts/contracts/SecurityToken.sol/SecurityToken.json. Inside this file is a JSON object containing a key named abi. The address is the one you noted when you deployed your contract to a testnet (e.g., Sepolia). If you don't have it handy, you can redeploy it using npx hardhat run scripts/deploy.js --network sepolia and get a fresh address.

The tutorial from Web3 University explains precisely how to locate and use this artifact file.

Interact With Your Smart Contract

This tutorial from Web3 University walks through creating an interaction script for a Hardhat project.

Focus on Step 3, which shows how to find the ABI file generated by Hardhat. Then, review the start of Step 4, which introduces the concepts of Provider, Signer, and Contract in Ethers.js.

3. Setting Up Your Standalone Script

While you have used ethers.js within Hardhat for testing, we will now create a completely separate Node.js project for our interaction scripts. This is common practice for building backend services or automation bots that interact with smart contracts.

  1. Create a Project Directory: Outside of your Hardhat project folder, create a new directory for your scripts.

    mkdir security-token-scripts
    cd security-token-scripts
    
  2. Initialize Node.js Project:

    npm init -y
    

    This creates a package.json file.

  3. Install Dependencies: We need ethers for blockchain interaction and dotenv to manage our private keys and API URLs securely.

    npm install ethers dotenv
    
  4. Create Files:

    • Create a .env file to store your Alchemy URL.
    • Create your main script file, query.js.
    • Create a folder abis and copy the SecurityToken.json file from your Hardhat project's artifacts folder into it. Your structure should look like this:
      security-token-scripts/
      ├── abis/
      │   └── SecurityToken.json
      ├── node_modules/
      ├── .env
      ├── package.json
      └── query.js
      
  5. Configure .env: Open the .env file and add your Alchemy HTTPS URL.

    SEPOLIA_RPC_URL="https://eth-sepolia.g.alchemy.com/v2/YOUR_ALCHEMY_API_KEY"
    

4. Building the Query Script

Now, let's write the code in query.js to connect to your SecurityToken contract and read its data. We will call functions like name(), symbol(), and totalSupply().

The following video from Dapp University provides an excellent step-by-step walkthrough of this entire process. We'll watch the relevant sections as we build our script.

Master Ethers.js for Blockchain Step-by-Step (Full Course 2025)

This video from Dapp University is a comprehensive guide to using Ethers.js. It clearly demonstrates how to set up a provider and interact with a deployed contract.

First, watch the segment on setting up the connection. This covers creating the provider object using a URL from an environment variable. Then, watch the detailed section on interacting with a contract. Pay close attention to how the ABI is defined, how the ethers.Contract object is instantiated, and how read-only functions are called and their results logged.

Based on the video and our setup, here is how you can structure your query.js script.

// 1. Imports and setup
require('dotenv').config();
const { ethers } = require('ethers');
const contractABI = require('./abis/SecurityToken.json').abi; // Import the ABI

// 2. Configuration
const rpcUrl = process.env.SEPOLIA_RPC_URL;
const contractAddress = '0x...'; // PASTE YOUR DEPLOYED SecurityToken ADDRESS HERE

// Main function to execute logic
async function main() {
  try {
    // 3. Create a provider instance
    const provider = new ethers.JsonRpcProvider(rpcUrl);
    console.log('Successfully connected to network.');

    // Test connection by getting the latest block number
    const blockNumber = await provider.getBlockNumber();
    console.log(`Current block number: ${blockNumber}`);

    // 4. Create a contract instance
    const securityTokenContract = new ethers.Contract(contractAddress, contractABI, provider);
    console.log(`Contract instance created for address: ${contractAddress}`);

    // 5. Call read-only functions from the contract
    console.log('\n--- Querying Contract Data ---');
    const name = await securityTokenContract.name();
    const symbol = await securityTokenContract.symbol();
    const totalSupply = await securityTokenContract.totalSupply();

    console.log(`Token Name: ${name}`);
    console.log(`Token Symbol: ${symbol}`);
    
    // Total supply is a BigNumber, so we format it using 18 decimals
    console.log(`Total Supply: ${ethers.formatUnits(totalSupply, 18)}`);
    console.log('----------------------------\n');

  } catch (error) {
    console.error('An error occurred:', error);
    process.exit(1);
  }
}

// Execute the main function
main();

Breaking down the script:

  • Step 1 & 2: We import dotenv to load our .env file, ethers itself, and the ABI from our copied JSON file. We then define the RPC URL and the contract address.
  • Step 3: We initialize JsonRpcProvider with our Alchemy URL. This establishes our read-only connection to the Sepolia testnet.
  • Step 4: The core of the connection. We create a Contract object, passing the address, ABI, and provider. This object is now our gateway to all the functions defined in the SecurityToken contract.
  • Step 5: We call the name(), symbol(), and totalSupply() functions. Since these are network requests, they are asynchronous, so we use await. Notice how we use ethers.formatUnits() to convert the totalSupply from its smallest unit (like wei) into a human-readable number, just as you did in your tests.

To run the script, open your terminal in the security-token-scripts directory and execute:

node query.js

If everything is configured correctly, you will see the connection status, the current block number, and the name, symbol, and total supply of your security token printed to the console. The image below shows a similar script reading data from a contract, with the output visible in the integrated terminal.

A Node.js script using ethers.js to query smart contract data, with the results displayed in the terminal. This demonstrates the typical workflow you've just implemented.

Conclusion

In this lesson, you have taken a significant step from on-chain development to off-chain integration. You learned how to create a standalone Node.js script that connects to the Ethereum network and reads data from a live smart contract using ethers.js.

Key Takeaways:

  • Interacting with a smart contract from an off-chain script requires three core components: the contract address, the ABI, and a provider connection.
  • Node provider services like Alchemy are essential for gaining reliable access to the blockchain without running your own node.
  • Hardhat artifacts (.json files) are the primary source for a contract's ABI in a development environment.
  • The ethers.Contract class is the central object for contract interaction, combining the address, ABI, and provider into a single, easy-to-use interface.

You can now query any read-only (view or pure) function on your SecurityToken. However, the real power of tokenization comes from changing state: minting new tokens, transferring them between investors, and managing roles. In the next lesson, you will build on today's script to execute these write operations, learning how to sign and send transactions programmatically.

Can't find a good explanation? Sign up and we'll make it for you

Sign up