htcli 1.1.0__py3-none-any.whl

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 (140) hide show
  1. htcli-1.1.0.dist-info/METADATA +509 -0
  2. htcli-1.1.0.dist-info/RECORD +140 -0
  3. htcli-1.1.0.dist-info/WHEEL +4 -0
  4. htcli-1.1.0.dist-info/entry_points.txt +2 -0
  5. htcli-1.1.0.dist-info/licenses/LICENSE +21 -0
  6. src/__init__.py +0 -0
  7. src/htcli/__init__.py +5 -0
  8. src/htcli/client/__init__.py +338 -0
  9. src/htcli/client/extrinsics/__init__.py +26 -0
  10. src/htcli/client/extrinsics/base.py +487 -0
  11. src/htcli/client/extrinsics/consensus.py +79 -0
  12. src/htcli/client/extrinsics/governance.py +714 -0
  13. src/htcli/client/extrinsics/identity.py +490 -0
  14. src/htcli/client/extrinsics/node.py +1054 -0
  15. src/htcli/client/extrinsics/overwatch.py +401 -0
  16. src/htcli/client/extrinsics/staking.py +1504 -0
  17. src/htcli/client/extrinsics/subnet.py +2218 -0
  18. src/htcli/client/extrinsics/validator.py +203 -0
  19. src/htcli/client/extrinsics/wallet.py +323 -0
  20. src/htcli/client/offchain/__init__.py +10 -0
  21. src/htcli/client/offchain/backup.py +385 -0
  22. src/htcli/client/offchain/config.py +541 -0
  23. src/htcli/client/offchain/wallet.py +839 -0
  24. src/htcli/client/rpc/__init__.py +20 -0
  25. src/htcli/client/rpc/chain.py +568 -0
  26. src/htcli/client/rpc/node.py +783 -0
  27. src/htcli/client/rpc/overwatch.py +680 -0
  28. src/htcli/client/rpc/staking.py +216 -0
  29. src/htcli/client/rpc/subnet.py +2104 -0
  30. src/htcli/client/rpc/wallet.py +912 -0
  31. src/htcli/commands/__init__.py +31 -0
  32. src/htcli/commands/chain/__init__.py +66 -0
  33. src/htcli/commands/chain/display.py +204 -0
  34. src/htcli/commands/chain/handlers.py +260 -0
  35. src/htcli/commands/config/__init__.py +158 -0
  36. src/htcli/commands/config/display.py +353 -0
  37. src/htcli/commands/config/handlers.py +347 -0
  38. src/htcli/commands/config/prompts.py +357 -0
  39. src/htcli/commands/consensus/__init__.py +61 -0
  40. src/htcli/commands/consensus/handlers.py +100 -0
  41. src/htcli/commands/governance/__init__.py +49 -0
  42. src/htcli/commands/governance/handlers.py +81 -0
  43. src/htcli/commands/node/__init__.py +304 -0
  44. src/htcli/commands/node/display.py +749 -0
  45. src/htcli/commands/node/error_handling.py +470 -0
  46. src/htcli/commands/node/handlers.py +844 -0
  47. src/htcli/commands/node/prompts.py +346 -0
  48. src/htcli/commands/overwatch/__init__.py +219 -0
  49. src/htcli/commands/overwatch/display.py +396 -0
  50. src/htcli/commands/overwatch/error_handling.py +276 -0
  51. src/htcli/commands/overwatch/handlers.py +443 -0
  52. src/htcli/commands/overwatch/prompts.py +359 -0
  53. src/htcli/commands/stake/__init__.py +736 -0
  54. src/htcli/commands/stake/display.py +1103 -0
  55. src/htcli/commands/stake/error_handling.py +425 -0
  56. src/htcli/commands/stake/handlers.py +1902 -0
  57. src/htcli/commands/stake/prompts.py +1080 -0
  58. src/htcli/commands/subnet/__init__.py +639 -0
  59. src/htcli/commands/subnet/display.py +801 -0
  60. src/htcli/commands/subnet/error_handling.py +524 -0
  61. src/htcli/commands/subnet/handlers.py +2855 -0
  62. src/htcli/commands/subnet/prompts.py +1225 -0
  63. src/htcli/commands/validator/__init__.py +192 -0
  64. src/htcli/commands/validator/display.py +54 -0
  65. src/htcli/commands/validator/handlers.py +340 -0
  66. src/htcli/commands/wallet/__init__.py +546 -0
  67. src/htcli/commands/wallet/display.py +806 -0
  68. src/htcli/commands/wallet/error_handling.py +210 -0
  69. src/htcli/commands/wallet/handlers.py +3040 -0
  70. src/htcli/commands/wallet/prompts.py +1518 -0
  71. src/htcli/config.py +184 -0
  72. src/htcli/dependencies.py +186 -0
  73. src/htcli/errors/__init__.py +63 -0
  74. src/htcli/errors/base.py +141 -0
  75. src/htcli/errors/display.py +20 -0
  76. src/htcli/errors/handlers.py +710 -0
  77. src/htcli/main.py +343 -0
  78. src/htcli/models/__init__.py +21 -0
  79. src/htcli/models/enums/enum_types.py +35 -0
  80. src/htcli/models/errors.py +103 -0
  81. src/htcli/models/requests/__init__.py +197 -0
  82. src/htcli/models/requests/config.py +70 -0
  83. src/htcli/models/requests/consensus.py +19 -0
  84. src/htcli/models/requests/governance.py +38 -0
  85. src/htcli/models/requests/identity.py +51 -0
  86. src/htcli/models/requests/key.py +22 -0
  87. src/htcli/models/requests/node.py +91 -0
  88. src/htcli/models/requests/overwatch.py +64 -0
  89. src/htcli/models/requests/staking.py +580 -0
  90. src/htcli/models/requests/subnet.py +195 -0
  91. src/htcli/models/requests/validator.py +139 -0
  92. src/htcli/models/requests/wallet.py +118 -0
  93. src/htcli/models/responses/__init__.py +147 -0
  94. src/htcli/models/responses/base.py +18 -0
  95. src/htcli/models/responses/chain.py +39 -0
  96. src/htcli/models/responses/config.py +58 -0
  97. src/htcli/models/responses/identity.py +102 -0
  98. src/htcli/models/responses/overwatch.py +51 -0
  99. src/htcli/models/responses/staking.py +502 -0
  100. src/htcli/models/responses/subnet.py +856 -0
  101. src/htcli/models/responses/wallet.py +185 -0
  102. src/htcli/ui/__init__.py +87 -0
  103. src/htcli/ui/colors.py +309 -0
  104. src/htcli/ui/components/__init__.py +60 -0
  105. src/htcli/ui/components/panels.py +174 -0
  106. src/htcli/ui/components/progress.py +166 -0
  107. src/htcli/ui/components/spinners.py +92 -0
  108. src/htcli/ui/components/tables.py +809 -0
  109. src/htcli/ui/components/trees.py +721 -0
  110. src/htcli/ui/display.py +336 -0
  111. src/htcli/ui/prompts.py +870 -0
  112. src/htcli/utils/__init__.py +76 -0
  113. src/htcli/utils/blockchain/__init__.py +75 -0
  114. src/htcli/utils/blockchain/formatting.py +368 -0
  115. src/htcli/utils/blockchain/patches.py +286 -0
  116. src/htcli/utils/blockchain/peer_id.py +186 -0
  117. src/htcli/utils/blockchain/staking.py +448 -0
  118. src/htcli/utils/blockchain/type_registry.py +1373 -0
  119. src/htcli/utils/blockchain/validation.py +179 -0
  120. src/htcli/utils/cache.py +613 -0
  121. src/htcli/utils/constants.py +38 -0
  122. src/htcli/utils/legacy/__init__.py +12 -0
  123. src/htcli/utils/legacy/colors.py +311 -0
  124. src/htcli/utils/legacy/crypto.py +1176 -0
  125. src/htcli/utils/legacy/formatting.py +452 -0
  126. src/htcli/utils/legacy/interactive.py +306 -0
  127. src/htcli/utils/legacy/subnet_manifest.py +265 -0
  128. src/htcli/utils/legacy/validation.py +488 -0
  129. src/htcli/utils/logging.py +183 -0
  130. src/htcli/utils/network/__init__.py +20 -0
  131. src/htcli/utils/network/subnet.py +344 -0
  132. src/htcli/utils/prompts.py +27 -0
  133. src/htcli/utils/scale_codec.py +155 -0
  134. src/htcli/utils/validation/__init__.py +57 -0
  135. src/htcli/utils/validation/prompt_validators.py +267 -0
  136. src/htcli/utils/wallet/__init__.py +65 -0
  137. src/htcli/utils/wallet/auth.py +151 -0
  138. src/htcli/utils/wallet/core.py +1069 -0
  139. src/htcli/utils/wallet/crypto.py +1615 -0
  140. src/htcli/utils/wallet/migration.py +159 -0
