@@ -1483,6 +1483,74 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
14831483 }
14841484}
14851485
1486+ // Creates an access list on top of given state
1487+ // identical to AccessList, with the exception of state is loaded in arguments
1488+ func AccessListOnState (ctx context.Context , b Backend , header * types.Header , db * state.StateDB , args TransactionArgs ) (acl types.AccessList , gasUsed uint64 , vmErr error , err error ) {
1489+ // If the gas amount is not set, extract this as it will depend on access
1490+ // lists and we'll need to reestimate every time
1491+ nogas := args .Gas == nil
1492+
1493+ // Ensure any missing fields are filled, extract the recipient and input data
1494+ if err := args .setDefaults (ctx , b ); err != nil {
1495+ return nil , 0 , nil , err
1496+ }
1497+ var to common.Address
1498+ if args .To != nil {
1499+ to = * args .To
1500+ } else {
1501+ to = crypto .CreateAddress (args .from (), uint64 (* args .Nonce ))
1502+ }
1503+ // Retrieve the precompiles since they don't need to be added to the access list
1504+ isPostMerge := header .Difficulty .Cmp (common .Big0 ) == 0
1505+ // Retrieve the precompiles since they don't need to be added to the access list
1506+ precompiles := vm .ActivePrecompiles (b .ChainConfig ().Rules (header .Number , isPostMerge ))
1507+
1508+ // Create an initial tracer
1509+ prevTracer := logger .NewAccessListTracer (nil , args .from (), to , precompiles )
1510+ if args .AccessList != nil {
1511+ prevTracer = logger .NewAccessListTracer (* args .AccessList , args .from (), to , precompiles )
1512+ }
1513+ for {
1514+ // Retrieve the current access list to expand
1515+ accessList := prevTracer .AccessList ()
1516+ log .Trace ("Creating access list" , "input" , accessList )
1517+
1518+ // If no gas amount was specified, each unique access list needs it's own
1519+ // gas calculation. This is quite expensive, but we need to be accurate
1520+ // and it's convered by the sender only anyway.
1521+ if nogas {
1522+ args .Gas = nil
1523+ if err := args .setDefaults (ctx , b ); err != nil {
1524+ return nil , 0 , nil , err // shouldn't happen, just in case
1525+ }
1526+ }
1527+
1528+ statedb := db .Copy () // woops shouldn't have removed this lol
1529+ // Set the accesslist to the last al
1530+ args .AccessList = & accessList
1531+ msg , err := args .ToMessage (b .RPCGasCap (), header .BaseFee )
1532+ if err != nil {
1533+ return nil , 0 , nil , err
1534+ }
1535+
1536+ // Apply the transaction with the access list tracer
1537+ tracer := logger .NewAccessListTracer (accessList , args .from (), to , precompiles )
1538+ config := vm.Config {Tracer : tracer , Debug : true , NoBaseFee : true }
1539+ vmenv , _ , err := b .GetEVM (ctx , msg , statedb , header , & config )
1540+ if err != nil {
1541+ return nil , 0 , nil , err
1542+ }
1543+ res , err := core .ApplyMessage (vmenv , msg , new (core.GasPool ).AddGas (msg .Gas ()))
1544+ if err != nil {
1545+ return nil , 0 , nil , fmt .Errorf ("failed to apply transaction: %v err: %v" , args .toTransaction ().Hash (), err )
1546+ }
1547+ if tracer .Equal (prevTracer ) {
1548+ return accessList , res .UsedGas , res .Err , nil
1549+ }
1550+ prevTracer = tracer
1551+ }
1552+ }
1553+
14861554// PublicTransactionPoolAPI exposes methods for the RPC interface
14871555type PublicTransactionPoolAPI struct {
14881556 b Backend
@@ -2246,6 +2314,8 @@ type CallBundleArgs struct {
22462314 GasLimit * uint64 `json:"gasLimit"`
22472315 Difficulty * big.Int `json:"difficulty"`
22482316 BaseFee * big.Int `json:"baseFee"`
2317+ SimulationLogs bool `json:"simulationLogs"`
2318+ CreateAccessList bool `json:"createAccessList"`
22492319}
22502320
22512321// CallBundle will simulate a bundle of transactions at the top of a given block
@@ -2345,6 +2415,8 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st
23452415 coinbaseBalanceBeforeTx := state .GetBalance (coinbase )
23462416 state .Prepare (tx .Hash (), i )
23472417
2418+ accessListState := state .Copy () // create a copy just in case we use it later for access list creation
2419+
23482420 receipt , result , err := core .ApplyTransactionWithResult (s .b .ChainConfig (), s .chain , & coinbase , gp , state , header , tx , & header .GasUsed , vmconfig )
23492421 if err != nil {
23502422 return nil , fmt .Errorf ("err: %w; txhash %s" , err , tx .Hash ())
@@ -2384,6 +2456,46 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st
23842456 hex .Encode (dst , result .Return ())
23852457 jsonResult ["value" ] = "0x" + string (dst )
23862458 }
2459+ // if simulation logs are requested append it to logs
2460+ if args .SimulationLogs {
2461+ jsonResult ["logs" ] = receipt .Logs
2462+ }
2463+ // if an access list is requested create and append
2464+ if args .CreateAccessList {
2465+ // ifdk another way to fill all values so this will have to do - x2
2466+ txArgGas := hexutil .Uint64 (tx .Gas ())
2467+ txArgNonce := hexutil .Uint64 (tx .Nonce ())
2468+ txArgData := hexutil .Bytes (tx .Data ())
2469+ txargs := TransactionArgs {
2470+ From : & from ,
2471+ To : tx .To (),
2472+ Gas : & txArgGas ,
2473+ Nonce : & txArgNonce ,
2474+ Data : & txArgData ,
2475+ Value : (* hexutil .Big )(tx .Value ()),
2476+ ChainID : (* hexutil .Big )(tx .ChainId ()),
2477+ }
2478+ if tx .GasFeeCap ().Cmp (big .NewInt (0 )) == 0 { // no maxbasefee, set gasprice instead
2479+ txargs .GasPrice = (* hexutil .Big )(tx .GasPrice ())
2480+ } else { // otherwise set base and priority fee
2481+ txargs .MaxFeePerGas = (* hexutil .Big )(tx .GasFeeCap ())
2482+ txargs .MaxPriorityFeePerGas = (* hexutil .Big )(tx .GasTipCap ())
2483+ }
2484+ acl , gasUsed , vmerr , err := AccessListOnState (ctx , s .b , header , accessListState , txargs )
2485+ if err == nil {
2486+ if gasUsed != receipt .GasUsed {
2487+ log .Info ("Gas used in receipt differ from accesslist" , "receipt" , receipt .GasUsed , "acl" , gasUsed ) // weird bug but it works
2488+ }
2489+ if vmerr != nil {
2490+ log .Info ("CallBundle accesslist creation encountered vmerr" , "vmerr" , vmerr )
2491+ }
2492+ jsonResult ["accessList" ] = acl
2493+
2494+ } else {
2495+ log .Info ("CallBundle accesslist creation encountered err" , "err" , err )
2496+ jsonResult ["accessList" ] = acl //
2497+ } // return the empty accesslist either way
2498+ }
23872499 coinbaseDiffTx := new (big.Int ).Sub (state .GetBalance (coinbase ), coinbaseBalanceBeforeTx )
23882500 jsonResult ["coinbaseDiff" ] = coinbaseDiffTx .String ()
23892501 jsonResult ["gasFees" ] = gasFeesTx .String ()
@@ -2415,8 +2527,11 @@ type EstimateGasBundleArgs struct {
24152527 Coinbase * string `json:"coinbase"`
24162528 Timestamp * uint64 `json:"timestamp"`
24172529 Timeout * int64 `json:"timeout"`
2530+ //SimulationLogs bool `json:"simulationLogs"`
2531+ CreateAccessList bool `json:"createAccessList"`
24182532}
24192533
2534+ // im lazy to create a few extra core functions to return logs here, maybe sometime later or if someone else does it
24202535func (s * BundleAPI ) EstimateGasBundle (ctx context.Context , args EstimateGasBundleArgs ) (map [string ]interface {}, error ) {
24212536 if len (args .Txs ) == 0 {
24222537 return nil , errors .New ("bundle missing txs" )
@@ -2495,6 +2610,8 @@ func (s *BundleAPI) EstimateGasBundle(ctx context.Context, args EstimateGasBundl
24952610 // New random hash since its a call
24962611 statedb .Prepare (randomHash , i )
24972612
2613+ accessListState := statedb .Copy () // create a copy just in case we use it later for access list creation
2614+
24982615 // Convert tx args to msg to apply state transition
24992616 msg , err := txArgs .ToMessage (globalGasCap , header .BaseFee )
25002617 if err != nil {
@@ -2521,6 +2638,43 @@ func (s *BundleAPI) EstimateGasBundle(ctx context.Context, args EstimateGasBundl
25212638 jsonResult := map [string ]interface {}{
25222639 "gasUsed" : result .UsedGas ,
25232640 }
2641+
2642+ // if simulation logs are requested append it to logs
2643+ // if an access list is requested create and append
2644+ if args .CreateAccessList {
2645+ // welp guess we're copying these again sigh
2646+ txArgFrom := msg .From ()
2647+ txArgGas := hexutil .Uint64 (msg .Gas ())
2648+ txArgNonce := hexutil .Uint64 (msg .Nonce ())
2649+ txArgData := hexutil .Bytes (msg .Data ())
2650+ txargs := TransactionArgs {
2651+ From : & txArgFrom ,
2652+ To : msg .To (),
2653+ Gas : & txArgGas ,
2654+ Nonce : & txArgNonce ,
2655+ Data : & txArgData ,
2656+ ChainID : (* hexutil .Big )(s .chain .Config ().ChainID ),
2657+ Value : (* hexutil .Big )(msg .Value ()),
2658+ }
2659+ if msg .GasFeeCap ().Cmp (big .NewInt (0 )) == 0 { // no maxbasefee, set gasprice instead
2660+ txargs .GasPrice = (* hexutil .Big )(msg .GasPrice ())
2661+ } else { // otherwise set base and priority fee
2662+ txargs .MaxFeePerGas = (* hexutil .Big )(msg .GasFeeCap ())
2663+ txargs .MaxPriorityFeePerGas = (* hexutil .Big )(msg .GasTipCap ())
2664+ }
2665+ acl , _ , vmerr , err := AccessListOnState (ctx , s .b , header , accessListState , txargs )
2666+ if err == nil {
2667+ if vmerr != nil {
2668+ log .Info ("CallBundle accesslist creation encountered vmerr" , "vmerr" , vmerr )
2669+ }
2670+ jsonResult ["accessList" ] = acl
2671+
2672+ } else {
2673+ log .Info ("CallBundle accesslist creation encountered err" , "err" , err )
2674+ jsonResult ["accessList" ] = acl //
2675+ } // return the empty accesslist either way
2676+ }
2677+
25242678 results = append (results , jsonResult )
25252679 }
25262680
0 commit comments