@@ -18,6 +18,7 @@ package ethapi
1818
1919import (
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