Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions tx-submitter/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ func Main() func(ctx *cli.Context) error {
"rough_estimate_per_l1_msg", cfg.RollupTxGasPerL1Msg,
"log_level", cfg.LogLevel,
"leveldb_pathname", cfg.LeveldbPathName,
"min_tip", cfg.MinTip,
"max_tip", cfg.MaxTip,
"max_base", cfg.MaxBaseFee,
"tip_bump", cfg.TipFeeBump,
)

ctx, cancel := context.WithCancel(context.Background())
Expand Down
33 changes: 27 additions & 6 deletions tx-submitter/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,29 @@ var (
Value: "StakingEventStore.json",
}

CalldataFeeBumpFlag = cli.Uint64Flag{
Name: "call_data_fee_bump",
Usage: "The fee bump for call data",
Value: 100, //fee = x * origin_fee/100
EnvVar: prefixEnvVar("CALL_DATA_FEE_BUMP"),
TipFeeBumpFlag = cli.Uint64Flag{
Name: "TIP_FEE_BUMP",
Usage: "The fee bump for tip",
Value: 120, //bumpTip = tip * TipFeeBump/100
EnvVar: prefixEnvVar("TIP_FEE_BUMP"),
}
MaxTipFlag = cli.Uint64Flag{
Name: "max_tip",
Usage: "The maximum tip for a transaction",
Value: 10e9, //10gwei
EnvVar: prefixEnvVar("MAX_TIP"),
}
MinTipFlag = cli.Uint64Flag{
Name: "min_tip",
Usage: "The minimum tip for a transaction",
Value: 5e8, //0.5gwei
EnvVar: prefixEnvVar("MIN_TIP"),
}
MaxBaseFeeFlag = cli.Uint64Flag{
Name: "max_base_fee",
Usage: "The maximum base fee for a transaction",
Value: 100e9, //100gwei
EnvVar: prefixEnvVar("MAX_BASE_FEE"),
}

MaxTxsInPendingPoolFlag = cli.Uint64Flag{
Expand Down Expand Up @@ -345,7 +363,10 @@ var optionalFlags = []cli.Flag{
PrivateKeyFlag,
L2SequencerAddressFlag,
L2GovAddressFlag,
CalldataFeeBumpFlag,
TipFeeBumpFlag,
MaxTipFlag,
MinTipFlag,
MaxBaseFeeFlag,
MaxTxsInPendingPoolFlag,

// external sign
Expand Down
87 changes: 87 additions & 0 deletions tx-submitter/mock/l1mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package mock

import (
"context"
"math/big"

"github.com/morph-l2/go-ethereum"
"github.com/morph-l2/go-ethereum/common"
"github.com/morph-l2/go-ethereum/core/types"
)

type L1ClientWrapper struct {
Block *types.Block
TipCap *big.Int
}

func NewL1ClientWrapper() *L1ClientWrapper {
return &L1ClientWrapper{}
}

func (c *L1ClientWrapper) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
return nil, nil
}

func (c *L1ClientWrapper) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
return big.NewInt(0), nil
}

func (c *L1ClientWrapper) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
return nil, false, nil
}

func (c *L1ClientWrapper) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
return 0, nil
}

func (c *L1ClientWrapper) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
return nil, nil
}

func (c *L1ClientWrapper) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
return c.Block.Header(), nil
}

func (c *L1ClientWrapper) BlockNumber(ctx context.Context) (uint64, error) {
return c.Block.NumberU64(), nil
}

func (c *L1ClientWrapper) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
ch <- c.Block.Header()
return nil, nil
}

func (c *L1ClientWrapper) SetBlock(block *types.Block) {
c.Block = block
}
func (c *L1ClientWrapper) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
return nil, nil
}

func (c *L1ClientWrapper) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
return nil, nil
}
func (c *L1ClientWrapper) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
return nil, nil
}
func (c *L1ClientWrapper) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
return 0, nil
}
func (c *L1ClientWrapper) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
return big.NewInt(0), nil
}
func (c *L1ClientWrapper) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
return c.TipCap, nil
}
func (c *L1ClientWrapper) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) {
return 0, nil
}
func (c *L1ClientWrapper) SendTransaction(ctx context.Context, tx *types.Transaction) error {
return nil
}
func (c *L1ClientWrapper) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) {
return nil, nil
}
func (c *L1ClientWrapper) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
return nil, nil
}
47 changes: 34 additions & 13 deletions tx-submitter/services/rollup.go
Original file line number Diff line number Diff line change
Expand Up @@ -849,14 +849,32 @@ func (r *Rollup) buildSignatureInput(batch *eth.RPCRollupBatch) (*bindings.IRoll
}

