Make many view calls in single eth_call
Find a file
cary ff027d0059 fix: bounds check, CanSet guard, and call.Failed on unpack error
- call.go: guard against struct fields > ABI output count (panic)
- call.go: check field.CanSet() before field.Set() (panic on unexported fields)
- caller.go: set call.Failed=true when CanFail=true and unpack fails

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 15:55:51 +08:00
.github/workflows first commit 2023-04-24 15:27:26 +03:00
contracts/contract_multicall Update abi.json 2024-12-24 19:48:24 +08:00
examples update 2024-12-24 20:13:09 +08:00
.gitignore first commit 2023-04-24 15:27:26 +03:00
.golangci.yml first commit 2023-04-24 15:27:26 +03:00
call.go fix: bounds check, CanSet guard, and call.Failed on unpack error 2026-04-18 15:55:51 +08:00
call_test.go first commit 2023-04-24 15:27:26 +03:00
caller.go fix: bounds check, CanSet guard, and call.Failed on unpack error 2026-04-18 15:55:51 +08:00
caller_test.go update 2024-12-24 20:13:09 +08:00
go.mod update 2024-12-24 20:13:09 +08:00
go.sum update 2024-12-24 20:13:09 +08:00
LICENSE first commit 2023-04-24 15:27:26 +03:00
Makefile first commit 2023-04-24 15:27:26 +03:00
README.md update 2024-12-24 20:13:09 +08:00

go-multicall

coverage build

A thin Go client for making multiple function calls in single eth_call request

Warning: MakerDAO Multicall contracts are different than the OpenZeppelin Multicall contract. Please see this thread in the OpenZeppelin forum if you are looking for an explanation.

Install

go get github.com/forta-network/go-multicall

Example

(See other examples under the examples directory!)

Multicall

package main

import (
	"context"
	"fmt"
	"math/big"

	"github.com/0xcary/go-multicall"
	"github.com/ethereum/go-ethereum/common"
)

const (
	APIURL   = "https://cloudflare-eth.com"
	ERC20ABI = `[
		{
			"constant":true,
			"inputs":[
					{
						"name":"tokenOwner",
						"type":"address"
					}
			],
			"name":"balanceOf",
			"outputs":[
					{
						"name":"balance",
						"type":"uint256"
					}
			],
			"payable":false,
			"stateMutability":"view",
			"type":"function"
		}
	]`
)

type balanceOutput struct {
	Balance *big.Int
}

func main() {
	caller, err := multicall.Dial(context.Background(), APIURL)
	if err != nil {
		panic(err)
	}

	contract, err := multicall.NewContract(ERC20ABI, "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")
	if err != nil {
		panic(err)
	}

	calls, err := caller.Call(nil,
		contract.NewCall(
			new(balanceOutput),
			"balanceOf",
			common.HexToAddress("0xcEe284F754E854890e311e3280b767F80797180d"), // Arbitrum One gateway
		).Name("Arbitrum One gateway balance"),
		contract.NewCall(
			new(balanceOutput),
			"balanceOf",
			common.HexToAddress("0x40ec5B33f54e0E8A33A975908C5BA1c14e5BbbDf"), // Polygon ERC20 bridge
		).Name("Polygon ERC20 bridge balance"),
	)
	if err != nil {
		panic(err)
	}
	for _, call := range calls {
		fmt.Println(call.CallName, ":", call.Outputs.(*balanceOutput).Balance)
	}
}

SingleCall

package main

import (
	"context"
	"fmt"
	"github.com/ethereum/go-ethereum/common"
	"github.com/0xcary/go-multicall"
)

const (
	APIURL   = "https://cloudflare-eth.com"
	ERC20ABI = `[
		{
			"constant":true,
			"inputs":[
					{
						"name":"tokenOwner",
						"type":"address"
					}
			],
			"name":"balanceOf",
			"outputs":[
					{
						"name":"balance",
						"type":"uint256"
					}
			],
			"payable":false,
			"stateMutability":"view",
			"type":"function"
		}
	]`
)

func main() {
	caller, err := multicall.Dial(context.Background(), APIURL)
	if err != nil {
		panic(err)
	}

	contract, err := multicall.NewContract(ERC20ABI, "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")
	if err != nil {
		panic(err)
	}

	single, err := caller.CallSingle(nil,
		contract.NewCall(
			nil,
			"balanceOf",
			common.HexToAddress("0xcEe284F754E854890e311e3280b767F80797180d"), // Arbitrum One gateway
		).Name("Arbitrum One gateway balance").SetExtend(map[string]string{
			"account": "0xcEe284F754E854890e311e3280b767F80797180d",
			"token":   "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
		}))
	if err != nil {
		return
	}

	fmt.Println(single.CallName, ":", single.UnpackResult()[0].(*big.Int), single.Extend)
}