Documentation
¶
Overview ¶
Package multicall batches Ethereum contract calls through the Multicall3 contract, collapsing many eth_call round trips into a few.
Create a client over any RPC backend, add typed calls to a Batch using abigen v2 pack/unpack functions, then Execute:
mc := multicall.New(client)
batch := mc.NewBatch()
name := batch.Add(tokenAddr, erc20.TryPackName, erc20.UnpackName)
if err := batch.Execute(ctx); err != nil {
return err
}
fmt.Println(name.Value)
Calls are chunked by BatchSize and sent concurrently (Concurrency). Individual call failures surface in each call's Result or callback; Execute's error only joins chunk-level RPC failures and ctx.Err() when cancelled. Concurrent chunks may observe different blocks: every Result reports the block its chunk landed on (Result.Block), and pinning a block with NewBatchWithOpts forces the whole batch onto one block.
Execute consumes the batch — a second call returns ErrBatchExecuted (ErrBatchInProgress while one is in flight). Retry re-sends the calls that failed, writing the outcome back into the same Results.
Index ¶
- Constants
- Variables
- func AddWithCallback[T any](b *Batch, addr common.Address, pack func() ([]byte, error), ...)deprecated
- func IsCallFailed(err error) bool
- func RevertReason(err error) string
- type Batch
- func (b *Batch) Add[T any](addr common.Address, pack func() ([]byte, error), ...) *Result[T]
- func (b *Batch) AddCall(addr common.Address, pack func() ([]byte, error)) *Result[[]byte]
- func (b *Batch) AddWithCallback[T any](addr common.Address, pack func() ([]byte, error), ...)
- func (b *Batch) AddWithResult[T any](addr common.Address, pack func() ([]byte, error), ...)
- func (b *Batch) Execute(ctx context.Context) error
- func (b *Batch) Len() int
- func (b *Batch) Retry(ctx context.Context) error
- type BlockInfo
- type CallFailedError
- type Caller
- type Config
- type Logger
- type Method
- type Multicall
- type Option
- type Result
Constants ¶
const DefaultMulticall3Address = "0xcA11bde05977b3631167028862bE2a173976CA11"
DefaultMulticall3Address is the standard deployment address for Multicall3 contract (universal across all EVM chains)
Variables ¶
var ( // ErrResultMissing result count does not match call count ErrResultMissing = errors.New("multicall: result missing from response") // ErrBatchInProgress is returned by Execute and Retry when another // Execute or Retry on the same batch has not returned yet. ErrBatchInProgress = errors.New("multicall: batch execution already in progress") // ErrBatchExecuted is returned by Execute on a batch that already ran // and received no new calls since: the calls are consumed. Retry // re-sends the failed ones instead. ErrBatchExecuted = errors.New("multicall: batch already executed") )
var ( // TryPack functions - encode method calls into calldata (return error on failure) TryPackGetBasefee = codec.TryPackGetBasefee TryPackGetBlockHash = codec.TryPackGetBlockHash TryPackGetBlockNumber = codec.TryPackGetBlockNumber TryPackGetChainId = codec.TryPackGetChainId TryPackGetCurrentBlockCoinbase = codec.TryPackGetCurrentBlockCoinbase TryPackGetCurrentBlockDifficulty = codec.TryPackGetCurrentBlockDifficulty TryPackGetCurrentBlockGasLimit = codec.TryPackGetCurrentBlockGasLimit TryPackGetCurrentBlockTimestamp = codec.TryPackGetCurrentBlockTimestamp TryPackGetEthBalance = codec.TryPackGetEthBalance TryPackGetLastBlockHash = codec.TryPackGetLastBlockHash // Unpack functions - decode result data into Go types UnpackGetBasefee = codec.UnpackGetBasefee UnpackGetBlockHash = codec.UnpackGetBlockHash UnpackGetBlockNumber = codec.UnpackGetBlockNumber UnpackGetChainId = codec.UnpackGetChainId UnpackGetCurrentBlockCoinbase = codec.UnpackGetCurrentBlockCoinbase UnpackGetCurrentBlockDifficulty = codec.UnpackGetCurrentBlockDifficulty UnpackGetCurrentBlockGasLimit = codec.UnpackGetCurrentBlockGasLimit UnpackGetCurrentBlockTimestamp = codec.UnpackGetCurrentBlockTimestamp UnpackGetEthBalance = codec.UnpackGetEthBalance UnpackGetLastBlockHash = codec.UnpackGetLastBlockHash )
Functions ¶
func AddWithCallback
deprecated
func AddWithCallback[T any]( b *Batch, addr common.Address, pack func() ([]byte, error), unpack func([]byte) (T, error), callback func(T, error), )
AddWithCallback adds a call to the batch, automatically dispatches results via callback on Execute.
Deprecated: Use (*Batch).AddWithCallback instead; migrate with cmd/multicallfix (see the shim note above).
func IsCallFailed ¶
IsCallFailed checks if error is an individual call failure
func RevertReason ¶
RevertReason extracts revert reason from error, returns empty string if not CallFailedError
Types ¶
type Batch ¶
type Batch struct {
// contains filtered or unexported fields
}
Batch represents a batch of pending calls
func (*Batch) Add ¶ added in v0.1.0
func (b *Batch) Add[T any]( addr common.Address, pack func() ([]byte, error), unpack func([]byte) (T, error), ) *Result[T]
Add adds a call to the batch and returns a Result reference, readable after Execute. pack is an abigen v2 TryPack* method, unpack its Unpack*. If pack itself fails, the returned Result already carries the error — no RPC is issued for it and Execute skips it.
func (*Batch) AddCall ¶ added in v0.1.0
AddCall adds a raw call without decoding, only cares about success/failure
func (*Batch) AddWithCallback ¶ added in v0.1.0
func (b *Batch) AddWithCallback[T any]( addr common.Address, pack func() ([]byte, error), unpack func([]byte) (T, error), callback func(T, error), )
AddWithCallback adds a call whose callback receives the unpacked result (or error) when the batch executes. Callbacks may fire concurrently and must not panic; see AddWithResult for the contract. If pack itself fails, callback runs synchronously before AddWithCallback returns instead of waiting for Execute, and no RPC is issued for the call.
func (*Batch) AddWithResult ¶ added in v0.1.0
func (b *Batch) AddWithResult[T any]( addr common.Address, pack func() ([]byte, error), unpack func([]byte) (T, error), callback func(Result[T]), )
AddWithResult adds a call whose callback receives the full Result — including Block, the block the chunk landed on — when the batch executes.
Callbacks run on their chunk's goroutine, so callbacks of different calls may fire concurrently: guard any state they share. A callback must not panic: on a single-chunk batch the panic propagates out of Execute (or Retry) — the batch stays usable, its unsettled calls are not retried — while on a concurrent chunk it crashes the process.
If pack itself fails, callback runs synchronously before AddWithResult returns with the error already set, and no RPC is issued for the call.
func (*Batch) Execute ¶
Execute executes all calls in the batch, chunked and concurrent. It consumes the batch: calling Execute again before adding new calls returns ErrBatchExecuted (ErrBatchInProgress while the first Execute is still running). Calls that failed — the whole chunk on an RPC error, individual reverts otherwise — stay eligible for Retry.
The returned error joins chunk-level RPC errors — and ctx.Err() when cancelled — while individual call failures surface in each call's Result or callback instead.
func (*Batch) Retry ¶ added in v0.1.0
Retry re-executes the calls that failed the last execution — chunk-level RPC errors and individual call failures alike — writing the outcome back into the same Result/callback values; successful calls are not re-sent. The failed subset is re-chunked under the current BatchSize, and a block number pinned at batch creation keeps applying. With nothing to retry (never executed, or everything succeeded) Retry is a no-op returning nil.
Retrying re-invokes the callbacks of the failed calls, so callbacks must be re-entrant. The retry policy — attempts, backoff, which errors are worth it — stays with the caller:
err := batch.Execute(ctx)
for attempt := 0; shouldRetry(err) && attempt < 3; attempt++ {
time.Sleep(backoff(attempt))
err = batch.Retry(ctx)
}
Like Execute, Retry returns ErrBatchInProgress while another Execute or Retry is in flight, and joins chunk-level errors otherwise.
type BlockInfo ¶ added in v0.1.0
type BlockInfo struct {
// Number is the block number; nil when unavailable — the chunk's RPC
// failed, the call never went on chain (pack error), or the
// configured Method does not report blocks.
Number *big.Int
// Hash is the block hash; zero when the Method reports only the number.
Hash [32]byte
}
BlockInfo describes the block a chunk of calls landed in, as reported by the Multicall contract.
type CallFailedError ¶
type CallFailedError struct {
Reason string // Decoded revert reason
Data []byte // Raw return data
}
CallFailedError error for individual call failure, includes revert reason (if any)
func (*CallFailedError) Error ¶
func (e *CallFailedError) Error() string
type Caller ¶
type Caller interface {
CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
}
Caller is the underlying RPC call interface, implemented by *ethclient.Client
type Config ¶
type Config struct {
Address common.Address // Multicall3 contract address
BatchSize int // Max calls per batch
Concurrency int // Concurrent batch count
// Method selects the underlying contract method; the zero value is
// MethodTryBlockAndAggregate.
Method Method
// AllowFailure (default true) sets the failure tolerance of the
// configured method: it maps to requireSuccess with
// MethodTryBlockAndAggregate and to the per-call flag with
// MethodAggregate3. When true, a failed call surfaces as
// CallFailedError in its own Result while the rest succeeds. When
// false, one failing call reverts its whole chunk and every Result in
// it carries that error. Ignored by MethodBlockAndAggregate and
// MethodAggregate, which are strict by nature.
AllowFailure bool
// Logger receives diagnostics; nil logs nothing (see Logger).
Logger Logger
}
Config client configuration
type Logger ¶ added in v0.1.0
Logger is the minimal logging interface the library writes diagnostics to; wire it to any logging backend via WithLogger. Events are logged sparingly: successful chunks at debug level, failures, cancellations and malformed responses at warn level. Errors are always returned as values too, so logging is purely a diagnostic aid.
type Method ¶ added in v0.1.0
type Method int
Method selects the Multicall contract method used under the hood.
const ( // MethodTryBlockAndAggregate is the default. Failure tolerance is // configurable via AllowFailure, and every Result reports the block // its chunk landed on. Supported by Multicall2 and later. MethodTryBlockAndAggregate Method = iota // MethodBlockAndAggregate reverts the whole chunk if any call fails, // and reports the block in every Result. Supported by Multicall2 and // later. MethodBlockAndAggregate // MethodAggregate3 is Multicall3's per-call allowFailure method, // without block reporting; AllowFailure sets the per-call flag. MethodAggregate3 // MethodAggregate is the greatest common denominator across // Multicall1/2/3: any failing call reverts the whole chunk. It // reports the block number, not its hash. Use it when the configured // address predates Multicall2. MethodAggregate )
type Multicall ¶
type Multicall struct {
// contains filtered or unexported fields
}
Multicall client
type Option ¶
type Option func(*Config)
Option configuration option
func WithAddress ¶
WithAddress sets custom Multicall3 contract address
func WithAggregate
deprecated
added in
v0.0.2
func WithAggregate() Option
WithAggregate uses aggregate instead of the default tryBlockAndAggregate.
Deprecated: WithMethod(MethodAggregate) is the exact equivalent; consider WithMethod(MethodBlockAndAggregate) to additionally get block info in every Result.
func WithAllowFailure ¶
WithAllowFailure sets the default per-call failure tolerance; see Config.AllowFailure for what it means per Method.
func WithConcurrency ¶
WithConcurrency sets concurrent batch count
func WithLogger ¶ added in v0.1.0
WithLogger sets the logger the library writes diagnostics to; see Logger. nil (the default) logs nothing.
func WithMethod ¶ added in v0.1.0
WithMethod selects the underlying Multicall contract method; see Method.
type Result ¶
type Result[T any] struct { Value T Err error Ok bool // whether the call succeeded (exactly Err == nil) // Block is the block its chunk landed in; see BlockInfo for when it // is zero. A failed call still reports its block. Block BlockInfo }
Result generic result container, read after Execute.
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
builtin
command
Package main demonstrates using Multicall3 built-in methods These methods are available as package-level functions for convenience.
|
Package main demonstrates using Multicall3 built-in methods These methods are available as package-level functions for convenience. |
|
callback-based
command
Package main demonstrates Pattern 2: Callback-Based usage of multicall Add calls with a callback function that automatically receives results during Execute().
|
Package main demonstrates Pattern 2: Callback-Based usage of multicall Add calls with a callback function that automatically receives results during Execute(). |
|
result-based
command
Package main demonstrates Pattern 1: Result-Based usage of multicall Add calls and get a Result reference.
|
Package main demonstrates Pattern 1: Result-Based usage of multicall Add calls and get a Result reference. |