package multicall import ( "context" "fmt" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/forta-network/go-multicall/contracts/contract_multicall" ) // DefaultAddress is the same for all chains (Multicall3). // Taken from https://github.com/mds1/multicall const DefaultAddress = "0xcA11bde05977b3631167028862bE2a173976CA11" // Caller makes multicalls. type Caller struct { contract contract_multicall.Interface } // New creates a new caller. func New(client bind.ContractCaller, multicallAddr ...string) (*Caller, error) { addr := DefaultAddress if multicallAddr != nil { addr = multicallAddr[0] } contract, err := contract_multicall.NewMulticallCaller(common.HexToAddress(addr), client) if err != nil { return nil, err } return &Caller{ contract: contract, }, nil } // Dial dials and Ethereum JSON-RPC API and uses the client as the // caller backend. func Dial(ctx context.Context, rawUrl string, multicallAddr ...string) (*Caller, error) { client, err := ethclient.DialContext(ctx, rawUrl) if err != nil { return nil, err } return New(client, multicallAddr...) } // Call makes multicalls. func (caller *Caller) Call(opts *bind.CallOpts, calls ...*Call) ([]*Call, error) { var multiCalls []contract_multicall.Multicall3Call3 for i, call := range calls { b, err := call.Pack() if err != nil { return calls, fmt.Errorf("failed to pack call inputs at index [%d]: %v", i, err) } multiCalls = append(multiCalls, contract_multicall.Multicall3Call3{ Target: call.Contract.Address, AllowFailure: call.CanFail, CallData: b, }) } results, err := caller.contract.Aggregate3(opts, multiCalls) if err != nil { return calls, fmt.Errorf("multicall failed: %v", err) } for i, result := range results { call := calls[i] // index always matches call.Failed = !result.Success if err := call.Unpack(result.ReturnData); err != nil { return calls, fmt.Errorf("failed to unpack call outputs at index [%d]: %v", i, err) } } return calls, nil }