Skip to content
This repository was archived by the owner on Oct 20, 2024. It is now read-only.

Commit

Permalink
Remove redundant eth_call from eth_estimateUserOperationGas (#147)
Browse files Browse the repository at this point in the history
  • Loading branch information
hazim-j authored Apr 18, 2023
1 parent 989aaa2 commit 19ee343
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 81 deletions.
3 changes: 1 addition & 2 deletions internal/start/private.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
3 changes: 1 addition & 2 deletions internal/start/searcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
59 changes: 23 additions & 36 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(),
}
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand Down
43 changes: 11 additions & 32 deletions pkg/client/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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)
}
}

Expand Down
19 changes: 10 additions & 9 deletions pkg/gas/callgas.go → pkg/gas/estimate.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,44 +10,45 @@ 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()
tg := big.NewInt(0).Div(sim.Paid, op.MaxFeePerGas)
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
}

0 comments on commit 19ee343

Please sign in to comment.