From 19ee343fb5a596ed7fd8b6e0716cb042a9cf41f3 Mon Sep 17 00:00:00 2001 From: hazim Date: Tue, 18 Apr 2023 21:50:55 +1000 Subject: [PATCH] Remove redundant eth_call from eth_estimateUserOperationGas (#147) --- internal/start/private.go | 3 +- internal/start/searcher.go | 3 +- pkg/client/client.go | 59 +++++++++++------------------ pkg/client/utils.go | 43 ++++++--------------- pkg/gas/{callgas.go => estimate.go} | 19 +++++----- 5 files changed, 46 insertions(+), 81 deletions(-) rename pkg/gas/{callgas.go => estimate.go} (76%) diff --git a/internal/start/private.go b/internal/start/private.go index a746831f..78501056 100644 --- a/internal/start/private.go +++ b/internal/start/private.go @@ -82,8 +82,7 @@ func PrivateMode() { // Init Client c := client.New(mem, chain, conf.SupportedEntryPoints) c.SetGetUserOpReceiptFunc(client.GetUserOpReceiptWithEthClient(eth)) - c.SetGetSimulateValidationFunc(client.GetSimulateValidationWithRpcClient(rpc)) - c.SetGetCallGasEstimateFunc(client.GetCallGasEstimateWithEthClient(rpc, chain, conf.BundlerErrorTracer)) + c.SetGetGasEstimateFunc(client.GetGasEstimateWithEthClient(rpc, chain, conf.BundlerErrorTracer)) c.SetGetUserOpByHashFunc(client.GetUserOpByHashWithEthClient(eth)) c.UseLogger(logr) c.UseModules( diff --git a/internal/start/searcher.go b/internal/start/searcher.go index 523eb99e..c2f98a50 100644 --- a/internal/start/searcher.go +++ b/internal/start/searcher.go @@ -84,8 +84,7 @@ func SearcherMode() { // Init Client c := client.New(mem, chain, conf.SupportedEntryPoints) c.SetGetUserOpReceiptFunc(client.GetUserOpReceiptWithEthClient(eth)) - c.SetGetSimulateValidationFunc(client.GetSimulateValidationWithRpcClient(rpc)) - c.SetGetCallGasEstimateFunc(client.GetCallGasEstimateWithEthClient(rpc, chain, conf.BundlerErrorTracer)) + c.SetGetGasEstimateFunc(client.GetGasEstimateWithEthClient(rpc, chain, conf.BundlerErrorTracer)) c.SetGetUserOpByHashFunc(client.GetUserOpByHashWithEthClient(eth)) c.UseLogger(logr) c.UseModules( diff --git a/pkg/client/client.go b/pkg/client/client.go index 76a5de11..4dbbcaa3 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -20,15 +20,14 @@ import ( // Client controls the end to end process of adding incoming UserOperations to the mempool. It also // implements the required RPC methods as specified in EIP-4337. type Client struct { - mempool *mempool.Mempool - chainID *big.Int - supportedEntryPoints []common.Address - userOpHandler modules.UserOpHandlerFunc - logger logr.Logger - getUserOpReceipt GetUserOpReceiptFunc - getSimulateValidation GetSimulateValidationFunc - getCallGasEstimate GetCallGasEstimateFunc - getUserOpByHash GetUserOpByHashFunc + mempool *mempool.Mempool + chainID *big.Int + supportedEntryPoints []common.Address + userOpHandler modules.UserOpHandlerFunc + logger logr.Logger + getUserOpReceipt GetUserOpReceiptFunc + getGasEstimate GetGasEstimateFunc + getUserOpByHash GetUserOpByHashFunc } // New initializes a new ERC-4337 client which can be extended with modules for validating UserOperations @@ -39,15 +38,14 @@ func New( supportedEntryPoints []common.Address, ) *Client { return &Client{ - mempool: mempool, - chainID: chainID, - supportedEntryPoints: supportedEntryPoints, - userOpHandler: noop.UserOpHandler, - logger: logger.NewZeroLogr().WithName("client"), - getUserOpReceipt: getUserOpReceiptNoop(), - getSimulateValidation: getSimulateValidationNoop(), - getCallGasEstimate: getCallGasEstimateNoop(), - getUserOpByHash: getUserOpByHashNoop(), + mempool: mempool, + chainID: chainID, + supportedEntryPoints: supportedEntryPoints, + userOpHandler: noop.UserOpHandler, + logger: logger.NewZeroLogr().WithName("client"), + getUserOpReceipt: getUserOpReceiptNoop(), + getGasEstimate: getGasEstimateNoop(), + getUserOpByHash: getUserOpByHashNoop(), } } @@ -77,16 +75,11 @@ func (i *Client) SetGetUserOpReceiptFunc(fn GetUserOpReceiptFunc) { i.getUserOpReceipt = fn } -// SetGetSimulateValidationFunc defines a general function for fetching simulateValidation results given a -// userOp and EntryPoint address. This function is called in *Client.EstimateUserOperationGas. -func (i *Client) SetGetSimulateValidationFunc(fn GetSimulateValidationFunc) { - i.getSimulateValidation = fn -} - -// SetGetCallGasEstimateFunc defines a general function for fetching an estimate for callGasLimit given a -// userOp and EntryPoint address. This function is called in *Client.EstimateUserOperationGas. -func (i *Client) SetGetCallGasEstimateFunc(fn GetCallGasEstimateFunc) { - i.getCallGasEstimate = fn +// SetGetGasEstimateFunc defines a general function for fetching an estimate for verificationGasLimit and +// callGasLimit given a userOp and EntryPoint address. This function is called in +// *Client.EstimateUserOperationGas. +func (i *Client) SetGetGasEstimateFunc(fn GetGasEstimateFunc) { + i.getGasEstimate = fn } // SetGetUserOpByHashFunc defines a general function for fetching a userOp given a userOpHash, EntryPoint @@ -169,13 +162,7 @@ func (i *Client) EstimateUserOperationGas(op map[string]any, ep string) (*gas.Ga hash := userOp.GetUserOpHash(epAddr, i.chainID) l = l.WithValues("userop_hash", hash) - sim, err := i.getSimulateValidation(epAddr, userOp) - if err != nil { - l.Error(err, "eth_estimateUserOperationGas error") - return nil, err - } - - cg, err := i.getCallGasEstimate(epAddr, userOp) + vg, cg, err := i.getGasEstimate(epAddr, userOp) if err != nil { l.Error(err, "eth_estimateUserOperationGas error") return nil, err @@ -184,7 +171,7 @@ func (i *Client) EstimateUserOperationGas(op map[string]any, ep string) (*gas.Ga l.Info("eth_estimateUserOperationGas ok") return &gas.GasEstimates{ PreVerificationGas: gas.NewDefaultOverhead().CalcPreVerificationGas(userOp), - VerificationGas: sim.ReturnInfo.PreOpGas, + VerificationGas: big.NewInt(int64(vg)), CallGasLimit: big.NewInt(int64(cg)), }, nil } diff --git a/pkg/client/utils.go b/pkg/client/utils.go index 5520b079..fcfb172c 100644 --- a/pkg/client/utils.go +++ b/pkg/client/utils.go @@ -8,8 +8,6 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/filter" - "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/reverts" - "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/simulation" "github.com/stackup-wallet/stackup-bundler/pkg/gas" "github.com/stackup-wallet/stackup-bundler/pkg/userop" ) @@ -33,41 +31,22 @@ func GetUserOpReceiptWithEthClient(eth *ethclient.Client) GetUserOpReceiptFunc { } } -// GetSimulateValidationFunc is a general interface for fetching simulateValidation results given a userOp -// and EntryPoint address. -type GetSimulateValidationFunc = func(ep common.Address, op *userop.UserOperation) (*reverts.ValidationResultRevert, error) +// GetGasEstimateFunc is a general interface for fetching an estimate for verificationGasLimit and +// callGasLimit given a userOp and EntryPoint address. +type GetGasEstimateFunc = func(ep common.Address, op *userop.UserOperation) (verificationGas uint64, callGas uint64, err error) -func getSimulateValidationNoop() GetSimulateValidationFunc { - return func(ep common.Address, op *userop.UserOperation) (*reverts.ValidationResultRevert, error) { +func getGasEstimateNoop() GetGasEstimateFunc { + return func(ep common.Address, op *userop.UserOperation) (verificationGas uint64, callGas uint64, err error) { //lint:ignore ST1005 This needs to match the bundler test spec. - return nil, errors.New("Missing/invalid userOpHash") - } -} - -// GetSimulateValidationWithRpcClient returns an implementation of GetSimulateValidationFunc that relies on a -// rpc client to fetch simulateValidation results. -func GetSimulateValidationWithRpcClient(rpc *rpc.Client) GetSimulateValidationFunc { - return func(ep common.Address, op *userop.UserOperation) (*reverts.ValidationResultRevert, error) { - return simulation.SimulateValidation(rpc, ep, op) - } -} - -// GetCallGasEstimateFunc is a general interface for fetching an estimate for callGasLimit given a userOp and -// EntryPoint address. -type GetCallGasEstimateFunc = func(ep common.Address, op *userop.UserOperation) (uint64, error) - -func getCallGasEstimateNoop() GetCallGasEstimateFunc { - return func(ep common.Address, op *userop.UserOperation) (uint64, error) { - //lint:ignore ST1005 This needs to match the bundler test spec. - return 0, errors.New("Missing/invalid userOpHash") + return 0, 0, errors.New("Missing/invalid userOpHash") } } -// GetCallGasEstimateWithEthClient returns an implementation of GetCallGasEstimateFunc that relies on an eth -// client to fetch an estimate for callGasLimit. -func GetCallGasEstimateWithEthClient(rpc *rpc.Client, chain *big.Int, tracer string) GetCallGasEstimateFunc { - return func(ep common.Address, op *userop.UserOperation) (uint64, error) { - return gas.CallGasEstimate(rpc, ep, op, chain, tracer) +// GetGasEstimateWithEthClient returns an implementation of GetGasEstimateFunc that relies on an eth client to +// fetch an estimate for verificationGasLimit and callGasLimit. +func GetGasEstimateWithEthClient(rpc *rpc.Client, chain *big.Int, tracer string) GetGasEstimateFunc { + return func(ep common.Address, op *userop.UserOperation) (verificationGas uint64, callGas uint64, err error) { + return gas.EstimateGas(rpc, ep, op, chain, tracer) } } diff --git a/pkg/gas/callgas.go b/pkg/gas/estimate.go similarity index 76% rename from pkg/gas/callgas.go rename to pkg/gas/estimate.go index 07d3840c..75ee369d 100644 --- a/pkg/gas/callgas.go +++ b/pkg/gas/estimate.go @@ -10,36 +10,37 @@ import ( "github.com/stackup-wallet/stackup-bundler/pkg/userop" ) -// CallGasEstimate uses the simulateHandleOp method on the EntryPoint to derive an estimate for callGasLimit. +// EstimateGas uses the simulateHandleOp method on the EntryPoint to derive an estimate for +// verificationGasLimit and callGasLimit. // // TODO: This function requires an eth_call and a debug_traceCall. It could probably be optimized further by // just using a debug_traceCall. -func CallGasEstimate( +func EstimateGas( rpc *rpc.Client, from common.Address, op *userop.UserOperation, chainID *big.Int, tracer string, -) (uint64, error) { +) (verificationGas uint64, callGas uint64, err error) { data, err := op.ToMap() if err != nil { - return 0, err + return 0, 0, err } // Set MaxPriorityFeePerGas = MaxFeePerGas to simplify callGasLimit calculation. data["maxPriorityFeePerGas"] = hexutil.EncodeBig(op.MaxFeePerGas) simOp, err := userop.New(data) if err != nil { - return 0, err + return 0, 0, err } sim, err := execution.SimulateHandleOp(rpc, from, simOp, common.Address{}, []byte{}) if err != nil { - return 0, err + return 0, 0, err } if err := execution.TraceSimulateHandleOp(rpc, from, op, chainID, tracer, common.Address{}, []byte{}); err != nil { - return 0, err + return 0, 0, err } ov := NewDefaultOverhead() @@ -47,7 +48,7 @@ func CallGasEstimate( cgl := big.NewInt(0).Add(big.NewInt(0).Sub(tg, sim.PreOpGas), big.NewInt(int64(ov.fixed))) min := ov.NonZeroValueCall() if cgl.Cmp(min) >= 1 { - return cgl.Uint64(), nil + return sim.PreOpGas.Uint64(), cgl.Uint64(), nil } - return min.Uint64(), nil + return sim.PreOpGas.Uint64(), min.Uint64(), nil }