ABI Bindings

ABI bindings allow the encoding and decoding of Smart Contract function calls or the decoding of events. In w3 ABI bindings are defined for individual functions or events at runtime using Solidity syntax.

  • Easy to write: Creating an ABI binding only requires the Solidity function signature. No need to firstly generate an ABI json file using solc and secondly generate ABI bindings using abigen.
  • Flexible: ABI bindings for a function or event can be used with any Smart Contract. No need to generate overlapping bindings for multiple Smart Contracts.

Functions

Function ABI bindings can be defined using

  • func NewFunc(signature, returns string) (*Func, error), or
  • func MustNewFunc(signature, returns string) *Func which panics on error.

Syntax

Function signatures are defined using Solidity syntax. Arguments and returns can optionally be named. While naming is optional, it is recommended for more complex functions or tuple variables. Alias types, such as uint for uint256, are supported.

Example: ERC20 balanceOf

ABI binding for the ERC20 balanceOf function (Playground):

Solidity
interface IERC20 {
    function balanceOf(address account) external view returns (uint256);
    // ...
}
Go
var funcBalanceOf = w3.MustNewFunc("balanceOf(address)", "uint256")
// or
var funcBalanceOf = w3.MustNewFunc("balanceOf(address who)", "uint256 amount")

Example: QuoterV2 quoteExactInputSingle

ABI binding for the Uniswap QuoterV2 quoteExactInputSingle function with a tuple parameter (Solidity struct):

Solidity
interface QuoterV2 {
 
    struct QuoteExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint256 amountIn;
        uint24 fee;
        uint160 sqrtPriceLimitX96;
    }
 
    function quoteExactInputSingle(QuoteExactInputSingleParams memory params)
        external
        returns (
            uint256 amountOut,
            uint160 sqrtPriceX96After,
            uint32 initializedTicksCrossed,
            uint256 gasEstimate
        );
    // ...
}
Go
type QuoteExactInputSingleParams struct {
    TokenIn           common.Address
    TokenOut          common.Address
    AmountIn          *big.Int
    Fee               *big.Int
    SqrtPriceLimitX96 *big.Int
}
 
var funcQuoteExactInputSingle = w3.MustNewFunc(
    `quoteExactInputSingle((
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint24 fee,
        uint160 sqrtPriceLimitX96
    ) params)`,
    `uint256 amountOut,
     uint160 sqrtPriceX96After,
     uint32 initializedTicksCrossed,
     uint256 gasEstimate`,
)

Tuples (Solidity struct’s)

Tuple types need to be embedded in parentheses, with comma-separated fields. Fields must be named, so they can be mapped to the fields of a Go struct.

To map a tuple type to a Go struct, the struct must be defined manually with each tuple field being mapped to a Go struct field. Field names need to match, but Go field names must always start with an uppercase letter. E.g. the tuple field address tokenIn must be matched to the Go struct field TokenIn common.Address.

See Type Mappings for more information on how to map primitive Solidity types to Go types and vice versa.

EncodeArgs

The EncodeArgs method of a Func ABI encodes a Solidity function call. Each argument of the Solidity function must be matched by a corresponding Go value.

DecodeArgs and DecodeReturns

The DecodeArgs and DecodeReturns methods of a Func ABI decode the arguments and returns of a Solidity function call. The Go values that should hold the decoded data must be defined beforehand, and passed as pointers to the decode methods. Values that should not be decoded can be passed as nil. Tailing nil values can be omitted.

Example: Uniswap Pair getReserves

ABI decode the output of the Uniswap Pair getReserves function (Playground):

Go
var (
    funcGetReserves = w3.MustNewFunc("getReserves()", "uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast")
    output []byte   = w3.B("0x00…")
)
 
var (
    reserve0, reserve1 *big.Int
    blockTimestampLast uint32
)
if err := funcGetReserves.DecodeReturns(output, &reserve0, &reserve1, &blockTimestampLast); err != nil {
    // ...
}

In case only the reserves should be decoded, the blockTimestampLast can be ignored using funcGetReserves.DecodeReturns(output, &reserve0, &reserve1, nil), which is equivalent to funcGetReserves.DecodeReturns(output, &reserve0, &reserve1).

Events

Event ABI bindings can be defined using

  • func NewEvent(signature string) (*Event, error), or
  • func MustNewEvent(signature string) *Event which panics on error.

Example: ERC20 Transfer

ABI binding for the ERC20 Transfer event:

Solidity
interface IERC20 {
    event Transfer(address indexed from, address indexed to, uint256 value);
    // ...
}
Go
var evtTransfer = w3.MustNewEvent("Transfer(address indexed from, address indexed to, uint256 value)")

Type Mappings

Solidity TypeGo Type
boolbool
int8int8
int16int16
int32int32
int64int64
int24,int40int56,int72int256,int*big.Int
uint8uint8
uint16uint16
uint32uint32
uint64uint64
uint24,uint40uint56,uint72uint256,uint*big.Int
bytes[]byte
bytes1bytes32[1]byte[32]byte
addresscommon.Address/[20]byte
bytes32common.Hash/[32]byte

Arrays and Slices

Solidity arrays and slices are mapped to Go arrays and slices respectively and vice versa.

Solidity TypeGo Type
type[n][n]type
type[][]type