mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
623 lines
18 KiB
Go
623 lines
18 KiB
Go
// Copyright (C) 2022-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package ammcmd
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math/big"
|
|
"time"
|
|
|
|
"github.com/luxfi/cli/pkg/application"
|
|
"github.com/luxfi/cli/pkg/ux"
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// Global flags
|
|
var (
|
|
networkFlag string
|
|
rpcFlag string
|
|
privateKeyFlag string
|
|
)
|
|
|
|
// NewCmd creates a new amm command
|
|
func NewCmd(_ *application.Lux) *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "amm",
|
|
Short: "Trade on Lux/Zoo AMM (Uniswap V2/V3)",
|
|
Long: `Commands for trading on Lux Exchange AMM pools.
|
|
|
|
Supported networks:
|
|
- lux (Lux Mainnet C-Chain, chain ID 96369)
|
|
- zoo (Zoo Mainnet, chain ID 200200)
|
|
- lux-testnet (Lux Testnet, chain ID 96368)
|
|
|
|
Wallet access via:
|
|
- MNEMONIC environment variable (BIP39 mnemonic)
|
|
- PRIVATE_KEY environment variable (hex private key)
|
|
- --private-key flag (hex private key)
|
|
|
|
Example usage:
|
|
lux amm balance --network zoo
|
|
lux amm swap --network zoo --from LUX --to USDT --amount 100
|
|
lux amm pools --network zoo
|
|
lux amm quote --network zoo --from LUX --to USDT --amount 100
|
|
lux amm balance --network zoo --private-key 0x...`,
|
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
return cmd.Help()
|
|
},
|
|
}
|
|
|
|
// Global flags
|
|
cmd.PersistentFlags().StringVar(&networkFlag, "network", "zoo", "Network: lux, zoo, or lux-testnet")
|
|
cmd.PersistentFlags().StringVar(&rpcFlag, "rpc", "", "Custom RPC endpoint (overrides network default)")
|
|
cmd.PersistentFlags().StringVar(&privateKeyFlag, "private-key", "", "Private key (hex) for wallet access")
|
|
|
|
// Add subcommands
|
|
cmd.AddCommand(newBalanceCmd())
|
|
cmd.AddCommand(newSwapCmd())
|
|
cmd.AddCommand(newQuoteCmd())
|
|
cmd.AddCommand(newPoolsCmd())
|
|
cmd.AddCommand(newTokensCmd())
|
|
cmd.AddCommand(newStatusCmd())
|
|
|
|
return cmd
|
|
}
|
|
|
|
// getAMM creates an AMM client based on flags
|
|
func getAMM() (*AMM, error) {
|
|
config := GetNetwork(networkFlag)
|
|
if config == nil {
|
|
return nil, fmt.Errorf("unknown network: %s", networkFlag)
|
|
}
|
|
|
|
// Override RPC if specified
|
|
if rpcFlag != "" {
|
|
config.RPC = rpcFlag
|
|
}
|
|
|
|
return NewAMM(config)
|
|
}
|
|
|
|
// newBalanceCmd creates the balance subcommand
|
|
func newBalanceCmd() *cobra.Command {
|
|
var tokenAddr string
|
|
|
|
cmd := &cobra.Command{
|
|
Use: "balance",
|
|
Short: "Show wallet balance",
|
|
Long: `Display native token and ERC20 token balances.
|
|
|
|
Examples:
|
|
lux amm balance --network zoo
|
|
lux amm balance --network zoo --token 0x...`,
|
|
RunE: func(_ *cobra.Command, _ []string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
amm, err := getAMM()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer amm.Close()
|
|
|
|
if err := amm.LoadWalletWithKey(privateKeyFlag); err != nil {
|
|
return err
|
|
}
|
|
|
|
ux.Logger.PrintToUser("Wallet: %s", amm.GetAddress().Hex())
|
|
ux.Logger.PrintToUser("Network: %s (Chain ID: %d)", amm.config.Name, amm.config.ChainID)
|
|
ux.Logger.PrintToUser("")
|
|
|
|
// Get native balance
|
|
balance, err := amm.GetBalance(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get balance: %w", err)
|
|
}
|
|
|
|
// Convert to LUX (18 decimals)
|
|
luxBalance := new(big.Float).SetInt(balance)
|
|
luxBalance.Quo(luxBalance, big.NewFloat(1e18))
|
|
ux.Logger.PrintToUser("Native Balance: %s LUX", luxBalance.Text('f', 6))
|
|
|
|
// Get token balance if specified
|
|
if tokenAddr != "" {
|
|
addr := common.HexToAddress(tokenAddr)
|
|
info, err := amm.GetTokenInfo(ctx, addr)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get token info: %w", err)
|
|
}
|
|
|
|
if info.Balance != nil {
|
|
divisor := new(big.Float).SetFloat64(1)
|
|
for i := uint8(0); i < info.Decimals; i++ {
|
|
divisor.Mul(divisor, big.NewFloat(10))
|
|
}
|
|
tokenBal := new(big.Float).SetInt(info.Balance)
|
|
tokenBal.Quo(tokenBal, divisor)
|
|
ux.Logger.PrintToUser("%s Balance: %s %s", info.Name, tokenBal.Text('f', 6), info.Symbol)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
cmd.Flags().StringVar(&tokenAddr, "token", "", "ERC20 token address to check")
|
|
|
|
return cmd
|
|
}
|
|
|
|
// newSwapCmd creates the swap subcommand
|
|
func newSwapCmd() *cobra.Command {
|
|
var (
|
|
fromToken string
|
|
toToken string
|
|
amount float64
|
|
slippage float64
|
|
dryRun bool
|
|
useV3 bool
|
|
)
|
|
|
|
cmd := &cobra.Command{
|
|
Use: "swap",
|
|
Short: "Swap tokens on AMM",
|
|
Long: `Swap tokens using Uniswap V2/V3 style AMM.
|
|
Tries V2 pools first, then V3 if no V2 pool exists.
|
|
|
|
Examples:
|
|
lux amm swap --network zoo --from 0x... --to 0x... --amount 100
|
|
lux amm swap --network zoo --from 0x... --to 0x... --amount 100 --slippage 1.0
|
|
lux amm swap --network zoo --from 0x... --to 0x... --amount 100 --v3
|
|
lux amm swap --network zoo --from 0x... --to 0x... --amount 100 --dry-run`,
|
|
RunE: func(_ *cobra.Command, _ []string) error {
|
|
if fromToken == "" || toToken == "" || amount == 0 {
|
|
return fmt.Errorf("required flags: --from, --to, --amount")
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
|
defer cancel()
|
|
|
|
amm, err := getAMM()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer amm.Close()
|
|
|
|
if err := amm.LoadWalletWithKey(privateKeyFlag); err != nil {
|
|
return err
|
|
}
|
|
|
|
fromAddr := common.HexToAddress(fromToken)
|
|
toAddr := common.HexToAddress(toToken)
|
|
|
|
// Get token info
|
|
fromInfo, err := amm.GetTokenInfo(ctx, fromAddr)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get from token info: %w", err)
|
|
}
|
|
|
|
toInfo, err := amm.GetTokenInfo(ctx, toAddr)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get to token info: %w", err)
|
|
}
|
|
|
|
// Convert amount to wei
|
|
amountWei := new(big.Float).SetFloat64(amount)
|
|
multiplier := new(big.Float).SetFloat64(1)
|
|
for i := uint8(0); i < fromInfo.Decimals; i++ {
|
|
multiplier.Mul(multiplier, big.NewFloat(10))
|
|
}
|
|
amountWei.Mul(amountWei, multiplier)
|
|
amountIn, _ := amountWei.Int(nil)
|
|
|
|
ux.Logger.PrintToUser("Swap Details:")
|
|
ux.Logger.PrintToUser(" From: %s (%s)", fromInfo.Symbol, fromAddr.Hex())
|
|
ux.Logger.PrintToUser(" To: %s (%s)", toInfo.Symbol, toAddr.Hex())
|
|
ux.Logger.PrintToUser(" Amount: %f %s", amount, fromInfo.Symbol)
|
|
ux.Logger.PrintToUser(" Slippage: %.2f%%", slippage)
|
|
ux.Logger.PrintToUser("")
|
|
|
|
var amountOut *big.Int
|
|
var isV3 bool
|
|
var feeTier uint32
|
|
|
|
// Try V2 first unless --v3 flag is set
|
|
if !useV3 {
|
|
path := []common.Address{fromAddr, toAddr}
|
|
amounts, err := amm.GetAmountsOut(ctx, amountIn, path)
|
|
if err == nil && len(amounts) >= 2 {
|
|
amountOut = amounts[len(amounts)-1]
|
|
ux.Logger.PrintToUser("Using V2 Pool")
|
|
}
|
|
}
|
|
|
|
// Try V3 if V2 failed or --v3 flag is set
|
|
if amountOut == nil {
|
|
feeTier, amountOut, err = amm.FindBestV3Pool(ctx, fromAddr, toAddr, amountIn)
|
|
if err != nil {
|
|
return fmt.Errorf("no pool found for pair: %w", err)
|
|
}
|
|
isV3 = true
|
|
ux.Logger.PrintToUser("Using V3 Pool (%.2f%% fee)", float64(feeTier)/10000)
|
|
}
|
|
|
|
divisor := new(big.Float).SetFloat64(1)
|
|
for i := uint8(0); i < toInfo.Decimals; i++ {
|
|
divisor.Mul(divisor, big.NewFloat(10))
|
|
}
|
|
outFloat := new(big.Float).SetInt(amountOut)
|
|
outFloat.Quo(outFloat, divisor)
|
|
|
|
ux.Logger.PrintToUser("Expected Output: %s %s", outFloat.Text('f', 6), toInfo.Symbol)
|
|
|
|
if dryRun {
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser("(dry-run mode - no transaction sent)")
|
|
return nil
|
|
}
|
|
|
|
// Calculate minimum output with slippage
|
|
slippageMul := 1.0 - (slippage / 100.0)
|
|
amountOutMinFloat := new(big.Float).SetInt(amountOut)
|
|
amountOutMinFloat.Mul(amountOutMinFloat, big.NewFloat(slippageMul))
|
|
amountOutMin, _ := amountOutMinFloat.Int(nil)
|
|
|
|
ux.Logger.PrintToUser("Min Output (with %.2f%% slippage): %s %s", slippage,
|
|
new(big.Float).Quo(new(big.Float).SetInt(amountOutMin), divisor).Text('f', 6), toInfo.Symbol)
|
|
ux.Logger.PrintToUser("")
|
|
|
|
if isV3 {
|
|
// V3 swap flow
|
|
allowance, err := amm.GetV3Allowance(ctx, fromAddr)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get V3 allowance: %w", err)
|
|
}
|
|
|
|
if allowance.Cmp(amountIn) < 0 {
|
|
ux.Logger.PrintToUser("Approving %s for V3 router...", fromInfo.Symbol)
|
|
maxUint256 := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1))
|
|
approveTx, err := amm.ApproveTokenForV3(ctx, fromAddr, maxUint256)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to approve for V3: %w", err)
|
|
}
|
|
ux.Logger.PrintToUser("Approval tx: %s", approveTx.Hash().Hex())
|
|
|
|
receipt, err := amm.WaitForTx(ctx, approveTx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to wait for approval: %w", err)
|
|
}
|
|
if receipt.Status != 1 {
|
|
return fmt.Errorf("approval transaction failed")
|
|
}
|
|
ux.Logger.PrintToUser("Approval confirmed!")
|
|
ux.Logger.PrintToUser("")
|
|
}
|
|
|
|
ux.Logger.PrintToUser("Executing V3 swap...")
|
|
deadline := time.Now().Add(20 * time.Minute)
|
|
swapTx, err := amm.SwapExactInputSingleV3(ctx, fromAddr, toAddr, feeTier, amountIn, amountOutMin, deadline)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute V3 swap: %w", err)
|
|
}
|
|
ux.Logger.PrintToUser("Swap tx: %s", swapTx.Hash().Hex())
|
|
|
|
receipt, err := amm.WaitForTx(ctx, swapTx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to wait for swap: %w", err)
|
|
}
|
|
if receipt.Status != 1 {
|
|
return fmt.Errorf("V3 swap transaction failed")
|
|
}
|
|
ux.Logger.PrintToUser("V3 Swap confirmed in block %d!", receipt.BlockNumber.Uint64())
|
|
ux.Logger.PrintToUser("Gas used: %d", receipt.GasUsed)
|
|
} else {
|
|
// V2 swap flow
|
|
allowance, err := amm.GetAllowance(ctx, fromAddr)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get allowance: %w", err)
|
|
}
|
|
|
|
if allowance.Cmp(amountIn) < 0 {
|
|
ux.Logger.PrintToUser("Approving %s for router...", fromInfo.Symbol)
|
|
maxUint256 := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1))
|
|
approveTx, err := amm.ApproveToken(ctx, fromAddr, maxUint256)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to approve: %w", err)
|
|
}
|
|
ux.Logger.PrintToUser("Approval tx: %s", approveTx.Hash().Hex())
|
|
|
|
receipt, err := amm.WaitForTx(ctx, approveTx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to wait for approval: %w", err)
|
|
}
|
|
if receipt.Status != 1 {
|
|
return fmt.Errorf("approval transaction failed")
|
|
}
|
|
ux.Logger.PrintToUser("Approval confirmed!")
|
|
ux.Logger.PrintToUser("")
|
|
}
|
|
|
|
ux.Logger.PrintToUser("Executing swap...")
|
|
deadline := time.Now().Add(20 * time.Minute)
|
|
path := []common.Address{fromAddr, toAddr}
|
|
swapTx, err := amm.SwapExactTokensForTokens(ctx, amountIn, amountOutMin, path, deadline)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute swap: %w", err)
|
|
}
|
|
ux.Logger.PrintToUser("Swap tx: %s", swapTx.Hash().Hex())
|
|
|
|
receipt, err := amm.WaitForTx(ctx, swapTx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to wait for swap: %w", err)
|
|
}
|
|
if receipt.Status != 1 {
|
|
return fmt.Errorf("swap transaction failed")
|
|
}
|
|
ux.Logger.PrintToUser("Swap confirmed in block %d!", receipt.BlockNumber.Uint64())
|
|
ux.Logger.PrintToUser("Gas used: %d", receipt.GasUsed)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
cmd.Flags().StringVar(&fromToken, "from", "", "Token address to swap from")
|
|
cmd.Flags().StringVar(&toToken, "to", "", "Token address to swap to")
|
|
cmd.Flags().Float64Var(&amount, "amount", 0, "Amount to swap")
|
|
cmd.Flags().Float64Var(&slippage, "slippage", 0.5, "Max slippage tolerance (%)")
|
|
cmd.Flags().BoolVar(&dryRun, "dry-run", false, "Only show quote, don't execute")
|
|
cmd.Flags().BoolVar(&useV3, "v3", false, "Force V3 pool")
|
|
|
|
return cmd
|
|
}
|
|
|
|
// newQuoteCmd creates the quote subcommand
|
|
func newQuoteCmd() *cobra.Command {
|
|
var (
|
|
fromToken string
|
|
toToken string
|
|
amount float64
|
|
useV3 bool
|
|
)
|
|
|
|
cmd := &cobra.Command{
|
|
Use: "quote",
|
|
Short: "Get swap quote",
|
|
Long: `Get a quote for swapping tokens without executing.
|
|
Tries V2 pools first, then V3 if no V2 pool exists.
|
|
|
|
Examples:
|
|
lux amm quote --network zoo --from 0x... --to 0x... --amount 100
|
|
lux amm quote --network zoo --from 0x... --to 0x... --amount 100 --v3`,
|
|
RunE: func(_ *cobra.Command, _ []string) error {
|
|
if fromToken == "" || toToken == "" || amount == 0 {
|
|
return fmt.Errorf("required flags: --from, --to, --amount")
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
amm, err := getAMM()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer amm.Close()
|
|
|
|
fromAddr := common.HexToAddress(fromToken)
|
|
toAddr := common.HexToAddress(toToken)
|
|
|
|
// Get token info
|
|
fromInfo, err := amm.GetTokenInfo(ctx, fromAddr)
|
|
if err != nil {
|
|
fromInfo = &TokenInfo{Symbol: "TOKEN", Decimals: 18}
|
|
}
|
|
|
|
toInfo, err := amm.GetTokenInfo(ctx, toAddr)
|
|
if err != nil {
|
|
toInfo = &TokenInfo{Symbol: "TOKEN", Decimals: 18}
|
|
}
|
|
|
|
// Convert amount to wei
|
|
amountWei := new(big.Float).SetFloat64(amount)
|
|
multiplier := new(big.Float).SetFloat64(1)
|
|
for i := uint8(0); i < fromInfo.Decimals; i++ {
|
|
multiplier.Mul(multiplier, big.NewFloat(10))
|
|
}
|
|
amountWei.Mul(amountWei, multiplier)
|
|
amountIn, _ := amountWei.Int(nil)
|
|
|
|
var amountOut *big.Int
|
|
var poolType string
|
|
var feeTier uint32
|
|
|
|
// Try V2 first unless --v3 flag is set
|
|
if !useV3 {
|
|
path := []common.Address{fromAddr, toAddr}
|
|
amounts, err := amm.GetAmountsOut(ctx, amountIn, path)
|
|
if err == nil && len(amounts) >= 2 {
|
|
amountOut = amounts[len(amounts)-1]
|
|
poolType = "V2"
|
|
}
|
|
}
|
|
|
|
// Try V3 if V2 failed or --v3 flag is set
|
|
if amountOut == nil {
|
|
feeTier, amountOut, err = amm.FindBestV3Pool(ctx, fromAddr, toAddr, amountIn)
|
|
if err != nil {
|
|
return fmt.Errorf("no pool found for pair: %w", err)
|
|
}
|
|
poolType = fmt.Sprintf("V3 (%.2f%% fee)", float64(feeTier)/10000)
|
|
}
|
|
|
|
divisor := new(big.Float).SetFloat64(1)
|
|
for i := uint8(0); i < toInfo.Decimals; i++ {
|
|
divisor.Mul(divisor, big.NewFloat(10))
|
|
}
|
|
outFloat := new(big.Float).SetInt(amountOut)
|
|
outFloat.Quo(outFloat, divisor)
|
|
|
|
// Calculate price
|
|
inFloat := new(big.Float).SetFloat64(amount)
|
|
price := new(big.Float).Quo(outFloat, inFloat)
|
|
|
|
ux.Logger.PrintToUser("Quote (%s):", poolType)
|
|
ux.Logger.PrintToUser(" Input: %f %s", amount, fromInfo.Symbol)
|
|
ux.Logger.PrintToUser(" Output: %s %s", outFloat.Text('f', 6), toInfo.Symbol)
|
|
ux.Logger.PrintToUser(" Price: 1 %s = %s %s", fromInfo.Symbol, price.Text('f', 6), toInfo.Symbol)
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
cmd.Flags().StringVar(&fromToken, "from", "", "Token address to swap from")
|
|
cmd.Flags().StringVar(&toToken, "to", "", "Token address to swap to")
|
|
cmd.Flags().Float64Var(&amount, "amount", 0, "Amount to quote")
|
|
cmd.Flags().BoolVar(&useV3, "v3", false, "Force V3 pool")
|
|
|
|
return cmd
|
|
}
|
|
|
|
// newPoolsCmd creates the pools subcommand
|
|
func newPoolsCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "pools",
|
|
Short: "List liquidity pools",
|
|
Long: `List all liquidity pools on the AMM.
|
|
|
|
Examples:
|
|
lux amm pools --network zoo`,
|
|
RunE: func(_ *cobra.Command, _ []string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
amm, err := getAMM()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer amm.Close()
|
|
|
|
ux.Logger.PrintToUser("Network: %s (Chain ID: %d)", amm.config.Name, amm.config.ChainID)
|
|
ux.Logger.PrintToUser("")
|
|
|
|
count, err := amm.GetPoolCount(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get pool count: %w", err)
|
|
}
|
|
|
|
ux.Logger.PrintToUser("Total Pools: %d", count)
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser("V2 Factory: %s", amm.config.V2Factory.Hex())
|
|
ux.Logger.PrintToUser("V3 Factory: %s", amm.config.V3Factory.Hex())
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
return cmd
|
|
}
|
|
|
|
// newTokensCmd creates the tokens subcommand
|
|
func newTokensCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "tokens [address...]",
|
|
Short: "Get token information",
|
|
Long: `Get information about ERC20 tokens.
|
|
|
|
Examples:
|
|
lux amm tokens --network zoo 0x...`,
|
|
RunE: func(_ *cobra.Command, args []string) error {
|
|
if len(args) == 0 {
|
|
return fmt.Errorf("specify at least one token address")
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
amm, err := getAMM()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer amm.Close()
|
|
|
|
ux.Logger.PrintToUser("Token Information:")
|
|
ux.Logger.PrintToUser("")
|
|
|
|
for _, addr := range args {
|
|
tokenAddr := common.HexToAddress(addr)
|
|
info, err := amm.GetTokenInfo(ctx, tokenAddr)
|
|
if err != nil {
|
|
ux.Logger.PrintToUser(" %s: error - %v", addr, err)
|
|
continue
|
|
}
|
|
|
|
ux.Logger.PrintToUser(" %s (%s)", info.Name, info.Symbol)
|
|
ux.Logger.PrintToUser(" Address: %s", info.Address.Hex())
|
|
ux.Logger.PrintToUser(" Decimals: %d", info.Decimals)
|
|
ux.Logger.PrintToUser("")
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
return cmd
|
|
}
|
|
|
|
// newStatusCmd creates the status subcommand
|
|
func newStatusCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "status",
|
|
Short: "Show AMM status",
|
|
Long: `Show AMM contract status and network info.
|
|
|
|
Examples:
|
|
lux amm status --network zoo`,
|
|
RunE: func(_ *cobra.Command, _ []string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
amm, err := getAMM()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer amm.Close()
|
|
|
|
blockNum, err := amm.client.BlockNumber(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get block number: %w", err)
|
|
}
|
|
|
|
ux.Logger.PrintToUser("AMM Status:")
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser("Network:")
|
|
ux.Logger.PrintToUser(" Name: %s", amm.config.Name)
|
|
ux.Logger.PrintToUser(" Chain ID: %d", amm.config.ChainID)
|
|
ux.Logger.PrintToUser(" RPC: %s", amm.config.RPC)
|
|
ux.Logger.PrintToUser(" Block: %d", blockNum)
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser("Contracts:")
|
|
ux.Logger.PrintToUser(" V2 Factory: %s", amm.config.V2Factory.Hex())
|
|
ux.Logger.PrintToUser(" V2 Router: %s", amm.config.V2Router.Hex())
|
|
ux.Logger.PrintToUser(" V3 Factory: %s", amm.config.V3Factory.Hex())
|
|
ux.Logger.PrintToUser(" V3 Router: %s", amm.config.V3Router.Hex())
|
|
ux.Logger.PrintToUser(" Multicall: %s", amm.config.Multicall.Hex())
|
|
ux.Logger.PrintToUser(" Quoter: %s", amm.config.Quoter.Hex())
|
|
|
|
// Get pool count
|
|
count, err := amm.GetPoolCount(ctx)
|
|
if err == nil {
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser("Statistics:")
|
|
ux.Logger.PrintToUser(" V2 Pools: %d", count)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
return cmd
|
|
}
|