forked from EscanBE/evermint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
staking-rewards.go
127 lines (110 loc) · 5.21 KB
/
staking-rewards.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package testutil
import (
"fmt"
"github.com/servprotocolorg/serv/v12/constants"
"testing"
sdkmath "cosmossdk.io/math"
"github.com/servprotocolorg/serv/v12/app"
testutiltx "github.com/servprotocolorg/serv/v12/testutil/tx"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
sdk "github.com/cosmos/cosmos-sdk/types"
distributionkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper"
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
"github.com/cosmos/cosmos-sdk/x/staking"
teststaking "github.com/cosmos/cosmos-sdk/x/staking/testutil"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
// PrepareAccountsForDelegationRewards prepares the test suite for testing to withdraw delegation rewards.
//
// Balance is the amount of tokens that will be left in the account after the setup is done.
// For each defined reward, a validator is created and tokens are allocated to it using the distribution keeper,
// such that the given amount of tokens is outstanding as a staking reward for the account.
//
// The setup is done in the following way:
// - Fund the account with the given address with the given balance.
// - If the given balance is zero, the account will be created with zero balance.
//
// For every reward defined in the rewards argument, the following steps are executed:
// - Set up a validator with zero commission and delegate to it -> the account delegation will be 50% of the total delegation.
// - Allocate rewards to the validator.
//
// The function returns the updated context along with a potential error.
func PrepareAccountsForDelegationRewards(t *testing.T, ctx sdk.Context, app *app.Serv, addr sdk.AccAddress, balance sdkmath.Int, rewards ...sdkmath.Int) (sdk.Context, error) {
// Calculate the necessary amount of tokens to fund the account in order for the desired residual balance to
// be left after creating validators and delegating to them.
totalRewards := sdk.ZeroInt()
for _, reward := range rewards {
totalRewards = totalRewards.Add(reward)
}
totalNeededBalance := balance.Add(totalRewards)
if totalNeededBalance.IsZero() {
app.AccountKeeper.SetAccount(ctx, app.AccountKeeper.NewAccountWithAddress(ctx, addr))
} else {
// Fund account with enough tokens to stake them
err := FundAccountWithBaseDenom(ctx, app.BankKeeper, addr, totalNeededBalance.Int64())
if err != nil {
return sdk.Context{}, fmt.Errorf("failed to fund account: %s", err.Error())
}
}
if totalRewards.IsZero() {
return ctx, nil
}
// reset historical count in distribution keeper which is necessary
// for the delegation rewards to be calculated correctly
app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx)
// set distribution module account balance which pays out the rewards
distrAcc := app.DistrKeeper.GetDistributionAccount(ctx)
err := FundModuleAccount(ctx, app.BankKeeper, distrAcc.GetName(), sdk.NewCoins(sdk.NewCoin(constants.BaseDenom, totalRewards)))
if err != nil {
return sdk.Context{}, fmt.Errorf("failed to fund distribution module account: %s", err.Error())
}
app.AccountKeeper.SetModuleAccount(ctx, distrAcc)
for _, reward := range rewards {
if reward.IsZero() {
continue
}
// Set up validator and delegate to it
privKey := ed25519.GenPrivKey()
addr2, _ := testutiltx.NewAccAddressAndKey()
err := FundAccountWithBaseDenom(ctx, app.BankKeeper, addr2, reward.Int64())
if err != nil {
return sdk.Context{}, fmt.Errorf("failed to fund validator account: %s", err.Error())
}
zeroDec := sdk.ZeroDec()
stakingParams := app.StakingKeeper.GetParams(ctx)
stakingParams.BondDenom = constants.BaseDenom
stakingParams.MinCommissionRate = zeroDec
app.StakingKeeper.SetParams(ctx, stakingParams)
stakingHelper := teststaking.NewHelper(t, ctx, app.StakingKeeper)
stakingHelper.Commission = stakingtypes.NewCommissionRates(zeroDec, zeroDec, zeroDec)
stakingHelper.Denom = constants.BaseDenom
valAddr := sdk.ValAddress(addr2.Bytes())
// self-delegate the same amount of tokens as the delegate address also stakes
// this ensures, that the delegation rewards are 50% of the total rewards
stakingHelper.CreateValidator(valAddr, privKey.PubKey(), reward, true)
stakingHelper.Delegate(addr, valAddr, reward)
// end block to bond validator and increase block height
// Not using Commit() here because code panics due to invalid block height
staking.EndBlocker(ctx, app.StakingKeeper)
// allocate rewards to validator (of these 50% will be paid out to the delegator)
validator := app.StakingKeeper.Validator(ctx, valAddr)
allocatedRewards := sdk.NewDecCoins(sdk.NewDecCoin(constants.BaseDenom, reward.Mul(sdk.NewInt(2))))
app.DistrKeeper.AllocateTokensToValidator(ctx, validator, allocatedRewards)
}
return ctx, nil
}
// GetTotalDelegationRewards returns the total delegation rewards that are currently
// outstanding for the given address.
func GetTotalDelegationRewards(ctx sdk.Context, distributionKeeper distributionkeeper.Keeper, addr sdk.AccAddress) (sdk.DecCoins, error) {
querier := distributionkeeper.NewQuerier(distributionKeeper)
resp, err := querier.DelegationTotalRewards(
ctx,
&distributiontypes.QueryDelegationTotalRewardsRequest{
DelegatorAddress: addr.String(),
},
)
if err != nil {
return nil, err
}
return resp.Total, nil
}