Skip to content

Commit

Permalink
fixed duplicate pending unlock participant
Browse files Browse the repository at this point in the history
  • Loading branch information
faneaatiku committed Jun 1, 2024
1 parent b2d6409 commit 1ebea47
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 1 deletion.
13 changes: 12 additions & 1 deletion x/rewards/keeper/msg_server_exit_staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,24 @@ func (k msgServer) ExitStaking(goCtx context.Context, msg *types.MsgExitStaking)
func (k msgServer) beginUnlock(ctx sdk.Context, p types.StakingRewardParticipant, sr types.StakingReward) error {
lockedUntil := k.epochKeeper.GetEpochCountByIdentifier(ctx, expirationEpoch)
lockedUntil += int64(sr.Lock) * 24
pendingKey := types.CreatePendingUnlockParticipantKey(lockedUntil, fmt.Sprintf("%s/%s", sr.RewardId, p.Address))
pending := types.PendingUnlockParticipant{
Index: types.CreatePendingUnlockParticipantKey(lockedUntil, fmt.Sprintf("%s/%s", sr.RewardId, p.Address)),
Index: pendingKey,
Address: p.Address,
Amount: p.Amount,
Denom: sr.StakingDenom,
}

inStore, found := k.GetPendingUnlockParticipant(ctx, pendingKey)
if found {
//we already have a pending unlock for this reward and participant at the same epoch
//update the amount, so it can all be unlocked at once
inStoreAmount, _ := sdk.NewIntFromString(inStore.Amount)
pendingAmount, _ := sdk.NewIntFromString(pending.Amount)
pendingAmount.Add(inStoreAmount)
pending.Amount = pendingAmount.String()
}

//in case the lock is 0 send the funds immediately
if sr.Lock == 0 {
return k.performUnlock(ctx, &pending)
Expand Down
52 changes: 52 additions & 0 deletions x/rewards/keeper/msg_server_exit_staking_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,55 @@ func (suite *IntegrationTestSuite) TestMsgExitStaking_Success_RemovingStakingRew
_, f = suite.k.GetStakingReward(suite.ctx, sr.RewardId)
suite.Require().False(f)
}

func (suite *IntegrationTestSuite) TestMsgExitStaking_Success_RemovingStakingReward_WithoutLock() {
//dependencies
goCtx := sdk.WrapSDKContext(suite.ctx)
addr1 := sdk.AccAddress("addr1_______________")
sr := types.StakingReward{
RewardId: "01",
PrizeDenom: denomBze,
StakedAmount: "50",
DistributedStake: "5",
Lock: 0,
StakingDenom: denomBze,
Duration: 100,
Payouts: 100,
}
suite.k.SetStakingReward(suite.ctx, sr)

srp := types.StakingRewardParticipant{
Address: addr1.String(),
RewardId: sr.RewardId,
Amount: "50",
JoinedAt: "0",
}
suite.k.SetStakingRewardParticipant(suite.ctx, srp)

balances := sdk.NewCoins(newStakeCoin(10000), newBzeCoin(50000))
suite.Require().NoError(simapp.FundModuleAccount(suite.app.BankKeeper, suite.ctx, types.ModuleName, balances))

//tests and asserts
msg := types.MsgExitStaking{
Creator: addr1.String(),
RewardId: sr.RewardId,
}
_, err := suite.msgServer.ExitStaking(goCtx, &msg)
suite.Require().NoError(err)

creatorBalance := suite.app.BankKeeper.GetAllBalances(suite.ctx, addr1)
//check the user retrieves the unclaimed rewards + the staked balance
suite.Require().EqualValues(creatorBalance.AmountOf(denomBze).String(), "300")

//check the unlock was NOT created since the funds should be released immediately
unlockList := suite.k.GetAllPendingUnlockParticipant(suite.ctx)
suite.Require().Empty(unlockList)

//check the staking reward participant was deleted
_, f := suite.k.GetStakingRewardParticipant(suite.ctx, srp.Address, sr.RewardId)
suite.Require().False(f)

//check the staking reward was deleted
_, f = suite.k.GetStakingReward(suite.ctx, sr.RewardId)
suite.Require().False(f)
}
37 changes: 37 additions & 0 deletions x/rewards/keeper/msg_server_join_staking_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ func (suite *IntegrationTestSuite) TestMsgJoinStaking_AmountLowerThanMinStake()
//dependencies
goCtx := sdk.WrapSDKContext(suite.ctx)
addr1 := sdk.AccAddress("addr1_______________")
balances := sdk.NewCoins(sdk.NewInt64Coin("ubze", 10000))
suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, suite.ctx, addr1, balances))
sr := types.StakingReward{
RewardId: "01",
PrizeDenom: denomBze,
Expand All @@ -71,6 +73,41 @@ func (suite *IntegrationTestSuite) TestMsgJoinStaking_AmountLowerThanMinStake()
suite.Require().ErrorContains(err, "amount is smaller than staking reward min stake")
}

func (suite *IntegrationTestSuite) TestMsgJoinStaking_AllowedAmountLowerThanMinStake() {
//dependencies
goCtx := sdk.WrapSDKContext(suite.ctx)
addr1 := sdk.AccAddress("addr1_______________")
balances := sdk.NewCoins(sdk.NewInt64Coin("ubze", 10000))
suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, suite.ctx, addr1, balances))
sr := types.StakingReward{
RewardId: "01",
PrizeDenom: denomBze,
StakedAmount: "50",
DistributedStake: "5",
Lock: 100,
StakingDenom: denomBze,
Duration: 100,
Payouts: 5,
MinStake: 1000,
}
suite.k.SetStakingReward(suite.ctx, sr)

msg := types.MsgJoinStaking{
Creator: addr1.String(),
RewardId: sr.RewardId,
Amount: "1000",
}
//first stake the min amount allowed
_, err := suite.msgServer.JoinStaking(goCtx, &msg)
suite.Require().NoError(err)

//try to stake an amount lower than min stake
//it should be allowed since we already have a stake greater than/equal to min stake
msg.Amount = "50"
_, err = suite.msgServer.JoinStaking(goCtx, &msg)
suite.Require().NoError(err)
}

func (suite *IntegrationTestSuite) TestMsgJoinStaking_NotEnoughBalance() {
//dependencies
goCtx := sdk.WrapSDKContext(suite.ctx)
Expand Down
13 changes: 13 additions & 0 deletions x/rewards/keeper/pending_unlock_participant.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ func (k Keeper) SetPendingUnlockParticipant(ctx sdk.Context, p types.PendingUnlo
store.Set(types.PendingUnlockParticipantKey(p.Index), b)
}

// GetPendingUnlockParticipant returns a types.PendingUnlockParticipant from its index
func (k Keeper) GetPendingUnlockParticipant(ctx sdk.Context, index string) (val types.PendingUnlockParticipant, found bool) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PendingUnlockParticipantKeyPrefix))

b := store.Get(types.PendingUnlockParticipantKey(index))
if b == nil {
return val, false
}

k.cdc.MustUnmarshal(b, &val)
return val, true
}

// RemovePendingUnlockParticipant removes a types.PendingUnlockParticipant from the store
func (k Keeper) RemovePendingUnlockParticipant(ctx sdk.Context, p types.PendingUnlockParticipant) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PendingUnlockParticipantKeyPrefix))
Expand Down

0 comments on commit 1ebea47

Please sign in to comment.