func (r *Rollup) GetGasTipAndCap() (*big.Int, *big.Int, *big.Int, error) {
tip, err := r.L1Client.SuggestGasTipCap(context.Background())

head, err := r.L1Client.HeaderByNumber(context.Background(), nil)
if err != nil {
return nil, nil, nil, err
}
head, err := r.L1Client.HeaderByNumber(context.Background(), nil)
if head.BaseFee != nil {
log.Info("market fee info", "feecap", head.BaseFee)
if r.cfg.MaxBaseFee > 0 && head.BaseFee.Cmp(big.NewInt(int64(r.cfg.MaxBaseFee))) > 0 {
return nil, nil, nil, fmt.Errorf("base fee is too high, base fee %v exceeds max %v", head.BaseFee, r.cfg.MaxBaseFee)
}
}

tip, err := r.L1Client.SuggestGasTipCap(context.Background())
if err != nil {
return nil, nil, nil, err
}
log.Info("market fee info", "tip", tip)

if r.cfg.TipFeeBump > 0 {
tip = new(big.Int).Mul(tip, big.NewInt(int64(r.cfg.TipFeeBump)))
tip = new(big.Int).Div(tip, big.NewInt(100))
}
if r.cfg.MaxTip > 0 && tip.Cmp(big.NewInt(int64(r.cfg.MaxTip))) > 0 {
return nil, nil, nil, fmt.Errorf("tip is too high, tip %v exceeds max %v", tip, r.cfg.MaxTip)
}

var gasFeeCap *big.Int
if head.BaseFee != nil {
gasFeeCap = new(big.Int).Add(
Expand All @@ -873,15 +891,11 @@ func (r *Rollup) GetGasTipAndCap() (*big.Int, *big.Int, *big.Int, error) {
blobFee = eip4844.CalcBlobFee(*head.ExcessBlobGas)
}

//calldata fee bump x*fee/100
if r.cfg.CalldataFeeBump > 0 {
// feecap
gasFeeCap = new(big.Int).Mul(gasFeeCap, big.NewInt(int64(r.cfg.CalldataFeeBump)))
gasFeeCap = new(big.Int).Div(gasFeeCap, big.NewInt(100))
// tip
tip = new(big.Int).Mul(tip, big.NewInt(int64(r.cfg.CalldataFeeBump)))
tip = new(big.Int).Div(tip, big.NewInt(100))
}
log.Info("fee info after bump",
"tip", tip,
"feecap", gasFeeCap,
"blobfee", blobFee,
)

return tip, gasFeeCap, blobFee, nil
}
Expand Down Expand Up @@ -1099,7 +1113,7 @@ func (r *Rollup) SendTx(tx *types.Transaction) error {
return errors.New("nil tx")
}
// l1 health check
if !r.bm.IsGrowth() {
if r.bm != nil && !r.bm.IsGrowth() {
return fmt.Errorf("block not growth in %d blocks time", r.cfg.BlockNotIncreasedThreshold)
}

Expand All @@ -1110,7 +1124,9 @@ func (r *Rollup) SendTx(tx *types.Transaction) error {

// after send tx
// add to pending txs
r.pendingTxs.Add(tx)
if r.pendingTxs != nil {
r.pendingTxs.Add(tx)
}

return nil

Expand Down Expand Up @@ -1183,6 +1199,11 @@ func (r *Rollup) ReSubmitTx(resend bool, tx *types.Transaction) (*types.Transact
blobFeeCap = bumpedBlobFeeCap
}
}

if r.cfg.MinTip > 0 && tip.Cmp(big.NewInt(int64(r.cfg.MinTip))) < 0 {
log.Info("replace tip is too low, update tip to min tip ", "tip", tip, "min_tip", r.cfg.MinTip)
tip = big.NewInt(int64(r.cfg.MinTip))
}
}

var newTx *types.Transaction
Expand Down
110 changes: 107 additions & 3 deletions tx-submitter/services/rollup_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package services

import (
"context"
"math/big"
"testing"

"github.com/holiman/uint256"
"morph-l2/tx-submitter/mock"
"morph-l2/tx-submitter/utils"

"github.com/morph-l2/go-ethereum/common"
"github.com/morph-l2/go-ethereum/core/types"
"github.com/stretchr/testify/require"
"github.com/morph-l2/go-ethereum/crypto"

"morph-l2/tx-submitter/utils"
"github.com/holiman/uint256"
"github.com/stretchr/testify/require"
)

func TestSendTx(t *testing.T) {
Expand Down Expand Up @@ -49,3 +53,103 @@ func TestSendTx(t *testing.T) {
err = sendTx(nil, 1, blobTx)
require.ErrorContains(t, err, utils.ErrExceedFeeLimit.Error())
}

func TestGetGasTipAndCap(t *testing.T) {
l1Mock := mock.NewL1ClientWrapper()
initTip := big.NewInt(1e9)

baseFee := big.NewInt(1e9)
excessBlobGas := uint64(1)
block := types.NewBlockWithHeader(
&types.Header{
BaseFee: baseFee,
ExcessBlobGas: &excessBlobGas,
},
)
l1Mock.TipCap = initTip
l1Mock.Block = block
config := utils.Config{
MaxTip: 10e9,
MaxBaseFee: 100e9,
MinTip: 1e9,
TipFeeBump: 100,
}
r := NewRollup(context.Background(), nil, nil, l1Mock, nil, nil, nil, nil, nil, common.Address{}, nil, config, nil, nil, nil, nil)
tip, feecap, blobfee, err := r.GetGasTipAndCap()
require.NoError(t, err)
require.NotNil(t, tip)
require.NotNil(t, feecap)
require.NotNil(t, blobfee)
require.Equal(t, initTip, tip)

config = utils.Config{
MaxTip: 10e9,
MaxBaseFee: 100e9,
MinTip: 1e9,
TipFeeBump: 200,
}
r = NewRollup(context.Background(), nil, nil, l1Mock, nil, nil, nil, nil, nil, common.Address{}, nil, config, nil, nil, nil, nil)
tip, feecap, blobfee, err = r.GetGasTipAndCap()
require.NoError(t, err)
require.NotNil(t, tip)
require.NotNil(t, feecap)
require.NotNil(t, blobfee)
require.Equal(t, tip, initTip.Mul(initTip, big.NewInt(2)))

config = utils.Config{
MaxTip: 10e9,
MaxBaseFee: baseFee.Uint64() - 1,
MinTip: 1e9,
TipFeeBump: 200,
}
r = NewRollup(context.Background(), nil, nil, l1Mock, nil, nil, nil, nil, nil, common.Address{}, nil, config, nil, nil, nil, nil)
_, _, _, err = r.GetGasTipAndCap()
require.ErrorContains(t, err, "base fee is too high")

config = utils.Config{
MaxTip: initTip.Uint64() - 1,
MaxBaseFee: 100e9,
MinTip: 1e9,
TipFeeBump: 200,
}
r = NewRollup(context.Background(), nil, nil, l1Mock, nil, nil, nil, nil, nil, common.Address{}, nil, config, nil, nil, nil, nil)
_, _, _, err = r.GetGasTipAndCap()
require.ErrorContains(t, err, "tip is too high")

}

func TestReSubmitTx(t *testing.T) {
l1Mock := mock.NewL1ClientWrapper()
initTip := big.NewInt(1e9)

baseFee := big.NewInt(1e9)
excessBlobGas := uint64(1)
block := types.NewBlockWithHeader(
&types.Header{
BaseFee: baseFee,
ExcessBlobGas: &excessBlobGas,
},
)
l1Mock.TipCap = initTip
l1Mock.Block = block
config := utils.Config{
MaxTip: 10e12,
MaxBaseFee: 100e9,
MinTip: 1e10,
TipFeeBump: 100,
}

priv, err := crypto.GenerateKey()
require.NoError(t, err)

r := NewRollup(context.Background(), nil, nil, l1Mock, nil, nil, nil, big.NewInt(1), priv, common.Address{}, nil, config, nil, nil, nil, nil)
_, err = r.ReSubmitTx(false, nil)
require.ErrorContains(t, err, "nil tx")
oldTx := types.NewTx(&types.DynamicFeeTx{
GasTipCap: initTip,
})
tx, err := r.ReSubmitTx(false, oldTx)
require.NoError(t, err)
require.EqualValues(t, config.MinTip, tx.GasTipCap().Uint64())

}
15 changes: 10 additions & 5 deletions tx-submitter/utils/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,11 @@ type Config struct {

// journal file path
JournalFilePath string
// calldata fee bump
CalldataFeeBump uint64
// tip bump
TipFeeBump uint64
MaxTip uint64
MinTip uint64
MaxBaseFee uint64
//max txs in pendingpool
MaxTxsInPendingPool uint64

Expand Down Expand Up @@ -151,9 +154,11 @@ func NewConfig(ctx *cli.Context) (Config, error) {

GasLimitBuffer: ctx.GlobalUint64(flags.GasLimitBuffer.Name),

JournalFilePath: ctx.GlobalString(flags.JournalFlag.Name),
// calldata fee bump
CalldataFeeBump: ctx.GlobalUint64(flags.CalldataFeeBumpFlag.Name),
JournalFilePath: ctx.GlobalString(flags.JournalFlag.Name),
TipFeeBump: ctx.GlobalUint64(flags.TipFeeBumpFlag.Name),
MaxTip: ctx.GlobalUint64(flags.MaxTipFlag.Name),
MinTip: ctx.GlobalUint64(flags.MinTipFlag.Name),
MaxBaseFee: ctx.GlobalUint64(flags.MaxBaseFeeFlag.Name),
MaxTxsInPendingPool: ctx.GlobalUint64(flags.MaxTxsInPendingPoolFlag.Name),

// external sign
Expand Down