Skip to content

Commit 72a9c78

Browse files
authored
flashbots: add eth_estimateGasBundle
Fixes ethereum#100
1 parent 6706c4d commit 72a9c78

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed

internal/ethapi/api.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package ethapi
1818

1919
import (
2020
"context"
21+
"crypto/rand"
2122
"encoding/hex"
2223
"errors"
2324
"fmt"
@@ -2308,3 +2309,127 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st
23082309
ret["bundleHash"] = "0x" + common.Bytes2Hex(bundleHash.Sum(nil))
23092310
return ret, nil
23102311
}
2312+
2313+
// EstimateGasBundleArgs represents the arguments for a call
2314+
type EstimateGasBundleArgs struct {
2315+
Txs []TransactionArgs `json:"txs"`
2316+
BlockNumber rpc.BlockNumber `json:"blockNumber"`
2317+
StateBlockNumberOrHash rpc.BlockNumberOrHash `json:"stateBlockNumber"`
2318+
Coinbase *string `json:"coinbase"`
2319+
Timestamp *uint64 `json:"timestamp"`
2320+
Timeout *int64 `json:"timeout"`
2321+
}
2322+
2323+
func (s *BundleAPI) EstimateGasBundle(ctx context.Context, args EstimateGasBundleArgs) (map[string]interface{}, error) {
2324+
if len(args.Txs) == 0 {
2325+
return nil, errors.New("bundle missing txs")
2326+
}
2327+
if args.BlockNumber == 0 {
2328+
return nil, errors.New("bundle missing blockNumber")
2329+
}
2330+
2331+
timeoutMS := int64(5000)
2332+
if args.Timeout != nil {
2333+
timeoutMS = *args.Timeout
2334+
}
2335+
timeout := time.Millisecond * time.Duration(timeoutMS)
2336+
2337+
state, parent, err := s.b.StateAndHeaderByNumberOrHash(ctx, args.StateBlockNumberOrHash)
2338+
if state == nil || err != nil {
2339+
return nil, err
2340+
}
2341+
blockNumber := big.NewInt(int64(args.BlockNumber))
2342+
timestamp := parent.Time + 1
2343+
if args.Timestamp != nil {
2344+
timestamp = *args.Timestamp
2345+
}
2346+
coinbase := parent.Coinbase
2347+
if args.Coinbase != nil {
2348+
coinbase = common.HexToAddress(*args.Coinbase)
2349+
}
2350+
2351+
header := &types.Header{
2352+
ParentHash: parent.Hash(),
2353+
Number: blockNumber,
2354+
GasLimit: parent.GasLimit,
2355+
Time: timestamp,
2356+
Difficulty: parent.Difficulty,
2357+
Coinbase: coinbase,
2358+
BaseFee: parent.BaseFee,
2359+
}
2360+
2361+
// Setup context so it may be cancelled when the call
2362+
// has completed or, in case of unmetered gas, setup
2363+
// a context with a timeout
2364+
var cancel context.CancelFunc
2365+
if timeout > 0 {
2366+
ctx, cancel = context.WithTimeout(ctx, timeout)
2367+
} else {
2368+
ctx, cancel = context.WithCancel(ctx)
2369+
}
2370+
2371+
// Make sure the context is cancelled when the call has completed
2372+
// This makes sure resources are cleaned up
2373+
defer cancel()
2374+
2375+
// RPC Call gas cap
2376+
globalGasCap := s.b.RPCGasCap()
2377+
2378+
// Results
2379+
results := []map[string]interface{}{}
2380+
2381+
// Copy the original db so we don't modify it
2382+
statedb := state.Copy()
2383+
2384+
// Gas pool
2385+
gp := new(core.GasPool).AddGas(math.MaxUint64)
2386+
2387+
// Block context
2388+
blockContext := core.NewEVMBlockContext(header, s.chain, &coinbase)
2389+
2390+
// Feed each of the transactions into the VM ctx
2391+
// And try and estimate the gas used
2392+
for i, txArgs := range args.Txs {
2393+
// Since its a txCall we'll just prepare the
2394+
// state with a random hash
2395+
var randomHash common.Hash
2396+
rand.Read(randomHash[:])
2397+
2398+
// New random hash since its a call
2399+
statedb.Prepare(randomHash, i)
2400+
2401+
// Convert tx args to msg to apply state transition
2402+
msg, err := txArgs.ToMessage(globalGasCap, header.BaseFee)
2403+
if err != nil {
2404+
return nil, err
2405+
}
2406+
2407+
// Prepare the hashes
2408+
txContext := core.NewEVMTxContext(msg)
2409+
2410+
// Get EVM Environment
2411+
vmenv := vm.NewEVM(blockContext, txContext, statedb, s.b.ChainConfig(), vm.Config{NoBaseFee: true})
2412+
2413+
// Apply state transition
2414+
result, err := core.ApplyMessage(vmenv, msg, gp)
2415+
if err != nil {
2416+
return nil, err
2417+
}
2418+
2419+
// Modifications are committed to the state
2420+
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
2421+
statedb.Finalise(vmenv.ChainConfig().IsEIP158(blockNumber))
2422+
2423+
// Append result
2424+
jsonResult := map[string]interface{}{
2425+
"gasUsed": result.UsedGas,
2426+
}
2427+
results = append(results, jsonResult)
2428+
}
2429+
2430+
// Return results
2431+
ret := map[string]interface{}{}
2432+
ret["results"] = results
2433+
2434+
return ret, nil
2435+
}

0 commit comments

Comments
 (0)