From 5691d733a306be3585bd55b86b0d1a367cdf3c8c Mon Sep 17 00:00:00 2001 From: allen Date: Wed, 8 Oct 2025 20:09:52 -0400 Subject: [PATCH 1/9] ethclient: add support for eth_simulateV1 --- ethclient/ethclient.go | 101 +++++++++++++++ ethclient/ethclient_test.go | 246 ++++++++++++++++++++++++++++++++++++ 2 files changed, 347 insertions(+) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 1195929f7d2e..bac90c3f70ec 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/rpc" ) @@ -819,3 +820,103 @@ func (p *rpcProgress) toSyncProgress() *ethereum.SyncProgress { StateIndexRemaining: uint64(p.StateIndexRemaining), } } + +// SimulateOptions represents the options for eth_simulateV1. +type SimulateOptions struct { + BlockStateCalls []SimulateBlock `json:"blockStateCalls"` + TraceTransfers bool `json:"traceTransfers"` + Validation bool `json:"validation"` + ReturnFullTransactions bool `json:"returnFullTransactions"` +} + +// SimulateBlock represents a batch of calls to be simulated. +type SimulateBlock struct { + BlockOverrides *BlockOverrides `json:"blockOverrides,omitempty"` + StateOverrides *StateOverride `json:"stateOverrides,omitempty"` + Calls []CallArgs `json:"calls"` +} + +// BlockOverrides is a set of header fields to override. +type BlockOverrides 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"` + FeeRecipient *common.Address `json:"feeRecipient,omitempty"` + PrevRandao *common.Hash `json:"prevRandao,omitempty"` + BaseFeePerGas *hexutil.Big `json:"baseFeePerGas,omitempty"` + BlobBaseFee *hexutil.Big `json:"blobBaseFee,omitempty"` + BeaconRoot *common.Hash `json:"beaconRoot,omitempty"` + Withdrawals *types.Withdrawals `json:"withdrawals,omitempty"` +} + +// StateOverride is the collection of overridden accounts. +type StateOverride map[common.Address]OverrideAccount + +// OverrideAccount indicates the overriding fields of account during the execution +// of a message call. +type OverrideAccount struct { + Nonce *hexutil.Uint64 `json:"nonce,omitempty"` + Code *hexutil.Bytes `json:"code,omitempty"` + Balance *hexutil.Big `json:"balance,omitempty"` + State map[common.Hash]common.Hash `json:"state,omitempty"` + StateDiff map[common.Hash]common.Hash `json:"stateDiff,omitempty"` + MovePrecompileTo *common.Address `json:"movePrecompileToAddress,omitempty"` +} + +// CallArgs represents the arguments to construct a transaction call. +type CallArgs struct { + From *common.Address `json:"from,omitempty"` + To *common.Address `json:"to,omitempty"` + Gas *hexutil.Uint64 `json:"gas,omitempty"` + GasPrice *hexutil.Big `json:"gasPrice,omitempty"` + MaxFeePerGas *hexutil.Big `json:"maxFeePerGas,omitempty"` + MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` + Value *hexutil.Big `json:"value,omitempty"` + Nonce *hexutil.Uint64 `json:"nonce,omitempty"` + Data *hexutil.Bytes `json:"data,omitempty"` + Input *hexutil.Bytes `json:"input,omitempty"` + AccessList *types.AccessList `json:"accessList,omitempty"` + ChainID *hexutil.Big `json:"chainId,omitempty"` + BlobFeeCap *hexutil.Big `json:"maxFeePerBlobGas,omitempty"` + BlobHashes []common.Hash `json:"blobVersionedHashes,omitempty"` + Blobs []kzg4844.Blob `json:"blobs,omitempty"` + Commitments []kzg4844.Commitment `json:"commitments,omitempty"` + Proofs []kzg4844.Proof `json:"proofs,omitempty"` + AuthorizationList []types.SetCodeAuthorization `json:"authorizationList,omitempty"` +} + +// SimulateCallResult is the result of a simulated call. +type SimulateCallResult struct { + ReturnValue hexutil.Bytes `json:"returnData"` + Logs []*types.Log `json:"logs"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + Status hexutil.Uint64 `json:"status"` + Error *CallError `json:"error,omitempty"` +} + +// CallError represents an error from a simulated call. +type CallError struct { + Code int `json:"code"` + Message string `json:"message"` + Data string `json:"data,omitempty"` +} + +// SimulateBlockResult represents the result of a simulated block. +type SimulateBlockResult struct { + Number hexutil.Uint64 `json:"number"` + Hash common.Hash `json:"hash"` + Timestamp hexutil.Uint64 `json:"timestamp"` + GasLimit hexutil.Uint64 `json:"gasLimit"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + FeeRecipient common.Address `json:"miner"` + BaseFeePerGas *hexutil.Big `json:"baseFeePerGas,omitempty"` + Calls []SimulateCallResult `json:"calls"` +} + +// SimulateV1 executes transactions on top of a base state. +func (ec *Client) SimulateV1(ctx context.Context, opts SimulateOptions, blockNrOrHash *rpc.BlockNumberOrHash) ([]SimulateBlockResult, error) { + var result []SimulateBlockResult + err := ec.c.CallContext(ctx, &result, "eth_simulateV1", opts, blockNrOrHash) + return result, err +} diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 815bc29de457..7b40b89add26 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" @@ -754,3 +755,248 @@ func ExampleRevertErrorData() { // revert: 08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a75736572206572726f72 // message: user error } + +func TestSimulateV1(t *testing.T) { + backend, _, err := newTestBackend(nil) + if err != nil { + t.Fatalf("Failed to create test backend: %v", err) + } + defer backend.Close() + + client := ethclient.NewClient(backend.Attach()) + defer client.Close() + + ctx := context.Background() + + // Get current base fee + header, err := client.HeaderByNumber(ctx, nil) + if err != nil { + t.Fatalf("Failed to get header: %v", err) + } + + // Simple test: transfer ETH from one account to another + from := testAddr + to := common.HexToAddress("0x0000000000000000000000000000000000000001") + value := hexutil.Big(*big.NewInt(100)) + gas := hexutil.Uint64(100000) + maxFeePerGas := hexutil.Big(*new(big.Int).Mul(header.BaseFee, big.NewInt(2))) + + opts := ethclient.SimulateOptions{ + BlockStateCalls: []ethclient.SimulateBlock{ + { + Calls: []ethclient.CallArgs{ + { + From: &from, + To: &to, + Value: &value, + Gas: &gas, + MaxFeePerGas: &maxFeePerGas, + }, + }, + }, + }, + Validation: true, + } + + results, err := client.SimulateV1(ctx, opts, nil) + if err != nil { + t.Fatalf("SimulateV1 failed: %v", err) + } + + if len(results) != 1 { + t.Fatalf("expected 1 block result, got %d", len(results)) + } + + if len(results[0].Calls) != 1 { + t.Fatalf("expected 1 call result, got %d", len(results[0].Calls)) + } + + // Check that the transaction succeeded + if results[0].Calls[0].Status != 1 { + t.Errorf("expected status 1 (success), got %d", results[0].Calls[0].Status) + } + + if results[0].Calls[0].Error != nil { + t.Errorf("expected no error, got %v", results[0].Calls[0].Error) + } +} + +func TestSimulateV1WithBlockOverrides(t *testing.T) { + backend, _, err := newTestBackend(nil) + if err != nil { + t.Fatalf("Failed to create test backend: %v", err) + } + defer backend.Close() + + client := ethclient.NewClient(backend.Attach()) + defer client.Close() + + ctx := context.Background() + + // Get current base fee + header, err := client.HeaderByNumber(ctx, nil) + if err != nil { + t.Fatalf("Failed to get header: %v", err) + } + + from := testAddr + to := common.HexToAddress("0x0000000000000000000000000000000000000001") + value := hexutil.Big(*big.NewInt(100)) + gas := hexutil.Uint64(100000) + maxFeePerGas := hexutil.Big(*new(big.Int).Mul(header.BaseFee, big.NewInt(2))) + + // Override timestamp only + timestamp := hexutil.Uint64(1234567890) + + opts := ethclient.SimulateOptions{ + BlockStateCalls: []ethclient.SimulateBlock{ + { + BlockOverrides: ðclient.BlockOverrides{ + Time: ×tamp, + }, + Calls: []ethclient.CallArgs{ + { + From: &from, + To: &to, + Value: &value, + Gas: &gas, + MaxFeePerGas: &maxFeePerGas, + }, + }, + }, + }, + Validation: true, + } + + results, err := client.SimulateV1(ctx, opts, nil) + if err != nil { + t.Fatalf("SimulateV1 with block overrides failed: %v", err) + } + + if len(results) != 1 { + t.Fatalf("expected 1 block result, got %d", len(results)) + } + + // Verify the timestamp was overridden + if results[0].Timestamp != timestamp { + t.Errorf("expected timestamp %d, got %d", timestamp, results[0].Timestamp) + } +} + +func TestSimulateV1WithStateOverrides(t *testing.T) { + backend, _, err := newTestBackend(nil) + if err != nil { + t.Fatalf("Failed to create test backend: %v", err) + } + defer backend.Close() + + client := ethclient.NewClient(backend.Attach()) + defer client.Close() + + ctx := context.Background() + + // Get current base fee + header, err := client.HeaderByNumber(ctx, nil) + if err != nil { + t.Fatalf("Failed to get header: %v", err) + } + + from := testAddr + to := common.HexToAddress("0x0000000000000000000000000000000000000001") + value := hexutil.Big(*big.NewInt(1000000000000000000)) // 1 ETH + gas := hexutil.Uint64(100000) + maxFeePerGas := hexutil.Big(*new(big.Int).Mul(header.BaseFee, big.NewInt(2))) + + // Override the balance of the 'from' address + balanceStr := "1000000000000000000000" + balance := new(big.Int) + balance.SetString(balanceStr, 10) + + opts := ethclient.SimulateOptions{ + BlockStateCalls: []ethclient.SimulateBlock{ + { + StateOverrides: ðclient.StateOverride{ + from: ethclient.OverrideAccount{ + Balance: (*hexutil.Big)(balance), + }, + }, + Calls: []ethclient.CallArgs{ + { + From: &from, + To: &to, + Value: &value, + Gas: &gas, + MaxFeePerGas: &maxFeePerGas, + }, + }, + }, + }, + Validation: true, + } + + results, err := client.SimulateV1(ctx, opts, nil) + if err != nil { + t.Fatalf("SimulateV1 with state overrides failed: %v", err) + } + + if len(results) != 1 { + t.Fatalf("expected 1 block result, got %d", len(results)) + } + + if results[0].Calls[0].Status != 1 { + t.Errorf("expected status 1 (success), got %d", results[0].Calls[0].Status) + } +} + +func TestSimulateV1WithBlockNumberOrHash(t *testing.T) { + backend, _, err := newTestBackend(nil) + if err != nil { + t.Fatalf("Failed to create test backend: %v", err) + } + defer backend.Close() + + client := ethclient.NewClient(backend.Attach()) + defer client.Close() + + ctx := context.Background() + + // Get current base fee + header, err := client.HeaderByNumber(ctx, nil) + if err != nil { + t.Fatalf("Failed to get header: %v", err) + } + + from := testAddr + to := common.HexToAddress("0x0000000000000000000000000000000000000001") + value := hexutil.Big(*big.NewInt(100)) + gas := hexutil.Uint64(100000) + maxFeePerGas := hexutil.Big(*new(big.Int).Mul(header.BaseFee, big.NewInt(2))) + + opts := ethclient.SimulateOptions{ + BlockStateCalls: []ethclient.SimulateBlock{ + { + Calls: []ethclient.CallArgs{ + { + From: &from, + To: &to, + Value: &value, + Gas: &gas, + MaxFeePerGas: &maxFeePerGas, + }, + }, + }, + }, + Validation: true, + } + + // Simulate on the latest block + latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) + results, err := client.SimulateV1(ctx, opts, &latest) + if err != nil { + t.Fatalf("SimulateV1 with latest block failed: %v", err) + } + + if len(results) != 1 { + t.Fatalf("expected 1 block result, got %d", len(results)) + } +} From 4042a9ba1b771a75764d414f5f3b61f1459316bd Mon Sep 17 00:00:00 2001 From: allen Date: Thu, 9 Oct 2025 12:34:01 -0400 Subject: [PATCH 2/9] fix --- ethclient/ethclient.go | 76 +++++++++--------------------- ethclient/ethclient_test.go | 94 +++++++++++++++++++------------------ 2 files changed, 70 insertions(+), 100 deletions(-) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index bac90c3f70ec..d5ef88e02012 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -28,7 +28,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto/kzg4844" + "github.com/ethereum/go-ethereum/ethclient/gethclient" "github.com/ethereum/go-ethereum/rpc" ) @@ -831,59 +831,27 @@ type SimulateOptions struct { // SimulateBlock represents a batch of calls to be simulated. type SimulateBlock struct { - BlockOverrides *BlockOverrides `json:"blockOverrides,omitempty"` - StateOverrides *StateOverride `json:"stateOverrides,omitempty"` - Calls []CallArgs `json:"calls"` -} - -// BlockOverrides is a set of header fields to override. -type BlockOverrides 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"` - FeeRecipient *common.Address `json:"feeRecipient,omitempty"` - PrevRandao *common.Hash `json:"prevRandao,omitempty"` - BaseFeePerGas *hexutil.Big `json:"baseFeePerGas,omitempty"` - BlobBaseFee *hexutil.Big `json:"blobBaseFee,omitempty"` - BeaconRoot *common.Hash `json:"beaconRoot,omitempty"` - Withdrawals *types.Withdrawals `json:"withdrawals,omitempty"` -} - -// StateOverride is the collection of overridden accounts. -type StateOverride map[common.Address]OverrideAccount - -// OverrideAccount indicates the overriding fields of account during the execution -// of a message call. -type OverrideAccount struct { - Nonce *hexutil.Uint64 `json:"nonce,omitempty"` - Code *hexutil.Bytes `json:"code,omitempty"` - Balance *hexutil.Big `json:"balance,omitempty"` - State map[common.Hash]common.Hash `json:"state,omitempty"` - StateDiff map[common.Hash]common.Hash `json:"stateDiff,omitempty"` - MovePrecompileTo *common.Address `json:"movePrecompileToAddress,omitempty"` -} - -// CallArgs represents the arguments to construct a transaction call. -type CallArgs struct { - From *common.Address `json:"from,omitempty"` - To *common.Address `json:"to,omitempty"` - Gas *hexutil.Uint64 `json:"gas,omitempty"` - GasPrice *hexutil.Big `json:"gasPrice,omitempty"` - MaxFeePerGas *hexutil.Big `json:"maxFeePerGas,omitempty"` - MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` - Value *hexutil.Big `json:"value,omitempty"` - Nonce *hexutil.Uint64 `json:"nonce,omitempty"` - Data *hexutil.Bytes `json:"data,omitempty"` - Input *hexutil.Bytes `json:"input,omitempty"` - AccessList *types.AccessList `json:"accessList,omitempty"` - ChainID *hexutil.Big `json:"chainId,omitempty"` - BlobFeeCap *hexutil.Big `json:"maxFeePerBlobGas,omitempty"` - BlobHashes []common.Hash `json:"blobVersionedHashes,omitempty"` - Blobs []kzg4844.Blob `json:"blobs,omitempty"` - Commitments []kzg4844.Commitment `json:"commitments,omitempty"` - Proofs []kzg4844.Proof `json:"proofs,omitempty"` - AuthorizationList []types.SetCodeAuthorization `json:"authorizationList,omitempty"` + BlockOverrides *gethclient.BlockOverrides `json:"blockOverrides,omitempty"` + StateOverrides *map[common.Address]gethclient.OverrideAccount `json:"stateOverrides,omitempty"` + Calls []ethereum.CallMsg `json:"calls"` +} + +// MarshalJSON implements json.Marshaler for SimulateBlock. +func (s SimulateBlock) MarshalJSON() ([]byte, error) { + type Alias struct { + BlockOverrides *gethclient.BlockOverrides `json:"blockOverrides,omitempty"` + StateOverrides *map[common.Address]gethclient.OverrideAccount `json:"stateOverrides,omitempty"` + Calls []interface{} `json:"calls"` + } + calls := make([]interface{}, len(s.Calls)) + for i, call := range s.Calls { + calls[i] = toCallArg(call) + } + return json.Marshal(Alias{ + BlockOverrides: s.BlockOverrides, + StateOverrides: s.StateOverrides, + Calls: calls, + }) } // SimulateCallResult is the result of a simulated call. diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 7b40b89add26..a8ec29579da1 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -29,7 +29,6 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" @@ -38,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/ethclient/gethclient" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" @@ -777,20 +777,20 @@ func TestSimulateV1(t *testing.T) { // Simple test: transfer ETH from one account to another from := testAddr to := common.HexToAddress("0x0000000000000000000000000000000000000001") - value := hexutil.Big(*big.NewInt(100)) - gas := hexutil.Uint64(100000) - maxFeePerGas := hexutil.Big(*new(big.Int).Mul(header.BaseFee, big.NewInt(2))) + value := big.NewInt(100) + gas := uint64(100000) + maxFeePerGas := new(big.Int).Mul(header.BaseFee, big.NewInt(2)) opts := ethclient.SimulateOptions{ BlockStateCalls: []ethclient.SimulateBlock{ { - Calls: []ethclient.CallArgs{ + Calls: []ethereum.CallMsg{ { - From: &from, - To: &to, - Value: &value, - Gas: &gas, - MaxFeePerGas: &maxFeePerGas, + From: from, + To: &to, + Value: value, + Gas: gas, + GasFeeCap: maxFeePerGas, }, }, }, @@ -841,26 +841,26 @@ func TestSimulateV1WithBlockOverrides(t *testing.T) { from := testAddr to := common.HexToAddress("0x0000000000000000000000000000000000000001") - value := hexutil.Big(*big.NewInt(100)) - gas := hexutil.Uint64(100000) - maxFeePerGas := hexutil.Big(*new(big.Int).Mul(header.BaseFee, big.NewInt(2))) + value := big.NewInt(100) + gas := uint64(100000) + maxFeePerGas := new(big.Int).Mul(header.BaseFee, big.NewInt(2)) // Override timestamp only - timestamp := hexutil.Uint64(1234567890) + timestamp := uint64(1234567890) opts := ethclient.SimulateOptions{ BlockStateCalls: []ethclient.SimulateBlock{ { - BlockOverrides: ðclient.BlockOverrides{ - Time: ×tamp, + BlockOverrides: &gethclient.BlockOverrides{ + Time: timestamp, }, - Calls: []ethclient.CallArgs{ + Calls: []ethereum.CallMsg{ { - From: &from, - To: &to, - Value: &value, - Gas: &gas, - MaxFeePerGas: &maxFeePerGas, + From: from, + To: &to, + Value: value, + Gas: gas, + GasFeeCap: maxFeePerGas, }, }, }, @@ -878,7 +878,7 @@ func TestSimulateV1WithBlockOverrides(t *testing.T) { } // Verify the timestamp was overridden - if results[0].Timestamp != timestamp { + if uint64(results[0].Timestamp) != timestamp { t.Errorf("expected timestamp %d, got %d", timestamp, results[0].Timestamp) } } @@ -903,30 +903,32 @@ func TestSimulateV1WithStateOverrides(t *testing.T) { from := testAddr to := common.HexToAddress("0x0000000000000000000000000000000000000001") - value := hexutil.Big(*big.NewInt(1000000000000000000)) // 1 ETH - gas := hexutil.Uint64(100000) - maxFeePerGas := hexutil.Big(*new(big.Int).Mul(header.BaseFee, big.NewInt(2))) + value := big.NewInt(1000000000000000000) // 1 ETH + gas := uint64(100000) + maxFeePerGas := new(big.Int).Mul(header.BaseFee, big.NewInt(2)) // Override the balance of the 'from' address balanceStr := "1000000000000000000000" balance := new(big.Int) balance.SetString(balanceStr, 10) + stateOverrides := map[common.Address]gethclient.OverrideAccount{ + from: { + Balance: balance, + }, + } + opts := ethclient.SimulateOptions{ BlockStateCalls: []ethclient.SimulateBlock{ { - StateOverrides: ðclient.StateOverride{ - from: ethclient.OverrideAccount{ - Balance: (*hexutil.Big)(balance), - }, - }, - Calls: []ethclient.CallArgs{ + StateOverrides: &stateOverrides, + Calls: []ethereum.CallMsg{ { - From: &from, - To: &to, - Value: &value, - Gas: &gas, - MaxFeePerGas: &maxFeePerGas, + From: from, + To: &to, + Value: value, + Gas: gas, + GasFeeCap: maxFeePerGas, }, }, }, @@ -968,20 +970,20 @@ func TestSimulateV1WithBlockNumberOrHash(t *testing.T) { from := testAddr to := common.HexToAddress("0x0000000000000000000000000000000000000001") - value := hexutil.Big(*big.NewInt(100)) - gas := hexutil.Uint64(100000) - maxFeePerGas := hexutil.Big(*new(big.Int).Mul(header.BaseFee, big.NewInt(2))) + value := big.NewInt(100) + gas := uint64(100000) + maxFeePerGas := new(big.Int).Mul(header.BaseFee, big.NewInt(2)) opts := ethclient.SimulateOptions{ BlockStateCalls: []ethclient.SimulateBlock{ { - Calls: []ethclient.CallArgs{ + Calls: []ethereum.CallMsg{ { - From: &from, - To: &to, - Value: &value, - Gas: &gas, - MaxFeePerGas: &maxFeePerGas, + From: from, + To: &to, + Value: value, + Gas: gas, + GasFeeCap: maxFeePerGas, }, }, }, From 5c514f6bccecfef52d9d08bfd3941ef9e765f07a Mon Sep 17 00:00:00 2001 From: allen Date: Thu, 9 Oct 2025 13:30:06 -0400 Subject: [PATCH 3/9] fix cycle import --- ethclient/ethclient.go | 14 ++-- ethclient/ethclient_test.go | 6 +- ethclient/gethclient/gethclient.go | 101 ++---------------------- ethclient/types/overrides.go | 121 +++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 104 deletions(-) create mode 100644 ethclient/types/overrides.go diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index d5ef88e02012..01c5c49e2514 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -28,7 +28,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethclient/gethclient" + clienttypes "github.com/ethereum/go-ethereum/ethclient/types" "github.com/ethereum/go-ethereum/rpc" ) @@ -831,17 +831,17 @@ type SimulateOptions struct { // SimulateBlock represents a batch of calls to be simulated. type SimulateBlock struct { - BlockOverrides *gethclient.BlockOverrides `json:"blockOverrides,omitempty"` - StateOverrides *map[common.Address]gethclient.OverrideAccount `json:"stateOverrides,omitempty"` - Calls []ethereum.CallMsg `json:"calls"` + BlockOverrides *clienttypes.BlockOverrides `json:"blockOverrides,omitempty"` + StateOverrides *map[common.Address]clienttypes.OverrideAccount `json:"stateOverrides,omitempty"` + Calls []ethereum.CallMsg `json:"calls"` } // MarshalJSON implements json.Marshaler for SimulateBlock. func (s SimulateBlock) MarshalJSON() ([]byte, error) { type Alias struct { - BlockOverrides *gethclient.BlockOverrides `json:"blockOverrides,omitempty"` - StateOverrides *map[common.Address]gethclient.OverrideAccount `json:"stateOverrides,omitempty"` - Calls []interface{} `json:"calls"` + BlockOverrides *clienttypes.BlockOverrides `json:"blockOverrides,omitempty"` + StateOverrides *map[common.Address]clienttypes.OverrideAccount `json:"stateOverrides,omitempty"` + Calls []interface{} `json:"calls"` } calls := make([]interface{}, len(s.Calls)) for i, call := range s.Calls { diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index a8ec29579da1..75540bd1b417 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -37,7 +37,7 @@ import ( "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethclient" - "github.com/ethereum/go-ethereum/ethclient/gethclient" + clienttypes "github.com/ethereum/go-ethereum/ethclient/types" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" @@ -851,7 +851,7 @@ func TestSimulateV1WithBlockOverrides(t *testing.T) { opts := ethclient.SimulateOptions{ BlockStateCalls: []ethclient.SimulateBlock{ { - BlockOverrides: &gethclient.BlockOverrides{ + BlockOverrides: &clienttypes.BlockOverrides{ Time: timestamp, }, Calls: []ethereum.CallMsg{ @@ -912,7 +912,7 @@ func TestSimulateV1WithStateOverrides(t *testing.T) { balance := new(big.Int) balance.SetString(balanceStr, 10) - stateOverrides := map[common.Address]gethclient.OverrideAccount{ + stateOverrides := map[common.Address]clienttypes.OverrideAccount{ from: { Balance: balance, }, diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go index 54997cbf51a4..80e6689bf224 100644 --- a/ethclient/gethclient/gethclient.go +++ b/ethclient/gethclient/gethclient.go @@ -19,7 +19,6 @@ package gethclient import ( "context" - "encoding/json" "fmt" "math/big" "runtime" @@ -30,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/tracers" + clienttypes "github.com/ethereum/go-ethereum/ethclient/types" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rpc" ) @@ -280,97 +280,10 @@ func toCallArg(msg ethereum.CallMsg) interface{} { return arg } -// OverrideAccount specifies the state of an account to be overridden. -type OverrideAccount struct { - // Nonce sets nonce of the account. Note: the nonce override will only - // be applied when it is set to a non-zero value. - Nonce uint64 +// OverrideAccount is an alias for ethclient/types.OverrideAccount. +// Deprecated: Use ethclienttypes.OverrideAccount instead. +type OverrideAccount = clienttypes.OverrideAccount - // Code sets the contract code. The override will be applied - // when the code is non-nil, i.e. setting empty code is possible - // using an empty slice. - Code []byte - - // Balance sets the account balance. - Balance *big.Int - - // State sets the complete storage. The override will be applied - // when the given map is non-nil. Using an empty map wipes the - // entire contract storage during the call. - State map[common.Hash]common.Hash - - // StateDiff allows overriding individual storage slots. - StateDiff map[common.Hash]common.Hash -} - -func (a OverrideAccount) MarshalJSON() ([]byte, error) { - type acc struct { - Nonce hexutil.Uint64 `json:"nonce,omitempty"` - Code string `json:"code,omitempty"` - Balance *hexutil.Big `json:"balance,omitempty"` - State interface{} `json:"state,omitempty"` - StateDiff map[common.Hash]common.Hash `json:"stateDiff,omitempty"` - } - - output := acc{ - Nonce: hexutil.Uint64(a.Nonce), - Balance: (*hexutil.Big)(a.Balance), - StateDiff: a.StateDiff, - } - if a.Code != nil { - output.Code = hexutil.Encode(a.Code) - } - if a.State != nil { - output.State = a.State - } - 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:"feeRecipient,omitempty"` - Random *common.Hash `json:"prevRandao,omitempty"` - BaseFee *hexutil.Big `json:"baseFeePerGas,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) -} +// BlockOverrides is an alias for ethclient/types.BlockOverrides. +// Deprecated: Use ethclienttypes.BlockOverrides instead. +type BlockOverrides = clienttypes.BlockOverrides diff --git a/ethclient/types/overrides.go b/ethclient/types/overrides.go new file mode 100644 index 000000000000..d672cfc8b5f1 --- /dev/null +++ b/ethclient/types/overrides.go @@ -0,0 +1,121 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package types contains common types. +package types + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// OverrideAccount specifies the state of an account to be overridden. +type OverrideAccount struct { + // Nonce sets nonce of the account. Note: the nonce override will only + // be applied when it is set to a non-zero value. + Nonce uint64 + + // Code sets the contract code. The override will be applied + // when the code is non-nil, i.e. setting empty code is possible + // using an empty slice. + Code []byte + + // Balance sets the account balance. + Balance *big.Int + + // State sets the complete storage. The override will be applied + // when the given map is non-nil. Using an empty map wipes the + // entire contract storage during the call. + State map[common.Hash]common.Hash + + // StateDiff allows overriding individual storage slots. + StateDiff map[common.Hash]common.Hash +} + +func (a OverrideAccount) MarshalJSON() ([]byte, error) { + type acc struct { + Nonce hexutil.Uint64 `json:"nonce,omitempty"` + Code string `json:"code,omitempty"` + Balance *hexutil.Big `json:"balance,omitempty"` + State interface{} `json:"state,omitempty"` + StateDiff map[common.Hash]common.Hash `json:"stateDiff,omitempty"` + } + + output := acc{ + Nonce: hexutil.Uint64(a.Nonce), + Balance: (*hexutil.Big)(a.Balance), + StateDiff: a.StateDiff, + } + if a.Code != nil { + output.Code = hexutil.Encode(a.Code) + } + if a.State != nil { + output.State = a.State + } + 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:"feeRecipient,omitempty"` + Random *common.Hash `json:"prevRandao,omitempty"` + BaseFee *hexutil.Big `json:"baseFeePerGas,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) +} From c8b67825a2439fc1148eff2648cfaaa8cd4d5b63 Mon Sep 17 00:00:00 2001 From: allen Date: Thu, 9 Oct 2025 13:45:49 -0400 Subject: [PATCH 4/9] fix --- ethclient/gethclient/gethclient.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go index 80e6689bf224..93295ceaec4f 100644 --- a/ethclient/gethclient/gethclient.go +++ b/ethclient/gethclient/gethclient.go @@ -281,9 +281,9 @@ func toCallArg(msg ethereum.CallMsg) interface{} { } // OverrideAccount is an alias for ethclient/types.OverrideAccount. -// Deprecated: Use ethclienttypes.OverrideAccount instead. +// Deprecated: Use clienttypes.OverrideAccount instead. type OverrideAccount = clienttypes.OverrideAccount // BlockOverrides is an alias for ethclient/types.BlockOverrides. -// Deprecated: Use ethclienttypes.BlockOverrides instead. +// Deprecated: Use clienttypes.BlockOverrides instead. type BlockOverrides = clienttypes.BlockOverrides From ab4049d119323a9cca8cee14fa45bf7f300c60a1 Mon Sep 17 00:00:00 2001 From: allen Date: Fri, 10 Oct 2025 11:01:11 -0400 Subject: [PATCH 5/9] fix --- ethclient/ethclient.go | 13 ++-- ethclient/ethclient_test.go | 5 +- ethclient/gethclient/gethclient.go | 11 +-- ethclient/types/overrides.go | 121 ----------------------------- interfaces.go | 97 +++++++++++++++++++++++ 5 files changed, 109 insertions(+), 138 deletions(-) delete mode 100644 ethclient/types/overrides.go diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 01c5c49e2514..f89f39a3bfde 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" - clienttypes "github.com/ethereum/go-ethereum/ethclient/types" "github.com/ethereum/go-ethereum/rpc" ) @@ -831,17 +830,17 @@ type SimulateOptions struct { // SimulateBlock represents a batch of calls to be simulated. type SimulateBlock struct { - BlockOverrides *clienttypes.BlockOverrides `json:"blockOverrides,omitempty"` - StateOverrides *map[common.Address]clienttypes.OverrideAccount `json:"stateOverrides,omitempty"` - Calls []ethereum.CallMsg `json:"calls"` + BlockOverrides *ethereum.BlockOverrides `json:"blockOverrides,omitempty"` + StateOverrides *map[common.Address]ethereum.OverrideAccount `json:"stateOverrides,omitempty"` + Calls []ethereum.CallMsg `json:"calls"` } // MarshalJSON implements json.Marshaler for SimulateBlock. func (s SimulateBlock) MarshalJSON() ([]byte, error) { type Alias struct { - BlockOverrides *clienttypes.BlockOverrides `json:"blockOverrides,omitempty"` - StateOverrides *map[common.Address]clienttypes.OverrideAccount `json:"stateOverrides,omitempty"` - Calls []interface{} `json:"calls"` + BlockOverrides *ethereum.BlockOverrides `json:"blockOverrides,omitempty"` + StateOverrides *map[common.Address]ethereum.OverrideAccount `json:"stateOverrides,omitempty"` + Calls []interface{} `json:"calls"` } calls := make([]interface{}, len(s.Calls)) for i, call := range s.Calls { diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 75540bd1b417..cf5ee74c53f1 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -37,7 +37,6 @@ import ( "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethclient" - clienttypes "github.com/ethereum/go-ethereum/ethclient/types" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" @@ -851,7 +850,7 @@ func TestSimulateV1WithBlockOverrides(t *testing.T) { opts := ethclient.SimulateOptions{ BlockStateCalls: []ethclient.SimulateBlock{ { - BlockOverrides: &clienttypes.BlockOverrides{ + BlockOverrides: ðereum.BlockOverrides{ Time: timestamp, }, Calls: []ethereum.CallMsg{ @@ -912,7 +911,7 @@ func TestSimulateV1WithStateOverrides(t *testing.T) { balance := new(big.Int) balance.SetString(balanceStr, 10) - stateOverrides := map[common.Address]clienttypes.OverrideAccount{ + stateOverrides := map[common.Address]ethereum.OverrideAccount{ from: { Balance: balance, }, diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go index 93295ceaec4f..6a0f5eb31233 100644 --- a/ethclient/gethclient/gethclient.go +++ b/ethclient/gethclient/gethclient.go @@ -29,7 +29,6 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/tracers" - clienttypes "github.com/ethereum/go-ethereum/ethclient/types" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rpc" ) @@ -280,10 +279,8 @@ func toCallArg(msg ethereum.CallMsg) interface{} { return arg } -// OverrideAccount is an alias for ethclient/types.OverrideAccount. -// Deprecated: Use clienttypes.OverrideAccount instead. -type OverrideAccount = clienttypes.OverrideAccount +// OverrideAccount is an alias for ethereum.OverrideAccount. +type OverrideAccount = ethereum.OverrideAccount -// BlockOverrides is an alias for ethclient/types.BlockOverrides. -// Deprecated: Use clienttypes.BlockOverrides instead. -type BlockOverrides = clienttypes.BlockOverrides +// BlockOverrides is an alias for ethereum.BlockOverrides. +type BlockOverrides = ethereum.BlockOverrides diff --git a/ethclient/types/overrides.go b/ethclient/types/overrides.go deleted file mode 100644 index d672cfc8b5f1..000000000000 --- a/ethclient/types/overrides.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2021 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package types contains common types. -package types - -import ( - "encoding/json" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" -) - -// OverrideAccount specifies the state of an account to be overridden. -type OverrideAccount struct { - // Nonce sets nonce of the account. Note: the nonce override will only - // be applied when it is set to a non-zero value. - Nonce uint64 - - // Code sets the contract code. The override will be applied - // when the code is non-nil, i.e. setting empty code is possible - // using an empty slice. - Code []byte - - // Balance sets the account balance. - Balance *big.Int - - // State sets the complete storage. The override will be applied - // when the given map is non-nil. Using an empty map wipes the - // entire contract storage during the call. - State map[common.Hash]common.Hash - - // StateDiff allows overriding individual storage slots. - StateDiff map[common.Hash]common.Hash -} - -func (a OverrideAccount) MarshalJSON() ([]byte, error) { - type acc struct { - Nonce hexutil.Uint64 `json:"nonce,omitempty"` - Code string `json:"code,omitempty"` - Balance *hexutil.Big `json:"balance,omitempty"` - State interface{} `json:"state,omitempty"` - StateDiff map[common.Hash]common.Hash `json:"stateDiff,omitempty"` - } - - output := acc{ - Nonce: hexutil.Uint64(a.Nonce), - Balance: (*hexutil.Big)(a.Balance), - StateDiff: a.StateDiff, - } - if a.Code != nil { - output.Code = hexutil.Encode(a.Code) - } - if a.State != nil { - output.State = a.State - } - 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:"feeRecipient,omitempty"` - Random *common.Hash `json:"prevRandao,omitempty"` - BaseFee *hexutil.Big `json:"baseFeePerGas,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) -} diff --git a/interfaces.go b/interfaces.go index be5b97085155..ce76738235bc 100644 --- a/interfaces.go +++ b/interfaces.go @@ -19,10 +19,12 @@ package ethereum import ( "context" + "encoding/json" "errors" "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" ) @@ -281,3 +283,98 @@ type BlockNumberReader interface { type ChainIDReader interface { ChainID(ctx context.Context) (*big.Int, error) } + +// OverrideAccount specifies the state of an account to be overridden. +type OverrideAccount struct { + // Nonce sets nonce of the account. Note: the nonce override will only + // be applied when it is set to a non-zero value. + Nonce uint64 + + // Code sets the contract code. The override will be applied + // when the code is non-nil, i.e. setting empty code is possible + // using an empty slice. + Code []byte + + // Balance sets the account balance. + Balance *big.Int + + // State sets the complete storage. The override will be applied + // when the given map is non-nil. Using an empty map wipes the + // entire contract storage during the call. + State map[common.Hash]common.Hash + + // StateDiff allows overriding individual storage slots. + StateDiff map[common.Hash]common.Hash +} + +func (a OverrideAccount) MarshalJSON() ([]byte, error) { + type acc struct { + Nonce hexutil.Uint64 `json:"nonce,omitempty"` + Code string `json:"code,omitempty"` + Balance *hexutil.Big `json:"balance,omitempty"` + State interface{} `json:"state,omitempty"` + StateDiff map[common.Hash]common.Hash `json:"stateDiff,omitempty"` + } + + output := acc{ + Nonce: hexutil.Uint64(a.Nonce), + Balance: (*hexutil.Big)(a.Balance), + StateDiff: a.StateDiff, + } + if a.Code != nil { + output.Code = hexutil.Encode(a.Code) + } + if a.State != nil { + output.State = a.State + } + 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:"feeRecipient,omitempty"` + Random *common.Hash `json:"prevRandao,omitempty"` + BaseFee *hexutil.Big `json:"baseFeePerGas,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) +} From e16270055aea2e66a6c9c747d48093c6bd4d6479 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 15 Oct 2025 18:25:48 +0200 Subject: [PATCH 6/9] use base types for simulate result struct --- ethclient/ethclient.go | 38 ++++++++---- ethclient/gen_simulate_block_result.go | 80 ++++++++++++++++++++++++++ ethclient/gen_simulate_call_result.go | 61 ++++++++++++++++++++ 3 files changed, 169 insertions(+), 10 deletions(-) create mode 100644 ethclient/gen_simulate_block_result.go create mode 100644 ethclient/gen_simulate_call_result.go diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index f89f39a3bfde..b73cc2d7d21f 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -853,13 +853,21 @@ func (s SimulateBlock) MarshalJSON() ([]byte, error) { }) } +//go:generate go run github.com/fjl/gencodec -type SimulateCallResult -field-override simulateCallResultMarshaling -out gen_simulate_call_result.go + // SimulateCallResult is the result of a simulated call. type SimulateCallResult struct { - ReturnValue hexutil.Bytes `json:"returnData"` - Logs []*types.Log `json:"logs"` - GasUsed hexutil.Uint64 `json:"gasUsed"` - Status hexutil.Uint64 `json:"status"` - Error *CallError `json:"error,omitempty"` + ReturnValue []byte `json:"returnData"` + Logs []*types.Log `json:"logs"` + GasUsed uint64 `json:"gasUsed"` + Status uint64 `json:"status"` + Error *CallError `json:"error,omitempty"` +} + +type simulateCallResultMarshaling struct { + ReturnValue hexutil.Bytes + GasUsed hexutil.Uint64 + Status hexutil.Uint64 } // CallError represents an error from a simulated call. @@ -869,18 +877,28 @@ type CallError struct { Data string `json:"data,omitempty"` } +//go:generate go run github.com/fjl/gencodec -type SimulateBlockResult -field-override simulateBlockResultMarshaling -out gen_simulate_block_result.go + // SimulateBlockResult represents the result of a simulated block. type SimulateBlockResult struct { - Number hexutil.Uint64 `json:"number"` + Number uint64 `json:"number"` Hash common.Hash `json:"hash"` - Timestamp hexutil.Uint64 `json:"timestamp"` - GasLimit hexutil.Uint64 `json:"gasLimit"` - GasUsed hexutil.Uint64 `json:"gasUsed"` + Timestamp uint64 `json:"timestamp"` + GasLimit uint64 `json:"gasLimit"` + GasUsed uint64 `json:"gasUsed"` FeeRecipient common.Address `json:"miner"` - BaseFeePerGas *hexutil.Big `json:"baseFeePerGas,omitempty"` + BaseFeePerGas *big.Int `json:"baseFeePerGas,omitempty"` Calls []SimulateCallResult `json:"calls"` } +type simulateBlockResultMarshaling struct { + Number hexutil.Uint64 + Timestamp hexutil.Uint64 + GasLimit hexutil.Uint64 + GasUsed hexutil.Uint64 + BaseFeePerGas *hexutil.Big +} + // SimulateV1 executes transactions on top of a base state. func (ec *Client) SimulateV1(ctx context.Context, opts SimulateOptions, blockNrOrHash *rpc.BlockNumberOrHash) ([]SimulateBlockResult, error) { var result []SimulateBlockResult diff --git a/ethclient/gen_simulate_block_result.go b/ethclient/gen_simulate_block_result.go new file mode 100644 index 000000000000..d3f712d102f7 --- /dev/null +++ b/ethclient/gen_simulate_block_result.go @@ -0,0 +1,80 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package ethclient + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*simulateBlockResultMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (s SimulateBlockResult) MarshalJSON() ([]byte, error) { + type SimulateBlockResult struct { + Number hexutil.Uint64 `json:"number"` + Hash common.Hash `json:"hash"` + Timestamp hexutil.Uint64 `json:"timestamp"` + GasLimit hexutil.Uint64 `json:"gasLimit"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + FeeRecipient common.Address `json:"miner"` + BaseFeePerGas *hexutil.Big `json:"baseFeePerGas,omitempty"` + Calls []SimulateCallResult `json:"calls"` + } + var enc SimulateBlockResult + enc.Number = hexutil.Uint64(s.Number) + enc.Hash = s.Hash + enc.Timestamp = hexutil.Uint64(s.Timestamp) + enc.GasLimit = hexutil.Uint64(s.GasLimit) + enc.GasUsed = hexutil.Uint64(s.GasUsed) + enc.FeeRecipient = s.FeeRecipient + enc.BaseFeePerGas = (*hexutil.Big)(s.BaseFeePerGas) + enc.Calls = s.Calls + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (s *SimulateBlockResult) UnmarshalJSON(input []byte) error { + type SimulateBlockResult struct { + Number *hexutil.Uint64 `json:"number"` + Hash *common.Hash `json:"hash"` + Timestamp *hexutil.Uint64 `json:"timestamp"` + GasLimit *hexutil.Uint64 `json:"gasLimit"` + GasUsed *hexutil.Uint64 `json:"gasUsed"` + FeeRecipient *common.Address `json:"miner"` + BaseFeePerGas *hexutil.Big `json:"baseFeePerGas,omitempty"` + Calls []SimulateCallResult `json:"calls"` + } + var dec SimulateBlockResult + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Number != nil { + s.Number = uint64(*dec.Number) + } + if dec.Hash != nil { + s.Hash = *dec.Hash + } + if dec.Timestamp != nil { + s.Timestamp = uint64(*dec.Timestamp) + } + if dec.GasLimit != nil { + s.GasLimit = uint64(*dec.GasLimit) + } + if dec.GasUsed != nil { + s.GasUsed = uint64(*dec.GasUsed) + } + if dec.FeeRecipient != nil { + s.FeeRecipient = *dec.FeeRecipient + } + if dec.BaseFeePerGas != nil { + s.BaseFeePerGas = (*big.Int)(dec.BaseFeePerGas) + } + if dec.Calls != nil { + s.Calls = dec.Calls + } + return nil +} diff --git a/ethclient/gen_simulate_call_result.go b/ethclient/gen_simulate_call_result.go new file mode 100644 index 000000000000..55e14cd697f6 --- /dev/null +++ b/ethclient/gen_simulate_call_result.go @@ -0,0 +1,61 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package ethclient + +import ( + "encoding/json" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" +) + +var _ = (*simulateCallResultMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (s SimulateCallResult) MarshalJSON() ([]byte, error) { + type SimulateCallResult struct { + ReturnValue hexutil.Bytes `json:"returnData"` + Logs []*types.Log `json:"logs"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + Status hexutil.Uint64 `json:"status"` + Error *CallError `json:"error,omitempty"` + } + var enc SimulateCallResult + enc.ReturnValue = s.ReturnValue + enc.Logs = s.Logs + enc.GasUsed = hexutil.Uint64(s.GasUsed) + enc.Status = hexutil.Uint64(s.Status) + enc.Error = s.Error + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (s *SimulateCallResult) UnmarshalJSON(input []byte) error { + type SimulateCallResult struct { + ReturnValue *hexutil.Bytes `json:"returnData"` + Logs []*types.Log `json:"logs"` + GasUsed *hexutil.Uint64 `json:"gasUsed"` + Status *hexutil.Uint64 `json:"status"` + Error *CallError `json:"error,omitempty"` + } + var dec SimulateCallResult + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ReturnValue != nil { + s.ReturnValue = *dec.ReturnValue + } + if dec.Logs != nil { + s.Logs = dec.Logs + } + if dec.GasUsed != nil { + s.GasUsed = uint64(*dec.GasUsed) + } + if dec.Status != nil { + s.Status = uint64(*dec.Status) + } + if dec.Error != nil { + s.Error = dec.Error + } + return nil +} From bbdbcd84ce574812678af058e08b0e5a38e16773 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 15 Oct 2025 18:50:20 +0200 Subject: [PATCH 7/9] fix lint --- ethclient/ethclient_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index cf5ee74c53f1..eca9e452e9f8 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -877,7 +877,7 @@ func TestSimulateV1WithBlockOverrides(t *testing.T) { } // Verify the timestamp was overridden - if uint64(results[0].Timestamp) != timestamp { + if results[0].Timestamp != timestamp { t.Errorf("expected timestamp %d, got %d", timestamp, results[0].Timestamp) } } From baa9835ee8e710627268e26794c54ee4e0fb5b29 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 15 Oct 2025 19:15:45 +0200 Subject: [PATCH 8/9] state override no pointer --- ethclient/ethclient.go | 12 ++++++------ ethclient/ethclient_test.go | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index b73cc2d7d21f..06203d7a3fdb 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -830,17 +830,17 @@ type SimulateOptions struct { // SimulateBlock represents a batch of calls to be simulated. type SimulateBlock struct { - BlockOverrides *ethereum.BlockOverrides `json:"blockOverrides,omitempty"` - StateOverrides *map[common.Address]ethereum.OverrideAccount `json:"stateOverrides,omitempty"` - Calls []ethereum.CallMsg `json:"calls"` + BlockOverrides *ethereum.BlockOverrides `json:"blockOverrides,omitempty"` + StateOverrides map[common.Address]ethereum.OverrideAccount `json:"stateOverrides,omitempty"` + Calls []ethereum.CallMsg `json:"calls"` } // MarshalJSON implements json.Marshaler for SimulateBlock. func (s SimulateBlock) MarshalJSON() ([]byte, error) { type Alias struct { - BlockOverrides *ethereum.BlockOverrides `json:"blockOverrides,omitempty"` - StateOverrides *map[common.Address]ethereum.OverrideAccount `json:"stateOverrides,omitempty"` - Calls []interface{} `json:"calls"` + BlockOverrides *ethereum.BlockOverrides `json:"blockOverrides,omitempty"` + StateOverrides map[common.Address]ethereum.OverrideAccount `json:"stateOverrides,omitempty"` + Calls []interface{} `json:"calls"` } calls := make([]interface{}, len(s.Calls)) for i, call := range s.Calls { diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index eca9e452e9f8..302ccf2e16c2 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -920,7 +920,7 @@ func TestSimulateV1WithStateOverrides(t *testing.T) { opts := ethclient.SimulateOptions{ BlockStateCalls: []ethclient.SimulateBlock{ { - StateOverrides: &stateOverrides, + StateOverrides: stateOverrides, Calls: []ethereum.CallMsg{ { From: from, From 14d351fe19d509a293a5192741c2a2720b2b39f3 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 16 Oct 2025 15:22:58 +0200 Subject: [PATCH 9/9] use big int for block number --- ethclient/ethclient.go | 4 ++-- ethclient/gen_simulate_block_result.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 06203d7a3fdb..bebb7b4aedd8 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -881,7 +881,7 @@ type CallError struct { // SimulateBlockResult represents the result of a simulated block. type SimulateBlockResult struct { - Number uint64 `json:"number"` + Number *big.Int `json:"number"` Hash common.Hash `json:"hash"` Timestamp uint64 `json:"timestamp"` GasLimit uint64 `json:"gasLimit"` @@ -892,7 +892,7 @@ type SimulateBlockResult struct { } type simulateBlockResultMarshaling struct { - Number hexutil.Uint64 + Number *hexutil.Big Timestamp hexutil.Uint64 GasLimit hexutil.Uint64 GasUsed hexutil.Uint64 diff --git a/ethclient/gen_simulate_block_result.go b/ethclient/gen_simulate_block_result.go index d3f712d102f7..b8cd6ebf2fc2 100644 --- a/ethclient/gen_simulate_block_result.go +++ b/ethclient/gen_simulate_block_result.go @@ -15,7 +15,7 @@ var _ = (*simulateBlockResultMarshaling)(nil) // MarshalJSON marshals as JSON. func (s SimulateBlockResult) MarshalJSON() ([]byte, error) { type SimulateBlockResult struct { - Number hexutil.Uint64 `json:"number"` + Number *hexutil.Big `json:"number"` Hash common.Hash `json:"hash"` Timestamp hexutil.Uint64 `json:"timestamp"` GasLimit hexutil.Uint64 `json:"gasLimit"` @@ -25,7 +25,7 @@ func (s SimulateBlockResult) MarshalJSON() ([]byte, error) { Calls []SimulateCallResult `json:"calls"` } var enc SimulateBlockResult - enc.Number = hexutil.Uint64(s.Number) + enc.Number = (*hexutil.Big)(s.Number) enc.Hash = s.Hash enc.Timestamp = hexutil.Uint64(s.Timestamp) enc.GasLimit = hexutil.Uint64(s.GasLimit) @@ -39,7 +39,7 @@ func (s SimulateBlockResult) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (s *SimulateBlockResult) UnmarshalJSON(input []byte) error { type SimulateBlockResult struct { - Number *hexutil.Uint64 `json:"number"` + Number *hexutil.Big `json:"number"` Hash *common.Hash `json:"hash"` Timestamp *hexutil.Uint64 `json:"timestamp"` GasLimit *hexutil.Uint64 `json:"gasLimit"` @@ -53,7 +53,7 @@ func (s *SimulateBlockResult) UnmarshalJSON(input []byte) error { return err } if dec.Number != nil { - s.Number = uint64(*dec.Number) + s.Number = (*big.Int)(dec.Number) } if dec.Hash != nil { s.Hash = *dec.Hash