From 9a2a3eeed1e8329d95f248eace808b10707127e9 Mon Sep 17 00:00:00 2001
From: Alex Johnson <alex@skip.money>
Date: Sun, 16 Jun 2024 11:46:31 -0400
Subject: [PATCH] fix: don't fail post handler on simulate tx with no fee
 (#122)

* ok

* ok

* format

* ok

* clean readme

* retract

* mod bump
---
 README.md                    |  6 ++--
 go.mod                       |  7 ++--
 go.sum                       | 10 +++---
 x/feemarket/ante/fee.go      |  2 +-
 x/feemarket/ante/fee_test.go | 54 ++++++++++++++++++++++++++--
 x/feemarket/post/fee.go      | 19 ++++++----
 x/feemarket/post/fee_test.go | 68 ++++++++++++++++++++++++++++++++++++
 7 files changed, 146 insertions(+), 20 deletions(-)

diff --git a/README.md b/README.md
index 29acdee..e48b9be 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,11 @@
-# `x/feemarket` Specification
+# `x/feemarket` Module
 
 ## Abstract
 
-This document specifies the feemarket module.
-
 The feemarket module is an implementation of the Additive Increase Multiplicative Decrease (AIMD) EIP-1559
 feemarket. More information about the implementation can be found [here](./x/feemarket/README.md).
 
-## Upgrading to FeeMarket
+## Upgrading to Feemarket
 
 More information about upgrading your chain to `x/feemarket` can be found in our dedicated [guide](docs/UPGRADING.md).
 
diff --git a/go.mod b/go.mod
index d168268..c0225cc 100644
--- a/go.mod
+++ b/go.mod
@@ -29,14 +29,14 @@ require (
 	github.com/grpc-ecosystem/grpc-gateway v1.16.0
 	github.com/skip-mev/chaintestutil v0.0.0-20240514161515-056d7ba45610
 	github.com/spf13/cast v1.6.0
-	github.com/spf13/cobra v1.8.0
+	github.com/spf13/cobra v1.8.1
 	github.com/spf13/viper v1.19.0
 	github.com/stretchr/testify v1.9.0
 	github.com/vektra/mockery/v2 v2.43.2
 	golang.org/x/tools v0.22.0
 	google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3
 	google.golang.org/grpc v1.64.0
-	google.golang.org/protobuf v1.34.1
+	google.golang.org/protobuf v1.34.2
 	mvdan.cc/gofumpt v0.6.0
 	pgregory.net/rapid v1.1.0
 )
@@ -351,3 +351,6 @@ replace (
 	github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0
 	github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
 )
+
+// simulation and fee UX
+retract [v1.0.0, v1.0.2]
diff --git a/go.sum b/go.sum
index 9de85c3..3b7fea5 100644
--- a/go.sum
+++ b/go.sum
@@ -442,7 +442,7 @@ github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5n
 github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creachadair/atomicfile v0.3.3 h1:yJlDq8qk9QmD/6ol+jq1X4bcoLNVdYq95+owOnauziE=
 github.com/creachadair/atomicfile v0.3.3/go.mod h1:X1r9P4wigJlGkYJO1HXZREdkVn+b1yHrsBBMLSj7tak=
 github.com/creachadair/mtest v0.0.0-20231015022703-31f2ea539dce h1:BFjvg2Oq88/2DOcUFu1ScIwKUn7KJYYvLr6AeuCJD54=
@@ -1236,8 +1236,8 @@ github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
 github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
-github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
-github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
+github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
+github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -2026,8 +2026,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
 google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
-google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/x/feemarket/ante/fee.go b/x/feemarket/ante/fee.go
index 912624b..1e6c6dc 100644
--- a/x/feemarket/ante/fee.go
+++ b/x/feemarket/ante/fee.go
@@ -104,7 +104,7 @@ func (dfd feeMarketCheckDecorator) anteHandle(ctx sdk.Context, tx sdk.Tx, simula
 
 	var feeCoin sdk.Coin
 	if simulate && len(feeCoins) == 0 {
-		feeCoin = sdk.NewCoin(params.FeeDenom, sdkmath.NewInt(0))
+		feeCoin = sdk.NewCoin(params.FeeDenom, sdkmath.ZeroInt())
 	} else {
 		feeCoin = feeCoins[0]
 	}
diff --git a/x/feemarket/ante/fee_test.go b/x/feemarket/ante/fee_test.go
index c520035..69e36ab 100644
--- a/x/feemarket/ante/fee_test.go
+++ b/x/feemarket/ante/fee_test.go
@@ -5,7 +5,6 @@ import (
 	"testing"
 
 	"cosmossdk.io/math"
-
 	"github.com/cosmos/cosmos-sdk/testutil/testdata"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@@ -77,7 +76,7 @@ func TestAnteHandle(t *testing.T) {
 			ExpErr:   sdkerrors.ErrOutOfGas,
 		},
 		{
-			Name: "0 gas given should pass in simulate",
+			Name: "0 gas given should pass in simulate - no fee",
 			Malleate: func(suite *antesuite.TestSuite) antesuite.TestCaseArgs {
 				accs := suite.CreateTestAccounts(1)
 
@@ -93,6 +92,23 @@ func TestAnteHandle(t *testing.T) {
 			ExpPass:  true,
 			ExpErr:   nil,
 		},
+		{
+			Name: "0 gas given should pass in simulate - fee",
+			Malleate: func(suite *antesuite.TestSuite) antesuite.TestCaseArgs {
+				accs := suite.CreateTestAccounts(1)
+
+				return antesuite.TestCaseArgs{
+					Msgs:      []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())},
+					GasLimit:  0,
+					FeeAmount: validFee,
+				}
+			},
+			RunAnte:  true,
+			RunPost:  false,
+			Simulate: true,
+			ExpPass:  true,
+			ExpErr:   nil,
+		},
 		{
 			Name: "signer has enough funds, should pass",
 			Malleate: func(suite *antesuite.TestSuite) antesuite.TestCaseArgs {
@@ -125,6 +141,40 @@ func TestAnteHandle(t *testing.T) {
 			ExpPass:  true,
 			ExpErr:   nil,
 		},
+		{
+			Name: "no fee - fail",
+			Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs {
+				accs := s.CreateTestAccounts(1)
+
+				return antesuite.TestCaseArgs{
+					Msgs:      []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())},
+					GasLimit:  1000000000,
+					FeeAmount: nil,
+				}
+			},
+			RunAnte:  true,
+			RunPost:  true,
+			Simulate: false,
+			ExpPass:  false,
+			ExpErr:   types.ErrNoFeeCoins,
+		},
+		{
+			Name: "no gas limit - fail",
+			Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs {
+				accs := s.CreateTestAccounts(1)
+
+				return antesuite.TestCaseArgs{
+					Msgs:      []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())},
+					GasLimit:  0,
+					FeeAmount: nil,
+				}
+			},
+			RunAnte:  true,
+			RunPost:  true,
+			Simulate: false,
+			ExpPass:  false,
+			ExpErr:   sdkerrors.ErrInvalidGasLimit,
+		},
 	}
 
 	for _, tc := range testCases {
diff --git a/x/feemarket/post/fee.go b/x/feemarket/post/fee.go
index 18a5b24..0433d0b 100644
--- a/x/feemarket/post/fee.go
+++ b/x/feemarket/post/fee.go
@@ -2,9 +2,10 @@ package post
 
 import (
 	"bytes"
-	"cosmossdk.io/math"
 	"fmt"
 
+	"cosmossdk.io/math"
+
 	errorsmod "cosmossdk.io/errors"
 
 	sdk "github.com/cosmos/cosmos-sdk/types"
@@ -75,14 +76,20 @@ func (dfd FeeMarketDeductDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simul
 	feeCoins := feeTx.GetFee()
 	gas := ctx.GasMeter().GasConsumed() // use context gas consumed
 
-	if len(feeCoins) != 1 {
-		if len(feeCoins) == 0 {
-			return ctx, errorsmod.Wrapf(feemarkettypes.ErrNoFeeCoins, "got length %d", len(feeCoins))
-		}
+	if len(feeCoins) == 0 && !simulate {
+		return ctx, errorsmod.Wrapf(feemarkettypes.ErrNoFeeCoins, "got length %d", len(feeCoins))
+	}
+	if len(feeCoins) > 1 {
 		return ctx, errorsmod.Wrapf(feemarkettypes.ErrTooManyFeeCoins, "got length %d", len(feeCoins))
 	}
 
-	feeCoin := feeCoins[0]
+	var feeCoin sdk.Coin
+	if simulate && len(feeCoins) == 0 {
+		feeCoin = sdk.NewCoin(params.FeeDenom, math.ZeroInt())
+	} else {
+		feeCoin = feeCoins[0]
+	}
+
 	feeGas := int64(feeTx.GetGas())
 
 	var (
diff --git a/x/feemarket/post/fee_test.go b/x/feemarket/post/fee_test.go
index a23a5bd..4a6d5a0 100644
--- a/x/feemarket/post/fee_test.go
+++ b/x/feemarket/post/fee_test.go
@@ -343,6 +343,74 @@ func TestPostHandle(t *testing.T) {
 			ExpPass:  true,
 			ExpErr:   nil,
 		},
+		{
+			Name: "0 gas given should pass in simulate - no fee",
+			Malleate: func(suite *antesuite.TestSuite) antesuite.TestCaseArgs {
+				accs := suite.CreateTestAccounts(1)
+
+				return antesuite.TestCaseArgs{
+					Msgs:      []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())},
+					GasLimit:  0,
+					FeeAmount: nil,
+				}
+			},
+			RunAnte:  true,
+			RunPost:  false,
+			Simulate: true,
+			ExpPass:  true,
+			ExpErr:   nil,
+		},
+		{
+			Name: "0 gas given should pass in simulate - fee",
+			Malleate: func(suite *antesuite.TestSuite) antesuite.TestCaseArgs {
+				accs := suite.CreateTestAccounts(1)
+
+				return antesuite.TestCaseArgs{
+					Msgs:      []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())},
+					GasLimit:  0,
+					FeeAmount: validFee,
+				}
+			},
+			RunAnte:  true,
+			RunPost:  false,
+			Simulate: true,
+			ExpPass:  true,
+			ExpErr:   nil,
+		},
+		{
+			Name: "no fee - fail",
+			Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs {
+				accs := s.CreateTestAccounts(1)
+
+				return antesuite.TestCaseArgs{
+					Msgs:      []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())},
+					GasLimit:  1000000000,
+					FeeAmount: nil,
+				}
+			},
+			RunAnte:  true,
+			RunPost:  true,
+			Simulate: false,
+			ExpPass:  false,
+			ExpErr:   types.ErrNoFeeCoins,
+		},
+		{
+			Name: "no gas limit - fail",
+			Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs {
+				accs := s.CreateTestAccounts(1)
+
+				return antesuite.TestCaseArgs{
+					Msgs:      []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())},
+					GasLimit:  0,
+					FeeAmount: nil,
+				}
+			},
+			RunAnte:  true,
+			RunPost:  true,
+			Simulate: false,
+			ExpPass:  false,
+			ExpErr:   sdkerrors.ErrInvalidGasLimit,
+		},
 	}
 
 	for _, tc := range testCases {