Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: x/metoken balances and prices query improvements #2198

Merged
merged 8 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/wasm/query/handle_metoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,6 @@ func (q UmeeQuery) HandleMeTokenIndexPrice(
ctx context.Context,
qs metoken.QueryServer,
) (proto.Message, error) {
req := metoken.QueryIndexPrice{MetokenDenom: q.IndexPrice.MetokenDenom}
return qs.IndexPrice(ctx, &req)
req := metoken.QueryIndexPrices{MetokenDenom: q.IndexPrice.MetokenDenom}
return qs.IndexPrices(ctx, &req)
}
2 changes: 1 addition & 1 deletion app/wasm/query/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ type UmeeQuery struct {
SwapFee *metoken.QuerySwapFee `json:"metoken_swapfee,omitempty"`
RedeemFee *metoken.QueryRedeemFee `json:"metoken_redeemfee,omitempty"`
IndexBalances *metoken.QueryIndexBalances `json:"metoken_indexbalances,omitempty"`
IndexPrice *metoken.QueryIndexPrice `json:"metoken_indexprice,omitempty"`
IndexPrice *metoken.QueryIndexPrices `json:"metoken_indexprice,omitempty"`
}

// MarshalResponse marshals any response.
Expand Down
2 changes: 1 addition & 1 deletion app/wasm/query/whitelist.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func init() {
setWhitelistedQuery(metokenBaseQueryPath+"SwapFee", &metoken.QuerySwapFeeResponse{})
setWhitelistedQuery(metokenBaseQueryPath+"RedeemFee", &metoken.QueryRedeemFeeResponse{})
setWhitelistedQuery(metokenBaseQueryPath+"IndexBalances", &metoken.QueryIndexBalancesResponse{})
setWhitelistedQuery(metokenBaseQueryPath+"IndexPrice", &metoken.QueryIndexPriceResponse{})
setWhitelistedQuery(metokenBaseQueryPath+"IndexPrices", &metoken.QueryIndexPricesResponse{})
}

// GetWhitelistedQuery returns the whitelisted query at the provided path.
Expand Down
37 changes: 35 additions & 2 deletions proto/umee/metoken/v1/metoken.proto
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ message AcceptedAsset {
];
}

// Price is used to inform meToken price and exponent.
message Price {
// IndexPrices provides meToken price related index information.
message IndexPrices {
kosegor marked this conversation as resolved.
Show resolved Hide resolved
option (gogoproto.equal) = true;

// meToken denom.
Expand All @@ -120,4 +120,37 @@ message Price {
// Exponent is the power of ten by which to multiply, in order to convert
// an amount of the meToken for the exchange operations.
uint32 exponent = 3;

repeated AssetPrice assets = 4 [(gogoproto.nullable) = false];
}

// AssetPrice information related to the index operations.
message AssetPrice {
option (gogoproto.equal) = true;

string base_denom = 1;

string symbol_denom = 2;

// Price in USD of one unit of asset, expressed in decimals.
string price = 3 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];

// Exponent is the power of ten by which to multiply, in order to convert
// an amount of the asset for the exchange operations.
uint32 exponent = 4;

// SwapRate used for exchange calculations asset -> meToken.
string swap_rate = 5 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];

// RedeemRate used for exchange calculations meToken -> asset.
string redeem_rate = 6 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}
20 changes: 11 additions & 9 deletions proto/umee/metoken/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ service Query {
option (google.api.http).get = "/umee/metoken/v1/index_balances";
}

// IndexPrice queries for Index's price of a specific or all the registered indexes.
rpc IndexPrice(QueryIndexPrice)
returns (QueryIndexPriceResponse) {
option (google.api.http).get = "/umee/metoken/v1/index_price";
// IndexPrices queries for Index's price of a specific or all the registered indexes. It also includes the
// underlying assets prices as well as swap and redeem rates.
rpc IndexPrices(QueryIndexPrices)
returns (QueryIndexPricesResponse) {
option (google.api.http).get = "/umee/metoken/v1/index_prices";
}
}

