From 5904ae6d922e13f0ff9e60b6af40a430781b1ca6 Mon Sep 17 00:00:00 2001 From: sampocs Date: Tue, 21 Nov 2023 21:29:36 -0600 Subject: [PATCH] wired up price query --- x/interchainquery/types/keys.go | 15 +++++++ x/stakeibc/keeper/icqcallbacks_pool_price.go | 45 ++++++++++--------- .../keeper/icqcallbacks_pool_spot_price.go | 45 ------------------- x/stakeibc/keeper/reward_converter.go | 22 ++++----- 4 files changed, 49 insertions(+), 78 deletions(-) delete mode 100644 x/stakeibc/keeper/icqcallbacks_pool_spot_price.go diff --git a/x/interchainquery/types/keys.go b/x/interchainquery/types/keys.go index 21117a0623..03de143c06 100644 --- a/x/interchainquery/types/keys.go +++ b/x/interchainquery/types/keys.go @@ -1,5 +1,7 @@ package types +import fmt "fmt" + const ( // ModuleName defines the module name ModuleName = "interchainquery" @@ -31,6 +33,14 @@ const ( STAKING_STORE_QUERY_WITH_PROOF = "store/staking/key" // The bank store is key'd by the account address BANK_STORE_QUERY_WITH_PROOF = "store/bank/key" + // The Osmosis twap store - key'd by the pool ID and denom's + TWAP_STORE_QUERY_WITH_PROOF = "store/twap/key" +) + +var ( + // Osmosis TWAP query info + OsmosisKeySeparator = "|" + OsmosisMostRecentTWAPsPrefix = "recent_twap" + OsmosisKeySeparator ) var ( @@ -42,3 +52,8 @@ var ( func KeyPrefix(p string) []byte { return []byte(p) } + +func FormatOsmosisMostRecentTWAPKey(poolId uint64, denom1, denom2 string) []byte { + poolIdBz := fmt.Sprintf("%0.20d", poolId) + return []byte(fmt.Sprintf("%s%s%s%s%s%s", OsmosisMostRecentTWAPsPrefix, poolIdBz, OsmosisKeySeparator, denom1, OsmosisKeySeparator, denom2)) +} diff --git a/x/stakeibc/keeper/icqcallbacks_pool_price.go b/x/stakeibc/keeper/icqcallbacks_pool_price.go index 24d38e3923..a731166417 100644 --- a/x/stakeibc/keeper/icqcallbacks_pool_price.go +++ b/x/stakeibc/keeper/icqcallbacks_pool_price.go @@ -1,10 +1,15 @@ package keeper import ( + "fmt" + + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/gogoproto/proto" "github.com/Stride-Labs/stride/v16/utils" icqtypes "github.com/Stride-Labs/stride/v16/x/interchainquery/types" + "github.com/Stride-Labs/stride/v16/x/stakeibc/types" ) // TradeRewardBalanceCallback is a callback handler for TradeRewardBalance queries. @@ -16,30 +21,30 @@ func PoolPriceCallback(k Keeper, ctx sdk.Context, args []byte, query icqtypes.Qu k.Logger(ctx).Info(utils.LogICQCallbackWithHostZone(query.ChainId, ICQCallbackID_PoolPrice, "Starting pool spot price callback, QueryId: %vs, QueryType: %s, Connection: %s", query.Id, query.QueryType, query.ConnectionId)) - // chainId := query.ChainId // should be the tradeZoneId, used in logging + chainId := query.ChainId // should be the tradeZoneId, used in logging - // TODO: [DYDX] Fix in separate PR - // Unmarshal the query response args, should be a SpotPriceResponse type - // var reponse types.QuerySpotPriceResponse - // err := reponse.Unmarshal(args) - // if err != nil { - // return errorsmod.Wrap(err, "unable to unmarshal the query response") - // } + // Unmarshal the query response args, should be a TwapRecord type + var twapRecord types.OsmosisTwapRecord + err := twapRecord.Unmarshal(args) + if err != nil { + return errorsmod.Wrap(err, "unable to unmarshal the query response") + } - // response.SpotPrice should be a string representation of the denom ratios in pool like "10.203" + price := sdk.ZeroDec() + fmt.Printf("%+v\n", twapRecord) // Unmarshal the callback data containing the tradeRoute we are on - // var tradeRoute types.TradeRoute - // if err := proto.Unmarshal(query.CallbackData, &tradeRoute); err != nil { - // return errorsmod.Wrapf(err, "unable to unmarshal trade reward balance callback data") - // } - // k.Logger(ctx).Info(utils.LogICQCallbackWithHostZone(chainId, ICQCallbackID_PoolSpotPrice, - // "Query response - spot price ratio of %s to %s is %s", - // tradeRoute.RewardDenomOnTradeZone, tradeRoute.TargetDenomOnTradeZone, reponse.SpotPrice)) - - // // Update the spot price stored on the trade route data in the keeper - // tradeRoute.SpotPrice = reponse.SpotPrice - // k.SetTradeRoute(ctx, tradeRoute) + var tradeRoute types.TradeRoute + if err := proto.Unmarshal(query.CallbackData, &tradeRoute); err != nil { + return errorsmod.Wrapf(err, "unable to unmarshal trade reward balance callback data") + } + k.Logger(ctx).Info(utils.LogICQCallbackWithHostZone(chainId, ICQCallbackID_PoolPrice, + "Query response - spot price ratio of %s to %s is %s", + tradeRoute.RewardDenomOnTradeZone, tradeRoute.TargetDenomOnTradeZone, price)) + + // Update the spot price stored on the trade route data in the keeper + tradeRoute.InputToOutputTwap = price + k.SetTradeRoute(ctx, tradeRoute) return nil } diff --git a/x/stakeibc/keeper/icqcallbacks_pool_spot_price.go b/x/stakeibc/keeper/icqcallbacks_pool_spot_price.go deleted file mode 100644 index 24d38e3923..0000000000 --- a/x/stakeibc/keeper/icqcallbacks_pool_spot_price.go +++ /dev/null @@ -1,45 +0,0 @@ -package keeper - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/Stride-Labs/stride/v16/utils" - icqtypes "github.com/Stride-Labs/stride/v16/x/interchainquery/types" -) - -// TradeRewardBalanceCallback is a callback handler for TradeRewardBalance queries. -// The query response will return the trade ICA account balance for a specific (foreign ibc) denom -// If the balance is non-zero, ICA MsgSends are submitted to initiate a swap on the tradeZone -// -// Note: for now, to get proofs in your ICQs, you need to query the entire store on the host zone! e.g. "store/bank/key" -func PoolPriceCallback(k Keeper, ctx sdk.Context, args []byte, query icqtypes.Query) error { - k.Logger(ctx).Info(utils.LogICQCallbackWithHostZone(query.ChainId, ICQCallbackID_PoolPrice, - "Starting pool spot price callback, QueryId: %vs, QueryType: %s, Connection: %s", query.Id, query.QueryType, query.ConnectionId)) - - // chainId := query.ChainId // should be the tradeZoneId, used in logging - - // TODO: [DYDX] Fix in separate PR - // Unmarshal the query response args, should be a SpotPriceResponse type - // var reponse types.QuerySpotPriceResponse - // err := reponse.Unmarshal(args) - // if err != nil { - // return errorsmod.Wrap(err, "unable to unmarshal the query response") - // } - - // response.SpotPrice should be a string representation of the denom ratios in pool like "10.203" - - // Unmarshal the callback data containing the tradeRoute we are on - // var tradeRoute types.TradeRoute - // if err := proto.Unmarshal(query.CallbackData, &tradeRoute); err != nil { - // return errorsmod.Wrapf(err, "unable to unmarshal trade reward balance callback data") - // } - // k.Logger(ctx).Info(utils.LogICQCallbackWithHostZone(chainId, ICQCallbackID_PoolSpotPrice, - // "Query response - spot price ratio of %s to %s is %s", - // tradeRoute.RewardDenomOnTradeZone, tradeRoute.TargetDenomOnTradeZone, reponse.SpotPrice)) - - // // Update the spot price stored on the trade route data in the keeper - // tradeRoute.SpotPrice = reponse.SpotPrice - // k.SetTradeRoute(ctx, tradeRoute) - - return nil -} diff --git a/x/stakeibc/keeper/reward_converter.go b/x/stakeibc/keeper/reward_converter.go index 8e1672fe6a..75dcad51e0 100644 --- a/x/stakeibc/keeper/reward_converter.go +++ b/x/stakeibc/keeper/reward_converter.go @@ -150,13 +150,12 @@ func (k Keeper) TradeRewardTokens(ctx sdk.Context, amount sdk.Int, route types.T // If there is a valid spot price, use it to set a floor for the acceptable minimum output tokens // 5% slippage is allowed so multiply by 0.95 the expected spot price to get the target minimum // minOut is the minimum number of route.TargetDenomOnTradeZone we must receive or the swap will fail - minOut := sdk.ZeroInt() if route.InputToOutputTwap == sdk.ZeroDec() { return fmt.Errorf("TWAP not found for pool %d", route.PoolId) } slippageRatio := sdk.NewDecWithPrec(95, 2) // 0.95 to allow 5% loss to slippage inputToOutputRatio := slippageRatio.Mul(route.InputToOutputTwap) - minOut = sdk.NewDecFromInt(amount).Mul(inputToOutputRatio).TruncateInt() + minOut := sdk.NewDecFromInt(amount).Mul(inputToOutputRatio).TruncateInt() tradeIcaAccount := route.RewardToTradeHop.ToAccount tradeTokens := sdk.NewCoin(route.RewardDenomOnTradeZone, amount) @@ -352,17 +351,14 @@ func (k Keeper) PoolPriceQuery(ctx sdk.Context, route types.TradeRoute) error { tradeAccount := route.RewardToTradeHop.ToAccount k.Logger(ctx).Info(utils.LogWithHostZone(tradeAccount.ChainId, "Submitting ICQ for spot price in this pool")) - // The stride interchainquery module likely can't actually handle this type of query so this likely won't work yet... + // Sort denom's + denom1, denom2 := route.RewardDenomOnTradeZone, route.TargetDenomOnTradeZone + if denom1 > denom2 { + denom1, denom2 = denom2, denom1 + } - // Encode the osmosis spot price query request for the specific pool and denoms - // spotPriceRequest := types.QuerySpotPriceRequest{ - // PoolId: route.PoolId, - // BaseAssetDenom: route.RewardDenomOnTradeZone, - // QuoteAssetDenom: route.TargetDenomOnTradeZone, - // } - // queryData := k.cdc.MustMarshal(&spotPriceRequest) - // TODO [DYDX]: Use TWAP in separate PR - queryData := []byte{} + // Build query request data which consists of the TWAP store key built from each denom + queryData := icqtypes.FormatOsmosisMostRecentTWAPKey(route.PoolId, denom1, denom2) // Timeout query at end of epoch strideEpochTracker, found := k.GetEpochTracker(ctx, epochstypes.STRIDE_EPOCH) @@ -383,7 +379,7 @@ func (k Keeper) PoolPriceQuery(ctx sdk.Context, route types.TradeRoute) error { query := icqtypes.Query{ ChainId: tradeAccount.ChainId, ConnectionId: tradeAccount.ConnectionId, // query needs to go to the trade zone, not the host zone - QueryType: icqtypes.BANK_STORE_QUERY_WITH_PROOF, + QueryType: icqtypes.TWAP_STORE_QUERY_WITH_PROOF, RequestData: queryData, CallbackModule: types.ModuleName, CallbackId: ICQCallbackID_PoolPrice,