-
Notifications
You must be signed in to change notification settings - Fork 44
/
module.go
145 lines (125 loc) · 5.56 KB
/
module.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package distribution
import (
"time"
"cosmossdk.io/math"
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/distribution/exported"
"github.com/cosmos/cosmos-sdk/x/distribution/keeper"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
assetprofilekeeper "github.com/elys-network/elys/x/assetprofile/keeper"
estakingkeeper "github.com/elys-network/elys/x/estaking/keeper"
ptypes "github.com/elys-network/elys/x/parameter/types"
)
var (
_ module.AppModule = AppModule{}
_ module.AppModuleBasic = AppModuleBasic{}
_ module.AppModuleSimulation = AppModule{}
)
// AppModule embeds the Cosmos SDK's x/distribution AppModuleBasic.
type AppModuleBasic struct {
distr.AppModuleBasic
}
// AppModule embeds the Cosmos SDK's x/distribution AppModule
type AppModule struct {
// embed the Cosmos SDK's x/distribution AppModule
distr.AppModule
keeper keeper.Keeper
accountKeeper distrtypes.AccountKeeper
bankKeeper distrtypes.BankKeeper
estakingKeeper *estakingkeeper.Keeper
assetprofileKeeper *assetprofilekeeper.Keeper
feeCollectorName string
}
// NewAppModule creates a new AppModule object using the native x/distribution module
// AppModule constructor.
func NewAppModule(
cdc codec.Codec, keeper keeper.Keeper, ak distrtypes.AccountKeeper,
bk distrtypes.BankKeeper,
sk *estakingkeeper.Keeper,
assetprofileKeeper *assetprofilekeeper.Keeper,
feeCollectorName string, subspace exported.Subspace,
) AppModule {
distrAppMod := distr.NewAppModule(cdc, keeper, ak, bk, sk, subspace)
return AppModule{
AppModule: distrAppMod,
keeper: keeper,
accountKeeper: ak,
bankKeeper: bk,
estakingKeeper: sk,
assetprofileKeeper: assetprofileKeeper,
feeCollectorName: feeCollectorName,
}
}
// BeginBlocker mirror functionality of cosmos-sdk/distribution BeginBlocker
// however it allocates no proposer reward
func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
defer telemetry.ModuleMeasureSince(distrtypes.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker)
if ctx.BlockHeight() > 1 {
am.AllocateTokens(ctx)
}
}
// RegisterInvariants registers the distribution module invariants.
func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {
}
func FilterDenoms(coins sdk.Coins, denoms ...string) sdk.Coins {
filtered := sdk.Coins{}
for _, denom := range denoms {
filtered = filtered.Add(sdk.NewCoin(denom, coins.AmountOf(denom)))
}
return filtered
}
// AllocateTokens handles distribution of the collected fees
func (am AppModule) AllocateTokens(ctx sdk.Context) {
// fetch and clear the collected fees for distribution, since this is
// called in BeginBlock, collected fees will be from the previous block
// (and distributed to the current representatives)
feeCollector := am.accountKeeper.GetModuleAccount(ctx, am.feeCollectorName)
feesCollectedInt := am.bankKeeper.GetAllBalances(ctx, feeCollector.GetAddress())
usdcDenom, _ := am.assetprofileKeeper.GetUsdcDenom(ctx)
filteredCoins := FilterDenoms(feesCollectedInt, usdcDenom, ptypes.Eden, ptypes.EdenB)
feesCollected := sdk.NewDecCoinsFromCoins(filteredCoins...)
// transfer collected fees to the distribution module account
if filteredCoins.IsAllPositive() {
err := am.bankKeeper.SendCoinsFromModuleToModule(ctx, am.feeCollectorName, distrtypes.ModuleName, filteredCoins)
if err != nil {
panic(err)
}
}
// calculate the fraction allocated to representatives by subtracting the community tax.
// e.g. if community tax is 0.02, representatives fraction will be 0.98 (2% goes to the community pool and the rest to the representatives)
remaining := feesCollected
communityTax := am.keeper.GetCommunityTax(ctx)
representativesFraction := sdk.OneDec().Sub(communityTax)
// Note: to prevent negative coin amount issue when invariant's broken,
// calculation of total bonded tokens manually through iteration
sumOfValTokens := math.ZeroInt()
am.estakingKeeper.IterateBondedValidatorsByPower(ctx, func(_ int64, validator stakingtypes.ValidatorI) bool {
sumOfValTokens = sumOfValTokens.Add(validator.GetTokens())
return false
})
totalBondedTokens := am.estakingKeeper.TotalBondedTokens(ctx)
if !totalBondedTokens.Equal(sumOfValTokens) {
ctx.Logger().Error("invariant broken", "sumOfValTokens", sumOfValTokens.String(), "totalBondedTokens", totalBondedTokens.String())
}
sumOfValTokensDec := sdk.NewDecFromInt(sumOfValTokens)
// allocate tokens proportionally to representatives voting power
am.estakingKeeper.IterateBondedValidatorsByPower(ctx, func(_ int64, validator stakingtypes.ValidatorI) bool {
// we get this validator's percentage of the total power by dividing their tokens by the total bonded tokens
powerFraction := sdk.NewDecFromInt(validator.GetTokens()).QuoTruncate(sumOfValTokensDec)
// we truncate here again, which means that the reward will be slightly lower than it should be
reward := feesCollected.MulDecTruncate(representativesFraction).MulDecTruncate(powerFraction)
am.keeper.AllocateTokensToValidator(ctx, validator, reward)
remaining = remaining.Sub(reward)
return false
})
// temporary workaround to keep CanWithdrawInvariant happy
feePool := am.keeper.GetFeePool(ctx)
feePool.CommunityPool = feePool.CommunityPool.Add(remaining...)
am.keeper.SetFeePool(ctx, feePool)
}