New to blockchain software development? Read my beginners guide here

Guide to Solidity ABIs

Created on August 2022 β€’ Tags: ethereumsolidityguides

A guide to understanding what ABIs are


Table of Contents for Guide to Solidity ABIs


When you compile a smart contract, you can export the Application Binary Interface (ABI).

This is a simple JSON document that contains the interface of all methods in a contract (including what arguments to pass in, and what the return).

It does not include the implementation of those functions.

You can use an ABI along with a deployed smart contract (or its bytecode) to easily interact with functionality.

Example of ABI from a Solidity smart contract

The following simple smart contract is listed below, and after that the ABI output.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract HelloWorld {
string greeting = "Default hello world";

function setGreeting(string calldata _newGreeting ) public {
greeting = _newGreeting;
}
function sayHi() public view returns (string memory){
return greeting;
}
}
[
{
"inputs": [],
"name": "sayHi",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "_newGreeting",
"type": "string"
}
],
"name": "setGreeting",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]

You can see from the ABI that there are a couple of functions to call:

  • sayHi which has no inputs, so its full structure is sayHi()
  • setGreeting which has one input, so its structure is setGreeting(string).

Function signatures

Given a function name & its arguments, you can create a hash of it and the first four bytes of this are used as the function selector.

Note: if two different functions have the same first four bytes as a signature in a smart contract, there will be a compile error.

This selector is saved to a mapping of selectors to the bytecode to execute for that function.

How function selectors are generated

  • they look at just the function name and types of arguments
  • it then turns that into a string
  • uint is turned into uint256. There are some other conversions (see below)
  • no spaces are used
  • return type is not used (note: they are in the ABI output though, just not used for the function selector)
  • then it takes the first (left, high order in big endian) four bytes of the keccak256 hash of that signature

So you would do this in pseudo code:

    let stringSignature = 'sayHi(string)';
let hash = keccak256(stringSignature);
let selector = first4Bytes(hash);

The first four bytes of the call data for a function call specifies the function to be called. It is the first (left, high-order in big-endian) four bytes of the Keccak-256 hash of the signature of the function. The signature is defined as the canonical expression of the basic prototype without data location specifier, i.e. the function name with the parenthesised list of parameter types. Parameter types are split by a single comma - no spaces are used.

See my full guide on generating function selectors in Solidity here

data types

  • uints - uint8, uint16, uint32, etc up to uint256 (always where the num of bytes is a multiple of 8
    • uint is the same as uint256, but for calculating function selectors uint256 is used
  • ints int8, uint16, etc up to uint256 (again where the number is a multiple of 8)
    • int is the same as int256, but for calculating function selectors int256 is used
  • address, which is stored the same as a uint160
  • bytesX where X is from 1-32, such as bytes1, bytes2, bytes32
  • function - a 20 bytes address followed by a function selector (4 bytes) - encoded in the same way as a bytes24

There are also fixed size arrays:

  • <type>[M] (e.g. uint[5]) a fixed-length array of M elements, M >= 0, of the given type.

and dynamic sized arrays:

  • bytes dynamic-sized byte sequence.
  • string dynamic-sized strings. Unicode, encoded in UTF8
  • <type>[] (e.g. uint[]) a variable-length array of elements of the given type.

And tuples: (T1,T2,...,Tn): tuple consisting of the types T1, …, Tn, n >= 0

A few more not covered here but listed in the docs:

fixed<M>x<N>: signed fixed-point decimal number of M bits, 8 <= M <= 256, M % 8 == 0, and 0 < N <= 80, which denotes the value v as v / (10 ** N). ufixed<M>x<N>: unsigned variant of fixed<M>x<N>. fixed, ufixed: synonyms for fixed128x18, ufixed128x18 respectively. For computing the function selector, fixed128x18 and ufixed128x18 have to be used.

How the data types are mapped

  • If your Solidity code has an argument of uint, the selector should actually use uint256 (setCountValue(uint256) for example)
  • address payable and contracts are mapped to just address
  • enum is mapped to uint8 (since 0.8.0 - before that version they could have more than 256 values and were mapped to the smallest uint type to fit all values).
  • struct is mapped to tuple

Spotted a typo or have a suggestion to make this crypto dev article better? Please let me know!

See all posts (70+ more)

See all posts (70+ more)

Was this post helpful? πŸ“§

If you liked this content and want to receive emails about future posts like this, enter your email. I'll never spam you.

Or follow me on @CryptoGuide_Dev on twitter

By using this site, you agree that you have read and understand its Privacy Policy and Terms of Use.
Use any information on this site at your own risk, I take no responsibility for the accuracy of safety of the information on this site.