From ABI to API: Understanding Smart Contract Interactions for JavaScript Engineer

Deep dive into the differences between ABIs and APIs, specifically tailored to JavaScript engineers transitioning into blockchain development.

Published on
March 30, 2025
From ABI to API: Understanding Smart Contract Interactions for JavaScript Engineer

If you're a JavaScript engineer transitioning into blockchain development, understanding ABIs is key to interacting with smart contracts.

This guide will help you navigate your JavaScript to Web3 transition with practical examples using OpenZeppelin contracts.

ABIs are to blockchain development what API specifications are to Web2 engineering - but they operate at a lower level of abstraction.

For Ethereum development with JavaScript, learning this fundamental difference is essential.

The Mental Model Shift: APIs vs. ABIs for JavaScript Engineers

As JavaScript developers, we make requests to endpoints and get a response back.

// REST API example
async function fetchUserData() {
 const response = await fetch('<https://api.example.com/users/123>', {
   headers: {
     'Authorization': 'Bearer token123'
   }
 });

 return response.json();
}

// Response
// { name: "Jules", age: "30" }

When interacting with smart contracts, we're not making requests to a server, but rather reading from the blockchain or initiating state-changing transactions.

Instead of APIs (Application Programming Interfaces), we have ABIs (Application Binary Interfaces).

Smart contract ABIs are generated when compiling the smart contract, which we do every time before deploying them to the blockchain.

Think of an ABI as the smart contract's public face — it tells you what functions exist, what parameters they take, and what they return. With these specifications, RPC providers are able to call on the right functions from your smart contracts.

ABI example

ABIs vs APIs: A Deeper Dive for JavaScript Engineers

To appreciate the difference in blockchain development for JavaScript engineers, we must understand how Web2 architecture differs from Web3 architecture.

Web2 vs Web3 architecture

In a traditional REST API flow:

  1. Your front-end applications constructs an HTTP request with a URL, method, and possibly headers/body to call your API endpoint
  2. The request travels over the internet to a specific server hosted in a cloud provider like AWS, GCP, Heroku, etc
  3. Your server software (i.e. your Express.js or Rails application) routes the request to the appropriate handler
  4. The handler executes code in your server, typically interacting with a database
  5. The server sends back a response, usually JSON
  6. Your front-end processes the response

In blockchain development with JavaScript:

  1. Your front-end application, via Web3 libraries for JavaScript like ethers.js or viem.js, encodes a function call using the contract's ABI
  2. The encoded call is wrapped in a transaction or a call message that gets sent to the blockchain
  3. This message is broadcasted to the entire blockchain network via a provider (like Alchemy, Infura, etc)
  4. Every node in the network processes the call according to the same rules
  5. The result is returned to your application
  6. Your frontend library decodes the result using the ABI definitions

The fundamental differences between ABIs vs APIs:

APIs ABIs
Relationship Client-server relationship Client to peer-to-peer node network
Hosting Call specific servers Broadcasts to an entire network
Permanence Changeable whenever a developer updates the code Permanent once deployed
Return Return data directly Return encoded data that must be interpreted with the support of a provider and/or library

Web3 Providers: Your Gateway to the Blockchain

If ABIs are the language dictionaries that help us communicate with smart contracts, Web3 providers are the messengers that carry our words to the blockchain.

A provider is your JavaScript application's connection to the Ethereum network (or any other blockchain).

It is responsible for submitting your transactions to the blockchain and reading data from it. Think of providers as the HTTP clients of the blockchain world — they handle the actual communication while you focus on what you want to say.

// Viem is a JavaScript Web3 JS library
import { createPublicClient, http, custom } from 'viem';
import { mainnet } from 'viem/chains';

// Connect to browser wallet like MetaMask
export const browserProvider = createPublicClient({
 chain: mainnet,
 transport: custom(window.ethereum)
});

// Connect to a specific network via public node
export const infuraProvider = (projectId) => createPublicClient({
 chain: mainnet,
 transport: http(`https://mainnet.infura.io/v3/${projectId}`)
});

// Connect to a local node
export const localProvider = createPublicClient({
 chain: mainnet,
 transport: http('<http://localhost:8545>')
});