@@ -0,0 +1,443 @@
1
+ """
2
+ Overwatch command handlers for RPC-based operations and extrinsic operations.
3
+ """
4
+
5
+ from typing import Optional
6
+
7
+ from ...dependencies import get_client
8
+ from ...errors.handlers import handle_and_display_error
9
+ from ...ui.components import HTCLILoadingContext
10
+ from ...utils import retrieve_wallet_with_validation
11
+ from .display import (
12
+ display_overwatch_list,
13
+ display_overwatch_node_info,
14
+ display_overwatch_qualification,
15
+ display_overwatch_register_result,
16
+ display_overwatch_remove_result,
17
+ display_overwatch_peer_id_result,
18
+ display_overwatch_stake_add_result,
19
+ display_overwatch_stake_remove_result,
20
+ )
21
+ from .error_handling import handle_overwatch_error
22
+ from .prompts import (
23
+ prompt_overwatch_register,
24
+ prompt_overwatch_remove,
25
+ prompt_overwatch_peer_id,
26
+ prompt_overwatch_add_stake,
27
+ prompt_overwatch_remove_stake,
28
+ prompt_overwatch_node_id,
29
+ )
30
+
31
+
32
+ # ============================================================================
33
+ # RPC HANDLERS - Read Operations
34
+ # ============================================================================
35
+
36
+
37
+ def check_overwatch_qualification_handler(
38
+ coldkey: Optional[str] = None,
39
+ hotkey: Optional[str] = None,
40
+ ):
41
+ """Handle checking if a coldkey can register an overwatch node.
42
+
43
+ This queries all qualification requirements and displays a detailed breakdown
44
+ of which requirements are met and which are not. Similar to check-activation
45
+ for subnets, but for overwatch node registration.
46
+
47
+ Args:
48
+ coldkey: Optional coldkey address or wallet name
49
+ hotkey: Optional hotkey address or wallet name to validate
50
+ """
51
+ from ...utils.wallet.crypto import format_address_display
52
+ try:
53
+ # Resolve coldkey address
54
+ if coldkey is None:
55
+ from ...ui.prompts import HTCLIPrompt
56
+ prompt = HTCLIPrompt()
57
+ coldkey = prompt.text_prompt("Enter coldkey address or wallet name to check")
58
+
59
+ coldkey_display = coldkey # Save for display before resolution
60
+
61
+ # Resolve wallet name to address if needed
62
+ if coldkey and not coldkey.startswith("0x"):
63
+ try:
64
+ from ...utils.wallet.crypto import get_wallet_info_by_name
65
+ wallet_info = get_wallet_info_by_name(coldkey, is_hotkey=False)
66
+ coldkey = wallet_info.get("address") or wallet_info.get("ss58_address")
67
+ if not coldkey:
68
+ from ...ui.display import print_error
69
+ print_error(f"Could not get address from wallet")
70
+ return
71
+ except FileNotFoundError:
72
+ from ...ui.display import print_error
73
+ print_error(f"Wallet '{coldkey}' not found")
74
+ return
75
+
76
+ # Resolve hotkey address if provided
77
+ hotkey_address = None
78
+ if hotkey:
79
+ if hotkey.startswith("0x"):
80
+ hotkey_address = hotkey
81
+ else:
82
+ try:
83
+ from ...utils.wallet.crypto import get_wallet_info_by_name
84
+ hotkey_info = get_wallet_info_by_name(
85
+ hotkey, is_hotkey=True, owner_coldkey_name=coldkey_display
86
+ )
87
+ hotkey_address = (
88
+ hotkey_info.get("evm_address")
89
+ or hotkey_info.get("address")
90
+ or hotkey_info.get("ss58_address")
91
+ )
92
+ except FileNotFoundError:
93
+ from ...ui.display import print_error
94
+ print_error(f"Hotkey wallet '{hotkey}' not found")
95
+ return
96
+
97
+ client = get_client()
98
+
99
+ if not client.substrate:
100
+ from ...ui.display import print_error
101
+ print_error("Not connected to blockchain. Please check your network configuration.")
102
+ return
103
+
104
+ loading_msg = f"Checking overwatch qualification for {format_address_display(coldkey)}"
105
+ if hotkey_address:
106
+ loading_msg += f" with hotkey {format_address_display(hotkey_address)}"
107
+
108
+ with HTCLILoadingContext(loading_msg):
109
+ result = client.rpc.overwatch.check_overwatch_qualification(coldkey, hotkey_address)
110
+
111
+ if result.get("success"):
112
+ display_overwatch_qualification(result["data"])
113
+ else:
114
+ from ...ui.display import print_error
115
+ print_error(f"Failed to check qualification: {result.get('error', 'Unknown error')}")
116
+
117
+ except RuntimeError as e:
118
+ from ...ui.display import print_error
119
+ print_error(f"Connection Error: {str(e)}")
120
+ print_error("Please ensure you are connected to the blockchain network")
121
+ except Exception as e:
122
+ handle_and_display_error(e, operation="check overwatch qualification")
123
+
124
+ def list_overwatch_nodes_handler():
125
+ """Handle listing all overwatch nodes using RPC."""
126
+ try:
127
+ client = get_client()
128
+
129
+ if not client.substrate:
130
+ from ...ui.display import print_error
131
+ print_error("Not connected to blockchain. Please check your network configuration.")
132
+ return
133
+
134
+ with HTCLILoadingContext("Fetching all overwatch nodes..."):
135
+ result = client.rpc.overwatch.list_overwatch_nodes()
136
+
137
+ if result is not None:
138
+ if len(result) == 0:
139
+ from ...ui.display import print_info
140
+ print_info("No overwatch nodes found on the network")
141
+ else:
142
+ display_overwatch_list(result)
143
+ else:
144
+ from ...ui.display import print_error
145
+ print_error("Failed to retrieve overwatch node information from the blockchain")
146
+
147
+ except RuntimeError as e:
148
+ from ...ui.display import print_error
149
+ print_error(f"Connection Error: {str(e)}")
150
+ print_error("Please ensure you are connected to the blockchain network")
151
+ except Exception as e:
152
+ handle_and_display_error(e, operation="list overwatch nodes")
153
+
154
+
155
+ def get_overwatch_node_info_handler(overwatch_node_id: Optional[int] = None):
156
+ """Handle getting specific overwatch node information using RPC."""
157
+ try:
158
+ if overwatch_node_id is None:
159
+ overwatch_node_id = prompt_overwatch_node_id()
160
+
161
+ client = get_client()
162
+
163
+ with HTCLILoadingContext(f"Fetching overwatch node {overwatch_node_id}..."):
164
+ result = client.rpc.overwatch.get_overwatch_node_info(overwatch_node_id)
165
+
166
+ if result:
167
+ display_overwatch_node_info(result)
168
+ else:
169
+ from ...ui.display import print_info
170
+ print_info(f"Overwatch node {overwatch_node_id} not found")
171
+
172
+ except Exception as e:
173
+ handle_and_display_error(e, operation="get overwatch node info")
174
+
175
+
176
+ # ============================================================================
177
+ # EXTRINSIC HANDLERS - Write Operations
178
+ # ============================================================================
179
+
180
+
181
+ def register_overwatch_node_handler(
182
+ hotkey: Optional[str] = None,
183
+ stake_amount: Optional[float] = None,
184
+ coldkey: Optional[str] = None,
185
+ ):
186
+ """Handle overwatch node registration."""
187
+ try:
188
+ # STEP 1: Get signing wallet (coldkey) first
189
+ if coldkey:
190
+ from ...utils.wallet.core import resolve_coldkey_and_get_keypair
191
+ wallet_name, keypair = resolve_coldkey_and_get_keypair(coldkey)
192
+ else:
193
+ wallet_name, keypair = retrieve_wallet_with_validation(
194
+ wallet_type="coldkey", purpose="sign the overwatch node registration"
195
+ )
196
+
197
+ # STEP 2: Collect parameters via prompts
198
+ hotkey_address, stake_wei = prompt_overwatch_register(
199
+ hotkey=hotkey,
200
+ stake_amount=stake_amount,
201
+ coldkey_name=wallet_name,
202
+ )
203
+
204
+ # STEP 3: Execute via client
205
+ client = get_client()
206
+
207
+ if not client.connect():
208
+ from ...ui.display import print_error
209
+ print_error("Failed to connect to blockchain")
210
+ return
211
+
212
+ if not client.substrate:
213
+ from ...ui.display import print_error
214
+ print_error("Blockchain connection failed!")
215
+ raise RuntimeError("Not connected to blockchain")
216
+
217
+ with HTCLILoadingContext("Registering overwatch node..."):
218
+ response = client.extrinsics.overwatch.register_overwatch_node(
219
+ hotkey=hotkey_address,
220
+ stake_to_be_added=stake_wei,
221
+ keypair=keypair,
222
+ )
223
+
224
+ # STEP 4: Display result
225
+ display_overwatch_register_result(response)
226
+
227
+ except Exception as e:
228
+ error_msg = str(e)
229
+ if not handle_overwatch_error(error_msg, operation="register overwatch node"):
230
+ handle_and_display_error(e, operation="register overwatch node")
231
+
232
+
233
+
234
+ def remove_overwatch_node_handler(
235
+ overwatch_node_id: Optional[int] = None,
236
+ coldkey: Optional[str] = None,
237
+ ):
238
+ """Handle removing an overwatch node."""
239
+ try:
240
+ # Get signing wallet (coldkey)
241
+ if coldkey:
242
+ from ...utils.wallet.core import resolve_coldkey_and_get_keypair
243
+ wallet_name, keypair = resolve_coldkey_and_get_keypair(coldkey)
244
+ else:
245
+ wallet_name, keypair = retrieve_wallet_with_validation(
246
+ wallet_type="coldkey", purpose="sign the overwatch node removal"
247
+ )
248
+
249
+ # Collect parameters via prompts
250
+ if overwatch_node_id is None:
251
+ overwatch_node_id = prompt_overwatch_remove()
252
+
253
+ # Execute via client
254
+ client = get_client()
255
+
256
+ if not client.connect():
257
+ from ...ui.display import print_error
258
+ print_error("Failed to connect to blockchain")
259
+ return
260
+
261
+ if not client.substrate:
262
+ from ...ui.display import print_error
263
+ print_error("Blockchain connection failed!")
264
+ raise RuntimeError("Not connected to blockchain")
265
+
266
+ with HTCLILoadingContext(f"Removing overwatch node {overwatch_node_id}..."):
267
+ response = client.extrinsics.overwatch.remove_overwatch_node(
268
+ overwatch_node_id=overwatch_node_id,
269
+ keypair=keypair,
270
+ )
271
+
272
+ # Display result
273
+ display_overwatch_remove_result(response)
274
+
275
+ except Exception as e:
276
+ error_msg = str(e)
277
+ if not handle_overwatch_error(error_msg, overwatch_node_id=overwatch_node_id, operation="remove overwatch node"):
278
+ handle_and_display_error(e, operation="remove overwatch node")
279
+
280
+
281
+
282
+ def set_overwatch_peer_id_handler(
283
+ subnet_id: Optional[int] = None,
284
+ overwatch_node_id: Optional[int] = None,
285
+ peer_id: Optional[str] = None,
286
+ coldkey: Optional[str] = None,
287
+ ):
288
+ """Handle setting peer ID for an overwatch node."""
289
+ try:
290
+ # Get signing wallet (coldkey)
291
+ if coldkey:
292
+ from ...utils.wallet.core import resolve_coldkey_and_get_keypair
293
+ wallet_name, keypair = resolve_coldkey_and_get_keypair(coldkey)
294
+ else:
295
+ wallet_name, keypair = retrieve_wallet_with_validation(
296
+ wallet_type="coldkey", purpose="sign the peer ID update"
297
+ )
298
+
299
+ # Collect parameters via prompts
300
+ subnet_id, overwatch_node_id, peer_id = prompt_overwatch_peer_id(
301
+ subnet_id=subnet_id,
302
+ overwatch_node_id=overwatch_node_id,
303
+ peer_id=peer_id,
304
+ )
305
+
306
+ # Execute via client
307
+ client = get_client()
308
+
309
+ if not client.connect():
310
+ from ...ui.display import print_error
311
+ print_error("Failed to connect to blockchain")
312
+ return
313
+
314
+ if not client.substrate:
315
+ from ...ui.display import print_error
316
+ print_error("Blockchain connection failed!")
317
+ raise RuntimeError("Not connected to blockchain")
318
+
319
+ with HTCLILoadingContext(f"Setting peer ID for overwatch node {overwatch_node_id} on subnet {subnet_id}..."):
320
+ response = client.extrinsics.overwatch.set_overwatch_node_peer_id(
321
+ subnet_id=subnet_id,
322
+ overwatch_node_id=overwatch_node_id,
323
+ peer_id=peer_id,
324
+ keypair=keypair,
325
+ )
326
+
327
+ # Display result
328
+ display_overwatch_peer_id_result(response)
329
+
330
+ except Exception as e:
331
+ error_msg = str(e)
332
+ if not handle_overwatch_error(error_msg, overwatch_node_id=overwatch_node_id, operation="set overwatch peer ID"):
333
+ handle_and_display_error(e, operation="set overwatch peer ID")
334
+
335
+
336
+
337
+ def add_overwatch_stake_handler(
338
+ overwatch_node_id: Optional[int] = None,
339
+ hotkey: Optional[str] = None,
340
+ stake_amount: Optional[float] = None,
341
+ coldkey: Optional[str] = None,
342
+ ):
343
+ """Handle adding stake to an overwatch node."""
344
+ try:
345
+ # Get signing wallet (coldkey)
346
+ if coldkey:
347
+ from ...utils.wallet.core import resolve_coldkey_and_get_keypair
348
+ wallet_name, keypair = resolve_coldkey_and_get_keypair(coldkey)
349
+ else:
350
+ wallet_name, keypair = retrieve_wallet_with_validation(
351
+ wallet_type="coldkey", purpose="sign the overwatch stake addition"
352
+ )
353
+
354
+ # Collect parameters via prompts
355
+ overwatch_node_id, hotkey_address, stake_wei = prompt_overwatch_add_stake(
356
+ overwatch_node_id=overwatch_node_id,
357
+ hotkey=hotkey,
358
+ stake_amount=stake_amount,
359
+ coldkey_name=wallet_name,
360
+ )
361
+
362
+ # Execute via client
363
+ client = get_client()
364
+
365
+ if not client.connect():
366
+ from ...ui.display import print_error
367
+ print_error("Failed to connect to blockchain")
368
+ return
369
+
370
+ if not client.substrate:
371
+ from ...ui.display import print_error
372
+ print_error("Blockchain connection failed!")
373
+ raise RuntimeError("Not connected to blockchain")
374
+
375
+ with HTCLILoadingContext(f"Adding stake to overwatch node {overwatch_node_id}..."):
376
+ response = client.extrinsics.overwatch.add_to_overwatch_stake(
377
+ overwatch_node_id=overwatch_node_id,
378
+ hotkey=hotkey_address,
379
+ stake_to_be_added=stake_wei,
380
+ keypair=keypair,
381
+ )
382
+
383
+ # Display result
384
+ display_overwatch_stake_add_result(response)
385
+
386
+ except Exception as e:
387
+ error_msg = str(e)
388
+ if not handle_overwatch_error(error_msg, overwatch_node_id=overwatch_node_id, operation="add overwatch stake"):
389
+ handle_and_display_error(e, operation="add overwatch stake")
390
+
391
+
392
+
393
+ def remove_overwatch_stake_handler(
394
+ hotkey: Optional[str] = None,
395
+ stake_amount: Optional[float] = None,
396
+ coldkey: Optional[str] = None,
397
+ ):
398
+ """Handle removing stake from an overwatch node."""
399
+ try:
400
+ # Get signing wallet (coldkey)
401
+ if coldkey:
402
+ from ...utils.wallet.core import resolve_coldkey_and_get_keypair
403
+ wallet_name, keypair = resolve_coldkey_and_get_keypair(coldkey)
404
+ else:
405
+ wallet_name, keypair = retrieve_wallet_with_validation(
406
+ wallet_type="coldkey", purpose="sign the overwatch stake removal"
407
+ )
408
+
409
+ # Collect parameters via prompts
410
+ hotkey_address, stake_wei = prompt_overwatch_remove_stake(
411
+ hotkey=hotkey,
412
+ stake_amount=stake_amount,
413
+ coldkey_name=wallet_name,
414
+ )
415
+
416
+ # Execute via client
417
+ client = get_client()
418
+
419
+ if not client.connect():
420
+ from ...ui.display import print_error
421
+ print_error("Failed to connect to blockchain")
422
+ return
423
+
424
+ if not client.substrate:
425
+ from ...ui.display import print_error
426
+ print_error("Blockchain connection failed!")
427
+ raise RuntimeError("Not connected to blockchain")
428
+
429
+ with HTCLILoadingContext("Removing stake from overwatch node..."):
430
+ response = client.extrinsics.overwatch.remove_overwatch_stake(
431
+ hotkey=hotkey_address,
432
+ stake_to_be_removed=stake_wei,
433
+ keypair=keypair,
434
+ )
435
+
436
+ # Display result
437
+ display_overwatch_stake_remove_result(response)
438
+
439
+ except Exception as e:
440
+ error_msg = str(e)
441
+ if not handle_overwatch_error(error_msg, operation="remove overwatch stake"):
442
+ handle_and_display_error(e, operation="remove overwatch stake")
443
+