Skip to content

Commit

Permalink
add current rates query for frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
toteki committed Apr 25, 2023
1 parent 28feba5 commit 06b4a55
Show file tree
Hide file tree
Showing 5 changed files with 713 additions and 61 deletions.
31 changes: 31 additions & 0 deletions proto/umee/incentive/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ service Query {
returns (QueryIncentiveProgramResponse) {
option (google.api.http).get = "/umee/incentive/v1/incentive_program/{id}";
}

// CurrentRates queries the hypothetical return of a bonded uToken denomination
// if current incentive rewards continued for one year. The response is an sdk.Coins
// of base token rewards, per reference amount (usually 10^exponent of the uToken.)
rpc CurrentRates(QueryCurrentRates)
returns (QueryCurrentRatesResponse) {
option (google.api.http).get = "/umee/incentive/v1/current_rates";
}
}

// QueryParams defines the request structure for the Params gRPC service
Expand Down Expand Up @@ -187,3 +195,26 @@ message QueryIncentiveProgram {
message QueryIncentiveProgramResponse {
IncentiveProgram program = 1 [(gogoproto.nullable) = false];
}

// QueryCurrentRates defines the request structure for the CurrentRates gRPC service handler.
message QueryCurrentRates {
// uToken is the uToken denomination whose current annual rate of rewards is being queried
string uToken = 1;
}

// QueryCurrentRatesResponse defines the response structure for the CurrentRates gRPC service handler.
message QueryCurrentRatesResponse {
// Reference Bond is an amount of bonded uTokens (usually 10^exponent) whose current rewards are being
// calculated. This amount can be used to compute an individual user's rewards: for example, if a user has
// 2.5x the reference amount currently bonded, then they would receive 2.5x the rewards below annually
// at current rates.
cosmos.base.v1beta1.Coin reference_bond = 1 [
(gogoproto.nullable) = false
];
// Rewards are the amount of base token rewards that the reference amount of bonded uTokens would earn
// if current rates continued for a full year.
repeated cosmos.base.v1beta1.Coin rewards = 2 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
}
32 changes: 32 additions & 0 deletions x/incentive/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func GetQueryCmd() *cobra.Command {
cmd.AddCommand(
GetCmdQueryParams(),
GetCmdQueryAccountBonds(),
GetCmdQueryCurrentRates(),
GetCmdQueryTotalBonded(),
GetCmdQueryTotalUnbonding(),
GetCmdQueryPendingRewards(),
Expand Down Expand Up @@ -110,6 +111,37 @@ func GetCmdQueryPendingRewards() *cobra.Command {
return cmd
}

// GetCmdQueryCurrentRates creates a Cobra command to query current annual rewards for a reference amount
// of a given bonded uToken.
func GetCmdQueryCurrentRates() *cobra.Command {
cmd := &cobra.Command{
Use: "current-rates[denom]",
Args: cobra.RangeArgs(0, 1),
Short: "Query the current annual rewards for a reference amount of a given bonded uToken.",
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
denom := ""
if len(args) > 0 {
denom = args[0]
}

queryClient := incentive.NewQueryClient(clientCtx)
resp, err := queryClient.CurrentRates(cmd.Context(), &incentive.QueryCurrentRates{UToken: denom})
if err != nil {
return err
}

return cli.PrintOrErr(resp, err, clientCtx)
},
}

flags.AddQueryFlagsToCmd(cmd)
return cmd
}

// GetCmdQueryTotalBonded creates a Cobra command to query bonded tokens across all users.
func GetCmdQueryTotalBonded() *cobra.Command {
cmd := &cobra.Command{
Expand Down
45 changes: 45 additions & 0 deletions x/incentive/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,48 @@ func (q Querier) TotalUnbonding(

return &incentive.QueryTotalUnbondingResponse{Unbonding: total}, nil
}

func (q Querier) CurrentRates(
goCtx context.Context,
req *incentive.QueryCurrentRates,
) (*incentive.QueryCurrentRatesResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

k, ctx := q.Keeper, sdk.UnwrapSDKContext(goCtx)

programs, err := k.getAllIncentivePrograms(ctx, incentive.ProgramStatusOngoing)
if err != nil {
return nil, err
}

// to compute the rewards a reference amount (10^exponent) of bonded uToken is currently earning,
// we need to divide the total rewards being distributed by all ongoing incentive programs targeting
// that uToken denom, by the ratio of the total bonded amount to the reference amount.
bonded := k.getTotalBonded(ctx, req.UToken)
rewards := sdk.NewCoins()
exponent := k.getRewardAccumulator(ctx, req.UToken).Exponent
for _, p := range programs {
if p.UToken == req.UToken {
// seconds per year / duration = programsPerYear (as this query assumes incentives will stay constant)
programsPerYear := sdk.MustNewDecFromStr("31557600").Quo(sdk.NewDec(p.Duration))
// reference amount / total bonded = rewardPortion (as the more uTokens bond, the fewer rewards each earns)
rewardPortion := ten.Power(uint64(exponent)).QuoInt(bonded.Amount)
// annual rewards for reference amount for this specific program, assuming current rates continue
rewardCoin := sdk.NewCoin(
p.TotalRewards.Denom,
programsPerYear.Mul(rewardPortion).MulInt(p.TotalRewards.Amount).TruncateInt(),
)
// add this program's annual rewards to the total for all programs incentivizing this uToken denom
rewards = rewards.Add(rewardCoin)
}
}
return &incentive.QueryCurrentRatesResponse{
ReferenceBond: sdk.NewCoin(
req.UToken,
ten.Power(uint64(exponent)).TruncateInt(),
),
Rewards: rewards,
}, nil
}
Loading

0 comments on commit 06b4a55

Please sign in to comment.