When you call a smart contract function from any application during Ethereum development with JavaScript, the provider:

  1. Formats your request according to the Ethereum JSON-RPC specification
  2. Sends it to the appropriate node
  3. Returns the response to your application

Without a Web3 provider, your JavaScript code has no way to communicate with the blockchain — it's like having a phone but no cellular service.

How do JavaScript Developers Interact with Smart Contracts?

When using an API, we create an endpoint request to fetch and write information.

In blockchain development, we instead pass a Web3 library our contract's ABI and use it so it generates functions we can call from our application - in this case viem.

// (a) REST API
const api = {
 baseUrl: '<https://api.example.com>',
 async getUser(id) {
   const response = await fetch(`${this.baseUrl}/users/${id}`);
   return response.json();
 }
};

// (b) Ethereum smart contract approach - using viem
import { createPublicClient, custom, getContract } from 'viem';
import { mainnet } from 'viem/chains';

// Create a public client to interact with the blockchain
const publicClient = createPublicClient({
 chain: mainnet,
 transport: custom(window.ethereum)
});

const contractABI = [
   {
     "inputs": [
       {
         "internalType": "address",
         "name": "recipient",
         "type": "address"
       },
       {
         "internalType": "address",
         "name": "initialOwner",
         "type": "address"
       }
     ],
     "stateMutability": "nonpayable",
     "type": "constructor"
   },
   {
     "inputs": [],
     "name": "ECDSAInvalidSignature",
     "type": "error"
   },
 // other functions would be listed here
];

const contract = getContract({
 address: '0x123...', // contract address on the blockchain
 abi: contractABI,
 publicClient
});

Without the ABI, viem wouldn't know how to properly format the call or interpret the response during smart contract interaction.

It's like viem needs to read the API documentation before writing a single line of code — the ABI is that documentation.

The ABI Decoded: What's Actually Happening in the Smart Contract Interaction

ABIs are the bridge between your JavaScript code and the Ethereum Virtual Machine (EVM).

{
 "inputs": [
   {
     "internalType": "uint256",
     "name": "id",
     "type": "uint256"
   }
 ],
 "name": "getUser",
 "outputs": [
   {
     "internalType": "string",
     "name": "name",
     "type": "string"
   },
   {
     "internalType": "uint256",
     "name": "balance",
     "type": "uint256"
   }
 ],
 "stateMutability": "view",
 "type": "function"
}

In this ABI example, the ABI tells us that our contract has a function named getUser that:

  • Takes a single parameter: id of type uint256
  • Returns two values: a string named name and a uint256 named balance
  • Has a "view" state mutability, meaning it doesn't modify blockchain state

Web3 libraries for JavaScript like viem and ethers use this information to encode our function calls into the format the EVM understands so that we can easily call on smart contract functions from our applications.

const { name, balance } = await contract.getUser(123);

Behind the scenes of a smart contract interaction:

  1. The function name and parameters are hashed into a "function selector" (the first 4 bytes of the Keccak-256 hash of the function signature)
  2. The parameters are encoded according to their types following the ABI specification
  3. The resulting data is sent as part of a transaction or call via your provider
  4. The returned data is decoded according to the output types specified in the ABI

It's like your JavaScript is being translated to blockchain-speak and back again through viem.

ABIs vs APIs: The Technical Distinctions for JavaScript Engineers

As JavaScript engineers transition into Web3, there are some technical distinctions to keep in mind when building blockchain applications.

REST APIs Smart Contract ABIs
Transport HTTP/HTTPS Blockchain transactions/calls
Addressing URL endpoints Contract addresses & function selectors
Authentication Tokens, cookies, OAuth Cryptographic signatures
Data format JSON, XML, etc ABI-encoded binary data
State management Server maintains state Globally shared state on the blockchain
Error handling HTTPS status codes Reverts with reason strings
Cost model Free, subscription, per request Gas fees per operation
Latency Milliseconds Seconds to minute (for transactions)
Concurrency High, server-dependent Limited by block space
Versioning Easily upgraded APIs Immutable once deployed (without upgrade patterns)

The biggest shift for JavaScript developers is understanding that smart contracts are state machines, not servers.

  1. APIs (Request-Response): The server processes your request and returns a response. The server's state can change in any way the developer programmed.
  2. Smart Contracts (State Machines): The contract has a defined set of state transitions. Your transaction proposes a state change that must follow the rules encoded in the contract.

