From 9c7d045c6e1d04d095425a7be79184c8eb900750 Mon Sep 17 00:00:00 2001 From: keruch Date: Wed, 4 Sep 2024 21:38:54 +0200 Subject: [PATCH 1/6] feat(incentives): added fees for adding to gauge and gauge creation --- app/upgrades/v4/upgrade.go | 11 ++ .../dymension/incentives/params.proto | 10 ++ x/incentives/keeper/genesis_test.go | 2 + x/incentives/keeper/msg_server.go | 13 +- x/incentives/keeper/msg_server_test.go | 10 +- x/incentives/types/constants.go | 17 +++ x/incentives/types/gauge.go | 11 -- x/incentives/types/genesis.go | 4 +- x/incentives/types/params.go | 44 ++++++- x/incentives/types/params.pb.go | 124 ++++++++++++++++-- x/rollapp/types/params.pb.go | 2 +- 11 files changed, 210 insertions(+), 38 deletions(-) create mode 100644 x/incentives/types/constants.go diff --git a/app/upgrades/v4/upgrade.go b/app/upgrades/v4/upgrade.go index 0bd6d7e70..aec9533cb 100644 --- a/app/upgrades/v4/upgrade.go +++ b/app/upgrades/v4/upgrade.go @@ -28,6 +28,8 @@ 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" + incentivestypes "github.com/dymensionxyz/dymension/v3/x/incentives/types" lightclientkeeper "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" rollappkeeper "github.com/dymensionxyz/dymension/v3/x/rollapp/keeper" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" @@ -56,6 +58,7 @@ func CreateUpgradeHandler( migrateSequencers(ctx, keepers.SequencerKeeper) migrateRollappLightClients(ctx, keepers.RollappKeeper, keepers.LightClientKeeper, keepers.IBCKeeper.ChannelKeeper) + migrateIncentivesParams(ctx, keepers.IncentivesKeeper) // TODO: create rollapp gauges for each existing rollapp (https://github.com/dymensionxyz/dymension/issues/1005) @@ -173,6 +176,14 @@ func migrateRollappLightClients(ctx sdk.Context, rollappkeeper *rollappkeeper.Ke } } +func migrateIncentivesParams(ctx sdk.Context, ik *incentiveskeeper.Keeper) { + params := ik.GetParams(ctx) + defaultParams := incentivestypes.DefaultParams() + params.CreateGaugeFee = defaultParams.CreateGaugeFee + params.AddToGaugeFee = defaultParams.AddToGaugeFee + ik.SetParams(ctx, params) +} + func ConvertOldRollappToNew(oldRollapp rollapptypes.Rollapp) rollapptypes.Rollapp { return rollapptypes.Rollapp{ RollappId: oldRollapp.RollappId, diff --git a/proto/dymensionxyz/dymension/incentives/params.proto b/proto/dymensionxyz/dymension/incentives/params.proto index db984093c..392f630f6 100644 --- a/proto/dymensionxyz/dymension/incentives/params.proto +++ b/proto/dymensionxyz/dymension/incentives/params.proto @@ -10,4 +10,14 @@ message Params { // (day, week, etc.) string distr_epoch_identifier = 1 [ (gogoproto.moretags) = "yaml:\"distr_epoch_identifier\"" ]; + // CreateGaugeFee is the fee required to create a new gauge. + string create_gauge_fee = 2 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int" + ]; + // AddToGaugeFee is the fee required to add to gauge. + string add_to_gauge_fee = 3 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int" + ]; } diff --git a/x/incentives/keeper/genesis_test.go b/x/incentives/keeper/genesis_test.go index fd0d0940c..0faf83085 100644 --- a/x/incentives/keeper/genesis_test.go +++ b/x/incentives/keeper/genesis_test.go @@ -97,6 +97,8 @@ func TestIncentivesInitGenesis(t *testing.T) { app.IncentivesKeeper.InitGenesis(ctx, types.GenesisState{ Params: types.Params{ DistrEpochIdentifier: "week", + CreateGaugeFee: sdk.ZeroInt(), + AddToGaugeFee: sdk.ZeroInt(), }, Gauges: []types.Gauge{gauge}, LockableDurations: []time.Duration{ diff --git a/x/incentives/keeper/msg_server.go b/x/incentives/keeper/msg_server.go index 057954a49..5d599a499 100644 --- a/x/incentives/keeper/msg_server.go +++ b/x/incentives/keeper/msg_server.go @@ -3,13 +3,12 @@ package keeper import ( "context" - "github.com/dymensionxyz/dymension/v3/x/incentives/types" - "github.com/osmosis-labs/osmosis/v15/osmoutils" - errorsmod "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/osmosis-labs/osmosis/v15/osmoutils" + + "github.com/dymensionxyz/dymension/v3/x/incentives/types" ) // msgServer provides a way to reference keeper pointer in the message server interface. @@ -35,7 +34,8 @@ func (server msgServer) CreateGauge(goCtx context.Context, msg *types.MsgCreateG return nil, err } - if err := server.keeper.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, types.CreateGaugeFee, msg.Coins); err != nil { + createGaugeFee := server.keeper.GetParams(ctx).CreateGaugeFee + if err := server.keeper.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, createGaugeFee, msg.Coins); err != nil { return nil, err } @@ -63,7 +63,8 @@ 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 { + addToGaugeFee := server.keeper.GetParams(ctx).AddToGaugeFee + if err := server.keeper.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, addToGaugeFee, msg.Rewards); err != nil { return nil, err } err = server.keeper.AddToGaugeRewards(ctx, owner, msg.Rewards, msg.GaugeId) diff --git a/x/incentives/keeper/msg_server_test.go b/x/incentives/keeper/msg_server_test.go index 625676282..59190e6c6 100644 --- a/x/incentives/keeper/msg_server_test.go +++ b/x/incentives/keeper/msg_server_test.go @@ -126,7 +126,8 @@ func (suite *KeeperTestSuite) TestCreateGauge_Fee() { if tc.expectErr { suite.Require().Equal(tc.accountBalanceToFund.String(), balanceAmount.String(), "test: %v", tc.name) } else { - fee := sdk.NewCoins(sdk.NewCoin("adym", types.CreateGaugeFee)) + createGaugeFee := suite.querier.GetParams(suite.Ctx).CreateGaugeFee + fee := sdk.NewCoins(sdk.NewCoin("adym", createGaugeFee)) accountBalance := tc.accountBalanceToFund.Sub(tc.gaugeAddition...) finalAccountBalance := accountBalance.Sub(fee...) suite.Require().Equal(finalAccountBalance.String(), balanceAmount.String(), "test: %v", tc.name) @@ -176,13 +177,13 @@ func (suite *KeeperTestSuite) TestAddToGauge_Fee() { name: "user tries to add to a non-perpetual gauge but does not have enough funds to pay for the create gauge fee", accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(20)))), gaugeAddition: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(20)))), - expectErr: false, // no addition fee + expectErr: true, }, { name: "user tries to add to a non-perpetual gauge but does not have the correct fee denom", accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(60)))), gaugeAddition: sdk.NewCoins(sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(10)))), - expectErr: false, // no addition fee + expectErr: true, }, } @@ -238,7 +239,8 @@ func (suite *KeeperTestSuite) TestAddToGauge_Fee() { if tc.expectErr { suite.Require().Equal(tc.accountBalanceToFund.String(), bal.String(), "test: %v", tc.name) } else { - fee := sdk.NewCoins(sdk.NewCoin("adym", types.AddToGaugeFee)) + addToGaugeFee := suite.querier.GetParams(suite.Ctx).AddToGaugeFee + fee := sdk.NewCoins(sdk.NewCoin("adym", addToGaugeFee)) accountBalance := tc.accountBalanceToFund.Sub(tc.gaugeAddition...) finalAccountBalance := accountBalance.Sub(fee...) suite.Require().Equal(finalAccountBalance.String(), bal.String(), "test: %v", tc.name) diff --git a/x/incentives/types/constants.go b/x/incentives/types/constants.go new file mode 100644 index 000000000..e6ee77606 --- /dev/null +++ b/x/incentives/types/constants.go @@ -0,0 +1,17 @@ +package types + +import ( + "math/big" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + // DYM represents 1 DYM + DYM = math.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) + + DefaultDistrEpochIdentifier = "week" + DefaultCreateGaugeFee = DYM.Mul(sdk.NewInt(10)) + DefaultAddToGaugeFee = DYM +) diff --git a/x/incentives/types/gauge.go b/x/incentives/types/gauge.go index 8b73255a3..637ec463c 100644 --- a/x/incentives/types/gauge.go +++ b/x/incentives/types/gauge.go @@ -1,7 +1,6 @@ package types import ( - "math/big" time "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -9,16 +8,6 @@ import ( lockuptypes "github.com/dymensionxyz/dymension/v3/x/lockup/types" ) -var ( - // DYM represents 1 DYM - DYM = sdk.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) - - // 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. func NewAssetGauge(id uint64, isPerpetual bool, distrTo lockuptypes.QueryCondition, coins sdk.Coins, startTime time.Time, numEpochsPaidOver uint64, filledEpochs uint64, distrCoins sdk.Coins) Gauge { return Gauge{ diff --git a/x/incentives/types/genesis.go b/x/incentives/types/genesis.go index 12fcc0221..11656a722 100644 --- a/x/incentives/types/genesis.go +++ b/x/incentives/types/genesis.go @@ -13,9 +13,7 @@ const DefaultIndex uint64 = 1 // DefaultGenesis returns the incentive module's default genesis state. func DefaultGenesis() *GenesisState { return &GenesisState{ - Params: Params{ - DistrEpochIdentifier: "week", - }, + Params: DefaultParams(), Gauges: []Gauge{}, LockableDurations: []time.Duration{ time.Second, diff --git a/x/incentives/types/params.go b/x/incentives/types/params.go index 2380f4506..d482cf196 100644 --- a/x/incentives/types/params.go +++ b/x/incentives/types/params.go @@ -1,6 +1,10 @@ package types import ( + "fmt" + + "cosmossdk.io/math" + "github.com/dymensionxyz/gerr-cosmos/gerrc" epochtypes "github.com/osmosis-labs/osmosis/v15/x/epochs/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" @@ -9,6 +13,8 @@ import ( // Incentives parameters key store. var ( KeyDistrEpochIdentifier = []byte("DistrEpochIdentifier") + KeyCreateGaugeFee = []byte("CreateGaugeFee") + KeyAddToGaugeFee = []byte("AddToGaugeFee") ) // ParamKeyTable returns the key table for the incentive module's parameters. @@ -17,16 +23,20 @@ func ParamKeyTable() paramtypes.KeyTable { } // NewParams takes an epoch distribution identifier, then returns an incentives Params struct. -func NewParams(distrEpochIdentifier string) Params { +func NewParams(distrEpochIdentifier string, createGaugeFee, addToGaugeFee math.Int) Params { return Params{ DistrEpochIdentifier: distrEpochIdentifier, + CreateGaugeFee: createGaugeFee, + AddToGaugeFee: addToGaugeFee, } } // DefaultParams returns the default incentives module parameters. func DefaultParams() Params { return Params{ - DistrEpochIdentifier: "week", + DistrEpochIdentifier: DefaultDistrEpochIdentifier, + CreateGaugeFee: DefaultCreateGaugeFee, + AddToGaugeFee: DefaultAddToGaugeFee, } } @@ -35,6 +45,12 @@ func (p Params) Validate() error { if err := epochtypes.ValidateEpochIdentifierInterface(p.DistrEpochIdentifier); err != nil { return err } + if err := validateCreateGaugeFeeInterface(p.CreateGaugeFee); err != nil { + return err + } + if err := validateAddToGaugeFeeInterface(p.AddToGaugeFee); err != nil { + return err + } return nil } @@ -42,5 +58,29 @@ func (p Params) Validate() error { func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { return paramtypes.ParamSetPairs{ paramtypes.NewParamSetPair(KeyDistrEpochIdentifier, &p.DistrEpochIdentifier, epochtypes.ValidateEpochIdentifierInterface), + paramtypes.NewParamSetPair(KeyCreateGaugeFee, &p.CreateGaugeFee, validateCreateGaugeFeeInterface), + paramtypes.NewParamSetPair(KeyAddToGaugeFee, &p.AddToGaugeFee, validateAddToGaugeFeeInterface), + } +} + +func validateCreateGaugeFeeInterface(i interface{}) error { + v, ok := i.(math.Int) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) } + if v.IsNegative() { + return gerrc.ErrInvalidArgument.Wrapf("must be >= 0, got %s", v) + } + return nil +} + +func validateAddToGaugeFeeInterface(i interface{}) error { + v, ok := i.(math.Int) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + if v.IsNegative() { + return gerrc.ErrInvalidArgument.Wrapf("must be >= 0, got %s", v) + } + return nil } diff --git a/x/incentives/types/params.pb.go b/x/incentives/types/params.pb.go index 4e7b4b08f..24844e96d 100644 --- a/x/incentives/types/params.pb.go +++ b/x/incentives/types/params.pb.go @@ -5,6 +5,7 @@ package types import ( fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" io "io" @@ -28,6 +29,10 @@ type Params struct { // distr_epoch_identifier is what epoch type distribution will be triggered by // (day, week, etc.) DistrEpochIdentifier string `protobuf:"bytes,1,opt,name=distr_epoch_identifier,json=distrEpochIdentifier,proto3" json:"distr_epoch_identifier,omitempty" yaml:"distr_epoch_identifier"` + // CreateGaugeFee is the fee required to create a new gauge. + CreateGaugeFee github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=create_gauge_fee,json=createGaugeFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"create_gauge_fee"` + // AddToGaugeFee is the fee required to add to gauge. + AddToGaugeFee github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=add_to_gauge_fee,json=addToGaugeFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"add_to_gauge_fee"` } func (m *Params) Reset() { *m = Params{} } @@ -79,21 +84,26 @@ func init() { } var fileDescriptor_256a114c8e13cfa0 = []byte{ - // 209 bytes of a gzipped FileDescriptorProto + // 299 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4b, 0xa9, 0xcc, 0x4d, 0xcd, 0x2b, 0xce, 0xcc, 0xcf, 0xab, 0xa8, 0xac, 0xd2, 0x87, 0x73, 0xf4, 0x33, 0xf3, 0x92, 0x53, 0xf3, 0x4a, 0x32, 0xcb, 0x52, 0x8b, 0xf5, 0x0b, 0x12, 0x8b, 0x12, 0x73, 0x8b, 0xf5, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x14, 0x91, 0xd5, 0x23, 0x34, 0xeb, 0x21, 0xd4, 0x4b, 0x89, 0xa4, 0xe7, - 0xa7, 0xe7, 0x83, 0x55, 0xeb, 0x83, 0x58, 0x10, 0x8d, 0x4a, 0x89, 0x5c, 0x6c, 0x01, 0x60, 0x83, - 0x84, 0xc2, 0xb9, 0xc4, 0x52, 0x32, 0x8b, 0x4b, 0x8a, 0xe2, 0x53, 0x0b, 0xf2, 0x93, 0x33, 0xe2, - 0x33, 0x53, 0x40, 0x1a, 0xd3, 0x32, 0x53, 0x8b, 0x24, 0x18, 0x15, 0x18, 0x35, 0x38, 0x9d, 0x14, - 0x3f, 0xdd, 0x93, 0x97, 0xad, 0x4c, 0xcc, 0xcd, 0xb1, 0x52, 0xc2, 0xae, 0x4e, 0x29, 0x48, 0x04, - 0x2c, 0xe1, 0x0a, 0x12, 0xf7, 0x84, 0x0b, 0x3b, 0x05, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, - 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, - 0xb1, 0x1c, 0x43, 0x94, 0x79, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, - 0x0e, 0x0f, 0x97, 0x19, 0xeb, 0x57, 0x20, 0xfb, 0xba, 0xa4, 0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, - 0xec, 0x78, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3b, 0xec, 0xe6, 0x15, 0x27, 0x01, 0x00, - 0x00, + 0xa7, 0xe7, 0x83, 0x55, 0xeb, 0x83, 0x58, 0x10, 0x8d, 0x4a, 0x53, 0x98, 0xb8, 0xd8, 0x02, 0xc0, + 0x26, 0x09, 0x85, 0x73, 0x89, 0xa5, 0x64, 0x16, 0x97, 0x14, 0xc5, 0xa7, 0x16, 0xe4, 0x27, 0x67, + 0xc4, 0x67, 0xa6, 0x80, 0x74, 0xa6, 0x65, 0xa6, 0x16, 0x49, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x3a, + 0x29, 0x7e, 0xba, 0x27, 0x2f, 0x5b, 0x99, 0x98, 0x9b, 0x63, 0xa5, 0x84, 0x5d, 0x9d, 0x52, 0x90, + 0x08, 0x58, 0xc2, 0x15, 0x24, 0xee, 0x09, 0x17, 0x16, 0x8a, 0xe0, 0x12, 0x48, 0x2e, 0x4a, 0x4d, + 0x2c, 0x49, 0x8d, 0x4f, 0x4f, 0x2c, 0x4d, 0x4f, 0x8d, 0x4f, 0x4b, 0x4d, 0x95, 0x60, 0x02, 0x1b, + 0xa9, 0x77, 0xe2, 0x9e, 0x3c, 0xc3, 0xad, 0x7b, 0xf2, 0x6a, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, + 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0xc9, 0xf9, 0xc5, 0xb9, 0xf9, 0xc5, 0x50, 0x4a, 0xb7, 0x38, 0x25, + 0x5b, 0xbf, 0xa4, 0xb2, 0x20, 0xb5, 0x58, 0xcf, 0x33, 0xaf, 0x24, 0x88, 0x0f, 0x62, 0x8e, 0x3b, + 0xc8, 0x18, 0xb7, 0xd4, 0x54, 0xa1, 0x70, 0x2e, 0x81, 0xc4, 0x94, 0x94, 0xf8, 0x92, 0x7c, 0x24, + 0x93, 0x99, 0xc9, 0x32, 0x99, 0x37, 0x31, 0x25, 0x25, 0x24, 0x1f, 0x66, 0xb0, 0x53, 0xe0, 0x89, + 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, + 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x99, 0x23, 0x19, 0x88, 0x23, 0x92, 0xca, + 0x8c, 0xf5, 0x2b, 0x90, 0x63, 0x0a, 0x6c, 0x4b, 0x12, 0x1b, 0x38, 0xc0, 0x8d, 0x01, 0x01, 0x00, + 0x00, 0xff, 0xff, 0xdc, 0xe1, 0xcd, 0x80, 0xdb, 0x01, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -116,6 +126,26 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size := m.AddToGaugeFee.Size() + i -= size + if _, err := m.AddToGaugeFee.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size := m.CreateGaugeFee.Size() + i -= size + if _, err := m.CreateGaugeFee.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 if len(m.DistrEpochIdentifier) > 0 { i -= len(m.DistrEpochIdentifier) copy(dAtA[i:], m.DistrEpochIdentifier) @@ -147,6 +177,10 @@ func (m *Params) Size() (n int) { if l > 0 { n += 1 + l + sovParams(uint64(l)) } + l = m.CreateGaugeFee.Size() + n += 1 + l + sovParams(uint64(l)) + l = m.AddToGaugeFee.Size() + n += 1 + l + sovParams(uint64(l)) return n } @@ -217,6 +251,74 @@ func (m *Params) Unmarshal(dAtA []byte) error { } m.DistrEpochIdentifier = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreateGaugeFee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.CreateGaugeFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AddToGaugeFee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.AddToGaugeFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipParams(dAtA[iNdEx:]) diff --git a/x/rollapp/types/params.pb.go b/x/rollapp/types/params.pb.go index d3b3fad70..e53d22453 100644 --- a/x/rollapp/types/params.pb.go +++ b/x/rollapp/types/params.pb.go @@ -36,7 +36,7 @@ type Params struct { LivenessSlashInterval uint64 `protobuf:"varint,5,opt,name=liveness_slash_interval,json=livenessSlashInterval,proto3" json:"liveness_slash_interval,omitempty" yaml:"liveness_slash_interval"` // The time (num hub blocks) a sequencer can be down after which he will be jailed rather than slashed LivenessJailBlocks uint64 `protobuf:"varint,6,opt,name=liveness_jail_blocks,json=livenessJailBlocks,proto3" json:"liveness_jail_blocks,omitempty" yaml:"liveness_jail_blocks"` - // app_registration_fee is the fee for registering the App + // app_registration_fee is the fee for registering an App AppRegistrationFee types.Coin `protobuf:"bytes,7,opt,name=app_registration_fee,json=appRegistrationFee,proto3" json:"app_registration_fee" yaml:"app_registration_fee"` } From 87d702c3bb64066a53b919aebe58bda2ecf6bac1 Mon Sep 17 00:00:00 2001 From: keruch Date: Wed, 4 Sep 2024 21:44:27 +0200 Subject: [PATCH 2/6] tests fix --- app/upgrades/v3/upgrade_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/upgrades/v3/upgrade_test.go b/app/upgrades/v3/upgrade_test.go index 70fd7bad3..67a37ca06 100644 --- a/app/upgrades/v3/upgrade_test.go +++ b/app/upgrades/v3/upgrade_test.go @@ -10,7 +10,6 @@ import ( cometbftproto "github.com/cometbft/cometbft/proto/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - incentivestypes "github.com/dymensionxyz/dymension/v3/x/incentives/types" "github.com/stretchr/testify/suite" "github.com/dymensionxyz/dymension/v3/app" @@ -39,9 +38,9 @@ var ( DYM = sdk.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) // CreateGaugeFee is the fee required to create a new gauge. - expectCreateGaugeFee = DYM.Mul(sdk.NewInt(10)) + // expectCreateGaugeFee = DYM.Mul(sdk.NewInt(10)) // AddToGaugeFee is the fee required to add to gauge. - expectAddToGaugeFee = sdk.ZeroInt() + // expectAddToGaugeFee = sdk.ZeroInt() expectDelayedackEpochIdentifier = "hour" expectDelayedackBridgingFee = sdk.NewDecWithPrec(1, 3) @@ -102,10 +101,11 @@ func (s *UpgradeTestSuite) TestUpgrade() { return fmt.Errorf("sequencer parameters not set correctly") } + // These fields are deleted in the next update // Check Incentives parameters - if !incentivestypes.CreateGaugeFee.Equal(expectCreateGaugeFee) || !incentivestypes.AddToGaugeFee.Equal(expectAddToGaugeFee) { - return fmt.Errorf("incentives parameters not set correctly") - } + //if !incentivestypes.CreateGaugeFee.Equal(expectCreateGaugeFee) || !incentivestypes.AddToGaugeFee.Equal(expectAddToGaugeFee) { + // return fmt.Errorf("incentives parameters not set correctly") + //} return nil }, From 21117b4213a688cc042e295d45df93e6b0efa2ca Mon Sep 17 00:00:00 2001 From: keruch Date: Thu, 5 Sep 2024 19:07:07 +0200 Subject: [PATCH 3/6] added BaseGasFeeForCreateGaug and BaseGasFeeForAddRewardToGaug params --- .../dymension/incentives/params.proto | 7 + x/incentives/keeper/gauge.go | 13 ++ x/incentives/keeper/gauge_test.go | 196 ++++++++++++++++++ x/incentives/keeper/genesis_test.go | 8 +- x/incentives/types/constants.go | 11 +- x/incentives/types/params.go | 54 ++++- x/incentives/types/params.pb.go | 119 +++++++++-- 7 files changed, 372 insertions(+), 36 deletions(-) diff --git a/proto/dymensionxyz/dymension/incentives/params.proto b/proto/dymensionxyz/dymension/incentives/params.proto index 392f630f6..0e55a7d7f 100644 --- a/proto/dymensionxyz/dymension/incentives/params.proto +++ b/proto/dymensionxyz/dymension/incentives/params.proto @@ -20,4 +20,11 @@ message Params { (gogoproto.nullable) = false, (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int" ]; + // BaseGasFeeForCreateGauge is the gas fee for creating a gauge. + // This gas is charged for each denom while creating a gauge. + uint64 base_gas_fee_for_create_gauge = 4; + // BaseGasFeeForAddRewardToGauge is the gas fee for adding reward to gauges. + // This gas is charged for each denom while adding to a gauge plus for + // each denom the gauge already holds. + uint64 base_gas_fee_for_add_reward_to_gauge = 5; } diff --git a/x/incentives/keeper/gauge.go b/x/incentives/keeper/gauge.go index 158df057d..520abad31 100644 --- a/x/incentives/keeper/gauge.go +++ b/x/incentives/keeper/gauge.go @@ -119,6 +119,12 @@ func (k Keeper) CreateGauge(ctx sdk.Context, isPerpetual bool, owner sdk.AccAddr NumEpochsPaidOver: numEpochsPaidOver, } + // Fixed gas consumption create gauge based on the number of coins to add + baseGasFee := k.GetParams(ctx).BaseGasFeeForCreateGauge + denoms := uint64(len(gauge.Coins)) + // Both baseGasFee and denoms are relatively small, so their multiplication shouldn't lead to overflow in practice + ctx.GasMeter().ConsumeGas(baseGasFee*denoms, "scaling gas cost for creating gauge rewards") + if err := k.bk.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, gauge.Coins); err != nil { return 0, err } @@ -149,6 +155,13 @@ func (k Keeper) AddToGaugeRewards(ctx sdk.Context, owner sdk.AccAddress, coins s if gauge.IsFinishedGauge(ctx.BlockTime()) { return types.UnexpectedFinishedGaugeError{GaugeId: gaugeID} } + + // Fixed gas consumption adding reward to gauges based on the number of coins to add + baseGasFee := k.GetParams(ctx).BaseGasFeeForAddRewardToGauge + denoms := uint64(len(coins) + len(gauge.Coins)) + // Both baseGasFee and denoms are relatively small, so their multiplication shouldn't lead to overflow in practice + ctx.GasMeter().ConsumeGas(baseGasFee*denoms, "scaling gas cost for adding to gauge rewards") + if err := k.bk.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, coins); err != nil { return err } diff --git a/x/incentives/keeper/gauge_test.go b/x/incentives/keeper/gauge_test.go index 4a13041af..d27d51f76 100644 --- a/x/incentives/keeper/gauge_test.go +++ b/x/incentives/keeper/gauge_test.go @@ -1,10 +1,13 @@ package keeper_test import ( + "fmt" "time" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" + "pgregory.net/rapid" "github.com/dymensionxyz/dymension/v3/app/apptesting" "github.com/dymensionxyz/dymension/v3/x/incentives/types" @@ -321,3 +324,196 @@ func (suite *KeeperTestSuite) TestChargeFeeIfSufficientFeeDenomBalance() { }) } } + +func (suite *KeeperTestSuite) TestAddToGaugeRewards() { + params := suite.App.IncentivesKeeper.GetParams(suite.Ctx) + addr := apptesting.CreateRandomAccounts(1)[0] + + testCases := []struct { + name string + owner sdk.AccAddress + coinsToAdd sdk.Coins + gaugeCoins sdk.Coins + gaugeId uint64 + minimumGasConsumed uint64 + + expectErr bool + }{ + { + name: "valid case: valid gauge", + owner: addr, + coinsToAdd: sdk.NewCoins( + sdk.NewCoin("uosmo", sdk.NewInt(100000)), + sdk.NewCoin("atom", sdk.NewInt(99999)), + ), + gaugeCoins: sdk.Coins{ + sdk.NewInt64Coin("stake1", 12), + }, + gaugeId: 1, + minimumGasConsumed: 3 * params.BaseGasFeeForAddRewardToGauge, + expectErr: false, + }, + { + name: "valid case: valid gauge with >4 denoms to add", + owner: addr, + coinsToAdd: sdk.NewCoins( + sdk.NewCoin("uosmo", sdk.NewInt(100000)), + sdk.NewCoin("atom", sdk.NewInt(99999)), + sdk.NewCoin("mars", sdk.NewInt(88888)), + sdk.NewCoin("akash", sdk.NewInt(77777)), + sdk.NewCoin("eth", sdk.NewInt(6666)), + sdk.NewCoin("usdc", sdk.NewInt(555)), + sdk.NewCoin("dai", sdk.NewInt(4444)), + sdk.NewCoin("ust", sdk.NewInt(3333)), + ), + gaugeCoins: sdk.Coins{ + sdk.NewInt64Coin("stake1", 12), + }, + gaugeId: 1, + minimumGasConsumed: 9 * params.BaseGasFeeForAddRewardToGauge, + expectErr: false, + }, + { + name: "valid case: valid gauge with >4 initial denoms", + owner: addr, + coinsToAdd: sdk.NewCoins( + sdk.NewCoin("uosmo", sdk.NewInt(100000)), + sdk.NewCoin("atom", sdk.NewInt(99999)), + sdk.NewCoin("mars", sdk.NewInt(88888)), + sdk.NewCoin("akash", sdk.NewInt(77777)), + sdk.NewCoin("eth", sdk.NewInt(6666)), + sdk.NewCoin("usdc", sdk.NewInt(555)), + sdk.NewCoin("dai", sdk.NewInt(4444)), + sdk.NewCoin("ust", sdk.NewInt(3333)), + ), + gaugeCoins: sdk.Coins{ + sdk.NewCoin("uosmo", sdk.NewInt(100000)), + sdk.NewCoin("atom", sdk.NewInt(99999)), + sdk.NewCoin("mars", sdk.NewInt(88888)), + sdk.NewCoin("akash", sdk.NewInt(77777)), + sdk.NewCoin("eth", sdk.NewInt(6666)), + sdk.NewCoin("usdc", sdk.NewInt(555)), + sdk.NewCoin("dai", sdk.NewInt(4444)), + sdk.NewCoin("ust", sdk.NewInt(3333)), + }, + gaugeId: 1, + minimumGasConsumed: 16 * params.BaseGasFeeForAddRewardToGauge, + expectErr: false, + }, + { + name: "invalid case: gauge Id is not valid", + owner: addr, + coinsToAdd: sdk.NewCoins( + sdk.NewCoin("uosmo", sdk.NewInt(100000)), + sdk.NewCoin("atom", sdk.NewInt(99999)), + ), + gaugeCoins: sdk.Coins{ + sdk.NewInt64Coin("stake1", 12), + sdk.NewInt64Coin("stake2", 12), + sdk.NewInt64Coin("stake3", 12), + }, + gaugeId: 0, + minimumGasConsumed: uint64(0), + expectErr: true, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() + _, _, existingGaugeCoins, _ := suite.SetupNewGauge(true, sdk.NewCoins(tc.gaugeCoins...)) + + suite.FundAcc(tc.owner, tc.coinsToAdd) + + existingGasConsumed := suite.Ctx.GasMeter().GasConsumed() + + err := suite.App.IncentivesKeeper.AddToGaugeRewards(suite.Ctx, tc.owner, tc.coinsToAdd, tc.gaugeId) + if tc.expectErr { + suite.Require().Error(err) + + // balance shouldn't change in the module + balance := suite.App.BankKeeper.GetAllBalances(suite.Ctx, suite.App.AccountKeeper.GetModuleAddress(types.ModuleName)) + suite.Require().Equal(existingGaugeCoins, balance) + + } else { + suite.Require().NoError(err) + + // Ensure that at least the minimum amount of gas was charged (based on number of additional gauge coins) + gasConsumed := suite.Ctx.GasMeter().GasConsumed() - existingGasConsumed + fmt.Println(gasConsumed, tc.minimumGasConsumed) + suite.Require().True(gasConsumed >= tc.minimumGasConsumed) + + // existing coins gets added to the module when we create gauge and add to gauge + expectedCoins := existingGaugeCoins.Add(tc.coinsToAdd...) + + // check module account balance, should go up + balance := suite.App.BankKeeper.GetAllBalances(suite.Ctx, suite.App.AccountKeeper.GetModuleAddress(types.ModuleName)) + suite.Require().Equal(expectedCoins, balance) + + // check gauge coins should go up + gauge, err := suite.App.IncentivesKeeper.GetGaugeByID(suite.Ctx, tc.gaugeId) + suite.Require().NoError(err) + + suite.Require().Equal(expectedCoins, gauge.Coins) + } + }) + } +} + +func (s *KeeperTestSuite) TestRapidTestAddToGaugeRewards() { + rapid.Check(s.T(), func(t *rapid.T) { + // Generate random data + existingDenoms := make(map[string]struct{}) + gcGen := rapid.Custom[sdk.Coin](func(t *rapid.T) sdk.Coin { + return sdk.Coin{ + Denom: rapid.StringOfN(rapid.RuneFrom([]rune{'a', 'b', 'c'}), 5, 100, -1). + Filter(func(s string) bool { + _, ok := existingDenoms[s] + existingDenoms[s] = struct{}{} + return !ok + }). + Draw(t, "denom"), + Amount: math.NewInt(rapid.Int64Range(1, 100_000).Draw(t, "coins")), + } + }) + gaugeCoins := sdk.NewCoins(rapid.SliceOfN[sdk.Coin](gcGen, 1, 100_000).Draw(t, "gaugeCoins")...) + coinsToAdd := sdk.NewCoins(rapid.SliceOfN[sdk.Coin](gcGen, 1, 100_000).Draw(t, "coinsToAdd")...) + + s.SetupTest() + + // Create a new gauge + _, _, existingGaugeCoins, _ := s.SetupNewGauge(true, gaugeCoins) + owner := apptesting.CreateRandomAccounts(1)[0] + // Fund the owner account + s.FundAcc(owner, coinsToAdd) + + // Save the gas meter before the method call + existingGasConsumed := s.Ctx.GasMeter().GasConsumed() + + // AddToGaugeRewards + err := s.App.IncentivesKeeper.AddToGaugeRewards(s.Ctx, owner, coinsToAdd, 1) + s.Require().NoError(err) + + // Min expected gas consumed + baseGasFee := s.App.IncentivesKeeper.GetParams(s.Ctx).BaseGasFeeForAddRewardToGauge + minimumGasConsumed := baseGasFee * uint64(len(gaugeCoins)+len(coinsToAdd)) + + // Ensure that at least the minimum amount of gas was charged (based on number of additional gauge coins) + gasConsumed := s.Ctx.GasMeter().GasConsumed() - existingGasConsumed + fmt.Println(gasConsumed, minimumGasConsumed) + s.Require().True(gasConsumed >= minimumGasConsumed) + + // Existing coins gets added to the module when we create gauge and add to gauge + expectedCoins := existingGaugeCoins.Add(coinsToAdd...) + + // Check module account balance, should go up + balance := s.App.BankKeeper.GetAllBalances(s.Ctx, s.App.AccountKeeper.GetModuleAddress(types.ModuleName)) + s.Require().Equal(expectedCoins, balance) + + // Check gauge coins should go up + gauge, err := s.App.IncentivesKeeper.GetGaugeByID(s.Ctx, 1) + s.Require().NoError(err) + + s.Require().Equal(expectedCoins, gauge.Coins) + }) +} diff --git a/x/incentives/keeper/genesis_test.go b/x/incentives/keeper/genesis_test.go index 0faf83085..b4832bdda 100644 --- a/x/incentives/keeper/genesis_test.go +++ b/x/incentives/keeper/genesis_test.go @@ -96,9 +96,11 @@ func TestIncentivesInitGenesis(t *testing.T) { // initialize genesis with specified parameter, the gauge created earlier, and lockable durations app.IncentivesKeeper.InitGenesis(ctx, types.GenesisState{ Params: types.Params{ - DistrEpochIdentifier: "week", - CreateGaugeFee: sdk.ZeroInt(), - AddToGaugeFee: sdk.ZeroInt(), + DistrEpochIdentifier: "week", + CreateGaugeFee: sdk.ZeroInt(), + AddToGaugeFee: sdk.ZeroInt(), + BaseGasFeeForCreateGauge: 0, + BaseGasFeeForAddRewardToGauge: 0, }, Gauges: []types.Gauge{gauge}, LockableDurations: []time.Duration{ diff --git a/x/incentives/types/constants.go b/x/incentives/types/constants.go index e6ee77606..e2ab7020c 100644 --- a/x/incentives/types/constants.go +++ b/x/incentives/types/constants.go @@ -11,7 +11,12 @@ var ( // DYM represents 1 DYM DYM = math.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) - DefaultDistrEpochIdentifier = "week" - DefaultCreateGaugeFee = DYM.Mul(sdk.NewInt(10)) - DefaultAddToGaugeFee = DYM + DefaultCreateGaugeFee = DYM.Mul(sdk.NewInt(10)) + DefaultAddToGaugeFee = DYM +) + +const ( + DefaultDistrEpochIdentifier = "week" + DefaultBaseGasFeeForCreateGauge uint64 = 10_000 + DefaultBaseGasFeeForAddRewardToGauge uint64 = 10_000 ) diff --git a/x/incentives/types/params.go b/x/incentives/types/params.go index d482cf196..88540349e 100644 --- a/x/incentives/types/params.go +++ b/x/incentives/types/params.go @@ -12,9 +12,11 @@ import ( // Incentives parameters key store. var ( - KeyDistrEpochIdentifier = []byte("DistrEpochIdentifier") - KeyCreateGaugeFee = []byte("CreateGaugeFee") - KeyAddToGaugeFee = []byte("AddToGaugeFee") + KeyDistrEpochIdentifier = []byte("DistrEpochIdentifier") + KeyCreateGaugeFee = []byte("CreateGaugeFee") + KeyAddToGaugeFee = []byte("AddToGaugeFee") + KeyBaseGasFeeForCreateGauge = []byte("BaseGasFeeForCreateGauge") + KeyBaseGasFeeForAddRewardToGauge = []byte("BaseGasFeeForAddRewardToGauge") ) // ParamKeyTable returns the key table for the incentive module's parameters. @@ -23,20 +25,28 @@ func ParamKeyTable() paramtypes.KeyTable { } // NewParams takes an epoch distribution identifier, then returns an incentives Params struct. -func NewParams(distrEpochIdentifier string, createGaugeFee, addToGaugeFee math.Int) Params { +func NewParams( + distrEpochIdentifier string, + createGaugeFee, addToGaugeFee math.Int, + baseGasFeeForCreateGauge, baseGasFeeForAddRewardToGauge uint64, +) Params { return Params{ - DistrEpochIdentifier: distrEpochIdentifier, - CreateGaugeFee: createGaugeFee, - AddToGaugeFee: addToGaugeFee, + DistrEpochIdentifier: distrEpochIdentifier, + CreateGaugeFee: createGaugeFee, + AddToGaugeFee: addToGaugeFee, + BaseGasFeeForCreateGauge: baseGasFeeForCreateGauge, + BaseGasFeeForAddRewardToGauge: baseGasFeeForAddRewardToGauge, } } // DefaultParams returns the default incentives module parameters. func DefaultParams() Params { return Params{ - DistrEpochIdentifier: DefaultDistrEpochIdentifier, - CreateGaugeFee: DefaultCreateGaugeFee, - AddToGaugeFee: DefaultAddToGaugeFee, + DistrEpochIdentifier: DefaultDistrEpochIdentifier, + CreateGaugeFee: DefaultCreateGaugeFee, + AddToGaugeFee: DefaultAddToGaugeFee, + BaseGasFeeForCreateGauge: DefaultBaseGasFeeForCreateGauge, + BaseGasFeeForAddRewardToGauge: DefaultBaseGasFeeForAddRewardToGauge, } } @@ -51,6 +61,12 @@ func (p Params) Validate() error { if err := validateAddToGaugeFeeInterface(p.AddToGaugeFee); err != nil { return err } + if err := validateBaseGasFeeForCreateGauge(p.BaseGasFeeForCreateGauge); err != nil { + return err + } + if err := validateBaseGasFeeForAddRewardToGauge(p.BaseGasFeeForAddRewardToGauge); err != nil { + return err + } return nil } @@ -60,6 +76,8 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { paramtypes.NewParamSetPair(KeyDistrEpochIdentifier, &p.DistrEpochIdentifier, epochtypes.ValidateEpochIdentifierInterface), paramtypes.NewParamSetPair(KeyCreateGaugeFee, &p.CreateGaugeFee, validateCreateGaugeFeeInterface), paramtypes.NewParamSetPair(KeyAddToGaugeFee, &p.AddToGaugeFee, validateAddToGaugeFeeInterface), + paramtypes.NewParamSetPair(KeyBaseGasFeeForCreateGauge, &p.BaseGasFeeForCreateGauge, validateBaseGasFeeForCreateGauge), + paramtypes.NewParamSetPair(KeyBaseGasFeeForAddRewardToGauge, &p.BaseGasFeeForAddRewardToGauge, validateBaseGasFeeForAddRewardToGauge), } } @@ -84,3 +102,19 @@ func validateAddToGaugeFeeInterface(i interface{}) error { } return nil } + +func validateBaseGasFeeForCreateGauge(i interface{}) error { + _, ok := i.(uint64) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + return nil +} + +func validateBaseGasFeeForAddRewardToGauge(i interface{}) error { + _, ok := i.(uint64) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + return nil +} diff --git a/x/incentives/types/params.pb.go b/x/incentives/types/params.pb.go index 24844e96d..732131597 100644 --- a/x/incentives/types/params.pb.go +++ b/x/incentives/types/params.pb.go @@ -33,6 +33,13 @@ type Params struct { CreateGaugeFee github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=create_gauge_fee,json=createGaugeFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"create_gauge_fee"` // AddToGaugeFee is the fee required to add to gauge. AddToGaugeFee github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=add_to_gauge_fee,json=addToGaugeFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"add_to_gauge_fee"` + // BaseGasFeeForCreateGauge is the gas fee for creating a gauge. + // This gas is charged for each denom while creating a gauge. + BaseGasFeeForCreateGauge uint64 `protobuf:"varint,4,opt,name=base_gas_fee_for_create_gauge,json=baseGasFeeForCreateGauge,proto3" json:"base_gas_fee_for_create_gauge,omitempty"` + // BaseGasFeeForAddRewardToGauge is the gas fee for adding reward to gauges. + // This gas is charged for each denom while adding to a gauge plus for + // each denom the gauge already holds. + BaseGasFeeForAddRewardToGauge uint64 `protobuf:"varint,5,opt,name=base_gas_fee_for_add_reward_to_gauge,json=baseGasFeeForAddRewardToGauge,proto3" json:"base_gas_fee_for_add_reward_to_gauge,omitempty"` } func (m *Params) Reset() { *m = Params{} } @@ -75,6 +82,20 @@ func (m *Params) GetDistrEpochIdentifier() string { return "" } +func (m *Params) GetBaseGasFeeForCreateGauge() uint64 { + if m != nil { + return m.BaseGasFeeForCreateGauge + } + return 0 +} + +func (m *Params) GetBaseGasFeeForAddRewardToGauge() uint64 { + if m != nil { + return m.BaseGasFeeForAddRewardToGauge + } + return 0 +} + func init() { proto.RegisterType((*Params)(nil), "dymensionxyz.dymension.incentives.Params") } @@ -84,26 +105,30 @@ func init() { } var fileDescriptor_256a114c8e13cfa0 = []byte{ - // 299 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4b, 0xa9, 0xcc, 0x4d, - 0xcd, 0x2b, 0xce, 0xcc, 0xcf, 0xab, 0xa8, 0xac, 0xd2, 0x87, 0x73, 0xf4, 0x33, 0xf3, 0x92, 0x53, - 0xf3, 0x4a, 0x32, 0xcb, 0x52, 0x8b, 0xf5, 0x0b, 0x12, 0x8b, 0x12, 0x73, 0x8b, 0xf5, 0x0a, 0x8a, - 0xf2, 0x4b, 0xf2, 0x85, 0x14, 0x91, 0xd5, 0x23, 0x34, 0xeb, 0x21, 0xd4, 0x4b, 0x89, 0xa4, 0xe7, - 0xa7, 0xe7, 0x83, 0x55, 0xeb, 0x83, 0x58, 0x10, 0x8d, 0x4a, 0x53, 0x98, 0xb8, 0xd8, 0x02, 0xc0, - 0x26, 0x09, 0x85, 0x73, 0x89, 0xa5, 0x64, 0x16, 0x97, 0x14, 0xc5, 0xa7, 0x16, 0xe4, 0x27, 0x67, - 0xc4, 0x67, 0xa6, 0x80, 0x74, 0xa6, 0x65, 0xa6, 0x16, 0x49, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x3a, - 0x29, 0x7e, 0xba, 0x27, 0x2f, 0x5b, 0x99, 0x98, 0x9b, 0x63, 0xa5, 0x84, 0x5d, 0x9d, 0x52, 0x90, - 0x08, 0x58, 0xc2, 0x15, 0x24, 0xee, 0x09, 0x17, 0x16, 0x8a, 0xe0, 0x12, 0x48, 0x2e, 0x4a, 0x4d, - 0x2c, 0x49, 0x8d, 0x4f, 0x4f, 0x2c, 0x4d, 0x4f, 0x8d, 0x4f, 0x4b, 0x4d, 0x95, 0x60, 0x02, 0x1b, - 0xa9, 0x77, 0xe2, 0x9e, 0x3c, 0xc3, 0xad, 0x7b, 0xf2, 0x6a, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, - 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0xc9, 0xf9, 0xc5, 0xb9, 0xf9, 0xc5, 0x50, 0x4a, 0xb7, 0x38, 0x25, - 0x5b, 0xbf, 0xa4, 0xb2, 0x20, 0xb5, 0x58, 0xcf, 0x33, 0xaf, 0x24, 0x88, 0x0f, 0x62, 0x8e, 0x3b, - 0xc8, 0x18, 0xb7, 0xd4, 0x54, 0xa1, 0x70, 0x2e, 0x81, 0xc4, 0x94, 0x94, 0xf8, 0x92, 0x7c, 0x24, - 0x93, 0x99, 0xc9, 0x32, 0x99, 0x37, 0x31, 0x25, 0x25, 0x24, 0x1f, 0x66, 0xb0, 0x53, 0xe0, 0x89, - 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, - 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x99, 0x23, 0x19, 0x88, 0x23, 0x92, 0xca, - 0x8c, 0xf5, 0x2b, 0x90, 0x63, 0x0a, 0x6c, 0x4b, 0x12, 0x1b, 0x38, 0xc0, 0x8d, 0x01, 0x01, 0x00, - 0x00, 0xff, 0xff, 0xdc, 0xe1, 0xcd, 0x80, 0xdb, 0x01, 0x00, 0x00, + // 368 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0x4f, 0x4a, 0xc3, 0x40, + 0x14, 0x87, 0x13, 0x5b, 0x0b, 0x06, 0x94, 0x12, 0x8a, 0x04, 0xa1, 0xe9, 0x1f, 0x44, 0xba, 0x31, + 0x59, 0x74, 0x21, 0xb8, 0x11, 0x2b, 0xb6, 0x14, 0x37, 0x1a, 0x84, 0x8a, 0x9b, 0x30, 0xcd, 0xbc, + 0xa6, 0x41, 0x93, 0x09, 0x33, 0xd3, 0xda, 0x78, 0x0a, 0xcf, 0xe0, 0x69, 0xba, 0xec, 0x52, 0x5c, + 0x14, 0x69, 0x6f, 0xe0, 0x09, 0x64, 0xa6, 0xa5, 0x89, 0xa8, 0x1b, 0x57, 0xc9, 0xbc, 0xf9, 0xde, + 0xc7, 0xef, 0x31, 0x4f, 0xb3, 0x70, 0x12, 0x42, 0xc4, 0x02, 0x12, 0x4d, 0x92, 0x67, 0x7b, 0x73, + 0xb0, 0x83, 0xc8, 0x83, 0x88, 0x07, 0x63, 0x60, 0x76, 0x8c, 0x28, 0x0a, 0x99, 0x15, 0x53, 0xc2, + 0x89, 0x5e, 0xcb, 0xf2, 0x69, 0xb3, 0x95, 0xf2, 0x07, 0x25, 0x9f, 0xf8, 0x44, 0xd2, 0xb6, 0xf8, + 0x5b, 0x35, 0xd6, 0x5f, 0x73, 0x5a, 0xe1, 0x5a, 0x9a, 0xf4, 0x9e, 0xb6, 0x8f, 0x03, 0xc6, 0xa9, + 0x0b, 0x31, 0xf1, 0x86, 0x6e, 0x80, 0x45, 0xe7, 0x20, 0x00, 0x6a, 0xa8, 0x55, 0xb5, 0xb1, 0xd3, + 0xaa, 0x7d, 0xce, 0x2b, 0xe5, 0x04, 0x85, 0x8f, 0xa7, 0xf5, 0xdf, 0xb9, 0xba, 0x53, 0x92, 0x17, + 0x97, 0xa2, 0xde, 0xdd, 0x94, 0xf5, 0x3b, 0xad, 0xe8, 0x51, 0x40, 0x1c, 0x5c, 0x1f, 0x8d, 0x7c, + 0x70, 0x07, 0x00, 0xc6, 0x96, 0x54, 0x5a, 0xd3, 0x79, 0x45, 0x79, 0x9f, 0x57, 0x8e, 0xfc, 0x80, + 0x0f, 0x47, 0x7d, 0xcb, 0x23, 0xa1, 0xed, 0x11, 0x16, 0x12, 0xb6, 0xfe, 0x1c, 0x33, 0xfc, 0x60, + 0xf3, 0x24, 0x06, 0x66, 0x75, 0x23, 0xee, 0xec, 0xad, 0x3c, 0x1d, 0xa1, 0x69, 0x03, 0xe8, 0x3d, + 0xad, 0x88, 0x30, 0x76, 0x39, 0xc9, 0x98, 0x73, 0xff, 0x32, 0xef, 0x22, 0x8c, 0x6f, 0xc9, 0x46, + 0x7c, 0xa6, 0x95, 0xfb, 0x88, 0x89, 0xc0, 0x4c, 0x48, 0xdd, 0x01, 0xa1, 0x6e, 0x76, 0x06, 0x23, + 0x5f, 0x55, 0x1b, 0x79, 0xc7, 0x10, 0x50, 0x07, 0xb1, 0x36, 0x40, 0x9b, 0xd0, 0x8b, 0x34, 0x9c, + 0x7e, 0xa5, 0x1d, 0xfe, 0x10, 0x88, 0xa8, 0x14, 0x9e, 0x10, 0x4d, 0x13, 0x1b, 0xdb, 0xd2, 0x53, + 0xfe, 0xe6, 0x39, 0xc7, 0xd8, 0x91, 0xd8, 0x3a, 0x50, 0xeb, 0x66, 0xba, 0x30, 0xd5, 0xd9, 0xc2, + 0x54, 0x3f, 0x16, 0xa6, 0xfa, 0xb2, 0x34, 0x95, 0xd9, 0xd2, 0x54, 0xde, 0x96, 0xa6, 0x72, 0x7f, + 0x92, 0x19, 0xef, 0x8f, 0x95, 0x19, 0x37, 0xed, 0x49, 0x76, 0x6f, 0xe4, 0xcc, 0xfd, 0x82, 0x7c, + 0xfe, 0xe6, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7c, 0x6e, 0x73, 0x8d, 0x69, 0x02, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -126,6 +151,16 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.BaseGasFeeForAddRewardToGauge != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.BaseGasFeeForAddRewardToGauge)) + i-- + dAtA[i] = 0x28 + } + if m.BaseGasFeeForCreateGauge != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.BaseGasFeeForCreateGauge)) + i-- + dAtA[i] = 0x20 + } { size := m.AddToGaugeFee.Size() i -= size @@ -181,6 +216,12 @@ func (m *Params) Size() (n int) { n += 1 + l + sovParams(uint64(l)) l = m.AddToGaugeFee.Size() n += 1 + l + sovParams(uint64(l)) + if m.BaseGasFeeForCreateGauge != 0 { + n += 1 + sovParams(uint64(m.BaseGasFeeForCreateGauge)) + } + if m.BaseGasFeeForAddRewardToGauge != 0 { + n += 1 + sovParams(uint64(m.BaseGasFeeForAddRewardToGauge)) + } return n } @@ -319,6 +360,44 @@ func (m *Params) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BaseGasFeeForCreateGauge", wireType) + } + m.BaseGasFeeForCreateGauge = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BaseGasFeeForCreateGauge |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BaseGasFeeForAddRewardToGauge", wireType) + } + m.BaseGasFeeForAddRewardToGauge = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BaseGasFeeForAddRewardToGauge |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipParams(dAtA[iNdEx:]) From c28e8f10b7da32456875d5733dd4200b89159460 Mon Sep 17 00:00:00 2001 From: keruch Date: Tue, 10 Sep 2024 10:00:25 +0200 Subject: [PATCH 4/6] threads --- app/upgrades/v3/upgrade_test.go | 3 ++- app/upgrades/v4/upgrade.go | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/upgrades/v3/upgrade_test.go b/app/upgrades/v3/upgrade_test.go index 67a37ca06..5f152a474 100644 --- a/app/upgrades/v3/upgrade_test.go +++ b/app/upgrades/v3/upgrade_test.go @@ -101,7 +101,8 @@ func (s *UpgradeTestSuite) TestUpgrade() { return fmt.Errorf("sequencer parameters not set correctly") } - // These fields are deleted in the next update + // These fields are deleted in the v4 update. Intentionally leave the commented code + // here for historical reference. // Check Incentives parameters //if !incentivestypes.CreateGaugeFee.Equal(expectCreateGaugeFee) || !incentivestypes.AddToGaugeFee.Equal(expectAddToGaugeFee) { // return fmt.Errorf("incentives parameters not set correctly") diff --git a/app/upgrades/v4/upgrade.go b/app/upgrades/v4/upgrade.go index c4a3e1b5e..c8a14893f 100644 --- a/app/upgrades/v4/upgrade.go +++ b/app/upgrades/v4/upgrade.go @@ -181,6 +181,8 @@ func migrateIncentivesParams(ctx sdk.Context, ik *incentiveskeeper.Keeper) { defaultParams := incentivestypes.DefaultParams() params.CreateGaugeFee = defaultParams.CreateGaugeFee params.AddToGaugeFee = defaultParams.AddToGaugeFee + params.BaseGasFeeForCreateGauge = defaultParams.BaseGasFeeForCreateGauge + params.BaseGasFeeForAddRewardToGauge = defaultParams.BaseGasFeeForAddRewardToGauge ik.SetParams(ctx, params) } From 380559fe75b045d16fb07584b24876372998a82c Mon Sep 17 00:00:00 2001 From: keruch Date: Thu, 12 Sep 2024 18:15:06 +0200 Subject: [PATCH 5/6] deleted gas charges --- app/upgrades/v4/upgrade.go | 7 +- .../dymension/incentives/params.proto | 24 +- x/incentives/keeper/gauge.go | 28 +- x/incentives/keeper/gauge_test.go | 200 +-------- x/incentives/keeper/genesis_test.go | 14 +- x/incentives/keeper/msg_server.go | 9 - x/incentives/keeper/msg_server_test.go | 403 ++++++++++++------ x/incentives/keeper/suite_test.go | 6 +- x/incentives/types/constants.go | 9 +- x/incentives/types/params.go | 62 +-- x/incentives/types/params.pb.go | 165 +++---- 11 files changed, 406 insertions(+), 521 deletions(-) diff --git a/app/upgrades/v4/upgrade.go b/app/upgrades/v4/upgrade.go index 94647dec8..60d3cb64e 100644 --- a/app/upgrades/v4/upgrade.go +++ b/app/upgrades/v4/upgrade.go @@ -196,10 +196,9 @@ func migrateStreamer(ctx sdk.Context, sk streamerkeeper.Keeper, ek *epochskeeper func migrateIncentivesParams(ctx sdk.Context, ik *incentiveskeeper.Keeper) { params := ik.GetParams(ctx) defaultParams := incentivestypes.DefaultParams() - params.CreateGaugeFee = defaultParams.CreateGaugeFee - params.AddToGaugeFee = defaultParams.AddToGaugeFee - params.BaseGasFeeForCreateGauge = defaultParams.BaseGasFeeForCreateGauge - params.BaseGasFeeForAddRewardToGauge = defaultParams.BaseGasFeeForAddRewardToGauge + params.CreateGaugeBaseFee = defaultParams.CreateGaugeBaseFee + params.AddToGaugeBaseFee = defaultParams.AddToGaugeBaseFee + params.AddDenomFee = defaultParams.AddDenomFee ik.SetParams(ctx, params) } diff --git a/proto/dymensionxyz/dymension/incentives/params.proto b/proto/dymensionxyz/dymension/incentives/params.proto index 0e55a7d7f..0e93c2407 100644 --- a/proto/dymensionxyz/dymension/incentives/params.proto +++ b/proto/dymensionxyz/dymension/incentives/params.proto @@ -10,21 +10,23 @@ message Params { // (day, week, etc.) string distr_epoch_identifier = 1 [ (gogoproto.moretags) = "yaml:\"distr_epoch_identifier\"" ]; - // CreateGaugeFee is the fee required to create a new gauge. - string create_gauge_fee = 2 [ + // CreateGaugeBaseFee is a base fee required to create a new gauge. The final + // fee is calculated as + // Fee = CreateGaugeBaseFee + AddDenomFee * (len(Denoms) + len(GaugeDenoms)). + string create_gauge_base_fee = 2 [ (gogoproto.nullable) = false, (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int" ]; - // AddToGaugeFee is the fee required to add to gauge. - string add_to_gauge_fee = 3 [ + // AddToGaugeBaseFee is a base fee required to add to gauge. The final + // fee is calculated as + // Fee = AddToGaugeBaseFee + AddDenomFee * len(Denoms). + string add_to_gauge_base_fee = 3 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int" + ]; + // AddDenomFee is a fee charged for adding every new denom to the gauge. + string add_denom_fee = 4 [ (gogoproto.nullable) = false, (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int" ]; - // BaseGasFeeForCreateGauge is the gas fee for creating a gauge. - // This gas is charged for each denom while creating a gauge. - uint64 base_gas_fee_for_create_gauge = 4; - // BaseGasFeeForAddRewardToGauge is the gas fee for adding reward to gauges. - // This gas is charged for each denom while adding to a gauge plus for - // each denom the gauge already holds. - uint64 base_gas_fee_for_add_reward_to_gauge = 5; } diff --git a/x/incentives/keeper/gauge.go b/x/incentives/keeper/gauge.go index 520abad31..144de30d0 100644 --- a/x/incentives/keeper/gauge.go +++ b/x/incentives/keeper/gauge.go @@ -110,6 +110,14 @@ func (k Keeper) CreateGauge(ctx sdk.Context, isPerpetual bool, owner sdk.AccAddr return 0, fmt.Errorf("denom does not exist: %s", distrTo.Denom) } + // Charge fess based on the number of coins to add + // Fee = CreateGaugeBaseFee + AddDenomFee * NumDenoms + params := k.GetParams(ctx) + fee := params.CreateGaugeBaseFee.Add(params.AddDenomFee.MulRaw(int64(len(coins)))) + if err := k.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, fee, coins); err != nil { + return 0, err + } + gauge := types.Gauge{ Id: k.GetLastGaugeID(ctx) + 1, IsPerpetual: isPerpetual, @@ -119,12 +127,6 @@ func (k Keeper) CreateGauge(ctx sdk.Context, isPerpetual bool, owner sdk.AccAddr NumEpochsPaidOver: numEpochsPaidOver, } - // Fixed gas consumption create gauge based on the number of coins to add - baseGasFee := k.GetParams(ctx).BaseGasFeeForCreateGauge - denoms := uint64(len(gauge.Coins)) - // Both baseGasFee and denoms are relatively small, so their multiplication shouldn't lead to overflow in practice - ctx.GasMeter().ConsumeGas(baseGasFee*denoms, "scaling gas cost for creating gauge rewards") - if err := k.bk.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, gauge.Coins); err != nil { return 0, err } @@ -156,13 +158,15 @@ func (k Keeper) AddToGaugeRewards(ctx sdk.Context, owner sdk.AccAddress, coins s return types.UnexpectedFinishedGaugeError{GaugeId: gaugeID} } - // Fixed gas consumption adding reward to gauges based on the number of coins to add - baseGasFee := k.GetParams(ctx).BaseGasFeeForAddRewardToGauge - denoms := uint64(len(coins) + len(gauge.Coins)) - // Both baseGasFee and denoms are relatively small, so their multiplication shouldn't lead to overflow in practice - ctx.GasMeter().ConsumeGas(baseGasFee*denoms, "scaling gas cost for adding to gauge rewards") + // Charge fess based on the number of coins to add + // Fee = AddToGaugeBaseFee + AddDenomFee * (NumAddedDenoms + NumGaugeDenoms) + params := k.GetParams(ctx) + fee := params.AddToGaugeBaseFee.Add(params.AddDenomFee.MulRaw(int64(len(coins) + len(gauge.Coins)))) + if err = k.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, fee, coins); err != nil { + return err + } - if err := k.bk.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, coins); err != nil { + if err = k.bk.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, coins); err != nil { return err } diff --git a/x/incentives/keeper/gauge_test.go b/x/incentives/keeper/gauge_test.go index d27d51f76..fad58f47d 100644 --- a/x/incentives/keeper/gauge_test.go +++ b/x/incentives/keeper/gauge_test.go @@ -1,13 +1,10 @@ package keeper_test import ( - "fmt" "time" - "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - "pgregory.net/rapid" "github.com/dymensionxyz/dymension/v3/app/apptesting" "github.com/dymensionxyz/dymension/v3/x/incentives/types" @@ -26,6 +23,8 @@ func (suite *KeeperTestSuite) TestInvalidDurationGaugeCreationValidation() { Denom: defaultLPDenom, Duration: defaultLockDuration / 2, // 0.5 second, invalid duration } + // add tokens for fees + suite.FundAcc(addrs[0], sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, types.DefaultCreateGaugeFee.MulRaw(2)))) _, err := suite.App.IncentivesKeeper.CreateGauge(suite.Ctx, false, addrs[0], defaultLiquidTokens, distrTo, time.Time{}, 1) suite.Require().Error(err) @@ -45,6 +44,8 @@ func (suite *KeeperTestSuite) TestNonExistentDenomGaugeCreation() { Denom: defaultLPDenom, Duration: defaultLockDuration, } + // add tokens for fees + suite.FundAcc(addrs[0], sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, types.DefaultCreateGaugeFee.MulRaw(2)))) _, err := suite.App.IncentivesKeeper.CreateGauge(suite.Ctx, false, addrNoSupply, defaultLiquidTokens, distrTo, time.Time{}, 1) suite.Require().Error(err) @@ -324,196 +325,3 @@ func (suite *KeeperTestSuite) TestChargeFeeIfSufficientFeeDenomBalance() { }) } } - -func (suite *KeeperTestSuite) TestAddToGaugeRewards() { - params := suite.App.IncentivesKeeper.GetParams(suite.Ctx) - addr := apptesting.CreateRandomAccounts(1)[0] - - testCases := []struct { - name string - owner sdk.AccAddress - coinsToAdd sdk.Coins - gaugeCoins sdk.Coins - gaugeId uint64 - minimumGasConsumed uint64 - - expectErr bool - }{ - { - name: "valid case: valid gauge", - owner: addr, - coinsToAdd: sdk.NewCoins( - sdk.NewCoin("uosmo", sdk.NewInt(100000)), - sdk.NewCoin("atom", sdk.NewInt(99999)), - ), - gaugeCoins: sdk.Coins{ - sdk.NewInt64Coin("stake1", 12), - }, - gaugeId: 1, - minimumGasConsumed: 3 * params.BaseGasFeeForAddRewardToGauge, - expectErr: false, - }, - { - name: "valid case: valid gauge with >4 denoms to add", - owner: addr, - coinsToAdd: sdk.NewCoins( - sdk.NewCoin("uosmo", sdk.NewInt(100000)), - sdk.NewCoin("atom", sdk.NewInt(99999)), - sdk.NewCoin("mars", sdk.NewInt(88888)), - sdk.NewCoin("akash", sdk.NewInt(77777)), - sdk.NewCoin("eth", sdk.NewInt(6666)), - sdk.NewCoin("usdc", sdk.NewInt(555)), - sdk.NewCoin("dai", sdk.NewInt(4444)), - sdk.NewCoin("ust", sdk.NewInt(3333)), - ), - gaugeCoins: sdk.Coins{ - sdk.NewInt64Coin("stake1", 12), - }, - gaugeId: 1, - minimumGasConsumed: 9 * params.BaseGasFeeForAddRewardToGauge, - expectErr: false, - }, - { - name: "valid case: valid gauge with >4 initial denoms", - owner: addr, - coinsToAdd: sdk.NewCoins( - sdk.NewCoin("uosmo", sdk.NewInt(100000)), - sdk.NewCoin("atom", sdk.NewInt(99999)), - sdk.NewCoin("mars", sdk.NewInt(88888)), - sdk.NewCoin("akash", sdk.NewInt(77777)), - sdk.NewCoin("eth", sdk.NewInt(6666)), - sdk.NewCoin("usdc", sdk.NewInt(555)), - sdk.NewCoin("dai", sdk.NewInt(4444)), - sdk.NewCoin("ust", sdk.NewInt(3333)), - ), - gaugeCoins: sdk.Coins{ - sdk.NewCoin("uosmo", sdk.NewInt(100000)), - sdk.NewCoin("atom", sdk.NewInt(99999)), - sdk.NewCoin("mars", sdk.NewInt(88888)), - sdk.NewCoin("akash", sdk.NewInt(77777)), - sdk.NewCoin("eth", sdk.NewInt(6666)), - sdk.NewCoin("usdc", sdk.NewInt(555)), - sdk.NewCoin("dai", sdk.NewInt(4444)), - sdk.NewCoin("ust", sdk.NewInt(3333)), - }, - gaugeId: 1, - minimumGasConsumed: 16 * params.BaseGasFeeForAddRewardToGauge, - expectErr: false, - }, - { - name: "invalid case: gauge Id is not valid", - owner: addr, - coinsToAdd: sdk.NewCoins( - sdk.NewCoin("uosmo", sdk.NewInt(100000)), - sdk.NewCoin("atom", sdk.NewInt(99999)), - ), - gaugeCoins: sdk.Coins{ - sdk.NewInt64Coin("stake1", 12), - sdk.NewInt64Coin("stake2", 12), - sdk.NewInt64Coin("stake3", 12), - }, - gaugeId: 0, - minimumGasConsumed: uint64(0), - expectErr: true, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - _, _, existingGaugeCoins, _ := suite.SetupNewGauge(true, sdk.NewCoins(tc.gaugeCoins...)) - - suite.FundAcc(tc.owner, tc.coinsToAdd) - - existingGasConsumed := suite.Ctx.GasMeter().GasConsumed() - - err := suite.App.IncentivesKeeper.AddToGaugeRewards(suite.Ctx, tc.owner, tc.coinsToAdd, tc.gaugeId) - if tc.expectErr { - suite.Require().Error(err) - - // balance shouldn't change in the module - balance := suite.App.BankKeeper.GetAllBalances(suite.Ctx, suite.App.AccountKeeper.GetModuleAddress(types.ModuleName)) - suite.Require().Equal(existingGaugeCoins, balance) - - } else { - suite.Require().NoError(err) - - // Ensure that at least the minimum amount of gas was charged (based on number of additional gauge coins) - gasConsumed := suite.Ctx.GasMeter().GasConsumed() - existingGasConsumed - fmt.Println(gasConsumed, tc.minimumGasConsumed) - suite.Require().True(gasConsumed >= tc.minimumGasConsumed) - - // existing coins gets added to the module when we create gauge and add to gauge - expectedCoins := existingGaugeCoins.Add(tc.coinsToAdd...) - - // check module account balance, should go up - balance := suite.App.BankKeeper.GetAllBalances(suite.Ctx, suite.App.AccountKeeper.GetModuleAddress(types.ModuleName)) - suite.Require().Equal(expectedCoins, balance) - - // check gauge coins should go up - gauge, err := suite.App.IncentivesKeeper.GetGaugeByID(suite.Ctx, tc.gaugeId) - suite.Require().NoError(err) - - suite.Require().Equal(expectedCoins, gauge.Coins) - } - }) - } -} - -func (s *KeeperTestSuite) TestRapidTestAddToGaugeRewards() { - rapid.Check(s.T(), func(t *rapid.T) { - // Generate random data - existingDenoms := make(map[string]struct{}) - gcGen := rapid.Custom[sdk.Coin](func(t *rapid.T) sdk.Coin { - return sdk.Coin{ - Denom: rapid.StringOfN(rapid.RuneFrom([]rune{'a', 'b', 'c'}), 5, 100, -1). - Filter(func(s string) bool { - _, ok := existingDenoms[s] - existingDenoms[s] = struct{}{} - return !ok - }). - Draw(t, "denom"), - Amount: math.NewInt(rapid.Int64Range(1, 100_000).Draw(t, "coins")), - } - }) - gaugeCoins := sdk.NewCoins(rapid.SliceOfN[sdk.Coin](gcGen, 1, 100_000).Draw(t, "gaugeCoins")...) - coinsToAdd := sdk.NewCoins(rapid.SliceOfN[sdk.Coin](gcGen, 1, 100_000).Draw(t, "coinsToAdd")...) - - s.SetupTest() - - // Create a new gauge - _, _, existingGaugeCoins, _ := s.SetupNewGauge(true, gaugeCoins) - owner := apptesting.CreateRandomAccounts(1)[0] - // Fund the owner account - s.FundAcc(owner, coinsToAdd) - - // Save the gas meter before the method call - existingGasConsumed := s.Ctx.GasMeter().GasConsumed() - - // AddToGaugeRewards - err := s.App.IncentivesKeeper.AddToGaugeRewards(s.Ctx, owner, coinsToAdd, 1) - s.Require().NoError(err) - - // Min expected gas consumed - baseGasFee := s.App.IncentivesKeeper.GetParams(s.Ctx).BaseGasFeeForAddRewardToGauge - minimumGasConsumed := baseGasFee * uint64(len(gaugeCoins)+len(coinsToAdd)) - - // Ensure that at least the minimum amount of gas was charged (based on number of additional gauge coins) - gasConsumed := s.Ctx.GasMeter().GasConsumed() - existingGasConsumed - fmt.Println(gasConsumed, minimumGasConsumed) - s.Require().True(gasConsumed >= minimumGasConsumed) - - // Existing coins gets added to the module when we create gauge and add to gauge - expectedCoins := existingGaugeCoins.Add(coinsToAdd...) - - // Check module account balance, should go up - balance := s.App.BankKeeper.GetAllBalances(s.Ctx, s.App.AccountKeeper.GetModuleAddress(types.ModuleName)) - s.Require().Equal(expectedCoins, balance) - - // Check gauge coins should go up - gauge, err := s.App.IncentivesKeeper.GetGaugeByID(s.Ctx, 1) - s.Require().NoError(err) - - s.Require().Equal(expectedCoins, gauge.Coins) - }) -} diff --git a/x/incentives/keeper/genesis_test.go b/x/incentives/keeper/genesis_test.go index b4832bdda..06626193e 100644 --- a/x/incentives/keeper/genesis_test.go +++ b/x/incentives/keeper/genesis_test.go @@ -4,6 +4,7 @@ import ( "testing" "time" + "cosmossdk.io/math" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" bankutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" @@ -27,7 +28,9 @@ func TestIncentivesExportGenesis(t *testing.T) { // create an address and fund with coins addr := sdk.AccAddress([]byte("addr1---------------")) coins := sdk.Coins{sdk.NewInt64Coin("stake", 10000)} - err := bankutil.FundAccount(app.BankKeeper, ctx, addr, coins) + // balance including fees + addrCoins := coins.Add(sdk.NewCoin("stake", types.DYM.MulRaw(1000))) + err := bankutil.FundAccount(app.BankKeeper, ctx, addr, addrCoins) require.NoError(t, err) // mints LP tokens and send to address created earlier @@ -96,11 +99,10 @@ func TestIncentivesInitGenesis(t *testing.T) { // initialize genesis with specified parameter, the gauge created earlier, and lockable durations app.IncentivesKeeper.InitGenesis(ctx, types.GenesisState{ Params: types.Params{ - DistrEpochIdentifier: "week", - CreateGaugeFee: sdk.ZeroInt(), - AddToGaugeFee: sdk.ZeroInt(), - BaseGasFeeForCreateGauge: 0, - BaseGasFeeForAddRewardToGauge: 0, + DistrEpochIdentifier: "week", + CreateGaugeBaseFee: math.ZeroInt(), + AddToGaugeBaseFee: math.ZeroInt(), + AddDenomFee: math.ZeroInt(), }, Gauges: []types.Gauge{gauge}, LockableDurations: []time.Duration{ diff --git a/x/incentives/keeper/msg_server.go b/x/incentives/keeper/msg_server.go index 5d599a499..4373156b3 100644 --- a/x/incentives/keeper/msg_server.go +++ b/x/incentives/keeper/msg_server.go @@ -34,11 +34,6 @@ func (server msgServer) CreateGauge(goCtx context.Context, msg *types.MsgCreateG return nil, err } - createGaugeFee := server.keeper.GetParams(ctx).CreateGaugeFee - if err := server.keeper.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, createGaugeFee, msg.Coins); err != nil { - return nil, 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()) @@ -63,10 +58,6 @@ func (server msgServer) AddToGauge(goCtx context.Context, msg *types.MsgAddToGau return nil, err } - addToGaugeFee := server.keeper.GetParams(ctx).AddToGaugeFee - if err := server.keeper.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, 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()) diff --git a/x/incentives/keeper/msg_server_test.go b/x/incentives/keeper/msg_server_test.go index 59190e6c6..496893191 100644 --- a/x/incentives/keeper/msg_server_test.go +++ b/x/incentives/keeper/msg_server_test.go @@ -27,36 +27,63 @@ func (suite *KeeperTestSuite) TestCreateGauge_Fee() { }{ { name: "user creates a non-perpetual gauge and fills gauge with all remaining tokens", - accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(60)))), - gaugeAddition: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(10)))), + accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(60)))), + gaugeAddition: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(10)))), }, { name: "user creates a non-perpetual gauge and fills gauge with some remaining tokens", - accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(70)))), - gaugeAddition: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(10)))), + accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(70)))), + gaugeAddition: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(10)))), }, { name: "user with multiple denoms creates a non-perpetual gauge and fills gauge with some remaining tokens", - accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(70))), sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(70)))), - gaugeAddition: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(10)))), + accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(70))), sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(70)))), + gaugeAddition: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(10)))), + }, + { + name: "user tries to create a non-perpetual gauge but includes too many denoms so does not have enough funds to pay fees", + accountBalanceToFund: sdk.NewCoins( + sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(40))), // 40 >= 20 (adym) + 10 (creation fee) + 10 (for every denom) = 40 + sdk.NewCoin("osmo", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("atom", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("abcd", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("efgh", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("igkl", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("mnop", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("qrst", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("uvwx", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("yzzz", types.DYM.Mul(sdk.NewInt(20))), + ), + gaugeAddition: sdk.NewCoins( + sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("osmo", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("atom", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("abcd", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("efgh", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("igkl", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("mnop", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("qrst", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("uvwx", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("yzzz", types.DYM.Mul(sdk.NewInt(20))), + ), }, { name: "module account creates a perpetual gauge and fills gauge with some remaining tokens", - accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(70))), sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(70)))), - gaugeAddition: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(10)))), + accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(70))), sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(70)))), + gaugeAddition: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(10)))), isPerpetual: true, isModuleAccount: true, }, { name: "user with multiple denoms creates a perpetual gauge and fills gauge with some remaining tokens", - accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(70))), sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(70)))), - gaugeAddition: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(10)))), + accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(70))), sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(70)))), + gaugeAddition: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(10)))), isPerpetual: true, }, { name: "user tries to create a non-perpetual gauge but does not have enough funds to pay for the create gauge fee", - accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(5)))), - gaugeAddition: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(1)))), + accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(5)))), + gaugeAddition: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(1)))), expectErr: true, }, { @@ -65,73 +92,103 @@ func (suite *KeeperTestSuite) TestCreateGauge_Fee() { gaugeAddition: sdk.NewCoins(sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(10)))), expectErr: true, }, + { + name: "user tries to create a non-perpetual gauge but includes too many denoms so does not have enough funds to pay fees", + accountBalanceToFund: sdk.NewCoins( + sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(39))), // 39 < 20 (adym) + 10 (creation fee) + 10 (for every denom) = 40 + sdk.NewCoin("osmo", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("atom", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("abcd", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("efgh", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("igkl", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("mnop", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("qrst", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("uvwx", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("yzzz", types.DYM.Mul(sdk.NewInt(20))), + ), + gaugeAddition: sdk.NewCoins( + sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("osmo", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("atom", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("abcd", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("efgh", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("igkl", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("mnop", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("qrst", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("uvwx", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("yzzz", types.DYM.Mul(sdk.NewInt(20))), + ), + expectErr: true, + }, { name: "one user tries to create a gauge, has enough funds to pay for the create gauge fee but not enough to fill the gauge", - accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(20)))), - gaugeAddition: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(30)))), + accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(20)))), + gaugeAddition: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(30)))), expectErr: true, }, } for _, tc := range tests { - suite.SetupTest() - - err := suite.App.TxFeesKeeper.SetBaseDenom(suite.Ctx, "adym") - suite.Require().NoError(err) - - testAccountPubkey := secp256k1.GenPrivKeyFromSecret([]byte("acc")).PubKey() - testAccountAddress := sdk.AccAddress(testAccountPubkey.Address()) - - ctx := suite.Ctx - bankKeeper := suite.App.BankKeeper - accountKeeper := suite.App.AccountKeeper - msgServer := keeper.NewMsgServerImpl(suite.App.IncentivesKeeper) - - suite.FundAcc(testAccountAddress, tc.accountBalanceToFund) - - if tc.isModuleAccount { - modAcc := authtypes.NewModuleAccount(authtypes.NewBaseAccount(testAccountAddress, testAccountPubkey, 1, 0), - "module", - "permission", - ) - accountKeeper.SetModuleAccount(ctx, modAcc) - } - - suite.SetupManyLocks(1, defaultLiquidTokens, defaultLPTokens, defaultLockDuration) - distrTo := lockuptypes.QueryCondition{ - LockQueryType: lockuptypes.ByDuration, - Denom: defaultLPDenom, - Duration: defaultLockDuration, - } - - msg := &types.MsgCreateGauge{ - IsPerpetual: tc.isPerpetual, - Owner: testAccountAddress.String(), - DistributeTo: distrTo, - Coins: tc.gaugeAddition, - StartTime: time.Now(), - NumEpochsPaidOver: 1, - } - // System under test. - _, err = msgServer.CreateGauge(sdk.WrapSDKContext(ctx), msg) - - if tc.expectErr { - suite.Require().Error(err, "test: %v", tc.name) - } else { - 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 { - createGaugeFee := suite.querier.GetParams(suite.Ctx).CreateGaugeFee - fee := sdk.NewCoins(sdk.NewCoin("adym", createGaugeFee)) - accountBalance := tc.accountBalanceToFund.Sub(tc.gaugeAddition...) - finalAccountBalance := accountBalance.Sub(fee...) - suite.Require().Equal(finalAccountBalance.String(), balanceAmount.String(), "test: %v", tc.name) - } + suite.Run(tc.name, func() { + suite.SetupTest() + + testAccountPubkey := secp256k1.GenPrivKeyFromSecret([]byte("acc")).PubKey() + testAccountAddress := sdk.AccAddress(testAccountPubkey.Address()) + + ctx := suite.Ctx + bankKeeper := suite.App.BankKeeper + accountKeeper := suite.App.AccountKeeper + msgServer := keeper.NewMsgServerImpl(suite.App.IncentivesKeeper) + + suite.FundAcc(testAccountAddress, tc.accountBalanceToFund) + + if tc.isModuleAccount { + modAcc := authtypes.NewModuleAccount(authtypes.NewBaseAccount(testAccountAddress, testAccountPubkey, 1, 0), + "module", + "permission", + ) + accountKeeper.SetModuleAccount(ctx, modAcc) + } + + suite.SetupManyLocks(1, defaultLiquidTokens, defaultLPTokens, defaultLockDuration) + distrTo := lockuptypes.QueryCondition{ + LockQueryType: lockuptypes.ByDuration, + Denom: defaultLPDenom, + Duration: defaultLockDuration, + } + + msg := &types.MsgCreateGauge{ + IsPerpetual: tc.isPerpetual, + Owner: testAccountAddress.String(), + DistributeTo: distrTo, + Coins: tc.gaugeAddition, + StartTime: time.Now(), + NumEpochsPaidOver: 1, + } + // System under test. + _, err := msgServer.CreateGauge(sdk.WrapSDKContext(ctx), msg) + + if tc.expectErr { + suite.Require().Error(err, "test: %v", tc.name) + } else { + 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 { + // Fee = CreateGaugeBaseFee + AddDenomFee * NumDenoms + params := suite.querier.GetParams(suite.Ctx) + feeRaw := params.CreateGaugeBaseFee.Add(params.AddDenomFee.MulRaw(int64(len(tc.gaugeAddition)))) + fee := sdk.NewCoins(sdk.NewCoin("stake", feeRaw)) + + accountBalance := tc.accountBalanceToFund.Sub(tc.gaugeAddition...) + finalAccountBalance := accountBalance.Sub(fee...) + suite.Require().Equal(finalAccountBalance.String(), balanceAmount.String(), "test: %v", tc.name) + } + }) } } @@ -147,38 +204,93 @@ func (suite *KeeperTestSuite) TestAddToGauge_Fee() { }{ { name: "user creates a non-perpetual gauge and fills gauge with all remaining tokens", - accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(35)))), - gaugeAddition: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(10)))), + accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(35)))), + gaugeAddition: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(10)))), }, { name: "user creates a non-perpetual gauge and fills gauge with some remaining tokens", - accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(70)))), - gaugeAddition: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(10)))), + accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(70)))), + gaugeAddition: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(10)))), }, { name: "user with multiple denoms creates a non-perpetual gauge and fills gauge with some remaining tokens", - accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(70))), sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(70)))), - gaugeAddition: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(10)))), + accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(70))), sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(70)))), + gaugeAddition: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(10)))), + }, + { + name: "user adds to a non-perpetual gauge including many denoms", + accountBalanceToFund: sdk.NewCoins( + sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(31))), // 31 >= 20 (adym) + 10 (denoms) + 1 (initial denom) = 31 + sdk.NewCoin("osmo", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("atom", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("abcd", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("efgh", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("igkl", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("mnop", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("qrst", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("uvwx", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("yzzz", types.DYM.Mul(sdk.NewInt(20))), + ), + gaugeAddition: sdk.NewCoins( + sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("osmo", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("atom", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("abcd", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("efgh", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("igkl", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("mnop", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("qrst", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("uvwx", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("yzzz", types.DYM.Mul(sdk.NewInt(20))), + ), }, { name: "module account creates a perpetual gauge and fills gauge with some remaining tokens", - accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(70))), sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(70)))), - gaugeAddition: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(10)))), + accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(70))), sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(70)))), + gaugeAddition: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(10)))), isPerpetual: true, isModuleAccount: true, }, { name: "user with multiple denoms creates a perpetual gauge and fills gauge with some remaining tokens", - accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(70))), sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(70)))), - gaugeAddition: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(10)))), + accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(70))), sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(70)))), + gaugeAddition: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(10)))), isPerpetual: true, }, { name: "user tries to add to a non-perpetual gauge but does not have enough funds to pay for the create gauge fee", - accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(20)))), - gaugeAddition: sdk.NewCoins(sdk.NewCoin("adym", types.DYM.Mul(sdk.NewInt(20)))), + accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(20)))), + gaugeAddition: sdk.NewCoins(sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(20)))), expectErr: true, }, + { + name: "user tries to add to a non-perpetual gauge but includes too many denoms so does not have enough funds to pay fees", + accountBalanceToFund: sdk.NewCoins( + sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(30))), // 30 < 20 (adym) + 10 (denoms) + 1 (initial denom) = 31 + sdk.NewCoin("osmo", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("atom", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("abcd", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("efgh", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("igkl", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("mnop", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("qrst", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("uvwx", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("yzzz", types.DYM.Mul(sdk.NewInt(20))), + ), + gaugeAddition: sdk.NewCoins( + sdk.NewCoin("stake", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("osmo", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("atom", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("abcd", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("efgh", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("igkl", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("mnop", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("qrst", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("uvwx", types.DYM.Mul(sdk.NewInt(20))), + sdk.NewCoin("yzzz", types.DYM.Mul(sdk.NewInt(20))), + ), + expectErr: true, + }, { name: "user tries to add to a non-perpetual gauge but does not have the correct fee denom", accountBalanceToFund: sdk.NewCoins(sdk.NewCoin("foo", types.DYM.Mul(sdk.NewInt(60)))), @@ -188,62 +300,71 @@ func (suite *KeeperTestSuite) TestAddToGauge_Fee() { } for _, tc := range tests { - suite.SetupTest() - - err := suite.App.TxFeesKeeper.SetBaseDenom(suite.Ctx, "adym") - suite.Require().NoError(err) - - testAccountPubkey := secp256k1.GenPrivKeyFromSecret([]byte("acc")).PubKey() - testAccountAddress := sdk.AccAddress(testAccountPubkey.Address()) - // testAccountAddress := suite.TestAccs[0] - - ctx := suite.Ctx - bankKeeper := suite.App.BankKeeper - incentivesKeeper := suite.App.IncentivesKeeper - accountKeeper := suite.App.AccountKeeper - msgServer := keeper.NewMsgServerImpl(incentivesKeeper) - - // suite.FundAcc(testAccountAddress, testutil.DefaultAcctFunds) - suite.FundAcc(testAccountAddress, tc.accountBalanceToFund) - - if tc.isModuleAccount { - modAcc := authtypes.NewModuleAccount(authtypes.NewBaseAccount(testAccountAddress, testAccountPubkey, 1, 0), - "module", - "permission", - ) - accountKeeper.SetModuleAccount(ctx, modAcc) - } - - // 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, - Rewards: tc.gaugeAddition, - } - - _, err = msgServer.AddToGauge(sdk.WrapSDKContext(ctx), msg) - - if tc.expectErr { - suite.Require().Error(err, "test: %v", tc.name) - } else { - suite.Require().NoError(err, "test: %v", tc.name) - } - - bal := bankKeeper.GetAllBalances(ctx, testAccountAddress) - - if tc.expectErr { - suite.Require().Equal(tc.accountBalanceToFund.String(), bal.String(), "test: %v", tc.name) - } else { - addToGaugeFee := suite.querier.GetParams(suite.Ctx).AddToGaugeFee - fee := sdk.NewCoins(sdk.NewCoin("adym", addToGaugeFee)) - accountBalance := tc.accountBalanceToFund.Sub(tc.gaugeAddition...) - finalAccountBalance := accountBalance.Sub(fee...) - suite.Require().Equal(finalAccountBalance.String(), bal.String(), "test: %v", tc.name) - } + suite.Run(tc.name, func() { + suite.SetupTest() + + err := suite.App.TxFeesKeeper.SetBaseDenom(suite.Ctx, "stake") + suite.Require().NoError(err) + + testAccountPubkey := secp256k1.GenPrivKeyFromSecret([]byte("acc")).PubKey() + testAccountAddress := sdk.AccAddress(testAccountPubkey.Address()) + // testAccountAddress := suite.TestAccs[0] + + ctx := suite.Ctx + bankKeeper := suite.App.BankKeeper + incentivesKeeper := suite.App.IncentivesKeeper + accountKeeper := suite.App.AccountKeeper + msgServer := keeper.NewMsgServerImpl(incentivesKeeper) + + // suite.FundAcc(testAccountAddress, testutil.DefaultAcctFunds) + suite.FundAcc(testAccountAddress, tc.accountBalanceToFund) + + if tc.isModuleAccount { + modAcc := authtypes.NewModuleAccount(authtypes.NewBaseAccount(testAccountAddress, testAccountPubkey, 1, 0), + "module", + "permission", + ) + accountKeeper.SetModuleAccount(ctx, modAcc) + } + + // 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, + Rewards: tc.gaugeAddition, + } + + params := suite.querier.GetParams(suite.Ctx) + feeRaw := params.AddToGaugeBaseFee.Add(params.AddDenomFee.MulRaw(int64(len(tc.gaugeAddition) + len(gauge.Coins)))) + suite.T().Log(feeRaw, params.AddToGaugeBaseFee, params.AddDenomFee) + + _, err = msgServer.AddToGauge(sdk.WrapSDKContext(ctx), msg) + + if tc.expectErr { + suite.Require().Error(err, "test: %v", tc.name) + } else { + suite.Require().NoError(err, "test: %v", tc.name) + } + + bal := bankKeeper.GetAllBalances(ctx, testAccountAddress) + + if tc.expectErr { + suite.Require().Equal(tc.accountBalanceToFund.String(), bal.String(), "test: %v", tc.name) + } else { + // Fee = AddToGaugeBaseFee + AddDenomFee * (NumAddedDenoms + NumGaugeDenoms) + params := suite.querier.GetParams(suite.Ctx) + feeRaw := params.AddToGaugeBaseFee.Add(params.AddDenomFee.MulRaw(int64(len(tc.gaugeAddition) + len(gauge.Coins)))) + fee := sdk.NewCoins(sdk.NewCoin("stake", feeRaw)) + + accountBalance := tc.accountBalanceToFund.Sub(tc.gaugeAddition...) + finalAccountBalance := accountBalance.Sub(fee...) + suite.Require().Equal(finalAccountBalance.String(), bal.String(), "test: %v", tc.name) + } + }) } } diff --git a/x/incentives/keeper/suite_test.go b/x/incentives/keeper/suite_test.go index 2cd38ea61..b32aec893 100644 --- a/x/incentives/keeper/suite_test.go +++ b/x/incentives/keeper/suite_test.go @@ -92,7 +92,8 @@ func (suite *KeeperTestSuite) SetupGauges(gaugeDescriptors []perpGaugeDesc, deno // CreateGauge creates a gauge struct given the required params. func (suite *KeeperTestSuite) CreateGauge(isPerpetual bool, addr sdk.AccAddress, coins sdk.Coins, distrTo lockuptypes.QueryCondition, startTime time.Time, numEpoch uint64) (uint64, *types.Gauge) { - suite.FundAcc(addr, coins) + addrCoins := coins.Add(sdk.NewCoin(sdk.DefaultBondDenom, types.DYM.MulRaw(10_000))) // create gauge fees + suite.FundAcc(addr, addrCoins) gaugeID, err := suite.App.IncentivesKeeper.CreateGauge(suite.Ctx, isPerpetual, addr, coins, distrTo, startTime, numEpoch) suite.Require().NoError(err) gauge, err := suite.App.IncentivesKeeper.GetGaugeByID(suite.Ctx, gaugeID) @@ -103,7 +104,8 @@ func (suite *KeeperTestSuite) CreateGauge(isPerpetual bool, addr sdk.AccAddress, // AddToGauge adds coins to the specified gauge. func (suite *KeeperTestSuite) AddToGauge(coins sdk.Coins, gaugeID uint64) uint64 { addr := sdk.AccAddress([]byte("addrx---------------")) - suite.FundAcc(addr, coins) + addrCoins := coins.Add(sdk.NewCoin(sdk.DefaultBondDenom, types.DYM.MulRaw(10_000))) // add to gauge fees + suite.FundAcc(addr, addrCoins) err := suite.App.IncentivesKeeper.AddToGaugeRewards(suite.Ctx, addr, coins, gaugeID) suite.Require().NoError(err) return gaugeID diff --git a/x/incentives/types/constants.go b/x/incentives/types/constants.go index e2ab7020c..87bad0eb1 100644 --- a/x/incentives/types/constants.go +++ b/x/incentives/types/constants.go @@ -12,11 +12,8 @@ var ( DYM = math.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) DefaultCreateGaugeFee = DYM.Mul(sdk.NewInt(10)) - DefaultAddToGaugeFee = DYM + DefaultAddToGaugeFee = math.ZeroInt() + DefaultAddDenomFee = DYM ) -const ( - DefaultDistrEpochIdentifier = "week" - DefaultBaseGasFeeForCreateGauge uint64 = 10_000 - DefaultBaseGasFeeForAddRewardToGauge uint64 = 10_000 -) +const DefaultDistrEpochIdentifier = "week" diff --git a/x/incentives/types/params.go b/x/incentives/types/params.go index 88540349e..52a43252c 100644 --- a/x/incentives/types/params.go +++ b/x/incentives/types/params.go @@ -12,11 +12,10 @@ import ( // Incentives parameters key store. var ( - KeyDistrEpochIdentifier = []byte("DistrEpochIdentifier") - KeyCreateGaugeFee = []byte("CreateGaugeFee") - KeyAddToGaugeFee = []byte("AddToGaugeFee") - KeyBaseGasFeeForCreateGauge = []byte("BaseGasFeeForCreateGauge") - KeyBaseGasFeeForAddRewardToGauge = []byte("BaseGasFeeForAddRewardToGauge") + KeyDistrEpochIdentifier = []byte("DistrEpochIdentifier") + KeyCreateGaugeFee = []byte("CreateGaugeFee") + KeyAddToGaugeFee = []byte("AddToGaugeFee") + KeyAddDenomFee = []byte("AddDenomFee") ) // ParamKeyTable returns the key table for the incentive module's parameters. @@ -25,28 +24,22 @@ func ParamKeyTable() paramtypes.KeyTable { } // NewParams takes an epoch distribution identifier, then returns an incentives Params struct. -func NewParams( - distrEpochIdentifier string, - createGaugeFee, addToGaugeFee math.Int, - baseGasFeeForCreateGauge, baseGasFeeForAddRewardToGauge uint64, -) Params { +func NewParams(distrEpochIdentifier string, createGaugeFee, addToGaugeFee, addDenomFee math.Int) Params { return Params{ - DistrEpochIdentifier: distrEpochIdentifier, - CreateGaugeFee: createGaugeFee, - AddToGaugeFee: addToGaugeFee, - BaseGasFeeForCreateGauge: baseGasFeeForCreateGauge, - BaseGasFeeForAddRewardToGauge: baseGasFeeForAddRewardToGauge, + DistrEpochIdentifier: distrEpochIdentifier, + CreateGaugeBaseFee: createGaugeFee, + AddToGaugeBaseFee: addToGaugeFee, + AddDenomFee: addDenomFee, } } // DefaultParams returns the default incentives module parameters. func DefaultParams() Params { return Params{ - DistrEpochIdentifier: DefaultDistrEpochIdentifier, - CreateGaugeFee: DefaultCreateGaugeFee, - AddToGaugeFee: DefaultAddToGaugeFee, - BaseGasFeeForCreateGauge: DefaultBaseGasFeeForCreateGauge, - BaseGasFeeForAddRewardToGauge: DefaultBaseGasFeeForAddRewardToGauge, + DistrEpochIdentifier: DefaultDistrEpochIdentifier, + CreateGaugeBaseFee: DefaultCreateGaugeFee, + AddToGaugeBaseFee: DefaultAddToGaugeFee, + AddDenomFee: DefaultAddDenomFee, } } @@ -55,16 +48,13 @@ func (p Params) Validate() error { if err := epochtypes.ValidateEpochIdentifierInterface(p.DistrEpochIdentifier); err != nil { return err } - if err := validateCreateGaugeFeeInterface(p.CreateGaugeFee); err != nil { + if err := validateCreateGaugeFeeInterface(p.CreateGaugeBaseFee); err != nil { return err } - if err := validateAddToGaugeFeeInterface(p.AddToGaugeFee); err != nil { + if err := validateAddToGaugeFeeInterface(p.AddToGaugeBaseFee); err != nil { return err } - if err := validateBaseGasFeeForCreateGauge(p.BaseGasFeeForCreateGauge); err != nil { - return err - } - if err := validateBaseGasFeeForAddRewardToGauge(p.BaseGasFeeForAddRewardToGauge); err != nil { + if err := validateAddDenomFee(p.AddDenomFee); err != nil { return err } return nil @@ -74,10 +64,9 @@ func (p Params) Validate() error { func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { return paramtypes.ParamSetPairs{ paramtypes.NewParamSetPair(KeyDistrEpochIdentifier, &p.DistrEpochIdentifier, epochtypes.ValidateEpochIdentifierInterface), - paramtypes.NewParamSetPair(KeyCreateGaugeFee, &p.CreateGaugeFee, validateCreateGaugeFeeInterface), - paramtypes.NewParamSetPair(KeyAddToGaugeFee, &p.AddToGaugeFee, validateAddToGaugeFeeInterface), - paramtypes.NewParamSetPair(KeyBaseGasFeeForCreateGauge, &p.BaseGasFeeForCreateGauge, validateBaseGasFeeForCreateGauge), - paramtypes.NewParamSetPair(KeyBaseGasFeeForAddRewardToGauge, &p.BaseGasFeeForAddRewardToGauge, validateBaseGasFeeForAddRewardToGauge), + paramtypes.NewParamSetPair(KeyCreateGaugeFee, &p.CreateGaugeBaseFee, validateCreateGaugeFeeInterface), + paramtypes.NewParamSetPair(KeyAddToGaugeFee, &p.AddToGaugeBaseFee, validateAddToGaugeFeeInterface), + paramtypes.NewParamSetPair(KeyAddDenomFee, &p.AddDenomFee, validateAddDenomFee), } } @@ -103,18 +92,13 @@ func validateAddToGaugeFeeInterface(i interface{}) error { return nil } -func validateBaseGasFeeForCreateGauge(i interface{}) error { - _, ok := i.(uint64) +func validateAddDenomFee(i interface{}) error { + v, ok := i.(math.Int) if !ok { return fmt.Errorf("invalid parameter type: %T", i) } - return nil -} - -func validateBaseGasFeeForAddRewardToGauge(i interface{}) error { - _, ok := i.(uint64) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) + if v.IsNegative() { + return gerrc.ErrInvalidArgument.Wrapf("must be >= 0, got %s", v) } return nil } diff --git a/x/incentives/types/params.pb.go b/x/incentives/types/params.pb.go index 732131597..0773d1bc4 100644 --- a/x/incentives/types/params.pb.go +++ b/x/incentives/types/params.pb.go @@ -29,17 +29,16 @@ type Params struct { // distr_epoch_identifier is what epoch type distribution will be triggered by // (day, week, etc.) DistrEpochIdentifier string `protobuf:"bytes,1,opt,name=distr_epoch_identifier,json=distrEpochIdentifier,proto3" json:"distr_epoch_identifier,omitempty" yaml:"distr_epoch_identifier"` - // CreateGaugeFee is the fee required to create a new gauge. - CreateGaugeFee github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=create_gauge_fee,json=createGaugeFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"create_gauge_fee"` - // AddToGaugeFee is the fee required to add to gauge. - AddToGaugeFee github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=add_to_gauge_fee,json=addToGaugeFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"add_to_gauge_fee"` - // BaseGasFeeForCreateGauge is the gas fee for creating a gauge. - // This gas is charged for each denom while creating a gauge. - BaseGasFeeForCreateGauge uint64 `protobuf:"varint,4,opt,name=base_gas_fee_for_create_gauge,json=baseGasFeeForCreateGauge,proto3" json:"base_gas_fee_for_create_gauge,omitempty"` - // BaseGasFeeForAddRewardToGauge is the gas fee for adding reward to gauges. - // This gas is charged for each denom while adding to a gauge plus for - // each denom the gauge already holds. - BaseGasFeeForAddRewardToGauge uint64 `protobuf:"varint,5,opt,name=base_gas_fee_for_add_reward_to_gauge,json=baseGasFeeForAddRewardToGauge,proto3" json:"base_gas_fee_for_add_reward_to_gauge,omitempty"` + // CreateGaugeBaseFee is a base fee required to create a new gauge. The final + // fee is calculated as + // Fee = CreateGaugeBaseFee + AddDenomFee * (len(Denoms) + len(GaugeDenoms)). + CreateGaugeBaseFee github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=create_gauge_base_fee,json=createGaugeBaseFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"create_gauge_base_fee"` + // AddToGaugeBaseFee is a base fee required to add to gauge. The final + // fee is calculated as + // Fee = AddToGaugeBaseFee + AddDenomFee * len(Denoms). + AddToGaugeBaseFee github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=add_to_gauge_base_fee,json=addToGaugeBaseFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"add_to_gauge_base_fee"` + // AddDenomFee is a fee charged for adding every new denom to the gauge. + AddDenomFee github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=add_denom_fee,json=addDenomFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"add_denom_fee"` } func (m *Params) Reset() { *m = Params{} } @@ -82,20 +81,6 @@ func (m *Params) GetDistrEpochIdentifier() string { return "" } -func (m *Params) GetBaseGasFeeForCreateGauge() uint64 { - if m != nil { - return m.BaseGasFeeForCreateGauge - } - return 0 -} - -func (m *Params) GetBaseGasFeeForAddRewardToGauge() uint64 { - if m != nil { - return m.BaseGasFeeForAddRewardToGauge - } - return 0 -} - func init() { proto.RegisterType((*Params)(nil), "dymensionxyz.dymension.incentives.Params") } @@ -105,30 +90,28 @@ func init() { } var fileDescriptor_256a114c8e13cfa0 = []byte{ - // 368 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0x4f, 0x4a, 0xc3, 0x40, - 0x14, 0x87, 0x13, 0x5b, 0x0b, 0x06, 0x94, 0x12, 0x8a, 0x04, 0xa1, 0xe9, 0x1f, 0x44, 0xba, 0x31, - 0x59, 0x74, 0x21, 0xb8, 0x11, 0x2b, 0xb6, 0x14, 0x37, 0x1a, 0x84, 0x8a, 0x9b, 0x30, 0xcd, 0xbc, - 0xa6, 0x41, 0x93, 0x09, 0x33, 0xd3, 0xda, 0x78, 0x0a, 0xcf, 0xe0, 0x69, 0xba, 0xec, 0x52, 0x5c, - 0x14, 0x69, 0x6f, 0xe0, 0x09, 0x64, 0xa6, 0xa5, 0x89, 0xa8, 0x1b, 0x57, 0xc9, 0xbc, 0xf9, 0xde, - 0xc7, 0xef, 0x31, 0x4f, 0xb3, 0x70, 0x12, 0x42, 0xc4, 0x02, 0x12, 0x4d, 0x92, 0x67, 0x7b, 0x73, - 0xb0, 0x83, 0xc8, 0x83, 0x88, 0x07, 0x63, 0x60, 0x76, 0x8c, 0x28, 0x0a, 0x99, 0x15, 0x53, 0xc2, - 0x89, 0x5e, 0xcb, 0xf2, 0x69, 0xb3, 0x95, 0xf2, 0x07, 0x25, 0x9f, 0xf8, 0x44, 0xd2, 0xb6, 0xf8, - 0x5b, 0x35, 0xd6, 0x5f, 0x73, 0x5a, 0xe1, 0x5a, 0x9a, 0xf4, 0x9e, 0xb6, 0x8f, 0x03, 0xc6, 0xa9, - 0x0b, 0x31, 0xf1, 0x86, 0x6e, 0x80, 0x45, 0xe7, 0x20, 0x00, 0x6a, 0xa8, 0x55, 0xb5, 0xb1, 0xd3, - 0xaa, 0x7d, 0xce, 0x2b, 0xe5, 0x04, 0x85, 0x8f, 0xa7, 0xf5, 0xdf, 0xb9, 0xba, 0x53, 0x92, 0x17, - 0x97, 0xa2, 0xde, 0xdd, 0x94, 0xf5, 0x3b, 0xad, 0xe8, 0x51, 0x40, 0x1c, 0x5c, 0x1f, 0x8d, 0x7c, - 0x70, 0x07, 0x00, 0xc6, 0x96, 0x54, 0x5a, 0xd3, 0x79, 0x45, 0x79, 0x9f, 0x57, 0x8e, 0xfc, 0x80, - 0x0f, 0x47, 0x7d, 0xcb, 0x23, 0xa1, 0xed, 0x11, 0x16, 0x12, 0xb6, 0xfe, 0x1c, 0x33, 0xfc, 0x60, - 0xf3, 0x24, 0x06, 0x66, 0x75, 0x23, 0xee, 0xec, 0xad, 0x3c, 0x1d, 0xa1, 0x69, 0x03, 0xe8, 0x3d, - 0xad, 0x88, 0x30, 0x76, 0x39, 0xc9, 0x98, 0x73, 0xff, 0x32, 0xef, 0x22, 0x8c, 0x6f, 0xc9, 0x46, - 0x7c, 0xa6, 0x95, 0xfb, 0x88, 0x89, 0xc0, 0x4c, 0x48, 0xdd, 0x01, 0xa1, 0x6e, 0x76, 0x06, 0x23, - 0x5f, 0x55, 0x1b, 0x79, 0xc7, 0x10, 0x50, 0x07, 0xb1, 0x36, 0x40, 0x9b, 0xd0, 0x8b, 0x34, 0x9c, - 0x7e, 0xa5, 0x1d, 0xfe, 0x10, 0x88, 0xa8, 0x14, 0x9e, 0x10, 0x4d, 0x13, 0x1b, 0xdb, 0xd2, 0x53, - 0xfe, 0xe6, 0x39, 0xc7, 0xd8, 0x91, 0xd8, 0x3a, 0x50, 0xeb, 0x66, 0xba, 0x30, 0xd5, 0xd9, 0xc2, - 0x54, 0x3f, 0x16, 0xa6, 0xfa, 0xb2, 0x34, 0x95, 0xd9, 0xd2, 0x54, 0xde, 0x96, 0xa6, 0x72, 0x7f, - 0x92, 0x19, 0xef, 0x8f, 0x95, 0x19, 0x37, 0xed, 0x49, 0x76, 0x6f, 0xe4, 0xcc, 0xfd, 0x82, 0x7c, - 0xfe, 0xe6, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7c, 0x6e, 0x73, 0x8d, 0x69, 0x02, 0x00, 0x00, + // 332 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0x31, 0x4f, 0xf2, 0x40, + 0x18, 0xc7, 0x5b, 0xde, 0x37, 0x24, 0xd6, 0x38, 0xd8, 0x80, 0x21, 0x26, 0x16, 0x61, 0x30, 0x2e, + 0xf6, 0x06, 0x06, 0x13, 0x47, 0xa2, 0x18, 0x36, 0x25, 0x26, 0x26, 0x2e, 0xf5, 0xe8, 0x3d, 0x94, + 0x8b, 0xde, 0x3d, 0x4d, 0xef, 0x20, 0xd4, 0x4f, 0xe1, 0xc7, 0x62, 0x64, 0x34, 0x0e, 0xc4, 0xc0, + 0x37, 0x70, 0x74, 0x32, 0x77, 0x18, 0x40, 0xa3, 0x0b, 0x53, 0x7b, 0xcf, 0xfd, 0xff, 0xbf, 0xdf, + 0x70, 0x8f, 0x17, 0xb2, 0x5c, 0x80, 0x54, 0x1c, 0xe5, 0x28, 0x7f, 0x22, 0xcb, 0x03, 0xe1, 0x32, + 0x06, 0xa9, 0xf9, 0x10, 0x14, 0x49, 0x69, 0x46, 0x85, 0x0a, 0xd3, 0x0c, 0x35, 0xfa, 0xb5, 0xf5, + 0xfc, 0xaa, 0x1c, 0xae, 0xf2, 0xfb, 0xa5, 0x04, 0x13, 0xb4, 0x69, 0x62, 0xfe, 0x16, 0xc5, 0xfa, + 0x47, 0xc1, 0x2b, 0x5e, 0x59, 0x92, 0x7f, 0xeb, 0xed, 0x31, 0xae, 0x74, 0x16, 0x41, 0x8a, 0x71, + 0x3f, 0xe2, 0xcc, 0x34, 0x7b, 0x1c, 0xb2, 0x8a, 0x7b, 0xe8, 0x1e, 0x6f, 0x35, 0x6b, 0xef, 0xd3, + 0xea, 0x41, 0x4e, 0xc5, 0xe3, 0x59, 0xfd, 0xf7, 0x5c, 0xbd, 0x53, 0xb2, 0x17, 0x17, 0x66, 0xde, + 0x5e, 0x8e, 0x7d, 0xea, 0x95, 0xe3, 0x0c, 0xa8, 0x86, 0x28, 0xa1, 0x83, 0x04, 0xa2, 0x2e, 0x55, + 0x10, 0xf5, 0x00, 0x2a, 0x05, 0xcb, 0x0d, 0xc7, 0xd3, 0xaa, 0xf3, 0x3a, 0xad, 0x1e, 0x25, 0x5c, + 0xf7, 0x07, 0xdd, 0x30, 0x46, 0x41, 0x62, 0x54, 0x02, 0xd5, 0xd7, 0xe7, 0x44, 0xb1, 0x07, 0xa2, + 0xf3, 0x14, 0x54, 0xd8, 0x96, 0xba, 0xe3, 0x2f, 0x60, 0x97, 0x86, 0xd5, 0xa4, 0x0a, 0x5a, 0x00, + 0xfe, 0xbd, 0x57, 0xa6, 0x8c, 0x45, 0x1a, 0x7f, 0x2a, 0xfe, 0x6d, 0xa4, 0xd8, 0xa5, 0x8c, 0xdd, + 0xe0, 0x37, 0x43, 0xc7, 0xdb, 0x31, 0x06, 0x06, 0x12, 0x85, 0x25, 0xff, 0xdf, 0x88, 0xbc, 0x4d, + 0x19, 0x3b, 0x37, 0x8c, 0x16, 0x40, 0xf3, 0x7a, 0x3c, 0x0b, 0xdc, 0xc9, 0x2c, 0x70, 0xdf, 0x66, + 0x81, 0xfb, 0x3c, 0x0f, 0x9c, 0xc9, 0x3c, 0x70, 0x5e, 0xe6, 0x81, 0x73, 0x77, 0xba, 0x86, 0xfb, + 0x63, 0x15, 0x86, 0x0d, 0x32, 0x5a, 0xdf, 0x07, 0xeb, 0xe8, 0x16, 0xed, 0xb3, 0x36, 0x3e, 0x03, + 0x00, 0x00, 0xff, 0xff, 0x71, 0x76, 0x81, 0x6d, 0x41, 0x02, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -151,20 +134,20 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.BaseGasFeeForAddRewardToGauge != 0 { - i = encodeVarintParams(dAtA, i, uint64(m.BaseGasFeeForAddRewardToGauge)) - i-- - dAtA[i] = 0x28 - } - if m.BaseGasFeeForCreateGauge != 0 { - i = encodeVarintParams(dAtA, i, uint64(m.BaseGasFeeForCreateGauge)) - i-- - dAtA[i] = 0x20 + { + size := m.AddDenomFee.Size() + i -= size + if _, err := m.AddDenomFee.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x22 { - size := m.AddToGaugeFee.Size() + size := m.AddToGaugeBaseFee.Size() i -= size - if _, err := m.AddToGaugeFee.MarshalTo(dAtA[i:]); err != nil { + if _, err := m.AddToGaugeBaseFee.MarshalTo(dAtA[i:]); err != nil { return 0, err } i = encodeVarintParams(dAtA, i, uint64(size)) @@ -172,9 +155,9 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x1a { - size := m.CreateGaugeFee.Size() + size := m.CreateGaugeBaseFee.Size() i -= size - if _, err := m.CreateGaugeFee.MarshalTo(dAtA[i:]); err != nil { + if _, err := m.CreateGaugeBaseFee.MarshalTo(dAtA[i:]); err != nil { return 0, err } i = encodeVarintParams(dAtA, i, uint64(size)) @@ -212,16 +195,12 @@ func (m *Params) Size() (n int) { if l > 0 { n += 1 + l + sovParams(uint64(l)) } - l = m.CreateGaugeFee.Size() + l = m.CreateGaugeBaseFee.Size() n += 1 + l + sovParams(uint64(l)) - l = m.AddToGaugeFee.Size() + l = m.AddToGaugeBaseFee.Size() + n += 1 + l + sovParams(uint64(l)) + l = m.AddDenomFee.Size() n += 1 + l + sovParams(uint64(l)) - if m.BaseGasFeeForCreateGauge != 0 { - n += 1 + sovParams(uint64(m.BaseGasFeeForCreateGauge)) - } - if m.BaseGasFeeForAddRewardToGauge != 0 { - n += 1 + sovParams(uint64(m.BaseGasFeeForAddRewardToGauge)) - } return n } @@ -294,7 +273,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CreateGaugeFee", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field CreateGaugeBaseFee", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -322,13 +301,13 @@ func (m *Params) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.CreateGaugeFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.CreateGaugeBaseFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AddToGaugeFee", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field AddToGaugeBaseFee", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -356,15 +335,15 @@ func (m *Params) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.AddToGaugeFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.AddToGaugeBaseFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field BaseGasFeeForCreateGauge", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AddDenomFee", wireType) } - m.BaseGasFeeForCreateGauge = 0 + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowParams @@ -374,30 +353,26 @@ func (m *Params) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.BaseGasFeeForCreateGauge |= uint64(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - case 5: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field BaseGasFeeForAddRewardToGauge", wireType) + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthParams } - m.BaseGasFeeForAddRewardToGauge = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowParams - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.BaseGasFeeForAddRewardToGauge |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.AddDenomFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipParams(dAtA[iNdEx:]) From 0ac2b4ca0c97998644d2f4e2581b7506b5a03a9a Mon Sep 17 00:00:00 2001 From: keruch Date: Thu, 12 Sep 2024 18:49:50 +0200 Subject: [PATCH 6/6] tests --- x/incentives/keeper/gauge.go | 38 ++++++++++------------------ x/incentives/keeper/gauge_test.go | 4 --- x/incentives/keeper/genesis_test.go | 4 +-- x/incentives/keeper/msg_server.go | 23 ++++++++++++++++- x/incentives/keeper/suite_test.go | 10 ++++---- x/incentives/types/constants.go | 15 ++++------- x/streamer/keeper/distribute.go | 2 +- x/streamer/types/expected_keepers.go | 2 +- 8 files changed, 49 insertions(+), 49 deletions(-) diff --git a/x/incentives/keeper/gauge.go b/x/incentives/keeper/gauge.go index 144de30d0..eb9b61e95 100644 --- a/x/incentives/keeper/gauge.go +++ b/x/incentives/keeper/gauge.go @@ -110,14 +110,6 @@ func (k Keeper) CreateGauge(ctx sdk.Context, isPerpetual bool, owner sdk.AccAddr return 0, fmt.Errorf("denom does not exist: %s", distrTo.Denom) } - // Charge fess based on the number of coins to add - // Fee = CreateGaugeBaseFee + AddDenomFee * NumDenoms - params := k.GetParams(ctx) - fee := params.CreateGaugeBaseFee.Add(params.AddDenomFee.MulRaw(int64(len(coins)))) - if err := k.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, fee, coins); err != nil { - return 0, err - } - gauge := types.Gauge{ Id: k.GetLastGaugeID(ctx) + 1, IsPerpetual: isPerpetual, @@ -149,29 +141,17 @@ func (k Keeper) CreateGauge(ctx sdk.Context, isPerpetual bool, owner sdk.AccAddr } // AddToGaugeRewards adds coins to gauge. -func (k Keeper) AddToGaugeRewards(ctx sdk.Context, owner sdk.AccAddress, coins sdk.Coins, gaugeID uint64) error { - gauge, err := k.GetGaugeByID(ctx, gaugeID) - if err != nil { - return err - } +func (k Keeper) AddToGaugeRewards(ctx sdk.Context, owner sdk.AccAddress, coins sdk.Coins, gauge *types.Gauge) error { if gauge.IsFinishedGauge(ctx.BlockTime()) { - return types.UnexpectedFinishedGaugeError{GaugeId: gaugeID} + return types.UnexpectedFinishedGaugeError{GaugeId: gauge.Id} } - // Charge fess based on the number of coins to add - // Fee = AddToGaugeBaseFee + AddDenomFee * (NumAddedDenoms + NumGaugeDenoms) - params := k.GetParams(ctx) - fee := params.AddToGaugeBaseFee.Add(params.AddDenomFee.MulRaw(int64(len(coins) + len(gauge.Coins)))) - if err = k.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, fee, coins); err != nil { - return err - } - - if err = k.bk.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, coins); err != nil { + if err := k.bk.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, coins); err != nil { return err } gauge.Coins = gauge.Coins.Add(coins...) - err = k.setGauge(ctx, gauge) + err := k.setGauge(ctx, gauge) if err != nil { return err } @@ -179,6 +159,16 @@ func (k Keeper) AddToGaugeRewards(ctx sdk.Context, owner sdk.AccAddress, coins s return nil } +// AddToGaugeRewardsByID adds coins to gauge. +// TODO: Used only in x/streamer. Delete after https://github.com/dymensionxyz/dymension/pull/1173 is merged! +func (k Keeper) AddToGaugeRewardsByID(ctx sdk.Context, owner sdk.AccAddress, coins sdk.Coins, gaugeID uint64) error { + gauge, err := k.GetGaugeByID(ctx, gaugeID) + if err != nil { + return err + } + return k.AddToGaugeRewards(ctx, owner, coins, gauge) +} + // GetGaugeByID returns gauge from gauge ID. func (k Keeper) GetGaugeByID(ctx sdk.Context, gaugeID uint64) (*types.Gauge, error) { gauge := types.Gauge{} diff --git a/x/incentives/keeper/gauge_test.go b/x/incentives/keeper/gauge_test.go index fad58f47d..4a13041af 100644 --- a/x/incentives/keeper/gauge_test.go +++ b/x/incentives/keeper/gauge_test.go @@ -23,8 +23,6 @@ func (suite *KeeperTestSuite) TestInvalidDurationGaugeCreationValidation() { Denom: defaultLPDenom, Duration: defaultLockDuration / 2, // 0.5 second, invalid duration } - // add tokens for fees - suite.FundAcc(addrs[0], sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, types.DefaultCreateGaugeFee.MulRaw(2)))) _, err := suite.App.IncentivesKeeper.CreateGauge(suite.Ctx, false, addrs[0], defaultLiquidTokens, distrTo, time.Time{}, 1) suite.Require().Error(err) @@ -44,8 +42,6 @@ func (suite *KeeperTestSuite) TestNonExistentDenomGaugeCreation() { Denom: defaultLPDenom, Duration: defaultLockDuration, } - // add tokens for fees - suite.FundAcc(addrs[0], sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, types.DefaultCreateGaugeFee.MulRaw(2)))) _, err := suite.App.IncentivesKeeper.CreateGauge(suite.Ctx, false, addrNoSupply, defaultLiquidTokens, distrTo, time.Time{}, 1) suite.Require().Error(err) diff --git a/x/incentives/keeper/genesis_test.go b/x/incentives/keeper/genesis_test.go index 06626193e..7bd05cab6 100644 --- a/x/incentives/keeper/genesis_test.go +++ b/x/incentives/keeper/genesis_test.go @@ -28,9 +28,7 @@ func TestIncentivesExportGenesis(t *testing.T) { // create an address and fund with coins addr := sdk.AccAddress([]byte("addr1---------------")) coins := sdk.Coins{sdk.NewInt64Coin("stake", 10000)} - // balance including fees - addrCoins := coins.Add(sdk.NewCoin("stake", types.DYM.MulRaw(1000))) - err := bankutil.FundAccount(app.BankKeeper, ctx, addr, addrCoins) + err := bankutil.FundAccount(app.BankKeeper, ctx, addr, coins) require.NoError(t, err) // mints LP tokens and send to address created earlier diff --git a/x/incentives/keeper/msg_server.go b/x/incentives/keeper/msg_server.go index 4373156b3..f603425d9 100644 --- a/x/incentives/keeper/msg_server.go +++ b/x/incentives/keeper/msg_server.go @@ -34,6 +34,14 @@ func (server msgServer) CreateGauge(goCtx context.Context, msg *types.MsgCreateG return nil, err } + // Charge fess based on the number of coins to add + // Fee = CreateGaugeBaseFee + AddDenomFee * NumDenoms + params := server.keeper.GetParams(ctx) + fee := params.CreateGaugeBaseFee.Add(params.AddDenomFee.MulRaw(int64(len(msg.Coins)))) + if err = server.keeper.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, fee, msg.Coins); err != nil { + return nil, 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()) @@ -58,7 +66,20 @@ func (server msgServer) AddToGauge(goCtx context.Context, msg *types.MsgAddToGau return nil, err } - err = server.keeper.AddToGaugeRewards(ctx, owner, msg.Rewards, msg.GaugeId) + gauge, err := server.keeper.GetGaugeByID(ctx, msg.GaugeId) + if err != nil { + return nil, err + } + + // Charge fess based on the number of coins to add + // 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.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, fee, msg.Rewards); err != nil { + return nil, err + } + + err = server.keeper.AddToGaugeRewards(ctx, owner, msg.Rewards, gauge) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error()) } diff --git a/x/incentives/keeper/suite_test.go b/x/incentives/keeper/suite_test.go index b32aec893..eccb86390 100644 --- a/x/incentives/keeper/suite_test.go +++ b/x/incentives/keeper/suite_test.go @@ -92,8 +92,7 @@ func (suite *KeeperTestSuite) SetupGauges(gaugeDescriptors []perpGaugeDesc, deno // CreateGauge creates a gauge struct given the required params. func (suite *KeeperTestSuite) CreateGauge(isPerpetual bool, addr sdk.AccAddress, coins sdk.Coins, distrTo lockuptypes.QueryCondition, startTime time.Time, numEpoch uint64) (uint64, *types.Gauge) { - addrCoins := coins.Add(sdk.NewCoin(sdk.DefaultBondDenom, types.DYM.MulRaw(10_000))) // create gauge fees - suite.FundAcc(addr, addrCoins) + suite.FundAcc(addr, coins) gaugeID, err := suite.App.IncentivesKeeper.CreateGauge(suite.Ctx, isPerpetual, addr, coins, distrTo, startTime, numEpoch) suite.Require().NoError(err) gauge, err := suite.App.IncentivesKeeper.GetGaugeByID(suite.Ctx, gaugeID) @@ -103,10 +102,11 @@ func (suite *KeeperTestSuite) CreateGauge(isPerpetual bool, addr sdk.AccAddress, // AddToGauge adds coins to the specified gauge. func (suite *KeeperTestSuite) AddToGauge(coins sdk.Coins, gaugeID uint64) uint64 { + gauge, err := suite.App.IncentivesKeeper.GetGaugeByID(suite.Ctx, gaugeID) + suite.Require().NoError(err) addr := sdk.AccAddress([]byte("addrx---------------")) - addrCoins := coins.Add(sdk.NewCoin(sdk.DefaultBondDenom, types.DYM.MulRaw(10_000))) // add to gauge fees - suite.FundAcc(addr, addrCoins) - err := suite.App.IncentivesKeeper.AddToGaugeRewards(suite.Ctx, addr, coins, gaugeID) + suite.FundAcc(addr, coins) + err = suite.App.IncentivesKeeper.AddToGaugeRewards(suite.Ctx, addr, coins, gauge) suite.Require().NoError(err) return gaugeID } diff --git a/x/incentives/types/constants.go b/x/incentives/types/constants.go index 87bad0eb1..35a5f974c 100644 --- a/x/incentives/types/constants.go +++ b/x/incentives/types/constants.go @@ -1,19 +1,14 @@ package types -import ( - "math/big" - - "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" -) +import "cosmossdk.io/math" var ( // DYM represents 1 DYM - DYM = math.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) + DYM = math.NewIntWithDecimal(1, 18) - DefaultCreateGaugeFee = DYM.Mul(sdk.NewInt(10)) - DefaultAddToGaugeFee = math.ZeroInt() - DefaultAddDenomFee = DYM + DefaultCreateGaugeFee = DYM.MulRaw(10) // 10 DYM + DefaultAddToGaugeFee = math.ZeroInt() // 0 DYM + DefaultAddDenomFee = DYM // 1 DYM ) const DefaultDistrEpochIdentifier = "week" diff --git a/x/streamer/keeper/distribute.go b/x/streamer/keeper/distribute.go index 4adb1352c..bf17a3557 100644 --- a/x/streamer/keeper/distribute.go +++ b/x/streamer/keeper/distribute.go @@ -43,7 +43,7 @@ func (k Keeper) DistributeToGauge(ctx sdk.Context, coins sdk.Coins, record types } allocatedCoin := sdk.Coin{Denom: coin.Denom, Amount: allocatingAmount} - err = k.ik.AddToGaugeRewards(ctx, k.ak.GetModuleAddress(types.ModuleName), sdk.NewCoins(allocatedCoin), record.GaugeId) + err = k.ik.AddToGaugeRewardsByID(ctx, k.ak.GetModuleAddress(types.ModuleName), sdk.NewCoins(allocatedCoin), record.GaugeId) if err != nil { return sdk.Coins{}, fmt.Errorf("add rewards to gauge %d: %w", record.GaugeId, err) } diff --git a/x/streamer/types/expected_keepers.go b/x/streamer/types/expected_keepers.go index 51572aead..39ac4c604 100644 --- a/x/streamer/types/expected_keepers.go +++ b/x/streamer/types/expected_keepers.go @@ -37,7 +37,7 @@ type IncentivesKeeper interface { GetLockableDurations(ctx sdk.Context) []time.Duration GetGaugeByID(ctx sdk.Context, gaugeID uint64) (*incentivestypes.Gauge, error) - AddToGaugeRewards(ctx sdk.Context, owner sdk.AccAddress, coins sdk.Coins, gaugeID uint64) error + AddToGaugeRewardsByID(ctx sdk.Context, owner sdk.AccAddress, coins sdk.Coins, gaugeID uint64) error } type SponsorshipKeeper interface {