Skip to content

Commit ff77780

Browse files
libevmjparyani
authored andcommitted
flashbots: add eth_estimateGasBundle
Fixes ethereum#100
1 parent d6c9db6 commit ff77780

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"
@@ -2382,3 +2383,127 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st
23822383
ret["bundleHash"] = "0x" + common.Bytes2Hex(bundleHash.Sum(nil))
23832384
return ret, nil
23842385
}
2386+
2387+
// EstimateGasBundleArgs represents the arguments for a call
2388+
type EstimateGasBundleArgs struct {
2389+
Txs []TransactionArgs `json:"txs"`
2390+
BlockNumber rpc.BlockNumber `json:"blockNumber"`
2391+
StateBlockNumberOrHash rpc.BlockNumberOrHash `json:"stateBlockNumber"`
2392+
Coinbase *string `json:"coinbase"`
2393+
Timestamp *uint64 `json:"timestamp"`
2394+
Timeout *int64 `json:"timeout"`
2395+
}
2396+
2397+
func (s *BundleAPI) EstimateGasBundle(ctx context.Context, args EstimateGasBundleArgs) (map[string]interface{}, error) {
2398+
if len(args.Txs) == 0 {
2399+
return nil, errors.New("bundle missing txs")
2400+
}
2401+
if args.BlockNumber == 0 {
2402+
return nil, errors.New("bundle missing blockNumber")
2403+
}
2404+
2405+
timeoutMS := int64(5000)
2406+
if args.Timeout != nil {
2407+
timeoutMS = *args.Timeout
2408+
}
2409+
timeout := time.Millisecond * time.Duration(timeoutMS)
2410+
2411+
state, parent, err := s.b.StateAndHeaderByNumberOrHash(ctx, args.StateBlockNumberOrHash)
2412+
if state == nil || err != nil {
2413+
return nil, err
2414+
}
2415+
blockNumber := big.NewInt(int64(args.BlockNumber))
2416+
timestamp := parent.Time + 1
2417+
if args.Timestamp != nil {
2418+
timestamp = *args.Timestamp
2419+
}
2420+
coinbase := parent.Coinbase
2421+
if args.Coinbase != nil {
2422+
coinbase = common.HexToAddress(*args.Coinbase)
2423+
}
2424+
2425+
header := &types.Header{
2426+
ParentHash: parent.Hash(),
2427+
Number: blockNumber,
2428+
GasLimit: parent.GasLimit,
2429+
Time: timestamp,
2430+
Difficulty: parent.Difficulty,
2431+
Coinbase: coinbase,
2432+
BaseFee: parent.BaseFee,
2433+
}
2434+
2435+
// Setup context so it may be cancelled when the call
2436+
// has completed or, in case of unmetered gas, setup
2437+
// a context with a timeout
2438+
var cancel context.CancelFunc
2439+
if timeout > 0 {
2440+
ctx, cancel = context.WithTimeout(ctx, timeout)
2441+
} else {
2442+
ctx, cancel = context.WithCancel(ctx)
2443+
}
2444+
2445+
// Make sure the context is cancelled when the call has completed
2446+
// This makes sure resources are cleaned up
2447+
defer cancel()
2448+
2449+
// RPC Call gas cap
2450+
globalGasCap := s.b.RPCGasCap()
2451+
2452+
// Results
2453+
results := []map[string]interface{}{}
2454+
2455+
// Copy the original db so we don't modify it
2456+
statedb := state.Copy()
2457+
2458+
// Gas pool
2459+
gp := new(core.GasPool).AddGas(math.MaxUint64)
2460+
2461+
// Block context
2462+
blockContext := core.NewEVMBlockContext(header, s.chain, &coinbase)
2463+
2464+
// Feed each of the transactions into the VM ctx
2465+
// And try and estimate the gas used
2466+
for i, txArgs := range args.Txs {
2467+
// Since its a txCall we'll just prepare the
2468+
// state with a random hash
2469+
var randomHash common.Hash
2470+
rand.Read(randomHash[:])
2471+
2472+
// New random hash since its a call
2473+
statedb.Prepare(randomHash, i)
2474+
2475+
// Convert tx args to msg to apply state transition
2476+
msg, err := txArgs.ToMessage(globalGasCap, header.BaseFee)
2477+
if err != nil {
2478+
return nil, err
2479+
}
2480+
2481+
// Prepare the hashes
2482+
txContext := core.NewEVMTxContext(msg)
2483+
2484+
// Get EVM Environment
2485+
vmenv := vm.NewEVM(blockContext, txContext, statedb, s.b.ChainConfig(), vm.Config{NoBaseFee: true})
2486+
2487+
// Apply state transition
2488+
result, err := core.ApplyMessage(vmenv, msg, gp)
2489+
if err != nil {
2490+
return nil, err
2491+
}
2492+
2493+
// Modifications are committed to the state
2494+
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
2495+
statedb.Finalise(vmenv.ChainConfig().IsEIP158(blockNumber))
2496+
2497+
// Append result
2498+
jsonResult := map[string]interface{}{
2499+
"gasUsed": result.UsedGas,
2500+
}
2501+
results = append(results, jsonResult)
2502+
}
2503+
2504+
// Return results
2505+
ret := map[string]interface{}{}
2506+
ret["results"] = results
2507+
2508+
return ret, nil
2509+
}

0 commit comments

Comments
 (0)