Skip to content

Commit

Permalink
flashbots: add eth_estimateGasBundle
Browse files Browse the repository at this point in the history
Fixes #100
  • Loading branch information
libevm authored Dec 20, 2021
1 parent 6706c4d commit 72a9c78
Showing 1 changed file with 125 additions and 0 deletions.
125 changes: 125 additions & 0 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package ethapi

import (
"context"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
Expand Down Expand Up @@ -2308,3 +2309,127 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st
ret["bundleHash"] = "0x" + common.Bytes2Hex(bundleHash.Sum(nil))
return ret, nil
}

// EstimateGasBundleArgs represents the arguments for a call
type EstimateGasBundleArgs struct {
Txs []TransactionArgs `json:"txs"`
BlockNumber rpc.BlockNumber `json:"blockNumber"`
StateBlockNumberOrHash rpc.BlockNumberOrHash `json:"stateBlockNumber"`
Coinbase *string `json:"coinbase"`
Timestamp *uint64 `json:"timestamp"`
Timeout *int64 `json:"timeout"`
}

func (s *BundleAPI) EstimateGasBundle(ctx context.Context, args EstimateGasBundleArgs) (map[string]interface{}, error) {
if len(args.Txs) == 0 {
return nil, errors.New("bundle missing txs")
}
if args.BlockNumber == 0 {
return nil, errors.New("bundle missing blockNumber")
}

timeoutMS := int64(5000)
if args.Timeout != nil {
timeoutMS = *args.Timeout
}
timeout := time.Millisecond * time.Duration(timeoutMS)

state, parent, err := s.b.StateAndHeaderByNumberOrHash(ctx, args.StateBlockNumberOrHash)
if state == nil || err != nil {
return nil, err
}
blockNumber := big.NewInt(int64(args.BlockNumber))
timestamp := parent.Time + 1
if args.Timestamp != nil {
timestamp = *args.Timestamp
}
coinbase := parent.Coinbase
if args.Coinbase != nil {
coinbase = common.HexToAddress(*args.Coinbase)
}

header := &types.Header{
ParentHash: parent.Hash(),
Number: blockNumber,
GasLimit: parent.GasLimit,
Time: timestamp,
Difficulty: parent.Difficulty,
Coinbase: coinbase,
BaseFee: parent.BaseFee,
}

// Setup context so it may be cancelled when the call
// has completed or, in case of unmetered gas, setup
// a context with a timeout
var cancel context.CancelFunc
if timeout > 0 {
ctx, cancel = context.WithTimeout(ctx, timeout)
} else {
ctx, cancel = context.WithCancel(ctx)
}

// Make sure the context is cancelled when the call has completed
// This makes sure resources are cleaned up
defer cancel()

// RPC Call gas cap
globalGasCap := s.b.RPCGasCap()

// Results
results := []map[string]interface{}{}

// Copy the original db so we don't modify it
statedb := state.Copy()

// Gas pool
gp := new(core.GasPool).AddGas(math.MaxUint64)

// Block context
blockContext := core.NewEVMBlockContext(header, s.chain, &coinbase)

// Feed each of the transactions into the VM ctx
// And try and estimate the gas used
for i, txArgs := range args.Txs {
// Since its a txCall we'll just prepare the
// state with a random hash
var randomHash common.Hash
rand.Read(randomHash[:])

// New random hash since its a call
statedb.Prepare(randomHash, i)

// Convert tx args to msg to apply state transition
msg, err := txArgs.ToMessage(globalGasCap, header.BaseFee)
if err != nil {
return nil, err
}

// Prepare the hashes
txContext := core.NewEVMTxContext(msg)

// Get EVM Environment
vmenv := vm.NewEVM(blockContext, txContext, statedb, s.b.ChainConfig(), vm.Config{NoBaseFee: true})

// Apply state transition
result, err := core.ApplyMessage(vmenv, msg, gp)
if err != nil {
return nil, err
}

// Modifications are committed to the state
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
statedb.Finalise(vmenv.ChainConfig().IsEIP158(blockNumber))

// Append result
jsonResult := map[string]interface{}{
"gasUsed": result.UsedGas,
}
results = append(results, jsonResult)
}

// Return results
ret := map[string]interface{}{}
ret["results"] = results

return ret, nil
}

0 comments on commit 72a9c78

Please sign in to comment.