0g-chain/x/incentive/keeper/querier.go
drklee3 f757d7ab15
feat: upgrade to Cosmos v0.46 (#1477)
* Update cosmos-sdk to v0.45.10-kava

* Add RegisterNodeService to app

* Update cosmos proto files

* Update cosmos proto files

* Use tagged v0.45.10-kava-v0.19-0.21 cosmos version

* update x/auth/legacy to x/auth/migrations

* Delete rest packages and registration

* Remove rest from proposal handlers

* Remove legacy types referencing removed sdk types

* Remove legacy tx broadcast handler

* Update incentive staking hooks to return error

* Remove grpc replace directive, use new grpc version

* Fix storetypes import

* Update tally_handler with updated gov types

* Delete legacy types

* Use new gov default config

* Update RegisterTendermintService params

Signed-off-by: drklee3 <derrick@dlee.dev>

* Replace sdk.StoreKey with storetypes.StoreKey

* Replace sdk.Int#ToDec with sdk.NewDecFromInt

* Replace sdk.NewUintFromBigInt with sdkmath.NewUintFromBigInt

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update most intances of govtypes to govv1beta1

* Unpack coin slice for Coins#Sub and Coins#SafeSub

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update committee gov codec registration

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update migrate utils period_vesting Coins#Sub

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update Coin#Sub in community proposal handler

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update Coin#Sub, FundModuleAccount/FundAccount in banktestutil

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update community, earn, kavadist proposal gov registration

* Update evm cli client EthSecp256k1Type check

* AccAddressFromHex to AccAddressFromHexUnsafe

* Add mint DefaultInflationCalculationFn to earn test

* Update use of removed staking.NewHandler

* Rename FlagIAVLFastNode -> FlagDisableIAVLFastNode

* cmd: Update new snapshot app option

Signed-off-by: drklee3 <derrick@dlee.dev>

* cmd: Add tendermint default config, use cosmos rpc status command

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update ethermint import path

github.com/tharsis/ethermint -> github.com/evmos/ethermint

* Upgrade ibc-go to v6

* Update proto dependencies

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update Tally handler test with new gov types

* Update helpers.GenTx -> helpers.GenSignedMockTx

* Update evmkeeper.NewKeeper params

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update ante authz, tests

* Add feemarket transient key, pass subspaces to evm/feemarket keepers

* Update new ante decorators

* Add new addModuleInitFlags to server commands

* Pass codec to keyring.New in genaccounts

* Pass codec to client keys add

* Add SendCoins to evmutil bank_keeper

* Use github.com/cosmos/iavl@v0.19.5

* Add ante HandlerOptions

* Add unimplemented SendCoins to evmutil bank keeper

Ethermint x/evm does not use this method

* Update init-new-chain script to disable post-london blocks

* Modify test genesis states to append 1 validator

* Update tally handler test to use string values

* Prevent querying balance for empty sdk.AccAddress in auction bidding test

* Set default bond denom to ukava

* Remove overwritten bank genesis total supply in committee proposal test

Signed-off-by: drklee3 <derrick@dlee.dev>

* Use ukava for testing staked balance

* Disable minting in community proposal handler test

Previously stake denom is used, which resulted in 0 minted coins

* Update hard APYToSPY test expected value

Increased iterations in sdk.ApproxRoot, updated closer to real value

* Fix NewDecCoinsFromCoins bug in incentive collectDerivativeStakingRewards

* Allow bkava earn incentive test values to match within small margin for rounding

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update invalid denom in issuance message coin validation

Colons are now valid in denoms

Signed-off-by: drklee3 <derrick@dlee.dev>

* Remove genesis validator in incentive delegation tests

* Update pricefeed market test for invalid denom

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update incentive delegator rewards test without genesis validator

Signed-off-by: drklee3 <derrick@dlee.dev>

* Add validator to export test

* Clear bank state in minting tests

Signed-off-by: drklee3 <derrick@dlee.dev>

* Remove validator for no stake tally test

Signed-off-by: drklee3 <derrick@dlee.dev>

* Clear incentive state before InitGenesis in incentive genesis export test

* Update swagger

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update ethermint version to match replaced version

* Remove legacy swagger

* Add NewEthEmitEventDecorator

* Remove redundant func for AddModuleInitFlags

* Remove unused addBankBalanceForAddress func

* Add SetIAVLLazyLoading option to app cmd

* Use legacy.RegisterAminoMsg for committee msg concrete registration

* Remove unnecessary Amino field

* Add evm_util bankkeeper SendCoins comment

* Update test method ResetBankState to DeleteGenesisValidatorCoins to be more clear

* Validate incentive params.RewardsPerSecond to be non-zero

* Validate swap pools to disallow colons in token denoms

* Register all legacy amino types on gov modulecdc

* Remove redundant Comittee interface registration

* Pin goleveldb to v1.0.1-0.20210819022825-2ae1ddf74ef7

Causes failed to load state at height errors

* Update ethermint to new pinned version with minGasPrices parse error fix

* Update cosmos fork dependcy commit to include reverted account constructor patch

* Update Cosmos v0.46.11 and cometbft v0.34.27

* Bump minimum go version to 1.19

* Update tendermint proto

* Update internal testnet genesis

* Move NewCanTransferDecorator before NewEthGasConsumeDecorator

* Add hard borrow store tests (#1514)

* add store tests for Borrow type

* refactor Deposit tests to match

* Fix old bep3 tests (#1515)

* Update Ethermint to 1b17445 to fix duplicate proto registration

* Add custom status command to use snake_case and stdout

* Add SetInflation helper

* Reduce ambiguity with evm CanSignEthTx error

* Remove init genesis validator claim in test

* Add disabled evmante.NewMinGasPriceDecorator with x/feemarket note

* chore: use tagged versions for Cosmos and Ethermint forks

* update kvtool & increase wait for ibc transfer test

---------

Signed-off-by: drklee3 <derrick@dlee.dev>
Co-authored-by: Ruaridh <rhuairahrighairidh@users.noreply.github.com>
Co-authored-by: Robert Pirtle <astropirtle@gmail.com>
2023-04-03 20:08:45 -04:00

550 lines
17 KiB
Go

package keeper
import (
"fmt"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
abci "github.com/tendermint/tendermint/abci/types"
earntypes "github.com/kava-labs/kava/x/earn/types"
"github.com/kava-labs/kava/x/incentive/types"
liquidtypes "github.com/kava-labs/kava/x/liquid/types"
)
const (
SecondsPerYear = 31536000
)
// NewQuerier is the module level router for state queries
func NewQuerier(k Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier {
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err error) {
switch path[0] {
case types.QueryGetParams:
return queryGetParams(ctx, req, k, legacyQuerierCdc)
case types.QueryGetHardRewards:
return queryGetHardRewards(ctx, req, k, legacyQuerierCdc)
case types.QueryGetUSDXMintingRewards:
return queryGetUSDXMintingRewards(ctx, req, k, legacyQuerierCdc)
case types.QueryGetDelegatorRewards:
return queryGetDelegatorRewards(ctx, req, k, legacyQuerierCdc)
case types.QueryGetSwapRewards:
return queryGetSwapRewards(ctx, req, k, legacyQuerierCdc)
case types.QueryGetSavingsRewards:
return queryGetSavingsRewards(ctx, req, k, legacyQuerierCdc)
case types.QueryGetRewardFactors:
return queryGetRewardFactors(ctx, req, k, legacyQuerierCdc)
case types.QueryGetEarnRewards:
return queryGetEarnRewards(ctx, req, k, legacyQuerierCdc)
case types.QueryGetAPYs:
return queryGetAPYs(ctx, req, k, legacyQuerierCdc)
default:
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint", types.ModuleName)
}
}
}
// query params in the store
func queryGetParams(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) {
// Get params
params := k.GetParams(ctx)
// Encode results
bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, params)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}
func queryGetHardRewards(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) {
var params types.QueryRewardsParams
err := legacyQuerierCdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
owner := len(params.Owner) > 0
var hardClaims types.HardLiquidityProviderClaims
switch {
case owner:
hardClaim, foundHardClaim := k.GetHardLiquidityProviderClaim(ctx, params.Owner)
if foundHardClaim {
hardClaims = append(hardClaims, hardClaim)
}
default:
hardClaims = k.GetAllHardLiquidityProviderClaims(ctx)
}
var paginatedHardClaims types.HardLiquidityProviderClaims
startH, endH := client.Paginate(len(hardClaims), params.Page, params.Limit, 100)
if startH < 0 || endH < 0 {
paginatedHardClaims = types.HardLiquidityProviderClaims{}
} else {
paginatedHardClaims = hardClaims[startH:endH]
}
if !params.Unsynchronized {
for i, claim := range paginatedHardClaims {
paginatedHardClaims[i] = k.SimulateHardSynchronization(ctx, claim)
}
}
// Marshal Hard claims
bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, paginatedHardClaims)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}
func queryGetUSDXMintingRewards(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) {
var params types.QueryRewardsParams
err := legacyQuerierCdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
owner := len(params.Owner) > 0
var usdxMintingClaims types.USDXMintingClaims
switch {
case owner:
usdxMintingClaim, foundUsdxMintingClaim := k.GetUSDXMintingClaim(ctx, params.Owner)
if foundUsdxMintingClaim {
usdxMintingClaims = append(usdxMintingClaims, usdxMintingClaim)
}
default:
usdxMintingClaims = k.GetAllUSDXMintingClaims(ctx)
}
var paginatedUsdxMintingClaims types.USDXMintingClaims
startU, endU := client.Paginate(len(usdxMintingClaims), params.Page, params.Limit, 100)
if startU < 0 || endU < 0 {
paginatedUsdxMintingClaims = types.USDXMintingClaims{}
} else {
paginatedUsdxMintingClaims = usdxMintingClaims[startU:endU]
}
if !params.Unsynchronized {
for i, claim := range paginatedUsdxMintingClaims {
paginatedUsdxMintingClaims[i] = k.SimulateUSDXMintingSynchronization(ctx, claim)
}
}
// Marshal USDX minting claims
bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, paginatedUsdxMintingClaims)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}
func queryGetDelegatorRewards(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) {
var params types.QueryRewardsParams
err := legacyQuerierCdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
owner := len(params.Owner) > 0
var delegatorClaims types.DelegatorClaims
switch {
case owner:
delegatorClaim, foundDelegatorClaim := k.GetDelegatorClaim(ctx, params.Owner)
if foundDelegatorClaim {
delegatorClaims = append(delegatorClaims, delegatorClaim)
}
default:
delegatorClaims = k.GetAllDelegatorClaims(ctx)
}
var paginatedDelegatorClaims types.DelegatorClaims
startH, endH := client.Paginate(len(delegatorClaims), params.Page, params.Limit, 100)
if startH < 0 || endH < 0 {
paginatedDelegatorClaims = types.DelegatorClaims{}
} else {
paginatedDelegatorClaims = delegatorClaims[startH:endH]
}
if !params.Unsynchronized {
for i, claim := range paginatedDelegatorClaims {
paginatedDelegatorClaims[i] = k.SimulateDelegatorSynchronization(ctx, claim)
}
}
// Marshal Hard claims
bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, paginatedDelegatorClaims)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}
func queryGetSwapRewards(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) {
var params types.QueryRewardsParams
err := legacyQuerierCdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
owner := len(params.Owner) > 0
var claims types.SwapClaims
switch {
case owner:
claim, found := k.GetSwapClaim(ctx, params.Owner)
if found {
claims = append(claims, claim)
}
default:
claims = k.GetAllSwapClaims(ctx)
}
var paginatedClaims types.SwapClaims
startH, endH := client.Paginate(len(claims), params.Page, params.Limit, 100)
if startH < 0 || endH < 0 {
paginatedClaims = types.SwapClaims{}
} else {
paginatedClaims = claims[startH:endH]
}
if !params.Unsynchronized {
for i, claim := range paginatedClaims {
syncedClaim, found := k.GetSynchronizedSwapClaim(ctx, claim.Owner)
if !found {
panic("previously found claim should still be found")
}
paginatedClaims[i] = syncedClaim
}
}
// Marshal claims
bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, paginatedClaims)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}
func queryGetSavingsRewards(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) {
var params types.QueryRewardsParams
err := legacyQuerierCdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
owner := len(params.Owner) > 0
var claims types.SavingsClaims
switch {
case owner:
claim, found := k.GetSavingsClaim(ctx, params.Owner)
if found {
claims = append(claims, claim)
}
default:
claims = k.GetAllSavingsClaims(ctx)
}
var paginatedClaims types.SavingsClaims
startH, endH := client.Paginate(len(claims), params.Page, params.Limit, 100)
if startH < 0 || endH < 0 {
paginatedClaims = types.SavingsClaims{}
} else {
paginatedClaims = claims[startH:endH]
}
if !params.Unsynchronized {
for i, claim := range paginatedClaims {
syncedClaim, found := k.GetSynchronizedSavingsClaim(ctx, claim.Owner)
if !found {
panic("previously found claim should still be found")
}
paginatedClaims[i] = syncedClaim
}
}
// Marshal claims
bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, paginatedClaims)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}
func queryGetEarnRewards(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) {
var params types.QueryRewardsParams
err := legacyQuerierCdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
owner := len(params.Owner) > 0
var claims types.EarnClaims
switch {
case owner:
claim, found := k.GetEarnClaim(ctx, params.Owner)
if found {
claims = append(claims, claim)
}
default:
claims = k.GetAllEarnClaims(ctx)
}
var paginatedClaims types.EarnClaims
startH, endH := client.Paginate(len(claims), params.Page, params.Limit, 100)
if startH < 0 || endH < 0 {
paginatedClaims = types.EarnClaims{}
} else {
paginatedClaims = claims[startH:endH]
}
if !params.Unsynchronized {
for i, claim := range paginatedClaims {
syncedClaim, found := k.GetSynchronizedEarnClaim(ctx, claim.Owner)
if !found {
panic("previously found claim should still be found")
}
paginatedClaims[i] = syncedClaim
}
}
// Marshal claims
bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, paginatedClaims)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}
func queryGetRewardFactors(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) {
var usdxFactors types.RewardIndexes
k.IterateUSDXMintingRewardFactors(ctx, func(collateralType string, factor sdk.Dec) (stop bool) {
usdxFactors = usdxFactors.With(collateralType, factor)
return false
})
var supplyFactors types.MultiRewardIndexes
k.IterateHardSupplyRewardIndexes(ctx, func(denom string, indexes types.RewardIndexes) (stop bool) {
supplyFactors = supplyFactors.With(denom, indexes)
return false
})
var borrowFactors types.MultiRewardIndexes
k.IterateHardBorrowRewardIndexes(ctx, func(denom string, indexes types.RewardIndexes) (stop bool) {
borrowFactors = borrowFactors.With(denom, indexes)
return false
})
var delegatorFactors types.MultiRewardIndexes
k.IterateDelegatorRewardIndexes(ctx, func(denom string, indexes types.RewardIndexes) (stop bool) {
delegatorFactors = delegatorFactors.With(denom, indexes)
return false
})
var swapFactors types.MultiRewardIndexes
k.IterateSwapRewardIndexes(ctx, func(poolID string, indexes types.RewardIndexes) (stop bool) {
swapFactors = swapFactors.With(poolID, indexes)
return false
})
var savingsFactors types.MultiRewardIndexes
k.IterateSavingsRewardIndexes(ctx, func(denom string, indexes types.RewardIndexes) (stop bool) {
savingsFactors = savingsFactors.With(denom, indexes)
return false
})
var earnFactors types.MultiRewardIndexes
k.IterateEarnRewardIndexes(ctx, func(denom string, indexes types.RewardIndexes) (stop bool) {
earnFactors = earnFactors.With(denom, indexes)
return false
})
response := types.NewQueryGetRewardFactorsResponse(
usdxFactors,
supplyFactors,
borrowFactors,
delegatorFactors,
swapFactors,
savingsFactors,
earnFactors,
)
bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, response)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}
func queryGetAPYs(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) {
params := k.GetParams(ctx)
var apys types.APYs
// bkava APY (staking + incentive rewards)
stakingAPR, err := GetStakingAPR(ctx, k, params)
if err != nil {
return nil, err
}
apys = append(apys, types.NewAPY(liquidtypes.DefaultDerivativeDenom, stakingAPR))
// Incentive only APYs
for _, param := range params.EarnRewardPeriods {
// Skip bkava as it's calculated earlier with staking rewards
if param.CollateralType == liquidtypes.DefaultDerivativeDenom {
continue
}
// Value in the vault in the same denom as CollateralType
vaultTotalValue, err := k.earnKeeper.GetVaultTotalValue(ctx, param.CollateralType)
if err != nil {
return nil, err
}
apy, err := GetAPYFromMultiRewardPeriod(ctx, k, param.CollateralType, param, vaultTotalValue.Amount)
if err != nil {
return nil, err
}
apys = append(apys, types.NewAPY(param.CollateralType, apy))
}
// Marshal APYs
res := types.NewQueryGetAPYsResponse(apys)
bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, res)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}
// GetStakingAPR returns the total APR for staking and incentive rewards
func GetStakingAPR(ctx sdk.Context, k Keeper, params types.Params) (sdk.Dec, error) {
// Get staking APR + incentive APR
inflationRate := k.mintKeeper.GetMinter(ctx).Inflation
communityTax := k.distrKeeper.GetCommunityTax(ctx)
bondedTokens := k.stakingKeeper.TotalBondedTokens(ctx)
circulatingSupply := k.bankKeeper.GetSupply(ctx, types.BondDenom)
// Staking APR = (Inflation Rate * (1 - Community Tax)) / (Bonded Tokens / Circulating Supply)
stakingAPR := inflationRate.
Mul(sdk.OneDec().Sub(communityTax)).
Quo(sdk.NewDecFromInt(bondedTokens).
Quo(sdk.NewDecFromInt(circulatingSupply.Amount)))
// Get incentive APR
bkavaRewardPeriod, found := params.EarnRewardPeriods.GetMultiRewardPeriod(liquidtypes.DefaultDerivativeDenom)
if !found {
// No incentive rewards for bkava, only staking rewards
return stakingAPR, nil
}
// Total amount of bkava in earn vaults, this may be lower than total bank
// supply of bkava as some bkava may not be deposited in earn vaults
totalEarnBkavaDeposited := sdk.ZeroInt()
var iterErr error
k.earnKeeper.IterateVaultRecords(ctx, func(record earntypes.VaultRecord) (stop bool) {
if !k.liquidKeeper.IsDerivativeDenom(ctx, record.TotalShares.Denom) {
return false
}
vaultValue, err := k.earnKeeper.GetVaultTotalValue(ctx, record.TotalShares.Denom)
if err != nil {
iterErr = err
return false
}
totalEarnBkavaDeposited = totalEarnBkavaDeposited.Add(vaultValue.Amount)
return false
})
if iterErr != nil {
return sdk.ZeroDec(), iterErr
}
// Incentive APR = rewards per second * seconds per year / total supplied to earn vaults
// Override collateral type to use "kava" instead of "bkava" when fetching
incentiveAPY, err := GetAPYFromMultiRewardPeriod(ctx, k, types.BondDenom, bkavaRewardPeriod, totalEarnBkavaDeposited)
if err != nil {
return sdk.ZeroDec(), err
}
totalAPY := stakingAPR.Add(incentiveAPY)
return totalAPY, nil
}
// GetAPYFromMultiRewardPeriod calculates the APY for a given MultiRewardPeriod
func GetAPYFromMultiRewardPeriod(
ctx sdk.Context,
k Keeper,
collateralType string,
rewardPeriod types.MultiRewardPeriod,
totalSupply sdk.Int,
) (sdk.Dec, error) {
if totalSupply.IsZero() {
return sdk.ZeroDec(), nil
}
// Get USD value of collateral type
collateralUSDValue, err := k.pricefeedKeeper.GetCurrentPrice(ctx, getMarketID(collateralType))
if err != nil {
return sdk.ZeroDec(), fmt.Errorf(
"failed to get price for incentive collateralType %s with market ID %s: %w",
collateralType, getMarketID(collateralType), err,
)
}
// Total USD value of the collateral type total supply
totalSupplyUSDValue := sdk.NewDecFromInt(totalSupply).Mul(collateralUSDValue.Price)
totalUSDRewardsPerSecond := sdk.ZeroDec()
// In many cases, RewardsPerSecond are assets that are different from the
// CollateralType, so we need to use the USD value of CollateralType and
// RewardsPerSecond to determine the APY.
for _, reward := range rewardPeriod.RewardsPerSecond {
// Get USD value of 1 unit of reward asset type, using TWAP
rewardDenomUSDValue, err := k.pricefeedKeeper.GetCurrentPrice(ctx, getMarketID(reward.Denom))
if err != nil {
return sdk.ZeroDec(), fmt.Errorf("failed to get price for RewardsPerSecond asset %s: %w", reward.Denom, err)
}
rewardPerSecond := sdk.NewDecFromInt(reward.Amount).Mul(rewardDenomUSDValue.Price)
totalUSDRewardsPerSecond = totalUSDRewardsPerSecond.Add(rewardPerSecond)
}
// APY = USD rewards per second * seconds per year / USD total supplied
apy := totalUSDRewardsPerSecond.
MulInt64(SecondsPerYear).
Quo(totalSupplyUSDValue)
return apy, nil
}
func getMarketID(denom string) string {
// Rewrite denoms as pricefeed has different names for some assets,
// e.g. "ukava" -> "kava", "erc20/multichain/usdc" -> "usdc"
// bkava is not included as it is handled separately
// TODO: Replace hardcoded conversion with possible params set somewhere
// to be more flexible. E.g. a map of denoms to pricefeed market denoms in
// pricefeed params.
switch denom {
case types.BondDenom:
denom = "kava"
case "erc20/multichain/usdc":
denom = "usdc"
case "erc20/multichain/usdt":
denom = "usdt"
case "erc20/multichain/dai":
denom = "dai"
}
return fmt.Sprintf("%s:usd:30", denom)
}