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,425 @@
1
+ """
2
+ Error handling utilities for stake commands.
3
+ Provides user-friendly error messages for all stake-related errors.
4
+ """
5
+
6
+ from typing import Optional
7
+
8
+ from ...ui.components import HTCLIPanel
9
+ from ...ui.display import HTCLIConsole
10
+
11
+
12
+ def handle_stake_error(
13
+ error_msg: str,
14
+ subnet_id: Optional[int] = None,
15
+ subnet_node_id: Optional[int] = None,
16
+ operation: str = "operation",
17
+ client=None,
18
+ ) -> bool:
19
+ """
20
+ Handle stake-related errors with user-friendly messages.
21
+
22
+ Args:
23
+ error_msg: The error message from the blockchain
24
+ subnet_id: The subnet ID (if applicable)
25
+ subnet_node_id: The subnet node ID (if applicable)
26
+ operation: Description of the operation being performed
27
+ client: Optional client instance for fetching additional info
28
+
29
+ Returns:
30
+ bool: True if error was handled, False otherwise
31
+ """
32
+ console = HTCLIConsole()
33
+ subnet_id_str = f" {subnet_id}" if subnet_id is not None else ""
34
+ node_id_str = f" {subnet_node_id}" if subnet_node_id is not None else ""
35
+
36
+ # System-level errors (checked first)
37
+ if "Paused" in error_msg and "Subnet" not in error_msg:
38
+ error_panel = HTCLIPanel(
39
+ "The network is currently paused.\n\n"
40
+ "⏸️ All transactions are temporarily disabled on the network.\n\n"
41
+ "πŸ’‘ Please wait for the network to resume operations.\n"
42
+ " Check network status or try again later.",
43
+ title="⏸️ Network Paused",
44
+ border_style="htcli.warning",
45
+ highlight=True,
46
+ )
47
+ error_panel.render(console.console)
48
+ return True
49
+
50
+ elif "BadOrigin" in error_msg or (
51
+ "invalid" in error_msg.lower() and "signature" in error_msg.lower()
52
+ ):
53
+ error_panel = HTCLIPanel(
54
+ "Invalid transaction signature.\n\n"
55
+ "πŸ’‘ This usually means:\n"
56
+ " - Your wallet is not properly unlocked\n"
57
+ " - The signing key doesn't match the account\n"
58
+ " - There's an issue with your wallet configuration\n\n"
59
+ " Try: htcli wallet unlock to unlock your wallet\n"
60
+ " Or check your wallet configuration: htcli wallet list",
61
+ title="❌ Invalid Signature",
62
+ border_style="htcli.error",
63
+ highlight=True,
64
+ )
65
+ error_panel.render(console.console)
66
+ return True
67
+
68
+ elif "TxRateLimitExceeded" in error_msg:
69
+ error_panel = HTCLIPanel(
70
+ "Transaction rate limit exceeded.\n\n"
71
+ "⏱️ You've exceeded the maximum number of transactions allowed for your account.\n\n"
72
+ "πŸ’‘ Wait a moment before submitting another transaction.\n"
73
+ " Rate limits help prevent spam and ensure network stability.",
74
+ title="⏳ Rate Limit Exceeded",
75
+ border_style="htcli.warning",
76
+ highlight=True,
77
+ )
78
+ error_panel.render(console.console)
79
+ return True
80
+
81
+ # Minimum delegate stake deposit errors
82
+ elif "MinDelegateStakeDepositNotReached" in error_msg:
83
+ error_panel = HTCLIPanel(
84
+ f"The operation failed because the minimum delegate stake deposit requirement was not met.\n\n"
85
+ f"πŸ’‘ What this means:\n"
86
+ f" β€’ Adding delegate stake requires a minimum deposit amount\n"
87
+ f" β€’ Transferring delegate stake must leave at least the minimum amount\n"
88
+ f" β€’ The minimum prevents dust amounts that could spam the network\n\n"
89
+ f"πŸ’‘ How to fix:\n"
90
+ f" β€’ Check the minimum delegate stake requirement: htcli subnet info --subnet-id{subnet_id_str}\n"
91
+ f" β€’ Increase your stake amount to meet the minimum\n"
92
+ f" β€’ For transfers: ensure both sender and recipient meet minimums",
93
+ title="❌ Minimum Delegate Stake Not Reached",
94
+ border_style="htcli.error",
95
+ highlight=True,
96
+ )
97
+ error_panel.render(console.console)
98
+ return True
99
+
100
+ # Common errors
101
+ elif "InvalidSubnetId" in error_msg:
102
+ error_panel = HTCLIPanel(
103
+ f"Subnet{subnet_id_str} does not exist.\n\n"
104
+ f"πŸ’‘ Check the subnet ID and try again.\n"
105
+ f" Use: htcli subnet list to see available subnets",
106
+ title="❌ Invalid Subnet ID",
107
+ border_style="htcli.error",
108
+ highlight=True,
109
+ )
110
+ error_panel.render(console.console)
111
+ return True
112
+
113
+ elif "InvalidSubnetNodeId" in error_msg:
114
+ error_panel = HTCLIPanel(
115
+ f"Subnet node{node_id_str} does not exist in subnet{subnet_id_str}.\n\n"
116
+ f"πŸ’‘ Check the subnet node ID and try again.\n"
117
+ f" Use: htcli node list --subnet-id{subnet_id_str} to see available nodes",
118
+ title="❌ Invalid Subnet Node ID",
119
+ border_style="htcli.error",
120
+ highlight=True,
121
+ )
122
+ error_panel.render(console.console)
123
+ return True
124
+
125
+ elif "NotKeyOwner" in error_msg:
126
+ error_panel = HTCLIPanel(
127
+ "You are not the owner of this hotkey.\n\n"
128
+ "πŸ’‘ Make sure you're using the correct coldkey wallet.\n"
129
+ " Only the hotkey owner (coldkey) can perform this operation.\n"
130
+ " Check your wallet: htcli wallet list",
131
+ title="❌ Permission Denied",
132
+ border_style="htcli.error",
133
+ highlight=True,
134
+ )
135
+ error_panel.render(console.console)
136
+ return True
137
+
138
+ # Amount validation errors
139
+ elif "InvalidAmount" in error_msg or "AmountZero" in error_msg:
140
+ error_panel = HTCLIPanel(
141
+ "Invalid stake amount.\n\n"
142
+ "πŸ’‘ Stake amount must be greater than zero.\n"
143
+ " Provide a valid stake amount and try again.",
144
+ title="❌ Invalid Amount",
145
+ border_style="htcli.error",
146
+ highlight=True,
147
+ )
148
+ error_panel.render(console.console)
149
+ return True
150
+
151
+ elif "SharesZero" in error_msg:
152
+ error_panel = HTCLIPanel(
153
+ "Invalid shares amount.\n\n"
154
+ "πŸ’‘ Shares amount must be greater than zero.\n"
155
+ " Provide a valid shares amount and try again.",
156
+ title="❌ Invalid Shares",
157
+ border_style="htcli.error",
158
+ highlight=True,
159
+ )
160
+ error_panel.render(console.console)
161
+ return True
162
+
163
+ # Conversion errors
164
+ elif "CouldNotConvertToBalance" in error_msg:
165
+ error_panel = HTCLIPanel(
166
+ "Balance conversion failed.\n\n"
167
+ "πŸ’‘ Failed to convert u128 value to BalanceOf type.\n"
168
+ " This usually indicates an overflow or invalid value.\n"
169
+ " Check your input values and try again.",
170
+ title="❌ Conversion Error",
171
+ border_style="htcli.error",
172
+ highlight=True,
173
+ )
174
+ error_panel.render(console.console)
175
+ return True
176
+
177
+ elif "CouldNotConvertToShares" in error_msg:
178
+ error_panel = HTCLIPanel(
179
+ "Shares conversion failed.\n\n"
180
+ "πŸ’‘ Failed to convert stake amount to shares.\n"
181
+ " This usually happens when the amount is too small after rounding.\n"
182
+ " Try increasing your stake amount slightly.",
183
+ title="❌ Shares Conversion Error",
184
+ border_style="htcli.error",
185
+ highlight=True,
186
+ )
187
+ error_panel.render(console.console)
188
+ return True
189
+
190
+ # Balance errors
191
+ elif "NotEnoughBalanceToStake" in error_msg:
192
+ error_panel = HTCLIPanel(
193
+ "Insufficient balance to stake.\n\n"
194
+ "πŸ’‘ You don't have enough balance to stake and keep your account alive.\n"
195
+ " Ensure your wallet has sufficient funds.\n"
196
+ " Check your balance: htcli wallet balance",
197
+ title="❌ Insufficient Balance",
198
+ border_style="htcli.error",
199
+ highlight=True,
200
+ )
201
+ error_panel.render(console.console)
202
+ return True
203
+
204
+ elif "NotEnoughBalance" in error_msg:
205
+ error_panel = HTCLIPanel(
206
+ "Insufficient balance.\n\n"
207
+ "πŸ’‘ You don't have enough balance to complete this transaction.\n"
208
+ " Ensure your wallet has sufficient funds.\n"
209
+ " Check your balance: htcli wallet balance",
210
+ title="❌ Insufficient Balance",
211
+ border_style="htcli.error",
212
+ highlight=True,
213
+ )
214
+ error_panel.render(console.console)
215
+ return True
216
+
217
+ elif "NotEnoughStakeToWithdraw" in error_msg:
218
+ error_panel = HTCLIPanel(
219
+ "Insufficient stake to withdraw.\n\n"
220
+ "πŸ’‘ You don't have enough staked balance to withdraw this amount.\n"
221
+ " Check your current stake: htcli stake list\n"
222
+ " Reduce the withdrawal amount and try again.",
223
+ title="❌ Insufficient Stake",
224
+ border_style="htcli.error",
225
+ highlight=True,
226
+ )
227
+ error_panel.render(console.console)
228
+ return True
229
+
230
+ elif "BalanceWithdrawalError" in error_msg:
231
+ error_panel = HTCLIPanel(
232
+ "Cannot withdraw balance - would kill account.\n\n"
233
+ "πŸ’‘ Withdrawing this amount would leave your account with insufficient balance.\n"
234
+ " Accounts must maintain a minimum balance to stay alive.\n"
235
+ " Reduce the withdrawal amount and try again.",
236
+ title="❌ Balance Withdrawal Error",
237
+ border_style="htcli.error",
238
+ highlight=True,
239
+ )
240
+ error_panel.render(console.console)
241
+ return True
242
+
243
+ # Stake limit errors
244
+ elif "MinStakeNotReached" in error_msg:
245
+ error_panel = HTCLIPanel(
246
+ "Stake amount below minimum requirement.\n\n"
247
+ "πŸ’‘ The stake amount must meet the subnet's minimum stake requirement.\n"
248
+ " Check the subnet's min_stake parameter and increase your stake.\n"
249
+ " For subnet nodes, you must maintain minimum stake even when removing.",
250
+ title="❌ Below Minimum Stake",
251
+ border_style="htcli.error",
252
+ highlight=True,
253
+ )
254
+ error_panel.render(console.console)
255
+ return True
256
+
257
+ elif "MaxStakeReached" in error_msg:
258
+ error_panel = HTCLIPanel(
259
+ "Stake amount exceeds maximum limit.\n\n"
260
+ "πŸ’‘ The stake amount exceeds the subnet's maximum stake limit.\n"
261
+ " Check the subnet's max_stake parameter and reduce your stake.",
262
+ title="❌ Exceeds Maximum Stake",
263
+ border_style="htcli.error",
264
+ highlight=True,
265
+ )
266
+ error_panel.render(console.console)
267
+ return True
268
+
269
+ # Delegate stake errors
270
+ elif "MinDelegateStakeDepositNotReached" in error_msg:
271
+ error_panel = HTCLIPanel(
272
+ "Delegate stake deposit below minimum.\n\n"
273
+ "πŸ’‘ The delegate stake amount must meet the minimum deposit requirement.\n"
274
+ " Increase your delegate stake amount and try again.",
275
+ title="❌ Below Minimum Delegate Stake",
276
+ border_style="htcli.error",
277
+ highlight=True,
278
+ )
279
+ error_panel.render(console.console)
280
+ return True
281
+
282
+ elif "MinNodeDelegateStakeDepositNotReached" in error_msg:
283
+ error_panel = HTCLIPanel(
284
+ "Node delegate stake deposit below minimum.\n\n"
285
+ "πŸ’‘ The node delegate stake amount must meet the minimum deposit requirement.\n"
286
+ " Increase your node delegate stake amount and try again.",
287
+ title="❌ Below Minimum Node Delegate Stake",
288
+ border_style="htcli.error",
289
+ highlight=True,
290
+ )
291
+ error_panel.render(console.console)
292
+ return True
293
+
294
+ elif "MinDelegateStake" in error_msg:
295
+ error_panel = HTCLIPanel(
296
+ "Remaining delegate stake below minimum.\n\n"
297
+ "πŸ’‘ After this operation, your remaining delegate stake would be below the minimum.\n"
298
+ " Either remove all stake or keep enough to meet the minimum requirement.",
299
+ title="❌ Below Minimum Delegate Stake",
300
+ border_style="htcli.error",
301
+ highlight=True,
302
+ )
303
+ error_panel.render(console.console)
304
+ return True
305
+
306
+ # Unbonding/claiming errors
307
+ elif "NoStakeUnbondingsOrCooldownNotMet" in error_msg:
308
+ error_panel = HTCLIPanel(
309
+ "No stake available to claim.\n\n"
310
+ "πŸ’‘ There are no unbonded stakes ready to claim, or the cooldown period hasn't ended.\n"
311
+ " Stakes must complete the cooldown period before they can be claimed.\n"
312
+ " Check your unbonding stakes: htcli stake list",
313
+ title="⏳ No Claimable Stakes",
314
+ border_style="htcli.warning",
315
+ highlight=True,
316
+ )
317
+ error_panel.render(console.console)
318
+ return True
319
+
320
+ elif "MaxUnlockingsReached" in error_msg:
321
+ error_panel = HTCLIPanel(
322
+ "Maximum unlockings reached.\n\n"
323
+ "πŸ’‘ You've reached the maximum number of concurrent unbonding entries.\n"
324
+ " Wait for some unbondings to complete before creating new ones.\n"
325
+ " Check your unbonding stakes: htcli stake list",
326
+ title="❌ Maximum Unlockings Reached",
327
+ border_style="htcli.error",
328
+ highlight=True,
329
+ )
330
+ error_panel.render(console.console)
331
+ return True
332
+
333
+ # Validator restrictions
334
+ elif "ElectedValidatorCannotUnstake" in error_msg:
335
+ error_panel = HTCLIPanel(
336
+ "Cannot unstake - node is an elected validator.\n\n"
337
+ "πŸ’‘ Elected validators cannot unstake during the current epoch.\n"
338
+ " This ensures validators can be properly rewarded or penalized.\n"
339
+ " Wait until the next epoch and try again.",
340
+ title="⚠️ Cannot Unstake Validator",
341
+ border_style="htcli.warning",
342
+ highlight=True,
343
+ )
344
+ error_panel.render(console.console)
345
+ return True
346
+
347
+ elif "MinActiveNodeStakeEpochs" in error_msg:
348
+ error_panel = HTCLIPanel(
349
+ "Minimum active stake epochs not met.\n\n"
350
+ "πŸ’‘ Activated nodes must maintain stake for a minimum number of epochs.\n"
351
+ " This ensures network stability and prevents rapid stake changes.\n"
352
+ " Wait for the minimum stake period to complete before removing stake.",
353
+ title="⏳ Minimum Stake Period Not Met",
354
+ border_style="htcli.warning",
355
+ highlight=True,
356
+ )
357
+ error_panel.render(console.console)
358
+ return True
359
+
360
+ # Subnet state errors
361
+ elif "SubnetMustBeActive" in error_msg:
362
+ error_panel = HTCLIPanel(
363
+ f"Subnet{subnet_id_str} must be active for this operation.\n\n"
364
+ f"πŸ’‘ This operation requires the subnet to be in an active state.\n"
365
+ f" Check subnet status: htcli subnet info --subnet-id{subnet_id_str}",
366
+ title="⚠️ Subnet Not Active",
367
+ border_style="htcli.warning",
368
+ highlight=True,
369
+ )
370
+ error_panel.render(console.console)
371
+ return True
372
+
373
+ elif "SubnetIsPaused" in error_msg:
374
+ error_panel = HTCLIPanel(
375
+ f"Subnet{subnet_id_str} is paused and cannot perform this action.\n\n"
376
+ f"πŸ’‘ The subnet must be unpaused first.\n"
377
+ f" Use: htcli subnet unpause --subnet-id{subnet_id_str}",
378
+ title="⏸️ Subnet Paused",
379
+ border_style="htcli.warning",
380
+ highlight=True,
381
+ )
382
+ error_panel.render(console.console)
383
+ return True
384
+
385
+ # =========================================================================
386
+ # GENERIC STAKING FALLBACK
387
+ # If we get here, the error was not specifically handled but it's still
388
+ # a staking operation. Show a helpful staking-specific error message
389
+ # instead of letting it fall through to the generic subnet error handler.
390
+ # =========================================================================
391
+ else:
392
+ # Format the raw error message to be more user-friendly
393
+ display_error = error_msg
394
+
395
+ # Try to extract the actual error name from substrate format
396
+ # e.g., "Module { index: 9, error: [0, 0, 0, 0], message: Some("SomeError") }"
397
+ if "message: Some(" in error_msg:
398
+ try:
399
+ import re
400
+
401
+ match = re.search(r'message: Some\("([^"]+)"\)', error_msg)
402
+ if match:
403
+ display_error = match.group(1)
404
+ except Exception:
405
+ pass
406
+
407
+ error_panel = HTCLIPanel(
408
+ f"Staking {operation} failed.\\n\\n"
409
+ f"Error: {display_error}\\n\\n"
410
+ f"πŸ’‘ Helpful Suggestions:\\n"
411
+ f" β€’ Check your balance: htcli wallet balance\\n"
412
+ f" β€’ Verify subnet exists: htcli subnet info --subnet-id{subnet_id_str}\\n"
413
+ + (
414
+ f" β€’ Verify node exists: htcli node info --subnet-id{subnet_id_str} --node-id{node_id_str}\\n"
415
+ if node_id_str
416
+ else ""
417
+ )
418
+ + f" β€’ Check your stake: htcli stake list\\n"
419
+ f" β€’ Review command help: htcli stake {operation.replace(' ', '-')} --help",
420
+ title=f"❌ Staking {operation.replace('_', ' ').title()} Failed",
421
+ border_style="htcli.error",
422
+ highlight=True,
423
+ )
424
+ error_panel.render(console.console)
425
+ return True