Skip to content

Commit

Permalink
feat(incentives)!: create gauge and add to gauge fee charge (#2227)
Browse files Browse the repository at this point in the history
* feat(incentives)!: create gauge and add to gauge fee charge

* initialize txfees keeper before incentives

* finish TestChargeFee

* refactor to charge fee in message server

* more tests

* clean up

* test balances

* test create gauge fees (#2228)

* test create gauge fees

* add mod account to test

* test create gauge fees

* add mod account to test

* move to msg server

* merge

* add comments

* account keeper comment

* fix TestCreateGaugeFee

* apply appparams.BaseCoinUnit

* remove txfees keeper from incentives and revert order

* clean up

* remove unused keepers fromm incentives keeper

* Update x/incentives/keeper/gauge.go

* Update x/incentives/keeper/gauge.go

* clean up

* fixture names

* chargeFeeIfSufficientFeeDenomBalance test name

* changelog

* comment

* finished tests (#2230)

* finished tests

* use keeper instead of math

* remove unused tests

* clean up

* sim only allow accounts with enough to pay fee

* lint

* move fee to types and reuse in simulation (#2234)

Co-authored-by: Adam Tucker <[email protected]>
Co-authored-by: Adam Tucker <[email protected]>
  • Loading branch information
3 people authored Jul 26, 2022
1 parent bc78ade commit cbfde11
Show file tree
Hide file tree
Showing 11 changed files with 395 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* [#2016](https://github.com/osmosis-labs/osmosis/pull/2016) Add fixed 10000 gas cost for each Balancer swap
* [#2147](https://github.com/osmosis-labs/osmosis/pull/2147) Set MaxAgeNumBlocks in v11 Upgrade Handler to two weeks.
* [#2193](https://github.com/osmosis-labs/osmosis/pull/2193) Add TwapKeeper to the Osmosis app
* [#2227](https://github.com/osmosis-labs/osmosis/pull/2227) Enable charging fee in base denom for `CreateGauge` and `AddToGauge`.

#### Golang API breaks

Expand Down
1 change: 1 addition & 0 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers(
appKeepers.BankKeeper,
appKeepers.LockupKeeper,
appKeepers.EpochsKeeper,
appKeepers.DistrKeeper,
)

appKeepers.SuperfluidKeeper = superfluidkeeper.NewKeeper(
Expand Down
9 changes: 7 additions & 2 deletions x/incentives/keeper/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
)

// AddGaugeRefByKey appends the provided gauge ID into an array associated with the provided key.
func (k Keeper) AddGaugeRefByKey(ctx sdk.Context, key []byte, guageID uint64) error {
return k.addGaugeRefByKey(ctx, key, guageID)
func (k Keeper) AddGaugeRefByKey(ctx sdk.Context, key []byte, gaugeID uint64) error {
return k.addGaugeRefByKey(ctx, key, gaugeID)
}

// DeleteGaugeRefByKey removes the provided gauge ID from an array associated with the provided key.
Expand All @@ -35,3 +35,8 @@ func (k Keeper) MoveUpcomingGaugeToActiveGauge(ctx sdk.Context, gauge types.Gaug
func (k Keeper) MoveActiveGaugeToFinishedGauge(ctx sdk.Context, gauge types.Gauge) error {
return k.moveActiveGaugeToFinishedGauge(ctx, gauge)
}

// ChargeFeeIfSufficientFeeDenomBalance see chargeFeeIfSufficientFeeDenomBalance spec.
func (k Keeper) ChargeFeeIfSufficientFeeDenomBalance(ctx sdk.Context, address sdk.AccAddress, fee sdk.Int, gaugeCoins sdk.Coins) error {
return k.chargeFeeIfSufficientFeeDenomBalance(ctx, address, fee, gaugeCoins)
}
19 changes: 19 additions & 0 deletions x/incentives/keeper/gauge.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import (
"strings"
"time"

sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/gogo/protobuf/proto"
db "github.com/tendermint/tm-db"

appparams "github.com/osmosis-labs/osmosis/v10/app/params"
epochtypes "github.com/osmosis-labs/osmosis/v10/x/epochs/types"
"github.com/osmosis-labs/osmosis/v10/x/incentives/types"
lockuptypes "github.com/osmosis-labs/osmosis/v10/x/lockup/types"
Expand Down Expand Up @@ -278,3 +280,20 @@ func (k Keeper) GetEpochInfo(ctx sdk.Context) epochtypes.EpochInfo {
params := k.GetParams(ctx)
return k.ek.GetEpochInfo(ctx, params.DistrEpochIdentifier)
}

// chargeFeeIfSufficientFeeDenomBalance charges fee in the base denom on the address if the address has
// balance that is less than fee + amount of the coin from gaugeCoins that is of base denom.
// gaugeCoins might not have a coin of tx base denom. In that case, fee is only compared to balance.
// The fee is sent to the community pool.
// Returns nil on success, error otherwise.
func (k Keeper) chargeFeeIfSufficientFeeDenomBalance(ctx sdk.Context, address sdk.AccAddress, fee sdk.Int, gaugeCoins sdk.Coins) (err error) {
totalCost := gaugeCoins.AmountOf(appparams.BaseCoinUnit).Add(fee)
accountBalance := k.bk.GetBalance(ctx, address, appparams.BaseCoinUnit).Amount
if accountBalance.LT(totalCost) {
return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "account's balance of %s (%s) is less than the total cost of the message (%s)", appparams.BaseCoinUnit, accountBalance, totalCost)
}
if err := k.dk.FundCommunityPool(ctx, sdk.NewCoins(sdk.NewCoin(appparams.BaseCoinUnit, fee)), address); err != nil {
return err
}
return nil
}
96 changes: 96 additions & 0 deletions x/incentives/keeper/gauge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/stretchr/testify/suite"

appparams "github.com/osmosis-labs/osmosis/v10/app/params"
"github.com/osmosis-labs/osmosis/v10/x/incentives/types"
lockuptypes "github.com/osmosis-labs/osmosis/v10/x/lockup/types"

Expand Down Expand Up @@ -237,3 +238,98 @@ func (suite *KeeperTestSuite) TestGaugeOperations() {
}
}
}

func (suite *KeeperTestSuite) TestChargeFeeIfSufficientFeeDenomBalance() {
const baseFee = int64(100)

testcases := map[string]struct {
accountBalanceToFund sdk.Coin
feeToCharge int64
gaugeCoins sdk.Coins

expectError bool
}{
"fee + base denom gauge coin == acount balance, success": {
accountBalanceToFund: sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(baseFee)),
feeToCharge: baseFee / 2,
gaugeCoins: sdk.NewCoins(sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(baseFee/2))),
},
"fee + base denom gauge coin < acount balance, success": {
accountBalanceToFund: sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(baseFee)),
feeToCharge: baseFee/2 - 1,
gaugeCoins: sdk.NewCoins(sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(baseFee/2))),
},
"fee + base denom gauge coin > acount balance, error": {
accountBalanceToFund: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseFee)),
feeToCharge: baseFee/2 + 1,
gaugeCoins: sdk.NewCoins(sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(baseFee/2))),

expectError: true,
},
"fee + base denom gauge coin < acount balance, custom values, success": {
accountBalanceToFund: sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(11793193112)),
feeToCharge: 55,
gaugeCoins: sdk.NewCoins(sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(328812))),
},
"account funded with coins other than base denom, error": {
accountBalanceToFund: sdk.NewCoin("usdc", sdk.NewInt(baseFee)),
feeToCharge: baseFee,
gaugeCoins: sdk.NewCoins(sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(baseFee/2))),

expectError: true,
},
"fee == account balance, no gauge coins, success": {
accountBalanceToFund: sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(baseFee)),
feeToCharge: baseFee,
},
"gauge coins == account balance, no fee, success": {
accountBalanceToFund: sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(baseFee)),
gaugeCoins: sdk.NewCoins(sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(baseFee))),
},
"fee == account balance, gauge coins in denom other than base, success": {
accountBalanceToFund: sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(baseFee)),
feeToCharge: baseFee,
gaugeCoins: sdk.NewCoins(sdk.NewCoin("usdc", sdk.NewInt(baseFee*2))),
},
"fee + gauge coins == account balance, multiple gauge coins, one in denom other than base, success": {
accountBalanceToFund: sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(baseFee)),
feeToCharge: baseFee / 2,
gaugeCoins: sdk.NewCoins(sdk.NewCoin("usdc", sdk.NewInt(baseFee*2)), sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(baseFee/2))),
},
}

