From 159b8221338874f2147a5b59d09abaa429bc2377 Mon Sep 17 00:00:00 2001 From: Michael Tsitrin Date: Sun, 18 Aug 2024 22:09:43 +0300 Subject: [PATCH 1/7] rollapp gauges upgrade habdler --- app/upgrades/v4/upgrade.go | 17 ++++++++++++++++- app/upgrades/v4/upgrade_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/app/upgrades/v4/upgrade.go b/app/upgrades/v4/upgrade.go index f80548933..57497b58b 100644 --- a/app/upgrades/v4/upgrade.go +++ b/app/upgrades/v4/upgrade.go @@ -26,6 +26,7 @@ import ( "github.com/dymensionxyz/dymension/v3/app/upgrades" delayedackkeeper "github.com/dymensionxyz/dymension/v3/x/delayedack/keeper" delayedacktypes "github.com/dymensionxyz/dymension/v3/x/delayedack/types" + incentiveskeeper "github.com/dymensionxyz/dymension/v3/x/incentives/keeper" rollappkeeper "github.com/dymensionxyz/dymension/v3/x/rollapp/keeper" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" sequencerkeeper "github.com/dymensionxyz/dymension/v3/x/sequencer/keeper" @@ -50,7 +51,9 @@ func CreateUpgradeHandler( } migrateSequencers(ctx, keepers.SequencerKeeper) - // TODO: create rollapp gauges for each existing rollapp + if err := migrateRollappGauges(ctx, keepers.RollappKeeper, keepers.IncentivesKeeper); err != nil { + return nil, err + } // Start running the module migrations logger.Debug("running module migrations ...") @@ -112,6 +115,18 @@ func migrateRollappParams(ctx sdk.Context, rollappkeeper *rollappkeeper.Keeper) rollappkeeper.SetParams(ctx, params) } +// migrateRollappGauges creates a gauge for each rollapp in the store +func migrateRollappGauges(ctx sdk.Context, rollappkeeper *rollappkeeper.Keeper, incentivizeKeeper *incentiveskeeper.Keeper) error { + rollapps := rollappkeeper.GetAllRollapps(ctx) + for _, rollapp := range rollapps { + _, err := incentivizeKeeper.CreateRollappGauge(ctx, rollapp.RollappId) + if err != nil { + return err + } + } + return nil +} + func migrateRollapps(ctx sdk.Context, rollappkeeper *rollappkeeper.Keeper) error { list := rollappkeeper.GetAllRollapps(ctx) for _, oldRollapp := range list { diff --git a/app/upgrades/v4/upgrade_test.go b/app/upgrades/v4/upgrade_test.go index f69e6e0c7..327b24deb 100644 --- a/app/upgrades/v4/upgrade_test.go +++ b/app/upgrades/v4/upgrade_test.go @@ -116,6 +116,10 @@ func (s *UpgradeTestSuite) TestUpgrade() { return } + // Check rollapp gauges + if err = s.validateRollappGaugesMigration(); err != nil { + return + } return }, expPass: true, @@ -183,6 +187,33 @@ func (s *UpgradeTestSuite) validateRollappsMigration(numRoll int) error { return nil } +// validate rollapp gauges +func (s *UpgradeTestSuite) validateRollappGaugesMigration() error { + rollappMap := make(map[string]struct{}) // Create a map to store rollapps + + rollapps := s.App.RollappKeeper.GetAllRollapps(s.Ctx) + for _, rollapp := range rollapps { + rollappMap[rollapp.RollappId] = struct{}{} // Store rollapp in the map + } + + gauges := s.App.IncentivesKeeper.GetActiveGauges(s.Ctx) + if len(gauges) == len(rollapps) { + return fmt.Errorf("rollapp gauges not created for all rollapps") + } + + for _, gauge := range gauges { + if gauge.GetRollapp() != nil { + rollappMap[gauge.GetRollapp().RollappId] = struct{}{} + } + } + // Check that for each rollapp there exists a rollapp gauge + if len(rollappMap) != len(rollapps) { + return fmt.Errorf("rollapp gauges not created for all rollapps") + } + + return nil +} + func (s *UpgradeTestSuite) validateSequencersMigration(numSeq int) error { expectSequencers := make([]sequencertypes.Sequencer, numSeq) for i, sequencer := range s.seedSequencers(numSeq) { From 60cd13ddaa27977b4f83db29d7bb7469b887e665 Mon Sep 17 00:00:00 2001 From: Michael Tsitrin Date: Sun, 18 Aug 2024 22:11:40 +0300 Subject: [PATCH 2/7] refactored fee charge. minor UT cleanup --- x/incentives/keeper/export_test.go | 5 -- x/incentives/keeper/gauge.go | 9 +-- x/incentives/keeper/gauge_rollapp.go | 9 +-- x/incentives/keeper/gauge_test.go | 97 -------------------------- x/incentives/keeper/msg_server.go | 51 +++++--------- x/incentives/keeper/msg_server_test.go | 22 ++---- x/incentives/types/gauge.go | 10 +-- 7 files changed, 31 insertions(+), 172 deletions(-) diff --git a/x/incentives/keeper/export_test.go b/x/incentives/keeper/export_test.go index 2dd0a69e3..4cc96a81f 100644 --- a/x/incentives/keeper/export_test.go +++ b/x/incentives/keeper/export_test.go @@ -35,8 +35,3 @@ func (k Keeper) MoveUpcomingGaugeToActiveGauge(ctx sdk.Context, gauge types.Gaug func (k Keeper) MoveActiveGaugeToFinishedGauge(ctx sdk.Context, gauge types.Gauge) error { return k.moveActiveGaugeToFinishedGauge(ctx, gauge) } - -// ChargeFeeIfSufficientFeeDenomBalance see chargeFeeIfSufficientFeeDenomBalance spec. -func (k Keeper) ChargeFeeIfSufficientFeeDenomBalance(ctx sdk.Context, address sdk.AccAddress, fee sdk.Int, gaugeCoins sdk.Coins) error { - return k.chargeFeeIfSufficientFeeDenomBalance(ctx, address, fee, gaugeCoins) -} diff --git a/x/incentives/keeper/gauge.go b/x/incentives/keeper/gauge.go index 41385a51a..08c2ee7bd 100644 --- a/x/incentives/keeper/gauge.go +++ b/x/incentives/keeper/gauge.go @@ -111,14 +111,7 @@ func (k Keeper) CreateGauge(ctx sdk.Context, isPerpetual bool, owner sdk.AccAddr return 0, fmt.Errorf("denom does not exist: %s", distrTo.Denom) } - gauge := types.Gauge{ - Id: k.GetLastGaugeID(ctx) + 1, - IsPerpetual: isPerpetual, - DistributeTo: &types.Gauge_Asset{Asset: &distrTo}, - Coins: coins, - StartTime: startTime, - NumEpochsPaidOver: numEpochsPaidOver, - } + gauge := types.NewAssetGauge(k.GetLastGaugeID(ctx)+1, isPerpetual, distrTo, coins, startTime, numEpochsPaidOver) if err := k.bk.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, gauge.Coins); err != nil { return 0, err diff --git a/x/incentives/keeper/gauge_rollapp.go b/x/incentives/keeper/gauge_rollapp.go index 5de9f4cfc..fd50c593c 100644 --- a/x/incentives/keeper/gauge_rollapp.go +++ b/x/incentives/keeper/gauge_rollapp.go @@ -16,14 +16,7 @@ func (k Keeper) CreateRollappGauge(ctx sdk.Context, rollappId string) (uint64, e return 0, fmt.Errorf("rollapp %s not found", rollappId) } - gauge := types.Gauge{ - Id: k.GetLastGaugeID(ctx) + 1, - IsPerpetual: true, - DistributeTo: &types.Gauge_Rollapp{ - Rollapp: &types.RollappGauge{RollappId: rollappId}, - }, - NumEpochsPaidOver: 1, - } + gauge := types.NewRollappGauge(k.GetLastGaugeID(ctx)+1, rollappId) err := k.setGauge(ctx, &gauge) if err != nil { diff --git a/x/incentives/keeper/gauge_test.go b/x/incentives/keeper/gauge_test.go index 1a1949a10..229a9a79d 100644 --- a/x/incentives/keeper/gauge_test.go +++ b/x/incentives/keeper/gauge_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" - apptesting "github.com/dymensionxyz/dymension/v3/app/apptesting" "github.com/dymensionxyz/dymension/v3/x/incentives/types" lockuptypes "github.com/osmosis-labs/osmosis/v15/x/lockup/types" @@ -226,99 +225,3 @@ func (suite *KeeperTestSuite) TestGaugeOperations() { } } } - -func (suite *KeeperTestSuite) TestChargeFeeIfSufficientFeeDenomBalance() { - const baseFee = int64(100) - - testcases := map[string]struct { - accountBalanceToFund sdk.Coin - feeToCharge int64 - gaugeCoins sdk.Coins - - expectError bool - }{ - "fee + base denom gauge coin == acount balance, success": { - accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), - feeToCharge: baseFee / 2, - gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), - }, - "fee + base denom gauge coin < acount balance, success": { - accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), - feeToCharge: baseFee/2 - 1, - gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), - }, - "fee + base denom gauge coin > acount balance, error": { - accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), - feeToCharge: baseFee/2 + 1, - gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), - expectError: true, - }, - "fee + base denom gauge coin < acount balance, custom values, success": { - accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(11793193112)), - feeToCharge: 55, - gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(328812))), - }, - "account funded with coins other than base denom, error": { - accountBalanceToFund: sdk.NewCoin("usdc", sdk.NewInt(baseFee)), - feeToCharge: baseFee, - gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), - expectError: true, - }, - "fee == account balance, no gauge coins, success": { - accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), - feeToCharge: baseFee, - }, - "gauge coins == account balance, no fee, success": { - accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), - gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee))), - }, - "fee == account balance, gauge coins in denom other than base, success": { - accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), - feeToCharge: baseFee, - gaugeCoins: sdk.NewCoins(sdk.NewCoin("usdc", sdk.NewInt(baseFee*2))), - }, - "fee + gauge coins == account balance, multiple gauge coins, one in denom other than base, success": { - accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), - feeToCharge: baseFee / 2, - gaugeCoins: sdk.NewCoins(sdk.NewCoin("usdc", sdk.NewInt(baseFee*2)), sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), - }, - } - - for name, tc := range testcases { - suite.Run(name, func() { - suite.SetupTest() - - err := suite.App.TxFeesKeeper.SetBaseDenom(suite.Ctx, "adym") - suite.Require().NoError(err) - - testAccount := apptesting.CreateRandomAccounts(1)[0] - ctx := suite.Ctx - incentivesKeepers := suite.App.IncentivesKeeper - bankKeeper := suite.App.BankKeeper - - // Pre-fund account. - // suite.FundAcc(testAccount, testutil.DefaultAcctFunds) - suite.FundAcc(testAccount, sdk.NewCoins(tc.accountBalanceToFund)) - - oldBalanceAmount := bankKeeper.GetBalance(ctx, testAccount, "adym").Amount - - // System under test. - err = incentivesKeepers.ChargeFeeIfSufficientFeeDenomBalance(ctx, testAccount, sdk.NewInt(tc.feeToCharge), tc.gaugeCoins) - - // Assertions. - newBalanceAmount := bankKeeper.GetBalance(ctx, testAccount, "adym").Amount - if tc.expectError { - suite.Require().Error(err) - - // check account balance unchanged - suite.Require().Equal(oldBalanceAmount, newBalanceAmount) - } else { - suite.Require().NoError(err) - - // check account balance changed. - expectedNewBalanceAmount := oldBalanceAmount.Sub(sdk.NewInt(tc.feeToCharge)) - suite.Require().Equal(expectedNewBalanceAmount.String(), newBalanceAmount.String()) - } - }) - } -} diff --git a/x/incentives/keeper/msg_server.go b/x/incentives/keeper/msg_server.go index 057954a49..f351c1059 100644 --- a/x/incentives/keeper/msg_server.go +++ b/x/incentives/keeper/msg_server.go @@ -2,14 +2,17 @@ package keeper import ( "context" + "fmt" + + errorsmod "cosmossdk.io/errors" "github.com/dymensionxyz/dymension/v3/x/incentives/types" + "github.com/dymensionxyz/gerr-cosmos/gerrc" "github.com/osmosis-labs/osmosis/v15/osmoutils" - errorsmod "cosmossdk.io/errors" + txfeestypes "github.com/osmosis-labs/osmosis/v15/x/txfees/types" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) // msgServer provides a way to reference keeper pointer in the message server interface. @@ -27,21 +30,24 @@ func NewMsgServerImpl(keeper *Keeper) types.MsgServer { var _ types.MsgServer = msgServer{} // CreateGauge creates a gauge and sends coins to the gauge. +// Creation fee is charged from the address and sent to the txfees module to be burned. // Emits create gauge event and returns the create gauge response. func (server msgServer) CreateGauge(goCtx context.Context, msg *types.MsgCreateGauge) (*types.MsgCreateGaugeResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + k := server.keeper owner, err := sdk.AccAddressFromBech32(msg.Owner) if err != nil { return nil, err } - if err := server.keeper.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, types.CreateGaugeFee, msg.Coins); err != nil { - return nil, err + // charge creation fee + if err := k.chargeCreationFee(ctx, owner); err != nil { + return nil, fmt.Errorf("charge creation fee: %w", err) } gaugeID, err := server.keeper.CreateGauge(ctx, msg.IsPerpetual, owner, msg.Coins, msg.DistributeTo, msg.StartTime, msg.NumEpochsPaidOver) if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error()) + return nil, fmt.Errorf("create gauge: %w", err) } ctx.EventManager().EmitEvents(sdk.Events{ @@ -63,12 +69,9 @@ func (server msgServer) AddToGauge(goCtx context.Context, msg *types.MsgAddToGau return nil, err } - if err := server.keeper.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, types.AddToGaugeFee, msg.Rewards); err != nil { - return nil, err - } err = server.keeper.AddToGaugeRewards(ctx, owner, msg.Rewards, msg.GaugeId) if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error()) + return nil, fmt.Errorf("add to gauge rewards: %w", err) } ctx.EventManager().EmitEvents(sdk.Events{ @@ -81,30 +84,12 @@ func (server msgServer) AddToGauge(goCtx context.Context, msg *types.MsgAddToGau return &types.MsgAddToGaugeResponse{}, nil } -// chargeFeeIfSufficientFeeDenomBalance charges fee in the base denom on the address if the address has -// balance that is less than fee + amount of the coin from gaugeCoins that is of base denom. -// gaugeCoins might not have a coin of tx base denom. In that case, fee is only compared to balance. -// The fee is sent to the community pool. -func (k Keeper) chargeFeeIfSufficientFeeDenomBalance(ctx sdk.Context, address sdk.AccAddress, fee sdk.Int, gaugeCoins sdk.Coins) (err error) { - var feeDenom string - if k.tk == nil { - feeDenom, err = sdk.GetBaseDenom() - } else { - feeDenom, err = k.tk.GetBaseDenom(ctx) - } +// chargeCreationFee charges the creation fee from the address. +// The fee is sent to the txfees module, to be burned. +func (k Keeper) chargeCreationFee(ctx sdk.Context, address sdk.AccAddress) (err error) { + feeDenom, err := k.tk.GetBaseDenom(ctx) if err != nil { - return err - } - - totalCost := gaugeCoins.AmountOf(feeDenom).Add(fee) - accountBalance := k.bk.GetBalance(ctx, address, feeDenom).Amount - - if accountBalance.LT(totalCost) { - return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, "account's balance is less than the total cost of the message. Balance: %s %s, Total Cost: %s", feeDenom, accountBalance, totalCost) - } - - if err := k.ck.FundCommunityPool(ctx, sdk.NewCoins(sdk.NewCoin(feeDenom, fee)), address); err != nil { - return err + return errorsmod.Wrapf(gerrc.ErrInternal, "get base denom: %v", err) } - return nil + return k.bk.SendCoinsFromAccountToModule(ctx, address, txfeestypes.ModuleName, sdk.NewCoins(sdk.NewCoin(feeDenom, types.CreateGaugeFee))) } diff --git a/x/incentives/keeper/msg_server_test.go b/x/incentives/keeper/msg_server_test.go index 0b073c96a..c5c9dbde3 100644 --- a/x/incentives/keeper/msg_server_test.go +++ b/x/incentives/keeper/msg_server_test.go @@ -17,12 +17,11 @@ import ( var _ = suite.TestingSuite(nil) -func (suite *KeeperTestSuite) TestCreateGauge_Fee() { +func (suite *KeeperTestSuite) TestCreateGauge() { tests := []struct { name string accountBalanceToFund sdk.Coins gaugeAddition sdk.Coins - expectedEndBalance sdk.Coins isPerpetual bool isModuleAccount bool expectErr bool @@ -123,25 +122,23 @@ func (suite *KeeperTestSuite) TestCreateGauge_Fee() { suite.Require().NoError(err, "test: %v", tc.name) } - balanceAmount := bankKeeper.GetAllBalances(ctx, testAccountAddress) - - if tc.expectErr { - suite.Require().Equal(tc.accountBalanceToFund.String(), balanceAmount.String(), "test: %v", tc.name) - } else { + if !tc.expectErr { + balanceAmount := bankKeeper.GetAllBalances(ctx, testAccountAddress) fee := sdk.NewCoins(sdk.NewCoin("adym", types.CreateGaugeFee)) accountBalance := tc.accountBalanceToFund.Sub(tc.gaugeAddition...) finalAccountBalance := accountBalance.Sub(fee...) suite.Require().Equal(finalAccountBalance.String(), balanceAmount.String(), "test: %v", tc.name) + + //FIXME: test fee charged to txfees module account } } } -func (suite *KeeperTestSuite) TestAddToGauge_Fee() { +func (suite *KeeperTestSuite) TestAddToGauge() { tests := []struct { name string accountBalanceToFund sdk.Coins gaugeAddition sdk.Coins - nonexistentGauge bool isPerpetual bool isModuleAccount bool expectErr bool @@ -196,7 +193,6 @@ func (suite *KeeperTestSuite) TestAddToGauge_Fee() { testAccountPubkey := secp256k1.GenPrivKeyFromSecret([]byte("acc")).PubKey() testAccountAddress := sdk.AccAddress(testAccountPubkey.Address()) - // testAccountAddress := suite.TestAccs[0] ctx := suite.Ctx bankKeeper := suite.App.BankKeeper @@ -204,7 +200,6 @@ func (suite *KeeperTestSuite) TestAddToGauge_Fee() { accountKeeper := suite.App.AccountKeeper msgServer := keeper.NewMsgServerImpl(incentivesKeeper) - // suite.FundAcc(testAccountAddress, testutil.DefaultAcctFunds) suite.FundAcc(testAccountAddress, tc.accountBalanceToFund) if tc.isModuleAccount { @@ -218,9 +213,6 @@ func (suite *KeeperTestSuite) TestAddToGauge_Fee() { // System under test. coins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(500000000))) gaugeID, _, _, _ := suite.SetupNewGauge(true, coins) - if tc.nonexistentGauge { - gaugeID = incentivesKeeper.GetLastGaugeID(ctx) + 1 - } msg := &types.MsgAddToGauge{ Owner: testAccountAddress.String(), GaugeId: gaugeID, @@ -238,8 +230,6 @@ func (suite *KeeperTestSuite) TestAddToGauge_Fee() { bal := bankKeeper.GetAllBalances(ctx, testAccountAddress) if tc.expectErr { - suite.Require().Equal(tc.accountBalanceToFund.String(), bal.String(), "test: %v", tc.name) - } else { fee := sdk.NewCoins(sdk.NewCoin("adym", types.AddToGaugeFee)) accountBalance := tc.accountBalanceToFund.Sub(tc.gaugeAddition...) finalAccountBalance := accountBalance.Sub(fee...) diff --git a/x/incentives/types/gauge.go b/x/incentives/types/gauge.go index 6012109a3..29e4354bf 100644 --- a/x/incentives/types/gauge.go +++ b/x/incentives/types/gauge.go @@ -15,12 +15,12 @@ var ( // CreateGaugeFee is the fee required to create a new gauge. CreateGaugeFee = DYM.Mul(sdk.NewInt(10)) - // AddToGagugeFee is the fee required to add to gauge. + // AddToGaugeFee is the fee required to add to gauge. AddToGaugeFee = sdk.ZeroInt() ) // NewAssetGauge creates a new asset gauge to stream rewards to some asset lockup conditions. -func NewAssetGauge(id uint64, isPerpetual bool, distrTo lockuptypes.QueryCondition, coins sdk.Coins, startTime time.Time, numEpochsPaidOver uint64, filledEpochs uint64, distrCoins sdk.Coins) Gauge { +func NewAssetGauge(id uint64, isPerpetual bool, distrTo lockuptypes.QueryCondition, coins sdk.Coins, startTime time.Time, numEpochsPaidOver uint64) Gauge { return Gauge{ Id: id, IsPerpetual: isPerpetual, @@ -28,8 +28,8 @@ func NewAssetGauge(id uint64, isPerpetual bool, distrTo lockuptypes.QueryConditi Coins: coins, StartTime: startTime, NumEpochsPaidOver: numEpochsPaidOver, - FilledEpochs: filledEpochs, - DistributedCoins: distrCoins, + FilledEpochs: 0, + DistributedCoins: sdk.NewCoins(), } } @@ -37,7 +37,7 @@ func NewAssetGauge(id uint64, isPerpetual bool, distrTo lockuptypes.QueryConditi func NewRollappGauge(id uint64, rollappId string) Gauge { return Gauge{ Id: id, - IsPerpetual: false, + IsPerpetual: true, DistributeTo: &Gauge_Rollapp{Rollapp: &RollappGauge{RollappId: rollappId}}, Coins: sdk.NewCoins(), StartTime: time.Time{}, From c76aee1a5b0c955f94c1ab05e7d6d3920e68e2dc Mon Sep 17 00:00:00 2001 From: Michael Tsitrin Date: Sun, 18 Aug 2024 22:27:01 +0300 Subject: [PATCH 3/7] cleanup. fixed ut --- app/upgrades/v3/upgrade_test.go | 4 +--- x/incentives/keeper/msg_server_test.go | 11 +++++++---- x/incentives/types/gauge.go | 2 -- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/upgrades/v3/upgrade_test.go b/app/upgrades/v3/upgrade_test.go index 70fd7bad3..98afda566 100644 --- a/app/upgrades/v3/upgrade_test.go +++ b/app/upgrades/v3/upgrade_test.go @@ -40,8 +40,6 @@ var ( // CreateGaugeFee is the fee required to create a new gauge. expectCreateGaugeFee = DYM.Mul(sdk.NewInt(10)) - // AddToGaugeFee is the fee required to add to gauge. - expectAddToGaugeFee = sdk.ZeroInt() expectDelayedackEpochIdentifier = "hour" expectDelayedackBridgingFee = sdk.NewDecWithPrec(1, 3) @@ -103,7 +101,7 @@ func (s *UpgradeTestSuite) TestUpgrade() { } // Check Incentives parameters - if !incentivestypes.CreateGaugeFee.Equal(expectCreateGaugeFee) || !incentivestypes.AddToGaugeFee.Equal(expectAddToGaugeFee) { + if !incentivestypes.CreateGaugeFee.Equal(expectCreateGaugeFee) { return fmt.Errorf("incentives parameters not set correctly") } diff --git a/x/incentives/keeper/msg_server_test.go b/x/incentives/keeper/msg_server_test.go index c5c9dbde3..4057f3525 100644 --- a/x/incentives/keeper/msg_server_test.go +++ b/x/incentives/keeper/msg_server_test.go @@ -11,6 +11,7 @@ import ( "github.com/dymensionxyz/dymension/v3/x/incentives/keeper" "github.com/dymensionxyz/dymension/v3/x/incentives/types" lockuptypes "github.com/osmosis-labs/osmosis/v15/x/lockup/types" + "github.com/osmosis-labs/osmosis/v15/x/txfees" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -105,6 +106,8 @@ func (suite *KeeperTestSuite) TestCreateGauge() { Duration: defaultLockDuration, } + txfeesBalanceBefore := bankKeeper.GetBalance(ctx, accountKeeper.GetModuleAddress(txfees.ModuleName), "adym") + msg := &types.MsgCreateGauge{ IsPerpetual: tc.isPerpetual, Owner: testAccountAddress.String(), @@ -129,7 +132,9 @@ func (suite *KeeperTestSuite) TestCreateGauge() { finalAccountBalance := accountBalance.Sub(fee...) suite.Require().Equal(finalAccountBalance.String(), balanceAmount.String(), "test: %v", tc.name) - //FIXME: test fee charged to txfees module account + // test fee charged to txfees module account + txfeesBalanceAfter := bankKeeper.GetBalance(ctx, accountKeeper.GetModuleAddress(txfees.ModuleName), "adym") + suite.Require().Equal(txfeesBalanceBefore.Amount.Add(types.CreateGaugeFee), txfeesBalanceAfter.Amount, "test: %v", tc.name) } } } @@ -230,10 +235,8 @@ func (suite *KeeperTestSuite) TestAddToGauge() { bal := bankKeeper.GetAllBalances(ctx, testAccountAddress) if tc.expectErr { - fee := sdk.NewCoins(sdk.NewCoin("adym", types.AddToGaugeFee)) accountBalance := tc.accountBalanceToFund.Sub(tc.gaugeAddition...) - finalAccountBalance := accountBalance.Sub(fee...) - suite.Require().Equal(finalAccountBalance.String(), bal.String(), "test: %v", tc.name) + suite.Require().Equal(accountBalance.String(), bal.String(), "test: %v", tc.name) } } } diff --git a/x/incentives/types/gauge.go b/x/incentives/types/gauge.go index 29e4354bf..338979d40 100644 --- a/x/incentives/types/gauge.go +++ b/x/incentives/types/gauge.go @@ -15,8 +15,6 @@ var ( // CreateGaugeFee is the fee required to create a new gauge. CreateGaugeFee = DYM.Mul(sdk.NewInt(10)) - // AddToGaugeFee is the fee required to add to gauge. - AddToGaugeFee = sdk.ZeroInt() ) // NewAssetGauge creates a new asset gauge to stream rewards to some asset lockup conditions. From 1d50576ad41daa3b0d54c6f2f804231397fbd3c3 Mon Sep 17 00:00:00 2001 From: Michael Tsitrin Date: Wed, 4 Sep 2024 09:22:21 +0300 Subject: [PATCH 4/7] pr comments --- app/upgrades/v4/upgrade_test.go | 24 +++++++++++++++--------- x/incentives/keeper/msg_server.go | 5 +++-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/app/upgrades/v4/upgrade_test.go b/app/upgrades/v4/upgrade_test.go index 2faa8b249..e1f9f7a6d 100644 --- a/app/upgrades/v4/upgrade_test.go +++ b/app/upgrades/v4/upgrade_test.go @@ -193,27 +193,33 @@ func (s *UpgradeTestSuite) validateRollappsMigration(numRoll int) error { // validate rollapp gauges func (s *UpgradeTestSuite) validateRollappGaugesMigration() error { - rollappMap := make(map[string]struct{}) // Create a map to store rollapps + rollappMap := make(map[string]bool) // Create a map to store rollappId<->gaugeCreated rollapps := s.App.RollappKeeper.GetAllRollapps(s.Ctx) for _, rollapp := range rollapps { - rollappMap[rollapp.RollappId] = struct{}{} // Store rollapp in the map + rollappMap[rollapp.RollappId] = false // false until gauge is validated } - gauges := s.App.IncentivesKeeper.GetActiveGauges(s.Ctx) - if len(gauges) == len(rollapps) { + gauges := s.App.IncentivesKeeper.GetGauges(s.Ctx) + if len(gauges) != len(rollapps) { return fmt.Errorf("rollapp gauges not created for all rollapps") } + // Check that for each rollapp there exists a rollapp gauge for _, gauge := range gauges { if gauge.GetRollapp() != nil { - rollappMap[gauge.GetRollapp().RollappId] = struct{}{} + gaugeExists, ok := rollappMap[gauge.GetRollapp().RollappId] + if !ok { + return fmt.Errorf("rollapp gauge for unknown rollapp %s", gauge.GetRollapp().RollappId) + } + + if gaugeExists { + return fmt.Errorf("rollapp gauge for rollapp %s already created", gauge.GetRollapp().RollappId) + } + + rollappMap[gauge.GetRollapp().RollappId] = true } } - // Check that for each rollapp there exists a rollapp gauge - if len(rollappMap) != len(rollapps) { - return fmt.Errorf("rollapp gauges not created for all rollapps") - } return nil } diff --git a/x/incentives/keeper/msg_server.go b/x/incentives/keeper/msg_server.go index f351c1059..428041d9d 100644 --- a/x/incentives/keeper/msg_server.go +++ b/x/incentives/keeper/msg_server.go @@ -45,7 +45,7 @@ func (server msgServer) CreateGauge(goCtx context.Context, msg *types.MsgCreateG return nil, fmt.Errorf("charge creation fee: %w", err) } - gaugeID, err := server.keeper.CreateGauge(ctx, msg.IsPerpetual, owner, msg.Coins, msg.DistributeTo, msg.StartTime, msg.NumEpochsPaidOver) + gaugeID, err := k.CreateGauge(ctx, msg.IsPerpetual, owner, msg.Coins, msg.DistributeTo, msg.StartTime, msg.NumEpochsPaidOver) if err != nil { return nil, fmt.Errorf("create gauge: %w", err) } @@ -64,12 +64,13 @@ func (server msgServer) CreateGauge(goCtx context.Context, msg *types.MsgCreateG // Emits add to gauge event and returns the add to gauge response. func (server msgServer) AddToGauge(goCtx context.Context, msg *types.MsgAddToGauge) (*types.MsgAddToGaugeResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + k := server.keeper owner, err := sdk.AccAddressFromBech32(msg.Owner) if err != nil { return nil, err } - err = server.keeper.AddToGaugeRewards(ctx, owner, msg.Rewards, msg.GaugeId) + err = k.AddToGaugeRewards(ctx, owner, msg.Rewards, msg.GaugeId) if err != nil { return nil, fmt.Errorf("add to gauge rewards: %w", err) } From 5edb0982afe76f9851fe4351997f739b94ce9d59 Mon Sep 17 00:00:00 2001 From: Michael Tsitrin Date: Wed, 4 Sep 2024 09:31:04 +0300 Subject: [PATCH 5/7] more linter --- app/apptesting/test_suite.go | 2 +- x/sequencer/keeper/msg_server_create_sequencer.go | 3 ++- x/sponsorship/types/genesis.go | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/apptesting/test_suite.go b/app/apptesting/test_suite.go index f1b3889c8..f592da5aa 100644 --- a/app/apptesting/test_suite.go +++ b/app/apptesting/test_suite.go @@ -109,7 +109,7 @@ func (s *KeeperTestHelper) CreateSequencerByPubkey(ctx sdk.Context, rollappId st func (s *KeeperTestHelper) PostStateUpdate(ctx sdk.Context, rollappId, seqAddr string, startHeight, numOfBlocks uint64) (lastHeight uint64, err error) { var bds rollapptypes.BlockDescriptors bds.BD = make([]rollapptypes.BlockDescriptor, numOfBlocks) - for k := 0; k < int(numOfBlocks); k++ { + for k := uint64(0); k < numOfBlocks; k++ { bds.BD[k] = rollapptypes.BlockDescriptor{Height: startHeight + uint64(k), Timestamp: time.Now().UTC()} } diff --git a/x/sequencer/keeper/msg_server_create_sequencer.go b/x/sequencer/keeper/msg_server_create_sequencer.go index baecefd9a..c729722b4 100644 --- a/x/sequencer/keeper/msg_server_create_sequencer.go +++ b/x/sequencer/keeper/msg_server_create_sequencer.go @@ -2,6 +2,7 @@ package keeper import ( "context" + "errors" "slices" "strconv" "strings" @@ -27,7 +28,7 @@ func (k msgServer) CreateSequencer(goCtx context.Context, msg *types.MsgCreateSe } if err := msg.VMSpecificValidate(rollapp.VmType); err != nil { - return nil, errorsmod.Wrapf(types.ErrInvalidRequest, err.Error()) + return nil, errors.Join(types.ErrInvalidRequest, err) } // check to see if the sequencer has been registered before diff --git a/x/sponsorship/types/genesis.go b/x/sponsorship/types/genesis.go index ec82c6e50..1e40ee5ea 100644 --- a/x/sponsorship/types/genesis.go +++ b/x/sponsorship/types/genesis.go @@ -1,6 +1,8 @@ package types import ( + "errors" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -15,13 +17,13 @@ func DefaultGenesis() *GenesisState { func (g GenesisState) Validate() error { err := g.Params.Validate() if err != nil { - return ErrInvalidGenesis.Wrapf(err.Error()) + return errors.Join(ErrInvalidGenesis, err) } for _, i := range g.VoterInfos { err = i.Validate() if err != nil { - return ErrInvalidGenesis.Wrapf(err.Error()) + return errors.Join(ErrInvalidGenesis, err) } } From 9669ebde0e1648acf467980bb568ddeb346cf0ba Mon Sep 17 00:00:00 2001 From: Michael Tsitrin Date: Wed, 4 Sep 2024 09:58:34 +0300 Subject: [PATCH 6/7] linter --- app/apptesting/test_suite.go | 2 +- x/sponsorship/types/genesis.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/apptesting/test_suite.go b/app/apptesting/test_suite.go index f592da5aa..a8603bdfd 100644 --- a/app/apptesting/test_suite.go +++ b/app/apptesting/test_suite.go @@ -110,7 +110,7 @@ func (s *KeeperTestHelper) PostStateUpdate(ctx sdk.Context, rollappId, seqAddr s var bds rollapptypes.BlockDescriptors bds.BD = make([]rollapptypes.BlockDescriptor, numOfBlocks) for k := uint64(0); k < numOfBlocks; k++ { - bds.BD[k] = rollapptypes.BlockDescriptor{Height: startHeight + uint64(k), Timestamp: time.Now().UTC()} + bds.BD[k] = rollapptypes.BlockDescriptor{Height: startHeight + k, Timestamp: time.Now().UTC()} } updateState := rollapptypes.MsgUpdateState{ diff --git a/x/sponsorship/types/genesis.go b/x/sponsorship/types/genesis.go index 1e40ee5ea..85a9d3525 100644 --- a/x/sponsorship/types/genesis.go +++ b/x/sponsorship/types/genesis.go @@ -3,6 +3,7 @@ package types import ( "errors" + errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -33,15 +34,14 @@ func (g GenesisState) Validate() error { func (v VoterInfo) Validate() error { _, err := sdk.AccAddressFromBech32(v.Voter) if err != nil { - return ErrInvalidVoterInfo.Wrapf( - "voter '%s' must be a valid bech32 address: %s", - v.Voter, err.Error(), + return errorsmod.Wrapf(errors.Join(ErrInvalidVoterInfo, err), + "voter '%s' must be a valid bech32 address", v.Voter, ) } err = v.Vote.Validate() if err != nil { - return ErrInvalidVoterInfo.Wrapf(err.Error()) + return errors.Join(ErrInvalidVoterInfo, err) } // Validate validators From 4eb4fb63a696d1f8533009ef458b3dedba55984e Mon Sep 17 00:00:00 2001 From: Michael Tsitrin Date: Tue, 17 Sep 2024 11:12:42 +0300 Subject: [PATCH 7/7] fixed UT --- x/incentives/client/cli/cli_test.go | 3 +- x/incentives/client/cli/query.go | 3 +- x/incentives/keeper/distribute.go | 5 +- x/incentives/keeper/msg_server.go | 8 +- x/incentives/keeper/msg_server_test.go | 114 +++++++++++++++++++++++-- 5 files changed, 117 insertions(+), 16 deletions(-) diff --git a/x/incentives/client/cli/cli_test.go b/x/incentives/client/cli/cli_test.go index 02cf8d814..d5c805cb9 100644 --- a/x/incentives/client/cli/cli_test.go +++ b/x/incentives/client/cli/cli_test.go @@ -5,9 +5,10 @@ import ( "github.com/cosmos/cosmos-sdk/types/query" + "github.com/osmosis-labs/osmosis/v15/osmoutils/osmocli" + "github.com/dymensionxyz/dymension/v3/x/incentives/client/cli" "github.com/dymensionxyz/dymension/v3/x/incentives/types" - "github.com/osmosis-labs/osmosis/v15/osmoutils/osmocli" ) func TestGetCmdGauges(t *testing.T) { diff --git a/x/incentives/client/cli/query.go b/x/incentives/client/cli/query.go index 74b3188dd..5b2eb4cca 100644 --- a/x/incentives/client/cli/query.go +++ b/x/incentives/client/cli/query.go @@ -3,8 +3,9 @@ package cli import ( "github.com/spf13/cobra" - "github.com/dymensionxyz/dymension/v3/x/incentives/types" "github.com/osmosis-labs/osmosis/v15/osmoutils/osmocli" + + "github.com/dymensionxyz/dymension/v3/x/incentives/types" ) // GetQueryCmd returns the query commands for this module. diff --git a/x/incentives/keeper/distribute.go b/x/incentives/keeper/distribute.go index 65fe6ec8b..fe5047281 100644 --- a/x/incentives/keeper/distribute.go +++ b/x/incentives/keeper/distribute.go @@ -4,11 +4,10 @@ import ( "fmt" errorsmod "cosmossdk.io/errors" - + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/dymensionxyz/dymension/v3/x/incentives/types" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/x/incentives/types" ) // Distribute distributes coins from an array of gauges. diff --git a/x/incentives/keeper/msg_server.go b/x/incentives/keeper/msg_server.go index 76c35c1c7..22110e00d 100644 --- a/x/incentives/keeper/msg_server.go +++ b/x/incentives/keeper/msg_server.go @@ -40,7 +40,7 @@ func (server msgServer) CreateGauge(goCtx context.Context, msg *types.MsgCreateG // Fee = CreateGaugeBaseFee + AddDenomFee * NumDenoms params := server.keeper.GetParams(ctx) fee := params.CreateGaugeBaseFee.Add(params.AddDenomFee.MulRaw(int64(len(msg.Coins)))) - if err = server.keeper.chargeGaugesFee(ctx, owner, fee, msg.Coins); err != nil { + if err = server.keeper.ChargeGaugesFee(ctx, owner, fee, msg.Coins); err != nil { return nil, err } @@ -77,7 +77,7 @@ func (server msgServer) AddToGauge(goCtx context.Context, msg *types.MsgAddToGau // Fee = AddToGaugeBaseFee + AddDenomFee * (NumAddedDenoms + NumGaugeDenoms) params := server.keeper.GetParams(ctx) fee := params.AddToGaugeBaseFee.Add(params.AddDenomFee.MulRaw(int64(len(msg.Rewards) + len(gauge.Coins)))) - if err = server.keeper.chargeGaugesFee(ctx, owner, fee, msg.Rewards); err != nil { + if err = server.keeper.ChargeGaugesFee(ctx, owner, fee, msg.Rewards); err != nil { return nil, err } @@ -96,11 +96,11 @@ func (server msgServer) AddToGauge(goCtx context.Context, msg *types.MsgAddToGau return &types.MsgAddToGaugeResponse{}, nil } -// chargeGaugesFee charges fee in the base denom on the address if the address has +// ChargeGaugesFee charges fee in the base denom on the address if the address has // balance that is less than fee + amount of the coin from gaugeCoins that is of base denom. // gaugeCoins might not have a coin of tx base denom. In that case, fee is only compared to balance. // The fee is sent to the txfees module, to be burned. -func (k Keeper) chargeGaugesFee(ctx sdk.Context, address sdk.AccAddress, fee sdk.Int, gaugeCoins sdk.Coins) (err error) { +func (k Keeper) ChargeGaugesFee(ctx sdk.Context, address sdk.AccAddress, fee sdk.Int, gaugeCoins sdk.Coins) (err error) { var feeDenom string if k.tk == nil { feeDenom, err = sdk.GetBaseDenom() diff --git a/x/incentives/keeper/msg_server_test.go b/x/incentives/keeper/msg_server_test.go index 0b2e6b69d..3d88aacc3 100644 --- a/x/incentives/keeper/msg_server_test.go +++ b/x/incentives/keeper/msg_server_test.go @@ -8,10 +8,12 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/stretchr/testify/suite" + "github.com/osmosis-labs/osmosis/v15/x/txfees" + + "github.com/dymensionxyz/dymension/v3/app/apptesting" "github.com/dymensionxyz/dymension/v3/x/incentives/keeper" "github.com/dymensionxyz/dymension/v3/x/incentives/types" lockuptypes "github.com/dymensionxyz/dymension/v3/x/lockup/types" - "github.com/osmosis-labs/osmosis/v15/x/txfees" ) var _ = suite.TestingSuite(nil) @@ -165,7 +167,7 @@ func (suite *KeeperTestSuite) TestCreateGauge() { StartTime: time.Now(), NumEpochsPaidOver: 1, } - txfeesBalanceBefore := bankKeeper.GetBalance(ctx, accountKeeper.GetModuleAddress(txfees.ModuleName), "adym") + txfeesBalanceBefore := bankKeeper.GetBalance(ctx, accountKeeper.GetModuleAddress(txfees.ModuleName), "stake") // System under test. _, err := msgServer.CreateGauge(sdk.WrapSDKContext(ctx), msg) @@ -191,7 +193,7 @@ func (suite *KeeperTestSuite) TestCreateGauge() { suite.Require().Equal(finalAccountBalance.String(), balanceAmount.String(), "test: %v", tc.name) // test fee charged to txfees module account - txfeesBalanceAfter := bankKeeper.GetBalance(ctx, accountKeeper.GetModuleAddress(txfees.ModuleName), "adym") + txfeesBalanceAfter := bankKeeper.GetBalance(ctx, accountKeeper.GetModuleAddress(txfees.ModuleName), "stake") suite.Require().Equal(txfeesBalanceBefore.Amount.Add(feeRaw), txfeesBalanceAfter.Amount, "test: %v", tc.name) } }) @@ -204,7 +206,6 @@ func (suite *KeeperTestSuite) TestAddToGauge() { accountBalanceToFund sdk.Coins gaugeAddition sdk.Coins isPerpetual bool - nonexistentGauge bool isModuleAccount bool expectErr bool }{ @@ -334,9 +335,6 @@ func (suite *KeeperTestSuite) TestAddToGauge() { // System under test. coins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(500000000))) gaugeID, gauge, _, _ := suite.SetupNewGauge(true, coins) - if tc.nonexistentGauge { - gaugeID = incentivesKeeper.GetLastGaugeID(ctx) + 1 - } msg := &types.MsgAddToGauge{ Owner: testAccountAddress.String(), GaugeId: gaugeID, @@ -347,6 +345,8 @@ func (suite *KeeperTestSuite) TestAddToGauge() { feeRaw := params.AddToGaugeBaseFee.Add(params.AddDenomFee.MulRaw(int64(len(tc.gaugeAddition) + len(gauge.Coins)))) suite.T().Log(feeRaw, params.AddToGaugeBaseFee, params.AddDenomFee) + txfeesBalanceBefore := bankKeeper.GetBalance(ctx, accountKeeper.GetModuleAddress(txfees.ModuleName), "stake") + _, err = msgServer.AddToGauge(sdk.WrapSDKContext(ctx), msg) if tc.expectErr { @@ -368,6 +368,106 @@ func (suite *KeeperTestSuite) TestAddToGauge() { accountBalance := tc.accountBalanceToFund.Sub(tc.gaugeAddition...) finalAccountBalance := accountBalance.Sub(fee...) suite.Require().Equal(finalAccountBalance.String(), bal.String(), "test: %v", tc.name) + + // test fee charged to txfees module account + txfeesBalanceAfter := bankKeeper.GetBalance(ctx, accountKeeper.GetModuleAddress(txfees.ModuleName), "stake") + suite.Require().Equal(txfeesBalanceBefore.Amount.Add(feeRaw), txfeesBalanceAfter.Amount, "test: %v", tc.name) + } + }) + } +} + +func (suite *KeeperTestSuite) TestChargeFeeIfSufficientFeeDenomBalance() { + const baseFee = int64(100) + + testcases := map[string]struct { + accountBalanceToFund sdk.Coin + feeToCharge int64 + gaugeCoins sdk.Coins + + expectError bool + }{ + "fee + base denom gauge coin == acount balance, success": { + accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), + feeToCharge: baseFee / 2, + gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), + }, + "fee + base denom gauge coin < acount balance, success": { + accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), + feeToCharge: baseFee/2 - 1, + gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), + }, + "fee + base denom gauge coin > acount balance, error": { + accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), + feeToCharge: baseFee/2 + 1, + gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), + expectError: true, + }, + "fee + base denom gauge coin < acount balance, custom values, success": { + accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(11793193112)), + feeToCharge: 55, + gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(328812))), + }, + "account funded with coins other than base denom, error": { + accountBalanceToFund: sdk.NewCoin("usdc", sdk.NewInt(baseFee)), + feeToCharge: baseFee, + gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), + expectError: true, + }, + "fee == account balance, no gauge coins, success": { + accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), + feeToCharge: baseFee, + }, + "gauge coins == account balance, no fee, success": { + accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), + gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee))), + }, + "fee == account balance, gauge coins in denom other than base, success": { + accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), + feeToCharge: baseFee, + gaugeCoins: sdk.NewCoins(sdk.NewCoin("usdc", sdk.NewInt(baseFee*2))), + }, + "fee + gauge coins == account balance, multiple gauge coins, one in denom other than base, success": { + accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), + feeToCharge: baseFee / 2, + gaugeCoins: sdk.NewCoins(sdk.NewCoin("usdc", sdk.NewInt(baseFee*2)), sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), + }, + } + + for name, tc := range testcases { + suite.Run(name, func() { + suite.SetupTest() + + err := suite.App.TxFeesKeeper.SetBaseDenom(suite.Ctx, "adym") + suite.Require().NoError(err) + + testAccount := apptesting.CreateRandomAccounts(1)[0] + ctx := suite.Ctx + incentivesKeepers := suite.App.IncentivesKeeper + bankKeeper := suite.App.BankKeeper + + // Pre-fund account. + // suite.FundAcc(testAccount, testutil.DefaultAcctFunds) + suite.FundAcc(testAccount, sdk.NewCoins(tc.accountBalanceToFund)) + + oldBalanceAmount := bankKeeper.GetBalance(ctx, testAccount, "adym").Amount + + // System under test. + err = incentivesKeepers.ChargeGaugesFee(ctx, testAccount, sdk.NewInt(tc.feeToCharge), tc.gaugeCoins) + + // Assertions. + newBalanceAmount := bankKeeper.GetBalance(ctx, testAccount, "adym").Amount + if tc.expectError { + suite.Require().Error(err) + + // check account balance unchanged + suite.Require().Equal(oldBalanceAmount, newBalanceAmount) + } else { + suite.Require().NoError(err) + + // check account balance changed. + expectedNewBalanceAmount := oldBalanceAmount.Sub(sdk.NewInt(tc.feeToCharge)) + suite.Require().Equal(expectedNewBalanceAmount.String(), newBalanceAmount.String()) } }) }