mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-02-26 07:37:20 +00:00
ethclient: add support for eth_simulateV1 (#32856)
Adds ethclient support for the eth_simulateV1 RPC method, which allows simulating transactions on top of a base state without making changes to the blockchain. --------- Co-authored-by: Sina Mahmoodi <itz.s1na@gmail.com>
This commit is contained in:
parent
5c535074ac
commit
c37bd67019
6 changed files with 575 additions and 94 deletions
|
|
@ -828,3 +828,89 @@ func (p *rpcProgress) toSyncProgress() *ethereum.SyncProgress {
|
||||||
StateIndexRemaining: uint64(p.StateIndexRemaining),
|
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 *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"`
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//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 []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.
|
||||||
|
type CallError struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
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 *big.Int `json:"number"`
|
||||||
|
Hash common.Hash `json:"hash"`
|
||||||
|
Timestamp uint64 `json:"timestamp"`
|
||||||
|
GasLimit uint64 `json:"gasLimit"`
|
||||||
|
GasUsed uint64 `json:"gasUsed"`
|
||||||
|
FeeRecipient common.Address `json:"miner"`
|
||||||
|
BaseFeePerGas *big.Int `json:"baseFeePerGas,omitempty"`
|
||||||
|
Calls []SimulateCallResult `json:"calls"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type simulateBlockResultMarshaling struct {
|
||||||
|
Number *hexutil.Big
|
||||||
|
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
|
||||||
|
err := ec.c.CallContext(ctx, &result, "eth_simulateV1", opts, blockNrOrHash)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -754,3 +754,250 @@ func ExampleRevertErrorData() {
|
||||||
// revert: 08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a75736572206572726f72
|
// revert: 08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a75736572206572726f72
|
||||||
// message: user error
|
// 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 := big.NewInt(100)
|
||||||
|
gas := uint64(100000)
|
||||||
|
maxFeePerGas := new(big.Int).Mul(header.BaseFee, big.NewInt(2))
|
||||||
|
|
||||||
|
opts := ethclient.SimulateOptions{
|
||||||
|
BlockStateCalls: []ethclient.SimulateBlock{
|
||||||
|
{
|
||||||
|
Calls: []ethereum.CallMsg{
|
||||||
|
{
|
||||||
|
From: from,
|
||||||
|
To: &to,
|
||||||
|
Value: value,
|
||||||
|
Gas: gas,
|
||||||
|
GasFeeCap: 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 := big.NewInt(100)
|
||||||
|
gas := uint64(100000)
|
||||||
|
maxFeePerGas := new(big.Int).Mul(header.BaseFee, big.NewInt(2))
|
||||||
|
|
||||||
|
// Override timestamp only
|
||||||
|
timestamp := uint64(1234567890)
|
||||||
|
|
||||||
|
opts := ethclient.SimulateOptions{
|
||||||
|
BlockStateCalls: []ethclient.SimulateBlock{
|
||||||
|
{
|
||||||
|
BlockOverrides: ðereum.BlockOverrides{
|
||||||
|
Time: timestamp,
|
||||||
|
},
|
||||||
|
Calls: []ethereum.CallMsg{
|
||||||
|
{
|
||||||
|
From: from,
|
||||||
|
To: &to,
|
||||||
|
Value: value,
|
||||||
|
Gas: gas,
|
||||||
|
GasFeeCap: 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 := 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]ethereum.OverrideAccount{
|
||||||
|
from: {
|
||||||
|
Balance: balance,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := ethclient.SimulateOptions{
|
||||||
|
BlockStateCalls: []ethclient.SimulateBlock{
|
||||||
|
{
|
||||||
|
StateOverrides: stateOverrides,
|
||||||
|
Calls: []ethereum.CallMsg{
|
||||||
|
{
|
||||||
|
From: from,
|
||||||
|
To: &to,
|
||||||
|
Value: value,
|
||||||
|
Gas: gas,
|
||||||
|
GasFeeCap: 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 := big.NewInt(100)
|
||||||
|
gas := uint64(100000)
|
||||||
|
maxFeePerGas := new(big.Int).Mul(header.BaseFee, big.NewInt(2))
|
||||||
|
|
||||||
|
opts := ethclient.SimulateOptions{
|
||||||
|
BlockStateCalls: []ethclient.SimulateBlock{
|
||||||
|
{
|
||||||
|
Calls: []ethereum.CallMsg{
|
||||||
|
{
|
||||||
|
From: from,
|
||||||
|
To: &to,
|
||||||
|
Value: value,
|
||||||
|
Gas: gas,
|
||||||
|
GasFeeCap: 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
80
ethclient/gen_simulate_block_result.go
Normal file
80
ethclient/gen_simulate_block_result.go
Normal file
|
|
@ -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.Big `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.Big)(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.Big `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 = (*big.Int)(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
|
||||||
|
}
|
||||||
61
ethclient/gen_simulate_call_result.go
Normal file
61
ethclient/gen_simulate_call_result.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -19,7 +19,6 @@ package gethclient
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
@ -280,97 +279,8 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
|
||||||
return arg
|
return arg
|
||||||
}
|
}
|
||||||
|
|
||||||
// OverrideAccount specifies the state of an account to be overridden.
|
// OverrideAccount is an alias for ethereum.OverrideAccount.
|
||||||
type OverrideAccount struct {
|
type OverrideAccount = ethereum.OverrideAccount
|
||||||
// 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
|
// BlockOverrides is an alias for ethereum.BlockOverrides.
|
||||||
// when the code is non-nil, i.e. setting empty code is possible
|
type BlockOverrides = ethereum.BlockOverrides
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,12 @@ package ethereum
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -293,3 +295,98 @@ type BlockNumberReader interface {
|
||||||
type ChainIDReader interface {
|
type ChainIDReader interface {
|
||||||
ChainID(ctx context.Context) (*big.Int, error)
|
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)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue