From ec7e0c31635a95075a38acd88d23e1b94ca54de5 Mon Sep 17 00:00:00 2001
From: Solovyov1796 <xinyu@0g.ai>
Date: Sat, 8 Feb 2025 21:53:25 +0800
Subject: [PATCH 1/8] generate suggestion gas price during prepare proposal and
 pass it to ethermint via fee market keeper

---
 app/abci_utils.go     |  59 +++++-
 app/ante/ante_test.go |  10 +-
 app/app.go            |   7 +-
 app/app_test.go       |  23 ++-
 app/test_common.go    |   4 +-
 app/upgrades_test.go  | 433 ------------------------------------------
 cmd/0gchaind/app.go   |  10 +-
 7 files changed, 93 insertions(+), 453 deletions(-)

diff --git a/app/abci_utils.go b/app/abci_utils.go
index c1a3a661..ec75c3b6 100644
--- a/app/abci_utils.go
+++ b/app/abci_utils.go
@@ -2,6 +2,8 @@ package app
 
 import (
 	"fmt"
+	"math/big"
+	"sort"
 
 	"github.com/cockroachdb/errors"
 	abci "github.com/cometbft/cometbft/abci/types"
@@ -13,6 +15,8 @@ import (
 	"github.com/cosmos/cosmos-sdk/x/auth/signing"
 )
 
+const gasPriceSuggestionBlockNum int64 = 5
+
 type (
 	// GasTx defines the contract that a transaction with a gas limit must implement.
 	GasTx interface {
@@ -30,17 +34,22 @@ type (
 	// DefaultProposalHandler defines the default ABCI PrepareProposal and
 	// ProcessProposal handlers.
 	DefaultProposalHandler struct {
-		mempool    mempool.Mempool
-		txVerifier ProposalTxVerifier
-		txSelector TxSelector
+		mempool         mempool.Mempool
+		txVerifier      ProposalTxVerifier
+		txSelector      TxSelector
+		feemarketKeeper FeeMarketKeeper
+	}
+	FeeMarketKeeper interface {
+		SetSuggestionGasPrice(ctx sdk.Context, gas *big.Int)
 	}
 )
 
-func NewDefaultProposalHandler(mp mempool.Mempool, txVerifier ProposalTxVerifier) *DefaultProposalHandler {
+func NewDefaultProposalHandler(mp mempool.Mempool, txVerifier ProposalTxVerifier, feemarketKeeper FeeMarketKeeper) *DefaultProposalHandler {
 	return &DefaultProposalHandler{
-		mempool:    mp,
-		txVerifier: txVerifier,
-		txSelector: NewDefaultTxSelector(),
+		mempool:         mp,
+		txVerifier:      txVerifier,
+		txSelector:      NewDefaultTxSelector(),
+		feemarketKeeper: feemarketKeeper,
 	}
 }
 
@@ -97,11 +106,25 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
 			return abci.ResponsePrepareProposal{Txs: h.txSelector.SelectedTxs()}
 		}
 
+		gasPriceSlice := make([]*big.Int, 0, h.mempool.CountTx())
+
 		iterator := h.mempool.Select(ctx, req.Txs)
 		selectedTxsSignersSeqs := make(map[string]uint64)
 		var selectedTxsNums int
 		for iterator != nil {
 			memTx := iterator.Tx()
+
+			for _, msg := range memTx.GetMsgs() {
+				msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
+				if ok {
+					txData, err := evmtypes.UnpackTxData(msgEthTx.Data)
+					if err == nil {
+						gp := txData.GetGasPrice()
+						gasPriceSlice = append(gasPriceSlice, gp)
+					}
+				}
+			}
+
 			sigs, err := memTx.(signing.SigVerifiableTx).GetSignaturesV2()
 			if err != nil {
 				panic(fmt.Errorf("failed to get signatures: %w", err))
@@ -198,6 +221,28 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
 
 			iterator = iterator.Next()
 		}
+
+		sort.Slice(gasPriceSlice, func(i, j int) bool {
+			return gasPriceSlice[i].Cmp(gasPriceSlice[j]) > 0
+		})
+
+		remaing := gasPriceSuggestionBlockNum * int64(maxBlockGas)
+		for _, gp := range gasPriceSlice {
+			if remaing <= 0 {
+				h.feemarketKeeper.SetSuggestionGasPrice(ctx, gp)
+				break
+			}
+			remaing -= gp.Int64()
+		}
+
+		if remaing > 0 {
+			if len(gasPriceSlice) > 0 {
+				h.feemarketKeeper.SetSuggestionGasPrice(ctx, gasPriceSlice[len(gasPriceSlice)-1])
+			} else {
+				h.feemarketKeeper.SetSuggestionGasPrice(ctx, big.NewInt(0))
+			}
+		}
+
 		return abci.ResponsePrepareProposal{Txs: h.txSelector.SelectedTxs()}
 	}
 }
diff --git a/app/ante/ante_test.go b/app/ante/ante_test.go
index c96a5239..eb5aad35 100644
--- a/app/ante/ante_test.go
+++ b/app/ante/ante_test.go
@@ -52,13 +52,17 @@ func TestAppAnteHandler_AuthorizedMempool(t *testing.T) {
 
 	tApp := app.TestApp{
 		App: *app.NewApp(
-			log.NewNopLogger(),
-			tmdb.NewMemDB(),
 			chaincfg.DefaultNodeHome,
 			nil,
 			encodingConfig,
 			opts,
-			baseapp.SetChainID(app.TestChainId),
+			app.NewBaseApp(
+				log.NewNopLogger(),
+				tmdb.NewMemDB(),
+				encodingConfig,
+				baseapp.SetChainID(app.TestChainId),
+			),
+			nil,
 		),
 	}
 
diff --git a/app/app.go b/app/app.go
index b10fd016..49c71347 100644
--- a/app/app.go
+++ b/app/app.go
@@ -267,7 +267,7 @@ type App struct {
 	packetForwardKeeper   *packetforwardkeeper.Keeper
 	evmKeeper             *evmkeeper.Keeper
 	evmutilKeeper         evmutilkeeper.Keeper
-	feeMarketKeeper       feemarketkeeper.Keeper
+	feeMarketKeeper       *feemarketkeeper.Keeper
 	upgradeKeeper         upgradekeeper.Keeper
 	evidenceKeeper        evidencekeeper.Keeper
 	transferKeeper        ibctransferkeeper.Keeper
@@ -313,6 +313,7 @@ func NewApp(
 	encodingConfig chainparams.EncodingConfig,
 	options Options,
 	bApp *baseapp.BaseApp,
+	setAbciProposalHandler func(feemarketKeeper FeeMarketKeeper) sdk.PrepareProposalHandler,
 ) *App {
 	appCodec := encodingConfig.Marshaler
 	legacyAmino := encodingConfig.Amino
@@ -901,6 +902,10 @@ func NewApp(
 	app.SetBeginBlocker(app.BeginBlocker)
 	app.SetEndBlocker(app.EndBlocker)
 
+	if setAbciProposalHandler != nil {
+		abciProposalHandler := setAbciProposalHandler(app.feeMarketKeeper)
+		bApp.SetPrepareProposal(abciProposalHandler)
+	}
 	// load store
 	if !options.SkipLoadLatest {
 		if err := app.LoadLatestVersion(); err != nil {
diff --git a/app/app_test.go b/app/app_test.go
index 7ab5b262..becb9464 100644
--- a/app/app_test.go
+++ b/app/app_test.go
@@ -28,19 +28,36 @@ import (
 func TestNewApp(t *testing.T) {
 	chaincfg.SetSDKConfig()
 	NewApp(
-		log.NewTMLogger(log.NewSyncWriter(os.Stdout)),
-		db.NewMemDB(),
 		chaincfg.DefaultNodeHome,
 		nil,
 		MakeEncodingConfig(),
 		DefaultOptions,
+		NewBaseApp(
+			log.NewTMLogger(log.NewSyncWriter(os.Stdout)),
+			db.NewMemDB(),
+			MakeEncodingConfig(),
+			baseapp.SetChainID(TestChainId),
+		),
+		nil,
 	)
 }
 
 func TestExport(t *testing.T) {
 	chaincfg.SetSDKConfig()
 	db := db.NewMemDB()
-	app := NewApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, chaincfg.DefaultNodeHome, nil, MakeEncodingConfig(), DefaultOptions, baseapp.SetChainID(TestChainId))
+	app := NewApp(
+		chaincfg.DefaultNodeHome,
+		nil,
+		MakeEncodingConfig(),
+		DefaultOptions,
+		NewBaseApp(
+			log.NewTMLogger(log.NewSyncWriter(os.Stdout)),
+			db,
+			MakeEncodingConfig(),
+			baseapp.SetChainID(TestChainId),
+		),
+		nil,
+	)
 
 	genesisState := GenesisStateWithSingleValidator(&TestApp{App: *app}, NewDefaultGenesisState())
 
diff --git a/app/test_common.go b/app/test_common.go
index 52b527e0..6c469e4a 100644
--- a/app/test_common.go
+++ b/app/test_common.go
@@ -96,7 +96,7 @@ func NewTestAppFromSealed() TestApp {
 	bApp := NewBaseApp(log.NewNopLogger(), db, encCfg, baseapp.SetChainID(TestChainId))
 	app := NewApp(
 		chaincfg.DefaultNodeHome, nil,
-		encCfg, DefaultOptions, bApp,
+		encCfg, DefaultOptions, bApp, nil,
 	)
 	return TestApp{App: *app}
 }
@@ -117,7 +117,7 @@ func (tApp TestApp) GetPriceFeedKeeper() pricefeedkeeper.Keeper     { return tAp
 func (tApp TestApp) GetCommitteeKeeper() committeekeeper.Keeper     { return tApp.committeeKeeper }
 func (tApp TestApp) GetEvmutilKeeper() evmutilkeeper.Keeper         { return tApp.evmutilKeeper }
 func (tApp TestApp) GetEvmKeeper() *evmkeeper.Keeper                { return tApp.evmKeeper }
-func (tApp TestApp) GetFeeMarketKeeper() feemarketkeeper.Keeper     { return tApp.feeMarketKeeper }
+func (tApp TestApp) GetFeeMarketKeeper() *feemarketkeeper.Keeper    { return tApp.feeMarketKeeper }
 func (tApp TestApp) GetDASignersKeeper() dasignerskeeper.Keeper     { return tApp.dasignersKeeper }
 func (tApp TestApp) GetPrecisebankKeeper() precisebankkeeper.Keeper { return tApp.precisebankKeeper }
 func (tApp TestApp) GetWrappedA0GIBaseKeeper() wrappeda0gibasekeeper.Keeper {
diff --git a/app/upgrades_test.go b/app/upgrades_test.go
index d4783904..9e88d52a 100644
--- a/app/upgrades_test.go
+++ b/app/upgrades_test.go
@@ -1,434 +1 @@
 package app_test
-
-import (
-	"strconv"
-	"testing"
-	"time"
-
-	sdkmath "cosmossdk.io/math"
-	"github.com/0glabs/0g-chain/app"
-	evmutiltypes "github.com/0glabs/0g-chain/x/evmutil/types"
-	precisebankkeeper "github.com/0glabs/0g-chain/x/precisebank/keeper"
-	precisebanktypes "github.com/0glabs/0g-chain/x/precisebank/types"
-	tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
-	sdk "github.com/cosmos/cosmos-sdk/types"
-	"github.com/stretchr/testify/require"
-)
-
-func TestMigrateEvmutilToPrecisebank(t *testing.T) {
-	// Full test case with all components together
-	tests := []struct {
-		name               string
-		initialReserve     sdkmath.Int
-		fractionalBalances []sdkmath.Int
-	}{
-		{
-			"no fractional balances",
-			sdkmath.NewInt(0),
-			[]sdkmath.Int{},
-		},
-		{
-			"sufficient reserve, 0 remainder",
-			// Accounts adding up to 2 int units, same as reserve
-			sdkmath.NewInt(2),
-			[]sdkmath.Int{
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-			},
-		},
-		{
-			"insufficient reserve, 0 remainder",
-			// Accounts adding up to 2 int units, but only 1 int unit in reserve
-			sdkmath.NewInt(1),
-			[]sdkmath.Int{
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-			},
-		},
-		{
-			"excess reserve, 0 remainder",
-			// Accounts adding up to 2 int units, but 3 int unit in reserve
-			sdkmath.NewInt(3),
-			[]sdkmath.Int{
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-			},
-		},
-		{
-			"sufficient reserve, non-zero remainder",
-			// Accounts adding up to 1.5 int units, same as reserve
-			sdkmath.NewInt(2),
-			[]sdkmath.Int{
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-			},
-		},
-		{
-			"insufficient reserve, non-zero remainder",
-			// Accounts adding up to 1.5 int units, less than reserve,
-			// Reserve should be 2 and remainder 0.5
-			sdkmath.NewInt(1),
-			[]sdkmath.Int{
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-			},
-		},
-		{
-			"excess reserve, non-zero remainder",
-			// Accounts adding up to 1.5 int units, 3 int units in reserve
-			sdkmath.NewInt(3),
-			[]sdkmath.Int{
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			tApp := app.NewTestApp()
-			tApp.InitializeFromGenesisStates()
-			ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: time.Now()})
-
-			ak := tApp.GetAccountKeeper()
-			bk := tApp.GetBankKeeper()
-			evmuk := tApp.GetEvmutilKeeper()
-			pbk := tApp.GetPrecisebankKeeper()
-
-			reserveCoin := sdk.NewCoin(precisebanktypes.IntegerCoinDenom, tt.initialReserve)
-			err := bk.MintCoins(ctx, evmutiltypes.ModuleName, sdk.NewCoins(reserveCoin))
-			require.NoError(t, err)
-
-			oldReserveAddr := tApp.GetAccountKeeper().GetModuleAddress(evmutiltypes.ModuleName)
-			newReserveAddr := tApp.GetAccountKeeper().GetModuleAddress(precisebanktypes.ModuleName)
-
-			// Double check balances
-			oldReserveBalance := bk.GetBalance(ctx, oldReserveAddr, precisebanktypes.IntegerCoinDenom)
-			newReserveBalance := bk.GetBalance(ctx, newReserveAddr, precisebanktypes.IntegerCoinDenom)
-
-			require.Equal(t, tt.initialReserve, oldReserveBalance.Amount, "initial x/evmutil reserve balance")
-			require.True(t, newReserveBalance.IsZero(), "empty initial new reserve")
-
-			// Set accounts
-			for i, balance := range tt.fractionalBalances {
-				addr := sdk.AccAddress([]byte(strconv.Itoa(i)))
-
-				err := evmuk.SetBalance(ctx, addr, balance)
-				require.NoError(t, err)
-			}
-
-			// Run full x/evmutil -> x/precisebank migration
-			err = app.MigrateEvmutilToPrecisebank(
-				ctx,
-				ak,
-				bk,
-				evmuk,
-				pbk,
-			)
-			require.NoError(t, err)
-
-			// Check old reserve is empty
-			oldReserveBalanceAfter := bk.GetBalance(ctx, oldReserveAddr, precisebanktypes.IntegerCoinDenom)
-			require.True(t, oldReserveBalanceAfter.IsZero(), "old reserve should be empty")
-
-			// Check new reserve fully backs fractional balances
-			newReserveBalanceAfter := bk.GetBalance(ctx, newReserveAddr, precisebanktypes.IntegerCoinDenom)
-			fractionalBalanceTotal := pbk.GetTotalSumFractionalBalances(ctx)
-			remainder := pbk.GetRemainderAmount(ctx)
-
-			expectedReserveBal := fractionalBalanceTotal.Add(remainder)
-			require.Equal(
-				t,
-				expectedReserveBal,
-				newReserveBalanceAfter.Amount.Mul(precisebanktypes.ConversionFactor()),
-				"new reserve should equal total fractional balances",
-			)
-
-			// Check balances are deleted in evmutil and migrated to precisebank
-			for i := range tt.fractionalBalances {
-				addr := sdk.AccAddress([]byte(strconv.Itoa(i)))
-				acc := evmuk.GetAccount(ctx, addr)
-				require.Nil(t, acc, "account should be deleted")
-
-				balance := pbk.GetFractionalBalance(ctx, addr)
-				require.Equal(t, tt.fractionalBalances[i], balance, "balance should be migrated")
-			}
-
-			// Checks balances valid and remainder
-			res, stop := precisebankkeeper.AllInvariants(pbk)(ctx)
-			require.Falsef(t, stop, "invariants should pass: %s", res)
-		})
-	}
-}
-
-func TestTransferFractionalBalances(t *testing.T) {
-	tests := []struct {
-		name               string
-		fractionalBalances []sdkmath.Int
-	}{
-		{
-			"no fractional balances",
-			[]sdkmath.Int{},
-		},
-		{
-			"balanced fractional balances",
-			[]sdkmath.Int{
-				// 4 accounts
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-			},
-		},
-		{
-			"unbalanced balances",
-			[]sdkmath.Int{
-				// 3 accounts
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			tApp := app.NewTestApp()
-			tApp.InitializeFromGenesisStates()
-			ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: time.Now()})
-
-			evmutilk := tApp.GetEvmutilKeeper()
-			pbk := tApp.GetPrecisebankKeeper()
-
-			for i, balance := range tt.fractionalBalances {
-				addr := sdk.AccAddress([]byte(strconv.Itoa(i)))
-
-				err := evmutilk.SetBalance(ctx, addr, balance)
-				require.NoError(t, err)
-			}
-
-			// Run balance transfer
-			aggregateSum, err := app.TransferFractionalBalances(
-				ctx,
-				evmutilk,
-				pbk,
-			)
-			require.NoError(t, err)
-
-			// Check balances are deleted in evmutil and migrated to precisebank
-			sum := sdkmath.ZeroInt()
-			for i := range tt.fractionalBalances {
-				sum = sum.Add(tt.fractionalBalances[i])
-
-				addr := sdk.AccAddress([]byte(strconv.Itoa(i)))
-				acc := evmutilk.GetAccount(ctx, addr)
-				require.Nil(t, acc, "account should be deleted")
-
-				balance := pbk.GetFractionalBalance(ctx, addr)
-				require.Equal(t, tt.fractionalBalances[i], balance, "balance should be migrated")
-			}
-
-			require.Equal(t, sum, aggregateSum, "aggregate sum should be correct")
-		})
-	}
-}
-
-func TestInitializeRemainder(t *testing.T) {
-	tests := []struct {
-		name             string
-		giveAggregateSum sdkmath.Int
-		wantRemainder    sdkmath.Int
-	}{
-		{
-			"0 remainder, 1ukava",
-			precisebanktypes.ConversionFactor(),
-			sdkmath.NewInt(0),
-		},
-		{
-			"0 remainder, multiple ukava",
-			precisebanktypes.ConversionFactor().MulRaw(5),
-			sdkmath.NewInt(0),
-		},
-		{
-			"non-zero remainder, min",
-			precisebanktypes.ConversionFactor().SubRaw(1),
-			sdkmath.NewInt(1),
-		},
-		{
-			"non-zero remainder, max",
-			sdkmath.NewInt(1),
-			precisebanktypes.ConversionFactor().SubRaw(1),
-		},
-		{
-			"non-zero remainder, half",
-			precisebanktypes.ConversionFactor().QuoRaw(2),
-			precisebanktypes.ConversionFactor().QuoRaw(2),
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			tApp := app.NewTestApp()
-			tApp.InitializeFromGenesisStates()
-
-			pbk := tApp.GetPrecisebankKeeper()
-
-			ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: time.Now()})
-
-			remainder := app.InitializeRemainder(
-				ctx,
-				tApp.GetPrecisebankKeeper(),
-				tt.giveAggregateSum,
-			)
-			require.Equal(t, tt.wantRemainder, remainder)
-
-			// Check actual state
-			remainderAfter := pbk.GetRemainderAmount(ctx)
-			require.Equal(t, tt.wantRemainder, remainderAfter)
-
-			// Not checking invariants here since it requires actual balance state
-			aggregateSumWithRemainder := tt.giveAggregateSum.Add(remainder)
-			require.True(
-				t,
-				aggregateSumWithRemainder.
-					Mod(precisebanktypes.ConversionFactor()).
-					IsZero(),
-				"remainder + aggregate sum should be a multiple of the conversion factor",
-			)
-		})
-	}
-}
-
-func TestTransferFractionalBalanceReserve(t *testing.T) {
-	tests := []struct {
-		name               string
-		initialReserve     sdk.Coin
-		fractionalBalances []sdkmath.Int
-	}{
-		{
-			"balanced reserve, no remainder",
-			sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(1)),
-			[]sdkmath.Int{
-				// 2 accounts
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-			},
-		},
-		{
-			"insufficient reserve",
-			sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(1)),
-			[]sdkmath.Int{
-				// 4 accounts, total 2 int units
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-			},
-		},
-		{
-			"extra reserve funds",
-			sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(2)),
-			[]sdkmath.Int{
-				// 2 accounts, total 1 int units
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-			},
-		},
-		{
-			"insufficient reserve, with remainder",
-			sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(1)),
-			[]sdkmath.Int{
-				// 5 accounts, total 2.5 int units
-				// Expected 3 int units in reserve, 0.5 remainder
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-			},
-		},
-		{
-			"extra reserve funds, with remainder",
-			sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(3)),
-			[]sdkmath.Int{
-				// 3 accounts, total 1.5 int units.
-				// Expected 2 int units in reserve, 0.5 remainder
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-				precisebanktypes.ConversionFactor().QuoRaw(2),
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			tApp := app.NewTestApp()
-			tApp.InitializeFromGenesisStates()
-			ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: time.Now()})
-
-			bk := tApp.GetBankKeeper()
-			pbk := tApp.GetPrecisebankKeeper()
-			err := bk.MintCoins(ctx, evmutiltypes.ModuleName, sdk.NewCoins(tt.initialReserve))
-			require.NoError(t, err)
-
-			oldReserveAddr := tApp.GetAccountKeeper().GetModuleAddress(evmutiltypes.ModuleName)
-			newReserveAddr := tApp.GetAccountKeeper().GetModuleAddress(precisebanktypes.ModuleName)
-
-			// Double check balances
-			oldReserveBalance := bk.GetBalance(ctx, oldReserveAddr, precisebanktypes.IntegerCoinDenom)
-			newReserveBalance := bk.GetBalance(ctx, newReserveAddr, precisebanktypes.IntegerCoinDenom)
-
-			require.Equal(t, tt.initialReserve, oldReserveBalance)
-			require.True(t, newReserveBalance.IsZero(), "empty initial new reserve")
-
-			for i, balance := range tt.fractionalBalances {
-				addr := sdk.AccAddress([]byte{byte(i)})
-
-				require.NotPanics(t, func() {
-					pbk.SetFractionalBalance(ctx, addr, balance)
-				}, "given fractional balances should be valid")
-			}
-
-			// Run reserve migration
-			err = app.TransferFractionalBalanceReserve(
-				ctx,
-				tApp.GetAccountKeeper(),
-				bk,
-				tApp.GetPrecisebankKeeper(),
-			)
-			require.NoError(t, err)
-
-			// Check old reserve is empty
-			oldReserveBalanceAfter := bk.GetBalance(ctx, oldReserveAddr, precisebanktypes.IntegerCoinDenom)
-			require.True(t, oldReserveBalanceAfter.IsZero(), "old reserve should be empty")
-
-			// Check new reserve fully backs fractional balances
-			newReserveBalanceAfter := bk.GetBalance(ctx, newReserveAddr, precisebanktypes.IntegerCoinDenom)
-			fractionalBalanceTotal := pbk.GetTotalSumFractionalBalances(ctx)
-
-			expectedReserveBal := fractionalBalanceTotal.
-				Quo(precisebanktypes.ConversionFactor())
-
-			// Check if theres a remainder
-			if fractionalBalanceTotal.Mod(precisebanktypes.ConversionFactor()).IsPositive() {
-				expectedReserveBal = expectedReserveBal.Add(sdkmath.OneInt())
-			}
-
-			require.Equal(
-				t,
-				expectedReserveBal,
-				newReserveBalanceAfter.Amount,
-				"new reserve should equal total fractional balances + remainder",
-			)
-		})
-	}
-}
diff --git a/cmd/0gchaind/app.go b/cmd/0gchaind/app.go
index f09ec847..d8d3d10b 100644
--- a/cmd/0gchaind/app.go
+++ b/cmd/0gchaind/app.go
@@ -126,8 +126,6 @@ func (ac appCreator) newApp(
 		baseapp.SetMempool(mempool),
 	)
 	bApp.SetTxEncoder(ac.encodingConfig.TxConfig.TxEncoder())
