Skip to content

Commit

Permalink
v2 spot price queries (#3335) (#3339)
Browse files Browse the repository at this point in the history
* Make v2 queries

* More WIP

* Add twap query v2

* Update twap query in whitelist

* Add changelog

* v2 query test

* Add wasmbinding test

(cherry picked from commit 6c4f423)

Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>
  • Loading branch information
mergify[bot] and ValarDragon authored Nov 11, 2022
1 parent b33406c commit 28eaeb6
Show file tree
Hide file tree
Showing 17 changed files with 2,952 additions and 42 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* [#2739](https://github.com/osmosis-labs/osmosis/pull/2739) Add pool type query
* [#2956](https://github.com/osmosis-labs/osmosis/issues/2956) Add queries for calculating amount of shares/tokens you get by providing X tokens/shares when entering/exiting a pool
* [#3313](https://github.com/osmosis-labs/osmosis/pull/3313) Upgrade to IBC v3.4.0, allowing for IBC transfers with metadata.
* [#3335](https://github.com/osmosis-labs/osmosis/pull/3335) Add v2 spot price queries
- The v1beta1 queries actually have base asset and quote asset reversed, so you were always getting 1/correct spot price. People fixed this by reordering the arguments.
- This PR adds v2 queries for doing the correct thing, and giving people time to migrate from v1beta1 queries to v2.
- It also changes cosmwasm to only allow the v2 queries, as no contracts on Osmosis mainnet uses the v1beta1 queries.


### Bug fixes
Expand Down
38 changes: 19 additions & 19 deletions app/apptesting/gamm.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@ var DefaultAcctFunds sdk.Coins = sdk.NewCoins(
sdk.NewCoin("bar", sdk.NewInt(10000000)),
sdk.NewCoin("baz", sdk.NewInt(10000000)),
)
var DefaultPoolAssets = []balancer.PoolAsset{
{
Weight: sdk.NewInt(100),
Token: sdk.NewCoin("foo", sdk.NewInt(5000000)),
},
{
Weight: sdk.NewInt(200),
Token: sdk.NewCoin("bar", sdk.NewInt(5000000)),
},
{
Weight: sdk.NewInt(300),
Token: sdk.NewCoin("baz", sdk.NewInt(5000000)),
},
{
Weight: sdk.NewInt(400),
Token: sdk.NewCoin("uosmo", sdk.NewInt(5000000)),
},
}

// PrepareBalancerPoolWithCoins returns a balancer pool
// consisted of given coins with equal weight.
Expand Down Expand Up @@ -68,25 +86,7 @@ func (s *KeeperTestHelper) PrepareBalancerPoolWithPoolParams(poolParams balancer
// Mint some assets to the account.
s.FundAcc(s.TestAccs[0], DefaultAcctFunds)

poolAssets := []balancer.PoolAsset{
{
Weight: sdk.NewInt(100),
Token: sdk.NewCoin("foo", sdk.NewInt(5000000)),
},
{
Weight: sdk.NewInt(200),
Token: sdk.NewCoin("bar", sdk.NewInt(5000000)),
},
{
Weight: sdk.NewInt(300),
Token: sdk.NewCoin("baz", sdk.NewInt(5000000)),
},
{
Weight: sdk.NewInt(400),
Token: sdk.NewCoin("uosmo", sdk.NewInt(5000000)),
},
}
msg := balancer.NewMsgCreateBalancerPool(s.TestAccs[0], poolParams, poolAssets, "")
msg := balancer.NewMsgCreateBalancerPool(s.TestAccs[0], poolParams, DefaultPoolAssets, "")
poolId, err := s.App.GAMMKeeper.CreatePool(s.Ctx, msg)
s.NoError(err)
return poolId
Expand Down
38 changes: 38 additions & 0 deletions proto/osmosis/gamm/v2/query.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
syntax = "proto3";
package osmosis.gamm.v2;

import "gogoproto/gogo.proto";
import "osmosis/gamm/v1beta1/tx.proto";

import "cosmos/base/v1beta1/coin.proto";
import "cosmos/base/query/v1beta1/pagination.proto";
import "google/api/annotations.proto";
import "google/protobuf/any.proto";
import "cosmos_proto/cosmos.proto";

option go_package = "github.com/osmosis-labs/osmosis/v12/x/gamm/v2types";

service Query {
// SpotPrice defines a gRPC query handler that returns the spot price given
// a base denomination and a quote denomination.
rpc SpotPrice(QuerySpotPriceRequest) returns (QuerySpotPriceResponse) {
option (google.api.http).get = "/osmosis/gamm/v2/pools/{pool_id}/prices";
}
}

// QuerySpotPriceRequest defines the gRPC request structure for a SpotPrice
// query.
message QuerySpotPriceRequest {
uint64 pool_id = 1 [ (gogoproto.moretags) = "yaml:\"pool_id\"" ];
string base_asset_denom = 2
[ (gogoproto.moretags) = "yaml:\"base_asset_denom\"" ];
string quote_asset_denom = 3
[ (gogoproto.moretags) = "yaml:\"quote_asset_denom\"" ];
}

// QuerySpotPriceResponse defines the gRPC response structure for a SpotPrice
// query.
message QuerySpotPriceResponse {
// String of the Dec. Ex) 10.203uatom
string spot_price = 1 [ (gogoproto.moretags) = "yaml:\"spot_price\"" ];
}
66 changes: 66 additions & 0 deletions proto/osmosis/twap/v2/query.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
syntax = "proto3";
package osmosis.twap.v2;

import "gogoproto/gogo.proto";
import "osmosis/twap/v1beta1/twap_record.proto";
import "osmosis/twap/v1beta1/genesis.proto";

import "cosmos/base/v1beta1/coin.proto";
import "cosmos/base/query/v1beta1/pagination.proto";
import "google/api/annotations.proto";
import "google/protobuf/any.proto";
import "cosmos_proto/cosmos.proto";
import "google/protobuf/timestamp.proto";

option go_package = "github.com/osmosis-labs/osmosis/v12/x/twap/client/v2queryproto";

service Query {
rpc ArithmeticTwap(ArithmeticTwapRequest) returns (ArithmeticTwapResponse) {
option (google.api.http).get = "/osmosis/twap/v2/ArithmeticTwap";
}
rpc ArithmeticTwapToNow(ArithmeticTwapToNowRequest)
returns (ArithmeticTwapToNowResponse) {
option (google.api.http).get = "/osmosis/twap/v2/ArithmeticTwapToNow";
}
}

message ArithmeticTwapRequest {
uint64 pool_id = 1;
string base_asset = 2;
string quote_asset = 3;
google.protobuf.Timestamp start_time = 4 [
(gogoproto.nullable) = false,
(gogoproto.stdtime) = true,
(gogoproto.moretags) = "yaml:\"start_time\""
];
google.protobuf.Timestamp end_time = 5 [
(gogoproto.nullable) = true,
(gogoproto.stdtime) = true,
(gogoproto.moretags) = "yaml:\"end_time\""
];
}
message ArithmeticTwapResponse {
string arithmetic_twap = 1 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.moretags) = "yaml:\"arithmetic_twap\"",
(gogoproto.nullable) = false
];
}

message ArithmeticTwapToNowRequest {
uint64 pool_id = 1;
string base_asset = 2;
string quote_asset = 3;
google.protobuf.Timestamp start_time = 4 [
(gogoproto.nullable) = false,
(gogoproto.stdtime) = true,
(gogoproto.moretags) = "yaml:\"start_time\""
];
}
message ArithmeticTwapToNowResponse {
string arithmetic_twap = 1 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.moretags) = "yaml:\"arithmetic_twap\"",
(gogoproto.nullable) = false
];
}
44 changes: 43 additions & 1 deletion wasmbinding/query_plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@ import (
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
proto "github.com/golang/protobuf/proto"
"github.com/stretchr/testify/suite"
"github.com/tendermint/tendermint/crypto/ed25519"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

"github.com/osmosis-labs/osmosis/v12/app/apptesting"
"github.com/osmosis-labs/osmosis/v12/x/gamm/pool-models/balancer"
gammv2types "github.com/osmosis-labs/osmosis/v12/x/gamm/v2types"

"github.com/osmosis-labs/osmosis/v12/app"
epochtypes "github.com/osmosis-labs/osmosis/v12/x/epochs/types"
lockuptypes "github.com/osmosis-labs/osmosis/v12/x/lockup/types"
Expand Down Expand Up @@ -47,10 +52,11 @@ func (suite *StargateTestSuite) TestStargateQuerier() {
testSetup func()
path string
requestData func() []byte
responseProtoStruct interface{}
responseProtoStruct codec.ProtoMarshaler
expectedQuerierError bool
expectedUnMarshalError bool
resendRequest bool
checkResponseStruct bool
}{
{
name: "happy path",
Expand All @@ -63,6 +69,35 @@ func (suite *StargateTestSuite) TestStargateQuerier() {
},
responseProtoStruct: &epochtypes.QueryEpochsInfoResponse{},
},
{
name: "happy path gamm",
path: "/osmosis.gamm.v2.Query/SpotPrice",
testSetup: func() {
pk := ed25519.GenPrivKey().PubKey()
sender := sdk.AccAddress(pk.Address())
err := simapp.FundAccount(suite.app.BankKeeper, suite.ctx, sender, apptesting.DefaultAcctFunds)
suite.Require().NoError(err)
msg := balancer.NewMsgCreateBalancerPool(sender,
balancer.NewPoolParams(sdk.ZeroDec(), sdk.ZeroDec(), nil),
apptesting.DefaultPoolAssets, "")
_, err = suite.app.GAMMKeeper.CreatePool(suite.ctx, msg)
suite.NoError(err)
},
requestData: func() []byte {
queryrequest := gammv2types.QuerySpotPriceRequest{
PoolId: 1,
BaseAssetDenom: "bar",
QuoteAssetDenom: "uosmo",
}
bz, err := proto.Marshal(&queryrequest)
suite.Require().NoError(err)
return bz
},
checkResponseStruct: true,
responseProtoStruct: &gammv2types.QuerySpotPriceResponse{
SpotPrice: sdk.NewDecWithPrec(5, 1).String(),
},
},
{
name: "unregistered path(not whitelisted)",
path: "/osmosis.lockup.Query/AccountLockedLongerDuration",
Expand Down Expand Up @@ -192,6 +227,13 @@ func (suite *StargateTestSuite) TestStargateQuerier() {
suite.Require().Error(err)
return
}
if tc.checkResponseStruct {
expectedResponse, err := proto.Marshal(tc.responseProtoStruct)
suite.Require().NoError(err)
expJsonResp, err := wasmbinding.ConvertProtoToJSONMarshal(tc.responseProtoStruct, expectedResponse, suite.app.AppCodec())
suite.Require().NoError(err)
suite.Require().Equal(expJsonResp, stargateResponse)
}

suite.Require().NoError(err)

Expand Down
7 changes: 5 additions & 2 deletions wasmbinding/stargate_whitelist.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ import (

epochtypes "github.com/osmosis-labs/osmosis/v12/x/epochs/types"
gammtypes "github.com/osmosis-labs/osmosis/v12/x/gamm/types"
gammv2types "github.com/osmosis-labs/osmosis/v12/x/gamm/v2types"
incentivestypes "github.com/osmosis-labs/osmosis/v12/x/incentives/types"
lockuptypes "github.com/osmosis-labs/osmosis/v12/x/lockup/types"
minttypes "github.com/osmosis-labs/osmosis/v12/x/mint/types"
poolincentivestypes "github.com/osmosis-labs/osmosis/v12/x/pool-incentives/types"
superfluidtypes "github.com/osmosis-labs/osmosis/v12/x/superfluid/types"
tokenfactorytypes "github.com/osmosis-labs/osmosis/v12/x/tokenfactory/types"
twapquerytypes "github.com/osmosis-labs/osmosis/v12/x/twap/client/queryproto"
twapv2querytypes "github.com/osmosis-labs/osmosis/v12/x/twap/client/v2queryproto"
txfeestypes "github.com/osmosis-labs/osmosis/v12/x/txfees/types"
)

Expand Down Expand Up @@ -78,6 +80,7 @@ func init() {
setWhitelistedQuery("/osmosis.gamm.v1beta1.Query/TotalPoolLiquidity", &gammtypes.QueryTotalPoolLiquidityResponse{})
setWhitelistedQuery("/osmosis.gamm.v1beta1.Query/TotalShares", &gammtypes.QueryTotalSharesResponse{})
setWhitelistedQuery("/osmosis.gamm.v1beta1.Query/SpotPrice", &gammtypes.QuerySpotPriceResponse{})
setWhitelistedQuery("/osmosis.gamm.v2.Query/SpotPrice", &gammv2types.QuerySpotPriceResponse{})

// incentives
setWhitelistedQuery("/osmosis.incentives.Query/ModuleToDistributeCoins", &incentivestypes.ModuleToDistributeCoinsResponse{})
Expand Down Expand Up @@ -115,8 +118,8 @@ func init() {
// Does not include denoms_from_creator, TBD if this is the index we want contracts to use instead of admin

// twap
setWhitelistedQuery("/osmosis.twap.v1beta1.Query/ArithmeticTwap", &twapquerytypes.ArithmeticTwapResponse{})
setWhitelistedQuery("/osmosis.twap.v1beta1.Query/ArithmeticTwapToNow", &twapquerytypes.ArithmeticTwapToNowResponse{})
setWhitelistedQuery("/osmosis.twap.v2.Query/ArithmeticTwap", &twapv2querytypes.ArithmeticTwapResponse{})
setWhitelistedQuery("/osmosis.twap.v2.Query/ArithmeticTwapToNow", &twapv2querytypes.ArithmeticTwapToNowResponse{})
setWhitelistedQuery("/osmosis.twap.v1beta1.Query/Params", &twapquerytypes.ParamsResponse{})
}

Expand Down
36 changes: 36 additions & 0 deletions x/gamm/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/osmosis-labs/osmosis/v12/x/gamm/pool-models/balancer"
"github.com/osmosis-labs/osmosis/v12/x/gamm/types"
"github.com/osmosis-labs/osmosis/v12/x/gamm/v2types"
)

var sdkIntMaxValue = sdk.NewInt(0)
Expand Down Expand Up @@ -44,6 +45,16 @@ func NewQuerier(k Keeper) Querier {
return Querier{Keeper: k}
}

// QuerierV2 defines a wrapper around the x/gamm keeper providing gRPC method
// handlers for v2 queries.
type QuerierV2 struct {
Keeper
}

func NewV2Querier(k Keeper) QuerierV2 {
return QuerierV2{Keeper: k}
}

// Pool checks if a pool exists and their respective poolWeights.
func (q Querier) Pool(
ctx context.Context,
Expand Down Expand Up @@ -371,6 +382,31 @@ func (q Querier) SpotPrice(ctx context.Context, req *types.QuerySpotPriceRequest
}, nil
}

func (q QuerierV2) SpotPrice(ctx context.Context, req *v2types.QuerySpotPriceRequest) (*v2types.QuerySpotPriceResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

if req.BaseAssetDenom == "" {
return nil, status.Error(codes.InvalidArgument, "invalid base asset denom")
}

if req.QuoteAssetDenom == "" {
return nil, status.Error(codes.InvalidArgument, "invalid quote asset denom")
}

sdkCtx := sdk.UnwrapSDKContext(ctx)

sp, err := q.Keeper.CalculateSpotPrice(sdkCtx, req.PoolId, req.QuoteAssetDenom, req.BaseAssetDenom)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

return &v2types.QuerySpotPriceResponse{
SpotPrice: sp.String(),
}, nil
}

// TotalLiquidity returns total liquidity across all pools.
func (q Querier) TotalLiquidity(ctx context.Context, _ *types.QueryTotalLiquidityRequest) (*types.QueryTotalLiquidityResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
Expand Down
Loading

0 comments on commit 28eaeb6

Please sign in to comment.