Skip to content

Commit

Permalink
Keep the total supply constant. (#258)
Browse files Browse the repository at this point in the history
Mint validator slashing burned tokens and move to community pool.
  • Loading branch information
Xiong-stratos authored May 1, 2023
1 parent 4d648f7 commit 6207de4
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 76 deletions.
3 changes: 2 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ var (
registertypes.MetaNodeNotBondedPool: {authtypes.Minter},
registertypes.TotalUnissuedPrepay: {authtypes.Minter},

pottypes.ModuleName: {authtypes.Minter},
pottypes.FoundationAccount: {authtypes.Minter, authtypes.Burner},
pottypes.TotalRewardPool: nil,
//pottypes.TotalMinedTokens: {authtypes.Minter, authtypes.Burner},
Expand Down Expand Up @@ -509,7 +510,6 @@ func NewInitApp(
govtypes.ModuleName,
stakingtypes.ModuleName,
registertypes.ModuleName,
pottypes.ModuleName,
sdstypes.ModuleName,
evmtypes.ModuleName,
// no-op modules
Expand All @@ -525,6 +525,7 @@ func NewInitApp(
evidencetypes.ModuleName,
authz.ModuleName,
feegrant.ModuleName,
pottypes.ModuleName,
paramstypes.ModuleName,
upgradetypes.ModuleName,
vestingtypes.ModuleName,
Expand Down
5 changes: 5 additions & 0 deletions proto/stratos/pot/v1/pot.proto
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ message Params {
(gogoproto.moretags) = "yaml:\"community_tax\"",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec"
];
cosmos.base.v1beta1.Coin initial_total_supply = 6 [
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "initial_total_supply",
(gogoproto.moretags) = "yaml:\"initial_total_supply\""
];
}

message MiningRewardParam {
Expand Down
11 changes: 11 additions & 0 deletions x/pot/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
abci "github.com/tendermint/tendermint/abci/types"

sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"

"github.com/stratosnet/stratos-chain/x/pot/keeper"
"github.com/stratosnet/stratos-chain/x/pot/types"
Expand Down Expand Up @@ -53,5 +54,15 @@ func EndBlocker(ctx sdk.Context, req abci.RequestEndBlock, k keeper.Keeper) []ab
logger.Error("An error occurred while distributing the reward. ", "ErrMsg", err.Error())
}

// reset total supply to 100M stos
minter, amount := k.RestoreTotalSupply(ctx)
if minter.Empty() || amount.Empty() {
return []abci.ValidatorUpdate{}
}

ctx.EventManager().EmitEvent(
banktypes.NewCoinMintEvent(minter, amount),
)

return []abci.ValidatorUpdate{}
}
59 changes: 59 additions & 0 deletions x/pot/keeper/keeper.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package keeper

import (
"bytes"
"fmt"
"math"

"github.com/tendermint/tendermint/libs/log"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"

stratos "github.com/stratosnet/stratos-chain/types"
Expand Down Expand Up @@ -87,3 +89,60 @@ func (k Keeper) FoundationDeposit(ctx sdk.Context, amount sdk.Coins, from sdk.Ac
}
return nil
}

// Restore total supply to 100M stos
func (k Keeper) RestoreTotalSupply(ctx sdk.Context) (minter sdk.AccAddress, mintCoins sdk.Coins) {
// reset total supply to 100M stos
events := ctx.EventManager().Events()
attrKeyAmtBytes := []byte(sdk.AttributeKeyAmount)

totalBurnedCoins := sdk.Coins{}
for _, event := range events {
if event.Type == banktypes.EventTypeCoinBurn {
attributes := event.Attributes
for _, attr := range attributes {
if bytes.Equal(attr.Key, attrKeyAmtBytes) {
amount, err := sdk.ParseCoinsNormalized(string(attr.Value))
if err != nil {
ctx.Logger().Error("An error occurred while parsing burned amount. ", "ErrMsg", err.Error())
break
}
totalBurnedCoins = totalBurnedCoins.Add(amount...)
}
}
}
}

totalBurned := totalBurnedCoins.AmountOf(k.BondDenom(ctx))
if totalBurned.IsZero() {
return sdk.AccAddress{}, sdk.Coins{}
}

InitialTotalSupply := k.InitialTotalSupply(ctx).Amount
currentTotalSupply := k.bankKeeper.GetSupply(ctx, k.BondDenom(ctx)).Amount

if totalBurned.Add(currentTotalSupply).GT(InitialTotalSupply) {
mintCoins = sdk.NewCoins(
sdk.NewCoin(k.BondDenom(ctx), InitialTotalSupply.Sub(currentTotalSupply)),
)
} else {
mintCoins = totalBurnedCoins
}

// mint coins
err := k.bankKeeper.MintCoins(ctx, types.ModuleName, mintCoins)
if err != nil {
ctx.Logger().Error("Restore total supply failed:", err.Error())
return sdk.AccAddress{}, sdk.Coins{}
}

// send new mint coins to community pool
senderAddr := k.accountKeeper.GetModuleAddress(types.ModuleName)
err = k.distrKeeper.FundCommunityPool(ctx, mintCoins, senderAddr)
if err != nil {
ctx.Logger().Error("Restore total supply failed:", err.Error())
return sdk.AccAddress{}, sdk.Coins{}
}

return
}
6 changes: 6 additions & 0 deletions x/pot/keeper/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/stratosnet/stratos-chain/x/pot/types"
)

Expand Down Expand Up @@ -51,3 +52,8 @@ func (k Keeper) GetCommunityTax(ctx sdk.Context) (res sdk.Dec) {
k.paramSpace.Get(ctx, types.KeyCommunityTax, &res)
return
}

func (k Keeper) InitialTotalSupply(ctx sdk.Context) (res sdk.Coin) {
k.paramSpace.Get(ctx, types.KeyInitialTotalSupply, &res)
return
}
6 changes: 6 additions & 0 deletions x/pot/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ const (
codeErrMissingTargetAddress
codeErrInsufficientMatureTotal
codeErrMatureEpoch
codeErrMiningRewardParams
codeErrCommunityTax
codeErrInitialTotalSupply
codeErrEmptyFromAddr
codeErrEmptyReporterAddr
codeErrEmptyWalletVolumes
Expand Down Expand Up @@ -50,6 +53,9 @@ var (
ErrMissingTargetAddress = sdkerrors.Register(ModuleName, codeErrMissingTargetAddress, "missing target address")
ErrInsufficientMatureTotal = sdkerrors.Register(ModuleName, codeErrInsufficientMatureTotal, "insufficient mature total")
ErrMatureEpoch = sdkerrors.Register(ModuleName, codeErrMatureEpoch, "the value of epoch must be positive and greater than its previous one")
ErrMiningRewardParams = sdkerrors.Register(ModuleName, codeErrMiningRewardParams, "invalid mining reward param")
ErrCommunityTax = sdkerrors.Register(ModuleName, codeErrCommunityTax, "invalid community tax param")
ErrInitialTotalSupply = sdkerrors.Register(ModuleName, codeErrInitialTotalSupply, "invalid initial total supply param")
ErrEmptyFromAddr = sdkerrors.Register(ModuleName, codeErrEmptyFromAddr, "missing from address")
ErrEmptyReporterAddr = sdkerrors.Register(ModuleName, codeErrEmptyReporterAddr, "missing reporter address")
ErrEmptyWalletVolumes = sdkerrors.Register(ModuleName, codeErrEmptyWalletVolumes, "wallet volumes list empty")
Expand Down
2 changes: 2 additions & 0 deletions x/pot/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ type BankKeeper interface {
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
SendCoinsFromModuleToModule(ctx sdk.Context, senderPool, recipientPool string, amt sdk.Coins) error
GetSupply(ctx sdk.Context, denom string) sdk.Coin
MintCoins(ctx sdk.Context, moduleName string, amounts sdk.Coins) error
}

type RegisterKeeper interface {
Expand Down
54 changes: 44 additions & 10 deletions x/pot/types/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@ var (
KeyMatureEpoch = []byte("MatureEpoch")
KeyMiningRewardParams = []byte("MiningRewardParams")
KeyCommunityTax = []byte("CommunityTax")
KeyInitialTotalSupply = []byte("InitialTotalSupply")

DefaultCommunityTax = sdk.NewDecWithPrec(2, 2) // 2%
DefaultCommunityTax = sdk.NewDecWithPrec(2, 2) // 2%
DefaultInitialTotalSupply = sdk.NewCoin(DefaultBondDenom,
sdk.NewInt(1e8).Mul(sdk.NewInt(stratos.StosToWei)),
) //100,000,000 stos
)

// ParamKeyTable for pot module
Expand All @@ -36,13 +40,16 @@ func ParamKeyTable() paramtypes.KeyTable {
}

// NewParams creates a new Params object
func NewParams(bondDenom string, rewardDenom string, matureEpoch int64, miningRewardParams []MiningRewardParam, communityTax sdk.Dec) Params {
func NewParams(bondDenom string, rewardDenom string, matureEpoch int64, miningRewardParams []MiningRewardParam,
communityTax sdk.Dec, initialTotalSupply sdk.Coin) Params {

return Params{
BondDenom: bondDenom,
RewardDenom: rewardDenom,
MatureEpoch: matureEpoch,
MiningRewardParams: miningRewardParams,
CommunityTax: communityTax,
InitialTotalSupply: initialTotalSupply,
}
}

Expand Down Expand Up @@ -85,17 +92,26 @@ func DefaultParams() Params {
sdk.NewCoin(DefaultRewardDenom, sdk.NewInt(2500000000)),
sdk.NewInt(7000), sdk.NewInt(1000), sdk.NewInt(2000)))

return NewParams(DefaultBondDenom, DefaultRewardDenom, DefaultMatureEpoch, miningRewardParams, DefaultCommunityTax)
return NewParams(
DefaultBondDenom,
DefaultRewardDenom,
DefaultMatureEpoch,
miningRewardParams,
DefaultCommunityTax,
DefaultInitialTotalSupply,
)
}

// HrpString implements the stringer interface for Params
func (p Params) HrpString() string {
return fmt.Sprintf(`Params:
BondDenom: %s
RewardDenom: %s
MatureEpoch: %d
MiningRewardParams: %s`,
p.BondDenom, p.RewardDenom, p.MatureEpoch, p.MiningRewardParams)
BondDenom: %s
RewardDenom: %s
MatureEpoch: %d
MiningRewardParams: %s
CommunitiyTax: %v
InitialTotalSupply: %v`,
p.BondDenom, p.RewardDenom, p.MatureEpoch, p.MiningRewardParams, p.CommunityTax, p.InitialTotalSupply)
}

// ParamSetPairs - Implements params.ParamSet
Expand All @@ -106,6 +122,7 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
paramtypes.NewParamSetPair(KeyMatureEpoch, &p.MatureEpoch, validateMatureEpoch),
paramtypes.NewParamSetPair(KeyMiningRewardParams, &p.MiningRewardParams, validateMiningRewardParams),
paramtypes.NewParamSetPair(KeyCommunityTax, &p.CommunityTax, validateCommunityTax),
paramtypes.NewParamSetPair(KeyInitialTotalSupply, &p.InitialTotalSupply, validateInitialTotalSupply),
}
}

Expand Down Expand Up @@ -188,6 +205,20 @@ func validateCommunityTax(i interface{}) error {
return nil
}

func validateInitialTotalSupply(i interface{}) error {
v, ok := i.(sdk.Coin)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
if v.IsNil() {
return fmt.Errorf("total supply must be not nil")
}
if v.IsNegative() {
return fmt.Errorf("total supply must be positive: %s", v)
}
return nil
}

func (p Params) ValidateBasic() error {
if err := validateBondDenom(p.BondDenom); err != nil {
return sdkerrors.Wrap(ErrInvalidDenom, "failed to validate bond denomination")
Expand All @@ -199,10 +230,13 @@ func (p Params) ValidateBasic() error {
return sdkerrors.Wrap(ErrMatureEpoch, "failed to validate mature epoch")
}
if err := validateMiningRewardParams(p.MiningRewardParams); err != nil {
return sdkerrors.Wrap(ErrMatureEpoch, "failed to validate mining reward params")
return sdkerrors.Wrap(ErrMiningRewardParams, "failed to validate mining reward params")
}
if err := validateCommunityTax(p.CommunityTax); err != nil {
return sdkerrors.Wrap(ErrMatureEpoch, "failed to validate community tax")
return sdkerrors.Wrap(ErrCommunityTax, "failed to validate community tax")
}
if err := validateInitialTotalSupply(p.InitialTotalSupply); err != nil {
return sdkerrors.Wrap(ErrInitialTotalSupply, err.Error())
}
return nil
}
Loading

0 comments on commit 6207de4

Please sign in to comment.