With APIs, the server is in control. With smart contracts, the rules are in control because the “server” (the EVM’s execution environment) is globally shared —and they're transparent to everyone.

Smart contract journey from deployment to transactions

The blockchain version is often more complex because:

  1. We need user permission (wallet connection)
  2. We pay for computation (gas estimation)
  3. We wait for confirmation (tx.wait())
  4. We extract results from event logs rather than direct returns

Real World Example: ERC20 Implementation Using OpenZeppelin Contracts

One of the most common contracts to interact with are ERC20 token contracts from OpenZeppelin contracts library.

OpenZeppelin contracts let you extend standard tokens with additional functionality, making ERC20 implementation straightforward for JavaScript developers.

// Import OpenZeppelin's ERC20 and Pausable ABI and viem functions
import { createPublicClient, createWalletClient, custom, getContract } from 'viem';
import { mainnet } from 'viem/chains';
import ERC20PausableABI from '@openzeppelin/contracts/build/contracts/ERC20Pausable.json';

// Create a public client - used for reading blockchain data
// This client doesn't require a signer and can only perform read operations
// like calling view/pure functions, getting block information, etc.
const publicClient = createPublicClient({
 chain: mainnet,
 transport: custom(window.ethereum)
});

// Create a wallet client - used for writing data to the blockchain
// This client connects to the user's wallet (MetaMask, etc.) and can sign transactions
// It's required for any operation that changes blockchain state (transfers, contract writes, etc.)
const walletClient = createWalletClient({
 chain: mainnet,
 transport: custom(window.ethereum)
});

// Connect to the token contract
// We use the publicClient for the contract instance since most interactions are reads
const contract = getContract({
 address: tokenAddress,
 abi: ERC20PausableABI.abi,
 publicClient
});

// Get token details - these are read operations using the publicClient
const name = await contract.read.name();
const symbol = await contract.read.symbol();
const isPaused = await contract.read.paused();

console.log(`${name} (${symbol}) Is Paused: ${isPaused}`);

// To pause the token (if you have the rights)
if (!isPaused) {
 // Request user wallet access - needed for the wallet client to sign transactions
 // This prompts the user's wallet (e.g., MetaMask) to connect if not already connected
 const [address] = await walletClient.requestAddresses();

 // Send the transaction using the wallet client since we're writing to the blockchain
 // This requires the user to sign the transaction in their wallet
 const hash = await walletClient.writeContract({
   address: tokenAddress,
   abi: ERC20PausableABI.abi,
   functionName: 'pause',
   account: address
 });

 // Wait for the transaction to be mined - we use the publicClient for this
 // since it's just reading the transaction status from the blockchain
 await publicClient.waitForTransactionReceipt({ hash });

 console.log(`${symbol} is now paused`);
}

The JavaScript Developer's Toolkit for Web3

Here are the most common Web3 libraries for JavaScript to interact with blockchain during your JavaScript to Web3 transition:

1. Libraries

  • viem - TypeScript-first Ethereum library for all JS projects
  • wagmi - React hooks for Ethereum to make interacting with the blockchain easier for the frontend
  • ethers.js - An older version to viem 's similar functionality

2. Development Environment for Ethereum Development with JavaScript

  • Hardhat - Ethereum development environment with JavaScript support
  • Foundry - Fast, portable toolkit for Ethereum development, all Solidity

3. Testing Your Smart Contract Interaction

4. Frontend Authentication - Wallet Connectors

Conclusion: Mastering Your JavaScript to Web3 Transition

Moving from APIs to ABIs requires a mental shift, but here are the key differences to remember for JavaScript engineers:

  1. ABIs are your contract interface - They define how you can interact with the contract
  2. Web3 providers are your network gateway - They connect your JavaScript code to the blockchain
  3. Transactions, not requests - You're proposing state changes to a decentralized network
  4. Gas and confirmation - Operations cost money and take time to confirm
  5. Events for updates - Use contract events to track changes and updates
  6. State machine, not server - Contracts follow strict rules for state transitions

There's something satisfying about code that executes exactly as written, without hidden dependencies or server-side surprises. You're not calling an API — you're participating in a global state machine through Ethereum development with JavaScript.

That understanding makes all the difference in your JavaScript to Web3 transition.