cartha-cli 1.0.7__tar.gz → 1.0.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/PKG-INFO +1 -1
  2. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/config.py +1 -1
  3. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/miner_password.py +0 -7
  4. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/miner_status.py +4 -25
  5. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/pair_status.py +2 -27
  6. cartha_cli-1.0.8/cartha_cli/commands/pools.py +89 -0
  7. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/prove_lock.py +60 -143
  8. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/register.py +0 -16
  9. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/config.py +4 -4
  10. cartha_cli-1.0.8/cartha_cli/pool_client.py +214 -0
  11. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/verifier.py +15 -0
  12. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/pyproject.toml +1 -1
  13. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/tests/test_cli.py +38 -52
  14. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/uv.lock +1 -1
  15. cartha_cli-1.0.7/cartha_cli/commands/pools.py +0 -112
  16. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/.github/workflows/README.md +0 -0
  17. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/.github/workflows/ci.yml +0 -0
  18. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/.github/workflows/publish.yml +0 -0
  19. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/.gitignore +0 -0
  20. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/.ruff.toml +0 -0
  21. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/CONTRIBUTING.md +0 -0
  22. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/LICENSE +0 -0
  23. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/Makefile +0 -0
  24. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/README.md +0 -0
  25. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/__init__.py +0 -0
  26. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/bt.py +0 -0
  27. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/__init__.py +0 -0
  28. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/common.py +0 -0
  29. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/health.py +0 -0
  30. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/help.py +0 -0
  31. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/shared_options.py +0 -0
  32. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/version.py +0 -0
  33. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/display.py +0 -0
  34. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/eth712.py +0 -0
  35. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/main.py +0 -0
  36. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/pair.py +0 -0
  37. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/testnet/README.md +0 -0
  38. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/testnet/__init__.py +0 -0
  39. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/testnet/pool_ids.py +0 -0
  40. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/utils.py +0 -0
  41. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/wallet.py +0 -0
  42. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/docs/COMMANDS.md +0 -0
  43. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/docs/FEEDBACK.md +0 -0
  44. {cartha_cli-1.0.7 → cartha_cli-1.0.8}/tests/conftest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cartha-cli
3
- Version: 1.0.7
3
+ Version: 1.0.8
4
4
  Summary: CLI utilities for Cartha subnet miners.
5
5
  Project-URL: Homepage, https://cartha.finance
6
6
  Project-URL: Repository, https://github.com/General-Tao-Ventures/cartha-cli
