Skip to content

Commit

Permalink
osmocli: parse Use field's arguments dynamically from a message (back…
Browse files Browse the repository at this point in the history
…port #6005)
  • Loading branch information
p0mvn committed Aug 30, 2023
1 parent 2b8b293 commit 333ade2
Show file tree
Hide file tree
Showing 29 changed files with 300 additions and 107 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ Fixes mainnet bugs w/ incorrect accumulation sumtrees, and CL handling for a bal
* [#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
* [#6012](https://github.com/osmosis-labs/osmosis/pull/6012) chore: add autocomplete to makefile

### 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"}

// 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

0 comments on commit 333ade2

Please sign in to comment.