for name, tc := range testcases {
suite.Run(name, func() {
suite.SetupTest()

testAccount := suite.TestAccs[0]

ctx := suite.Ctx
incentivesKeepers := suite.App.IncentivesKeeper
bankKeeper := suite.App.BankKeeper

// Pre-fund account.
suite.FundAcc(testAccount, sdk.NewCoins(tc.accountBalanceToFund))

oldBalanceAmount := bankKeeper.GetBalance(ctx, testAccount, appparams.DefaultBondDenom).Amount

// System under test.
err := incentivesKeepers.ChargeFeeIfSufficientFeeDenomBalance(ctx, testAccount, sdk.NewInt(tc.feeToCharge), tc.gaugeCoins)

// Assertions.
newBalanceAmount := bankKeeper.GetBalance(ctx, testAccount, appparams.DefaultBondDenom).Amount
if tc.expectError {
suite.Require().Error(err)

// check account balance unchanged
suite.Require().Equal(oldBalanceAmount, newBalanceAmount)
} else {
suite.Require().NoError(err)

// check account balance changed.
expectedNewBalanceAmount := oldBalanceAmount.Sub(sdk.NewInt(tc.feeToCharge))
suite.Require().Equal(expectedNewBalanceAmount.String(), newBalanceAmount.String())
}
})
}
}
4 changes: 3 additions & 1 deletion x/incentives/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ type Keeper struct {
bk types.BankKeeper
lk types.LockupKeeper
ek types.EpochKeeper
dk types.DistrKeeper
}

// NewKeeper returns a new instance of the incentive module keeper struct.
func NewKeeper(cdc codec.Codec, storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, bk types.BankKeeper, lk types.LockupKeeper, ek types.EpochKeeper) *Keeper {
func NewKeeper(cdc codec.Codec, storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, bk types.BankKeeper, lk types.LockupKeeper, ek types.EpochKeeper, dk types.DistrKeeper) *Keeper {
if !paramSpace.HasKeyTable() {
paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable())
}
Expand All @@ -37,6 +38,7 @@ func NewKeeper(cdc codec.Codec, storeKey sdk.StoreKey, paramSpace paramtypes.Sub
bk: bk,
lk: lk,
ek: ek,
dk: dk,
}
}

Expand Down
8 changes: 8 additions & 0 deletions x/incentives/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ 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 {
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())
Expand All @@ -56,6 +60,10 @@ func (server msgServer) AddToGauge(goCtx context.Context, msg *types.MsgAddToGau
if err != nil {
return nil, err
}

if err := server.keeper.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, types.AddToGaugeFee, msg.Rewards); err != nil {
return nil, err
}
err = server.keeper.AddToGaugeRewards(ctx, owner, msg.Rewards, msg.GaugeId)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
Expand Down
Loading

0 comments on commit cbfde11

Please sign in to comment.