A Developer’s Guide to Mastering Smart Contract ABI in Solidity

The world is becoming a better place with the use cases of blockchain technology. Nowadays, we can send soulbound NFTs to be a testament to achievements, and create a robust decentralized association of people among other things.

Meanwhile, we should be mindful that blockchain rests mainly on smart contracts. Have you ever asked yourself how separate contracts get to communicate?

Or how do smart contracts get to synchronize with other extrinsic entities? The answer to these questions is smart contract ABI.

Perhaps you’re hearing about it for the first time, this short guide will explain all the intricacies of smart contract ABI to you like a 5-year-old.

What is a smart contract ABI in Solidity?

A smart contract Application Binary Interface, often shortened as ABI, in Solidity is a hinge that ensures effective communication among several distinct contracts and external applications.

Thereby, making it possible for smart contracts and web applications to work hand-in-hand to achieve the common goals the developers want them to achieve.

Meanwhile, it can be shocking to know that ABIs are neither limited to Web3 nor did they start with it. Thus, ABIs have been existing in Web2 software development.

To fully grab the roles ABIs are playing in Web3, we need to have a basic knowledge of how EVM high-level languages work vis-a-vis external applications.

How does the EVM Work?

After a developer has written the state of a contract and defined the functions, they will have to deploy it. At the time of deployment, the contract would have been compiled into bytecodes—which is a low-level language in binary form—so the Ethereum Virtual Machine will understand and process it accordingly.

For context, here is an example of bytecodes:

0x00000000000000000000000000000000000000000000000000000000000003020000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4

Now, this is the actual twist: While the EVM can only process bytecodes, other web applications cannot.

Therefore, a communication barrier may arise. To bridge that gap, ABIs help to convert bytecodes to what web applications and even humans can understand.

In a nutshell, smart contract ABIs act as the translator or interpreters between smart contracts and web applications.

How does ABI relate to API?

If you are a Web2 developer, there are chances that you tap into APIs from time to time. Therefore, you might be noticing some kind of similarities between APIs and ABIs. So how do ABIs relate to APIs?

The main similarity between both interfaces is that they enhance interaction between two entities. For instance, you can integrate a weather API to make your website provide weather updates in real time.

Although ABIs and APIs relate from this standpoint, they are wide apart in several instances, and the ballpoint rests on the centralization and decentralization dichotomy.

In the case of an API, it facilitates efficient communication of data between a centralized server and a program. Whereas, a smart contract ABI makes decentralized smart contracts communicate with each other or an external web app.

Do ABIs replace APIs in Web3?

No, smart contract ABIs do not replace APIs in Web3. Therefore, it is not the case that ABIs are an absolute substitute for APIs. Each of them serves different roles that the other cannot do.

In the first place, this question suggests a possible misunderstanding of some basic concepts of programming as well as Web3 in general.

It is noteworthy that APIs in Web3 are prepared interfaces that developers can leverage to build faster, thereby saving them time to build from scratch; as that will be the case of reinventing the wheel.

For instance, as a developer, you can utilize the Alchemy or Moralis NFT API to build your NFTs contracts faster and make real-time node requests accordingly.

Meanwhile, ABIs enhance communication either between contract to contract or between contracts and web apps. Having distilled the possible misconceptions, let’s proceed to know how we can get smart contract ABIs.

What are the four ways to generate smart contract ABI?

You can generate the ABI of a smart contract in four ways: through

  • The terminal of VS Code
  • Solc.js command
  • Blockchain explorers, and
  • By copying it from Remix IDE

For this quick guide, we will stick with the fourth method because it is the easiest. This is how it works:

  • Write the codes
  • Compile them
  • Check under the compilation details tab, and copy the ABI

Here is a pictorial representation:

abi side.png

What are the elements of an ABI in JSON format?

Here is the ABI of the above contract in JSON format:

