diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index 6aa0c4ee57..ab5b7c0a25 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -74,10 +74,11 @@ func (k Keeper) WithdrawDelegationReward(ctx sdk.Context, delegatorAddr sdk.AccA validator := k.stakeKeeper.Validator(ctx, validatorAddr) delegation := k.stakeKeeper.Delegation(ctx, delegatorAddr, validatorAddr) - delInfo, feePool, withdraw := delInfo.WithdrawRewards(feePool, valInfo, height, bondedTokens, + delInfo, valInfo, feePool, withdraw := delInfo.WithdrawRewards(feePool, valInfo, height, bondedTokens, validator.GetTokens(), validator.GetDelegatorShares(), delegation.GetShares(), validator.GetCommission()) k.SetFeePool(ctx, feePool) + k.SetValidatorDistInfo(ctx, valInfo) withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, delegatorAddr) _, _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, withdraw.TruncateDecimal()) if err != nil { @@ -113,10 +114,11 @@ func (k Keeper) GetDelegatorRewardsAll(ctx sdk.Context, delAddr sdk.AccAddress, validator := k.stakeKeeper.Validator(ctx, valAddr) delegation := k.stakeKeeper.Delegation(ctx, delAddr, valAddr) - delInfo, feePool, diWithdraw := delInfo.WithdrawRewards(feePool, valInfo, height, bondedTokens, + delInfo, valInfo, feePool, diWithdraw := delInfo.WithdrawRewards(feePool, valInfo, height, bondedTokens, validator.GetTokens(), validator.GetDelegatorShares(), delegation.GetShares(), validator.GetCommission()) withdraw = withdraw.Plus(diWithdraw) k.SetFeePool(ctx, feePool) + k.SetValidatorDistInfo(ctx, valInfo) k.SetDelegatorDistInfo(ctx, delInfo) return false } diff --git a/x/distribution/types/delegator_info.go b/x/distribution/types/delegator_info.go index e083cf2d4c..911fad9c98 100644 --- a/x/distribution/types/delegator_info.go +++ b/x/distribution/types/delegator_info.go @@ -1,6 +1,8 @@ package types -import sdk "github.com/cosmos/cosmos-sdk/types" +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) // distribution info for a delegation type DelegatorDistInfo struct { @@ -22,13 +24,14 @@ func NewDelegatorDistInfo(delegatorAddr sdk.AccAddress, valOperatorAddr sdk.ValA // withdraw rewards from delegator func (di DelegatorDistInfo) WithdrawRewards(fp FeePool, vi ValidatorDistInfo, height int64, totalBonded, vdTokens, totalDelShares, delegatorShares, - commissionRate sdk.Dec) (DelegatorDistInfo, FeePool, DecCoins) { + commissionRate sdk.Dec) (DelegatorDistInfo, ValidatorDistInfo, FeePool, DecCoins) { + + vi = vi.UpdateTotalDelAccum(height, totalDelShares) if vi.DelAccum.Accum.IsZero() { - return di, fp, DecCoins{} + return di, vi, fp, DecCoins{} } - vi.UpdateTotalDelAccum(height, totalDelShares) vi, fp = vi.TakeFeePoolRewards(fp, height, totalBonded, vdTokens, commissionRate) blocks := height - di.WithdrawalHeight @@ -40,7 +43,7 @@ func (di DelegatorDistInfo) WithdrawRewards(fp FeePool, vi ValidatorDistInfo, vi.Pool = remainingTokens vi.DelAccum.Accum = vi.DelAccum.Accum.Sub(accum) - return di, fp, withdrawalTokens + return di, vi, fp, withdrawalTokens } //_____________________________________________________________________ diff --git a/x/distribution/types/delegator_info_test.go b/x/distribution/types/delegator_info_test.go index af89f40ca0..f1bcbb4ec5 100644 --- a/x/distribution/types/delegator_info_test.go +++ b/x/distribution/types/delegator_info_test.go @@ -1,6 +1,56 @@ package types -import "testing" +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" +) func TestWithdrawRewards(t *testing.T) { + + // initialize + height := int64(0) + fp := InitialFeePool() + vi := NewValidatorDistInfo(valAddr1, height) + commissionRate := sdk.NewDecWithPrec(2, 2) + validatorTokens := sdk.NewDec(10) + validatorDelShares := sdk.NewDec(10) + totalBondedTokens := validatorTokens.Add(sdk.NewDec(90)) // validator-1 is 10% of total power + + di1 := NewDelegatorDistInfo(delAddr1, valAddr1, height) + di1Shares := sdk.NewDec(5) // this delegator has half the shares in the validator + + di2 := NewDelegatorDistInfo(delAddr2, valAddr1, height) + di2Shares := sdk.NewDec(5) + + // simulate adding some stake for inflation + height = 10 + fp.Pool = DecCoins{DecCoin{"stake", sdk.NewDec(1000)}} + + // withdraw rewards + di1, vi, fp, rewardRecv1 := di1.WithdrawRewards(fp, vi, height, totalBondedTokens, + validatorTokens, validatorDelShares, di1Shares, commissionRate) + + assert.Equal(t, height, di1.WithdrawalHeight) + assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.ValAccum.Accum)) + assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.Pool[0].Amount)) + assert.True(sdk.DecEq(t, sdk.NewDec(49), vi.Pool[0].Amount)) + assert.True(sdk.DecEq(t, sdk.NewDec(2), vi.PoolCommission[0].Amount)) + assert.True(sdk.DecEq(t, sdk.NewDec(49), rewardRecv1[0].Amount)) + + // add more blocks and inflation + height = 20 + fp.Pool[0].Amount = fp.Pool[0].Amount.Add(sdk.NewDec(1000)) + + // withdraw rewards + di2, vi, fp, rewardRecv2 := di2.WithdrawRewards(fp, vi, height, totalBondedTokens, + validatorTokens, validatorDelShares, di2Shares, commissionRate) + + assert.Equal(t, height, di2.WithdrawalHeight) + assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.ValAccum.Accum)) + assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.Pool[0].Amount)) + assert.True(sdk.DecEq(t, sdk.NewDec(49), vi.Pool[0].Amount)) + assert.True(sdk.DecEq(t, sdk.NewDec(4), vi.PoolCommission[0].Amount)) + assert.True(sdk.DecEq(t, sdk.NewDec(147), rewardRecv2[0].Amount)) } diff --git a/x/distribution/types/validator_info_test.go b/x/distribution/types/validator_info_test.go index 44c5e5daa9..da3b2c10d8 100644 --- a/x/distribution/types/validator_info_test.go +++ b/x/distribution/types/validator_info_test.go @@ -56,30 +56,30 @@ func TestWithdrawCommission(t *testing.T) { // initialize height := int64(0) fp := InitialFeePool() - vi1 := NewValidatorDistInfo(valAddr1, height) - commissionRate1 := sdk.NewDecWithPrec(2, 2) - validatorTokens1 := sdk.NewDec(10) - totalBondedTokens := validatorTokens1.Add(sdk.NewDec(90)) // validator-1 is 10% of total power + vi := NewValidatorDistInfo(valAddr1, height) + commissionRate := sdk.NewDecWithPrec(2, 2) + validatorTokens := sdk.NewDec(10) + totalBondedTokens := validatorTokens.Add(sdk.NewDec(90)) // validator-1 is 10% of total power // simulate adding some stake for inflation height = 10 fp.Pool = DecCoins{DecCoin{"stake", sdk.NewDec(1000)}} // for a more fun staring condition, have an non-withdraw update - vi1, fp = vi1.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens1, commissionRate1) + vi, fp = vi.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens, commissionRate) require.True(sdk.DecEq(t, sdk.NewDec(900), fp.ValAccum.Accum)) assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.Pool[0].Amount)) - assert.True(sdk.DecEq(t, sdk.NewDec(100-2), vi1.Pool[0].Amount)) - assert.True(sdk.DecEq(t, sdk.NewDec(2), vi1.PoolCommission[0].Amount)) + assert.True(sdk.DecEq(t, sdk.NewDec(100-2), vi.Pool[0].Amount)) + assert.True(sdk.DecEq(t, sdk.NewDec(2), vi.PoolCommission[0].Amount)) // add more blocks and inflation height = 20 fp.Pool[0].Amount = fp.Pool[0].Amount.Add(sdk.NewDec(1000)) - vi1, fp, commissionRecv := vi1.WithdrawCommission(fp, height, totalBondedTokens, validatorTokens1, commissionRate1) + vi, fp, commissionRecv := vi.WithdrawCommission(fp, height, totalBondedTokens, validatorTokens, commissionRate) require.True(sdk.DecEq(t, sdk.NewDec(1800), fp.ValAccum.Accum)) assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.Pool[0].Amount)) - assert.True(sdk.DecEq(t, sdk.NewDec(200-4), vi1.Pool[0].Amount)) - assert.Zero(t, len(vi1.PoolCommission)) + assert.True(sdk.DecEq(t, sdk.NewDec(200-4), vi.Pool[0].Amount)) + assert.Zero(t, len(vi.PoolCommission)) assert.True(sdk.DecEq(t, sdk.NewDec(4), commissionRecv[0].Amount)) }