Skip to content

What is ABI?

If you want to write code that interacts with a contract, your app needs to know things about the code, you need to know:

  • The binary format the EVM expects
  • The function signature and how to encode it
  • The data types of the parameters and return values

The Application Binary Interface (ABI) helps provide answers to the above. It lives in the form of JSON; here's an example:

[
  {
    "type": "constructor",
    "inputs": [
      {
        "name": "initialValue",
        "type": "uint256",
      }
    ]
  },
  {
    "type": "function",
    "name": "getValue",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "setValue",
    "inputs": [
      {
        "name": "newValue",
        "type": "uint256",
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "event",
    "name": "ValueChanged",
    "inputs": [
      {
        "indexed": true,
        "name": "oldValue",
        "type": "uint256"
      },
      {
        "indexed": true,
        "name": "newValue",
        "type": "uint256"
      }
    ],
    "anonymous": false
  }
]

Consider the above ABI; it's an array. Notice the first object in the array has a "type" member with value "constructor." That object specifies the signature for the constructor for a smart contract. It takes as a parameter an unsigned integer, called initialValue; we can presume the code for the smart contrat will store the value internally.

Next is a function called getValue; notice its "type" member has value "function" and its "name" member has value "getValue". The "inputs" member is an empty array, meaning it takes no parameters. The "outputs" member has a "type" member with value "uint256" meaning this function returns an unsigned integer; we can presume this returns the internal value.

The next object describes the function called "setValue". This one does have an object in the inputs array; the object has a "type" member of "uint256", and a "name" member of value "newValue" which means this function takes a parameter of type unsigned integer called newValue. We can presume that this updates the internal value.

So far, this means we have three functions:

constructor(initialValue: uint256) getValue(): uint256 setValue(newValue: uint256)

Before talking about the final object, notice the third object, the setValue function, has an additional member called "stateMutability" with value "nonpayable". This means that the function doesn't accept Eth.

The final object has type "event". This means it's what's called an event emitter, and it's name is is ValueChanged. We can presume that this event emitter happens when the setValue function is called.

The above completely describes the interface of a smart contract. It does not, however, show us the code that implements the contract. When a developer writes a smart contract in Solidity, the compiler automatically generates an ABI like this one. This ABI allows other code — like frontend apps or other contracts — to know exactly how to interact with the contract.

There's more!

So far this just scratches the surface of ABIs. There's much more to them.

Payable vs Non-payable

In the above our example has "nonpayable" for "stateMutability". The other option is "payable" which means the function accepts Eth. The reason they set up ABIs this way is to make it clear — both to tools and to the Ethereum Virtual Machine — whether a function is allowed to receive funds. This distinction helps prevent accidental ETH transfers to functions that aren’t expecting them, and it gives wallets and libraries the information they need to construct transactions safely.

Custom Errors

Suppose you have a smart contract that represents a checking account, and it includes a withdraw function (which would appear in the ABI). The withdraw function would have an amount as a parameter. But if they try to withdraw more than is available, you want to throw an error. That's where custom errors come into play; here's an example object that might be present inside an ABI:

{
  "type": "error",
  "name": "InsufficientBalance",
  "inputs": [
    {
      "name": "requested",
      "type": "uint256"
    },
    {
      "name": "available",
      "type": "uint256"
    }
  ]
}

If you were to call the withdraw function from, for example, a JavaScript app, and you ask for more than is available, you would get an exception thrown, with the above error type embedded in it.

How Libraries use ABIs

If you're calling a smart contract from an app, you'll likely be using a third-party library meant for Ethereum. For example, in JavaScript you have either Ethers.js or Web3.js. The Ethers.js library can generate JavaScript objects based on an ABI.

Similar libraries exist for most programming languages, including Python, Java, Go, and Rust.