[
    {
        "inputs": [],
        "stateMutability": "nonpayable",
        "type": "constructor"
    },
    {
        "anonymous": false,
        "inputs": [
            {
                "indexed": true,
                "internalType": "address",
                "name": "owner",
                "type": "address"
            },
            {
                "indexed": false,
                "internalType": "uint256",
                "name": "amount",
                "type": "uint256"
            },
            {
                "indexed": false,
                "internalType": "uint256",
                "name": "balance",
                "type": "uint256"
            }
        ],
        "name": "Receive",
        "type": "event"
    },
    {
        "anonymous": false,
        "inputs": [
            {
                "indexed": true,
                "internalType": "address",
                "name": "owner",
                "type": "address"
            },
            {
                "indexed": false,
                "internalType": "uint256",
                "name": "amount",
                "type": "uint256"
            },
            {
                "indexed": false,
                "internalType": "uint256",
                "name": "balance",
                "type": "uint256"
            }
        ],
        "name": "Withdraw",
        "type": "event"
    },
    {
        "inputs": [],
        "name": "John",
        "outputs": [
            {
                "internalType": "address",
                "name": "",
                "type": "address"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "johnWithdrawEverything",
        "outputs": [],
        "stateMutability": "payable",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "receiveEther",
        "outputs": [],
        "stateMutability": "payable",
        "type": "function"
    }
]

Even though you couldn’t access the code base of the above contract, you can deduce the functions and parameters that were declared in it.

First of all, the state mutability description shows two things: the visibility of the function and its extent of payability. In terms of visibility, it only captions pure and view.

The payability depends on whether or not the variable or function was enabled to receive ether with the payable keyword.

Moving on, the type field can contain the constructor, function, and events. The input hosts the parameters of each function.

How is an ABI encoded?

Solidity has a couple of conventions in the way it encodes. But then, you are also free not to follow the said conventions while encoding your data.

If you want to follow the conventions, you will use the abi.encode method when encoding your data. The abi.encode method converts data into bytes at its most basic level for the EVM to comprehend it.

The bytes will have 96 characters in total, and it comprises 3 segments which contain 32 bytes each. Here is a quick breakdown of each segment of each byte segment:

  • The first 32 bytes are the offset of the string
  • The second 32 bytes are the length of the string
  • The third 32 bytes are the actual encoded string in the UTF8 format

Now, let’s roll up our sleeves and encode some pieces of data:

pragma solidity ^0.8.16;

  contract Encoding{

   function encodeJohn (
     uint _y,
     address _addr

   ) external pure returns (bytes memory){
    return abi.encode(_y, _addr);
   }

  }

After this code was deployed, we inputed some figures and an address, and this was the result of the bytes:

0x00000000000000000000000000000000000000000000000000000000000003020000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4

Having explained the abi.encode method, let’s move on to the abi.encodePacked method. This method doesn’t follow the general Solidity conventions encoding.

It should be used when you are working on dynamic data types or when you want to hash. We encoded a piece of data in the function below with the abi.encodePacked method:

function encodeGrace (
     uint _y,
     address _addr

   ) external pure returns (bytes memory){
    return abi.encodePacked(_y, _addr);
   }

It works like the earlier method and can be decoded the same way.

How is an ABI decoded?

Can you remember how we encoded data earlier? We can also decode them and make them plain. Do you want to know the figures and address that I input earlier? Let’s decode it:

function decodeJohn(bytes memory data) external pure returns( uint _y,address _addr){

     (_y, _addr) = abi.decode( data, (uint, address));
  }

Add this function and deploy it. You should get this result:

decode.png

This is the logic: Even though you were not there when I inputted these details upon deployment, you can know with the decoding.

Bottom Line – What is the essence of ABI in Solidity Smart Contracts?

The Application Binary Interface helps us to connect contracts and even with external applications. It is quite similar to how APIs work.

As a developer, you can decode or encode any ABI using the abi.encode and abi.decode methods respectively.