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

Commit a42d516

Browse files
authored
Refactor: only use debug_traceCall during gas estimation (#168)
1 parent 6532b12 commit a42d516

File tree

5 files changed

+59
-20
lines changed

5 files changed

+59
-20
lines changed

pkg/entrypoint/execution/trace.go

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,45 @@ package execution
22

33
import (
44
"context"
5+
"fmt"
56
"math"
67
"math/big"
78

89
"github.com/ethereum/go-ethereum/accounts/abi/bind"
910
"github.com/ethereum/go-ethereum/common"
1011
"github.com/ethereum/go-ethereum/common/hexutil"
1112
"github.com/ethereum/go-ethereum/ethclient"
12-
"github.com/ethereum/go-ethereum/rpc"
13+
ethRpc "github.com/ethereum/go-ethereum/rpc"
1314
"github.com/stackup-wallet/stackup-bundler/pkg/entrypoint"
15+
"github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/reverts"
1416
"github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/utils"
1517
"github.com/stackup-wallet/stackup-bundler/pkg/errors"
1618
"github.com/stackup-wallet/stackup-bundler/pkg/tracer"
1719
"github.com/stackup-wallet/stackup-bundler/pkg/userop"
1820
)
1921

2022
func TraceSimulateHandleOp(
21-
rpc *rpc.Client,
23+
rpc *ethRpc.Client,
2224
entryPoint common.Address,
2325
op *userop.UserOperation,
2426
chainID *big.Int,
2527
customTracer string,
2628
target common.Address,
2729
data []byte,
28-
) error {
30+
) (*reverts.ExecutionResultRevert, error) {
2931
ep, err := entrypoint.NewEntrypoint(entryPoint, ethclient.NewClient(rpc))
3032
if err != nil {
31-
return err
33+
return nil, err
3234
}
3335
auth, err := bind.NewKeyedTransactorWithChainID(utils.DummyPk, chainID)
3436
if err != nil {
35-
return err
37+
return nil, err
3638
}
3739
auth.GasLimit = math.MaxUint64
3840
auth.NoSend = true
3941
tx, err := ep.SimulateHandleOp(auth, entrypoint.UserOperation(*op), target, data)
4042
if err != nil {
41-
return err
43+
return nil, err
4244
}
4345

4446
var res tracer.BundlerErrorReturn
@@ -51,24 +53,37 @@ func TraceSimulateHandleOp(
5153
Tracer: customTracer,
5254
}
5355
if err := rpc.CallContext(context.Background(), &res, "debug_traceCall", &req, "latest", &opts); err != nil {
54-
return err
56+
return nil, err
57+
}
58+
outErr, err := errors.ParseHexToRpcDataError(res.Output)
59+
if err != nil {
60+
return nil, err
61+
}
62+
63+
sim, simErr := reverts.NewExecutionResult(outErr)
64+
if simErr != nil {
65+
fo, foErr := reverts.NewFailedOp(outErr)
66+
if foErr != nil {
67+
return nil, fmt.Errorf("%s, %s", simErr, foErr)
68+
}
69+
return nil, errors.NewRPCError(errors.REJECTED_BY_EP_OR_ACCOUNT, fo.Reason, fo)
5570
}
5671

5772
if len(res.Reverts) != 0 {
5873
data, err := hexutil.Decode(res.Reverts[len(res.Reverts)-1])
5974
if err != nil {
60-
return err
75+
return sim, err
6176
}
6277

6378
if len(data) == 0 {
64-
return errors.NewRPCError(errors.EXECUTION_REVERTED, "execution reverted", nil)
79+
return sim, errors.NewRPCError(errors.EXECUTION_REVERTED, "execution reverted", nil)
6580
}
6681

6782
reason, err := errors.DecodeRevert(data)
6883
if err != nil {
69-
return err
84+
return sim, err
7085
}
71-
return errors.NewRPCError(errors.EXECUTION_REVERTED, reason, reason)
86+
return sim, errors.NewRPCError(errors.EXECUTION_REVERTED, reason, reason)
7287
}
73-
return nil
88+
return sim, nil
7489
}

pkg/errors/string.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package errors
2+
3+
import (
4+
"github.com/ethereum/go-ethereum/common"
5+
"github.com/ethereum/go-ethereum/rpc"
6+
)
7+
8+
type hexDataErrorWrapper struct {
9+
output string
10+
}
11+
12+
// ParseHexToRpcDataError is a utility function that converts a hex string into an interface that's compatible
13+
// with rpc.DataError. This is useful for parsing output from a debug_traceCall that resulted in a transaction
14+
// revert.
15+
func ParseHexToRpcDataError(hex string) (rpc.DataError, error) {
16+
if _, err := common.ParseHexOrString(hex); err != nil {
17+
return nil, err
18+
}
19+
20+
res := &hexDataErrorWrapper{output: hex}
21+
return res, nil
22+
}
23+
24+
func (t *hexDataErrorWrapper) Error() string {
25+
return t.output
26+
}
27+
func (t *hexDataErrorWrapper) ErrorData() any {
28+
return t.output
29+
}

pkg/gas/estimate.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ import (
1313

1414
// EstimateGas uses the simulateHandleOp method on the EntryPoint to derive an estimate for
1515
// verificationGasLimit and callGasLimit.
16-
//
17-
// TODO: This function requires an eth_call and a debug_traceCall. It could probably be optimized further by
18-
// just using a debug_traceCall.
1916
func EstimateGas(
2017
rpc *rpc.Client,
2118
from common.Address,
@@ -43,15 +40,11 @@ func EstimateGas(
4340
return 0, 0, err
4441
}
4542

46-
sim, err := execution.SimulateHandleOp(rpc, from, simOp, common.Address{}, []byte{})
43+
sim, err := execution.TraceSimulateHandleOp(rpc, from, simOp, chainID, tracer, common.Address{}, []byte{})
4744
if err != nil {
4845
return 0, 0, err
4946
}
5047

51-
if err := execution.TraceSimulateHandleOp(rpc, from, op, chainID, tracer, common.Address{}, []byte{}); err != nil {
52-
return 0, 0, err
53-
}
54-
5548
tg := big.NewInt(0).Div(sim.Paid, op.MaxFeePerGas)
5649
cgl := big.NewInt(0).Add(big.NewInt(0).Sub(tg, sim.PreOpGas), big.NewInt(int64(ov.fixed)))
5750
min := ov.NonZeroValueCall()

pkg/tracer/BundlerErrorTracer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var tracer = {
66
result: function result(ctx, db) {
77
return {
88
reverts: this.reverts,
9+
output: toHex(ctx.output),
910
};
1011
},
1112

pkg/tracer/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,5 @@ type BundlerCollectorReturn struct {
5757
// BundlerErrorReturn is the return value from performing an EVM trace with BundlerErrorTracer.js.
5858
type BundlerErrorReturn struct {
5959
Reverts []string `json:"reverts"`
60+
Output string `json:"output"`
6061
}

0 commit comments

Comments
 (0)