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

Commit 1335362

Browse files
authored
Account for Optimism gas in PVG (#185)
1 parent 5408be0 commit 1335362

File tree

6 files changed

+143
-0
lines changed

6 files changed

+143
-0
lines changed

internal/config/constants.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ var (
77
GoerliChainID = big.NewInt(5)
88
ArbitrumOneChainID = big.NewInt(42161)
99
ArbitrumGoerliChainID = big.NewInt(421613)
10+
OptimismChainID = big.NewInt(10)
11+
OptimismGoerliChainID = big.NewInt(420)
1012
)

internal/start/private.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ func PrivateMode() {
6161
if chain.Cmp(config.ArbitrumOneChainID) == 0 || chain.Cmp(config.ArbitrumGoerliChainID) == 0 {
6262
ov.SetCalcPreVerificationGasFunc(gas.CalcArbitrumPVGWithEthClient(rpc, conf.SupportedEntryPoints[0]))
6363
}
64+
if chain.Cmp(config.OptimismChainID) == 0 || chain.Cmp(config.OptimismGoerliChainID) == 0 {
65+
ov.SetCalcPreVerificationGasFunc(
66+
gas.CalcOptimismPVGWithEthClient(rpc, chain, conf.SupportedEntryPoints[0]),
67+
)
68+
}
6469

6570
mem, err := mempool.New(db)
6671
if err != nil {

pkg/entrypoint/transaction/handleops.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,15 @@ func CreateRawHandleOps(
139139
}
140140
auth.GasLimit = gas
141141
auth.NoSend = true
142+
if baseFee != nil {
143+
tip, err := eth.SuggestGasTipCap(context.Background())
144+
if err != nil {
145+
return "", err
146+
}
147+
148+
auth.GasTipCap = tip
149+
auth.GasFeeCap = big.NewInt(0).Add(baseFee, tip)
150+
}
142151

143152
tx, err := ep.HandleOps(auth, toAbiType(batch), beneficiary)
144153
if err != nil {

pkg/gas/pvg.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package gas
22

33
import (
4+
"context"
5+
"math"
46
"math/big"
57

68
"github.com/ethereum/go-ethereum/common"
79
"github.com/ethereum/go-ethereum/common/hexutil"
810
"github.com/ethereum/go-ethereum/crypto"
11+
"github.com/ethereum/go-ethereum/ethclient"
912
"github.com/ethereum/go-ethereum/rpc"
1013
"github.com/stackup-wallet/stackup-bundler/pkg/arbitrum/nodeinterface"
1114
"github.com/stackup-wallet/stackup-bundler/pkg/entrypoint"
1215
"github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/methods"
16+
"github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/transaction"
17+
"github.com/stackup-wallet/stackup-bundler/pkg/optimism/gaspriceoracle"
1318
"github.com/stackup-wallet/stackup-bundler/pkg/signer"
1419
"github.com/stackup-wallet/stackup-bundler/pkg/userop"
1520
)
@@ -76,3 +81,70 @@ func CalcArbitrumPVGWithEthClient(
7681
return big.NewInt(0).Add(static, big.NewInt(int64(gas.GasEstimateForL1))), nil
7782
}
7883
}
84+
85+
// CalcOptimismPVGWithEthClient uses Optimism's Gas Price Oracle precompile to get an estimate for
86+
// preVerificationGas that takes into account the L1 gas component.
87+
func CalcOptimismPVGWithEthClient(
88+
rpc *rpc.Client,
89+
chainID *big.Int,
90+
entryPoint common.Address,
91+
) CalcPreVerificationGasFunc {
92+
pk, _ := crypto.GenerateKey()
93+
dummy, _ := signer.New(hexutil.Encode(crypto.FromECDSA(pk))[2:])
94+
return func(op *userop.UserOperation, static *big.Int) (*big.Int, error) {
95+
// Create Raw HandleOps Transaction
96+
eth := ethclient.NewClient(rpc)
97+
head, err := eth.HeaderByNumber(context.Background(), nil)
98+
if err != nil {
99+
return nil, err
100+
}
101+
tx, err := transaction.CreateRawHandleOps(
102+
dummy,
103+
eth,
104+
chainID,
105+
entryPoint,
106+
[]*userop.UserOperation{op},
107+
dummy.Address,
108+
math.MaxUint64,
109+
head.BaseFee,
110+
)
111+
if err != nil {
112+
return nil, err
113+
}
114+
115+
// Encode function data for GetL1Fee
116+
data, err := hexutil.Decode(tx)
117+
if err != nil {
118+
return nil, err
119+
}
120+
ge, err := gaspriceoracle.GetL1FeeMethod.Inputs.Pack(data)
121+
if err != nil {
122+
return nil, err
123+
}
124+
125+
// Use eth_call to call the Gas Price Oracle precompile
126+
req := map[string]any{
127+
"from": common.HexToAddress("0x"),
128+
"to": gaspriceoracle.PrecompileAddress,
129+
"data": hexutil.Encode(append(gaspriceoracle.GetL1FeeMethod.ID, ge...)),
130+
}
131+
var out any
132+
if err := rpc.Call(&out, "eth_call", &req, "latest"); err != nil {
133+
return nil, err
134+
}
135+
136+
// Get L1Fee and L2Price
137+
l1fee, err := gaspriceoracle.DecodeGetL1FeeMethodOutput(out)
138+
if err != nil {
139+
return nil, err
140+
}
141+
l2price := op.MaxFeePerGas
142+
l2priority := big.NewInt(0).Add(op.MaxPriorityFeePerGas, head.BaseFee)
143+
if l2priority.Cmp(l2price) == -1 {
144+
l2price = l2priority
145+
}
146+
147+
// Return static + L1 buffer as PVG. L1 buffer is equal to L1Fee/L2Price.
148+
return big.NewInt(0).Add(static, big.NewInt(0).Div(l1fee, l2price)), nil
149+
}
150+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package gaspriceoracle
2+
3+
import "github.com/ethereum/go-ethereum/common"
4+
5+
var (
6+
PrecompileAddress = common.HexToAddress("0x420000000000000000000000000000000000000F")
7+
)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package gaspriceoracle
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"math/big"
7+
8+
"github.com/ethereum/go-ethereum/accounts/abi"
9+
"github.com/ethereum/go-ethereum/common/hexutil"
10+
)
11+
12+
var (
13+
bytesT, _ = abi.NewType("bytes", "", nil)
14+
uint256T, _ = abi.NewType("uint256", "", nil)
15+
16+
GetL1FeeMethod = abi.NewMethod(
17+
"getL1Fee",
18+
"getL1Fee",
19+
abi.Function,
20+
"",
21+
false,
22+
false,
23+
abi.Arguments{
24+
{Name: "data", Type: bytesT},
25+
},
26+
abi.Arguments{
27+
{Name: "fee", Type: uint256T},
28+
},
29+
)
30+
)
31+
32+
func DecodeGetL1FeeMethodOutput(out any) (*big.Int, error) {
33+
hex, ok := out.(string)
34+
if !ok {
35+
return nil, errors.New("getL1Fee: cannot assert type: hex is not of type string")
36+
}
37+
data, err := hexutil.Decode(hex)
38+
if err != nil {
39+
return nil, fmt.Errorf("getL1Fee: %s", err)
40+
}
41+
42+
args, err := GetL1FeeMethod.Outputs.Unpack(data)
43+
if err != nil {
44+
return nil, fmt.Errorf("getL1Fee: %s", err)
45+
}
46+
47+
return args[0].(*big.Int), nil
48+
}

0 commit comments

Comments
 (0)