-	abciProposalHandler := app.NewDefaultProposalHandler(mempool, bApp)
-	bApp.SetPrepareProposal(abciProposalHandler.PrepareProposalHandler())
 
 	newApp := app.NewApp(
 		homeDir, traceStore, ac.encodingConfig,
@@ -142,6 +140,10 @@ func (ac appCreator) newApp(
 			EVMMaxGasWanted:       cast.ToUint64(appOpts.Get(ethermintflags.EVMMaxTxGasWanted)),
 		},
 		bApp,
+		func(k app.FeeMarketKeeper) sdk.PrepareProposalHandler {
+			abciProposalHandler := app.NewDefaultProposalHandler(mempool, bApp, k)
+			return abciProposalHandler.PrepareProposalHandler()
+		},
 	)
 
 	return newApp
@@ -170,14 +172,14 @@ func (ac appCreator) appExport(
 	var tempApp *app.App
 	if height != -1 {
 		bApp := app.NewBaseApp(logger, db, ac.encodingConfig)
-		tempApp = app.NewApp(homePath, traceStore, ac.encodingConfig, options, bApp)
+		tempApp = app.NewApp(homePath, traceStore, ac.encodingConfig, options, bApp, nil)
 
 		if err := tempApp.LoadHeight(height); err != nil {
 			return servertypes.ExportedApp{}, err
 		}
 	} else {
 		bApp := app.NewBaseApp(logger, db, ac.encodingConfig)
-		tempApp = app.NewApp(homePath, traceStore, ac.encodingConfig, options, bApp)
+		tempApp = app.NewApp(homePath, traceStore, ac.encodingConfig, options, bApp, nil)
 	}
 	return tempApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport)
 }