@@ -17,7 +17,7 @@ from .common import console
17
17
  ENV_VAR_DOCS: dict[str, dict[str, Any]] = {
18
18
  "CARTHA_VERIFIER_URL": {
19
19
  "description": "URL of the Cartha verifier service",
20
- "default": "https://cartha-verifier-826542474079.us-central1.run.app",
20
+ "default": "https://cartha-verifier-193291340038.us-central1.run.app",
21
21
  "required": False,
22
22
  },
23
23
  "CARTHA_NETWORK": {
@@ -69,13 +69,6 @@ def miner_password(
69
69
  netuid = 78
70
70
  elif network == "finney":
71
71
  netuid = 35
72
- # Warn that mainnet is not live yet
73
- console.print()
74
- console.print("[bold yellow]⚠️ MAINNET NOT AVAILABLE YET[/]")
75
- console.print("[yellow]Cartha subnet is currently in testnet phase (subnet 78).[/]")
76
- console.print("[yellow]Mainnet (subnet 35) has not been announced yet.[/]")
77
- console.print("[dim]Use --network test to access testnet.[/]")
78
- console.print()
79
72
  # Note: netuid parameter is kept for backwards compatibility / explicit override
80
73
 
81
74
  from ..config import get_verifier_url_for_network
@@ -79,13 +79,6 @@ def miner_status(
79
79
  netuid = 78
80
80
  elif network == "finney":
81
81
  netuid = 35
82
- # Warn that mainnet is not live yet
83
- console.print()
84
- console.print("[bold yellow]⚠️ MAINNET NOT AVAILABLE YET[/]")
85
- console.print("[yellow]Cartha subnet is currently in testnet phase (subnet 78).[/]")
86
- console.print("[yellow]Mainnet (subnet 35) has not been announced yet.[/]")
87
- console.print("[dim]Use --network test to access testnet.[/]")
88
- console.print()
89
82
  # Note: netuid parameter is kept for backwards compatibility / explicit override
90
83
 
91
84
  from ..config import get_verifier_url_for_network
@@ -488,25 +481,14 @@ def miner_status(
488
481
 
489
482
  console.print(pool_table)
490
483
 
491
- # Concise reminder
492
- console.print()
493
- console.print("[bold cyan]━━━ Reminders ━━━[/]")
494
- console.print(
495
- "• Lock expiration: USDC returned automatically, emissions stop for that pool."
496
- )
497
- console.print(
498
- "• Top-ups/extensions: Happen automatically on-chain. No CLI action needed."
499
- )
500
- if pools and len(pools) > 1:
501
- console.print(
502
- "• Multiple pools: Each pool is tracked separately. Expired pools stop earning, others continue."
503
- )
504
-
505
484
  # Link to web interface
506
485
  console.print()
507
486
  console.print("[bold cyan]━━━ Web Interface ━━━[/]")
508
487
  console.print(
509
- "[cyan]🌐 View and manage your positions:[/] [bold]https://cartha.finance[/]"
488
+ "[cyan]🌐 Manage your positions:[/] [bold]https://cartha.finance[/]"
489
+ )
490
+ console.print(
491
+ "[dim] • Deposit new lock positions[/]"
510
492
  )
511
493
  console.print(
512
494
  "[dim] • View all your lock positions[/]"
@@ -517,8 +499,5 @@ def miner_status(
517
499
  console.print(
518
500
  "[dim] • Top up existing positions[/]"
519
501
  )
520
- console.print(
521
- "[dim] • Claim testnet USDC from faucet[/]"
522
- )
523
502
 
524
503
  return
@@ -34,26 +34,8 @@ from .shared_options import (
34
34
  json_output_option,
35
35
  )
36
36
 
37
- # Import pool name helper
38
- # Initialize fallback function first to ensure it's always defined
39
- def _fallback_pool_id_to_name(pool_id: str) -> str | None:
40
- """Simple fallback to decode pool ID."""
41
- try:
42
- hex_str = pool_id.lower().removeprefix("0x")
43
- pool_bytes = bytes.fromhex(hex_str)
44
- name = pool_bytes.rstrip(b"\x00").decode("utf-8", errors="ignore")
45
- if name and name.isprintable():
46
- return name
47
- except Exception:
48
- pass
49
- return None
50
-
51
- # Try to import from testnet module, fallback to default if not available
52
- try:
53
- from ..testnet.pool_ids import pool_id_to_name
54
- except (ImportError, ModuleNotFoundError):
55
- # Use fallback function
56
- pool_id_to_name = _fallback_pool_id_to_name
37
+ # Import pool name helper from pool_client (fetches from verifier API)
38
+ from ..pool_client import pool_id_to_name
57
39
 
58
40
 
59
41
  def pair_status(
@@ -100,13 +82,6 @@ def pair_status(
100
82
  netuid = 78
101
83
  elif network == "finney":
102
84
  netuid = 35
103
- # Warn that mainnet is not live yet
104
- console.print()
105
- console.print("[bold yellow]⚠️ MAINNET NOT AVAILABLE YET[/]")
106
- console.print("[yellow]Cartha subnet is currently in testnet phase (subnet 78).[/]")
107
- console.print("[yellow]Mainnet (subnet 35) has not been announced yet.[/]")
108
- console.print("[dim]Use --network test to access testnet.[/]")
109
- console.print()
110
85
  # Note: netuid parameter is kept for backwards compatibility / explicit override
111
86
 
112
87
  from ..config import get_verifier_url_for_network
@@ -0,0 +1,89 @@
1
+ """Pools command - show current available pools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typer
6
+
7
+ from .common import console
8
+ from ..verifier import VerifierError, fetch_pools
9
+
10
+
11
+ def pools(
12
+ json_output: bool = typer.Option(
13
+ False, "--json", help="Emit responses as JSON."
14
+ ),
15
+ ) -> None:
16
+ """Show all available pools with their names, IDs, vault addresses, and chain IDs.
17
+
18
+ USAGE:
19
+ ------
20
+ cartha vault pools (or: cartha v pools)
21
+ cartha vault pools --json (for JSON output)
22
+
23
+ OUTPUT:
24
+ -------
25
+ - Pool names: BTC/USD, ETH/USD, EUR/USD, etc.
26
+ - Pool IDs: Full hex identifiers (0x...)
27
+ - Vault addresses: Contract addresses for each pool
28
+ - Chain IDs: Which blockchain network (8453 for Base Mainnet, 84532 for Base Sepolia)
29
+
30
+ Use these pool names directly in 'cartha vault lock -p BTCUSD ...'
31
+ """
32
+ try:
33
+ # Fetch pools from verifier API
34
+ pools_list = fetch_pools()
35
+
36
+ if json_output:
37
+ # JSON output format
38
+ import json
39
+ console.print(json.dumps(pools_list, indent=2))
40
+ return
41
+
42
+ # Multi-line text output
43
+ if not pools_list:
44
+ console.print("[yellow]No pools available.[/]")
45
+ return
46
+
47
+ console.print("\n[bold cyan]Available Pools[/]\n")
48
+
49
+ for idx, pool in enumerate(pools_list, 1):
50
+ pool_name = pool.get("name", "Unknown")
51
+ # Verifier returns camelCase keys
52
+ pool_id_hex = pool.get("poolId", "")
53
+ vault_addr = pool.get("vaultAddress")
54
+ chain_id = pool.get("chainId")
55
+ network = pool.get("network", "")
56
+
57
+ # Ensure full pool ID is displayed (normalize to ensure 0x prefix)
58
+ pool_id_display = pool_id_hex if pool_id_hex.startswith("0x") else f"0x{pool_id_hex}"
59
+
60
+ # Ensure full vault address is displayed
61
+ vault_display = vault_addr if vault_addr else "[dim]N/A[/]"
62
+
63
+ # Format chain display with network name
64
+ if chain_id:
65
+ if chain_id == 8453:
66
+ chain_display = f"{chain_id} (Base Mainnet)"
67
+ elif chain_id == 84532:
68
+ chain_display = f"{chain_id} (Base Sepolia)"
69
+ else:
70
+ chain_display = str(chain_id)
71
+ else:
72
+ chain_display = "[dim]N/A[/]"
73
+
74
+ console.print(f"[bold cyan]Pool {idx}:[/] {pool_name}")
75
+ console.print(f" [yellow]Pool ID:[/] {pool_id_display}")
76
+ console.print(f" [green]Vault Address:[/] {vault_display}")
77
+ console.print(f" [dim]Chain ID:[/] {chain_display}")
78
+
79
+ # Add spacing between pools except for the last one
80
+ if idx < len(pools_list):
81
+ console.print()
82
+
83
+ except VerifierError as exc:
84
+ console.print(f"[bold red]Error:[/] Failed to fetch pools from verifier: {exc}")
85
+ console.print("[dim]Tip: Check your network connection and verifier URL configuration.[/]")
86
+ raise typer.Exit(code=1)
87
+ except Exception as exc:
88
+ console.print(f"[bold red]Error:[/] Failed to list pools: {exc}")
89
+ raise typer.Exit(code=1)
@@ -42,71 +42,17 @@ from .shared_options import (
42
42
  json_output_option,
43
43
  )
44
44
 
45
- # Import pool helpers for pool_id conversion
46
- # Initialize fallback functions first to ensure they're always defined
47
- def _fallback_pool_name_to_id(pool_name: str) -> str:
48
- """Fallback: encode pool name as hex."""
49
- name_bytes = pool_name.encode("utf-8")
50
- padded = name_bytes.ljust(32, b"\x00")
51
- return "0x" + padded.hex()
52
-
53
- def _fallback_pool_id_to_name(pool_id: str) -> str | None:
54
- """Fallback: try to decode."""
55
- try:
56
- hex_str = pool_id.lower().removeprefix("0x")
57
- pool_bytes = bytes.fromhex(hex_str)
58
- name = pool_bytes.rstrip(b"\x00").decode("utf-8", errors="ignore")
59
- return name if name and name.isprintable() else None
60
- except Exception:
61
- return None
62
-
63
- def _fallback_format_pool_id(pool_id: str) -> str:
64
- """Fallback: return pool_id as-is."""
65
- return pool_id
66
-
67
- def _fallback_list_pools() -> dict[str, str]:
68
- """Fallback: return empty dict."""
69
- return {}
70
-
71
- def _fallback_pool_id_to_vault_address(pool_id: str) -> str | None:
72
- """Fallback: return None."""
73
- return None
74
-
75
- def _fallback_vault_address_to_pool_id(vault_address: str) -> str | None:
76
- """Fallback: return None."""
77
- return None
78
-
79
- def _fallback_pool_id_to_chain_id(pool_id: str) -> int | None:
80
- """Fallback: return None."""
81
- return None
82
-
83
- def _fallback_vault_address_to_chain_id(vault_address: str) -> int | None:
84
- """Fallback: return None."""
85
- return None
86
-
87
- # Try to import from testnet module, fallback to defaults if not available
88
- try:
89
- # Import from cartha_cli.testnet (works both in development and when installed)
90
- from ..testnet.pool_ids import (
91
- format_pool_id,
92
- list_pools,
93
- pool_id_to_chain_id,
94
- pool_id_to_name,
95
- pool_id_to_vault_address,
96
- pool_name_to_id,
97
- vault_address_to_chain_id,
98
- vault_address_to_pool_id,
99
- )
100
- except (ImportError, ModuleNotFoundError):
101
- # Use fallback functions if import failed
102
- pool_name_to_id = _fallback_pool_name_to_id
103
- pool_id_to_name = _fallback_pool_id_to_name
104
- format_pool_id = _fallback_format_pool_id
105
- list_pools = _fallback_list_pools
106
- pool_id_to_vault_address = _fallback_pool_id_to_vault_address
107
- vault_address_to_pool_id = _fallback_vault_address_to_pool_id
108
- pool_id_to_chain_id = _fallback_pool_id_to_chain_id
109
- vault_address_to_chain_id = _fallback_vault_address_to_chain_id
45
+ # Import pool helpers from pool_client (fetches from verifier API)
46
+ from ..pool_client import (
47
+ format_pool_id,
48
+ list_pools,
49
+ pool_id_to_chain_id,
50
+ pool_id_to_name,
51
+ pool_id_to_vault_address,
52
+ pool_name_to_id,
53
+ vault_address_to_chain_id,
54
+ vault_address_to_pool_id,
55
+ )
110
56
 
111
57
 
112
58
  def prove_lock(
@@ -153,22 +99,6 @@ def prove_lock(
153
99
  netuid = 78
154
100
  elif network == "finney":
155
101
  netuid = 35
156
- # Warn that mainnet is not live yet
157
- console.print()
158
- console.print("[bold yellow]⚠️ MAINNET NOT AVAILABLE YET[/]")
159
- console.print()
160
- console.print("[yellow]Cartha subnet is currently in testnet phase (subnet 78 on test network).[/]")
161
- console.print("[yellow]Mainnet (subnet 35 on finney network) has not been announced yet.[/]")
162
- console.print()
163
- console.print("[bold cyan]To use testnet:[/]")
164
- console.print(" cartha vault lock --network test ...")
165
- console.print()
166
- console.print("[dim]If you continue with finney network, the CLI will attempt to connect[/]")
167
- console.print("[dim]but the subnet may not be operational yet.[/]")
168
- console.print()
169
- if not Confirm.ask("[yellow]Continue with finney network anyway?[/]", default=False):
170
- console.print("[yellow]Cancelled. Use --network test for testnet.[/]")
171
- raise typer.Exit(code=0)
172
102
  else:
173
103
  # Default to finney settings if unknown network
174
104
  netuid = 35
@@ -386,72 +316,51 @@ def prove_lock(
386
316
  if not pool_id_normalized.startswith("0x"):
387
317
  pool_id_normalized = "0x" + pool_id_normalized
388
318
 
389
- try:
390
- auto_chain_id = pool_id_to_chain_id(pool_id_normalized)
391
- except (NameError, AttributeError, TypeError):
392
- # Function not available - this shouldn't happen if imports worked
393
- # But handle gracefully by trying to import it
394
- try:
395
- from ..testnet.pool_ids import pool_id_to_chain_id
396
- auto_chain_id = pool_id_to_chain_id(pool_id_normalized)
397
- except (ImportError, ModuleNotFoundError, TypeError):
398
- pass
319
+ auto_chain_id = pool_id_to_chain_id(pool_id_normalized)
399
320
 
400
321
  if not auto_chain_id:
401
322
  # Fallback: try to get from vault address
402
- try:
403
- auto_chain_id = vault_address_to_chain_id(vault)
404
- except (NameError, AttributeError, TypeError):
405
- try:
406
- from ..testnet.pool_ids import vault_address_to_chain_id
407
- auto_chain_id = vault_address_to_chain_id(vault)
408
- except (ImportError, ModuleNotFoundError, TypeError):
409
- pass
323
+ auto_chain_id = vault_address_to_chain_id(vault)
410
324
 
411
325
  if auto_chain_id:
412
326
  chain = auto_chain_id
413
- chain_name = "Base Sepolia" if chain == 84532 else f"Chain {chain}"
327
+ if chain == 8453:
328
+ chain_name = "Base Mainnet"
329
+ elif chain == 84532:
330
+ chain_name = "Base Sepolia"
331
+ else:
332
+ chain_name = f"Chain {chain}"
414
333
  console.print(
415
334
  f"[bold green]✓ Auto-matched chain ID[/] - {chain_name} (chain ID: {chain})"
416
335
  )
417
336
  else:
418
- # Fallback: if on testnet and we have a vault, default to Base Sepolia (84532)
419
- if network == "test" and vault:
420
- chain = 84532
421
- console.print(
422
- f"[bold green]✓ Auto-matched chain ID[/] - Base Sepolia (chain ID: 84532) [dim](testnet default)[/]"
423
- )
424
- else:
425
- # Prompt for chain ID if no mapping found
426
- console.print(
427
- "[yellow]⚠ No chain ID mapping found. Please provide chain ID.[/]"
428
- )
429
- while True:
430
- try:
431
- chain_input = typer.prompt("Chain ID", show_default=False)
432
- chain = int(chain_input)
433
- if chain <= 0:
434
- console.print(
435
- "[bold red]Error:[/] Chain ID must be a positive integer"
436
- )
437
- continue
438
- break
439
- except ValueError:
440
- console.print("[bold red]Error:[/] Chain ID must be a valid integer")
337
+ # Prompt for chain ID if no mapping found
338
+ console.print(
339
+ "[yellow]⚠ No chain ID mapping found. Please provide chain ID.[/]"
340
+ )
341
+ while True:
342
+ try:
343
+ chain_input = typer.prompt("Chain ID", show_default=False)
344
+ chain = int(chain_input)
345
+ if chain <= 0:
346
+ console.print(
347
+ "[bold red]Error:[/] Chain ID must be a positive integer"
348
+ )
349
+ continue
350
+ break
351
+ except ValueError:
352
+ console.print("[bold red]Error:[/] Chain ID must be a valid integer")
441
353
  else:
442
354
  # Chain ID was provided, verify it matches vault if possible
443
- expected_chain_id = None
444
- try:
445
- expected_chain_id = vault_address_to_chain_id(vault)
446
- except (NameError, AttributeError):
447
- try:
448
- from ..testnet.pool_ids import vault_address_to_chain_id
449
- expected_chain_id = vault_address_to_chain_id(vault)
450
- except (ImportError, ModuleNotFoundError):
451
- pass
355
+ expected_chain_id = vault_address_to_chain_id(vault)
452
356
 
453
357
  if expected_chain_id and expected_chain_id != chain:
454
- chain_name = "Base Sepolia" if expected_chain_id == 84532 else f"Chain {expected_chain_id}"
358
+ if expected_chain_id == 8453:
359
+ chain_name = "Base Mainnet"
360
+ elif expected_chain_id == 84532:
361
+ chain_name = "Base Sepolia"
362
+ else:
363
+ chain_name = f"Chain {expected_chain_id}"
455
364
  console.print(
456
365
  f"[bold yellow]⚠ Warning:[/] Vault {vault} is on {chain_name} (chain ID: {expected_chain_id}), "
457
366
  f"but you specified chain ID {chain}"
@@ -674,8 +583,11 @@ def prove_lock(
674
583
  "\n[bold yellow]⚠️ Execute these transactions to complete your lock:[/]\n"
675
584
  )
676
585
 
677
- # USDC contract address for Base Sepolia
678
- usdc_contract_address = "0x2340D09c348930A76c8c2783EDa8610F699A51A8"
586
+ # USDC contract address based on chain ID
587
+ if chain == 8453: # Base Mainnet
588
+ usdc_contract_address = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
589
+ else: # Base Sepolia (default)
590
+ usdc_contract_address = "0x2340D09c348930A76c8c2783EDa8610F699A51A8"
679
591
 
680
592
  console.print("[bold cyan]━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[/]")
681
593
  console.print("[bold]Phase 1: Approve USDC[/]")
@@ -694,7 +606,7 @@ def prove_lock(
694
606
  phase1_params = {
695
607
  "phase": "1",
696
608
  "chainId": str(chain),
697
- "usdcAddress": "0x2340D09c348930A76c8c2783EDa8610F699A51A8",
609
+ "usdcAddress": usdc_contract_address,
698
610
  "vaultAddress": vault,
699
611
  "spender": vault,
700
612
  "amount": str(amount_base_units),
@@ -714,8 +626,11 @@ def prove_lock(
714
626
  console.print("[dim]The CLI will automatically detect when the approval is complete.[/]")
715
627
  console.print("[dim]You can also press Ctrl+C to skip and continue manually.[/]")
716
628
 
717
- # Base Sepolia RPC endpoint
718
- base_sepolia_rpc = "https://sepolia.base.org"
629
+ # RPC endpoint based on chain ID
630
+ if chain == 8453: # Base Mainnet
631
+ rpc_endpoint = "https://mainnet.base.org"
632
+ else: # Base Sepolia (default)
633
+ rpc_endpoint = "https://sepolia.base.org"
719
634
 
720
635
  # ERC20 ABI for allowance function and Approval event
721
636
  erc20_abi = [
@@ -746,7 +661,7 @@ def prove_lock(
746
661
 
747
662
  approval_detected = False
748
663
  try:
749
- w3 = Web3(Web3.HTTPProvider(base_sepolia_rpc))
664
+ w3 = Web3(Web3.HTTPProvider(rpc_endpoint))
750
665
  usdc_contract = w3.eth.contract(
751
666
  address=Web3.to_checksum_address(usdc_contract_address),
752
667
  abi=erc20_abi
@@ -941,13 +856,15 @@ def prove_lock(
941
856
  }
942
857
  ]
943
858
 
944
- # Get RPC endpoint for Base Sepolia
859
+ # Get RPC endpoint based on chain ID
945
860
  rpc_url = None
946
- if chain == 84532: # Base Sepolia
861
+ if chain == 8453: # Base Mainnet
862
+ rpc_url = "https://mainnet.base.org"
863
+ elif chain == 84532: # Base Sepolia
947
864
  rpc_url = "https://sepolia.base.org"
948
865
  else:
949
- # Only Base Sepolia is supported for auto-detection
950
- console.print(f"[yellow]Warning:[/] Auto-detection only supports Base Sepolia (chain ID 84532), but chain ID {chain} was specified.")
866
+ # Unsupported chain for auto-detection
867
+ console.print(f"[yellow]Warning:[/] Auto-detection only supports Base Mainnet (8453) and Base Sepolia (84532), but chain ID {chain} was specified.")
951
868
  console.print("[dim]You'll need to enter the transaction hash manually.[/]")
952
869
  rpc_url = None
953
870
 
@@ -79,22 +79,6 @@ def register(
79
79
  netuid = 78
80
80
  elif network == "finney":
81
81
  netuid = 35
82
- # Warn that mainnet is not live yet
83
- console.print()
84
- console.print("[bold yellow]⚠️ MAINNET NOT AVAILABLE YET[/]")
85
- console.print()
86
- console.print("[yellow]Cartha subnet is currently in testnet phase (subnet 78 on test network).[/]")
87
- console.print("[yellow]Mainnet (subnet 35 on finney network) has not been announced yet.[/]")
88
- console.print()
89
- console.print("[bold cyan]To use testnet:[/]")
90
- console.print(" cartha miner register --network test")
91
- console.print()
92
- console.print("[dim]If you continue with finney network, registration will attempt[/]")
93
- console.print("[dim]subnet 35 but the subnet may not be operational yet.[/]")
94
- console.print()
95
- if not Confirm.ask("[yellow]Continue with finney network anyway?[/]", default=False):
96
- console.print("[yellow]Cancelled. Use --network test for testnet.[/]")
97
- raise typer.Exit(code=0)
98
82
  # Note: netuid parameter is kept for backwards compatibility / explicit override
99
83
 
100
84
  from ..config import get_verifier_url_for_network
@@ -13,7 +13,7 @@ from pydantic_settings import BaseSettings
13
13
  # Network to verifier URL mapping
14
14
  NETWORK_VERIFIER_MAP = {
15
15
  "test": "https://cartha-verifier-826542474079.us-central1.run.app",
16
- "finney": None, # No mainnet verifier yet - use default or env var
16
+ "finney": "https://cartha-verifier-193291340038.us-central1.run.app",
17
17
  }
18
18
 
19
19
 
@@ -36,13 +36,13 @@ def get_verifier_url_for_network(network: str) -> str:
36
36
  if mapped_url:
37
37
  return mapped_url
38
38
 
39
- # Default fallback (testnet for now since no mainnet)
40
- return "https://cartha-verifier-826542474079.us-central1.run.app"
39
+ # Default fallback to mainnet
40
+ return "https://cartha-verifier-193291340038.us-central1.run.app"
41
41
 
42
42
 
43
43
  class Settings(BaseSettings):
44
44
  verifier_url: str = Field(
45
- "https://cartha-verifier-826542474079.us-central1.run.app", alias="CARTHA_VERIFIER_URL"
45
+ "https://cartha-verifier-193291340038.us-central1.run.app", alias="CARTHA_VERIFIER_URL"
46
46
  )
47
47
  network: str = Field("finney", alias="CARTHA_NETWORK")
48
48
  netuid: int = Field(35, alias="CARTHA_NETUID")
@@ -0,0 +1,214 @@
1
+ """Pool client - fetches pool data from verifier API with caching."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from functools import lru_cache
6
+ from typing import Any
7
+
8
+ from .verifier import VerifierError, fetch_pools
9
+
10
+
11
+ # Cache pool data for 5 minutes (300 seconds)
12
+ # Using a module-level cache that can be cleared if needed
13
+ _pools_cache: dict[str, Any] | None = None
14
+ _cache_timestamp: float = 0
15
+ _CACHE_TTL_SECONDS = 300
16
+
17
+
18
+ def _get_cached_pools() -> list[dict[str, Any]]:
19
+ """Get pools with caching (5 minute TTL)."""
20
+ global _pools_cache, _cache_timestamp
21
+ import time
22
+
23
+ now = time.time()
24
+ if _pools_cache is None or (now - _cache_timestamp) > _CACHE_TTL_SECONDS:
25
+ try:
26
+ _pools_cache = fetch_pools()
27
+ _cache_timestamp = now
28
+ except VerifierError:
29
+ # If fetch fails and we have cached data, use it
30
+ if _pools_cache is not None:
31
+ return _pools_cache
32
+ # Otherwise return empty list
33
+ return []
34
+
35
+ return _pools_cache or []
36
+
37
+
38
+ def clear_cache() -> None:
39
+ """Clear the pools cache."""
40
+ global _pools_cache, _cache_timestamp
41
+ _pools_cache = None
42
+ _cache_timestamp = 0
43
+
44
+
45
+ def list_pools() -> dict[str, str]:
46
+ """List all available pools from verifier.
47
+
48
+ Returns:
49
+ Dictionary mapping pool names (e.g., "BTCUSD") to hex pool IDs
50
+ """
51
+ pools = _get_cached_pools()
52
+ result = {}
53
+ for pool in pools:
54
+ name = pool.get("name", "")
55
+ # Verifier returns camelCase keys
56
+ pool_id = pool.get("poolId", "")
57
+ if name and pool_id:
58
+ # Normalize name: "BTC/USD" -> "BTCUSD"
59
+ normalized_name = name.replace("/", "").upper()
60
+ result[normalized_name] = pool_id.lower()
61
+ return result
62
+
63
+
64
+ def pool_name_to_id(pool_name: str) -> str:
65
+ """Convert a readable pool name to hex pool ID.
66
+
67
+ Args:
68
+ pool_name: Readable name (e.g., "BTCUSD", "BTC/USD")
69
+
70
+ Returns:
71
+ Hex pool ID (bytes32 format)
72
+ """
73
+ normalized = pool_name.replace("/", "").upper()
74
+ pools = list_pools()
75
+
76
+ if normalized in pools:
77
+ return pools[normalized]
78
+
79
+ # Fallback: encode the name as hex (for unknown pools)
80
+ name_bytes = pool_name.encode("utf-8")
81
+ if len(name_bytes) > 32:
82
+ raise ValueError(f"Pool name too long: {pool_name} (max 32 bytes)")
83
+ padded = name_bytes.rjust(32, b"\x00")
84
+ return "0x" + padded.hex()
85
+
86
+
87
+ def pool_id_to_name(pool_id: str) -> str | None:
88
+ """Convert a hex pool ID to readable name if available.
89
+
90
+ Args:
91
+ pool_id: Hex pool ID (bytes32 format)
92
+
93
+ Returns:
94
+ Readable name if found, None otherwise
95
+ """
96
+ pools = _get_cached_pools()
97
+ pool_id_lower = pool_id.lower()
98
+
99
+ for pool in pools:
100
+ # Verifier returns camelCase keys
101
+ if pool.get("poolId", "").lower() == pool_id_lower:
102
+ name = pool.get("name", "")
103
+ # Return normalized name without slash
104
+ return name.replace("/", "").upper() if name else None
105
+
106
+ return None
107
+
108
+
109
+ def format_pool_id(pool_id: str) -> str:
110
+ """Format a pool ID for display (shows readable name if available).
111
+
112
+ Args:
113
+ pool_id: Hex pool ID
114
+
115
+ Returns:
116
+ Formatted string: "BTCUSD (0x...)" or just "0x..." if no name found
117
+ """
118
+ name = pool_id_to_name(pool_id)
119
+ if name:
120
+ return f"{name} ({pool_id})"
121
+ return pool_id
122
+
123
+
124
+ def pool_id_to_vault_address(pool_id: str) -> str | None:
125
+ """Get vault address for a given pool ID.
126
+
127
+ Args:
128
+ pool_id: Pool ID in hex format (bytes32)
129
+
130
+ Returns:
131
+ Vault address if found, None otherwise
132
+ """
133
+ pools = _get_cached_pools()
134
+ pool_id_lower = pool_id.lower()
135
+
136
+ for pool in pools:
137
+ # Verifier returns camelCase keys
138
+ if pool.get("poolId", "").lower() == pool_id_lower:
139
+ return pool.get("vaultAddress")
140
+
141
+ return None
142
+
143
+
144
+ def vault_address_to_pool_id(vault_address: str) -> str | None:
145
+ """Get pool ID for a given vault address.
146
+
147
+ Args:
148
+ vault_address: Vault contract address
149
+
150
+ Returns:
151
+ Pool ID if found, None otherwise
152
+ """
153
+ pools = _get_cached_pools()
154
+ vault_lower = vault_address.lower()
155
+
156
+ for pool in pools:
157
+ # Verifier returns camelCase keys
158
+ if pool.get("vaultAddress", "").lower() == vault_lower:
159
+ return pool.get("poolId", "").lower()
160
+
161
+ return None
162
+
163
+
164
+ def pool_id_to_chain_id(pool_id: str) -> int | None:
165
+ """Get chain ID for a given pool ID.
166
+
167
+ Args:
168
+ pool_id: Pool ID in hex format (bytes32)
169
+
170
+ Returns:
171
+ Chain ID if found, None otherwise
172
+ """
173
+ pools = _get_cached_pools()
174
+ pool_id_lower = pool_id.lower()
175
+
176
+ for pool in pools:
177
+ # Verifier returns camelCase keys
178
+ if pool.get("poolId", "").lower() == pool_id_lower:
179
+ return pool.get("chainId")
180
+
181
+ return None
182
+
183
+
184
+ def vault_address_to_chain_id(vault_address: str) -> int | None:
185
+ """Get chain ID for a given vault address.
186
+
187
+ Args:
188
+ vault_address: Vault contract address
189
+
190
+ Returns:
191
+ Chain ID if found, None otherwise
192
+ """
193
+ pools = _get_cached_pools()
194
+ vault_lower = vault_address.lower()
195
+
196
+ for pool in pools:
197
+ # Verifier returns camelCase keys
198
+ if pool.get("vaultAddress", "").lower() == vault_lower:
199
+ return pool.get("chainId")
200
+
201
+ return None
202
+
203
+
204
+ __all__ = [
205
+ "clear_cache",
206
+ "list_pools",
207
+ "pool_name_to_id",
208
+ "pool_id_to_name",
209
+ "format_pool_id",
210
+ "pool_id_to_vault_address",
211
+ "vault_address_to_pool_id",
212
+ "pool_id_to_chain_id",
213
+ "vault_address_to_chain_id",
214
+ ]
@@ -324,6 +324,20 @@ def process_lock_transaction(
324
324
  )
325
325
 
326
326
 
327
+ def fetch_pools() -> list[dict[str, Any]]:
328
+ """Fetch available pools from verifier.
329
+
330
+ Returns list of pools with:
331
+ - name: Human-readable pool name (e.g., "BTC/USD")
332
+ - pool_id: Hex pool ID (bytes32)
333
+ - vault_address: Vault contract address
334
+ - chain_id: Chain ID
335
+ - network: Network name ("mainnet" or "testnet")
336
+ """
337
+ data = _request("GET", "/pools")
338
+ return data.get("pools", [])
339
+
340
+
327
341
  # REMOVED: Old endpoints - replaced by new lock flow
328
342
  # fetch_pair_password, register_pair_password, submit_lock_proof removed
329
343
 
@@ -339,4 +353,5 @@ __all__ = [
339
353
  "request_lock_signature",
340
354
  "get_lock_status",
341
355
  "process_lock_transaction",
356
+ "fetch_pools",
342
357
  ]
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "cartha-cli"
7
- version = "1.0.7"
7
+ version = "1.0.8"
8
8
  description = "CLI utilities for Cartha subnet miners."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11,<3.12"
@@ -1466,37 +1466,32 @@ def test_prove_lock_eip712_signature_without_lock_days(monkeypatch):
1466
1466
 
1467
1467
  def test_vault_pools_command(monkeypatch):
1468
1468
  """Test vault pools command."""
1469
- # Mock list_pools to return test data
1470
- def fake_list_pools():
1471
- return {
1472
- "BTCUSD": "0xee62665949c883f9e0f6f002eac32e00bd59dfe6c34e92a91c37d6a8322d6489",
1473
- "ETHUSD": "0x0b43555ace6b39aae1b894097d0a9fc17f504c62fea598fa206cc6f5088e6e45",
1474
- }
1475
-
1476
- def fake_pool_id_to_vault_address(pool_id: str):
1477
- vaults = {
1478
- "0xee62665949c883f9e0f6f002eac32e00bd59dfe6c34e92a91c37d6a8322d6489": "0x471D86764B7F99b894ee38FcD3cEFF6EAB321b69",
1479
- "0x0b43555ace6b39aae1b894097d0a9fc17f504c62fea598fa206cc6f5088e6e45": "0xdB74B44957A71c95406C316f8d3c5571FA588248",
1480
- }
1481
- return vaults.get(pool_id.lower())
1482
-
1483
- def fake_pool_id_to_chain_id(pool_id: str):
1484
- return 84532
1469
+ # Mock fetch_pools to return test data (verifier API response format with camelCase keys)
1470
+ def fake_fetch_pools():
1471
+ return [
1472
+ {
1473
+ "name": "BTC/USD",
1474
+ "poolId": "0xee62665949c883f9e0f6f002eac32e00bd59dfe6c34e92a91c37d6a8322d6489",
1475
+ "vaultAddress": "0x471D86764B7F99b894ee38FcD3cEFF6EAB321b69",
1476
+ "chainId": 84532,
1477
+ "network": "testnet",
1478
+ },
1479
+ {
1480
+ "name": "ETH/USD",
1481
+ "poolId": "0x0b43555ace6b39aae1b894097d0a9fc17f504c62fea598fa206cc6f5088e6e45",
1482
+ "vaultAddress": "0xdB74B44957A71c95406C316f8d3c5571FA588248",
1483
+ "chainId": 84532,
1484
+ "network": "testnet",
1485
+ },
1486
+ ]
1485
1487
 
1486
- monkeypatch.setattr("cartha_cli.commands.pools.list_pools", fake_list_pools)
1487
- monkeypatch.setattr(
1488
- "cartha_cli.commands.pools.pool_id_to_vault_address",
1489
- fake_pool_id_to_vault_address,
1490
- )
1491
- monkeypatch.setattr(
1492
- "cartha_cli.commands.pools.pool_id_to_chain_id", fake_pool_id_to_chain_id
1493
- )
1488
+ monkeypatch.setattr("cartha_cli.commands.pools.fetch_pools", fake_fetch_pools)
1494
1489
 
1495
1490
  result = runner.invoke(app, ["vault", "pools"])
1496
1491
  assert result.exit_code == 0
1497
1492
  assert "Available Pools" in result.stdout
1498
- assert "BTCUSD" in result.stdout
1499
- assert "ETHUSD" in result.stdout
1493
+ assert "BTC/USD" in result.stdout
1494
+ assert "ETH/USD" in result.stdout
1500
1495
  assert "0xee62665949c883f9e0f6f002eac32e00bd59dfe6c34e92a91c37d6a8322d6489" in result.stdout
1501
1496
  assert "0x471D86764B7F99b894ee38FcD3cEFF6EAB321b69" in result.stdout
1502
1497
  assert "84532" in result.stdout
@@ -1504,29 +1499,19 @@ def test_vault_pools_command(monkeypatch):
1504
1499
 
1505
1500
  def test_vault_pools_command_json(monkeypatch):
1506
1501
  """Test vault pools command with JSON output."""
1507
- # Mock list_pools to return test data
1508
- def fake_list_pools():
1509
- return {
1510
- "BTCUSD": "0xee62665949c883f9e0f6f002eac32e00bd59dfe6c34e92a91c37d6a8322d6489",
1511
- }
1512
-
1513
- def fake_pool_id_to_vault_address(pool_id: str):
1514
- vaults = {
1515
- "0xee62665949c883f9e0f6f002eac32e00bd59dfe6c34e92a91c37d6a8322d6489": "0x471D86764B7F99b894ee38FcD3cEFF6EAB321b69",
1516
- }
1517
- return vaults.get(pool_id.lower())
1518
-
1519
- def fake_pool_id_to_chain_id(pool_id: str):
1520
- return 84532
1502
+ # Mock fetch_pools to return test data (verifier API response format with camelCase keys)
1503
+ def fake_fetch_pools():
1504
+ return [
1505
+ {
1506
+ "name": "BTC/USD",
1507
+ "poolId": "0xee62665949c883f9e0f6f002eac32e00bd59dfe6c34e92a91c37d6a8322d6489",
1508
+ "vaultAddress": "0x471D86764B7F99b894ee38FcD3cEFF6EAB321b69",
1509
+ "chainId": 84532,
1510
+ "network": "testnet",
1511
+ },
1512
+ ]
1521
1513
 
1522
- monkeypatch.setattr("cartha_cli.commands.pools.list_pools", fake_list_pools)
1523
- monkeypatch.setattr(
1524
- "cartha_cli.commands.pools.pool_id_to_vault_address",
1525
- fake_pool_id_to_vault_address,
1526
- )
1527
- monkeypatch.setattr(
1528
- "cartha_cli.commands.pools.pool_id_to_chain_id", fake_pool_id_to_chain_id
1529
- )
1514
+ monkeypatch.setattr("cartha_cli.commands.pools.fetch_pools", fake_fetch_pools)
1530
1515
 
1531
1516
  result = runner.invoke(app, ["vault", "pools", "--json"])
1532
1517
  assert result.exit_code == 0
@@ -1535,10 +1520,11 @@ def test_vault_pools_command_json(monkeypatch):
1535
1520
  json_end = stdout.rfind("]")
1536
1521
  payload = json.loads(stdout[json_start : json_end + 1])
1537
1522
  assert len(payload) == 1
1538
- assert payload[0]["name"] == "BTCUSD"
1539
- assert payload[0]["pool_id"] == "0xee62665949c883f9e0f6f002eac32e00bd59dfe6c34e92a91c37d6a8322d6489"
1540
- assert payload[0]["vault_address"] == "0x471D86764B7F99b894ee38FcD3cEFF6EAB321b69"
1541
- assert payload[0]["chain_id"] == 84532
1523
+ # JSON output uses camelCase keys from verifier response
1524
+ assert payload[0]["name"] == "BTC/USD"
1525
+ assert payload[0]["poolId"] == "0xee62665949c883f9e0f6f002eac32e00bd59dfe6c34e92a91c37d6a8322d6489"
1526
+ assert payload[0]["vaultAddress"] == "0x471D86764B7F99b894ee38FcD3cEFF6EAB321b69"
1527
+ assert payload[0]["chainId"] == 84532
1542
1528
 
1543
1529
 
1544
1530
  def test_miner_status_with_refresh_already_verified(monkeypatch):
@@ -238,7 +238,7 @@ wheels = [
238
238
 
239
239
  [[package]]
240
240
  name = "cartha-cli"
241
- version = "0.1.0"
241
+ version = "1.0.8"
242
242
  source = { editable = "." }
243
243
  dependencies = [
244
244
  { name = "bittensor" },
@@ -1,112 +0,0 @@
1
- """Pools command - show current available pools."""
2
-
3
- from __future__ import annotations
4
-
5
- import typer
6
-
7
- from .common import console
8
-
9
- # Import pool helpers for pool_id conversion
10
- # Initialize fallback functions first to ensure they're always defined
11
- def _fallback_list_pools() -> dict[str, str]:
12
- """Fallback: return empty dict."""
13
- return {}
14
-
15
- def _fallback_pool_id_to_vault_address(pool_id: str) -> str | None:
16
- """Fallback: return None."""
17
- return None
18
-
19
- def _fallback_pool_id_to_chain_id(pool_id: str) -> int | None:
20
- """Fallback: return None."""
21
- return None
22
-
23
- # Try to import from testnet module, fallback to defaults if not available
24
- try:
25
- from ..testnet.pool_ids import (
26
- list_pools,
27
- pool_id_to_chain_id,
28
- pool_id_to_vault_address,
29
- )
30
- except (ImportError, ModuleNotFoundError):
31
- # Use fallback functions if import failed
32
- list_pools = _fallback_list_pools
33
- pool_id_to_vault_address = _fallback_pool_id_to_vault_address
34
- pool_id_to_chain_id = _fallback_pool_id_to_chain_id
35
-
36
-
37
- def pools(
38
- json_output: bool = typer.Option(
39
- False, "--json", help="Emit responses as JSON."
40
- ),
41
- ) -> None:
42
- """Show all available pools with their names, IDs, vault addresses, and chain IDs.
43
-
44
- USAGE:
45
- ------
46
- cartha vault pools (or: cartha v pools)
47
- cartha vault pools --json (for JSON output)
48
-
49
- OUTPUT:
50
- -------
51
- - Pool names: BTCUSD, ETHUSD, EURUSD, etc.
52
- - Pool IDs: Full hex identifiers (0x...)
53
- - Vault addresses: Contract addresses for each pool
54
- - Chain IDs: Which blockchain network (e.g., 84532 for Base Sepolia)
55
-
56
- Use these pool names directly in 'cartha vault lock -p BTCUSD ...'
57
- """
58
- try:
59
- available_pools = list_pools()
60
-
61
- if json_output:
62
- # JSON output format
63
- import json
64
-
65
- pools_data = []
66
- for pool_name, pool_id_hex in sorted(available_pools.items()):
67
- vault_addr = pool_id_to_vault_address(pool_id_hex)
68
- chain_id = pool_id_to_chain_id(pool_id_hex)
69
- pool_data = {
70
- "name": pool_name,
71
- "pool_id": pool_id_hex,
72
- }
73
- if vault_addr:
74
- pool_data["vault_address"] = vault_addr
75
- if chain_id:
76
- pool_data["chain_id"] = chain_id
77
- pools_data.append(pool_data)
78
-
79
- console.print(json.dumps(pools_data, indent=2))
80
- return
81
-
82
- # Multi-line text output
83
- if not available_pools:
84
- console.print("[yellow]No pools available.[/]")
85
- return
86
-
87
- console.print("\n[bold cyan]Available Pools[/]\n")
88
-
89
- for idx, (pool_name, pool_id_hex) in enumerate(sorted(available_pools.items()), 1):
90
- vault_addr = pool_id_to_vault_address(pool_id_hex)
91
- chain_id = pool_id_to_chain_id(pool_id_hex)
92
-
93
- # Ensure full pool ID is displayed (normalize to ensure 0x prefix)
94
- pool_id_display = pool_id_hex if pool_id_hex.startswith("0x") else f"0x{pool_id_hex}"
95
-
96
- # Ensure full vault address is displayed
97
- vault_display = vault_addr if vault_addr else "[dim]N/A[/]"
98
-
99
- chain_display = str(chain_id) if chain_id else "[dim]N/A[/]"
100
-
101
- console.print(f"[bold cyan]Pool {idx}:[/] {pool_name}")
102
- console.print(f" [yellow]Pool ID:[/] {pool_id_display}")
103
- console.print(f" [green]Vault Address:[/] {vault_display}")
104
- console.print(f" [dim]Chain ID:[/] {chain_display}")
105
-
106
- # Add spacing between pools except for the last one
107
- if idx < len(available_pools):
108
- console.print()
109
-
110
- except Exception as exc:
111
- console.print(f"[bold red]Error:[/] Failed to list pools: {exc}")
112
- raise typer.Exit(code=1)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes