From 8bc1887564b380d4de82dfd97fe80a71613a7cdb Mon Sep 17 00:00:00 2001 From: Daniel Liu <139250065@qq.com> Date: Fri, 22 Aug 2025 16:48:17 +0800 Subject: [PATCH] ethclient/gethclient: add block overrides to eth_call #26414 (#1377) --- ethclient/gethclient/gethclient.go | 71 ++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go index a87af72697..9f0cc1ee8a 100644 --- a/ethclient/gethclient/gethclient.go +++ b/ethclient/gethclient/gethclient.go @@ -144,6 +144,28 @@ func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockN return hex, err } +// CallContractWithBlockOverrides executes a message call transaction, which is directly executed +// in the VM of the node, but never mined into the blockchain. +// +// blockNumber selects the block height at which the call runs. It can be nil, in which +// case the code is taken from the latest known block. Note that state from very old +// blocks might not be available. +// +// overrides specifies a map of contract states that should be overwritten before executing +// the message call. +// +// blockOverrides specifies block fields exposed to the EVM that can be overridden for the call. +// +// Please use ethclient.CallContract instead if you don't need the override functionality. +func (ec *Client) CallContractWithBlockOverrides(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int, overrides *map[common.Address]OverrideAccount, blockOverrides BlockOverrides) ([]byte, error) { + var hex hexutil.Bytes + err := ec.c.CallContext( + ctx, &hex, "eth_call", toCallArg(msg), + toBlockNumArg(blockNumber), overrides, blockOverrides, + ) + return hex, err +} + // GCStats retrieves the current garbage collection stats from a geth node. func (ec *Client) GCStats(ctx context.Context) (*debug.GCStats, error) { var result debug.GCStats @@ -271,3 +293,52 @@ func (a OverrideAccount) MarshalJSON() ([]byte, error) { } return json.Marshal(output) } + +// BlockOverrides specifies the set of header fields to override. +type BlockOverrides struct { + // Number overrides the block number. + Number *big.Int + // Difficulty overrides the block difficulty. + Difficulty *big.Int + // Time overrides the block timestamp. Time is applied only when + // it is non-zero. + Time uint64 + // GasLimit overrides the block gas limit. GasLimit is applied only when + // it is non-zero. + GasLimit uint64 + // Coinbase overrides the block coinbase. Coinbase is applied only when + // it is different from the zero address. + Coinbase common.Address + // Random overrides the block extra data which feeds into the RANDOM opcode. + // Random is applied only when it is a non-zero hash. + Random common.Hash + // BaseFee overrides the block base fee. + BaseFee *big.Int +} + +func (o BlockOverrides) MarshalJSON() ([]byte, error) { + type override struct { + Number *hexutil.Big `json:"number,omitempty"` + Difficulty *hexutil.Big `json:"difficulty,omitempty"` + Time hexutil.Uint64 `json:"time,omitempty"` + GasLimit hexutil.Uint64 `json:"gasLimit,omitempty"` + Coinbase *common.Address `json:"coinbase,omitempty"` + Random *common.Hash `json:"random,omitempty"` + BaseFee *hexutil.Big `json:"baseFee,omitempty"` + } + + output := override{ + Number: (*hexutil.Big)(o.Number), + Difficulty: (*hexutil.Big)(o.Difficulty), + Time: hexutil.Uint64(o.Time), + GasLimit: hexutil.Uint64(o.GasLimit), + BaseFee: (*hexutil.Big)(o.BaseFee), + } + if o.Coinbase != (common.Address{}) { + output.Coinbase = &o.Coinbase + } + if o.Random != (common.Hash{}) { + output.Random = &o.Random + } + return json.Marshal(output) +}