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