diff --git a/.github/workflows/ci-default.yml b/.github/workflows/ci-default.yml
index 1c4105b7..b1d68f52 100644
--- a/.github/workflows/ci-default.yml
+++ b/.github/workflows/ci-default.yml
@@ -33,37 +33,38 @@ jobs:
run: make test
- name: run e2e tests
run: make docker-build test-e2e
- validate-internal-testnet-genesis:
- runs-on: ubuntu-latest
- steps:
- - name: checkout repo from current commit
- uses: actions/checkout@v3
- - name: save version of kava that will be deployed if this pr is merged
- id: kava-version
- run: |
- echo "KAVA_VERSION=$(cat ./ci/env/kava-internal-testnet/KAVA.VERSION)" >> $GITHUB_OUTPUT
- - name: checkout repo from master
- uses: actions/checkout@v3
- with:
- ref: master
- - name: checkout version of kava that will be deployed if this pr is merged
- run: |
- git pull -p
- git checkout $KAVA_VERSION
- env:
- KAVA_VERSION: ${{ steps.kava-version.outputs.KAVA_VERSION }}
- - name: Set up Go
- uses: actions/setup-go@v3
- with:
- go-version: "1.20"
- check-latest: true
- cache: true
- - name: build kava cli
- run: make install
- - name: checkout repo from current commit to validate current branch's genesis
- uses: actions/checkout@v3
- - name: validate testnet genesis
- run: kava validate-genesis ci/env/kava-internal-testnet/genesis.json
+ # this is only applicable for PRs based on master. Disabling for this release branch.
+ # validate-internal-testnet-genesis:
+ # runs-on: ubuntu-latest
+ # steps:
+ # - name: checkout repo from current commit
+ # uses: actions/checkout@v3
+ # - name: save version of kava that will be deployed if this pr is merged
+ # id: kava-version
+ # run: |
+ # echo "KAVA_VERSION=$(cat ./ci/env/kava-internal-testnet/KAVA.VERSION)" >> $GITHUB_OUTPUT
+ # - name: checkout repo from master
+ # uses: actions/checkout@v3
+ # with:
+ # ref: master
+ # - name: checkout version of kava that will be deployed if this pr is merged
+ # run: |
+ # git pull -p
+ # git checkout $KAVA_VERSION
+ # env:
+ # KAVA_VERSION: ${{ steps.kava-version.outputs.KAVA_VERSION }}
+ # - name: Set up Go
+ # uses: actions/setup-go@v3
+ # with:
+ # go-version: "1.20"
+ # check-latest: true
+ # cache: true
+ # - name: build kava cli
+ # run: make install
+ # - name: checkout repo from current commit to validate current branch's genesis
+ # uses: actions/checkout@v3
+ # - name: validate testnet genesis
+ # run: kava validate-genesis ci/env/kava-internal-testnet/genesis.json
validate-protonet-genesis:
runs-on: ubuntu-latest
steps:
diff --git a/.github/workflows/ci-lint.yml b/.github/workflows/ci-lint.yml
index 378deadc..01b0b60c 100644
--- a/.github/workflows/ci-lint.yml
+++ b/.github/workflows/ci-lint.yml
@@ -14,3 +14,4 @@ jobs:
with:
github_token: ${{ secrets.github_token }}
reporter: github-pr-review
+ golangci_lint_flags: --timeout 10m
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 210be41e..e21cc7ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Features
- (metrics) [#1668] Adds non-state breaking x/metrics module for custom telemetry.
- (metrics) [#1669] Add performance timing metrics to all Begin/EndBlockers
+- (community) [#1751] Add `AnnualizedRewards` query endpoint
## [v0.24.0](https://github.com/Kava-Labs/kava/releases/tag/v0.24.0)
@@ -274,6 +275,7 @@ the [changelog](https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/CHANGELOG.md).
- [#257](https://github.com/Kava-Labs/kava/pulls/257) Include scripts to run
large-scale simulations remotely using aws-batch
+[#1751]: https://github.com/Kava-Labs/kava/pull/1751
[#1669]: https://github.com/Kava-Labs/kava/pull/1669
[#1668]: https://github.com/Kava-Labs/kava/pull/1668
[#1624]: https://github.com/Kava-Labs/kava/pull/1624
diff --git a/app/app.go b/app/app.go
index 38177c4d..6c298ed3 100644
--- a/app/app.go
+++ b/app/app.go
@@ -641,14 +641,6 @@ func NewApp(
&app.distrKeeper,
)
- // x/community's deposit/withdraw to lend proposals depend on hard keeper.
- app.communityKeeper = communitykeeper.NewKeeper(
- app.accountKeeper,
- app.bankKeeper,
- &cdpKeeper,
- app.distrKeeper,
- &hardKeeper,
- )
app.kavadistKeeper = kavadistkeeper.NewKeeper(
appCodec,
keys[kavadisttypes.StoreKey],
@@ -669,6 +661,17 @@ func NewApp(
authtypes.FeeCollectorName,
)
+ // x/community's deposit/withdraw to lend proposals depend on hard keeper.
+ app.communityKeeper = communitykeeper.NewKeeper(
+ app.accountKeeper,
+ app.bankKeeper,
+ &cdpKeeper,
+ app.distrKeeper,
+ &hardKeeper,
+ &app.mintKeeper,
+ app.stakingKeeper,
+ )
+
app.incentiveKeeper = incentivekeeper.NewKeeper(
appCodec,
keys[incentivetypes.StoreKey],
diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md
index 3eca9d59..b0fe3771 100644
--- a/docs/core/proto-docs.md
+++ b/docs/core/proto-docs.md
@@ -188,6 +188,8 @@
- [CommunityPoolLendWithdrawProposal](#kava.community.v1beta1.CommunityPoolLendWithdrawProposal)
- [kava/community/v1beta1/query.proto](#kava/community/v1beta1/query.proto)
+ - [QueryAnnualizedRewardsRequest](#kava.community.v1beta1.QueryAnnualizedRewardsRequest)
+ - [QueryAnnualizedRewardsResponse](#kava.community.v1beta1.QueryAnnualizedRewardsResponse)
- [QueryBalanceRequest](#kava.community.v1beta1.QueryBalanceRequest)
- [QueryBalanceResponse](#kava.community.v1beta1.QueryBalanceResponse)
- [QueryTotalBalanceRequest](#kava.community.v1beta1.QueryTotalBalanceRequest)
@@ -2977,6 +2979,31 @@ CommunityPoolLendWithdrawProposal withdraws a lend position back to the communit
+
+
+### QueryAnnualizedRewardsRequest
+QueryAnnualizedRewardsRequest defines the request type for querying the annualized rewards.
+
+
+
+
+
+
+
+
+### QueryAnnualizedRewardsResponse
+QueryAnnualizedRewardsResponse defines the response type for querying the annualized rewards.
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| `staking_rewards` | [string](#string) | | staking_rewards is the calculated annualized staking rewards percentage rate |
+
+
+
+
+
+
### QueryBalanceRequest
@@ -3043,6 +3070,7 @@ Query defines the gRPC querier service for x/community.
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `Balance` | [QueryBalanceRequest](#kava.community.v1beta1.QueryBalanceRequest) | [QueryBalanceResponse](#kava.community.v1beta1.QueryBalanceResponse) | Balance queries the balance of all coins of x/community module. | GET|/kava/community/v1beta1/balance|
| `TotalBalance` | [QueryTotalBalanceRequest](#kava.community.v1beta1.QueryTotalBalanceRequest) | [QueryTotalBalanceResponse](#kava.community.v1beta1.QueryTotalBalanceResponse) | TotalBalance queries the balance of all coins, including x/distribution, x/community, and supplied balances. | GET|/kava/community/v1beta1/total_balance|
+| `AnnualizedRewards` | [QueryAnnualizedRewardsRequest](#kava.community.v1beta1.QueryAnnualizedRewardsRequest) | [QueryAnnualizedRewardsResponse](#kava.community.v1beta1.QueryAnnualizedRewardsResponse) | AnnualizedRewards calculates and returns the current annualized reward percentages, like staking rewards, for the chain. | GET|/kava/community/v1beta1/annualized_rewards|
diff --git a/proto/kava/community/v1beta1/query.proto b/proto/kava/community/v1beta1/query.proto
index 53feab6b..cd066d68 100644
--- a/proto/kava/community/v1beta1/query.proto
+++ b/proto/kava/community/v1beta1/query.proto
@@ -2,6 +2,7 @@ syntax = "proto3";
package kava.community.v1beta1;
import "cosmos/base/v1beta1/coin.proto";
+import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
@@ -19,6 +20,12 @@ service Query {
rpc TotalBalance(QueryTotalBalanceRequest) returns (QueryTotalBalanceResponse) {
option (google.api.http).get = "/kava/community/v1beta1/total_balance";
}
+
+ // AnnualizedRewards calculates and returns the current annualized reward percentages,
+ // like staking rewards, for the chain.
+ rpc AnnualizedRewards(QueryAnnualizedRewardsRequest) returns (QueryAnnualizedRewardsResponse) {
+ option (google.api.http).get = "/kava/community/v1beta1/annualized_rewards";
+ }
}
// QueryBalanceRequest defines the request type for querying x/community balance.
@@ -44,3 +51,16 @@ message QueryTotalBalanceResponse {
(gogoproto.nullable) = false
];
}
+
+// QueryAnnualizedRewardsRequest defines the request type for querying the annualized rewards.
+message QueryAnnualizedRewardsRequest {}
+
+// QueryAnnualizedRewardsResponse defines the response type for querying the annualized rewards.
+message QueryAnnualizedRewardsResponse {
+ // staking_rewards is the calculated annualized staking rewards percentage rate
+ string staking_rewards = 1 [
+ (cosmos_proto.scalar) = "cosmos.Dec",
+ (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
+ (gogoproto.nullable) = false
+ ];
+}
diff --git a/x/community/keeper/grpc_query.go b/x/community/keeper/grpc_query.go
index 20500794..bef2c2f8 100644
--- a/x/community/keeper/grpc_query.go
+++ b/x/community/keeper/grpc_query.go
@@ -3,6 +3,7 @@ package keeper
import (
"context"
+ sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/community/types"
)
@@ -45,3 +46,33 @@ func (s queryServer) TotalBalance(
Pool: totalBalance,
}, nil
}
+
+// AnnualizedRewards calculates the annualized rewards for the chain.
+// This method is backported from v0.25.x to allow for early migration.
+func (s queryServer) AnnualizedRewards(
+ c context.Context,
+ req *types.QueryAnnualizedRewardsRequest,
+) (*types.QueryAnnualizedRewardsResponse, error) {
+ ctx := sdk.UnwrapSDKContext(c)
+
+ bondDenom := s.keeper.stakingKeeper.BondDenom(ctx)
+
+ totalSupply := s.keeper.bankKeeper.GetSupply(ctx, bondDenom).Amount
+ totalBonded := s.keeper.stakingKeeper.TotalBondedTokens(ctx)
+ rewardsPerSecond := sdkmath.LegacyZeroDec() // always zero. this method is backported from v0.25.x to allow for early migration.
+ // need to convert these from sdk.Dec to sdkmath.LegacyDec
+ inflationRate := convertDecToLegacyDec(s.keeper.mintKeeper.GetMinter(ctx).Inflation)
+ communityTax := convertDecToLegacyDec(s.keeper.distrKeeper.GetCommunityTax(ctx))
+
+ return &types.QueryAnnualizedRewardsResponse{
+ StakingRewards: CalculateStakingAnnualPercentage(totalSupply, totalBonded, inflationRate, communityTax, rewardsPerSecond),
+ }, nil
+}
+
+// convertDecToLegacyDec is a helper method for converting between new and old Dec types
+// current version of cosmos-sdk in this repo uses sdk.Dec
+// this module uses sdkmath.LegacyDec in its parameters
+// TODO: remove me after upgrade to cosmos-sdk v50 (LegacyDec is everywhere)
+func convertDecToLegacyDec(in sdk.Dec) sdkmath.LegacyDec {
+ return sdkmath.LegacyNewDecFromBigIntWithPrec(in.BigInt(), sdk.Precision)
+}
diff --git a/x/community/keeper/grpc_query_test.go b/x/community/keeper/grpc_query_test.go
index a40b0e76..369628a6 100644
--- a/x/community/keeper/grpc_query_test.go
+++ b/x/community/keeper/grpc_query_test.go
@@ -9,18 +9,20 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/suite"
+ "github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/community/keeper"
+ "github.com/kava-labs/kava/x/community/testutil"
"github.com/kava-labs/kava/x/community/types"
)
type grpcQueryTestSuite struct {
- KeeperTestSuite
+ testutil.Suite
queryClient types.QueryClient
}
func (suite *grpcQueryTestSuite) SetupTest() {
- suite.KeeperTestSuite.SetupTest()
+ suite.Suite.SetupTest()
queryHelper := baseapp.NewQueryServerTestHelper(suite.Ctx, suite.App.InterfaceRegistry())
types.RegisterQueryServer(queryHelper, keeper.NewQueryServerImpl(suite.Keeper))
@@ -144,3 +146,105 @@ func (suite *grpcQueryTestSuite) TestGrpcQueryTotalBalance() {
})
}
}
+
+// backported from v0.25.x. Does not actually use `rewardsPerSec` because concept does not exist.
+// NOTE: this test makes use of the fact that there is always an initial 1e6 bonded tokens
+// To adjust the bonded ratio, it adjusts the total supply by minting tokens.
+func (suite *grpcQueryTestSuite) TestGrpcQueryAnnualizedRewards() {
+ testCases := []struct {
+ name string
+ bondedRatio sdk.Dec
+ inflation sdk.Dec
+ rewardsPerSec sdkmath.LegacyDec
+ communityTax sdk.Dec
+ expectedRate sdkmath.LegacyDec
+ }{
+ {
+ name: "sanity check: no inflation, no rewards => 0%",
+ bondedRatio: sdk.MustNewDecFromStr("0.3456"),
+ inflation: sdk.ZeroDec(),
+ rewardsPerSec: sdkmath.LegacyZeroDec(),
+ expectedRate: sdkmath.LegacyZeroDec(),
+ },
+ {
+ name: "inflation sanity check: 100% inflation, 100% bonded => 100%",
+ bondedRatio: sdk.OneDec(),
+ inflation: sdk.OneDec(),
+ rewardsPerSec: sdkmath.LegacyZeroDec(),
+ expectedRate: sdkmath.LegacyOneDec(),
+ },
+ {
+ name: "inflation sanity check: 100% community tax => 0%",
+ bondedRatio: sdk.OneDec(),
+ inflation: sdk.OneDec(),
+ communityTax: sdk.OneDec(),
+ rewardsPerSec: sdkmath.LegacyZeroDec(),
+ expectedRate: sdkmath.LegacyZeroDec(),
+ },
+ {
+ name: "inflation enabled: realistic example",
+ bondedRatio: sdk.MustNewDecFromStr("0.148"),
+ inflation: sdk.MustNewDecFromStr("0.595"),
+ communityTax: sdk.MustNewDecFromStr("0.9495"),
+ rewardsPerSec: sdkmath.LegacyZeroDec(),
+ // expect ~20.23%
+ expectedRate: sdkmath.LegacyMustNewDecFromStr("0.203023625910000000"),
+ },
+ }
+
+ for _, tc := range testCases {
+ suite.Run(tc.name, func() {
+ suite.SetupTest()
+
+ // set inflation
+ mk := suite.App.GetMintKeeper()
+ minter := mk.GetMinter(suite.Ctx)
+ minter.Inflation = tc.inflation
+ mk.SetMinter(suite.Ctx, minter)
+
+ // set community tax
+ communityTax := sdk.ZeroDec()
+ if !tc.communityTax.IsNil() {
+ communityTax = tc.communityTax
+ }
+ dk := suite.App.GetDistrKeeper()
+ distParams := dk.GetParams(suite.Ctx)
+ distParams.CommunityTax = communityTax
+ dk.SetParams(suite.Ctx, distParams)
+
+ // set bonded tokens
+ suite.adjustBondedRatio(tc.bondedRatio)
+
+ // query for annualized rewards
+ res, err := suite.queryClient.AnnualizedRewards(suite.Ctx, &types.QueryAnnualizedRewardsRequest{})
+ // verify results match expected
+ suite.Require().NoError(err)
+ suite.Equal(tc.expectedRate, res.StakingRewards)
+ })
+ }
+}
+
+// adjustBondRatio changes the ratio of bonded coins
+// it leverages the fact that there is a constant number of bonded tokens
+// and adjusts the total supply to make change the bonded ratio.
+// returns the new total supply of the bond denom
+func (suite *grpcQueryTestSuite) adjustBondedRatio(desiredRatio sdk.Dec) sdkmath.Int {
+ // from the InitGenesis validator
+ bondedTokens := sdkmath.NewInt(1e6)
+ bondDenom := suite.App.GetStakingKeeper().BondDenom(suite.Ctx)
+
+ // first, burn all non-delegated coins (bonded ratio = 100%)
+ suite.App.DeleteGenesisValidatorCoins(suite.T(), suite.Ctx)
+
+ if desiredRatio.Equal(sdk.OneDec()) {
+ return bondedTokens
+ }
+
+ // mint new tokens to adjust the bond ratio
+ newTotalSupply := sdk.NewDecFromInt(bondedTokens).Quo(desiredRatio).TruncateInt()
+ coinsToMint := newTotalSupply.Sub(bondedTokens)
+ err := suite.App.FundAccount(suite.Ctx, app.RandomAddress(), sdk.NewCoins(sdk.NewCoin(bondDenom, coinsToMint)))
+ suite.Require().NoError(err)
+
+ return newTotalSupply
+}
diff --git a/x/community/keeper/keeper.go b/x/community/keeper/keeper.go
index 1148d9d6..aafa496e 100644
--- a/x/community/keeper/keeper.go
+++ b/x/community/keeper/keeper.go
@@ -16,12 +16,22 @@ type Keeper struct {
distrKeeper types.DistributionKeeper
hardKeeper types.HardKeeper
moduleAddress sdk.AccAddress
+ mintKeeper types.MintKeeper
+ stakingKeeper types.StakingKeeper
legacyCommunityPoolAddress sdk.AccAddress
}
// NewKeeper creates a new community Keeper instance
-func NewKeeper(ak types.AccountKeeper, bk types.BankKeeper, ck types.CdpKeeper, dk types.DistributionKeeper, hk types.HardKeeper) Keeper {
+func NewKeeper(
+ ak types.AccountKeeper,
+ bk types.BankKeeper,
+ ck types.CdpKeeper,
+ dk types.DistributionKeeper,
+ hk types.HardKeeper,
+ mk types.MintKeeper,
+ sk types.StakingKeeper,
+) Keeper {
// ensure community module account is set
addr := ak.GetModuleAddress(types.ModuleAccountName)
if addr == nil {
@@ -37,6 +47,8 @@ func NewKeeper(ak types.AccountKeeper, bk types.BankKeeper, ck types.CdpKeeper,
cdpKeeper: ck,
distrKeeper: dk,
hardKeeper: hk,
+ mintKeeper: mk,
+ stakingKeeper: sk,
moduleAddress: addr,
legacyCommunityPoolAddress: legacyAddr,
diff --git a/x/community/keeper/rewards.go b/x/community/keeper/rewards.go
new file mode 100644
index 00000000..1f9198a3
--- /dev/null
+++ b/x/community/keeper/rewards.go
@@ -0,0 +1,27 @@
+package keeper
+
+import (
+ sdkmath "cosmossdk.io/math"
+)
+
+const SecondsPerYear = 365 * 24 * 3600
+
+// CalculateStakingAnnualPercentage returns the annualized staking reward rate.
+// It assumes that staking comes from one of two sources depending on if inflation is enabled or not.
+func CalculateStakingAnnualPercentage(totalSupply, totalBonded sdkmath.Int, inflationRate, communityTax, rewardsPerSecond sdkmath.LegacyDec) sdkmath.LegacyDec {
+ // no rewards are given if no tokens are bonded, in addition avoid division by zero
+ if totalBonded.IsZero() {
+ return sdkmath.LegacyZeroDec()
+ }
+
+ // the percent of inflationRate * totalSupply tokens that are distributed to stakers
+ percentInflationDistributedToStakers := sdkmath.LegacyOneDec().Sub(communityTax)
+
+ // the total amount of tokens distributed to stakers in a year
+ amountGivenPerYear := inflationRate.
+ MulInt(totalSupply).Mul(percentInflationDistributedToStakers). // portion provided by inflation via mint & distribution modules
+ Add(rewardsPerSecond.Mul(sdkmath.LegacyNewDec(SecondsPerYear))) // portion provided by community module
+
+ // divide by total bonded tokens to get the percent return
+ return amountGivenPerYear.QuoInt(totalBonded)
+}
diff --git a/x/community/keeper/rewards_test.go b/x/community/keeper/rewards_test.go
new file mode 100644
index 00000000..8aed1f76
--- /dev/null
+++ b/x/community/keeper/rewards_test.go
@@ -0,0 +1,189 @@
+package keeper_test
+
+import (
+ "math/big"
+ "testing"
+
+ sdkmath "cosmossdk.io/math"
+ "github.com/stretchr/testify/require"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+
+ "github.com/kava-labs/kava/x/community/keeper"
+)
+
+func TestStakingRewardsCalculator(t *testing.T) {
+ hugeInflation := new(big.Int).Exp(big.NewInt(2), big.NewInt(205), nil)
+ hugeRewardsPerSec := new(big.Int).Exp(big.NewInt(2), big.NewInt(230), nil)
+
+ testCases := []struct {
+ name string
+ totalSupply sdkmath.Int
+ totalBonded sdkmath.Int
+ inflation sdkmath.LegacyDec
+ communityTax sdkmath.LegacyDec
+ perSecReward sdkmath.LegacyDec
+ expectedRate sdkmath.LegacyDec
+ }{
+ {
+ name: "no inflation, no rewards per sec -> 0%",
+ totalSupply: sdkmath.ZeroInt(),
+ totalBonded: sdkmath.ZeroInt(),
+ inflation: sdkmath.LegacyZeroDec(),
+ communityTax: sdkmath.LegacyZeroDec(),
+ perSecReward: sdkmath.LegacyZeroDec(),
+ expectedRate: sdkmath.LegacyZeroDec(),
+ },
+ //
+ //
+ // inflation-only
+ //
+ //
+ {
+ name: "inflation only: no bonded tokens -> 0%",
+ totalSupply: sdk.NewInt(42),
+ totalBonded: sdkmath.ZeroInt(),
+ inflation: sdkmath.LegacyOneDec(),
+ communityTax: sdkmath.LegacyZeroDec(),
+ perSecReward: sdkmath.LegacyZeroDec(),
+ expectedRate: sdkmath.LegacyZeroDec(),
+ },
+ {
+ name: "inflation only: 0% inflation -> 0%",
+ totalSupply: sdk.NewInt(123),
+ totalBonded: sdkmath.NewInt(45),
+ inflation: sdkmath.LegacyZeroDec(),
+ communityTax: sdkmath.LegacyZeroDec(),
+ perSecReward: sdkmath.LegacyZeroDec(),
+ expectedRate: sdkmath.LegacyZeroDec(),
+ },
+ {
+ name: "inflation only: 100% bonded w/ 100% inflation -> 100%",
+ totalSupply: sdk.NewInt(42),
+ totalBonded: sdk.NewInt(42),
+ inflation: sdkmath.LegacyOneDec(),
+ communityTax: sdkmath.LegacyZeroDec(),
+ perSecReward: sdkmath.LegacyZeroDec(),
+ expectedRate: sdkmath.LegacyOneDec(),
+ },
+ {
+ name: "inflation only: 100% community tax -> 0%",
+ totalSupply: sdk.NewInt(123),
+ totalBonded: sdkmath.NewInt(45),
+ inflation: sdkmath.LegacyMustNewDecFromStr("0.853"),
+ communityTax: sdkmath.LegacyOneDec(),
+ perSecReward: sdkmath.LegacyZeroDec(),
+ expectedRate: sdkmath.LegacyZeroDec(),
+ },
+ {
+ name: "inflation only: Oct 2023 case",
+ totalSupply: sdk.NewInt(857570000e6),
+ totalBonded: sdk.NewInt(127680000e6),
+ inflation: sdkmath.LegacyMustNewDecFromStr("0.595"),
+ communityTax: sdkmath.LegacyMustNewDecFromStr("0.9495"),
+ perSecReward: sdkmath.LegacyZeroDec(),
+ // expect 20.18% staking reward
+ expectedRate: sdkmath.LegacyMustNewDecFromStr("0.201815746984649122"), // verified manually
+ },
+ {
+ name: "inflation only: low inflation",
+ totalSupply: sdk.NewInt(857570000e6),
+ totalBonded: sdk.NewInt(127680000e6),
+ inflation: sdkmath.LegacyMustNewDecFromStr("0.0000000001"),
+ communityTax: sdkmath.LegacyMustNewDecFromStr("0.9495"),
+ perSecReward: sdkmath.LegacyZeroDec(),
+ expectedRate: sdkmath.LegacyMustNewDecFromStr("0.000000000033918612"), // verified manually, rounded would be 0.000000000033918613
+ },
+ {
+ name: "inflation only: absurdly high inflation",
+ totalSupply: sdk.NewInt(857570000e6),
+ totalBonded: sdk.NewInt(127680000e6),
+ inflation: sdkmath.LegacyNewDecFromBigInt(hugeInflation), // 2^205. a higher exponent than this overflows.
+ communityTax: sdkmath.LegacyMustNewDecFromStr("0.9495"),
+ perSecReward: sdkmath.LegacyZeroDec(),
+ // https://www.wolframalpha.com/input?i=%282%5E205%29+*+%281+-+0.9495%29+*+%28857570000e6+%2F127680000e6%29
+ expectedRate: sdkmath.LegacyMustNewDecFromStr("17441635052648297161685283657196753398188161373334495592570113.113824561403508771"), // verified manually, would round up
+ },
+ //
+ //
+ // rewards-only
+ //
+ //
+ {
+ name: "rps only: no bonded tokens -> 0%",
+ totalSupply: sdk.NewInt(42),
+ totalBonded: sdkmath.ZeroInt(),
+ inflation: sdkmath.LegacyZeroDec(),
+ communityTax: sdkmath.LegacyZeroDec(),
+ perSecReward: sdkmath.LegacyMustNewDecFromStr("1234567.123456"),
+ expectedRate: sdkmath.LegacyZeroDec(),
+ },
+ {
+ name: "rps only: rps = total bonded / seconds in year -> basically 100%",
+ totalSupply: sdk.NewInt(12345),
+ totalBonded: sdkmath.NewInt(1234),
+ inflation: sdkmath.LegacyZeroDec(),
+ communityTax: sdkmath.LegacyZeroDec(),
+ perSecReward: sdkmath.LegacyNewDec(1234).Quo(sdkmath.LegacyNewDec(keeper.SecondsPerYear)),
+ expectedRate: sdkmath.LegacyMustNewDecFromStr("0.999999999999987228"), // <-- for 6-decimal token, this is negligible rounding
+ },
+ {
+ name: "rps only: 10M kava / year rewards",
+ totalSupply: sdk.NewInt(870950000e6),
+ totalBonded: sdkmath.NewInt(130380000e6),
+ inflation: sdkmath.LegacyZeroDec(),
+ communityTax: sdkmath.LegacyZeroDec(),
+ perSecReward: sdkmath.LegacyMustNewDecFromStr("317097.919837645865043125"), // 10 million kava per year
+ expectedRate: sdkmath.LegacyMustNewDecFromStr("0.076698880196349133"), // verified manually
+ },
+ {
+ name: "rps only: 25M kava / year rewards",
+ totalSupply: sdk.NewInt(870950000e6),
+ totalBonded: sdkmath.NewInt(130380000e6),
+ inflation: sdkmath.LegacyZeroDec(),
+ communityTax: sdkmath.LegacyZeroDec(),
+ perSecReward: sdkmath.LegacyMustNewDecFromStr("792744.799594114662607813"), // 25 million kava per year
+ expectedRate: sdkmath.LegacyMustNewDecFromStr("0.191747200490872833"), // verified manually
+ },
+ {
+ name: "rps only: too much kava / year rewards",
+ totalSupply: sdk.NewInt(870950000e6),
+ totalBonded: sdkmath.NewInt(130380000e6),
+ inflation: sdkmath.LegacyZeroDec(),
+ communityTax: sdkmath.LegacyZeroDec(),
+ perSecReward: sdkmath.LegacyNewDecFromBigInt(hugeRewardsPerSec), // 2^230. a higher exponent than this overflows.
+ // https://www.wolframalpha.com/input?i=%28%28365+*+24+*+3600%29+%2F+130380000e6%29+*+%282%5E230%29
+ expectedRate: sdkmath.LegacyMustNewDecFromStr("417344440850566075319340506352140425426634017001007267992800590.431305795858260469"), // verified manually
+ },
+ {
+ name: "rps only: low kava / year rewards",
+ totalSupply: sdk.NewInt(870950000e6),
+ totalBonded: sdkmath.NewInt(130380000e6),
+ inflation: sdkmath.LegacyZeroDec(),
+ communityTax: sdkmath.LegacyZeroDec(),
+ perSecReward: sdkmath.LegacyMustNewDecFromStr("0.1"),
+ expectedRate: sdkmath.LegacyMustNewDecFromStr("0.000000024187758858"), // verified manually, rounded would be 0.000000024187758859
+ },
+ {
+ name: "rps only: 1 ukava / year rewards",
+ totalSupply: sdk.NewInt(870950000e6),
+ totalBonded: sdkmath.NewInt(130380000e6),
+ inflation: sdkmath.LegacyZeroDec(),
+ communityTax: sdkmath.LegacyZeroDec(),
+ perSecReward: sdkmath.LegacyMustNewDecFromStr("0.000000031709791984"), // 1 ukava per year
+ expectedRate: sdkmath.LegacyMustNewDecFromStr("0.000000000000007669"), // verified manually, rounded would be 0.000000000000007670
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ rewardRate := keeper.CalculateStakingAnnualPercentage(
+ tc.totalSupply,
+ tc.totalBonded,
+ tc.inflation,
+ tc.communityTax,
+ tc.perSecReward)
+ require.Equal(t, tc.expectedRate, rewardRate)
+ })
+ }
+}
diff --git a/x/community/testutil/main.go b/x/community/testutil/main.go
index c9ec69fd..b802e9dc 100644
--- a/x/community/testutil/main.go
+++ b/x/community/testutil/main.go
@@ -28,9 +28,8 @@ func (suite *Suite) SetupTest() {
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()})
- tApp.InitializeFromGenesisStates()
+ suite.App = tApp.InitializeFromGenesisStates()
- suite.App = tApp
suite.Ctx = ctx
suite.Keeper = tApp.GetCommunityKeeper()
communityPoolAddress := tApp.GetAccountKeeper().GetModuleAddress(types.ModuleAccountName)
diff --git a/x/community/types/expected_keepers.go b/x/community/types/expected_keepers.go
index f642dfe0..74920a27 100644
--- a/x/community/types/expected_keepers.go
+++ b/x/community/types/expected_keepers.go
@@ -1,8 +1,10 @@
package types
import (
+ sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+ minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
)
// AccountKeeper defines the contract required for account APIs.
@@ -17,6 +19,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
+
+ GetSupply(ctx sdk.Context, denom string) sdk.Coin
}
// CdpKeeper defines the contract needed to be fulfilled for cdp dependencies.
@@ -36,4 +40,15 @@ type DistributionKeeper interface {
DistributeFromFeePool(ctx sdk.Context, amount sdk.Coins, receiveAddr sdk.AccAddress) error
FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error
GetFeePoolCommunityCoins(ctx sdk.Context) sdk.DecCoins
+ GetCommunityTax(ctx sdk.Context) sdk.Dec
+}
+
+type MintKeeper interface {
+ GetMinter(ctx sdk.Context) (minter minttypes.Minter)
+}
+
+// StakingKeeper expected interface for the staking keeper
+type StakingKeeper interface {
+ BondDenom(ctx sdk.Context) string
+ TotalBondedTokens(ctx sdk.Context) sdkmath.Int
}
diff --git a/x/community/types/query.pb.go b/x/community/types/query.pb.go
index 5da1aae9..2de07ce5 100644
--- a/x/community/types/query.pb.go
+++ b/x/community/types/query.pb.go
@@ -5,7 +5,9 @@ package types
import (
context "context"
+ cosmossdk_io_math "cosmossdk.io/math"
fmt "fmt"
+ _ "github.com/cosmos/cosmos-proto"
github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
types "github.com/cosmos/cosmos-sdk/types"
_ "github.com/gogo/protobuf/gogoproto"
@@ -197,11 +199,89 @@ func (m *QueryTotalBalanceResponse) GetPool() github_com_cosmos_cosmos_sdk_types
return nil
}
+// QueryAnnualizedRewardsRequest defines the request type for querying the annualized rewards.
+type QueryAnnualizedRewardsRequest struct {
+}
+
+func (m *QueryAnnualizedRewardsRequest) Reset() { *m = QueryAnnualizedRewardsRequest{} }
+func (m *QueryAnnualizedRewardsRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryAnnualizedRewardsRequest) ProtoMessage() {}
+func (*QueryAnnualizedRewardsRequest) Descriptor() ([]byte, []int) {
+ return fileDescriptor_f236f06c43149273, []int{4}
+}
+func (m *QueryAnnualizedRewardsRequest) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *QueryAnnualizedRewardsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_QueryAnnualizedRewardsRequest.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *QueryAnnualizedRewardsRequest) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_QueryAnnualizedRewardsRequest.Merge(m, src)
+}
+func (m *QueryAnnualizedRewardsRequest) XXX_Size() int {
+ return m.Size()
+}
+func (m *QueryAnnualizedRewardsRequest) XXX_DiscardUnknown() {
+ xxx_messageInfo_QueryAnnualizedRewardsRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryAnnualizedRewardsRequest proto.InternalMessageInfo
+
+// QueryAnnualizedRewardsResponse defines the response type for querying the annualized rewards.
+type QueryAnnualizedRewardsResponse struct {
+ // staking_rewards is the calculated annualized staking rewards percentage rate
+ StakingRewards cosmossdk_io_math.LegacyDec `protobuf:"bytes,1,opt,name=staking_rewards,json=stakingRewards,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"staking_rewards"`
+}
+
+func (m *QueryAnnualizedRewardsResponse) Reset() { *m = QueryAnnualizedRewardsResponse{} }
+func (m *QueryAnnualizedRewardsResponse) String() string { return proto.CompactTextString(m) }
+func (*QueryAnnualizedRewardsResponse) ProtoMessage() {}
+func (*QueryAnnualizedRewardsResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_f236f06c43149273, []int{5}
+}
+func (m *QueryAnnualizedRewardsResponse) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *QueryAnnualizedRewardsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_QueryAnnualizedRewardsResponse.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *QueryAnnualizedRewardsResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_QueryAnnualizedRewardsResponse.Merge(m, src)
+}
+func (m *QueryAnnualizedRewardsResponse) XXX_Size() int {
+ return m.Size()
+}
+func (m *QueryAnnualizedRewardsResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_QueryAnnualizedRewardsResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryAnnualizedRewardsResponse proto.InternalMessageInfo
+
func init() {
proto.RegisterType((*QueryBalanceRequest)(nil), "kava.community.v1beta1.QueryBalanceRequest")
proto.RegisterType((*QueryBalanceResponse)(nil), "kava.community.v1beta1.QueryBalanceResponse")
proto.RegisterType((*QueryTotalBalanceRequest)(nil), "kava.community.v1beta1.QueryTotalBalanceRequest")
proto.RegisterType((*QueryTotalBalanceResponse)(nil), "kava.community.v1beta1.QueryTotalBalanceResponse")
+ proto.RegisterType((*QueryAnnualizedRewardsRequest)(nil), "kava.community.v1beta1.QueryAnnualizedRewardsRequest")
+ proto.RegisterType((*QueryAnnualizedRewardsResponse)(nil), "kava.community.v1beta1.QueryAnnualizedRewardsResponse")
}
func init() {
@@ -209,33 +289,41 @@ func init() {
}
var fileDescriptor_f236f06c43149273 = []byte{
- // 411 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0x4f, 0x8e, 0xd3, 0x30,
- 0x14, 0xc6, 0x93, 0x42, 0x41, 0x32, 0xac, 0x42, 0x41, 0x6d, 0x54, 0xa5, 0x10, 0x09, 0xb5, 0x52,
- 0xa9, 0xdd, 0x3f, 0x37, 0x28, 0x70, 0x00, 0x2a, 0x56, 0x6c, 0x90, 0x13, 0xac, 0x10, 0x35, 0xf5,
- 0x4b, 0x6b, 0xa7, 0x22, 0xdb, 0xee, 0x91, 0x90, 0xb8, 0x01, 0x4b, 0xce, 0xc0, 0x01, 0xba, 0xac,
- 0xc4, 0x66, 0x56, 0x33, 0xa3, 0x76, 0x0e, 0x32, 0xb2, 0xe3, 0xa9, 0x3a, 0xa3, 0x74, 0xd4, 0x59,
- 0xd9, 0xb2, 0xdf, 0xf7, 0x7d, 0x3f, 0xbf, 0x67, 0xe4, 0x4f, 0xe9, 0x92, 0x92, 0x10, 0x66, 0xb3,
- 0x8c, 0xc7, 0x32, 0x27, 0xcb, 0x41, 0xc0, 0x24, 0x1d, 0x90, 0x79, 0xc6, 0x16, 0x39, 0x4e, 0x17,
- 0x20, 0xc1, 0x79, 0xa5, 0x6a, 0xf0, 0xbe, 0x06, 0x9b, 0x1a, 0xd7, 0x0b, 0x41, 0xcc, 0x40, 0x90,
- 0x80, 0x0a, 0xb6, 0x17, 0x86, 0x10, 0xf3, 0x42, 0xe7, 0xd6, 0x22, 0x88, 0x40, 0x6f, 0x89, 0xda,
- 0x99, 0xd3, 0x66, 0x04, 0x10, 0x25, 0x8c, 0xd0, 0x34, 0x26, 0x94, 0x73, 0x90, 0x54, 0xc6, 0xc0,
- 0x45, 0x71, 0xeb, 0xbf, 0x44, 0x2f, 0x3e, 0xa9, 0xe8, 0x31, 0x4d, 0x28, 0x0f, 0xd9, 0x84, 0xcd,
- 0x33, 0x26, 0xa4, 0x9f, 0xa3, 0xda, 0xed, 0x63, 0x91, 0x02, 0x17, 0xcc, 0xa1, 0xa8, 0xaa, 0x02,
- 0x45, 0xdd, 0x7e, 0xfd, 0xa8, 0xf3, 0x6c, 0xd8, 0xc0, 0x05, 0x12, 0x56, 0x48, 0x37, 0x9c, 0xf8,
- 0x3d, 0xc4, 0x7c, 0xdc, 0x5f, 0x9f, 0xb7, 0xac, 0xbf, 0x17, 0xad, 0x4e, 0x14, 0xcb, 0xef, 0x59,
- 0xa0, 0x9e, 0x43, 0x0c, 0x7f, 0xb1, 0xf4, 0xc4, 0xb7, 0x29, 0x91, 0x79, 0xca, 0x84, 0x16, 0x88,
- 0x49, 0xe1, 0xec, 0xbb, 0xa8, 0xae, 0xa3, 0x3f, 0x83, 0xa4, 0xc9, 0x1d, 0xac, 0x95, 0x8d, 0x1a,
- 0x25, 0x97, 0x06, 0x8e, 0xa1, 0xc7, 0x29, 0x40, 0x62, 0xd8, 0x9a, 0xa5, 0x6c, 0x1f, 0x58, 0xa8,
- 0xf1, 0x46, 0x06, 0xaf, 0x7b, 0x02, 0x9e, 0xd1, 0x88, 0x89, 0xb6, 0x1f, 0xfe, 0xab, 0xa0, 0xaa,
- 0x86, 0x70, 0x7e, 0xda, 0xe8, 0xa9, 0x81, 0x70, 0xba, 0xb8, 0x7c, 0x6a, 0xb8, 0xa4, 0xbd, 0xee,
- 0xbb, 0xd3, 0x8a, 0x8b, 0x77, 0xf9, 0xed, 0xd5, 0xff, 0xab, 0xdf, 0x95, 0x37, 0x4e, 0x8b, 0x1c,
- 0xf9, 0x3c, 0x81, 0x61, 0xf8, 0x63, 0xa3, 0xe7, 0x87, 0x9d, 0x71, 0xfa, 0xf7, 0xe6, 0x94, 0x74,
- 0xd8, 0x1d, 0x3c, 0x40, 0x61, 0xf0, 0x7a, 0x1a, 0xaf, 0xed, 0xbc, 0x3d, 0x86, 0x27, 0x95, 0xea,
- 0xab, 0x81, 0x1c, 0x7f, 0x5c, 0x6f, 0x3d, 0x7b, 0xb3, 0xf5, 0xec, 0xcb, 0xad, 0x67, 0xff, 0xda,
- 0x79, 0xd6, 0x66, 0xe7, 0x59, 0x67, 0x3b, 0xcf, 0xfa, 0x72, 0x38, 0x0b, 0x65, 0xd5, 0x4b, 0x68,
- 0x20, 0x0a, 0xd3, 0x1f, 0x07, 0xb6, 0x7a, 0x28, 0xc1, 0x13, 0xfd, 0x7f, 0x47, 0xd7, 0x01, 0x00,
- 0x00, 0xff, 0xff, 0x67, 0x9d, 0xae, 0xd2, 0x51, 0x03, 0x00, 0x00,
+ // 537 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x94, 0x31, 0x6f, 0xd3, 0x40,
+ 0x14, 0xc7, 0x73, 0x94, 0x82, 0x38, 0x10, 0x08, 0x53, 0x50, 0x62, 0x8a, 0x5d, 0x2c, 0xa1, 0x46,
+ 0xb4, 0xf1, 0x35, 0xa9, 0x60, 0x27, 0x84, 0x8d, 0x05, 0x8b, 0xa9, 0x4b, 0x74, 0x76, 0x4e, 0xae,
+ 0x15, 0xe7, 0xce, 0xcd, 0x9d, 0x0b, 0x46, 0x4c, 0xdd, 0x91, 0x90, 0xf8, 0x06, 0x8c, 0xcc, 0x88,
+ 0xcf, 0xd0, 0xb1, 0x82, 0x05, 0x31, 0x14, 0x94, 0xf0, 0x15, 0xd8, 0x91, 0xcf, 0x2f, 0x51, 0xa0,
+ 0x76, 0xd5, 0x4e, 0xf6, 0xdd, 0x7b, 0xff, 0xf7, 0x7e, 0xcf, 0xef, 0x2f, 0x63, 0x67, 0x48, 0xf7,
+ 0x29, 0x09, 0xc4, 0x68, 0x94, 0xf2, 0x48, 0x65, 0x64, 0xbf, 0xed, 0x33, 0x45, 0xdb, 0x64, 0x2f,
+ 0x65, 0xe3, 0xcc, 0x4d, 0xc6, 0x42, 0x09, 0xe3, 0x4e, 0x9e, 0xe3, 0xce, 0x73, 0x5c, 0xc8, 0x31,
+ 0xad, 0x40, 0xc8, 0x91, 0x90, 0xc4, 0xa7, 0x92, 0xcd, 0x85, 0x81, 0x88, 0x78, 0xa1, 0x33, 0x1b,
+ 0x45, 0xbc, 0xaf, 0x4f, 0xa4, 0x38, 0x40, 0x68, 0x25, 0x14, 0xa1, 0x28, 0xee, 0xf3, 0x37, 0xb8,
+ 0x5d, 0x0d, 0x85, 0x08, 0x63, 0x46, 0x68, 0x12, 0x11, 0xca, 0xb9, 0x50, 0x54, 0x45, 0x82, 0x83,
+ 0xc6, 0xb9, 0x8d, 0x6f, 0xbd, 0xc8, 0xa9, 0xba, 0x34, 0xa6, 0x3c, 0x60, 0x1e, 0xdb, 0x4b, 0x99,
+ 0x54, 0x4e, 0x86, 0x57, 0xfe, 0xbd, 0x96, 0x89, 0xe0, 0x92, 0x19, 0x14, 0x2f, 0xe7, 0x2c, 0xb2,
+ 0x8e, 0xd6, 0x96, 0x9a, 0x57, 0x3b, 0x0d, 0x17, 0x00, 0x72, 0xda, 0xd9, 0x08, 0xee, 0x53, 0x11,
+ 0xf1, 0xee, 0xd6, 0xe1, 0xb1, 0x5d, 0xfb, 0xf4, 0xd3, 0x6e, 0x86, 0x91, 0xda, 0x4d, 0xfd, 0x7c,
+ 0x52, 0xa0, 0x85, 0x47, 0x4b, 0x0e, 0x86, 0x44, 0x65, 0x09, 0x93, 0x5a, 0x20, 0xbd, 0xa2, 0xb2,
+ 0x63, 0xe2, 0xba, 0x6e, 0xfd, 0x52, 0x28, 0x1a, 0xff, 0x87, 0x75, 0x80, 0x70, 0xa3, 0x24, 0x08,
+ 0x70, 0x0c, 0x5f, 0x4c, 0x84, 0x88, 0x81, 0x6d, 0xb5, 0x94, 0xad, 0xc7, 0x02, 0x8d, 0xb7, 0x0d,
+ 0x78, 0x1b, 0x67, 0xc0, 0x03, 0x8d, 0xf4, 0x74, 0x79, 0xc7, 0xc6, 0xf7, 0x34, 0xc3, 0x13, 0xce,
+ 0x53, 0x1a, 0x47, 0x6f, 0xd8, 0xc0, 0x63, 0xaf, 0xe8, 0x78, 0x20, 0x67, 0x94, 0x6f, 0xb1, 0x55,
+ 0x95, 0x00, 0xa4, 0x3b, 0xf8, 0x86, 0x54, 0x74, 0x18, 0xf1, 0xb0, 0x3f, 0x2e, 0x42, 0x75, 0xb4,
+ 0x86, 0x9a, 0x57, 0xba, 0xed, 0x1c, 0xeb, 0xc7, 0xb1, 0x7d, 0xb7, 0x80, 0x90, 0x83, 0xa1, 0x1b,
+ 0x09, 0x32, 0xa2, 0x6a, 0xd7, 0x7d, 0xce, 0x42, 0x1a, 0x64, 0x3d, 0x16, 0x7c, 0xfd, 0xdc, 0xc2,
+ 0x30, 0x5a, 0x8f, 0x05, 0xde, 0x75, 0xa8, 0x04, 0x3d, 0x3a, 0x7f, 0x96, 0xf0, 0xb2, 0x6e, 0x6f,
+ 0xbc, 0x43, 0xf8, 0x32, 0x7c, 0x23, 0x63, 0xc3, 0x2d, 0xf7, 0x9b, 0x5b, 0xb2, 0x7d, 0x73, 0xf3,
+ 0x6c, 0xc9, 0xc5, 0x30, 0xce, 0xfa, 0xc1, 0xb7, 0xdf, 0x1f, 0x2e, 0xdc, 0x37, 0x6c, 0x52, 0x61,
+ 0x7b, 0x1f, 0x18, 0x3e, 0x22, 0x7c, 0x6d, 0x71, 0x71, 0xc6, 0xd6, 0xa9, 0x7d, 0x4a, 0x0c, 0x60,
+ 0xb6, 0xcf, 0xa1, 0x00, 0xbc, 0x96, 0xc6, 0x5b, 0x37, 0x1e, 0x54, 0xe1, 0xa9, 0x5c, 0xd5, 0x9f,
+ 0x41, 0x7e, 0x41, 0xf8, 0xe6, 0x89, 0xc5, 0x19, 0x8f, 0x4e, 0xed, 0x5b, 0xe5, 0x04, 0xf3, 0xf1,
+ 0x79, 0x65, 0xc0, 0xdc, 0xd1, 0xcc, 0x9b, 0xc6, 0xc3, 0x2a, 0x66, 0x3a, 0x97, 0xce, 0x0c, 0xd4,
+ 0x7d, 0x76, 0x38, 0xb1, 0xd0, 0xd1, 0xc4, 0x42, 0xbf, 0x26, 0x16, 0x7a, 0x3f, 0xb5, 0x6a, 0x47,
+ 0x53, 0xab, 0xf6, 0x7d, 0x6a, 0xd5, 0x76, 0x16, 0x3d, 0x9e, 0xd7, 0x6b, 0xc5, 0xd4, 0x97, 0x45,
+ 0xe5, 0xd7, 0x0b, 0xb5, 0xb5, 0xd9, 0xfd, 0x4b, 0xfa, 0xbf, 0xb0, 0xfd, 0x37, 0x00, 0x00, 0xff,
+ 0xff, 0x67, 0x42, 0x2a, 0xb9, 0xc4, 0x04, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -255,6 +343,9 @@ type QueryClient interface {
// TotalBalance queries the balance of all coins, including x/distribution,
// x/community, and supplied balances.
TotalBalance(ctx context.Context, in *QueryTotalBalanceRequest, opts ...grpc.CallOption) (*QueryTotalBalanceResponse, error)
+ // AnnualizedRewards calculates and returns the current annualized reward percentages,
+ // like staking rewards, for the chain.
+ AnnualizedRewards(ctx context.Context, in *QueryAnnualizedRewardsRequest, opts ...grpc.CallOption) (*QueryAnnualizedRewardsResponse, error)
}
type queryClient struct {
@@ -283,6 +374,15 @@ func (c *queryClient) TotalBalance(ctx context.Context, in *QueryTotalBalanceReq
return out, nil
}
+func (c *queryClient) AnnualizedRewards(ctx context.Context, in *QueryAnnualizedRewardsRequest, opts ...grpc.CallOption) (*QueryAnnualizedRewardsResponse, error) {
+ out := new(QueryAnnualizedRewardsResponse)
+ err := c.cc.Invoke(ctx, "/kava.community.v1beta1.Query/AnnualizedRewards", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
// QueryServer is the server API for Query service.
type QueryServer interface {
// Balance queries the balance of all coins of x/community module.
@@ -290,6 +390,9 @@ type QueryServer interface {
// TotalBalance queries the balance of all coins, including x/distribution,
// x/community, and supplied balances.
TotalBalance(context.Context, *QueryTotalBalanceRequest) (*QueryTotalBalanceResponse, error)
+ // AnnualizedRewards calculates and returns the current annualized reward percentages,
+ // like staking rewards, for the chain.
+ AnnualizedRewards(context.Context, *QueryAnnualizedRewardsRequest) (*QueryAnnualizedRewardsResponse, error)
}
// UnimplementedQueryServer can be embedded to have forward compatible implementations.
@@ -302,6 +405,9 @@ func (*UnimplementedQueryServer) Balance(ctx context.Context, req *QueryBalanceR
func (*UnimplementedQueryServer) TotalBalance(ctx context.Context, req *QueryTotalBalanceRequest) (*QueryTotalBalanceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method TotalBalance not implemented")
}
+func (*UnimplementedQueryServer) AnnualizedRewards(ctx context.Context, req *QueryAnnualizedRewardsRequest) (*QueryAnnualizedRewardsResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method AnnualizedRewards not implemented")
+}
func RegisterQueryServer(s grpc1.Server, srv QueryServer) {
s.RegisterService(&_Query_serviceDesc, srv)
@@ -343,6 +449,24 @@ func _Query_TotalBalance_Handler(srv interface{}, ctx context.Context, dec func(
return interceptor(ctx, in, info, handler)
}
+func _Query_AnnualizedRewards_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(QueryAnnualizedRewardsRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(QueryServer).AnnualizedRewards(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/kava.community.v1beta1.Query/AnnualizedRewards",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(QueryServer).AnnualizedRewards(ctx, req.(*QueryAnnualizedRewardsRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
var _Query_serviceDesc = grpc.ServiceDesc{
ServiceName: "kava.community.v1beta1.Query",
HandlerType: (*QueryServer)(nil),
@@ -355,6 +479,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{
MethodName: "TotalBalance",
Handler: _Query_TotalBalance_Handler,
},
+ {
+ MethodName: "AnnualizedRewards",
+ Handler: _Query_AnnualizedRewards_Handler,
+ },
},
Streams: []grpc.StreamDesc{},
Metadata: "kava/community/v1beta1/query.proto",
@@ -480,6 +608,62 @@ func (m *QueryTotalBalanceResponse) MarshalToSizedBuffer(dAtA []byte) (int, erro
return len(dAtA) - i, nil
}
+func (m *QueryAnnualizedRewardsRequest) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *QueryAnnualizedRewardsRequest) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryAnnualizedRewardsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ return len(dAtA) - i, nil
+}
+
+func (m *QueryAnnualizedRewardsResponse) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *QueryAnnualizedRewardsResponse) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryAnnualizedRewardsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ {
+ size := m.StakingRewards.Size()
+ i -= size
+ if _, err := m.StakingRewards.MarshalTo(dAtA[i:]); err != nil {
+ return 0, err
+ }
+ i = encodeVarintQuery(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0xa
+ return len(dAtA) - i, nil
+}
+
func encodeVarintQuery(dAtA []byte, offset int, v uint64) int {
offset -= sovQuery(v)
base := offset
@@ -539,6 +723,26 @@ func (m *QueryTotalBalanceResponse) Size() (n int) {
return n
}
+func (m *QueryAnnualizedRewardsRequest) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ return n
+}
+
+func (m *QueryAnnualizedRewardsResponse) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ l = m.StakingRewards.Size()
+ n += 1 + l + sovQuery(uint64(l))
+ return n
+}
+
func sovQuery(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
@@ -813,6 +1017,140 @@ func (m *QueryTotalBalanceResponse) Unmarshal(dAtA []byte) error {
}
return nil
}
+func (m *QueryAnnualizedRewardsRequest) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowQuery
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: QueryAnnualizedRewardsRequest: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: QueryAnnualizedRewardsRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ default:
+ iNdEx = preIndex
+ skippy, err := skipQuery(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthQuery
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *QueryAnnualizedRewardsResponse) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowQuery
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: QueryAnnualizedRewardsResponse: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: QueryAnnualizedRewardsResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field StakingRewards", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowQuery
+ }
+ 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 ErrInvalidLengthQuery
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthQuery
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.StakingRewards.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipQuery(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthQuery
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
func skipQuery(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
diff --git a/x/community/types/query.pb.gw.go b/x/community/types/query.pb.gw.go
index 9f649a31..4cc2d791 100644
--- a/x/community/types/query.pb.gw.go
+++ b/x/community/types/query.pb.gw.go
@@ -69,6 +69,24 @@ func local_request_Query_TotalBalance_0(ctx context.Context, marshaler runtime.M
}
+func request_Query_AnnualizedRewards_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq QueryAnnualizedRewardsRequest
+ var metadata runtime.ServerMetadata
+
+ msg, err := client.AnnualizedRewards(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_Query_AnnualizedRewards_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq QueryAnnualizedRewardsRequest
+ var metadata runtime.ServerMetadata
+
+ msg, err := server.AnnualizedRewards(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
// RegisterQueryHandlerServer registers the http handlers for service Query to "mux".
// UnaryRPC :call QueryServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@@ -121,6 +139,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv
})
+ mux.Handle("GET", pattern_Query_AnnualizedRewards_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ var stream runtime.ServerTransportStream
+ ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := local_request_Query_AnnualizedRewards_0(rctx, inboundMarshaler, server, req, pathParams)
+ md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+ ctx = runtime.NewServerMetadataContext(ctx, md)
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_Query_AnnualizedRewards_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
return nil
}
@@ -202,6 +243,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie
})
+ mux.Handle("GET", pattern_Query_AnnualizedRewards_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ rctx, err := runtime.AnnotateContext(ctx, mux, req)
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_Query_AnnualizedRewards_0(rctx, inboundMarshaler, client, req, pathParams)
+ ctx = runtime.NewServerMetadataContext(ctx, md)
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_Query_AnnualizedRewards_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
return nil
}
@@ -209,10 +270,14 @@ var (
pattern_Query_Balance_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"kava", "community", "v1beta1", "balance"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Query_TotalBalance_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"kava", "community", "v1beta1", "total_balance"}, "", runtime.AssumeColonVerbOpt(false)))
+
+ pattern_Query_AnnualizedRewards_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"kava", "community", "v1beta1", "annualized_rewards"}, "", runtime.AssumeColonVerbOpt(false)))
)
var (
forward_Query_Balance_0 = runtime.ForwardResponseMessage
forward_Query_TotalBalance_0 = runtime.ForwardResponseMessage
+
+ forward_Query_AnnualizedRewards_0 = runtime.ForwardResponseMessage
)