github.com/gravity-devs/liquidity@v1.5.3/x/liquidity/client/cli/tx.go (about)

     1  package cli
     2  
     3  // DONTCOVER
     4  // client is excluded from test coverage in the poc phase milestone 1 and will be included in milestone 2 with completeness
     5  
     6  import (
     7  	"fmt"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/cosmos/cosmos-sdk/client"
    12  	"github.com/cosmos/cosmos-sdk/client/flags"
    13  	"github.com/cosmos/cosmos-sdk/client/tx"
    14  	sdk "github.com/cosmos/cosmos-sdk/types"
    15  	"github.com/cosmos/cosmos-sdk/version"
    16  	"github.com/spf13/cobra"
    17  
    18  	"github.com/gravity-devs/liquidity/x/liquidity/types"
    19  )
    20  
    21  // GetTxCmd returns a root CLI command handler for all x/liquidity transaction commands.
    22  func GetTxCmd() *cobra.Command {
    23  	liquidityTxCmd := &cobra.Command{
    24  		Use:                        types.ModuleName,
    25  		Short:                      "Liquidity transaction subcommands",
    26  		DisableFlagParsing:         true,
    27  		SuggestionsMinimumDistance: 2,
    28  		RunE:                       client.ValidateCmd,
    29  	}
    30  
    31  	liquidityTxCmd.AddCommand(
    32  		NewCreatePoolCmd(),
    33  		NewDepositWithinBatchCmd(),
    34  		NewWithdrawWithinBatchCmd(),
    35  		NewSwapWithinBatchCmd(),
    36  	)
    37  
    38  	return liquidityTxCmd
    39  }
    40  
    41  // Create new liquidity pool with the specified pool type and deposit coins.
    42  func NewCreatePoolCmd() *cobra.Command {
    43  	cmd := &cobra.Command{
    44  		Use:   "create-pool [pool-type] [deposit-coins]",
    45  		Args:  cobra.ExactArgs(2),
    46  		Short: "Create liquidity pool and deposit coins",
    47  		Long: strings.TrimSpace(
    48  			fmt.Sprintf(`Create liquidity pool and deposit coins.
    49  
    50  Example:
    51  $ %s tx %s create-pool 1 1000000000uatom,50000000000uusd --from mykey
    52  
    53  This example creates a liquidity pool of pool-type 1 (two coins) and deposits 1000000000uatom and 50000000000uusd.
    54  New liquidity pools can be created only for coin combinations that do not already exist in the network.
    55  
    56  [pool-type]: The id of the liquidity pool-type. The only supported pool type is 1
    57  [deposit-coins]: The amount of coins to deposit to the liquidity pool. The number of deposit coins must be 2 in pool type 1.
    58  `,
    59  				version.AppName, types.ModuleName,
    60  			),
    61  		),
    62  		RunE: func(cmd *cobra.Command, args []string) error {
    63  			clientCtx, err := client.GetClientTxContext(cmd)
    64  			if err != nil {
    65  				return err
    66  			}
    67  			poolCreator := clientCtx.GetFromAddress()
    68  
    69  			// Get pool type index
    70  			poolTypeID, err := strconv.ParseUint(args[0], 10, 32)
    71  			if err != nil {
    72  				return fmt.Errorf("pool-type %s not a valid uint, input a valid unsigned 32-bit integer for pool-type", args[0])
    73  			}
    74  
    75  			// Get deposit coins
    76  			depositCoins, err := sdk.ParseCoinsNormalized(args[1])
    77  			if err != nil {
    78  				return err
    79  			}
    80  
    81  			err = depositCoins.Validate()
    82  			if err != nil {
    83  				return err
    84  			}
    85  
    86  			if poolTypeID != 1 {
    87  				return types.ErrPoolTypeNotExists
    88  			}
    89  
    90  			if depositCoins.Len() != 2 {
    91  				return fmt.Errorf("the number of deposit coins must be two in pool-type 1")
    92  			}
    93  
    94  			msg := types.NewMsgCreatePool(poolCreator, uint32(poolTypeID), depositCoins)
    95  			if err := msg.ValidateBasic(); err != nil {
    96  				return err
    97  			}
    98  
    99  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
   100  		},
   101  	}
   102  
   103  	flags.AddTxFlagsToCmd(cmd)
   104  
   105  	return cmd
   106  }
   107  
   108  // Deposit coins to the specified liquidity pool.
   109  func NewDepositWithinBatchCmd() *cobra.Command {
   110  	cmd := &cobra.Command{
   111  		Use:   "deposit [pool-id] [deposit-coins]",
   112  		Args:  cobra.ExactArgs(2),
   113  		Short: "Deposit coins to a liquidity pool",
   114  		Long: strings.TrimSpace(
   115  			fmt.Sprintf(`Deposit coins a liquidity pool.
   116  
   117  This deposit request is not processed immediately since it is accumulated in the liquidity pool batch.
   118  All requests in a batch are treated equally and executed at the same swap price.
   119  
   120  Example:
   121  $ %s tx %s deposit 1 100000000uatom,5000000000uusd --from mykey
   122  
   123  This example request deposits 100000000uatom and 5000000000uusd to pool-id 1.
   124  Deposits must be the same coin denoms as the reserve coins.
   125  
   126  [pool-id]: The pool id of the liquidity pool
   127  [deposit-coins]: The amount of coins to deposit to the liquidity pool
   128  `,
   129  				version.AppName, types.ModuleName,
   130  			),
   131  		),
   132  		RunE: func(cmd *cobra.Command, args []string) error {
   133  			clientCtx, err := client.GetClientTxContext(cmd)
   134  			if err != nil {
   135  				return err
   136  			}
   137  			depositor := clientCtx.GetFromAddress()
   138  
   139  			// Get pool type index
   140  			poolID, err := strconv.ParseUint(args[0], 10, 64)
   141  			if err != nil {
   142  				return fmt.Errorf("pool-id %s not a valid uint, input a valid unsigned 32-bit integer for pool-id", args[0])
   143  			}
   144  
   145  			// Get deposit coins
   146  			depositCoins, err := sdk.ParseCoinsNormalized(args[1])
   147  			if err != nil {
   148  				return err
   149  			}
   150  
   151  			err = depositCoins.Validate()
   152  			if err != nil {
   153  				return err
   154  			}
   155  
   156  			if depositCoins.Len() != 2 {
   157  				return fmt.Errorf("the number of deposit coins must be two in the pool-type 1")
   158  			}
   159  
   160  			msg := types.NewMsgDepositWithinBatch(depositor, poolID, depositCoins)
   161  			if err := msg.ValidateBasic(); err != nil {
   162  				return err
   163  			}
   164  
   165  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
   166  		},
   167  	}
   168  
   169  	flags.AddTxFlagsToCmd(cmd)
   170  
   171  	return cmd
   172  }
   173  
   174  // Withdraw pool coin from the specified liquidity pool.
   175  func NewWithdrawWithinBatchCmd() *cobra.Command {
   176  	cmd := &cobra.Command{
   177  		Use:   "withdraw [pool-id] [pool-coin]",
   178  		Args:  cobra.ExactArgs(2),
   179  		Short: "Withdraw pool coin from the specified liquidity pool",
   180  		Long: strings.TrimSpace(
   181  			fmt.Sprintf(`Withdraw pool coin from the specified liquidity pool.
   182  
   183  This swap request is not processed immediately since it is accumulated in the liquidity pool batch.
   184  All requests in a batch are treated equally and executed at the same swap price.
   185  
   186  Example:
   187  $ %s tx %s withdraw 1 10000pool96EF6EA6E5AC828ED87E8D07E7AE2A8180570ADD212117B2DA6F0B75D17A6295 --from mykey
   188  
   189  This example request withdraws 10000 pool coin from the specified liquidity pool.
   190  The appropriate pool coin must be requested from the specified pool.
   191  
   192  [pool-id]: The pool id of the liquidity pool
   193  [pool-coin]: The amount of pool coin to withdraw from the liquidity pool
   194  `,
   195  				version.AppName, types.ModuleName,
   196  			),
   197  		),
   198  		RunE: func(cmd *cobra.Command, args []string) error {
   199  			clientCtx, err := client.GetClientTxContext(cmd)
   200  			if err != nil {
   201  				return err
   202  			}
   203  			withdrawer := clientCtx.GetFromAddress()
   204  
   205  			// Get pool type index
   206  			poolID, err := strconv.ParseUint(args[0], 10, 64)
   207  			if err != nil {
   208  				return fmt.Errorf("pool-id %s not a valid uint, input a valid unsigned 32-bit integer for pool-id", args[0])
   209  			}
   210  
   211  			// Get pool coin of the target pool
   212  			poolCoin, err := sdk.ParseCoinNormalized(args[1])
   213  			if err != nil {
   214  				return err
   215  			}
   216  
   217  			err = poolCoin.Validate()
   218  			if err != nil {
   219  				return err
   220  			}
   221  
   222  			msg := types.NewMsgWithdrawWithinBatch(withdrawer, poolID, poolCoin)
   223  			if err := msg.ValidateBasic(); err != nil {
   224  				return err
   225  			}
   226  
   227  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
   228  		},
   229  	}
   230  
   231  	flags.AddTxFlagsToCmd(cmd)
   232  
   233  	return cmd
   234  }
   235  
   236  // Swap offer coin with demand coin from the specified liquidity pool with the given order price.
   237  func NewSwapWithinBatchCmd() *cobra.Command {
   238  	cmd := &cobra.Command{
   239  		Use:   "swap [pool-id] [swap-type] [offer-coin] [demand-coin-denom] [order-price] [swap-fee-rate]",
   240  		Args:  cobra.ExactArgs(6),
   241  		Short: "Swap offer coin with demand coin from the liquidity pool with the given order price",
   242  		Long: strings.TrimSpace(
   243  			fmt.Sprintf(`Swap offer coin with demand coin from the liquidity pool with the given order price.
   244  
   245  This swap request is not processed immediately since it is accumulated in the liquidity pool batch.
   246  All requests in a batch are treated equally and executed at the same swap price.
   247  The order of swap requests is ignored since the universal swap price is calculated in every batch to prevent front running.
   248  
   249  The requested swap is executed with a swap price that is calculated from the given swap price function of the pool, the other swap requests, and the liquidity pool coin reserve status.
   250  Swap orders are executed only when the execution swap price is equal to or greater than the submitted order price of the swap order.
   251  
   252  Example:
   253  $ %s tx %s swap 1 1 50000000uusd uatom 0.019 0.003 --from mykey
   254  
   255  For this example, imagine that an existing liquidity pool has with 1000000000uatom and 50000000000uusd.
   256  This example request swaps 50000000uusd for at least 950000uatom with the order price of 0.019 and swap fee rate of 0.003.
   257  A sufficient balance of half of the swap-fee-rate of the offer coin is required to reserve the offer coin fee.
   258  
   259  The order price is the exchange ratio of X/Y, where X is the amount of the first coin and Y is the amount of the second coin when their denoms are sorted alphabetically.
   260  Increasing order price reduces the possibility for your request to be processed and results in buying uatom at a lower price than the pool price.
   261  
   262  For explicit calculations, The swap fee rate must be the value that set as liquidity parameter in the current network.
   263  The only supported swap-type is 1. For the detailed swap algorithm, see https://github.com/gravity-devs/liquidity
   264  
   265  [pool-id]: The pool id of the liquidity pool 
   266  [swap-type]: The swap type of the swap message. The only supported swap type is 1 (instant swap).
   267  [offer-coin]: The amount of offer coin to swap 
   268  [demand-coin-denom]: The denomination of the coin to exchange with offer coin 
   269  [order-price]: The limit order price for the swap order. The price is the exchange ratio of X/Y where X is the amount of the first coin and Y is the amount of the second coin when their denoms are sorted alphabetically 
   270  [swap-fee-rate]: The swap fee rate to pay for swap that is proportional to swap amount. The swap fee rate must be the value that set as liquidity parameter in the current network.
   271  `,
   272  				version.AppName, types.ModuleName,
   273  			),
   274  		),
   275  		RunE: func(cmd *cobra.Command, args []string) error {
   276  			clientCtx, err := client.GetClientTxContext(cmd)
   277  			if err != nil {
   278  				return err
   279  			}
   280  			swapRequester := clientCtx.GetFromAddress()
   281  
   282  			// Get pool id
   283  			poolID, err := strconv.ParseUint(args[0], 10, 64)
   284  			if err != nil {
   285  				return fmt.Errorf("pool-id %s not a valid uint, input a valid unsigned 32-bit integer for pool-id", args[0])
   286  			}
   287  
   288  			// Get swap type
   289  			swapTypeID, err := strconv.ParseUint(args[1], 10, 32)
   290  			if err != nil {
   291  				return fmt.Errorf("swap-type %s not a valid uint, input a valid unsigned 32-bit integer for swap-type", args[2])
   292  			}
   293  
   294  			if swapTypeID != 1 {
   295  				return types.ErrSwapTypeNotExists
   296  			}
   297  
   298  			// Get offer coin
   299  			offerCoin, err := sdk.ParseCoinNormalized(args[2])
   300  			if err != nil {
   301  				return err
   302  			}
   303  
   304  			err = offerCoin.Validate()
   305  			if err != nil {
   306  				return err
   307  			}
   308  
   309  			err = sdk.ValidateDenom(args[3])
   310  			if err != nil {
   311  				return err
   312  			}
   313  
   314  			orderPrice, err := sdk.NewDecFromStr(args[4])
   315  			if err != nil {
   316  				return err
   317  			}
   318  
   319  			swapFeeRate, err := sdk.NewDecFromStr(args[5])
   320  			if err != nil {
   321  				return err
   322  			}
   323  
   324  			msg := types.NewMsgSwapWithinBatch(swapRequester, poolID, uint32(swapTypeID), offerCoin, args[3], orderPrice, swapFeeRate)
   325  			if err := msg.ValidateBasic(); err != nil {
   326  				return err
   327  			}
   328  
   329  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
   330  		},
   331  	}
   332  
   333  	flags.AddTxFlagsToCmd(cmd)
   334  
   335  	return cmd
   336  }