diff --git a/api/cosmos/mint/v1beta1/mint.pulsar.go b/api/cosmos/mint/v1beta1/mint.pulsar.go index 8007c79c9630..2ad70c1ed755 100644 --- a/api/cosmos/mint/v1beta1/mint.pulsar.go +++ b/api/cosmos/mint/v1beta1/mint.pulsar.go @@ -507,6 +507,7 @@ var ( fd_Params_inflation_min protoreflect.FieldDescriptor fd_Params_goal_bonded protoreflect.FieldDescriptor fd_Params_blocks_per_year protoreflect.FieldDescriptor + fd_Params_max_supply protoreflect.FieldDescriptor ) func init() { @@ -518,6 +519,7 @@ func init() { fd_Params_inflation_min = md_Params.Fields().ByName("inflation_min") fd_Params_goal_bonded = md_Params.Fields().ByName("goal_bonded") fd_Params_blocks_per_year = md_Params.Fields().ByName("blocks_per_year") + fd_Params_max_supply = md_Params.Fields().ByName("max_supply") } var _ protoreflect.Message = (*fastReflection_Params)(nil) @@ -621,6 +623,12 @@ func (x *fastReflection_Params) Range(f func(protoreflect.FieldDescriptor, proto return } } + if x.MaxSupply != "" { + value := protoreflect.ValueOfString(x.MaxSupply) + if !f(fd_Params_max_supply, value) { + return + } + } } // Has reports whether a field is populated. @@ -648,6 +656,8 @@ func (x *fastReflection_Params) Has(fd protoreflect.FieldDescriptor) bool { return x.GoalBonded != "" case "cosmos.mint.v1beta1.Params.blocks_per_year": return x.BlocksPerYear != uint64(0) + case "cosmos.mint.v1beta1.Params.max_supply": + return x.MaxSupply != "" default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.mint.v1beta1.Params")) @@ -676,6 +686,8 @@ func (x *fastReflection_Params) Clear(fd protoreflect.FieldDescriptor) { x.GoalBonded = "" case "cosmos.mint.v1beta1.Params.blocks_per_year": x.BlocksPerYear = uint64(0) + case "cosmos.mint.v1beta1.Params.max_supply": + x.MaxSupply = "" default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.mint.v1beta1.Params")) @@ -710,6 +722,9 @@ func (x *fastReflection_Params) Get(descriptor protoreflect.FieldDescriptor) pro case "cosmos.mint.v1beta1.Params.blocks_per_year": value := x.BlocksPerYear return protoreflect.ValueOfUint64(value) + case "cosmos.mint.v1beta1.Params.max_supply": + value := x.MaxSupply + return protoreflect.ValueOfString(value) default: if descriptor.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.mint.v1beta1.Params")) @@ -742,6 +757,8 @@ func (x *fastReflection_Params) Set(fd protoreflect.FieldDescriptor, value proto x.GoalBonded = value.Interface().(string) case "cosmos.mint.v1beta1.Params.blocks_per_year": x.BlocksPerYear = value.Uint() + case "cosmos.mint.v1beta1.Params.max_supply": + x.MaxSupply = value.Interface().(string) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.mint.v1beta1.Params")) @@ -774,6 +791,8 @@ func (x *fastReflection_Params) Mutable(fd protoreflect.FieldDescriptor) protore panic(fmt.Errorf("field goal_bonded of message cosmos.mint.v1beta1.Params is not mutable")) case "cosmos.mint.v1beta1.Params.blocks_per_year": panic(fmt.Errorf("field blocks_per_year of message cosmos.mint.v1beta1.Params is not mutable")) + case "cosmos.mint.v1beta1.Params.max_supply": + panic(fmt.Errorf("field max_supply of message cosmos.mint.v1beta1.Params is not mutable")) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.mint.v1beta1.Params")) @@ -799,6 +818,8 @@ func (x *fastReflection_Params) NewField(fd protoreflect.FieldDescriptor) protor return protoreflect.ValueOfString("") case "cosmos.mint.v1beta1.Params.blocks_per_year": return protoreflect.ValueOfUint64(uint64(0)) + case "cosmos.mint.v1beta1.Params.max_supply": + return protoreflect.ValueOfString("") default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.mint.v1beta1.Params")) @@ -891,6 +912,10 @@ func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods { if x.BlocksPerYear != 0 { n += 1 + runtime.Sov(uint64(x.BlocksPerYear)) } + l = len(x.MaxSupply) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } if x.unknownFields != nil { n += len(x.unknownFields) } @@ -920,6 +945,13 @@ func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods { i -= len(x.unknownFields) copy(dAtA[i:], x.unknownFields) } + if len(x.MaxSupply) > 0 { + i -= len(x.MaxSupply) + copy(dAtA[i:], x.MaxSupply) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.MaxSupply))) + i-- + dAtA[i] = 0x3a + } if x.BlocksPerYear != 0 { i = runtime.EncodeVarint(dAtA, i, uint64(x.BlocksPerYear)) i-- @@ -1188,6 +1220,38 @@ func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods { break } } + case 7: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field MaxSupply", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.MaxSupply = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := runtime.Skip(dAtA[iNdEx:]) @@ -1300,6 +1364,8 @@ type Params struct { GoalBonded string `protobuf:"bytes,5,opt,name=goal_bonded,json=goalBonded,proto3" json:"goal_bonded,omitempty"` // expected blocks per year BlocksPerYear uint64 `protobuf:"varint,6,opt,name=blocks_per_year,json=blocksPerYear,proto3" json:"blocks_per_year,omitempty"` + // maximum supply for the token + MaxSupply string `protobuf:"bytes,7,opt,name=max_supply,json=maxSupply,proto3" json:"max_supply,omitempty"` } func (x *Params) Reset() { @@ -1364,6 +1430,13 @@ func (x *Params) GetBlocksPerYear() uint64 { return 0 } +func (x *Params) GetMaxSupply() string { + if x != nil { + return x.MaxSupply + } + return "" +} + var File_cosmos_mint_v1beta1_mint_proto protoreflect.FileDescriptor var file_cosmos_mint_v1beta1_mint_proto_rawDesc = []byte{ @@ -1386,7 +1459,7 @@ var file_cosmos_mint_v1beta1_mint_proto_rawDesc = []byte{ 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x44, 0x65, 0x63, 0xd2, 0xb4, 0x2d, 0x0a, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x44, 0x65, 0x63, 0x52, 0x10, 0x61, 0x6e, 0x6e, 0x75, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xed, 0x03, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xb9, 0x04, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x74, 0x44, 0x65, 0x6e, 0x6f, 0x6d, 0x12, 0x6a, 0x0a, 0x15, 0x69, 0x6e, 0x66, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x61, 0x74, @@ -1415,22 +1488,26 @@ var file_cosmos_mint_v1beta1_mint_proto_rawDesc = []byte{ 0x2a, 0x01, 0x52, 0x0a, 0x67, 0x6f, 0x61, 0x6c, 0x42, 0x6f, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x79, 0x65, 0x61, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x50, - 0x65, 0x72, 0x59, 0x65, 0x61, 0x72, 0x3a, 0x1d, 0x8a, 0xe7, 0xb0, 0x2a, 0x18, 0x63, 0x6f, 0x73, - 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x78, 0x2f, 0x6d, 0x69, 0x6e, 0x74, 0x2f, 0x50, - 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, 0xc4, 0x01, 0x0a, 0x17, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, - 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x42, 0x09, 0x4d, 0x69, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, - 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x69, 0x6e, 0x74, 0x2f, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x3b, 0x6d, 0x69, 0x6e, 0x74, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0xa2, 0x02, 0x03, 0x43, 0x4d, 0x58, 0xaa, 0x02, 0x13, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, - 0x4d, 0x69, 0x6e, 0x74, 0x2e, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x13, 0x43, - 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x4d, 0x69, 0x6e, 0x74, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0xe2, 0x02, 0x1f, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x4d, 0x69, 0x6e, 0x74, - 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x4d, - 0x69, 0x6e, 0x74, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x72, 0x59, 0x65, 0x61, 0x72, 0x12, 0x4a, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x75, + 0x70, 0x70, 0x6c, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2b, 0xc8, 0xde, 0x1f, 0x00, + 0xda, 0xde, 0x1f, 0x15, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, + 0x2f, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x49, 0x6e, 0x74, 0xd2, 0xb4, 0x2d, 0x0a, 0x63, 0x6f, 0x73, + 0x6d, 0x6f, 0x73, 0x2e, 0x49, 0x6e, 0x74, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x53, 0x75, 0x70, 0x70, + 0x6c, 0x79, 0x3a, 0x1d, 0x8a, 0xe7, 0xb0, 0x2a, 0x18, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, + 0x73, 0x64, 0x6b, 0x2f, 0x78, 0x2f, 0x6d, 0x69, 0x6e, 0x74, 0x2f, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x42, 0xc4, 0x01, 0x0a, 0x17, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, + 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x42, 0x09, 0x4d, + 0x69, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x63, 0x6f, 0x73, 0x6d, + 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x73, + 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x69, 0x6e, 0x74, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x3b, 0x6d, 0x69, 0x6e, 0x74, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x43, + 0x4d, 0x58, 0xaa, 0x02, 0x13, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x4d, 0x69, 0x6e, 0x74, + 0x2e, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x13, 0x43, 0x6f, 0x73, 0x6d, 0x6f, + 0x73, 0x5c, 0x4d, 0x69, 0x6e, 0x74, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xe2, 0x02, + 0x1f, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x4d, 0x69, 0x6e, 0x74, 0x5c, 0x56, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0xea, 0x02, 0x15, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x4d, 0x69, 0x6e, 0x74, 0x3a, + 0x3a, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/tests/e2e/mint/grpc.go b/tests/e2e/mint/grpc.go index 8e509d9e60b0..188d641d5d41 100644 --- a/tests/e2e/mint/grpc.go +++ b/tests/e2e/mint/grpc.go @@ -29,7 +29,7 @@ func (s *E2ETestSuite) TestQueryGRPC() { &minttypes.QueryParamsResponse{}, &minttypes.QueryParamsResponse{ Params: minttypes.NewParams("stake", math.LegacyNewDecWithPrec(13, 2), math.LegacyNewDecWithPrec(100, 2), - math.LegacyNewDec(1), math.LegacyNewDecWithPrec(67, 2), (60 * 60 * 8766 / 5)), + math.LegacyNewDec(1), math.LegacyNewDecWithPrec(67, 2), (60 * 60 * 8766 / 5), math.ZeroInt()), }, }, { diff --git a/x/mint/CHANGELOG.md b/x/mint/CHANGELOG.md index 4b16a9cb86b8..ad9b78b53ed1 100644 --- a/x/mint/CHANGELOG.md +++ b/x/mint/CHANGELOG.md @@ -27,6 +27,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* [19896](https://github.com/cosmos/cosmos-sdk/pull/19896) Added a new max supply genesis param to existing params. + ### Improvements ### API Breaking Changes diff --git a/x/mint/README.md b/x/mint/README.md index 80198010dcb0..ff2467d7542a 100644 --- a/x/mint/README.md +++ b/x/mint/README.md @@ -63,17 +63,21 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/mint/v1beta1/ The mint module stores its params in state with the prefix of `0x01`, it can be updated with governance or the address with authority. +**Note:** With the latest update, the addition of the `MaxSupply` parameter allows controlling the maximum supply of tokens minted by the module. +A value of `0` indicates an unlimited supply. * Params: `mint/params -> legacy_amino(params)` ```protobuf reference -https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/mint/v1beta1/mint.proto#L26-L59 +https://github.com/cosmos/cosmos-sdk/blob/7068d0da52d954430054768b2c56aff44666933b/x/mint/proto/cosmos/mint/v1beta1/mint.proto#L26-L68 ``` ## Begin-Block Minting parameters are recalculated and inflation paid at the beginning of each block. +The minting logic in the `BeginBlocker` function provides an optional feature for controlling token minting based on the maximum allowable supply (MaxSupply). This feature allows users to adjust the minting process according to their specific requirements and use cases. However, it's important to note that the MaxSupply parameter is independent of the minting process and assumes that any adjustments to the total supply, including burning tokens, are handled by external modules. + ### Inflation rate calculation Inflation rate is calculated using an "inflation calculation function" that's @@ -136,15 +140,17 @@ BlockProvision(params Params) sdk.Coin { ## Parameters The minting module contains the following parameters: +Note: `0` indicates unlimited supply for MaxSupply param -| Key | Type | Example | -|---------------------|-----------------|------------------------| -| MintDenom | string | "uatom" | -| InflationRateChange | string (dec) | "0.130000000000000000" | -| InflationMax | string (dec) | "0.200000000000000000" | -| InflationMin | string (dec) | "0.070000000000000000" | -| GoalBonded | string (dec) | "0.670000000000000000" | -| BlocksPerYear | string (uint64) | "6311520" | +| Key | Type | Example | +|---------------------|------------------|------------------------| +| MintDenom | string | "uatom" | +| InflationRateChange | string (dec) | "0.130000000000000000" | +| InflationMax | string (dec) | "0.200000000000000000" | +| InflationMin | string (dec) | "0.070000000000000000" | +| GoalBonded | string (dec) | "0.670000000000000000" | +| BlocksPerYear | string (uint64) | "6311520" | +| MaxSupply | string (math.Int)| "0" | ## Events @@ -232,6 +238,7 @@ inflation_max: "0.200000000000000000" inflation_min: "0.070000000000000000" inflation_rate_change: "0.130000000000000000" mint_denom: stake +max_supply: "0" ``` ### gRPC @@ -306,7 +313,8 @@ Example Output: "inflationMax": "200000000000000000", "inflationMin": "70000000000000000", "goalBonded": "670000000000000000", - "blocksPerYear": "6311520" + "blocksPerYear": "6311520", + "maxSupply": "0", } } ``` @@ -377,7 +385,8 @@ Example Output: "inflationMax": "200000000000000000", "inflationMin": "70000000000000000", "goalBonded": "670000000000000000", - "blocksPerYear": "6311520" + "blocksPerYear": "6311520", + "maxSupply": "0", } } ``` diff --git a/x/mint/keeper/abci.go b/x/mint/keeper/abci.go index 478393e26c34..9c8ec7261262 100644 --- a/x/mint/keeper/abci.go +++ b/x/mint/keeper/abci.go @@ -36,19 +36,43 @@ func (k Keeper) BeginBlocker(ctx context.Context, ic types.InflationCalculationF return err } + // update minter's inflation and annual provisions minter.Inflation = ic(ctx, minter, params, bondedRatio) minter.AnnualProvisions = minter.NextAnnualProvisions(params, totalStakingSupply) if err = k.Minter.Set(ctx, minter); err != nil { return err } - // mint coins, update supply + // calculate minted coins mintedCoin := minter.BlockProvision(params) mintedCoins := sdk.NewCoins(mintedCoin) - err = k.MintCoins(ctx, mintedCoins) - if err != nil { - return err + maxSupply := params.MaxSupply + totalSupply := k.bankKeeper.GetSupply(ctx, params.MintDenom).Amount // fetch total supply from the bank module + + // if maxSupply is not infinite, check against max_supply parameter + if !maxSupply.IsZero() { + if totalSupply.Add(mintedCoins.AmountOf(params.MintDenom)).GT(maxSupply) { + // calculate the difference between maxSupply and totalSupply + diff := maxSupply.Sub(totalSupply) + // mint the difference + diffCoin := sdk.NewCoin(params.MintDenom, diff) + diffCoins := sdk.NewCoins(diffCoin) + + // mint coins + if err := k.MintCoins(ctx, diffCoins); err != nil { + return err + } + mintedCoins = diffCoins + } + } + + // mint coins if maxSupply is infinite or total staking supply is less than maxSupply + if maxSupply.IsZero() || totalSupply.Add(mintedCoins.AmountOf(params.MintDenom)).LT(maxSupply) { + // mint coins + if err := k.MintCoins(ctx, mintedCoins); err != nil { + return err + } } // send the minted coins to the fee collector account diff --git a/x/mint/keeper/genesis_test.go b/x/mint/keeper/genesis_test.go index 4dce9bf881da..754055066c27 100644 --- a/x/mint/keeper/genesis_test.go +++ b/x/mint/keeper/genesis_test.go @@ -71,6 +71,7 @@ func (s *GenesisTestSuite) TestImportExportGenesis() { math.LegacyNewDecWithPrec(9, 2), math.LegacyNewDecWithPrec(69, 2), uint64(60*60*8766/5), + math.ZeroInt(), ) err := s.keeper.InitGenesis(s.sdkCtx, s.accountKeeper, genesisState) diff --git a/x/mint/keeper/migrator.go b/x/mint/keeper/migrator.go index 855f3cd7154c..a2dc25e2e365 100644 --- a/x/mint/keeper/migrator.go +++ b/x/mint/keeper/migrator.go @@ -1,6 +1,10 @@ package keeper -import "context" +import ( + "context" + + "cosmossdk.io/x/mint/types" +) // Migrator is a struct for handling in-place state migrations. type Migrator struct { @@ -21,3 +25,24 @@ func NewMigrator(k Keeper) Migrator { func (m Migrator) Migrate1to2(ctx context.Context) error { return nil } + +// Migrate2to3 migrates the x/mint module state from the consensus version 2 to +// version 3. +func (m Migrator) Migrate2to3(ctx context.Context) error { + params, err := m.keeper.Params.Get(ctx) + if err != nil { + return err + } + + // Initialize the new MaxSupply parameter with the default value + defaultParams := types.DefaultParams() + params.MaxSupply = defaultParams.MaxSupply + + // Set the updated params + err = m.keeper.Params.Set(ctx, params) + if err != nil { + return err + } + + return nil +} diff --git a/x/mint/keeper/msg_server_test.go b/x/mint/keeper/msg_server_test.go index 3009e99776b2..a999150207f0 100644 --- a/x/mint/keeper/msg_server_test.go +++ b/x/mint/keeper/msg_server_test.go @@ -53,6 +53,7 @@ func (s *IntegrationTestSuite) TestUpdateParams() { InflationMin: sdkmath.LegacyNewDecWithPrec(2, 2), GoalBonded: sdkmath.LegacyNewDecWithPrec(37, 2), BlocksPerYear: uint64(60 * 60 * 8766 / 5), + MaxSupply: sdkmath.ZeroInt(), // infinite supply }, }, expectErr: false, diff --git a/x/mint/module.go b/x/mint/module.go index ce3befee347f..617868e39019 100644 --- a/x/mint/module.go +++ b/x/mint/module.go @@ -21,7 +21,7 @@ import ( ) // ConsensusVersion defines the current x/mint module consensus version. -const ConsensusVersion = 2 +const ConsensusVersion = 3 var ( _ module.HasName = AppModule{} @@ -109,6 +109,10 @@ func (am AppModule) RegisterMigrations(mr appmodule.MigrationRegistrar) error { return fmt.Errorf("failed to migrate x/%s from version 1 to 2: %w", types.ModuleName, err) } + if err := mr.Register(types.ModuleName, 2, m.Migrate2to3); err != nil { + return fmt.Errorf("failed to migrate x/%s from version 2 to 3: %w", types.ModuleName, err) + } + return nil } diff --git a/x/mint/proto/cosmos/mint/v1beta1/mint.proto b/x/mint/proto/cosmos/mint/v1beta1/mint.proto index 9e92f6b803aa..da2bfcbc8db6 100644 --- a/x/mint/proto/cosmos/mint/v1beta1/mint.proto +++ b/x/mint/proto/cosmos/mint/v1beta1/mint.proto @@ -59,4 +59,10 @@ message Params { ]; // expected blocks per year uint64 blocks_per_year = 6; + // maximum supply for the token + string max_supply = 7 [ + (cosmos_proto.scalar) = "cosmos.Int", + (gogoproto.customtype) = "cosmossdk.io/math.Int", + (gogoproto.nullable) = false + ]; } diff --git a/x/mint/simulation/genesis.go b/x/mint/simulation/genesis.go index 32138c372b0d..9933efa1971d 100644 --- a/x/mint/simulation/genesis.go +++ b/x/mint/simulation/genesis.go @@ -66,7 +66,7 @@ func RandomizedGenState(simState *module.SimulationState) { mintDenom := simState.BondDenom blocksPerYear := uint64(60 * 60 * 8766 / 5) - params := types.NewParams(mintDenom, inflationRateChange, inflationMax, inflationMin, goalBonded, blocksPerYear) + params := types.NewParams(mintDenom, inflationRateChange, inflationMax, inflationMin, goalBonded, blocksPerYear, math.ZeroInt()) mintGenesis := types.NewGenesisState(types.InitialMinter(inflation), params) diff --git a/x/mint/testutil/expected_keepers_mocks.go b/x/mint/testutil/expected_keepers_mocks.go index baf38f7dc2bf..0f799d05fb01 100644 --- a/x/mint/testutil/expected_keepers_mocks.go +++ b/x/mint/testutil/expected_keepers_mocks.go @@ -167,6 +167,20 @@ func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder { return m.recorder } +// GetSupply mocks base method. +func (m *MockBankKeeper) GetSupply(ctx context.Context, denom string) types.Coin { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSupply", ctx, denom) + ret0, _ := ret[0].(types.Coin) + return ret0 +} + +// GetSupply indicates an expected call of GetSupply. +func (mr *MockBankKeeperMockRecorder) GetSupply(ctx, denom interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSupply", reflect.TypeOf((*MockBankKeeper)(nil).GetSupply), ctx, denom) +} + // MintCoins mocks base method. func (m *MockBankKeeper) MintCoins(ctx context.Context, name string, amt types.Coins) error { m.ctrl.T.Helper() diff --git a/x/mint/types/expected_keepers.go b/x/mint/types/expected_keepers.go index 9a94f3f2e867..8d7ffb02854c 100644 --- a/x/mint/types/expected_keepers.go +++ b/x/mint/types/expected_keepers.go @@ -31,4 +31,5 @@ type BankKeeper interface { SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error SendCoinsFromModuleToModule(ctx context.Context, senderModule, recipientModule string, amt sdk.Coins) error MintCoins(ctx context.Context, name string, amt sdk.Coins) error + GetSupply(ctx context.Context, denom string) sdk.Coin } diff --git a/x/mint/types/mint.pb.go b/x/mint/types/mint.pb.go index 923979902190..7ce197d326ab 100644 --- a/x/mint/types/mint.pb.go +++ b/x/mint/types/mint.pb.go @@ -81,6 +81,8 @@ type Params struct { GoalBonded cosmossdk_io_math.LegacyDec `protobuf:"bytes,5,opt,name=goal_bonded,json=goalBonded,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"goal_bonded"` // expected blocks per year BlocksPerYear uint64 `protobuf:"varint,6,opt,name=blocks_per_year,json=blocksPerYear,proto3" json:"blocks_per_year,omitempty"` + // maximum supply for the token + MaxSupply cosmossdk_io_math.Int `protobuf:"bytes,7,opt,name=max_supply,json=maxSupply,proto3,customtype=cosmossdk.io/math.Int" json:"max_supply"` } func (m *Params) Reset() { *m = Params{} } @@ -138,34 +140,36 @@ func init() { func init() { proto.RegisterFile("cosmos/mint/v1beta1/mint.proto", fileDescriptor_2df116d183c1e223) } var fileDescriptor_2df116d183c1e223 = []byte{ - // 426 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x93, 0x4f, 0x8b, 0xd3, 0x40, - 0x18, 0xc6, 0x33, 0xee, 0x1a, 0xe8, 0xe8, 0xa2, 0x3b, 0xab, 0x90, 0x5d, 0xd9, 0xec, 0xb2, 0x07, - 0x59, 0x0a, 0x4d, 0x28, 0x05, 0x0f, 0x1e, 0x6b, 0x8f, 0x16, 0x4b, 0x2e, 0xa2, 0x82, 0xe1, 0x4d, - 0x32, 0xa6, 0x63, 0x93, 0x99, 0x32, 0x33, 0x96, 0xf6, 0x2b, 0x78, 0xf2, 0x63, 0x78, 0xec, 0xc1, - 0x8b, 0xdf, 0xa0, 0xc7, 0xe2, 0x49, 0x3c, 0x14, 0x69, 0x0f, 0x3d, 0xf9, 0x1d, 0x24, 0x99, 0x90, - 0xa2, 0x37, 0xed, 0x5e, 0x42, 0xe6, 0x79, 0xde, 0xf7, 0xf7, 0x3e, 0xcc, 0x1f, 0xec, 0xc6, 0x42, - 0xe5, 0x42, 0xf9, 0x39, 0xe3, 0xda, 0x9f, 0xb4, 0x23, 0xaa, 0xa1, 0x5d, 0x2e, 0xbc, 0xb1, 0x14, - 0x5a, 0x90, 0x13, 0xe3, 0x7b, 0xa5, 0x54, 0xf9, 0x67, 0x0f, 0x52, 0x91, 0x8a, 0xd2, 0xf7, 0x8b, - 0x3f, 0x53, 0x7a, 0x76, 0x6a, 0x4a, 0x43, 0x63, 0x54, 0x7d, 0xc6, 0x3a, 0x86, 0x9c, 0x71, 0xe1, - 0x97, 0x5f, 0x23, 0x5d, 0x7d, 0x45, 0xd8, 0xee, 0x33, 0xae, 0xa9, 0x24, 0x2f, 0x70, 0x83, 0xf1, - 0x77, 0x19, 0x68, 0x26, 0xb8, 0x83, 0x2e, 0xd1, 0x75, 0xa3, 0xdb, 0x5e, 0xac, 0x2e, 0xac, 0x1f, - 0xab, 0x8b, 0x47, 0x06, 0xa3, 0x92, 0x91, 0xc7, 0x84, 0x9f, 0x83, 0x1e, 0x7a, 0xcf, 0x69, 0x0a, - 0xf1, 0xac, 0x47, 0xe3, 0x6f, 0x5f, 0x5a, 0xb8, 0x9a, 0xd2, 0xa3, 0x71, 0xb0, 0x63, 0x90, 0xb7, - 0xf8, 0x18, 0x38, 0xff, 0x00, 0x59, 0x91, 0x65, 0xc2, 0x14, 0x13, 0x5c, 0x39, 0xb7, 0xfe, 0x17, - 0x7c, 0xdf, 0xb0, 0x06, 0x35, 0xea, 0xea, 0xd7, 0x01, 0xb6, 0x07, 0x20, 0x21, 0x57, 0xe4, 0x1c, - 0xe3, 0x62, 0x6b, 0xc2, 0x84, 0x72, 0x91, 0x9b, 0xf0, 0x41, 0xa3, 0x50, 0x7a, 0x85, 0x40, 0xde, - 0xe3, 0x87, 0x75, 0xac, 0x50, 0x82, 0xa6, 0x61, 0x3c, 0x04, 0x9e, 0xd2, 0x2a, 0xcd, 0x93, 0x7f, - 0x4e, 0xf3, 0x79, 0x3b, 0x6f, 0xa2, 0xe0, 0xa4, 0x86, 0x06, 0xa0, 0xe9, 0xb3, 0x12, 0x49, 0xde, - 0xe0, 0xa3, 0xdd, 0xac, 0x1c, 0xa6, 0xce, 0xc1, 0x5e, 0x33, 0xee, 0xd6, 0xb0, 0x3e, 0x4c, 0xff, - 0x82, 0x33, 0xee, 0x1c, 0xde, 0x14, 0x9c, 0x71, 0xf2, 0x12, 0xdf, 0x49, 0x05, 0x64, 0x61, 0x24, - 0x78, 0x42, 0x13, 0xe7, 0xf6, 0x5e, 0x68, 0x5c, 0xa0, 0xba, 0x25, 0x89, 0x3c, 0xc6, 0xf7, 0xa2, - 0x4c, 0xc4, 0x23, 0x15, 0x8e, 0xa9, 0x0c, 0x67, 0x14, 0xa4, 0x63, 0x5f, 0xa2, 0xeb, 0xc3, 0xe0, - 0xc8, 0xc8, 0x03, 0x2a, 0x5f, 0x51, 0x90, 0x4f, 0xcf, 0x3f, 0x6e, 0xe7, 0x4d, 0xc7, 0x90, 0x5a, - 0x2a, 0x19, 0xf9, 0x53, 0xf3, 0x20, 0xcc, 0x21, 0x77, 0x3b, 0x8b, 0xb5, 0x8b, 0x96, 0x6b, 0x17, - 0xfd, 0x5c, 0xbb, 0xe8, 0xd3, 0xc6, 0xb5, 0x96, 0x1b, 0xd7, 0xfa, 0xbe, 0x71, 0xad, 0xd7, 0xa7, - 0x7f, 0x84, 0xab, 0xba, 0xf4, 0x6c, 0x4c, 0x55, 0x64, 0x97, 0xf7, 0xbc, 0xf3, 0x3b, 0x00, 0x00, - 0xff, 0xff, 0xcd, 0x30, 0x64, 0xe0, 0x62, 0x03, 0x00, 0x00, + // 461 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x93, 0x4f, 0x6b, 0xd4, 0x40, + 0x18, 0xc6, 0x37, 0xba, 0xae, 0xec, 0x68, 0xd1, 0x4e, 0x2d, 0x4c, 0x2b, 0x4d, 0x4b, 0x0f, 0x52, + 0x2a, 0xdd, 0xb0, 0x14, 0x3c, 0x78, 0x5c, 0xf7, 0x52, 0xb1, 0xb8, 0xc4, 0x83, 0xa8, 0xe0, 0xf0, + 0x6e, 0x32, 0xa6, 0xe3, 0x66, 0x66, 0xc2, 0xcc, 0xb4, 0x64, 0xbf, 0x82, 0x27, 0x3f, 0x86, 0xc7, + 0x1e, 0xbc, 0xf4, 0x1b, 0xf4, 0x58, 0x3c, 0x89, 0x87, 0x22, 0xbb, 0x87, 0x7e, 0x0d, 0xc9, 0x4c, + 0x48, 0xfd, 0x73, 0xd2, 0x7a, 0x09, 0xc9, 0xf3, 0xcc, 0xfb, 0x7b, 0x1e, 0xc2, 0xbc, 0x28, 0x4c, + 0x94, 0x11, 0xca, 0x44, 0x82, 0x4b, 0x1b, 0x1d, 0xf5, 0xc7, 0xcc, 0x42, 0xdf, 0x7d, 0xf4, 0x0a, + 0xad, 0xac, 0xc2, 0x4b, 0xde, 0xef, 0x39, 0xa9, 0xf6, 0x57, 0xef, 0x65, 0x2a, 0x53, 0xce, 0x8f, + 0xaa, 0x37, 0x7f, 0x74, 0x75, 0xc5, 0x1f, 0xa5, 0xde, 0xa8, 0xe7, 0xbc, 0xb5, 0x08, 0x82, 0x4b, + 0x15, 0xb9, 0xa7, 0x97, 0x36, 0x4f, 0x02, 0xd4, 0xd9, 0xe7, 0xd2, 0x32, 0x8d, 0x9f, 0xa3, 0x2e, + 0x97, 0xef, 0x72, 0xb0, 0x5c, 0x49, 0x12, 0x6c, 0x04, 0x5b, 0xdd, 0x41, 0xff, 0xf4, 0x7c, 0xbd, + 0xf5, 0xed, 0x7c, 0xfd, 0xbe, 0xc7, 0x98, 0x74, 0xd2, 0xe3, 0x2a, 0x12, 0x60, 0x0f, 0x7a, 0xcf, + 0x58, 0x06, 0xc9, 0x74, 0xc8, 0x92, 0x2f, 0x9f, 0x77, 0x50, 0x9d, 0x32, 0x64, 0x49, 0x7c, 0xc9, + 0xc0, 0x6f, 0xd1, 0x22, 0x48, 0x79, 0x08, 0x79, 0xd5, 0xe5, 0x88, 0x1b, 0xae, 0xa4, 0x21, 0xd7, + 0xfe, 0x15, 0x7c, 0xd7, 0xb3, 0x46, 0x0d, 0x6a, 0xf3, 0xa4, 0x8d, 0x3a, 0x23, 0xd0, 0x20, 0x0c, + 0x5e, 0x43, 0xa8, 0xfa, 0x35, 0x34, 0x65, 0x52, 0x09, 0x5f, 0x3e, 0xee, 0x56, 0xca, 0xb0, 0x12, + 0xf0, 0x7b, 0xb4, 0xdc, 0xd4, 0xa2, 0x1a, 0x2c, 0xa3, 0xc9, 0x01, 0xc8, 0x8c, 0xd5, 0x6d, 0x1e, + 0xfd, 0x75, 0x9b, 0x4f, 0x17, 0xc7, 0xdb, 0x41, 0xbc, 0xd4, 0x40, 0x63, 0xb0, 0xec, 0x89, 0x43, + 0xe2, 0x37, 0x68, 0xe1, 0x32, 0x4b, 0x40, 0x49, 0xae, 0x5f, 0x29, 0xe3, 0x76, 0x03, 0xdb, 0x87, + 0xf2, 0x37, 0x38, 0x97, 0xa4, 0xfd, 0xbf, 0xe0, 0x5c, 0xe2, 0x97, 0xe8, 0x56, 0xa6, 0x20, 0xa7, + 0x63, 0x25, 0x53, 0x96, 0x92, 0x1b, 0x57, 0x42, 0xa3, 0x0a, 0x35, 0x70, 0x24, 0xfc, 0x00, 0xdd, + 0x19, 0xe7, 0x2a, 0x99, 0x18, 0x5a, 0x30, 0x4d, 0xa7, 0x0c, 0x34, 0xe9, 0x6c, 0x04, 0x5b, 0xed, + 0x78, 0xc1, 0xcb, 0x23, 0xa6, 0x5f, 0x31, 0xd0, 0xf8, 0x29, 0x42, 0x02, 0x4a, 0x6a, 0x0e, 0x8b, + 0x22, 0x9f, 0x92, 0x9b, 0x2e, 0xff, 0x61, 0x9d, 0xbf, 0xfc, 0x67, 0xfe, 0x9e, 0xb4, 0x3f, 0x25, + 0xef, 0x49, 0x1b, 0x77, 0x05, 0x94, 0x2f, 0xdc, 0xf4, 0xe3, 0xb5, 0x0f, 0x17, 0xc7, 0xdb, 0xc4, + 0x7b, 0x3b, 0x26, 0x9d, 0x44, 0xa5, 0x5f, 0x2e, 0x7f, 0x61, 0x06, 0xbb, 0xa7, 0xb3, 0x30, 0x38, + 0x9b, 0x85, 0xc1, 0xf7, 0x59, 0x18, 0x7c, 0x9c, 0x87, 0xad, 0xb3, 0x79, 0xd8, 0xfa, 0x3a, 0x0f, + 0x5b, 0xaf, 0x57, 0x7e, 0x09, 0xaa, 0xa7, 0xec, 0xb4, 0x60, 0x66, 0xdc, 0x71, 0x3b, 0xb3, 0xfb, + 0x23, 0x00, 0x00, 0xff, 0xff, 0x5e, 0xda, 0x10, 0x60, 0xae, 0x03, 0x00, 0x00, } func (m *Minter) Marshal() (dAtA []byte, err error) { @@ -231,6 +235,16 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size := m.MaxSupply.Size() + i -= size + if _, err := m.MaxSupply.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintMint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a if m.BlocksPerYear != 0 { i = encodeVarintMint(dAtA, i, uint64(m.BlocksPerYear)) i-- @@ -331,6 +345,8 @@ func (m *Params) Size() (n int) { if m.BlocksPerYear != 0 { n += 1 + sovMint(uint64(m.BlocksPerYear)) } + l = m.MaxSupply.Size() + n += 1 + l + sovMint(uint64(l)) return n } @@ -674,6 +690,40 @@ func (m *Params) Unmarshal(dAtA []byte) error { break } } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxSupply", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMint + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MaxSupply.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipMint(dAtA[iNdEx:]) diff --git a/x/mint/types/params.go b/x/mint/types/params.go index 4c31610d6f2a..9c5c483671d6 100644 --- a/x/mint/types/params.go +++ b/x/mint/types/params.go @@ -11,7 +11,7 @@ import ( ) // NewParams returns Params instance with the given values. -func NewParams(mintDenom string, inflationRateChange, inflationMax, inflationMin, goalBonded math.LegacyDec, blocksPerYear uint64) Params { +func NewParams(mintDenom string, inflationRateChange, inflationMax, inflationMin, goalBonded math.LegacyDec, blocksPerYear uint64, maxSupply math.Int) Params { return Params{ MintDenom: mintDenom, InflationRateChange: inflationRateChange, @@ -19,6 +19,7 @@ func NewParams(mintDenom string, inflationRateChange, inflationMax, inflationMin InflationMin: inflationMin, GoalBonded: goalBonded, BlocksPerYear: blocksPerYear, + MaxSupply: maxSupply, } } @@ -31,6 +32,7 @@ func DefaultParams() Params { InflationMin: math.LegacyNewDecWithPrec(7, 2), GoalBonded: math.LegacyNewDecWithPrec(67, 2), BlocksPerYear: uint64(60 * 60 * 8766 / 5), // assuming 5 second block times + MaxSupply: math.ZeroInt(), // assuming zero is infinite } } @@ -54,6 +56,9 @@ func (p Params) Validate() error { if err := validateBlocksPerYear(p.BlocksPerYear); err != nil { return err } + if err := validateMaxSupply(p.MaxSupply); err != nil { + return err + } if p.InflationMax.LT(p.InflationMin) { return fmt.Errorf( "max inflation (%s) must be greater than or equal to min inflation (%s)", @@ -168,3 +173,16 @@ func validateBlocksPerYear(i interface{}) error { return nil } + +func validateMaxSupply(i interface{}) error { + v, ok := i.(math.Int) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if v.IsNegative() { + return fmt.Errorf("max supply must be positive: %d", v) + } + + return nil +}