Skip to content

Commit

Permalink
Merge pull request #335 from terra-money/fix/v0.3/claim-accured-rewar…
Browse files Browse the repository at this point in the history
…ds-first

Fix/v0.3/claim accured rewards first
  • Loading branch information
emidev98 authored Mar 25, 2024
2 parents 61faf64 + e4b0a75 commit d576fa8
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 1 deletion.
6 changes: 6 additions & 0 deletions x/alliance/keeper/delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, validator type
if err != nil {
return nil, err
}
} else {
// Claim validator rewards first to ensure that we distribute accrued rewards to delegators before the new delegation
_, err = k.ClaimValidatorRewards(ctx, validator)
if err != nil {
return nil, err
}
}

// Create or update a delegation
Expand Down
38 changes: 37 additions & 1 deletion x/alliance/keeper/tests/delegation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"testing"
"time"

authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

"cosmossdk.io/math"

test_helpers "github.com/terra-money/alliance/app"
Expand Down Expand Up @@ -600,6 +602,7 @@ func TestSuccessfulUndelegation(t *testing.T) {
},
})
delegations := app.StakingKeeper.GetAllDelegations(ctx)
queryServer := keeper.NewQueryServerImpl(app.AllianceKeeper)
require.Len(t, delegations, 1)
unbondingTime := app.StakingKeeper.UnbondingTime(ctx)

Expand All @@ -617,6 +620,12 @@ func TestSuccessfulUndelegation(t *testing.T) {
err = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, delAddr, sdk.NewCoins(sdk.NewCoin(AllianceDenom, sdk.NewInt(2000_000))))
require.NoError(t, err)

// Mint reward tokens
err = app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, sdk.NewCoins(sdk.NewCoin("reward", math.NewInt(2000_000))))
require.NoError(t, err)
err = app.BankKeeper.SendCoinsFromModuleToModule(ctx, minttypes.ModuleName, authtypes.FeeCollectorName, sdk.NewCoins(sdk.NewCoin("reward", math.NewInt(2000_000))))
require.NoError(t, err)

// Delegate to a validator
_, err = app.AllianceKeeper.Delegate(ctx, delAddr, val, sdk.NewCoin(AllianceDenom, sdk.NewInt(1000_000)))
require.NoError(t, err)
Expand All @@ -640,15 +649,31 @@ func TestSuccessfulUndelegation(t *testing.T) {
Shares: sdk.NewDec(2),
}, d)

err = app.AllianceKeeper.AddAssetsToRewardPool(ctx, app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName), val, sdk.NewCoins(sdk.NewCoin("reward", math.NewInt(1000_000))))
require.NoError(t, err)

rewards, err := queryServer.AllianceDelegationRewards(ctx, &types.QueryAllianceDelegationRewardsRequest{
DelegatorAddr: delAddr.String(),
ValidatorAddr: valAddr.String(),
Denom: AllianceDenom,
Pagination: nil,
})
require.NoError(t, err)
require.Equal(t, 1, len(rewards.Rewards))
rewardAmount := rewards.Rewards[0].Amount

// Immediately undelegate from the validator
_, err = app.AllianceKeeper.Undelegate(ctx, delAddr, val, sdk.NewCoin(AllianceDenom, sdk.NewInt(250_000)))
require.NoError(t, err)

_, err = app.AllianceKeeper.Undelegate(ctx, delAddr, val, sdk.NewCoin(AllianceDenom, sdk.NewInt(250_000)))
require.NoError(t, err)

// Check that rewards sent out
rewardBalance := app.BankKeeper.GetBalance(ctx, delAddr, "reward")
require.Equal(t, rewardAmount, rewardBalance.Amount)

// Query unbondings directly from the entry point
queryServer := keeper.NewQueryServerImpl(app.AllianceKeeper)
res, err := queryServer.AllianceUnbondingsByDelegator(ctx, &types.QueryAllianceUnbondingsByDelegatorRequest{
DelegatorAddr: delAddr.String(),
})
Expand Down Expand Up @@ -725,6 +750,17 @@ func TestSuccessfulUndelegation(t *testing.T) {
// Completing again should not process anymore undelegations
err = app.AllianceKeeper.CompleteUnbondings(ctx)
require.NoError(t, err)

q := keeper.NewQueryServerImpl(app.AllianceKeeper)
rewardRes, err := q.AllianceDelegationRewards(ctx, &types.QueryAllianceDelegationRewardsRequest{
DelegatorAddr: delAddr.String(),
ValidatorAddr: valAddr.String(),
Denom: AllianceDenom,
Pagination: nil,
})

require.NoError(t, err)
require.Equal(t, 0, len(rewardRes.Rewards))
}

