viem-goviem-go

Reading Contracts

Read data from smart contracts using view and pure functions

Read data from smart contracts using view and pure functions. These calls don't require gas since they don't modify state.

Basic Usage

1import (
2 "github.com/ethereum/go-ethereum/common"
3 "github.com/ChefBingbong/viem-go/client"
4)
5
6result, err := publicClient.ReadContract(ctx, client.ReadContractOptions{
7 Address: common.HexToAddress("0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2"),
8 ABI: `[{"name":"totalSupply","type":"function","inputs":[],"outputs":[{"type":"uint256"}]}]`,
9 FunctionName: "totalSupply",
10})
11if err != nil {
12 log.Fatal(err)
13}
14
15totalSupply := result.(*big.Int)
16fmt.Println("Total Supply:", totalSupply)

Passing Arguments

Pass function arguments using the Args slice:

1result, err := publicClient.ReadContract(ctx, client.ReadContractOptions{
2 Address: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
3 ABI: `[{
4 "name": "balanceOf",
5 "type": "function",
6 "inputs": [{"name": "owner", "type": "address"}],
7 "outputs": [{"type": "uint256"}]
8 }]`,
9 FunctionName: "balanceOf",
10 Args: []any{
11 common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"),
12 },
13})
14
15balance := result.(*big.Int)

Parameters

The Public Client’s ReadContract uses ReadContractOptions. The contract package’s ReadContract[T] uses ReadContractParams. Both support the same concepts (address, ABI, function name, args, block).

Address (required)

  • Type: common.Address
  • The contract address to call.

ABI (required)

  • Type: anystring, []byte, or *abi.ABI
  • The contract ABI as JSON bytes, a JSON string, or a pre-parsed *abi.ABI from abi.Parse.

FunctionName (required)

  • Type: string
  • The name of the view/pure function to call (e.g. "balanceOf", "totalSupply").

Args (optional)

  • Type: []any
  • Default: nil
  • Function arguments in order. Omit for functions with no parameters.

From (optional)

  • Type: *common.Address
  • Default: nil
  • Caller address used for the call. Some contracts use this for access control or modifiers.

Block / BlockTag (optional)

  • Type: client.BlockTag (e.g. client.BlockTagLatest, "safe", "0x1234") or BlockTag in ReadContractParams
  • Default: latest
  • Block at which to execute the read (e.g. client.BlockTagSafe, hex block number).

Return type

  • PublicClient.ReadContract(ctx, opts) returns (any, error). The decoded value is the first return value (e.g. *big.Int, string, common.Address). For multiple return values, use contract.ReadContract with a struct type or SimulateContract which returns []any.
  • contract.ReadContract[T](client, params) returns (T, error) with type-safe result (e.g. ReadContract[*big.Int], ReadContract[string]). Use contract.ReadContractWithContext when you need to pass a context.

SimulateContract

Use SimulateContract to run a contract call (including state-changing functions) without sending a transaction. Useful to check revert reasons or return values before writing. See Simulate Contract.

result, err := publicClient.SimulateContract(ctx, client.SimulateContractOptions{
    ReadContractOptions: client.ReadContractOptions{
        Address:      contractAddress,
        ABI:          abi,
        FunctionName: "transfer",
        Args:         []any{recipient, amount},
        From:         &sender,
    },
    Value: big.NewInt(0),
})

Using Typed Contract Bindings

For frequently used contracts, viem-go provides pre-built typed bindings:

1import "github.com/ChefBingbong/viem-go/contracts/erc20"
2
3// Create typed ERC20 instance
4usdc := common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")
5token, err := erc20.New(usdc, publicClient)
6if err != nil {
7 log.Fatal(err)
8}
9
10// Call typed methods - no ABI needed
11balance, err := token.BalanceOf(ctx, ownerAddress)
12name, err := token.Name(ctx)
13symbol, err := token.Symbol(ctx)
14decimals, err := token.Decimals(ctx)
15totalSupply, err := token.TotalSupply(ctx)
16allowance, err := token.Allowance(ctx, owner, spender)

Available Bindings

ContractImportMethods
ERC20contracts/erc20BalanceOf, Name, Symbol, Decimals, TotalSupply, Allowance
ERC721contracts/erc721BalanceOf, OwnerOf, Name, Symbol, TokenURI, GetApproved
ERC1155contracts/erc1155BalanceOf, BalanceOfBatch, URI, IsApprovedForAll

Reading at Specific Blocks

Query contract state at a specific block:

// Read at block number
result, err := publicClient.ReadContract(ctx, client.ReadContractOptions{
    Address:      contractAddress,
    ABI:          abi,
    FunctionName: "totalSupply",
    Block:        "0xE8D4A0", // Block number as hex
})

// Read at block tag
result, err := publicClient.ReadContract(ctx, client.ReadContractOptions{
    Address:      contractAddress,
    ABI:          abi,
    FunctionName: "totalSupply",
    Block:        client.BlockTagSafe,
})

Error Handling

Contract calls can fail for various reasons:

result, err := publicClient.ReadContract(ctx, client.ReadContractOptions{
    Address:      contractAddress,
    ABI:          abi,
    FunctionName: "balanceOf",
    Args:         []any{owner},
})

if err != nil {
    // Check for revert
    if strings.Contains(err.Error(), "execution reverted") {
        log.Println("Contract reverted")
    }
    // Check for invalid function
    if strings.Contains(err.Error(), "method not found") {
        log.Println("Function doesn't exist on contract")
    }
    return err
}

See also

Type Conversions

The result type depends on the Solidity return type:

Solidity TypeGo Type
uint256, int256*big.Int
uint8 - uint128*big.Int
addresscommon.Address
boolbool
stringstring
bytes[]byte
bytes32[32]byte
tuplestruct or map[string]any
arrayslice
// Casting results
balance := result.(*big.Int)
owner := result.(common.Address)
approved := result.(bool)
name := result.(string)
data := result.([]byte)