From 07891d19f0de3a2aa3bbac3800d6b521b83666b5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 2 Nov 2022 21:30:31 +0100 Subject: [PATCH] Add TWAP CLI (backport #3214) (#3215) * Add TWAP CLI (#3214) ## What is the purpose of the change Add CLI query support for TWAP. I've eye ball-tested the values for correctness against imperator API. Ideally we add this logic to our querygen, or use proto-reflect from SDK to automate CLI boilerplate going for. ## Testing and Verifying Manually tested ## Documentation and Release Note - Does this pull request introduce a new feature or user-facing behavior changes? yes - Is a relevant changelog entry added to the `Unreleased` section in `CHANGELOG.md`? yes - How is the feature or change documented? CLI help (cherry picked from commit e3e8e27be051f19f962dfe6173e6f56773fc110d) # Conflicts: # CHANGELOG.md * Fix changelog conflict Co-authored-by: Dev Ojha Co-authored-by: Dev Ojha --- CHANGELOG.md | 3 +- x/twap/client/cli/query.go | 139 ++++++++++++++++++++++++++++++++++++ x/twap/twapmodule/module.go | 4 +- 3 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 x/twap/client/cli/query.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 8618ba5d486..e37e7ffe5fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,7 +50,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Misc Improvements -* [#2804](https://github.com/osmosis-labs/osmosis/pull/2804) Improve error handling and messages when parsing pool assets. +* [#2804](https://github.com/osmosis-labs/osmosis/pull/2804) Improve error messages when parsing pool assets. +* [#3214](https://github.com/osmosis-labs/osmosis/pull/3214) Add basic CLI query support for TWAP. ## v12.2.0 diff --git a/x/twap/client/cli/query.go b/x/twap/client/cli/query.go new file mode 100644 index 00000000000..36d30875b74 --- /dev/null +++ b/x/twap/client/cli/query.go @@ -0,0 +1,139 @@ +package twapcli + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + "github.com/spf13/cobra" + + gammtypes "github.com/osmosis-labs/osmosis/v12/x/gamm/types" + "github.com/osmosis-labs/osmosis/v12/x/twap/client/queryproto" + "github.com/osmosis-labs/osmosis/v12/x/twap/types" +) + +// GetQueryCmd returns the cli query commands for this module. +func GetQueryCmd() *cobra.Command { + // Group superfluid queries under a subcommand + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand(GetQueryTwapCommand()) + + return cmd +} + +// GetCmdAssetMultiplier returns multiplier of an asset by denom. +func GetQueryTwapCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "twap [poolid] [base denom] [start time] [end time]", + Short: "Query twap", + Long: strings.TrimSpace( + fmt.Sprintf(`Query twap for pull. Start time must be unix time. End time can be unix time or duration. + +Example: +$ %s q twap 1 uosmo 1667088000 24h +$ %s q twap 1 uosmo 1667088000 1667174400 +`, + version.AppName, version.AppName, + ), + ), + Args: cobra.ExactArgs(4), + RunE: func(cmd *cobra.Command, args []string) error { + // boilerplate parse fields + // + poolId, err := parseUint(args[0], "poolId") + if err != nil { + return err + } + + // + baseDenom := strings.TrimSpace(args[1]) + + // + startTime, err := parseUnixTime(args[2], "start time") + if err != nil { + return err + } + + // END TIME PARSE: ONEOF {, } + // try parsing in unix time, if failed try parsing in duration + endTime, err := parseUnixTime(args[3], "end time") + if err != nil { + // TODO if we don't use protoreflect: + // make better error combiner, rather than just returning last error + duration, err := time.ParseDuration(args[3]) + if err != nil { + return err + } + endTime = startTime.Add(duration) + } + + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := queryproto.NewQueryClient(clientCtx) + gammClient := gammtypes.NewQueryClient(clientCtx) + liquidity, err := gammClient.TotalPoolLiquidity(cmd.Context(), &gammtypes.QueryTotalPoolLiquidityRequest{PoolId: poolId}) + if err != nil { + return err + } + if len(liquidity.Liquidity) != 2 { + return fmt.Errorf("pool %d has %d assets of liquidity, CLI support only exists for 2 assets right now.", poolId, len(liquidity.Liquidity)) + } + quoteDenom := "" + if liquidity.Liquidity[0].Denom == baseDenom { + quoteDenom = liquidity.Liquidity[1].Denom + } else if liquidity.Liquidity[1].Denom == baseDenom { + quoteDenom = liquidity.Liquidity[0].Denom + } else { + return fmt.Errorf("pool %d doesn't have provided baseDenom %s, has %s and %s", + poolId, baseDenom, liquidity.Liquidity[0], liquidity.Liquidity[1]) + } + + res, err := queryClient.ArithmeticTwap(cmd.Context(), &queryproto.ArithmeticTwapRequest{ + PoolId: poolId, + BaseAsset: baseDenom, + QuoteAsset: quoteDenom, + StartTime: startTime, + EndTime: &endTime, + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +func parseUint(arg string, fieldName string) (uint64, error) { + v, err := strconv.ParseUint(arg, 10, 64) + if err != nil { + return 0, fmt.Errorf("could not parse %s as uint for field %s: %w", arg, fieldName, err) + } + return v, nil +} + +func parseUnixTime(arg string, fieldName string) (time.Time, error) { + timeUnix, err := strconv.ParseInt(arg, 10, 64) + if err != nil { + return time.Time{}, fmt.Errorf("could not parse %s as unix time for field %s: %w", arg, fieldName, err) + } + startTime := time.Unix(timeUnix, 0) + return startTime, nil +} diff --git a/x/twap/twapmodule/module.go b/x/twap/twapmodule/module.go index e1bd6994a67..f4a200fce1e 100644 --- a/x/twap/twapmodule/module.go +++ b/x/twap/twapmodule/module.go @@ -18,6 +18,7 @@ import ( "github.com/osmosis-labs/osmosis/v12/x/twap" twapclient "github.com/osmosis-labs/osmosis/v12/x/twap/client" + twapcli "github.com/osmosis-labs/osmosis/v12/x/twap/client/cli" "github.com/osmosis-labs/osmosis/v12/x/twap/client/grpc" "github.com/osmosis-labs/osmosis/v12/x/twap/client/queryproto" "github.com/osmosis-labs/osmosis/v12/x/twap/types" @@ -64,8 +65,7 @@ func (b AppModuleBasic) GetTxCmd() *cobra.Command { } func (b AppModuleBasic) GetQueryCmd() *cobra.Command { - return nil - // return cli.GetQueryCmd() + return twapcli.GetQueryCmd() } // RegisterInterfaces registers interfaces and implementations of the gamm module.