Paul Sitoh

I'm a software engineer who enjoys coding principally in Go and where necessary other languages.

Ethereum Application Binary Interface (ABI) in Go

31 Jul 2021 » go, solidity

About this post

The Application Binary Interface (ABI) is a standarised way to interact with a contract in an Ethereum network. The ABI is typically implemented in JSON format and used in conjunction with web3.js Javascript libraries to interact with a deployed solidity contract. I’ll briefly cover this technique here.

In this post, I’ll present an alternative approach using Go based ABI in lieu of JSON/web3.js combination. For the purpose of illustration, I’ll be using a deployed contract, also referred to as a token, named Tronix on Ethereum Mainnet. I’ll be demonstrating the process of obtaining a balance of the token.

It is NOT the purpose of this post to avocate the use of Go instead of JSON/wbe3.js. The intention here is to demonstrate, if you choose to use Go, the steps involved. I’ll leave the choice of using Go over JSON/web3.js or vice-versa to you to decide.

Tronix Solidity and ABI/Web3.js

The full Tronix source code for this post is here. The truncated version of the code with emphasis on two methods is shown below.

pragma solidity ^0.4.11;

contract TronToken {

    uint256 public decimals = 6; // Method to return the number of decimal places
    mapping (address => uint256) public balanceOf; // Method to return balace of token

      // items not shown ...
}

The ABI equivalent in JSON/Web3.js is below:

let abi=[// decimals
{
    "constant":true,
    "inputs":[],
    "name":"decimals",
    "outputs":[{"name":"","type":"uint256"}],
    "type":"function"
}
// balanceOf
{
    "constant":true,
    "inputs": [{"name":"","type":"address"}],
    "name":"balanceOf",
    "outputs":[{"name":"","type":"uint256"}],
}]

let tokenAddress = /*address of contract from blockchain console*/;
let walletAddress = /*address of wallet from bockchain console*/;

// Using web3 to interact with deployed Tronix
let contract = web3.eth.contract(minABI).at(tokenAddress);
contract.balanceOf(walletAddress, (error, balance) => {
  // Get decimals
  contract.decimals((error, decimals) => {
    // calculate a balance
    balance = balance.div(10**decimals);
    console.log(balance.toString());
  });
});

Abigen tool

Before you create apps in Go, you need to install a tool known as abigen. The process involve cloning the source code from https://github.com/ethereum/go-ethereum and the running the command make gen. Please refer to this post for details about building and using abigen tool.

Generating the Go ABI

Having built the abigen tool, the next step is to generate the Go ABI binding from the solidity source code.

abigen --sol ./solidity/trontoken.sol \
       --pkg trontoken \
       --out ./pkg/trontoken/trontoken.go

From this command, the Go ABI is generated and the full code is shown torntoken.go. The two main elements of the Go ABI are implemented as follows.

This is the implementation of the Decimals method.

func (_TronToken *TronTokenCaller) Decimals(opts *bind.CallOpts) (*big.Int, error) {
	var out []interface{}
	err := _TronToken.contract.Call(opts, &out, "decimals")

	if err != nil {
		return *new(*big.Int), err
	}

	out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)

	return out0, err

}

This is the implementation of the BalanceOf method.

func (_TronToken *TronTokenCaller) BalanceOf(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) {
	var out []interface{}
	err := _TronToken.contract.Call(opts, &out, "balanceOf", arg0)

	if err != nil {
		return *new(*big.Int), err
	}

	out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)

	return out0, err

}

Using the Go ABI

When you have generated the ABI package, building a Go based app importing the ABI package and implementing a main package. To obtain the balance of Tronix contract, here is a truncated example of a application main package:

netURL := fmt.Sprintf("%s/%s", infuraMainNetURL, infuraPID) // the URL and PID is provided by a third party services https://infura.io/.
conn, _ := ethclient.Dial(netURL) // Using this package "github.com/ethereum/go-ethereum/ethclient"

contract, _ := trontoken.NewTronToken(common.HexToAddress(contractAddr), conn)

// Invoke a method from tron struct
amt, _ := contract.BalanceOf(&bind.CallOpts{}, common.HexToAddress(walletAddr))

log.Println(amt)

Summing up

If you plan to create an app using Go based ABI, these are the steps:

  1. Build the abigen tool from the go-ethereum project and install/build the Go solidity compiler.

  2. Use abigen tool to create a Go ABI package from your solidity code.

  3. Import your generated Go ABI package your main package.

To make the execution of the steps mentioned above easier, Open Consentia, an open source initiative, has created a framework named gosol. Feel free to clone it from https://github.com/openconsentia/gosol and modify it to suit your requirements.

The pros and cons of using Go to build an application to interact with your contract, is beyomd the scope of this post. I shall leave that to you to decide.