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.
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/PKG-INFO +1 -1
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/config.py +1 -1
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/miner_password.py +0 -7
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/miner_status.py +4 -25
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/pair_status.py +2 -27
- cartha_cli-1.0.8/cartha_cli/commands/pools.py +89 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/prove_lock.py +60 -143
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/register.py +0 -16
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/config.py +4 -4
- cartha_cli-1.0.8/cartha_cli/pool_client.py +214 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/verifier.py +15 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/pyproject.toml +1 -1
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/tests/test_cli.py +38 -52
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/uv.lock +1 -1
- cartha_cli-1.0.7/cartha_cli/commands/pools.py +0 -112
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/.github/workflows/README.md +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/.github/workflows/ci.yml +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/.github/workflows/publish.yml +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/.gitignore +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/.ruff.toml +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/CONTRIBUTING.md +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/LICENSE +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/Makefile +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/README.md +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/__init__.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/bt.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/__init__.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/common.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/health.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/help.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/shared_options.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/commands/version.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/display.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/eth712.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/main.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/pair.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/testnet/README.md +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/testnet/__init__.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/testnet/pool_ids.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/utils.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/cartha_cli/wallet.py +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/docs/COMMANDS.md +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/docs/FEEDBACK.md +0 -0
- {cartha_cli-1.0.7 → cartha_cli-1.0.8}/tests/conftest.py +0 -0
|
@@ -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-
|
|
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]🌐
|
|
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
|
-
|
|
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
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
419
|
-
|
|
420
|
-
chain
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
678
|
-
|
|
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":
|
|
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
|
-
#
|
|
718
|
-
|
|
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(
|
|
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
|
|
859
|
+
# Get RPC endpoint based on chain ID
|
|
945
860
|
rpc_url = None
|
|
946
|
-
if chain ==
|
|
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
|
-
#
|
|
950
|
-
console.print(f"[yellow]Warning:[/] Auto-detection only supports Base
|
|
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":
|
|
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
|
|
40
|
-
return "https://cartha-verifier-
|
|
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-
|
|
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
|
]
|
|
@@ -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
|
|
1470
|
-
def
|
|
1471
|
-
return
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
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.
|
|
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 "
|
|
1499
|
-
assert "
|
|
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
|
|
1508
|
-
def
|
|
1509
|
-
return
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
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.
|
|
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
|
-
|
|
1539
|
-
assert payload[0]["
|
|
1540
|
-
assert payload[0]["
|
|
1541
|
-
assert payload[0]["
|
|
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):
|
|
@@ -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
|
|
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
|
|
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
|