fix(x/protocolpool): Stop allocating funds to expired funds (#20768)

This commit is contained in:
Facundo Medica
2024-06-26 10:29:04 +00:00
committed by GitHub
parent 4bda5d6a4e
commit 9c909a7238
3 changed files with 74 additions and 4 deletions
+11
View File
@@ -270,6 +270,17 @@ func (k Keeper) iterateAndUpdateFundsDistribution(ctx context.Context, toDistrib
if err != nil {
return true, err
}
cf, err := k.ContinuousFund.Get(ctx, key)
if err != nil {
return true, err
}
// Check if the continuous fund has expired
if cf.Expiry != nil && cf.Expiry.Before(k.HeaderService.HeaderInfo(ctx).Time) {
return false, nil
}
totalPercentageToBeDistributed = totalPercentageToBeDistributed.Add(value)
recipientFundList = append(recipientFundList, recipientFund{
RecipientAddr: addr,
+4 -4
View File
@@ -95,11 +95,11 @@ func (s *KeeperTestSuite) mockSendCoinsFromModuleToAccount(accAddr sdk.AccAddres
}
func (s *KeeperTestSuite) mockWithdrawContinuousFund() {
s.authKeeper.EXPECT().GetModuleAccount(s.ctx, types.ModuleName).Return(poolAcc).AnyTimes()
s.authKeeper.EXPECT().GetModuleAccount(gomock.Any(), types.ModuleName).Return(poolAcc).AnyTimes()
distrBal := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(100000)))
s.bankKeeper.EXPECT().GetAllBalances(s.ctx, gomock.Any()).Return(distrBal).AnyTimes()
s.bankKeeper.EXPECT().SendCoinsFromModuleToAccount(s.ctx, gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
s.stakingKeeper.EXPECT().BondDenom(s.ctx).AnyTimes()
s.bankKeeper.EXPECT().GetAllBalances(gomock.Any(), gomock.Any()).Return(distrBal).AnyTimes()
s.bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
s.stakingKeeper.EXPECT().BondDenom(gomock.Any()).Return("stake", nil).AnyTimes()
}
func (s *KeeperTestSuite) mockStreamFunds() {
+59
View File
@@ -968,3 +968,62 @@ func (suite *KeeperTestSuite) TestCancelContinuousFund() {
})
}
}
// TestWithdrawExpiredFunds checks that a continuous fund cannot be withdrawn if it has expired.
// There was a case in which an expired continuous fund would keep getting funds allocated when
// other funds were withdrawn. These funds would then get withdrawn if CancelContinuousFund was called.
func (suite *KeeperTestSuite) TestWithdrawExpiredFunds() {
suite.SetupTest()
recipientStrAddr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(recipientAddr)
suite.Require().NoError(err)
recipient2 := sdk.AccAddress([]byte("recipientAddr2___________________"))
recipient2StrAddr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(recipient2)
suite.Require().NoError(err)
expiration := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(24 * time.Hour)
_, err = suite.msgServer.CreateContinuousFund(suite.ctx, &types.MsgCreateContinuousFund{
Authority: suite.poolKeeper.GetAuthority(),
Recipient: recipientStrAddr,
Percentage: math.LegacyMustNewDecFromStr("0.5"),
Expiry: &expiration,
})
suite.Require().NoError(err)
_, err = suite.msgServer.CreateContinuousFund(suite.ctx, &types.MsgCreateContinuousFund{
Authority: suite.poolKeeper.GetAuthority(),
Recipient: recipient2StrAddr,
Percentage: math.LegacyMustNewDecFromStr("0.5"),
})
suite.Require().NoError(err)
toDistribute := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(100000)))
suite.mockStreamFunds()
err = suite.poolKeeper.SetToDistribute(suite.ctx, toDistribute, suite.poolKeeper.GetAuthority())
suite.Require().NoError(err)
suite.mockWithdrawContinuousFund()
_, err = suite.msgServer.WithdrawContinuousFund(suite.ctx, &types.MsgWithdrawContinuousFund{RecipientAddress: recipientStrAddr})
suite.Require().NoError(err)
header := suite.ctx.HeaderInfo()
header.Time = expiration.Add(1 * time.Second)
suite.ctx = suite.ctx.WithHeaderInfo(header)
_, err = suite.msgServer.WithdrawContinuousFund(suite.ctx, &types.MsgWithdrawContinuousFund{RecipientAddress: recipientStrAddr})
suite.Require().ErrorContains(err, "continuous fund expired for recipient")
suite.mockStreamFunds()
err = suite.poolKeeper.SetToDistribute(suite.ctx, toDistribute, suite.poolKeeper.GetAuthority())
suite.Require().NoError(err)
suite.mockWithdrawContinuousFund()
_, err = suite.msgServer.WithdrawContinuousFund(suite.ctx, &types.MsgWithdrawContinuousFund{RecipientAddress: recipient2StrAddr})
suite.Require().NoError(err)
res, err := suite.msgServer.CancelContinuousFund(suite.ctx, &types.MsgCancelContinuousFund{
Authority: suite.poolKeeper.GetAuthority(),
RecipientAddress: recipientStrAddr,
})
suite.Require().NoError(err)
suite.Require().Equal(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(0)), res.WithdrawnAllocatedFund)
}