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.
- htcli-1.1.0.dist-info/METADATA +509 -0
- htcli-1.1.0.dist-info/RECORD +140 -0
- htcli-1.1.0.dist-info/WHEEL +4 -0
- htcli-1.1.0.dist-info/entry_points.txt +2 -0
- htcli-1.1.0.dist-info/licenses/LICENSE +21 -0
- src/__init__.py +0 -0
- src/htcli/__init__.py +5 -0
- src/htcli/client/__init__.py +338 -0
- src/htcli/client/extrinsics/__init__.py +26 -0
- src/htcli/client/extrinsics/base.py +487 -0
- src/htcli/client/extrinsics/consensus.py +79 -0
- src/htcli/client/extrinsics/governance.py +714 -0
- src/htcli/client/extrinsics/identity.py +490 -0
- src/htcli/client/extrinsics/node.py +1054 -0
- src/htcli/client/extrinsics/overwatch.py +401 -0
- src/htcli/client/extrinsics/staking.py +1504 -0
- src/htcli/client/extrinsics/subnet.py +2218 -0
- src/htcli/client/extrinsics/validator.py +203 -0
- src/htcli/client/extrinsics/wallet.py +323 -0
- src/htcli/client/offchain/__init__.py +10 -0
- src/htcli/client/offchain/backup.py +385 -0
- src/htcli/client/offchain/config.py +541 -0
- src/htcli/client/offchain/wallet.py +839 -0
- src/htcli/client/rpc/__init__.py +20 -0
- src/htcli/client/rpc/chain.py +568 -0
- src/htcli/client/rpc/node.py +783 -0
- src/htcli/client/rpc/overwatch.py +680 -0
- src/htcli/client/rpc/staking.py +216 -0
- src/htcli/client/rpc/subnet.py +2104 -0
- src/htcli/client/rpc/wallet.py +912 -0
- src/htcli/commands/__init__.py +31 -0
- src/htcli/commands/chain/__init__.py +66 -0
- src/htcli/commands/chain/display.py +204 -0
- src/htcli/commands/chain/handlers.py +260 -0
- src/htcli/commands/config/__init__.py +158 -0
- src/htcli/commands/config/display.py +353 -0
- src/htcli/commands/config/handlers.py +347 -0
- src/htcli/commands/config/prompts.py +357 -0
- src/htcli/commands/consensus/__init__.py +61 -0
- src/htcli/commands/consensus/handlers.py +100 -0
- src/htcli/commands/governance/__init__.py +49 -0
- src/htcli/commands/governance/handlers.py +81 -0
- src/htcli/commands/node/__init__.py +304 -0
- src/htcli/commands/node/display.py +749 -0
- src/htcli/commands/node/error_handling.py +470 -0
- src/htcli/commands/node/handlers.py +844 -0
- src/htcli/commands/node/prompts.py +346 -0
- src/htcli/commands/overwatch/__init__.py +219 -0
- src/htcli/commands/overwatch/display.py +396 -0
- src/htcli/commands/overwatch/error_handling.py +276 -0
- src/htcli/commands/overwatch/handlers.py +443 -0
- src/htcli/commands/overwatch/prompts.py +359 -0
- src/htcli/commands/stake/__init__.py +736 -0
- src/htcli/commands/stake/display.py +1103 -0
- src/htcli/commands/stake/error_handling.py +425 -0
- src/htcli/commands/stake/handlers.py +1902 -0
- src/htcli/commands/stake/prompts.py +1080 -0
- src/htcli/commands/subnet/__init__.py +639 -0
- src/htcli/commands/subnet/display.py +801 -0
- src/htcli/commands/subnet/error_handling.py +524 -0
- src/htcli/commands/subnet/handlers.py +2855 -0
- src/htcli/commands/subnet/prompts.py +1225 -0
- src/htcli/commands/validator/__init__.py +192 -0
- src/htcli/commands/validator/display.py +54 -0
- src/htcli/commands/validator/handlers.py +340 -0
- src/htcli/commands/wallet/__init__.py +546 -0
- src/htcli/commands/wallet/display.py +806 -0
- src/htcli/commands/wallet/error_handling.py +210 -0
- src/htcli/commands/wallet/handlers.py +3040 -0
- src/htcli/commands/wallet/prompts.py +1518 -0
- src/htcli/config.py +184 -0
- src/htcli/dependencies.py +186 -0
- src/htcli/errors/__init__.py +63 -0
- src/htcli/errors/base.py +141 -0
- src/htcli/errors/display.py +20 -0
- src/htcli/errors/handlers.py +710 -0
- src/htcli/main.py +343 -0
- src/htcli/models/__init__.py +21 -0
- src/htcli/models/enums/enum_types.py +35 -0
- src/htcli/models/errors.py +103 -0
- src/htcli/models/requests/__init__.py +197 -0
- src/htcli/models/requests/config.py +70 -0
- src/htcli/models/requests/consensus.py +19 -0
- src/htcli/models/requests/governance.py +38 -0
- src/htcli/models/requests/identity.py +51 -0
- src/htcli/models/requests/key.py +22 -0
- src/htcli/models/requests/node.py +91 -0
- src/htcli/models/requests/overwatch.py +64 -0
- src/htcli/models/requests/staking.py +580 -0
- src/htcli/models/requests/subnet.py +195 -0
- src/htcli/models/requests/validator.py +139 -0
- src/htcli/models/requests/wallet.py +118 -0
- src/htcli/models/responses/__init__.py +147 -0
- src/htcli/models/responses/base.py +18 -0
- src/htcli/models/responses/chain.py +39 -0
- src/htcli/models/responses/config.py +58 -0
- src/htcli/models/responses/identity.py +102 -0
- src/htcli/models/responses/overwatch.py +51 -0
- src/htcli/models/responses/staking.py +502 -0
- src/htcli/models/responses/subnet.py +856 -0
- src/htcli/models/responses/wallet.py +185 -0
- src/htcli/ui/__init__.py +87 -0
- src/htcli/ui/colors.py +309 -0
- src/htcli/ui/components/__init__.py +60 -0
- src/htcli/ui/components/panels.py +174 -0
- src/htcli/ui/components/progress.py +166 -0
- src/htcli/ui/components/spinners.py +92 -0
- src/htcli/ui/components/tables.py +809 -0
- src/htcli/ui/components/trees.py +721 -0
- src/htcli/ui/display.py +336 -0
- src/htcli/ui/prompts.py +870 -0
- src/htcli/utils/__init__.py +76 -0
- src/htcli/utils/blockchain/__init__.py +75 -0
- src/htcli/utils/blockchain/formatting.py +368 -0
- src/htcli/utils/blockchain/patches.py +286 -0
- src/htcli/utils/blockchain/peer_id.py +186 -0
- src/htcli/utils/blockchain/staking.py +448 -0
- src/htcli/utils/blockchain/type_registry.py +1373 -0
- src/htcli/utils/blockchain/validation.py +179 -0
- src/htcli/utils/cache.py +613 -0
- src/htcli/utils/constants.py +38 -0
- src/htcli/utils/legacy/__init__.py +12 -0
- src/htcli/utils/legacy/colors.py +311 -0
- src/htcli/utils/legacy/crypto.py +1176 -0
- src/htcli/utils/legacy/formatting.py +452 -0
- src/htcli/utils/legacy/interactive.py +306 -0
- src/htcli/utils/legacy/subnet_manifest.py +265 -0
- src/htcli/utils/legacy/validation.py +488 -0
- src/htcli/utils/logging.py +183 -0
- src/htcli/utils/network/__init__.py +20 -0
- src/htcli/utils/network/subnet.py +344 -0
- src/htcli/utils/prompts.py +27 -0
- src/htcli/utils/scale_codec.py +155 -0
- src/htcli/utils/validation/__init__.py +57 -0
- src/htcli/utils/validation/prompt_validators.py +267 -0
- src/htcli/utils/wallet/__init__.py +65 -0
- src/htcli/utils/wallet/auth.py +151 -0
- src/htcli/utils/wallet/core.py +1069 -0
- src/htcli/utils/wallet/crypto.py +1615 -0
- src/htcli/utils/wallet/migration.py +159 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Overwatch node extrinsics for HTCLI.
|
|
3
|
+
|
|
4
|
+
Provides extrinsic operations for overwatch nodes including registration,
|
|
5
|
+
removal, staking, and peer ID management.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Optional
|
|
9
|
+
|
|
10
|
+
from substrateinterface import Keypair, SubstrateInterface
|
|
11
|
+
|
|
12
|
+
from ...utils.logging import get_logger
|
|
13
|
+
from .base import BaseExtrinsicClient, get_user_friendly_error
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class OverwatchExtrinsics(BaseExtrinsicClient):
|
|
19
|
+
"""Client for overwatch node-related extrinsics."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, substrate: Optional[SubstrateInterface] = None):
|
|
22
|
+
"""Initialize the overwatch client."""
|
|
23
|
+
super().__init__(substrate)
|
|
24
|
+
|
|
25
|
+
# ============================================================================
|
|
26
|
+
# NODE REGISTRATION & REMOVAL
|
|
27
|
+
# ============================================================================
|
|
28
|
+
|
|
29
|
+
def register_overwatch_node(
|
|
30
|
+
self, hotkey: str, stake_to_be_added: int, keypair: Keypair
|
|
31
|
+
) -> dict[str, Any]:
|
|
32
|
+
"""
|
|
33
|
+
Register a new overwatch node.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
hotkey: The hotkey account ID for the overwatch node
|
|
37
|
+
stake_to_be_added: Initial stake amount in wei
|
|
38
|
+
keypair: Coldkey keypair for signing the transaction
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Dictionary with operation result
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
logger.info(
|
|
45
|
+
f"Registering overwatch node with hotkey {hotkey[:16]}... "
|
|
46
|
+
f"and stake {stake_to_be_added / 1e18:.4f} TENSOR"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
call = self.substrate.compose_call(
|
|
50
|
+
call_module="Network",
|
|
51
|
+
call_function="register_overwatch_node",
|
|
52
|
+
call_params={
|
|
53
|
+
"hotkey": hotkey,
|
|
54
|
+
"stake_to_be_added": stake_to_be_added,
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
result = self._submit_extrinsic(call, keypair)
|
|
59
|
+
|
|
60
|
+
if result["success"]:
|
|
61
|
+
# Query the latest TotalOverwatchNodeUids to get the newly assigned ID
|
|
62
|
+
# Note: The registration code doesn't emit an event with the ID,
|
|
63
|
+
# so we query storage directly. The new node got the latest UID.
|
|
64
|
+
overwatch_node_id = None
|
|
65
|
+
try:
|
|
66
|
+
total_uids_result = self.substrate.query(
|
|
67
|
+
module="Network",
|
|
68
|
+
storage_function="TotalOverwatchNodeUids",
|
|
69
|
+
)
|
|
70
|
+
if total_uids_result and total_uids_result.value:
|
|
71
|
+
overwatch_node_id = total_uids_result.value
|
|
72
|
+
except Exception as query_error:
|
|
73
|
+
logger.warning(f"Could not query overwatch node ID: {query_error}")
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
"success": True,
|
|
77
|
+
"transaction_hash": result.get("extrinsic_hash"),
|
|
78
|
+
"block_hash": result.get("block_hash"),
|
|
79
|
+
"block_number": result.get("block_number"),
|
|
80
|
+
"overwatch_node_id": overwatch_node_id,
|
|
81
|
+
"hotkey": hotkey,
|
|
82
|
+
"stake_added": stake_to_be_added,
|
|
83
|
+
"message": f"Successfully registered overwatch node with {stake_to_be_added / 1e18:.4f} TENSOR stake",
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
error_msg = get_user_friendly_error(result.get("error", "Unknown error"))
|
|
88
|
+
return {
|
|
89
|
+
"success": False,
|
|
90
|
+
"error": error_msg,
|
|
91
|
+
"message": f"Failed to register overwatch node: {error_msg}",
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
except Exception as e:
|
|
95
|
+
logger.error(f"Error registering overwatch node: {e}")
|
|
96
|
+
return {
|
|
97
|
+
"success": False,
|
|
98
|
+
"error": str(e),
|
|
99
|
+
"message": f"Error registering overwatch node: {str(e)}",
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
def remove_overwatch_node(
|
|
103
|
+
self, overwatch_node_id: int, keypair: Keypair
|
|
104
|
+
) -> dict[str, Any]:
|
|
105
|
+
"""
|
|
106
|
+
Remove an overwatch node (owner only).
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
overwatch_node_id: The overwatch node ID to remove
|
|
110
|
+
keypair: Coldkey keypair for signing (must be node owner)
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Dictionary with operation result
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
logger.info(f"Removing overwatch node {overwatch_node_id}")
|
|
117
|
+
|
|
118
|
+
call = self.substrate.compose_call(
|
|
119
|
+
call_module="Network",
|
|
120
|
+
call_function="remove_overwatch_node",
|
|
121
|
+
call_params={
|
|
122
|
+
"overwatch_node_id": overwatch_node_id,
|
|
123
|
+
},
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
result = self._submit_extrinsic(call, keypair)
|
|
127
|
+
|
|
128
|
+
if result["success"]:
|
|
129
|
+
return {
|
|
130
|
+
"success": True,
|
|
131
|
+
"transaction_hash": result.get("extrinsic_hash"),
|
|
132
|
+
"block_hash": result.get("block_hash"),
|
|
133
|
+
"block_number": result.get("block_number"),
|
|
134
|
+
"overwatch_node_id": overwatch_node_id,
|
|
135
|
+
"message": f"Successfully removed overwatch node {overwatch_node_id}",
|
|
136
|
+
}
|
|
137
|
+
else:
|
|
138
|
+
error_msg = get_user_friendly_error(result.get("error", "Unknown error"))
|
|
139
|
+
return {
|
|
140
|
+
"success": False,
|
|
141
|
+
"error": error_msg,
|
|
142
|
+
"message": f"Failed to remove overwatch node: {error_msg}",
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
except Exception as e:
|
|
146
|
+
logger.error(f"Error removing overwatch node: {e}")
|
|
147
|
+
return {
|
|
148
|
+
"success": False,
|
|
149
|
+
"error": str(e),
|
|
150
|
+
"message": f"Error removing overwatch node: {str(e)}",
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
def anyone_remove_overwatch_node(
|
|
154
|
+
self, overwatch_node_id: int, keypair: Keypair
|
|
155
|
+
) -> dict[str, Any]:
|
|
156
|
+
"""
|
|
157
|
+
Remove an unqualified overwatch node (anyone can call).
|
|
158
|
+
|
|
159
|
+
This can only be called if the overwatch node is no longer qualified.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
overwatch_node_id: The overwatch node ID to remove
|
|
163
|
+
keypair: Any keypair for signing the transaction
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Dictionary with operation result
|
|
167
|
+
"""
|
|
168
|
+
try:
|
|
169
|
+
logger.info(f"Attempting to remove unqualified overwatch node {overwatch_node_id}")
|
|
170
|
+
|
|
171
|
+
call = self.substrate.compose_call(
|
|
172
|
+
call_module="Network",
|
|
173
|
+
call_function="anyone_remove_overwatch_node",
|
|
174
|
+
call_params={
|
|
175
|
+
"overwatch_node_id": overwatch_node_id,
|
|
176
|
+
},
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
result = self._submit_extrinsic(call, keypair)
|
|
180
|
+
|
|
181
|
+
if result["success"]:
|
|
182
|
+
return {
|
|
183
|
+
"success": True,
|
|
184
|
+
"transaction_hash": result.get("extrinsic_hash"),
|
|
185
|
+
"block_hash": result.get("block_hash"),
|
|
186
|
+
"block_number": result.get("block_number"),
|
|
187
|
+
"overwatch_node_id": overwatch_node_id,
|
|
188
|
+
"message": f"Successfully removed unqualified overwatch node {overwatch_node_id}",
|
|
189
|
+
}
|
|
190
|
+
else:
|
|
191
|
+
error_msg = get_user_friendly_error(result.get("error", "Unknown error"))
|
|
192
|
+
return {
|
|
193
|
+
"success": False,
|
|
194
|
+
"error": error_msg,
|
|
195
|
+
"message": f"Failed to remove overwatch node: {error_msg}",
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
except Exception as e:
|
|
199
|
+
logger.error(f"Error removing overwatch node: {e}")
|
|
200
|
+
return {
|
|
201
|
+
"success": False,
|
|
202
|
+
"error": str(e),
|
|
203
|
+
"message": f"Error removing overwatch node: {str(e)}",
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
# ============================================================================
|
|
207
|
+
# PEER ID MANAGEMENT
|
|
208
|
+
# ============================================================================
|
|
209
|
+
|
|
210
|
+
def set_overwatch_node_peer_id(
|
|
211
|
+
self,
|
|
212
|
+
subnet_id: int,
|
|
213
|
+
overwatch_node_id: int,
|
|
214
|
+
peer_id: str,
|
|
215
|
+
keypair: Keypair,
|
|
216
|
+
) -> dict[str, Any]:
|
|
217
|
+
"""
|
|
218
|
+
Set the peer ID for an overwatch node on a specific subnet.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
subnet_id: The subnet ID
|
|
222
|
+
overwatch_node_id: The overwatch node ID
|
|
223
|
+
peer_id: The peer ID to set
|
|
224
|
+
keypair: Keypair for signing (must be node owner)
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Dictionary with operation result
|
|
228
|
+
"""
|
|
229
|
+
try:
|
|
230
|
+
logger.info(
|
|
231
|
+
f"Setting peer ID for overwatch node {overwatch_node_id} "
|
|
232
|
+
f"on subnet {subnet_id}"
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Pass peer_id as string - substrate-interface handles encoding
|
|
236
|
+
# (Same pattern as node.py for subnet node peer IDs)
|
|
237
|
+
call = self.substrate.compose_call(
|
|
238
|
+
call_module="Network",
|
|
239
|
+
call_function="set_overwatch_node_peer_id",
|
|
240
|
+
call_params={
|
|
241
|
+
"subnet_id": subnet_id,
|
|
242
|
+
"overwatch_node_id": overwatch_node_id,
|
|
243
|
+
"peer_id": peer_id, # String, not encoded bytes
|
|
244
|
+
},
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
result = self._submit_extrinsic(call, keypair)
|
|
248
|
+
|
|
249
|
+
if result["success"]:
|
|
250
|
+
return {
|
|
251
|
+
"success": True,
|
|
252
|
+
"transaction_hash": result.get("extrinsic_hash"),
|
|
253
|
+
"block_hash": result.get("block_hash"),
|
|
254
|
+
"block_number": result.get("block_number"),
|
|
255
|
+
"subnet_id": subnet_id,
|
|
256
|
+
"overwatch_node_id": overwatch_node_id,
|
|
257
|
+
"peer_id": peer_id,
|
|
258
|
+
"message": f"Successfully set peer ID for overwatch node {overwatch_node_id} on subnet {subnet_id}",
|
|
259
|
+
}
|
|
260
|
+
else:
|
|
261
|
+
error_msg = get_user_friendly_error(result.get("error", "Unknown error"))
|
|
262
|
+
return {
|
|
263
|
+
"success": False,
|
|
264
|
+
"error": error_msg,
|
|
265
|
+
"message": f"Failed to set peer ID: {error_msg}",
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
except Exception as e:
|
|
269
|
+
logger.error(f"Error setting overwatch node peer ID: {e}")
|
|
270
|
+
return {
|
|
271
|
+
"success": False,
|
|
272
|
+
"error": str(e),
|
|
273
|
+
"message": f"Error setting peer ID: {str(e)}",
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
# ============================================================================
|
|
277
|
+
# STAKING
|
|
278
|
+
# ============================================================================
|
|
279
|
+
|
|
280
|
+
def add_to_overwatch_stake(
|
|
281
|
+
self,
|
|
282
|
+
overwatch_node_id: int,
|
|
283
|
+
hotkey: str,
|
|
284
|
+
stake_to_be_added: int,
|
|
285
|
+
keypair: Keypair,
|
|
286
|
+
) -> dict[str, Any]:
|
|
287
|
+
"""
|
|
288
|
+
Add stake to an overwatch node.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
overwatch_node_id: The overwatch node ID
|
|
292
|
+
hotkey: The hotkey of the overwatch node
|
|
293
|
+
stake_to_be_added: Amount to stake in wei
|
|
294
|
+
keypair: Coldkey keypair for signing (must own the hotkey)
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
Dictionary with operation result
|
|
298
|
+
"""
|
|
299
|
+
try:
|
|
300
|
+
logger.info(
|
|
301
|
+
f"Adding {stake_to_be_added / 1e18:.4f} TENSOR stake "
|
|
302
|
+
f"to overwatch node {overwatch_node_id}"
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
call = self.substrate.compose_call(
|
|
306
|
+
call_module="Network",
|
|
307
|
+
call_function="add_to_overwatch_stake",
|
|
308
|
+
call_params={
|
|
309
|
+
"overwatch_node_id": overwatch_node_id,
|
|
310
|
+
"hotkey": hotkey,
|
|
311
|
+
"stake_to_be_added": stake_to_be_added,
|
|
312
|
+
},
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
result = self._submit_extrinsic(call, keypair)
|
|
316
|
+
|
|
317
|
+
if result["success"]:
|
|
318
|
+
return {
|
|
319
|
+
"success": True,
|
|
320
|
+
"transaction_hash": result.get("extrinsic_hash"),
|
|
321
|
+
"block_hash": result.get("block_hash"),
|
|
322
|
+
"block_number": result.get("block_number"),
|
|
323
|
+
"overwatch_node_id": overwatch_node_id,
|
|
324
|
+
"hotkey": hotkey,
|
|
325
|
+
"stake_added": stake_to_be_added,
|
|
326
|
+
"message": f"Successfully added {stake_to_be_added / 1e18:.4f} TENSOR to overwatch stake",
|
|
327
|
+
}
|
|
328
|
+
else:
|
|
329
|
+
error_msg = get_user_friendly_error(result.get("error", "Unknown error"))
|
|
330
|
+
return {
|
|
331
|
+
"success": False,
|
|
332
|
+
"error": error_msg,
|
|
333
|
+
"message": f"Failed to add overwatch stake: {error_msg}",
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
except Exception as e:
|
|
337
|
+
logger.error(f"Error adding overwatch stake: {e}")
|
|
338
|
+
return {
|
|
339
|
+
"success": False,
|
|
340
|
+
"error": str(e),
|
|
341
|
+
"message": f"Error adding overwatch stake: {str(e)}",
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
def remove_overwatch_stake(
|
|
345
|
+
self, hotkey: str, stake_to_be_removed: int, keypair: Keypair
|
|
346
|
+
) -> dict[str, Any]:
|
|
347
|
+
"""
|
|
348
|
+
Remove stake from an overwatch node.
|
|
349
|
+
|
|
350
|
+
Creates an unbonding entry that will be claimable after the unbonding period.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
hotkey: The hotkey of the overwatch node
|
|
354
|
+
stake_to_be_removed: Amount to unstake in wei
|
|
355
|
+
keypair: Coldkey keypair for signing (must own the hotkey)
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
Dictionary with operation result
|
|
359
|
+
"""
|
|
360
|
+
try:
|
|
361
|
+
logger.info(
|
|
362
|
+
f"Removing {stake_to_be_removed / 1e18:.4f} TENSOR stake "
|
|
363
|
+
f"from overwatch node with hotkey {hotkey[:16]}..."
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
call = self.substrate.compose_call(
|
|
367
|
+
call_module="Network",
|
|
368
|
+
call_function="remove_overwatch_stake",
|
|
369
|
+
call_params={
|
|
370
|
+
"hotkey": hotkey,
|
|
371
|
+
"stake_to_be_removed": stake_to_be_removed,
|
|
372
|
+
},
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
result = self._submit_extrinsic(call, keypair)
|
|
376
|
+
|
|
377
|
+
if result["success"]:
|
|
378
|
+
return {
|
|
379
|
+
"success": True,
|
|
380
|
+
"transaction_hash": result.get("extrinsic_hash"),
|
|
381
|
+
"block_hash": result.get("block_hash"),
|
|
382
|
+
"block_number": result.get("block_number"),
|
|
383
|
+
"hotkey": hotkey,
|
|
384
|
+
"stake_removed": stake_to_be_removed,
|
|
385
|
+
"message": f"Successfully removed {stake_to_be_removed / 1e18:.4f} TENSOR from overwatch stake (will unbond after waiting period)",
|
|
386
|
+
}
|
|
387
|
+
else:
|
|
388
|
+
error_msg = get_user_friendly_error(result.get("error", "Unknown error"))
|
|
389
|
+
return {
|
|
390
|
+
"success": False,
|
|
391
|
+
"error": error_msg,
|
|
392
|
+
"message": f"Failed to remove overwatch stake: {error_msg}",
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
except Exception as e:
|
|
396
|
+
logger.error(f"Error removing overwatch stake: {e}")
|
|
397
|
+
return {
|
|
398
|
+
"success": False,
|
|
399
|
+
"error": str(e),
|
|
400
|
+
"message": f"Error removing overwatch stake: {str(e)}",
|
|
401
|
+
}
|