diff --git a/app/antedecorators/gasless.go b/app/antedecorators/gasless.go index 16c6f3516c..56e6357640 100644 --- a/app/antedecorators/gasless.go +++ b/app/antedecorators/gasless.go @@ -21,7 +21,16 @@ func NewGaslessDecorator(wrapped []sdk.AnteDecorator, oracleKeeper oraclekeeper. func (gd GaslessDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { if !isTxGasless(tx, ctx, gd.oracleKeeper) { // if not gasless, then we use the wrappers - return sdk.ChainAnteDecorators(gd.wrapped...)(ctx, tx, simulate) + + // AnteHandle always takes a `next` so we need a no-op to execute only one handler at a time + terminatorHandler := func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { + return ctx, nil + } + // iterating instead of recursing the handler for readability + for _, handler := range gd.wrapped { + ctx, err = handler.AnteHandle(ctx, tx, simulate, terminatorHandler) + } + return next(ctx, tx, simulate) } gaslessMeter := sdk.NewInfiniteGasMeter() @@ -29,6 +38,10 @@ func (gd GaslessDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, } func isTxGasless(tx sdk.Tx, ctx sdk.Context, oracleKeeper oraclekeeper.Keeper) bool { + if len(tx.GetMsgs()) == 0 { + // empty TX shouldn't be gasless + return false + } for _, msg := range tx.GetMsgs() { switch m := msg.(type) { case *dextypes.MsgPlaceOrders: diff --git a/app/antedecorators/gasless_test.go b/app/antedecorators/gasless_test.go new file mode 100644 index 0000000000..f4e2ee752b --- /dev/null +++ b/app/antedecorators/gasless_test.go @@ -0,0 +1,55 @@ +package antedecorators_test + +import ( + "fmt" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/sei-protocol/sei-chain/app/antedecorators" + oraclekeeper "github.com/sei-protocol/sei-chain/x/oracle/keeper" + "github.com/stretchr/testify/require" +) + +var output = "" + +type FakeAnteDecoratorOne struct{} + +func (ad FakeAnteDecoratorOne) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + output = fmt.Sprintf("%sone", output) + return next(ctx, tx, simulate) +} + +type FakeAnteDecoratorTwo struct{} + +func (ad FakeAnteDecoratorTwo) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + output = fmt.Sprintf("%stwo", output) + return next(ctx, tx, simulate) +} + +type FakeAnteDecoratorThree struct{} + +func (ad FakeAnteDecoratorThree) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + output = fmt.Sprintf("%sthree", output) + return next(ctx, tx, simulate) +} + +type FakeTx struct{} + +func (tx FakeTx) GetMsgs() []sdk.Msg { + return []sdk.Msg{} +} + +func (tx FakeTx) ValidateBasic() error { + return nil +} + +func TestGaslessDecorator(t *testing.T) { + anteDecorators := []sdk.AnteDecorator{ + FakeAnteDecoratorOne{}, + antedecorators.NewGaslessDecorator([]sdk.AnteDecorator{FakeAnteDecoratorTwo{}}, oraclekeeper.Keeper{}), + FakeAnteDecoratorThree{}, + } + chainedHandler := sdk.ChainAnteDecorators(anteDecorators...) + chainedHandler(sdk.Context{}, FakeTx{}, false) + require.Equal(t, "onetwothree", output) +}