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

osmocli: parse Use field's arguments dynamically from a message #6005

Merged
merged 24 commits into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from 21 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* [#5964](https://github.com/osmosis-labs/osmosis/pull/5964) fix e2e test concurrency bugs
* [#5948] (https://github.com/osmosis-labs/osmosis/pull/5948) Parameterizing Pool Type Information in Protorev
* [#6001](https://github.com/osmosis-labs/osmosis/pull/6001) feat: improve set-env CLI cmd
* [#6005](https://github.com/osmosis-labs/osmosis/pull/6005) osmocli: parse Use field's arguments dynamically

### Minor improvements & Bug Fixes

Expand Down
48 changes: 48 additions & 0 deletions osmoutils/osmocli/dynamic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package osmocli

import (
"fmt"
"reflect"
"regexp"
"strings"

"github.com/gogo/protobuf/proto"

"github.com/osmosis-labs/osmosis/osmoutils"
)

type Descriptor interface {
GetCustomFlagOverrides() map[string]string
AttachToUse(str string)
}

// fields that are not provided as arguments
var nonAttachableFields []string = []string{"sender", "pagination", "owner", "admin"}
Copy link
Member Author

Choose a reason for hiding this comment

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

to reviewers: are there some other fields which should not be passed as arguments of cli commands?


// attachFieldsToUse extracts fields from reqP proto message and dynamically appends them into Use field
func attachFieldsToUse[reqP proto.Message](desc Descriptor) {
req := osmoutils.MakeNew[reqP]()
v := reflect.ValueOf(req).Type().Elem() // get underlying non-pointer struct
var useField string
for i := 0; i < v.NumField(); i++ {
fn := pascalToKebab(v.Field(i).Name)

// if a field is parsed from a flag, skip it
if desc.GetCustomFlagOverrides()[fn] != "" || osmoutils.Contains(nonAttachableFields, fn) {
continue
}

useField += fmt.Sprintf(" [%s]", fn)
}

desc.AttachToUse(useField)
}

// pascalToKebab converts PascalCase string to kebab-case string
func pascalToKebab(s string) string {
reg := regexp.MustCompile(`([a-z0-9])([A-Z])`)
s = reg.ReplaceAllString(s, `${1}-${2}`)

// Convert everything to lowercase
return strings.ToLower(s)
}
116 changes: 116 additions & 0 deletions osmoutils/osmocli/dynamic_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package osmocli

import (
"testing"

"github.com/stretchr/testify/require"

clqueryproto "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/client/queryproto"
cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types"
lockuptypes "github.com/osmosis-labs/osmosis/v17/x/lockup/types"
)

// test-specific helper descriptor
type TestDescriptor interface {
Descriptor
GetUse() string
}

type TestQueryDescriptor struct {
*QueryDescriptor
}

func (tqd *TestQueryDescriptor) GetUse() string {
return tqd.Use
}

type TestTxCliDescriptor struct {
*TxCliDesc
}

func (ttxcd *TestTxCliDescriptor) GetUse() string {
return ttxcd.Use
}

func TestAttachFieldsToUse(t *testing.T) {
tests := map[string]struct {
desc TestDescriptor
attachFunc func(Descriptor)
expectedUse string
}{
"basic/TxCliDesc/attaches_2_args": {
desc: &TestTxCliDescriptor{
&TxCliDesc{
Use: "set-reward-receiver-address",
Short: "sets reward receiver address for the designated lock id",
Long: "sets reward receiver address for the designated lock id",
},
},
attachFunc: attachFieldsToUse[*lockuptypes.MsgSetRewardReceiverAddress],
expectedUse: "set-reward-receiver-address [lock-id] [reward-receiver]",
},
"basic/QueryDescriptor/attaches_1_arg": {
desc: &TestQueryDescriptor{
&QueryDescriptor{
Use: "pool-accumulator-rewards",
Short: "Query pool accumulator rewards",
Long: `{{.Short}}{{.ExampleHeader}}
{{.CommandPrefix}} pool-accumulator-rewards 1`,
},
},
attachFunc: attachFieldsToUse[*clqueryproto.PoolAccumulatorRewardsRequest],
expectedUse: "pool-accumulator-rewards [pool-id]",
},
"ignore_pagination/QueryDescriptor/no_args": {
desc: &TestQueryDescriptor{
&QueryDescriptor{
Use: "pools",
Short: "Query pools",
Long: `{{.Short}}{{.ExampleHeader}}
{{.CommandPrefix}} pools`,
},
},
attachFunc: attachFieldsToUse[*clqueryproto.PoolsRequest],
expectedUse: "pools",
},
"ignore_owner/TxCliDesc/attach_2_args": {
desc: &TestTxCliDescriptor{
&TxCliDesc{
Use: "lock-tokens",
Short: "lock tokens into lockup pool from user account",
},
},
attachFunc: attachFieldsToUse[*lockuptypes.MsgLockTokens],
expectedUse: "lock-tokens [duration] [coins]", // in osmosis, this command takes duration from a flag, but here it is just for testing purposes
},
"ignore_sender/TxCliDesc/attach_5_args": { // also tests that args are shown in kebab-case
desc: &TestTxCliDescriptor{
&TxCliDesc{
Use: "add-to-position",
Short: "add to an existing concentrated liquidity position",
Example: "osmosisd tx concentratedliquidity add-to-position 10 1000000000uosmo 10000000uion --from val --chain-id localosmosis -b block --keyring-backend test --fees 1000000uosmo",
},
},
attachFunc: attachFieldsToUse[*cltypes.MsgAddToPosition],
expectedUse: "add-to-position [position-id] [amount0] [amount1] [token-min-amount0] [token-min-amount1]",
},
"ignore_custom_flag_overrides/TxCliDesc/": {
desc: &TestTxCliDescriptor{
&TxCliDesc{
Use: "lock-tokens",
Short: "lock tokens into lockup pool from user account",
CustomFlagOverrides: map[string]string{
"duration": "duration",
},
},
},
attachFunc: attachFieldsToUse[*lockuptypes.MsgLockTokens],
expectedUse: "lock-tokens [coins]",
},
}

for _, tt := range tests {
tt.attachFunc(tt.desc)
require.Equal(t, tt.desc.GetUse(), tt.expectedUse)
}
}
13 changes: 13 additions & 0 deletions osmoutils/osmocli/query_cmd_wrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ type QueryDescriptor struct {
numArgs int
}

var _ Descriptor = &QueryDescriptor{}

// Implement Descriptor interface
func (desc QueryDescriptor) GetCustomFlagOverrides() map[string]string {
return desc.CustomFlagOverrides
}

// Implement Descriptor interface
func (desc *QueryDescriptor) AttachToUse(str string) {
desc.Use += str
}

func QueryIndexCmd(moduleName string) *cobra.Command {
cmd := IndexCmd(moduleName)
cmd.Short = fmt.Sprintf("Querying commands for the %s module", moduleName)
Expand Down Expand Up @@ -89,6 +101,7 @@ func BuildQueryCli[reqP proto.Message, querier any](desc *QueryDescriptor, newQu
}
}

attachFieldsToUse[reqP](desc)
cmd := &cobra.Command{
Use: desc.Use,
Short: desc.Short,
Expand Down
14 changes: 14 additions & 0 deletions osmoutils/osmocli/tx_cmd_wrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ type TxCliDesc struct {
CustomFieldParsers map[string]CustomFieldParserFn
}

var _ Descriptor = &TxCliDesc{}

// Implement Descriptor interface
func (desc TxCliDesc) GetCustomFlagOverrides() map[string]string {
return desc.CustomFlagOverrides
}

// Implement Descriptor interface
func (desc *TxCliDesc) AttachToUse(str string) {
desc.Use += str
}

func AddTxCmd[M sdk.Msg](cmd *cobra.Command, f func() (*TxCliDesc, M)) {
desc, _ := f()
subCmd := BuildTxCli[M](desc)
Expand All @@ -60,6 +72,8 @@ func BuildTxCli[M sdk.Msg](desc *TxCliDesc) *cobra.Command {
return ParseFieldsFromFlagsAndArgs[M](flagAdvice, flags, args)
}
}

attachFieldsToUse[M](desc)
return desc.BuildCommandCustomFn()
}

Expand Down
18 changes: 9 additions & 9 deletions x/concentrated-liquidity/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func GetQueryCmd() *cobra.Command {

func GetUserPositions() (*osmocli.QueryDescriptor, *queryproto.UserPositionsRequest) {
return &osmocli.QueryDescriptor{
Use: "user-positions [address]",
Use: "user-positions",
Short: "Query user's positions",
Long: `{{.Short}}{{.ExampleHeader}}
{{.CommandPrefix}} user-positions osmo12smx2wdlyttvyzvzg54y2vnqwq2qjateuf7thj`,
Expand All @@ -43,7 +43,7 @@ func GetUserPositions() (*osmocli.QueryDescriptor, *queryproto.UserPositionsRequ

func GetPositionById() (*osmocli.QueryDescriptor, *queryproto.PositionByIdRequest) {
return &osmocli.QueryDescriptor{
Use: "position-by-id [positionID]",
Use: "position-by-id",
Short: "Query position by ID",
Long: `{{.Short}}{{.ExampleHeader}}
{{.CommandPrefix}} position-by-id 53`,
Expand All @@ -62,7 +62,7 @@ func GetCmdPools() (*osmocli.QueryDescriptor, *queryproto.PoolsRequest) {

func GetClaimableSpreadRewards() (*osmocli.QueryDescriptor, *queryproto.ClaimableSpreadRewardsRequest) {
return &osmocli.QueryDescriptor{
Use: "claimable-spread-rewards [positionID]",
Use: "claimable-spread-rewards",
Short: "Query claimable spread rewards",
Long: `{{.Short}}{{.ExampleHeader}}
{{.CommandPrefix}} claimable-spread-rewards 53`,
Expand All @@ -71,7 +71,7 @@ func GetClaimableSpreadRewards() (*osmocli.QueryDescriptor, *queryproto.Claimabl

func GetClaimableIncentives() (*osmocli.QueryDescriptor, *queryproto.ClaimableIncentivesRequest) {
return &osmocli.QueryDescriptor{
Use: "claimable-incentives [positionID]",
Use: "claimable-incentives",
Short: "Query claimable incentives",
Long: `{{.Short}}{{.ExampleHeader}}
{{.CommandPrefix}} claimable-incentives 53`,
Expand All @@ -80,7 +80,7 @@ func GetClaimableIncentives() (*osmocli.QueryDescriptor, *queryproto.ClaimableIn

func GetIncentiveRecords() (*osmocli.QueryDescriptor, *queryproto.IncentiveRecordsRequest) {
return &osmocli.QueryDescriptor{
Use: "incentive-records [poolId]",
Use: "incentive-records",
Short: "Query incentive records for a given pool",
Long: `{{.Short}}{{.ExampleHeader}}
{{.CommandPrefix}} incentive-records 1`,
Expand All @@ -89,7 +89,7 @@ func GetIncentiveRecords() (*osmocli.QueryDescriptor, *queryproto.IncentiveRecor

func GetCFMMPoolIdLinkFromConcentratedPoolId() (*osmocli.QueryDescriptor, *queryproto.CFMMPoolIdLinkFromConcentratedPoolIdRequest) {
return &osmocli.QueryDescriptor{
Use: "cfmm-pool-link-from-cl [poolId]",
Use: "cfmm-pool-link-from-cl",
Short: "Query cfmm pool id link from concentrated pool id",
Long: `{{.Short}}{{.ExampleHeader}}
{{.CommandPrefix}} cfmm-pool-link-from-cl 1`,
Expand All @@ -107,7 +107,7 @@ func GetTotalLiquidity() (*osmocli.QueryDescriptor, *queryproto.GetTotalLiquidit

func GetTickLiquidityNetInDirection() (*osmocli.QueryDescriptor, *queryproto.LiquidityNetInDirectionRequest) {
return &osmocli.QueryDescriptor{
Use: "liquidity-net-in-direction [pool-id] [token-in-denom] [start-tick] [use-current-tick] [bound-tick] [use-no-bound]",
Use: "liquidity-net-in-direction",
Short: "Query liquidity net in direction",
Long: `{{.Short}}{{.ExampleHeader}}
{{.CommandPrefix}} 4 uosmo "[-18000000]" true "[-9000000]" true`,
Expand All @@ -116,7 +116,7 @@ func GetTickLiquidityNetInDirection() (*osmocli.QueryDescriptor, *queryproto.Liq

func GetPoolAccumulatorRewards() (*osmocli.QueryDescriptor, *queryproto.PoolAccumulatorRewardsRequest) {
return &osmocli.QueryDescriptor{
Use: "pool-accumulator-rewards [pool-id]",
Use: "pool-accumulator-rewards",
Short: "Query pool accumulator rewards",
Long: `{{.Short}}{{.ExampleHeader}}
{{.CommandPrefix}} pool-accumulator-rewards 1`,
Expand All @@ -125,7 +125,7 @@ func GetPoolAccumulatorRewards() (*osmocli.QueryDescriptor, *queryproto.PoolAccu

func GetTickAccumulatorTrackers() (*osmocli.QueryDescriptor, *queryproto.TickAccumulatorTrackersRequest) {
return &osmocli.QueryDescriptor{
Use: "tick-accumulator-trackers [pool-id] [tick-index]",
Use: "tick-accumulator-trackers",
Short: "Query tick accumulator trackers",
Long: `{{.Short}}{{.ExampleHeader}}
{{.CommandPrefix}} tick-accumulator-trackers 1 "[-18000000]"`,
Expand Down
14 changes: 7 additions & 7 deletions x/concentrated-liquidity/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ var poolIdFlagOverride = map[string]string{

func NewCreateConcentratedPoolCmd() (*osmocli.TxCliDesc, *clmodel.MsgCreateConcentratedPool) {
return &osmocli.TxCliDesc{
Use: "create-pool [denom-0] [denom-1] [tick-spacing] [spread-factor]",
Use: "create-pool",
Short: "create a concentrated liquidity pool with the given denom pair, tick spacing, and spread factor",
Long: "denom-1 (the quote denom), tick spacing, and spread factors must all be authorized by the concentrated liquidity module",
Example: "osmosisd tx concentratedliquidity create-pool uion uosmo 100 0.01 --from val --chain-id osmosis-1 -b block --keyring-backend test --fees 1000uosmo",
Expand All @@ -47,47 +47,47 @@ func NewCreateConcentratedPoolCmd() (*osmocli.TxCliDesc, *clmodel.MsgCreateConce

func NewCreatePositionCmd() (*osmocli.TxCliDesc, *types.MsgCreatePosition) {
return &osmocli.TxCliDesc{
Use: "create-position [pool-id] [lower-tick] [upper-tick] [tokensProvided] [token-0-min-amount] [token-1-min-amount]",
Use: "create-position",
Short: "create or add to existing concentrated liquidity position",
Example: "osmosisd tx concentratedliquidity create-position 1 \"[-69082]\" 69082 10000uosmo,10000uion 0 0 --from val --chain-id osmosis-1 -b block --keyring-backend test --fees 1000uosmo",
}, &types.MsgCreatePosition{}
}

func NewAddToPositionCmd() (*osmocli.TxCliDesc, *types.MsgAddToPosition) {
return &osmocli.TxCliDesc{
Use: "add-to-position [position-id] [token-0] [token-1]",
Use: "add-to-position",
Short: "add to an existing concentrated liquidity position",
Example: "osmosisd tx concentratedliquidity add-to-position 10 1000000000uosmo 10000000uion --from val --chain-id localosmosis -b block --keyring-backend test --fees 1000000uosmo",
}, &types.MsgAddToPosition{}
}

func NewWithdrawPositionCmd() (*osmocli.TxCliDesc, *types.MsgWithdrawPosition) {
return &osmocli.TxCliDesc{
Use: "withdraw-position [position-id] [liquidity]",
Use: "withdraw-position",
Short: "withdraw from an existing concentrated liquidity position",
Example: "osmosisd tx concentratedliquidity withdraw-position 1 1000 --from val --chain-id localosmosis --keyring-backend=test --fees=1000uosmo",
}, &types.MsgWithdrawPosition{}
}

func NewCollectSpreadRewardsCmd() (*osmocli.TxCliDesc, *types.MsgCollectSpreadRewards) {
return &osmocli.TxCliDesc{
Use: "collect-spread-rewards [position-ids]",
Use: "collect-spread-rewards",
Short: "collect spread rewards from liquidity position(s)",
Example: "osmosisd tx concentratedliquidity collect-spread-rewards 998 --from val --chain-id localosmosis -b block --keyring-backend test --fees 1000000uosmo",
}, &types.MsgCollectSpreadRewards{}
}

func NewCollectIncentivesCmd() (*osmocli.TxCliDesc, *types.MsgCollectIncentives) {
return &osmocli.TxCliDesc{
Use: "collect-incentives [position-ids]",
Use: "collect-incentives",
Short: "collect incentives from liquidity position(s)",
Example: "osmosisd tx concentratedliquidity collect-incentives 1 --from val --chain-id localosmosis -b block --keyring-backend test --fees 10000uosmo",
}, &types.MsgCollectIncentives{}
}

func NewFungifyChargedPositionsCmd() (*osmocli.TxCliDesc, *types.MsgFungifyChargedPositions) {
return &osmocli.TxCliDesc{
Use: "fungify-positions [position-ids]",
Use: "fungify-positions",
Short: "Combine fully charged positions within the same range into a new single fully charged position",
Example: "osmosisd tx concentratedliquidity fungify-positions 1,2 --from val --keyring-backend test -b=block --chain-id=localosmosis --gas=1000000 --fees 20000uosmo",
}, &types.MsgFungifyChargedPositions{}
Expand Down
2 changes: 1 addition & 1 deletion x/cosmwasmpool/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func GetCmdPools() (*osmocli.QueryDescriptor, *queryproto.PoolsRequest) {

func GetCmdContractInfoByPoolId() (*osmocli.QueryDescriptor, *queryproto.ContractInfoByPoolIdRequest) {
return &osmocli.QueryDescriptor{
Use: "contract-info [pool-id]",
Use: "contract-info",
Short: "Query contract info by pool id",
Long: `{{.Short}}{{.ExampleHeader}}
{{.CommandPrefix}} pools`,
Expand Down
2 changes: 1 addition & 1 deletion x/cosmwasmpool/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func NewTxCmd() *cobra.Command {

func NewCreateCWPoolCmd() (*osmocli.TxCliDesc, *model.MsgCreateCosmWasmPool) {
return &osmocli.TxCliDesc{
Use: "create-pool [code-id] [instantiate-msg] [sender]",
Use: "create-pool",
Short: "create a cosmwasm pool",
Example: "osmosisd tx cosmwasmpool create-pool 1 uion,uosmo --from lo-test1 --keyring-backend test --chain-id localosmosis --fees 875uosmo -b=block",
NumArgs: 2,
Expand Down
Loading