Reading Contracts
Read data from smart contracts using view and pure functions
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.
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)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)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).
common.Addressany — string, []byte, or *abi.ABIstring"balanceOf", "totalSupply").[]anynil*common.Addressnilclient.BlockTag (e.g. client.BlockTagLatest, "safe", "0x1234") or BlockTag in ReadContractParamsclient.BlockTagSafe, hex block number).(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.(T, error) with type-safe result (e.g. ReadContract[*big.Int], ReadContract[string]). Use contract.ReadContractWithContext when you need to pass a context.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),
})
For frequently used contracts, viem-go provides pre-built typed bindings:
1import "github.com/ChefBingbong/viem-go/contracts/erc20"2
3// Create typed ERC20 instance4usdc := common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")5token, err := erc20.New(usdc, publicClient)6if err != nil {7 log.Fatal(err)8}9
10// Call typed methods - no ABI needed11balance, 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)| Contract | Import | Methods |
|---|---|---|
| ERC20 | contracts/erc20 | BalanceOf, Name, Symbol, Decimals, TotalSupply, Allowance |
| ERC721 | contracts/erc721 | BalanceOf, OwnerOf, Name, Symbol, TokenURI, GetApproved |
| ERC1155 | contracts/erc1155 | BalanceOf, BalanceOfBatch, URI, IsApprovedForAll |
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,
})
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
}
The result type depends on the Solidity return type:
| Solidity Type | Go Type |
|---|---|
uint256, int256 | *big.Int |
uint8 - uint128 | *big.Int |
address | common.Address |
bool | bool |
string | string |
bytes | []byte |
bytes32 | [32]byte |
tuple | struct or map[string]any |
array | slice |
// Casting results
balance := result.(*big.Int)
owner := result.(common.Address)
approved := result.(bool)
name := result.(string)
data := result.([]byte)