From 6df54c84b5b1811c95ef38204d6f8e140d02bb18 Mon Sep 17 00:00:00 2001
From: Solovyov1796 <xinyu@0g.ai>
Date: Mon, 10 Feb 2025 14:37:15 +0800
Subject: [PATCH 2/8] update suggetsion gas price code

---
 app/abci_utils.go | 132 ++++++++++++++++++++++++++++++++++++----------
 1 file changed, 105 insertions(+), 27 deletions(-)

diff --git a/app/abci_utils.go b/app/abci_utils.go
index ec75c3b6..9f73565f 100644
--- a/app/abci_utils.go
+++ b/app/abci_utils.go
@@ -5,6 +5,7 @@ import (
 	"math/big"
 	"sort"
 
+	"github.com/0glabs/0g-chain/chaincfg"
 	"github.com/cockroachdb/errors"
 	abci "github.com/cometbft/cometbft/abci/types"
 	gethtypes "github.com/ethereum/go-ethereum/core/types"
@@ -42,6 +43,13 @@ type (
 	FeeMarketKeeper interface {
 		SetSuggestionGasPrice(ctx sdk.Context, gas *big.Int)
 	}
+
+	txnInfo struct {
+		gasPrice *big.Int
+		gasLimit uint64
+		nonce    uint64
+		sender   string
+	}
 )
 
 func NewDefaultProposalHandler(mp mempool.Mempool, txVerifier ProposalTxVerifier, feemarketKeeper FeeMarketKeeper) *DefaultProposalHandler {
@@ -106,7 +114,7 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
 			return abci.ResponsePrepareProposal{Txs: h.txSelector.SelectedTxs()}
 		}
 
-		gasPriceSlice := make([]*big.Int, 0, h.mempool.CountTx())
+		txnInfoMap := make(map[string][]txnInfo, h.mempool.CountTx())
 
 		iterator := h.mempool.Select(ctx, req.Txs)
 		selectedTxsSignersSeqs := make(map[string]uint64)
@@ -114,17 +122,6 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
 		for iterator != nil {
 			memTx := iterator.Tx()
 
-			for _, msg := range memTx.GetMsgs() {
-				msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
-				if ok {
-					txData, err := evmtypes.UnpackTxData(msgEthTx.Data)
-					if err == nil {
-						gp := txData.GetGasPrice()
-						gasPriceSlice = append(gasPriceSlice, gp)
-					}
-				}
-			}
-
 			sigs, err := memTx.(signing.SigVerifiableTx).GetSignaturesV2()
 			if err != nil {
 				panic(fmt.Errorf("failed to get signatures: %w", err))
@@ -157,10 +154,42 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
 									txSignersSeqs[signer] = nonce
 								}
 							}
+
+							if _, exists := txnInfoMap[signer]; !exists {
+								txnInfoMap[signer] = make([]txnInfo, 0, 128)
+							}
+
+							txnInfoMap[signer] = append(txnInfoMap[signer], txnInfo{
+								gasPrice: ethTx.GasPrice(),
+								gasLimit: ethTx.Gas(),
+								nonce:    nonce,
+								sender:   signer,
+							})
 						}
 					}
 				}
 			} else {
+				// ignore multisig case now
+				fee := memTx.(sdk.Fee)
+				if len(sigs) == 1 {
+					signer := sdk.AccAddress(sigs[0].PubKey.Address()).String()
+
+					if _, exists := txnInfoMap[signer]; !exists {
+						txnInfoMap[signer] = make([]txnInfo, 0, 16)
+					}
+
+					evmGasPrice, err := utilCosmosDemonGasPriceToEvmDemonGasPrice(fee.GetAmount())
+
+					if err == nil {
+						txnInfoMap[signer] = append(txnInfoMap[signer], txnInfo{
+							gasPrice: evmGasPrice,
+							gasLimit: utilCosmosDemonGasLimitToEvmDemonGasLimit(fee.GetGas()),
+							nonce:    sigs[0].Sequence,
+							sender:   signer,
+						})
+					}
+				}
+
 				for _, sig := range sigs {
 					signer := sdk.AccAddress(sig.PubKey.Address()).String()
 					seq, ok := selectedTxsSignersSeqs[signer]
@@ -222,23 +251,53 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
 			iterator = iterator.Next()
 		}
 
-		sort.Slice(gasPriceSlice, func(i, j int) bool {
-			return gasPriceSlice[i].Cmp(gasPriceSlice[j]) > 0
-		})
-
-		remaing := gasPriceSuggestionBlockNum * int64(maxBlockGas)
-		for _, gp := range gasPriceSlice {
-			if remaing <= 0 {
-				h.feemarketKeeper.SetSuggestionGasPrice(ctx, gp)
-				break
+		if len(txnInfoMap) == 0 {
+			h.feemarketKeeper.SetSuggestionGasPrice(ctx, big.NewInt(0))
+		} else {
+			senderCnt := 0
+			for sender := range txnInfoMap {
+				sort.Slice(txnInfoMap[sender], func(i, j int) bool {
+					return txnInfoMap[sender][i].nonce < txnInfoMap[sender][j].nonce
+				})
+				senderCnt++
 			}
-			remaing -= gp.Int64()
-		}
 
-		if remaing > 0 {
-			if len(gasPriceSlice) > 0 {
-				h.feemarketKeeper.SetSuggestionGasPrice(ctx, gasPriceSlice[len(gasPriceSlice)-1])
-			} else {
+			remaing := gasPriceSuggestionBlockNum * int64(maxBlockGas)
+			for senderCnt > 0 && remaing > 0 {
+				// peek top
+				txnInfoSlice := make([]txnInfo, 0, senderCnt)
+				for sender := range txnInfoMap {
+					txnInfoSlice = append(txnInfoSlice, txnInfoMap[sender][0])
+				}
+
+				sort.Slice(txnInfoSlice, func(i, j int) bool {
+					return txnInfoSlice[i].gasPrice.Cmp(txnInfoSlice[j].gasPrice) > 0
+				})
+
+				for i := range txnInfoSlice {
+					remaing -= int64(txnInfoSlice[i].gasLimit)
+					if remaing <= 0 {
+						h.feemarketKeeper.SetSuggestionGasPrice(ctx, txnInfoSlice[i].gasPrice)
+						break
+					}
+				}
+
+				// pop
+				if remaing > 0 {
+					senderCnt = 0
+					for sender := range txnInfoMap {
+						cnt := len(txnInfoMap[sender])
+						if cnt > 1 {
+							txnInfoMap[sender] = txnInfoMap[sender][1:]
+							senderCnt++
+						} else {
+							delete(txnInfoMap, sender)
+						}
+					}
+				}
+			}
+
+			if remaing > 0 {
 				h.feemarketKeeper.SetSuggestionGasPrice(ctx, big.NewInt(0))
 			}
 		}
@@ -381,3 +440,22 @@ func (ts *defaultTxSelector) SelectTxForProposal(maxTxBytes, maxBlockGas uint64,
 	// check if we've reached capacity; if so, we cannot select any more transactions
 	return ts.totalTxBytes >= maxTxBytes || (maxBlockGas > 0 && (ts.totalTxGas >= maxBlockGas))
 }
+
+func utilCosmosDemonGasPriceToEvmDemonGasPrice(gasGroup sdk.Coins) (*big.Int, error) {
+	gasPrice := big.NewInt(0)
+	for _, coin := range gasGroup {
+		if coin.Denom == chaincfg.GasDenom {
+			thisGasPrice := big.NewInt(0).SetUint64(coin.Amount.Uint64())
+			thisGasPrice = thisGasPrice.Mul(thisGasPrice, big.NewInt(0).SetInt64(chaincfg.GasDenomConversionMultiplier))
+			gasPrice = gasPrice.Add(gasPrice, thisGasPrice)
+		} else {
+			return big.NewInt(0), fmt.Errorf("invalid denom: %s", coin.Denom)
+		}
+	}
+
+	return gasPrice, nil
+}
+
+func utilCosmosDemonGasLimitToEvmDemonGasLimit(gasLimit uint64) uint64 {
+	return gasLimit * chaincfg.GasDenomConversionMultiplier
+}

From 7900795654e4d6ef4660344a8c5c854eac3decea Mon Sep 17 00:00:00 2001
From: Solovyov1796 <xinyu@0g.ai>
Date: Mon, 10 Feb 2025 17:55:58 +0800
Subject: [PATCH 3/8] update

---
 app/abci_utils.go | 79 ++++++++++++++++++++++++++++-------------------
 1 file changed, 48 insertions(+), 31 deletions(-)

diff --git a/app/abci_utils.go b/app/abci_utils.go
index 9f73565f..1e883feb 100644
--- a/app/abci_utils.go
+++ b/app/abci_utils.go
@@ -114,7 +114,7 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
 			return abci.ResponsePrepareProposal{Txs: h.txSelector.SelectedTxs()}
 		}
 
-		txnInfoMap := make(map[string][]txnInfo, h.mempool.CountTx())
+		txnInfoMap := make(map[string][]*txnInfo, h.mempool.CountTx())
 
 		iterator := h.mempool.Select(ctx, req.Txs)
 		selectedTxsSignersSeqs := make(map[string]uint64)
@@ -156,10 +156,10 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
 							}
 
 							if _, exists := txnInfoMap[signer]; !exists {
-								txnInfoMap[signer] = make([]txnInfo, 0, 128)
+								txnInfoMap[signer] = make([]*txnInfo, 0, 128)
 							}
 
-							txnInfoMap[signer] = append(txnInfoMap[signer], txnInfo{
+							txnInfoMap[signer] = append(txnInfoMap[signer], &txnInfo{
 								gasPrice: ethTx.GasPrice(),
 								gasLimit: ethTx.Gas(),
 								nonce:    nonce,
@@ -175,13 +175,13 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
 					signer := sdk.AccAddress(sigs[0].PubKey.Address()).String()
 
 					if _, exists := txnInfoMap[signer]; !exists {
-						txnInfoMap[signer] = make([]txnInfo, 0, 16)
+						txnInfoMap[signer] = make([]*txnInfo, 0, 16)
 					}
 
 					evmGasPrice, err := utilCosmosDemonGasPriceToEvmDemonGasPrice(fee.GetAmount())
 
 					if err == nil {
-						txnInfoMap[signer] = append(txnInfoMap[signer], txnInfo{
+						txnInfoMap[signer] = append(txnInfoMap[signer], &txnInfo{
 							gasPrice: evmGasPrice,
 							gasLimit: utilCosmosDemonGasLimitToEvmDemonGasLimit(fee.GetGas()),
 							nonce:    sigs[0].Sequence,
@@ -255,44 +255,39 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
 			h.feemarketKeeper.SetSuggestionGasPrice(ctx, big.NewInt(0))
 		} else {
 			senderCnt := 0
+			txnCnt := 0
 			for sender := range txnInfoMap {
 				sort.Slice(txnInfoMap[sender], func(i, j int) bool {
 					return txnInfoMap[sender][i].nonce < txnInfoMap[sender][j].nonce
 				})
+				txnCnt += len(txnInfoMap[sender])
 				senderCnt++
 			}
 
 			remaing := gasPriceSuggestionBlockNum * int64(maxBlockGas)
-			for senderCnt > 0 && remaing > 0 {
-				// peek top
-				txnInfoSlice := make([]txnInfo, 0, senderCnt)
+			for len(txnInfoMap) > 0 && remaing > 0 {
+
+				// peek each sender's top
+				sortSlice := make([]*txnInfo, 0, senderCnt)
 				for sender := range txnInfoMap {
-					txnInfoSlice = append(txnInfoSlice, txnInfoMap[sender][0])
-				}
-
-				sort.Slice(txnInfoSlice, func(i, j int) bool {
-					return txnInfoSlice[i].gasPrice.Cmp(txnInfoSlice[j].gasPrice) > 0
-				})
-
-				for i := range txnInfoSlice {
-					remaing -= int64(txnInfoSlice[i].gasLimit)
-					if remaing <= 0 {
-						h.feemarketKeeper.SetSuggestionGasPrice(ctx, txnInfoSlice[i].gasPrice)
-						break
+					endIndex := findFirstContinuousDecreasingSubsequence(txnInfoMap[sender])
+					appendSlice := txnInfoMap[sender][:endIndex]
+					sortSlice = append(sortSlice, appendSlice...)
+					txnInfoMap[sender] = txnInfoMap[sender][endIndex:]
+					if len(txnInfoMap[sender]) == 0 {
+						delete(txnInfoMap, sender)
 					}
 				}
 
-				// pop
-				if remaing > 0 {
-					senderCnt = 0
-					for sender := range txnInfoMap {
-						cnt := len(txnInfoMap[sender])
-						if cnt > 1 {
-							txnInfoMap[sender] = txnInfoMap[sender][1:]
-							senderCnt++
-						} else {
-							delete(txnInfoMap, sender)
-						}
+				sort.Slice(sortSlice, func(i, j int) bool {
+					return sortSlice[i].gasPrice.Cmp(sortSlice[j].gasPrice) > 0
+				})
+
+				for i := range sortSlice {
+					remaing -= int64(sortSlice[i].gasLimit)
+					if remaing <= 0 {
+						h.feemarketKeeper.SetSuggestionGasPrice(ctx, sortSlice[i].gasPrice)
+						break
 					}
 				}
 			}
@@ -459,3 +454,25 @@ func utilCosmosDemonGasPriceToEvmDemonGasPrice(gasGroup sdk.Coins) (*big.Int, er
 func utilCosmosDemonGasLimitToEvmDemonGasLimit(gasLimit uint64) uint64 {
 	return gasLimit * chaincfg.GasDenomConversionMultiplier
 }
+
+func findFirstContinuousDecreasingSubsequence(data []*txnInfo) int {
+	ll := len(data)
+	if ll < 2 {
+		return ll
+	}
+
+	for i := 0; i < ll-1; i++ {
+		if data[i].gasPrice.Cmp(data[i+1].gasPrice) >= 0 {
+			end := i + 1
+			for ; end < ll && data[end-1].gasPrice.Cmp(data[end].gasPrice) > 0; end++ {
+			}
+			if end == ll || data[end-1].gasPrice.Cmp(data[end].gasPrice) <= 0 {
+				return end
+			}
+		} else {
+			return i + 1
+		}
+	}
+
+	return ll
+}

From d4066b6a3d74b0d70c7870c17c8af68a1790e9ea Mon Sep 17 00:00:00 2001
From: Solovyov1796 <xinyu@0g.ai>
Date: Mon, 10 Feb 2025 19:35:13 +0800
Subject: [PATCH 4/8] using merge sort

---
 app/abci_utils.go | 44 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 35 insertions(+), 9 deletions(-)

diff --git a/app/abci_utils.go b/app/abci_utils.go
index 1e883feb..b563b5d4 100644
--- a/app/abci_utils.go
+++ b/app/abci_utils.go
@@ -267,26 +267,32 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
 			remaing := gasPriceSuggestionBlockNum * int64(maxBlockGas)
 			for len(txnInfoMap) > 0 && remaing > 0 {
 
-				// peek each sender's top
-				sortSlice := make([]*txnInfo, 0, senderCnt)
+				// pop each sender's first continuous decreasing subsequence
+				txnCnt := 0
+				senderNonceSortedSliceGroup := make([][]*txnInfo, 0, senderCnt)
 				for sender := range txnInfoMap {
 					endIndex := findFirstContinuousDecreasingSubsequence(txnInfoMap[sender])
 					appendSlice := txnInfoMap[sender][:endIndex]
-					sortSlice = append(sortSlice, appendSlice...)
+					senderNonceSortedSliceGroup = append(senderNonceSortedSliceGroup, appendSlice)
+					txnCnt += len(appendSlice)
 					txnInfoMap[sender] = txnInfoMap[sender][endIndex:]
 					if len(txnInfoMap[sender]) == 0 {
 						delete(txnInfoMap, sender)
 					}
 				}
 
-				sort.Slice(sortSlice, func(i, j int) bool {
-					return sortSlice[i].gasPrice.Cmp(sortSlice[j].gasPrice) > 0
-				})
+				var gasPriceSortedSlice []*txnInfo
+				if len(senderNonceSortedSliceGroup) > 0 {
+					gasPriceSortedSlice = make([]*txnInfo, 0, len(senderNonceSortedSliceGroup[0]))
+					for i := range senderNonceSortedSliceGroup {
+						gasPriceSortedSlice = mergeSort(len(gasPriceSortedSlice)+len(senderNonceSortedSliceGroup[i]), gasPriceSortedSlice, senderNonceSortedSliceGroup[i])
+					}
+				}
 
-				for i := range sortSlice {
-					remaing -= int64(sortSlice[i].gasLimit)
+				for i := range gasPriceSortedSlice {
+					remaing -= int64(gasPriceSortedSlice[i].gasLimit)
 					if remaing <= 0 {
-						h.feemarketKeeper.SetSuggestionGasPrice(ctx, sortSlice[i].gasPrice)
+						h.feemarketKeeper.SetSuggestionGasPrice(ctx, gasPriceSortedSlice[i].gasPrice)
 						break
 					}
 				}
@@ -476,3 +482,23 @@ func findFirstContinuousDecreasingSubsequence(data []*txnInfo) int {
 
 	return ll
 }
+
+func mergeSort(size int, left, right []*txnInfo) []*txnInfo {
+	result := make([]*txnInfo, 0, size)
+	i, j := 0, 0
+
+	for i < len(left) && j < len(right) {
+		if left[i].gasPrice.Cmp(right[j].gasPrice) > 0 {
+			result = append(result, left[i])
+			i++
+		} else {
+			result = append(result, right[j])
+			j++
+		}
+	}
+
+	result = append(result, left[i:]...)
+	result = append(result, right[j:]...)
+
+	return result
+}

From bbd02215f7ffaf92372e3cc074e897ad5006882b Mon Sep 17 00:00:00 2001
From: 0g-wh <wenhui.xing@0g.ai>
Date: Mon, 10 Feb 2025 14:23:18 +0000
Subject: [PATCH 5/8] update txn selection algo

---
 app/abci_utils.go | 56 ++++++++++++++++++++++++++---------------------
 1 file changed, 31 insertions(+), 25 deletions(-)

diff --git a/app/abci_utils.go b/app/abci_utils.go
index b563b5d4..0d51e4ae 100644
--- a/app/abci_utils.go
+++ b/app/abci_utils.go
@@ -265,41 +265,47 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
 			}
 
 			remaing := gasPriceSuggestionBlockNum * int64(maxBlockGas)
-			for len(txnInfoMap) > 0 && remaing > 0 {
+			var lastProcessedTx *txnInfo
 
-				// pop each sender's first continuous decreasing subsequence
-				txnCnt := 0
-				senderNonceSortedSliceGroup := make([][]*txnInfo, 0, senderCnt)
-				for sender := range txnInfoMap {
-					endIndex := findFirstContinuousDecreasingSubsequence(txnInfoMap[sender])
-					appendSlice := txnInfoMap[sender][:endIndex]
-					senderNonceSortedSliceGroup = append(senderNonceSortedSliceGroup, appendSlice)
-					txnCnt += len(appendSlice)
-					txnInfoMap[sender] = txnInfoMap[sender][endIndex:]
-					if len(txnInfoMap[sender]) == 0 {
+			for remaing > 0 && len(txnInfoMap) > 0 {
+				// Find the highest gas price among the first transaction of each account
+				var highestGasPrice *big.Int
+				var selectedSender string
+
+				// Compare first transaction (lowest nonce) from each account
+				for sender, txns := range txnInfoMap {
+					if len(txns) == 0 {
 						delete(txnInfoMap, sender)
+						continue
+					}
+
+					// First tx has lowest nonce due to earlier sorting
+					if highestGasPrice == nil || txns[0].gasPrice.Cmp(highestGasPrice) > 0 {
+						highestGasPrice = txns[0].gasPrice
+						selectedSender = sender
 					}
 				}
 
-				var gasPriceSortedSlice []*txnInfo
-				if len(senderNonceSortedSliceGroup) > 0 {
-					gasPriceSortedSlice = make([]*txnInfo, 0, len(senderNonceSortedSliceGroup[0]))
-					for i := range senderNonceSortedSliceGroup {
-						gasPriceSortedSlice = mergeSort(len(gasPriceSortedSlice)+len(senderNonceSortedSliceGroup[i]), gasPriceSortedSlice, senderNonceSortedSliceGroup[i])
-					}
+				if selectedSender == "" {
+					break
 				}
 
-				for i := range gasPriceSortedSlice {
-					remaing -= int64(gasPriceSortedSlice[i].gasLimit)
-					if remaing <= 0 {
-						h.feemarketKeeper.SetSuggestionGasPrice(ctx, gasPriceSortedSlice[i].gasPrice)
-						break
-					}
+				// Process the selected transaction
+				selectedTx := txnInfoMap[selectedSender][0]
+				remaing -= int64(selectedTx.gasLimit)
+				lastProcessedTx = selectedTx
+
+				// Remove processed transaction
+				txnInfoMap[selectedSender] = txnInfoMap[selectedSender][1:]
+				if len(txnInfoMap[selectedSender]) == 0 {
+					delete(txnInfoMap, selectedSender)
 				}
 			}
 
-			if remaing > 0 {
-				h.feemarketKeeper.SetSuggestionGasPrice(ctx, big.NewInt(0))
+			if lastProcessedTx != nil && remaing <= 0 {
+				h.feemarketKeeper.SetSuggestionGasPrice(ctx, lastProcessedTx.gasPrice)
+			} else {
+				h.feemarketKeeper.SetSuggestionGasPrice(ctx, big.NewInt(1))
 			}
 		}
 

From 8ecfda7bd2bb3bac80b71ddf9d97cb58c30f5037 Mon Sep 17 00:00:00 2001
From: Solovyov1796 <xinyu@0g.ai>
Date: Mon, 10 Feb 2025 22:23:46 +0800
Subject: [PATCH 6/8] update ethermint version

---
 go.mod | 2 +-
 go.sum | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/go.mod b/go.mod
index f26169f0..e8ee335e 100644
--- a/go.mod
+++ b/go.mod
@@ -250,7 +250,7 @@ replace (
 	// TODO: Tag before release
 	github.com/ethereum/go-ethereum => github.com/evmos/go-ethereum v1.10.26-evmos-rc2
 	// Use ethermint fork that respects min-gas-price with NoBaseFee true and london enabled, and includes eip712 support
-	github.com/evmos/ethermint => github.com/0glabs/ethermint v0.21.0-0g.v3.1.9
+	github.com/evmos/ethermint => github.com/0glabs/ethermint v0.21.0-0g.v3.1.10
 	// See https://github.com/cosmos/cosmos-sdk/pull/10401, https://github.com/cosmos/cosmos-sdk/commit/0592ba6158cd0bf49d894be1cef4faeec59e8320
 	github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.9.0
 	// Downgraded to avoid bugs in following commits which causes "version does not exist" errors
diff --git a/go.sum b/go.sum
index a56db589..f17c68f4 100644
--- a/go.sum
+++ b/go.sum
@@ -213,8 +213,8 @@ github.com/0glabs/cometbft v0.37.9-0glabs.1 h1:KQJG17Y21suKP3QNICLto4b5Ak73XbSmK
 github.com/0glabs/cometbft v0.37.9-0glabs.1/go.mod h1:j0Q3RqrCd+cztWCugs3obbzC4NyHGBPZZjtm/fWV00I=
 github.com/0glabs/cosmos-sdk v0.47.10-0glabs.10 h1:NJp0RwczHBO4EvrQdDxxftHOgUDBtNh7M/vpaG7wFtQ=
 github.com/0glabs/cosmos-sdk v0.47.10-0glabs.10/go.mod h1:KskIVnhXTFqrw7CDccMvx7To5KzUsOomIsQV7sPGOog=
-github.com/0glabs/ethermint v0.21.0-0g.v3.1.9 h1:oTHp7tSAqoML7D/+RXsBzA9DsbZ80MX93VJYjR/Akh4=
-github.com/0glabs/ethermint v0.21.0-0g.v3.1.9/go.mod h1:6e/gOcDLhvlDWK3JLJVBgki0gD6H4E1eG7l9byocgWA=
+github.com/0glabs/ethermint v0.21.0-0g.v3.1.10 h1:DE8hK2OJp9rKYdT2Y0G259/6nAFcOQi383l2e7/VFnE=
+github.com/0glabs/ethermint v0.21.0-0g.v3.1.10/go.mod h1:6e/gOcDLhvlDWK3JLJVBgki0gD6H4E1eG7l9byocgWA=
 github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
 github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM=

From a32dad8373a666cb376c5a4a5b31619dc56bf734 Mon Sep 17 00:00:00 2001
From: Solovyov1796 <xinyu@0g.ai>
Date: Mon, 10 Feb 2025 23:13:10 +0800
Subject: [PATCH 7/8] remove useless code

---
 app/abci_utils.go | 42 ------------------------------------------
 1 file changed, 42 deletions(-)

diff --git a/app/abci_utils.go b/app/abci_utils.go
index 0d51e4ae..c43e9938 100644
--- a/app/abci_utils.go
+++ b/app/abci_utils.go
@@ -466,45 +466,3 @@ func utilCosmosDemonGasPriceToEvmDemonGasPrice(gasGroup sdk.Coins) (*big.Int, er
 func utilCosmosDemonGasLimitToEvmDemonGasLimit(gasLimit uint64) uint64 {
 	return gasLimit * chaincfg.GasDenomConversionMultiplier
 }
-
-func findFirstContinuousDecreasingSubsequence(data []*txnInfo) int {
-	ll := len(data)
-	if ll < 2 {
-		return ll
-	}
-
-	for i := 0; i < ll-1; i++ {
-		if data[i].gasPrice.Cmp(data[i+1].gasPrice) >= 0 {
-			end := i + 1
-			for ; end < ll && data[end-1].gasPrice.Cmp(data[end].gasPrice) > 0; end++ {
-			}
-			if end == ll || data[end-1].gasPrice.Cmp(data[end].gasPrice) <= 0 {
-				return end
-			}
-		} else {
-			return i + 1
-		}
-	}
-
-	return ll
-}
-
-func mergeSort(size int, left, right []*txnInfo) []*txnInfo {
-	result := make([]*txnInfo, 0, size)
-	i, j := 0, 0
-
-	for i < len(left) && j < len(right) {
-		if left[i].gasPrice.Cmp(right[j].gasPrice) > 0 {
-			result = append(result, left[i])
-			i++
-		} else {
-			result = append(result, right[j])
-			j++
-		}
-	}
-
-	result = append(result, left[i:]...)
-	result = append(result, right[j:]...)
-
-	return result
-}

From a0426c63f759af044b49ae6c441d6928311f06e8 Mon Sep 17 00:00:00 2001
From: Solovyov1796 <xinyu@0g.ai>
Date: Mon, 10 Feb 2025 23:27:22 +0800
Subject: [PATCH 8/8] set  suggetsion gas price as 0 if not found

---
 app/abci_utils.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/abci_utils.go b/app/abci_utils.go
index c43e9938..dcc299cc 100644
--- a/app/abci_utils.go
+++ b/app/abci_utils.go
@@ -305,7 +305,7 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
 			if lastProcessedTx != nil && remaing <= 0 {
 				h.feemarketKeeper.SetSuggestionGasPrice(ctx, lastProcessedTx.gasPrice)
 			} else {
-				h.feemarketKeeper.SetSuggestionGasPrice(ctx, big.NewInt(1))
+				h.feemarketKeeper.SetSuggestionGasPrice(ctx, big.NewInt(0))
 			}
 		}