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

Add TWAP CLI #3214

Merged
merged 6 commits into from
Nov 2, 2022
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* [#2804](https://github.com/osmosis-labs/osmosis/pull/2804) Improve error handling and messages when parsing pool assets.
* [#3035](https://github.com/osmosis-labs/osmosis/pull/3035) Remove `PokePool` from `PoolI` interface. Define on a new WeightedPoolExtension` instead.
* [#3214](https://github.com/osmosis-labs/osmosis/pull/3214) Add basic CLI query support for TWAP.


## v12.0.0

Expand Down
139 changes: 139 additions & 0 deletions x/twap/client/cli/query.go
Original file line number Diff line number Diff line change
@@ -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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: don't know if its worth overhead, but would love to have some test cases on this since this includes lot of internal parsing. We could also make it a separate issue for this if u agree with the needs of testing for this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy if someone else wants to write tests for this, I think of it as pretty low priority.

I'd much rather allocate those mental cycles to getting most CLI code killed / better testing structures. (I believe SDK has progress on better CLI testing structure)

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
// <UINT PARSE>
poolId, err := parseUint(args[0], "poolId")
if err != nil {
return err
}

// <DENOM PARSE>
baseDenom := strings.TrimSpace(args[1])

// <UNIX TIME PARSE>
startTime, err := parseUnixTime(args[2], "start time")
if err != nil {
return err
}

// END TIME PARSE: ONEOF {<UNIX TIME PARSE>, <DURATION>}
// try parsing in unix time, if failed try parsing in duration
endTime, err := parseUnixTime(args[3], "end time")
ValarDragon marked this conversation as resolved.
Show resolved Hide resolved
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 {
mattverse marked this conversation as resolved.
Show resolved Hide resolved
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
}
4 changes: 2 additions & 2 deletions x/twap/twapmodule/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -63,8 +64,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.
Expand Down