feat(community): add AnnualizedRewards grpc query (backport #1751) (#1754)

* feat(community): add AnnualizedRewards grpc query (#1751)

* add annualized_reward query proto

* use sdkmath.LegacyDec to match RPS param...

* add AnnualizedRewards grpc query

* add changelog entry

* simplify calculation & expand test cases

(cherry picked from commit 0efe7f2281)

* fix conflicts, remove community param references

* backport update to lint CI

* disable internal testnet genesis check

* fix initialization order of keepers in app.go

---------

Co-authored-by: Robert Pirtle <Astropirtle@gmail.com>
This commit is contained in:
mergify[bot] 2023-10-25 10:09:19 -07:00 committed by Robert Pirtle
parent e7e2cbf41a
commit a9583b16f4
15 changed files with 906 additions and 71 deletions

View File

@ -33,37 +33,38 @@ jobs:
run: make test run: make test
- name: run e2e tests - name: run e2e tests
run: make docker-build test-e2e run: make docker-build test-e2e
validate-internal-testnet-genesis: # this is only applicable for PRs based on master. Disabling for this release branch.
runs-on: ubuntu-latest # validate-internal-testnet-genesis:
steps: # runs-on: ubuntu-latest
- name: checkout repo from current commit # steps:
uses: actions/checkout@v3 # - name: checkout repo from current commit
- name: save version of kava that will be deployed if this pr is merged # uses: actions/checkout@v3
id: kava-version # - name: save version of kava that will be deployed if this pr is merged
run: | # id: kava-version
echo "KAVA_VERSION=$(cat ./ci/env/kava-internal-testnet/KAVA.VERSION)" >> $GITHUB_OUTPUT # run: |
- name: checkout repo from master # echo "KAVA_VERSION=$(cat ./ci/env/kava-internal-testnet/KAVA.VERSION)" >> $GITHUB_OUTPUT
uses: actions/checkout@v3 # - name: checkout repo from master
with: # uses: actions/checkout@v3
ref: master # with:
- name: checkout version of kava that will be deployed if this pr is merged # ref: master
run: | # - name: checkout version of kava that will be deployed if this pr is merged
git pull -p # run: |
git checkout $KAVA_VERSION # git pull -p
env: # git checkout $KAVA_VERSION
KAVA_VERSION: ${{ steps.kava-version.outputs.KAVA_VERSION }} # env:
- name: Set up Go # KAVA_VERSION: ${{ steps.kava-version.outputs.KAVA_VERSION }}
uses: actions/setup-go@v3 # - name: Set up Go
with: # uses: actions/setup-go@v3
go-version: "1.20" # with:
check-latest: true # go-version: "1.20"
cache: true # check-latest: true
- name: build kava cli # cache: true
run: make install # - name: build kava cli
- name: checkout repo from current commit to validate current branch's genesis # run: make install
uses: actions/checkout@v3 # - name: checkout repo from current commit to validate current branch's genesis
- name: validate testnet genesis # uses: actions/checkout@v3
run: kava validate-genesis ci/env/kava-internal-testnet/genesis.json # - name: validate testnet genesis
# run: kava validate-genesis ci/env/kava-internal-testnet/genesis.json
validate-protonet-genesis: validate-protonet-genesis:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -14,3 +14,4 @@ jobs:
with: with:
github_token: ${{ secrets.github_token }} github_token: ${{ secrets.github_token }}
reporter: github-pr-review reporter: github-pr-review
golangci_lint_flags: --timeout 10m

View File

@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Features ### Features
- (metrics) [#1668] Adds non-state breaking x/metrics module for custom telemetry. - (metrics) [#1668] Adds non-state breaking x/metrics module for custom telemetry.
- (metrics) [#1669] Add performance timing metrics to all Begin/EndBlockers - (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) ## [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 - [#257](https://github.com/Kava-Labs/kava/pulls/257) Include scripts to run
large-scale simulations remotely using aws-batch 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 [#1669]: https://github.com/Kava-Labs/kava/pull/1669
[#1668]: https://github.com/Kava-Labs/kava/pull/1668 [#1668]: https://github.com/Kava-Labs/kava/pull/1668
[#1624]: https://github.com/Kava-Labs/kava/pull/1624 [#1624]: https://github.com/Kava-Labs/kava/pull/1624

View File

@ -641,14 +641,6 @@ func NewApp(
&app.distrKeeper, &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( app.kavadistKeeper = kavadistkeeper.NewKeeper(
appCodec, appCodec,
keys[kavadisttypes.StoreKey], keys[kavadisttypes.StoreKey],
@ -669,6 +661,17 @@ func NewApp(
authtypes.FeeCollectorName, 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( app.incentiveKeeper = incentivekeeper.NewKeeper(
appCodec, appCodec,
keys[incentivetypes.StoreKey], keys[incentivetypes.StoreKey],

View File

@ -188,6 +188,8 @@
- [CommunityPoolLendWithdrawProposal](#kava.community.v1beta1.CommunityPoolLendWithdrawProposal) - [CommunityPoolLendWithdrawProposal](#kava.community.v1beta1.CommunityPoolLendWithdrawProposal)
- [kava/community/v1beta1/query.proto](#kava/community/v1beta1/query.proto) - [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) - [QueryBalanceRequest](#kava.community.v1beta1.QueryBalanceRequest)
- [QueryBalanceResponse](#kava.community.v1beta1.QueryBalanceResponse) - [QueryBalanceResponse](#kava.community.v1beta1.QueryBalanceResponse)
- [QueryTotalBalanceRequest](#kava.community.v1beta1.QueryTotalBalanceRequest) - [QueryTotalBalanceRequest](#kava.community.v1beta1.QueryTotalBalanceRequest)
@ -2977,6 +2979,31 @@ CommunityPoolLendWithdrawProposal withdraws a lend position back to the communit
<a name="kava.community.v1beta1.QueryAnnualizedRewardsRequest"></a>
### QueryAnnualizedRewardsRequest
QueryAnnualizedRewardsRequest defines the request type for querying the annualized rewards.
<a name="kava.community.v1beta1.QueryAnnualizedRewardsResponse"></a>
### 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 |
<a name="kava.community.v1beta1.QueryBalanceRequest"></a> <a name="kava.community.v1beta1.QueryBalanceRequest"></a>
### QueryBalanceRequest ### 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| | `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| | `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|
<!-- end services --> <!-- end services -->

View File

@ -2,6 +2,7 @@ syntax = "proto3";
package kava.community.v1beta1; package kava.community.v1beta1;
import "cosmos/base/v1beta1/coin.proto"; import "cosmos/base/v1beta1/coin.proto";
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto"; import "gogoproto/gogo.proto";
import "google/api/annotations.proto"; import "google/api/annotations.proto";
@ -19,6 +20,12 @@ service Query {
rpc TotalBalance(QueryTotalBalanceRequest) returns (QueryTotalBalanceResponse) { rpc TotalBalance(QueryTotalBalanceRequest) returns (QueryTotalBalanceResponse) {
option (google.api.http).get = "/kava/community/v1beta1/total_balance"; 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. // QueryBalanceRequest defines the request type for querying x/community balance.
@ -44,3 +51,16 @@ message QueryTotalBalanceResponse {
(gogoproto.nullable) = false (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
];
}

View File

@ -3,6 +3,7 @@ package keeper
import ( import (
"context" "context"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/community/types" "github.com/kava-labs/kava/x/community/types"
) )
@ -45,3 +46,33 @@ func (s queryServer) TotalBalance(
Pool: totalBalance, Pool: totalBalance,
}, nil }, 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)
}

View File

@ -9,18 +9,20 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/suite" "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/keeper"
"github.com/kava-labs/kava/x/community/testutil"
"github.com/kava-labs/kava/x/community/types" "github.com/kava-labs/kava/x/community/types"
) )
type grpcQueryTestSuite struct { type grpcQueryTestSuite struct {
KeeperTestSuite testutil.Suite
queryClient types.QueryClient queryClient types.QueryClient
} }
func (suite *grpcQueryTestSuite) SetupTest() { func (suite *grpcQueryTestSuite) SetupTest() {
suite.KeeperTestSuite.SetupTest() suite.Suite.SetupTest()
queryHelper := baseapp.NewQueryServerTestHelper(suite.Ctx, suite.App.InterfaceRegistry()) queryHelper := baseapp.NewQueryServerTestHelper(suite.Ctx, suite.App.InterfaceRegistry())
types.RegisterQueryServer(queryHelper, keeper.NewQueryServerImpl(suite.Keeper)) 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
}

View File

@ -16,12 +16,22 @@ type Keeper struct {
distrKeeper types.DistributionKeeper distrKeeper types.DistributionKeeper
hardKeeper types.HardKeeper hardKeeper types.HardKeeper
moduleAddress sdk.AccAddress moduleAddress sdk.AccAddress
mintKeeper types.MintKeeper
stakingKeeper types.StakingKeeper
legacyCommunityPoolAddress sdk.AccAddress legacyCommunityPoolAddress sdk.AccAddress
} }
// NewKeeper creates a new community Keeper instance // 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 // ensure community module account is set
addr := ak.GetModuleAddress(types.ModuleAccountName) addr := ak.GetModuleAddress(types.ModuleAccountName)
if addr == nil { if addr == nil {
@ -37,6 +47,8 @@ func NewKeeper(ak types.AccountKeeper, bk types.BankKeeper, ck types.CdpKeeper,
cdpKeeper: ck, cdpKeeper: ck,
distrKeeper: dk, distrKeeper: dk,
hardKeeper: hk, hardKeeper: hk,
mintKeeper: mk,
stakingKeeper: sk,
moduleAddress: addr, moduleAddress: addr,
legacyCommunityPoolAddress: legacyAddr, legacyCommunityPoolAddress: legacyAddr,

View File

@ -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)
}

View File

@ -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)
})
}
}

View File

@ -28,9 +28,8 @@ func (suite *Suite) SetupTest() {
tApp := app.NewTestApp() tApp := app.NewTestApp()
ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()}) ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()})
tApp.InitializeFromGenesisStates() suite.App = tApp.InitializeFromGenesisStates()
suite.App = tApp
suite.Ctx = ctx suite.Ctx = ctx
suite.Keeper = tApp.GetCommunityKeeper() suite.Keeper = tApp.GetCommunityKeeper()
communityPoolAddress := tApp.GetAccountKeeper().GetModuleAddress(types.ModuleAccountName) communityPoolAddress := tApp.GetAccountKeeper().GetModuleAddress(types.ModuleAccountName)

View File

@ -1,8 +1,10 @@
package types package types
import ( import (
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/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. // 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 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 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. // 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 DistributeFromFeePool(ctx sdk.Context, amount sdk.Coins, receiveAddr sdk.AccAddress) error
FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error
GetFeePoolCommunityCoins(ctx sdk.Context) sdk.DecCoins 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
} }

View File

@ -5,7 +5,9 @@ package types
import ( import (
context "context" context "context"
cosmossdk_io_math "cosmossdk.io/math"
fmt "fmt" fmt "fmt"
_ "github.com/cosmos/cosmos-proto"
github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
types "github.com/cosmos/cosmos-sdk/types" types "github.com/cosmos/cosmos-sdk/types"
_ "github.com/gogo/protobuf/gogoproto" _ "github.com/gogo/protobuf/gogoproto"
@ -197,11 +199,89 @@ func (m *QueryTotalBalanceResponse) GetPool() github_com_cosmos_cosmos_sdk_types
return nil 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() { func init() {
proto.RegisterType((*QueryBalanceRequest)(nil), "kava.community.v1beta1.QueryBalanceRequest") proto.RegisterType((*QueryBalanceRequest)(nil), "kava.community.v1beta1.QueryBalanceRequest")
proto.RegisterType((*QueryBalanceResponse)(nil), "kava.community.v1beta1.QueryBalanceResponse") proto.RegisterType((*QueryBalanceResponse)(nil), "kava.community.v1beta1.QueryBalanceResponse")
proto.RegisterType((*QueryTotalBalanceRequest)(nil), "kava.community.v1beta1.QueryTotalBalanceRequest") proto.RegisterType((*QueryTotalBalanceRequest)(nil), "kava.community.v1beta1.QueryTotalBalanceRequest")
proto.RegisterType((*QueryTotalBalanceResponse)(nil), "kava.community.v1beta1.QueryTotalBalanceResponse") 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() { func init() {
@ -209,33 +289,41 @@ func init() {
} }
var fileDescriptor_f236f06c43149273 = []byte{ var fileDescriptor_f236f06c43149273 = []byte{
// 411 bytes of a gzipped FileDescriptorProto // 537 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0x4f, 0x8e, 0xd3, 0x30, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x94, 0x31, 0x6f, 0xd3, 0x40,
0x14, 0xc6, 0x93, 0x42, 0x41, 0x32, 0xac, 0x42, 0x41, 0x6d, 0x54, 0xa5, 0x10, 0x09, 0xb5, 0x52, 0x14, 0xc7, 0x73, 0x94, 0x82, 0x38, 0x10, 0x08, 0x53, 0x50, 0x62, 0x8a, 0x5d, 0x2c, 0xa1, 0x46,
0xa9, 0xdd, 0x3f, 0x37, 0x28, 0x70, 0x00, 0x2a, 0x56, 0x6c, 0x90, 0x13, 0xac, 0x10, 0x35, 0xf5, 0xb4, 0xf1, 0x35, 0xa9, 0x60, 0x27, 0x84, 0x8d, 0x05, 0x8b, 0xa9, 0x4b, 0x74, 0x76, 0x4e, 0xae,
0x4b, 0x6b, 0xa7, 0x22, 0xdb, 0xee, 0x91, 0x90, 0xb8, 0x01, 0x4b, 0xce, 0xc0, 0x01, 0xba, 0xac, 0x15, 0xe7, 0xce, 0xcd, 0x9d, 0x0b, 0x46, 0x4c, 0xdd, 0x91, 0x90, 0xf8, 0x06, 0x8c, 0xcc, 0x88,
0xc4, 0x66, 0x56, 0x33, 0xa3, 0x76, 0x0e, 0x32, 0xb2, 0xe3, 0xa9, 0x3a, 0xa3, 0x74, 0xd4, 0x59, 0xcf, 0xd0, 0xb1, 0x82, 0x05, 0x31, 0x14, 0x94, 0xf0, 0x15, 0xd8, 0x91, 0xcf, 0x2f, 0x51, 0xa0,
0xd9, 0xb2, 0xdf, 0xf7, 0x7d, 0x3f, 0xbf, 0x67, 0xe4, 0x4f, 0xe9, 0x92, 0x92, 0x10, 0x66, 0xb3, 0x76, 0xd5, 0x4e, 0xf6, 0xdd, 0x7b, 0xff, 0xf7, 0x7e, 0xcf, 0xef, 0x2f, 0x63, 0x67, 0x48, 0xf7,
0x8c, 0xc7, 0x32, 0x27, 0xcb, 0x41, 0xc0, 0x24, 0x1d, 0x90, 0x79, 0xc6, 0x16, 0x39, 0x4e, 0x17, 0x29, 0x09, 0xc4, 0x68, 0x94, 0xf2, 0x48, 0x65, 0x64, 0xbf, 0xed, 0x33, 0x45, 0xdb, 0x64, 0x2f,
0x20, 0xc1, 0x79, 0xa5, 0x6a, 0xf0, 0xbe, 0x06, 0x9b, 0x1a, 0xd7, 0x0b, 0x41, 0xcc, 0x40, 0x90, 0x65, 0xe3, 0xcc, 0x4d, 0xc6, 0x42, 0x09, 0xe3, 0x4e, 0x9e, 0xe3, 0xce, 0x73, 0x5c, 0xc8, 0x31,
0x80, 0x0a, 0xb6, 0x17, 0x86, 0x10, 0xf3, 0x42, 0xe7, 0xd6, 0x22, 0x88, 0x40, 0x6f, 0x89, 0xda, 0xad, 0x40, 0xc8, 0x91, 0x90, 0xc4, 0xa7, 0x92, 0xcd, 0x85, 0x81, 0x88, 0x78, 0xa1, 0x33, 0x1b,
0x99, 0xd3, 0x66, 0x04, 0x10, 0x25, 0x8c, 0xd0, 0x34, 0x26, 0x94, 0x73, 0x90, 0x54, 0xc6, 0xc0, 0x45, 0xbc, 0xaf, 0x4f, 0xa4, 0x38, 0x40, 0x68, 0x25, 0x14, 0xa1, 0x28, 0xee, 0xf3, 0x37, 0xb8,
0x45, 0x71, 0xeb, 0xbf, 0x44, 0x2f, 0x3e, 0xa9, 0xe8, 0x31, 0x4d, 0x28, 0x0f, 0xd9, 0x84, 0xcd, 0x5d, 0x0d, 0x85, 0x08, 0x63, 0x46, 0x68, 0x12, 0x11, 0xca, 0xb9, 0x50, 0x54, 0x45, 0x82, 0x83,
0x33, 0x26, 0xa4, 0x9f, 0xa3, 0xda, 0xed, 0x63, 0x91, 0x02, 0x17, 0xcc, 0xa1, 0xa8, 0xaa, 0x02, 0xc6, 0xb9, 0x8d, 0x6f, 0xbd, 0xc8, 0xa9, 0xba, 0x34, 0xa6, 0x3c, 0x60, 0x1e, 0xdb, 0x4b, 0x99,
0x45, 0xdd, 0x7e, 0xfd, 0xa8, 0xf3, 0x6c, 0xd8, 0xc0, 0x05, 0x12, 0x56, 0x48, 0x37, 0x9c, 0xf8, 0x54, 0x4e, 0x86, 0x57, 0xfe, 0xbd, 0x96, 0x89, 0xe0, 0x92, 0x19, 0x14, 0x2f, 0xe7, 0x2c, 0xb2,
0x3d, 0xc4, 0x7c, 0xdc, 0x5f, 0x9f, 0xb7, 0xac, 0xbf, 0x17, 0xad, 0x4e, 0x14, 0xcb, 0xef, 0x59, 0x8e, 0xd6, 0x96, 0x9a, 0x57, 0x3b, 0x0d, 0x17, 0x00, 0x72, 0xda, 0xd9, 0x08, 0xee, 0x53, 0x11,
0xa0, 0x9e, 0x43, 0x0c, 0x7f, 0xb1, 0xf4, 0xc4, 0xb7, 0x29, 0x91, 0x79, 0xca, 0x84, 0x16, 0x88, 0xf1, 0xee, 0xd6, 0xe1, 0xb1, 0x5d, 0xfb, 0xf4, 0xd3, 0x6e, 0x86, 0x91, 0xda, 0x4d, 0xfd, 0x7c,
0x49, 0xe1, 0xec, 0xbb, 0xa8, 0xae, 0xa3, 0x3f, 0x83, 0xa4, 0xc9, 0x1d, 0xac, 0x95, 0x8d, 0x1a, 0x52, 0xa0, 0x85, 0x47, 0x4b, 0x0e, 0x86, 0x44, 0x65, 0x09, 0x93, 0x5a, 0x20, 0xbd, 0xa2, 0xb2,
0x25, 0x97, 0x06, 0x8e, 0xa1, 0xc7, 0x29, 0x40, 0x62, 0xd8, 0x9a, 0xa5, 0x6c, 0x1f, 0x58, 0xa8, 0x63, 0xe2, 0xba, 0x6e, 0xfd, 0x52, 0x28, 0x1a, 0xff, 0x87, 0x75, 0x80, 0x70, 0xa3, 0x24, 0x08,
0xf1, 0x46, 0x06, 0xaf, 0x7b, 0x02, 0x9e, 0xd1, 0x88, 0x89, 0xb6, 0x1f, 0xfe, 0xab, 0xa0, 0xaa, 0x70, 0x0c, 0x5f, 0x4c, 0x84, 0x88, 0x81, 0x6d, 0xb5, 0x94, 0xad, 0xc7, 0x02, 0x8d, 0xb7, 0x0d,
0x86, 0x70, 0x7e, 0xda, 0xe8, 0xa9, 0x81, 0x70, 0xba, 0xb8, 0x7c, 0x6a, 0xb8, 0xa4, 0xbd, 0xee, 0x78, 0x1b, 0x67, 0xc0, 0x03, 0x8d, 0xf4, 0x74, 0x79, 0xc7, 0xc6, 0xf7, 0x34, 0xc3, 0x13, 0xce,
0xbb, 0xd3, 0x8a, 0x8b, 0x77, 0xf9, 0xed, 0xd5, 0xff, 0xab, 0xdf, 0x95, 0x37, 0x4e, 0x8b, 0x1c, 0x53, 0x1a, 0x47, 0x6f, 0xd8, 0xc0, 0x63, 0xaf, 0xe8, 0x78, 0x20, 0x67, 0x94, 0x6f, 0xb1, 0x55,
0xf9, 0x3c, 0x81, 0x61, 0xf8, 0x63, 0xa3, 0xe7, 0x87, 0x9d, 0x71, 0xfa, 0xf7, 0xe6, 0x94, 0x74, 0x95, 0x00, 0xa4, 0x3b, 0xf8, 0x86, 0x54, 0x74, 0x18, 0xf1, 0xb0, 0x3f, 0x2e, 0x42, 0x75, 0xb4,
0xd8, 0x1d, 0x3c, 0x40, 0x61, 0xf0, 0x7a, 0x1a, 0xaf, 0xed, 0xbc, 0x3d, 0x86, 0x27, 0x95, 0xea, 0x86, 0x9a, 0x57, 0xba, 0xed, 0x1c, 0xeb, 0xc7, 0xb1, 0x7d, 0xb7, 0x80, 0x90, 0x83, 0xa1, 0x1b,
0xab, 0x81, 0x1c, 0x7f, 0x5c, 0x6f, 0x3d, 0x7b, 0xb3, 0xf5, 0xec, 0xcb, 0xad, 0x67, 0xff, 0xda, 0x09, 0x32, 0xa2, 0x6a, 0xd7, 0x7d, 0xce, 0x42, 0x1a, 0x64, 0x3d, 0x16, 0x7c, 0xfd, 0xdc, 0xc2,
0x79, 0xd6, 0x66, 0xe7, 0x59, 0x67, 0x3b, 0xcf, 0xfa, 0x72, 0x38, 0x0b, 0x65, 0xd5, 0x4b, 0x68, 0x30, 0x5a, 0x8f, 0x05, 0xde, 0x75, 0xa8, 0x04, 0x3d, 0x3a, 0x7f, 0x96, 0xf0, 0xb2, 0x6e, 0x6f,
0x20, 0x0a, 0xd3, 0x1f, 0x07, 0xb6, 0x7a, 0x28, 0xc1, 0x13, 0xfd, 0x7f, 0x47, 0xd7, 0x01, 0x00, 0xbc, 0x43, 0xf8, 0x32, 0x7c, 0x23, 0x63, 0xc3, 0x2d, 0xf7, 0x9b, 0x5b, 0xb2, 0x7d, 0x73, 0xf3,
0x00, 0xff, 0xff, 0x67, 0x9d, 0xae, 0xd2, 0x51, 0x03, 0x00, 0x00, 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. // 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, // TotalBalance queries the balance of all coins, including x/distribution,
// x/community, and supplied balances. // x/community, and supplied balances.
TotalBalance(ctx context.Context, in *QueryTotalBalanceRequest, opts ...grpc.CallOption) (*QueryTotalBalanceResponse, error) 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 { type queryClient struct {
@ -283,6 +374,15 @@ func (c *queryClient) TotalBalance(ctx context.Context, in *QueryTotalBalanceReq
return out, nil 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. // QueryServer is the server API for Query service.
type QueryServer interface { type QueryServer interface {
// Balance queries the balance of all coins of x/community module. // 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, // TotalBalance queries the balance of all coins, including x/distribution,
// x/community, and supplied balances. // x/community, and supplied balances.
TotalBalance(context.Context, *QueryTotalBalanceRequest) (*QueryTotalBalanceResponse, error) 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. // 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) { func (*UnimplementedQueryServer) TotalBalance(ctx context.Context, req *QueryTotalBalanceRequest) (*QueryTotalBalanceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method TotalBalance not implemented") 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) { func RegisterQueryServer(s grpc1.Server, srv QueryServer) {
s.RegisterService(&_Query_serviceDesc, srv) 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) 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{ var _Query_serviceDesc = grpc.ServiceDesc{
ServiceName: "kava.community.v1beta1.Query", ServiceName: "kava.community.v1beta1.Query",
HandlerType: (*QueryServer)(nil), HandlerType: (*QueryServer)(nil),
@ -355,6 +479,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{
MethodName: "TotalBalance", MethodName: "TotalBalance",
Handler: _Query_TotalBalance_Handler, Handler: _Query_TotalBalance_Handler,
}, },
{
MethodName: "AnnualizedRewards",
Handler: _Query_AnnualizedRewards_Handler,
},
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
Metadata: "kava/community/v1beta1/query.proto", Metadata: "kava/community/v1beta1/query.proto",
@ -480,6 +608,62 @@ func (m *QueryTotalBalanceResponse) MarshalToSizedBuffer(dAtA []byte) (int, erro
return len(dAtA) - i, nil 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 { func encodeVarintQuery(dAtA []byte, offset int, v uint64) int {
offset -= sovQuery(v) offset -= sovQuery(v)
base := offset base := offset
@ -539,6 +723,26 @@ func (m *QueryTotalBalanceResponse) Size() (n int) {
return n 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) { func sovQuery(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7 return (math_bits.Len64(x|1) + 6) / 7
} }
@ -813,6 +1017,140 @@ func (m *QueryTotalBalanceResponse) Unmarshal(dAtA []byte) error {
} }
return nil 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) { func skipQuery(dAtA []byte) (n int, err error) {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0

View File

@ -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". // RegisterQueryHandlerServer registers the http handlers for service Query to "mux".
// UnaryRPC :call QueryServer directly. // UnaryRPC :call QueryServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. // 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 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 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_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_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 ( var (
forward_Query_Balance_0 = runtime.ForwardResponseMessage forward_Query_Balance_0 = runtime.ForwardResponseMessage
forward_Query_TotalBalance_0 = runtime.ForwardResponseMessage forward_Query_TotalBalance_0 = runtime.ForwardResponseMessage
forward_Query_AnnualizedRewards_0 = runtime.ForwardResponseMessage
) )