Expand Down Expand Up @@ -101,15 +102,16 @@ message QueryIndexBalances {
// QueryIndexBalanceResponse defines the response structure for the IndexBalances gRPC service handler.
message QueryIndexBalancesResponse {
repeated IndexBalances index_balances = 1 [(gogoproto.nullable) = false];
repeated IndexPrices prices = 2 [(gogoproto.nullable) = false];
kosegor marked this conversation as resolved.
Show resolved Hide resolved
}

// QueryIndexePrice defines the request structure for the IndexPrice gRPC service handler.
// QueryIndexPrices defines the request structure for the IndexPrice gRPC service handler.
// metoken_denom param is optional, if it is not informed the query will return all the Index's prices.
message QueryIndexPrice {
message QueryIndexPrices {
string metoken_denom = 1;
}

// QueryIndexPriceResponse defines the response structure for the IndexPrice gRPC service handler.
message QueryIndexPriceResponse {
repeated Price prices = 1 [(gogoproto.nullable) = false];
// QueryIndexPriceResponses defines the response structure for the IndexPrice gRPC service handler.
kosegor marked this conversation as resolved.
Show resolved Hide resolved
message QueryIndexPricesResponse {
repeated IndexPrices prices = 1 [(gogoproto.nullable) = false];
kosegor marked this conversation as resolved.
Show resolved Hide resolved
}
6 changes: 3 additions & 3 deletions x/metoken/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func GetCmdIndexBalances() *cobra.Command {
return cmd
}

// GetCmdIndexPrice creates a Cobra command to query for the x/metoken module Index Price.
// GetCmdIndexPrice creates a Cobra command to query for the x/metoken module Index Prices.
// metoken_denom is optional, if it isn't provided then prices for all the registered indexes will be returned.
func GetCmdIndexPrice() *cobra.Command {
cmd := &cobra.Command{
Expand All @@ -195,11 +195,11 @@ func GetCmdIndexPrice() *cobra.Command {
}

queryClient := metoken.NewQueryClient(clientCtx)
queryReq := metoken.QueryIndexPrice{}
queryReq := metoken.QueryIndexPrices{}
if len(args) > 0 {
queryReq.MetokenDenom = args[0]
}
resp, err := queryClient.IndexPrice(cmd.Context(), &queryReq)
resp, err := queryClient.IndexPrices(cmd.Context(), &queryReq)
return cli.PrintOrErr(resp, err, clientCtx)
},
}
Expand Down
8 changes: 4 additions & 4 deletions x/metoken/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,19 @@ func (ib IndexBalances) Validate() error {

// AssetBalance returns an asset balance and its index from balances, given a specific denom.
// If it isn't present, -1 as index.
func (ib IndexBalances) AssetBalance(denom string) (int, AssetBalance) {
func (ib IndexBalances) AssetBalance(denom string) (AssetBalance, int) {
for i, balance := range ib.AssetBalances {
if balance.Denom == denom {
return i, balance
return balance, i
}
}

return -1, AssetBalance{}
return AssetBalance{}, -1
}

// SetAssetBalance overrides an asset balance if exists in the list, otherwise add it to the list.
func (ib *IndexBalances) SetAssetBalance(balance AssetBalance) {
i, _ := ib.AssetBalance(balance.Denom)
_, i := ib.AssetBalance(balance.Denom)

if i < 0 {
ib.AssetBalances = append(ib.AssetBalances, balance)
Expand Down
8 changes: 4 additions & 4 deletions x/metoken/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,13 @@ func (aa AcceptedAsset) Validate() error {

// AcceptedAsset returns an accepted asset and its index in the list, given a specific denom. If it isn't present,
// returns -1.
func (i Index) AcceptedAsset(denom string) (int, AcceptedAsset) {
func (i Index) AcceptedAsset(denom string) (AcceptedAsset, int) {
for index, aa := range i.AcceptedAssets {
if aa.Denom == denom {
return index, aa
return aa, index
}
}
return -1, AcceptedAsset{}
return AcceptedAsset{}, -1
}

// HasAcceptedAsset returns true if an accepted asset is present in the index. Otherwise returns false.
Expand All @@ -192,7 +192,7 @@ func (i Index) HasAcceptedAsset(denom string) bool {

// SetAcceptedAsset overrides an accepted asset if exists in the list, otherwise add it to the list.
func (i *Index) SetAcceptedAsset(acceptedAsset AcceptedAsset) {
index, _ := i.AcceptedAsset(acceptedAsset.Denom)
_, index := i.AcceptedAsset(acceptedAsset.Denom)
if index < 0 {
i.AcceptedAssets = append(i.AcceptedAssets, acceptedAsset)
return
Expand Down
6 changes: 3 additions & 3 deletions x/metoken/index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ func TestIndex_Update(t *testing.T) {
index := validIndex()
assert.Check(t, len(index.AcceptedAssets) == 1)

i, _ := index.AcceptedAsset(existingAsset)
_, i := index.AcceptedAsset(existingAsset)
assert.Check(t, i >= 0)

i, _ = index.AcceptedAsset(newAsset)
_, i = index.AcceptedAsset(newAsset)
assert.Check(t, i == -1)

newAcceptedAsset := validAcceptedAsset(newAsset)
Expand All @@ -114,7 +114,7 @@ func TestIndex_Update(t *testing.T) {
newAcceptedAsset.ReservePortion = sdk.MustNewDecFromStr("0.5")
index.SetAcceptedAsset(newAcceptedAsset)

i, asset := index.AcceptedAsset(newAcceptedAsset.Denom)
asset, i := index.AcceptedAsset(newAcceptedAsset.Denom)
assert.Check(t, i >= 0)
assert.Check(t, sdk.MustNewDecFromStr("0.5").Equal(asset.ReservePortion))
}
Expand Down
14 changes: 5 additions & 9 deletions x/metoken/keeper/fee.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func (k Keeper) swapFee(index metoken.Index, indexPrices metoken.IndexPrices, as
sdk.Coin,
error,
) {
i, assetSettings := index.AcceptedAsset(asset.Denom)
assetSettings, i := index.AcceptedAsset(asset.Denom)
if i < 0 {
return sdk.Coin{}, sdkerrors.ErrNotFound.Wrapf("asset %s is not accepted in the index", asset.Denom)
}
Expand Down Expand Up @@ -41,7 +41,7 @@ func (k Keeper) redeemFee(index metoken.Index, indexPrices metoken.IndexPrices,
sdk.Coin,
error,
) {
i, assetSettings := index.AcceptedAsset(asset.Denom)
assetSettings, i := index.AcceptedAsset(asset.Denom)
if i < 0 {
return sdk.Coin{}, sdkerrors.ErrNotFound.Wrapf("asset %s is not accepted in the index", asset.Denom)
}
Expand Down Expand Up @@ -76,7 +76,7 @@ func (k Keeper) currentAllocation(
return sdk.Dec{}, err
}

i, balance := balances.AssetBalance(assetDenom)
balance, i := balances.AssetBalance(assetDenom)
if i < 0 {
return sdk.Dec{}, sdkerrors.ErrNotFound.Wrapf("balance for denom %s not found", assetDenom)
}
Expand All @@ -91,7 +91,7 @@ func (k Keeper) currentAllocation(
return sdk.ZeroDec(), nil
}

assetPrice, err := indexPrices.Price(assetDenom)
assetPrice, err := indexPrices.PriceByBaseDenom(assetDenom)
if err != nil {
return sdk.Dec{}, err
}
Expand All @@ -100,11 +100,7 @@ func (k Keeper) currentAllocation(
return sdk.Dec{}, err
}

meTokenPrice, err := indexPrices.Price(index.Denom)
if err != nil {
return sdk.Dec{}, err
}
meTokenUSD, err := valueInUSD(balances.MetokenSupply.Amount, meTokenPrice.Price, meTokenPrice.Exponent)
meTokenUSD, err := valueInUSD(balances.MetokenSupply.Amount, indexPrices.Price, indexPrices.Exponent)
if err != nil {
return sdk.Dec{}, err
}
Expand Down
4 changes: 2 additions & 2 deletions x/metoken/keeper/fee_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestSwapFee(t *testing.T) {

// target_allocation = 0 -> fee = max_fee * coin_amount
// fee = 0.5 * 10 = 5
i, usdtAsset := index.AcceptedAsset(mocks.USDTBaseDenom)
usdtAsset, i := index.AcceptedAsset(mocks.USDTBaseDenom)
require.True(t, i >= 0)

usdtAsset.TargetAllocation = sdk.ZeroDec()
Expand Down Expand Up @@ -65,7 +65,7 @@ func TestRedeemFee(t *testing.T) {

// target_allocation = 0 -> fee = min_fee * coin_amount
// fee = 0.01 * 10 = 0.1
i, usdtAsset := index.AcceptedAsset(mocks.USDTBaseDenom)
usdtAsset, i := index.AcceptedAsset(mocks.USDTBaseDenom)
require.True(t, i >= 0)

usdtAsset.TargetAllocation = sdk.ZeroDec()
Expand Down
47 changes: 28 additions & 19 deletions x/metoken/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,30 +134,46 @@ func (q Querier) IndexBalances(
balances = []metoken.IndexBalances{balance}
}

prices, err := q.getPrices(k, req.MetokenDenom)
if err != nil {
return nil, err
}

return &metoken.QueryIndexBalancesResponse{
IndexBalances: balances,
Prices: prices,
}, nil
}

// IndexPrice returns Index price from the x/metoken module. If index denom is not specified,
// IndexPrices returns Index price from the x/metoken module. If index denom is not specified,
// returns prices for all the registered indexes.
func (q Querier) IndexPrice(
func (q Querier) IndexPrices(
goCtx context.Context,
req *metoken.QueryIndexPrice,
) (*metoken.QueryIndexPriceResponse, error) {
req *metoken.QueryIndexPrices,
) (*metoken.QueryIndexPricesResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
k := q.Keeper(&ctx)

prices, err := q.getPrices(q.Keeper(&ctx), req.MetokenDenom)
if err != nil {
return nil, err
}

return &metoken.QueryIndexPricesResponse{
Prices: prices,
}, nil
}

func (q Querier) getPrices(k Keeper, meTokenDenom string) ([]metoken.IndexPrices, error) {
var indexes []metoken.Index
if len(req.MetokenDenom) > 0 {
if !metoken.IsMeToken(req.MetokenDenom) {
if len(meTokenDenom) > 0 {
if !metoken.IsMeToken(meTokenDenom) {
return nil, sdkerrors.ErrInvalidRequest.Wrapf(
"meToken denom %s should have the following format: me/<TokenName>",
req.MetokenDenom,
meTokenDenom,
)
}

index, err := k.RegisteredIndex(req.MetokenDenom)
index, err := k.RegisteredIndex(meTokenDenom)
if err != nil {
return nil, err
}
Expand All @@ -167,24 +183,17 @@ func (q Querier) IndexPrice(
indexes = k.GetAllRegisteredIndexes()
}

prices := make([]metoken.Price, len(indexes))
prices := make([]metoken.IndexPrices, len(indexes))
for i, index := range indexes {
ip, err := k.Prices(index)
if err != nil {
return nil, err
}

price, err := ip.Price(index.Denom)
if err != nil {
return nil, err
}

prices[i] = price
prices[i] = ip.QueryExport()
}

return &metoken.QueryIndexPriceResponse{
Prices: prices,
}, nil
return prices, nil
}

// NewQuerier returns Querier object.
Expand Down
Loading