func TestUndelegationWithoutDelegation(t *testing.T) {
Expand Down
91 changes: 91 additions & 0 deletions x/alliance/keeper/tests/reward_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1460,3 +1460,94 @@ func TestClaimRewardsWithDifferentValidators(t *testing.T) {
require.NoError(t, err)
require.Equal(t, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(800_000))), coins)
}

func TestRewardsDelegateBeforeValidatorClaim(t *testing.T) {
var err error
app, ctx := createTestContext(t)
ctx = ctx.WithBlockHeight(1)
app.AllianceKeeper.InitGenesis(ctx, &types.GenesisState{
Params: types.DefaultParams(),
Assets: []types.AllianceAsset{
types.NewAllianceAsset(AllianceDenom, sdk.NewDec(2), sdk.NewDec(0), sdk.NewDec(5), sdk.NewDec(0), ctx.BlockTime()),
types.NewAllianceAsset(AllianceDenomTwo, sdk.NewDec(8), sdk.NewDec(2), sdk.NewDec(12), sdk.NewDec(0), ctx.BlockTime()),
},
})

// Set tax and rewards to be zero for easier calculation
distParams := app.DistrKeeper.GetParams(ctx)
distParams.CommunityTax = sdk.ZeroDec()

err = app.DistrKeeper.SetParams(ctx, distParams)
require.NoError(t, err)

// Accounts
// mintPoolAddr := app.AccountKeeper.GetModuleAddress(minttypes.ModuleName)
bondDenom := app.StakingKeeper.BondDenom(ctx)
require.NoError(t, err)
addrs := test_helpers.AddTestAddrsIncremental(app, ctx, 4, sdk.NewCoins(
sdk.NewCoin(AllianceDenom, sdk.NewInt(10_000_000)),
sdk.NewCoin(AllianceDenomTwo, sdk.NewInt(10_000_000)),
))

pks := test_helpers.CreateTestPubKeys(2)

// Creating two validators with 0% commission
valAddr1 := sdk.ValAddress(addrs[0])
_val1 := teststaking.NewValidator(t, valAddr1, pks[0])
_val1.Commission = stakingtypes.Commission{
CommissionRates: stakingtypes.CommissionRates{
Rate: sdk.NewDec(0),
MaxRate: sdk.NewDec(0),
MaxChangeRate: sdk.NewDec(0),
},
UpdateTime: time.Now(),
}
test_helpers.RegisterNewValidator(t, app, ctx, _val1)
val1, _ := app.AllianceKeeper.GetAllianceValidator(ctx, valAddr1)

user1 := addrs[2]
user2 := addrs[3]

// Mint bond denom
err = app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, sdk.NewCoins(sdk.NewCoin(bondDenom, sdk.NewInt(40_000_000))))
require.NoError(t, err)

// New delegations
_, err = app.AllianceKeeper.Delegate(ctx, user2, val1, sdk.NewCoin(AllianceDenom, sdk.NewInt(1000_000)))
require.NoError(t, err)
assets := app.AllianceKeeper.GetAllAssets(ctx)
err = app.AllianceKeeper.RebalanceBondTokenWeights(ctx, assets)
require.NoError(t, err)

// Transfer to rewards to fee pool to be distributed
err = app.BankKeeper.SendCoinsFromModuleToModule(ctx, minttypes.ModuleName, authtypes.FeeCollectorName, sdk.NewCoins(sdk.NewCoin(bondDenom, sdk.NewInt(10_000_000))))
require.NoError(t, err)

ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// Distribute in the next begin block
// At the next begin block, tokens will be distributed from the fee pool
cons1, _ := val1.GetConsAddr()
var votingPower int64 = 100
app.DistrKeeper.AllocateTokens(ctx, votingPower, []abcitypes.VoteInfo{
{
Validator: abcitypes.Validator{
Address: cons1,
Power: 100,
},
SignedLastBlock: true,
},
})
require.NoError(t, err)

// New delegations
_, err = app.AllianceKeeper.Delegate(ctx, user1, val1, sdk.NewCoin(AllianceDenom, sdk.NewInt(1000_000)))
require.NoError(t, err)

assets = app.AllianceKeeper.GetAllAssets(ctx)
err = app.AllianceKeeper.RebalanceBondTokenWeights(ctx, assets)
require.NoError(t, err)

rewards1, err := app.AllianceKeeper.ClaimDelegationRewards(ctx, user1, val1, AllianceDenom)
require.NoError(t, err)
require.Equal(t, sdk.NewInt(0), rewards1.AmountOf(bondDenom))
}

0 comments on commit d576fa8

Please sign in to comment.