-
Notifications
You must be signed in to change notification settings - Fork 382
feat(incentives): rollapp gauges upgrade handler and burn creation fee #1113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
159b822
60cd13d
c76aee1
ac6c7bc
438d4a4
1d50576
5edb098
9669ebd
ea9acc8
4eb4fb6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,6 @@ import ( | |
|
||
"github.com/stretchr/testify/suite" | ||
|
||
apptesting "github.com/dymensionxyz/dymension/v3/app/apptesting" | ||
"github.com/dymensionxyz/dymension/v3/x/incentives/types" | ||
lockuptypes "github.com/osmosis-labs/osmosis/v15/x/lockup/types" | ||
|
||
|
@@ -226,99 +225,3 @@ func (suite *KeeperTestSuite) TestGaugeOperations() { | |
} | ||
} | ||
} | ||
|
||
func (suite *KeeperTestSuite) TestChargeFeeIfSufficientFeeDenomBalance() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. moved to msg_server_test.go |
||
const baseFee = int64(100) | ||
|
||
testcases := map[string]struct { | ||
accountBalanceToFund sdk.Coin | ||
feeToCharge int64 | ||
gaugeCoins sdk.Coins | ||
|
||
expectError bool | ||
}{ | ||
"fee + base denom gauge coin == acount balance, success": { | ||
accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), | ||
feeToCharge: baseFee / 2, | ||
gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), | ||
}, | ||
"fee + base denom gauge coin < acount balance, success": { | ||
accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), | ||
feeToCharge: baseFee/2 - 1, | ||
gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), | ||
}, | ||
"fee + base denom gauge coin > acount balance, error": { | ||
accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), | ||
feeToCharge: baseFee/2 + 1, | ||
gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), | ||
expectError: true, | ||
}, | ||
"fee + base denom gauge coin < acount balance, custom values, success": { | ||
accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(11793193112)), | ||
feeToCharge: 55, | ||
gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(328812))), | ||
}, | ||
"account funded with coins other than base denom, error": { | ||
accountBalanceToFund: sdk.NewCoin("usdc", sdk.NewInt(baseFee)), | ||
feeToCharge: baseFee, | ||
gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), | ||
expectError: true, | ||
}, | ||
"fee == account balance, no gauge coins, success": { | ||
accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), | ||
feeToCharge: baseFee, | ||
}, | ||
"gauge coins == account balance, no fee, success": { | ||
accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), | ||
gaugeCoins: sdk.NewCoins(sdk.NewCoin("adym", sdk.NewInt(baseFee))), | ||
}, | ||
"fee == account balance, gauge coins in denom other than base, success": { | ||
accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), | ||
feeToCharge: baseFee, | ||
gaugeCoins: sdk.NewCoins(sdk.NewCoin("usdc", sdk.NewInt(baseFee*2))), | ||
}, | ||
"fee + gauge coins == account balance, multiple gauge coins, one in denom other than base, success": { | ||
accountBalanceToFund: sdk.NewCoin("adym", sdk.NewInt(baseFee)), | ||
feeToCharge: baseFee / 2, | ||
gaugeCoins: sdk.NewCoins(sdk.NewCoin("usdc", sdk.NewInt(baseFee*2)), sdk.NewCoin("adym", sdk.NewInt(baseFee/2))), | ||
}, | ||
} | ||
|
||
for name, tc := range testcases { | ||
suite.Run(name, func() { | ||
suite.SetupTest() | ||
|
||
err := suite.App.TxFeesKeeper.SetBaseDenom(suite.Ctx, "adym") | ||
suite.Require().NoError(err) | ||
|
||
testAccount := apptesting.CreateRandomAccounts(1)[0] | ||
ctx := suite.Ctx | ||
incentivesKeepers := suite.App.IncentivesKeeper | ||
bankKeeper := suite.App.BankKeeper | ||
|
||
// Pre-fund account. | ||
// suite.FundAcc(testAccount, testutil.DefaultAcctFunds) | ||
suite.FundAcc(testAccount, sdk.NewCoins(tc.accountBalanceToFund)) | ||
|
||
oldBalanceAmount := bankKeeper.GetBalance(ctx, testAccount, "adym").Amount | ||
|
||
// System under test. | ||
err = incentivesKeepers.ChargeFeeIfSufficientFeeDenomBalance(ctx, testAccount, sdk.NewInt(tc.feeToCharge), tc.gaugeCoins) | ||
|
||
// Assertions. | ||
newBalanceAmount := bankKeeper.GetBalance(ctx, testAccount, "adym").Amount | ||
if tc.expectError { | ||
suite.Require().Error(err) | ||
|
||
// check account balance unchanged | ||
suite.Require().Equal(oldBalanceAmount, newBalanceAmount) | ||
} else { | ||
suite.Require().NoError(err) | ||
|
||
// check account balance changed. | ||
expectedNewBalanceAmount := oldBalanceAmount.Sub(sdk.NewInt(tc.feeToCharge)) | ||
suite.Require().Equal(expectedNewBalanceAmount.String(), newBalanceAmount.String()) | ||
} | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,14 +2,17 @@ package keeper | |
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
errorsmod "cosmossdk.io/errors" | ||
|
||
"github.com/dymensionxyz/dymension/v3/x/incentives/types" | ||
"github.com/dymensionxyz/gerr-cosmos/gerrc" | ||
"github.com/osmosis-labs/osmosis/v15/osmoutils" | ||
|
||
errorsmod "cosmossdk.io/errors" | ||
txfeestypes "github.com/osmosis-labs/osmosis/v15/x/txfees/types" | ||
mtsitrin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" | ||
) | ||
|
||
// msgServer provides a way to reference keeper pointer in the message server interface. | ||
|
@@ -27,21 +30,24 @@ func NewMsgServerImpl(keeper *Keeper) types.MsgServer { | |
var _ types.MsgServer = msgServer{} | ||
|
||
// CreateGauge creates a gauge and sends coins to the gauge. | ||
// Creation fee is charged from the address and sent to the txfees module to be burned. | ||
// Emits create gauge event and returns the create gauge response. | ||
func (server msgServer) CreateGauge(goCtx context.Context, msg *types.MsgCreateGauge) (*types.MsgCreateGaugeResponse, error) { | ||
ctx := sdk.UnwrapSDKContext(goCtx) | ||
k := server.keeper | ||
mtsitrin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
owner, err := sdk.AccAddressFromBech32(msg.Owner) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if err := server.keeper.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, types.CreateGaugeFee, msg.Coins); err != nil { | ||
return nil, err | ||
// charge creation fee | ||
if err := k.chargeCreationFee(ctx, owner); err != nil { | ||
return nil, fmt.Errorf("charge creation fee: %w", err) | ||
} | ||
|
||
gaugeID, err := server.keeper.CreateGauge(ctx, msg.IsPerpetual, owner, msg.Coins, msg.DistributeTo, msg.StartTime, msg.NumEpochsPaidOver) | ||
mtsitrin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error()) | ||
return nil, fmt.Errorf("create gauge: %w", err) | ||
} | ||
|
||
ctx.EventManager().EmitEvents(sdk.Events{ | ||
|
@@ -63,12 +69,9 @@ func (server msgServer) AddToGauge(goCtx context.Context, msg *types.MsgAddToGau | |
return nil, err | ||
} | ||
|
||
if err := server.keeper.chargeFeeIfSufficientFeeDenomBalance(ctx, owner, types.AddToGaugeFee, msg.Rewards); err != nil { | ||
return nil, err | ||
} | ||
err = server.keeper.AddToGaugeRewards(ctx, owner, msg.Rewards, msg.GaugeId) | ||
mtsitrin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error()) | ||
return nil, fmt.Errorf("add to gauge rewards: %w", err) | ||
} | ||
|
||
ctx.EventManager().EmitEvents(sdk.Events{ | ||
|
@@ -81,30 +84,12 @@ func (server msgServer) AddToGauge(goCtx context.Context, msg *types.MsgAddToGau | |
return &types.MsgAddToGaugeResponse{}, nil | ||
} | ||
|
||
// chargeFeeIfSufficientFeeDenomBalance charges fee in the base denom on the address if the address has | ||
// balance that is less than fee + amount of the coin from gaugeCoins that is of base denom. | ||
// gaugeCoins might not have a coin of tx base denom. In that case, fee is only compared to balance. | ||
// The fee is sent to the community pool. | ||
func (k Keeper) chargeFeeIfSufficientFeeDenomBalance(ctx sdk.Context, address sdk.AccAddress, fee sdk.Int, gaugeCoins sdk.Coins) (err error) { | ||
var feeDenom string | ||
if k.tk == nil { | ||
feeDenom, err = sdk.GetBaseDenom() | ||
} else { | ||
feeDenom, err = k.tk.GetBaseDenom(ctx) | ||
} | ||
// chargeCreationFee charges the creation fee from the address. | ||
// The fee is sent to the txfees module, to be burned. | ||
func (k Keeper) chargeCreationFee(ctx sdk.Context, address sdk.AccAddress) (err error) { | ||
feeDenom, err := k.tk.GetBaseDenom(ctx) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
totalCost := gaugeCoins.AmountOf(feeDenom).Add(fee) | ||
accountBalance := k.bk.GetBalance(ctx, address, feeDenom).Amount | ||
|
||
if accountBalance.LT(totalCost) { | ||
return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, "account's balance is less than the total cost of the message. Balance: %s %s, Total Cost: %s", feeDenom, accountBalance, totalCost) | ||
} | ||
|
||
if err := k.ck.FundCommunityPool(ctx, sdk.NewCoins(sdk.NewCoin(feeDenom, fee)), address); err != nil { | ||
return err | ||
return errorsmod.Wrapf(gerrc.ErrInternal, "get base denom: %v", err) | ||
} | ||
return nil | ||
return k.bk.SendCoinsFromAccountToModule(ctx, address, txfeestypes.ModuleName, sdk.NewCoins(sdk.NewCoin(feeDenom, types.CreateGaugeFee))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what happens to these coins then? They stay in the module, should we burn them? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the txfees module burns the accumulated tokens periodiocally |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we not skip frozen rollapps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer not.
It doesn't really matter, as this gaguge won't be active for frozen rollapp, but it breaks the assumption of
rollapp gauge for each rollapp