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,192 @@
1
+ """
2
+ Validator command module.
3
+ """
4
+
5
+ from typing import Optional
6
+
7
+ import typer
8
+
9
+ from . import handlers as validator_handlers
10
+
11
+ app = typer.Typer(name="validator", help="Validator operations")
12
+
13
+
14
+ @app.command("register")
15
+ def register_validator(
16
+ hotkey: Optional[str] = typer.Option(
17
+ None, "--hotkey", help="Validator hotkey address"
18
+ ),
19
+ delegate_rate: Optional[int] = typer.Option(
20
+ None,
21
+ "--delegate-rate",
22
+ help="Delegate reward rate (0-100 as percent, or 1e18-format value)",
23
+ ),
24
+ delegate_account: Optional[str] = typer.Option(
25
+ None, "--delegate-account", help="Optional delegate account address"
26
+ ),
27
+ delegate_account_rate: Optional[int] = typer.Option(
28
+ None,
29
+ "--delegate-account-rate",
30
+ help="Optional delegate account rate (0-100 as percent, or 1e18-format value)",
31
+ ),
32
+ coldkey: Optional[str] = typer.Option(
33
+ None,
34
+ "--coldkey",
35
+ help="Coldkey wallet name or address to sign the registration extrinsic",
36
+ ),
37
+ ):
38
+ """Register a validator."""
39
+ validator_handlers.register_validator_handler(
40
+ hotkey=hotkey,
41
+ delegate_rate=delegate_rate,
42
+ delegate_account=delegate_account,
43
+ delegate_account_rate=delegate_account_rate,
44
+ coldkey=coldkey,
45
+ )
46
+
47
+
48
+ @app.command("update-delegate-rate")
49
+ def update_delegate_rate(
50
+ validator_id: Optional[int] = typer.Option(
51
+ None, "--validator-id", help="Validator ID to update"
52
+ ),
53
+ delegate_rate: Optional[int] = typer.Option(
54
+ None,
55
+ "--delegate-rate",
56
+ help="Delegate reward rate (0-100 as percent, or 1e18-format value)",
57
+ ),
58
+ coldkey: Optional[str] = typer.Option(
59
+ None,
60
+ "--coldkey",
61
+ help="Coldkey wallet name or address to sign the update extrinsic",
62
+ ),
63
+ ):
64
+ """Update a validator delegate reward rate."""
65
+ validator_handlers.update_delegate_rate_handler(
66
+ validator_id=validator_id,
67
+ delegate_rate=delegate_rate,
68
+ coldkey=coldkey,
69
+ )
70
+
71
+
72
+ @app.command("update-delegate-account")
73
+ def update_delegate_account(
74
+ validator_id: Optional[int] = typer.Option(
75
+ None, "--validator-id", help="Validator ID to update"
76
+ ),
77
+ delegate_account: Optional[str] = typer.Option(
78
+ None, "--delegate-account", help="Delegate account address"
79
+ ),
80
+ delegate_account_rate: Optional[int] = typer.Option(
81
+ None,
82
+ "--delegate-account-rate",
83
+ help="Delegate account rate (0-100 as percent, or 1e18-format value)",
84
+ ),
85
+ clear: bool = typer.Option(
86
+ False, "--clear", help="Clear the validator delegate account"
87
+ ),
88
+ coldkey: Optional[str] = typer.Option(
89
+ None,
90
+ "--coldkey",
91
+ help="Coldkey wallet name or address to sign the update extrinsic",
92
+ ),
93
+ ):
94
+ """Update or clear a validator delegate account."""
95
+ validator_handlers.update_delegate_account_handler(
96
+ validator_id=validator_id,
97
+ delegate_account=delegate_account,
98
+ delegate_account_rate=delegate_account_rate,
99
+ clear=clear,
100
+ coldkey=coldkey,
101
+ )
102
+
103
+
104
+ @app.command("update-hotkey")
105
+ def update_hotkey(
106
+ validator_id: Optional[int] = typer.Option(
107
+ None, "--validator-id", help="Validator ID to update"
108
+ ),
109
+ hotkey: Optional[str] = typer.Option(
110
+ None, "--hotkey", help="New validator hotkey address"
111
+ ),
112
+ coldkey: Optional[str] = typer.Option(
113
+ None,
114
+ "--coldkey",
115
+ help="Coldkey wallet name or address to sign the update extrinsic",
116
+ ),
117
+ ):
118
+ """Update a validator hotkey."""
119
+ validator_handlers.update_hotkey_handler(
120
+ validator_id=validator_id,
121
+ hotkey=hotkey,
122
+ coldkey=coldkey,
123
+ )
124
+
125
+
126
+ @app.command("update-coldkey")
127
+ def update_coldkey(
128
+ validator_id: Optional[int] = typer.Option(
129
+ None, "--validator-id", help="Validator ID to update"
130
+ ),
131
+ new_coldkey: Optional[str] = typer.Option(
132
+ None, "--new-coldkey", help="New validator coldkey address"
133
+ ),
134
+ coldkey: Optional[str] = typer.Option(
135
+ None,
136
+ "--coldkey",
137
+ help="Current coldkey wallet name or address to sign the update extrinsic",
138
+ ),
139
+ ):
140
+ """Update a validator coldkey."""
141
+ validator_handlers.update_coldkey_handler(
142
+ validator_id=validator_id,
143
+ new_coldkey=new_coldkey,
144
+ coldkey=coldkey,
145
+ )
146
+
147
+
148
+ @app.command("update-identity")
149
+ def update_identity(
150
+ validator_id: Optional[int] = typer.Option(
151
+ None, "--validator-id", help="Validator ID to update"
152
+ ),
153
+ name: Optional[str] = typer.Option(None, "--name", help="Display name"),
154
+ url: Optional[str] = typer.Option(None, "--url", help="Website URL"),
155
+ image: Optional[str] = typer.Option(None, "--image", help="Profile image URL"),
156
+ discord: Optional[str] = typer.Option(None, "--discord", help="Discord handle"),
157
+ x: Optional[str] = typer.Option(None, "--x", help="X handle"),
158
+ telegram: Optional[str] = typer.Option(None, "--telegram", help="Telegram handle"),
159
+ github: Optional[str] = typer.Option(None, "--github", help="GitHub profile URL"),
160
+ hugging_face: Optional[str] = typer.Option(
161
+ None, "--hugging-face", help="Hugging Face profile URL"
162
+ ),
163
+ description: Optional[str] = typer.Option(
164
+ None, "--description", help="Short description"
165
+ ),
166
+ misc: Optional[str] = typer.Option(None, "--misc", help="Additional data"),
167
+ clear: bool = typer.Option(False, "--clear", help="Clear validator identity"),
168
+ coldkey: Optional[str] = typer.Option(
169
+ None,
170
+ "--coldkey",
171
+ help="Coldkey wallet name or address to sign the update extrinsic",
172
+ ),
173
+ ):
174
+ """Update or clear a validator identity."""
175
+ validator_handlers.update_identity_handler(
176
+ validator_id=validator_id,
177
+ name=name,
178
+ url=url,
179
+ image=image,
180
+ discord=discord,
181
+ x=x,
182
+ telegram=telegram,
183
+ github=github,
184
+ hugging_face=hugging_face,
185
+ description=description,
186
+ misc=misc,
187
+ clear=clear,
188
+ coldkey=coldkey,
189
+ )
190
+
191
+
192
+ __all__ = ["app"]
@@ -0,0 +1,54 @@
1
+ """
2
+ Validator command display helpers.
3
+ """
4
+
5
+ from typing import Any
6
+
7
+ from ...ui.colors import error, info, success
8
+ from ...ui.components import HTCLIPanel
9
+ from ...ui.display import HTCLIConsole
10
+
11
+ console = HTCLIConsole()
12
+
13
+
14
+ def _response_value(response: Any, field: str, default: Any = None) -> Any:
15
+ if isinstance(response, dict):
16
+ return response.get(field, default)
17
+ return getattr(response, field, default)
18
+
19
+
20
+ def display_validator_result(response: Any, operation: str) -> None:
21
+ """Display a validator extrinsic response."""
22
+ succeeded = _response_value(response, "success", False)
23
+ message = _response_value(response, "message") or operation
24
+ transaction_hash = _response_value(response, "transaction_hash")
25
+ block_number = _response_value(response, "block_number")
26
+ validator_id = _response_value(response, "validator_id")
27
+ hotkey = _response_value(response, "hotkey")
28
+
29
+ if not succeeded:
30
+ error_message = _response_value(response, "error", f"{operation} failed")
31
+ panel = HTCLIPanel(
32
+ error(str(error_message)),
33
+ title=f"{operation} Failed",
34
+ border_style="htcli.error",
35
+ )
36
+ panel.render(console.console)
37
+ return
38
+
39
+ lines = [success(str(message))]
40
+ if validator_id is not None:
41
+ lines.append(f"[htcli.value]Validator ID:[/htcli.value] {validator_id}")
42
+ if hotkey:
43
+ lines.append(f"[htcli.value]Hotkey:[/htcli.value] {hotkey}")
44
+ if transaction_hash:
45
+ lines.append(f"[htcli.value]Transaction:[/htcli.value] {transaction_hash}")
46
+ if block_number is not None:
47
+ lines.append(f"[htcli.value]Block:[/htcli.value] {block_number}")
48
+
49
+ panel = HTCLIPanel(
50
+ "\n".join(lines) if lines else info("Submitted"),
51
+ title=operation,
52
+ border_style="htcli.success",
53
+ )
54
+ panel.render(console.console)
@@ -0,0 +1,340 @@
1
+ """
2
+ Validator command handlers.
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 ...models.requests.validator import (
10
+ ValidatorColdkeyUpdateRequest,
11
+ ValidatorDelegateAccountUpdateRequest,
12
+ ValidatorDelegateRewardRateUpdateRequest,
13
+ ValidatorHotkeyUpdateRequest,
14
+ ValidatorIdentityUpdateRequest,
15
+ ValidatorRegisterRequest,
16
+ )
17
+ from ...ui.components import HTCLILoadingContext
18
+ from ...ui.display import print_error
19
+ from ...utils import retrieve_wallet_with_validation
20
+ from ...utils.prompts import integer_prompt, string_prompt
21
+ from ..node.prompts import normalize_delegate_reward_rate
22
+ from .display import display_validator_result
23
+
24
+
25
+ def _resolve_signing_keypair(coldkey: Optional[str], purpose: str):
26
+ if coldkey:
27
+ from ...utils.wallet.core import resolve_coldkey_and_get_keypair
28
+
29
+ return resolve_coldkey_and_get_keypair(coldkey)
30
+ return retrieve_wallet_with_validation(wallet_type="coldkey", purpose=purpose)
31
+
32
+
33
+ def _connect_client():
34
+ client = get_client()
35
+ if not client.connect():
36
+ print_error("Failed to connect to blockchain")
37
+ return None
38
+ if not client.substrate:
39
+ print_error("Blockchain connection failed!")
40
+ raise RuntimeError("Not connected to blockchain")
41
+ if not client.extrinsics:
42
+ print_error("Extrinsics layer not initialized. Connection may have failed.")
43
+ raise RuntimeError("Extrinsics layer not available")
44
+ return client
45
+
46
+
47
+ def _rate_or_prompt(value: Optional[int], prompt_text: str) -> int:
48
+ if value is None:
49
+ value = integer_prompt(prompt_text)
50
+ return normalize_delegate_reward_rate(value)
51
+
52
+
53
+ def register_validator_handler(
54
+ hotkey: Optional[str] = None,
55
+ delegate_rate: Optional[int] = None,
56
+ delegate_account: Optional[str] = None,
57
+ delegate_account_rate: Optional[int] = None,
58
+ coldkey: Optional[str] = None,
59
+ ):
60
+ """Handle validator registration."""
61
+ try:
62
+ if hotkey is None:
63
+ hotkey = string_prompt("Enter validator hotkey address")
64
+
65
+ delegate_reward_rate = _rate_or_prompt(
66
+ delegate_rate, "Enter delegate reward rate"
67
+ )
68
+ delegate_account_rate = (
69
+ normalize_delegate_reward_rate(delegate_account_rate)
70
+ if delegate_account_rate is not None
71
+ else None
72
+ )
73
+
74
+ _, keypair = _resolve_signing_keypair(
75
+ coldkey, "sign the validator registration transaction"
76
+ )
77
+
78
+ request = ValidatorRegisterRequest(
79
+ hotkey=hotkey,
80
+ delegate_reward_rate=delegate_reward_rate,
81
+ delegate_account_id=delegate_account,
82
+ delegate_account_rate=delegate_account_rate,
83
+ identity=None,
84
+ )
85
+
86
+ client = _connect_client()
87
+ if client is None:
88
+ return
89
+
90
+ with HTCLILoadingContext("Registering validator..."):
91
+ response = client.extrinsics.validator.register_validator(request, keypair)
92
+
93
+ display_validator_result(response, "Validator Registration")
94
+ except Exception as e:
95
+ handle_and_display_error(e, operation="validator register")
96
+
97
+
98
+ def update_delegate_rate_handler(
99
+ validator_id: Optional[int] = None,
100
+ delegate_rate: Optional[int] = None,
101
+ coldkey: Optional[str] = None,
102
+ ):
103
+ """Handle validator delegate reward rate updates."""
104
+ try:
105
+ if validator_id is None:
106
+ validator_id = integer_prompt("Enter validator ID")
107
+ new_delegate_reward_rate = _rate_or_prompt(
108
+ delegate_rate, "Enter new delegate reward rate"
109
+ )
110
+
111
+ _, keypair = _resolve_signing_keypair(
112
+ coldkey, "sign the validator delegate reward rate update transaction"
113
+ )
114
+ request = ValidatorDelegateRewardRateUpdateRequest(
115
+ validator_id=validator_id,
116
+ new_delegate_reward_rate=new_delegate_reward_rate,
117
+ )
118
+
119
+ client = _connect_client()
120
+ if client is None:
121
+ return
122
+
123
+ with HTCLILoadingContext(
124
+ f"Updating validator {validator_id} delegate reward rate..."
125
+ ):
126
+ response = (
127
+ client.extrinsics.validator.update_validator_delegate_reward_rate(
128
+ request, keypair
129
+ )
130
+ )
131
+
132
+ display_validator_result(response, "Validator Delegate Rate Update")
133
+ except Exception as e:
134
+ handle_and_display_error(e, operation="validator update delegate rate")
135
+
136
+
137
+ def update_delegate_account_handler(
138
+ validator_id: Optional[int] = None,
139
+ delegate_account: Optional[str] = None,
140
+ delegate_account_rate: Optional[int] = None,
141
+ clear: bool = False,
142
+ coldkey: Optional[str] = None,
143
+ ):
144
+ """Handle validator delegate account updates."""
145
+ try:
146
+ if validator_id is None:
147
+ validator_id = integer_prompt("Enter validator ID")
148
+
149
+ if clear:
150
+ delegate_account = None
151
+ delegate_rate = None
152
+ else:
153
+ if delegate_account is None:
154
+ delegate_account = string_prompt("Enter delegate account address")
155
+ delegate_rate = (
156
+ normalize_delegate_reward_rate(delegate_account_rate)
157
+ if delegate_account_rate is not None
158
+ else None
159
+ )
160
+
161
+ _, keypair = _resolve_signing_keypair(
162
+ coldkey, "sign the validator delegate account update transaction"
163
+ )
164
+ request = ValidatorDelegateAccountUpdateRequest(
165
+ validator_id=validator_id,
166
+ delegate_account_id=delegate_account,
167
+ delegate_rate=delegate_rate,
168
+ )
169
+
170
+ client = _connect_client()
171
+ if client is None:
172
+ return
173
+
174
+ with HTCLILoadingContext(
175
+ f"Updating validator {validator_id} delegate account..."
176
+ ):
177
+ response = client.extrinsics.validator.update_validator_delegate_account(
178
+ request, keypair
179
+ )
180
+
181
+ display_validator_result(response, "Validator Delegate Account Update")
182
+ except Exception as e:
183
+ handle_and_display_error(e, operation="validator update delegate account")
184
+
185
+
186
+ def update_hotkey_handler(
187
+ validator_id: Optional[int] = None,
188
+ hotkey: Optional[str] = None,
189
+ coldkey: Optional[str] = None,
190
+ ):
191
+ """Handle validator hotkey updates."""
192
+ try:
193
+ if validator_id is None:
194
+ validator_id = integer_prompt("Enter validator ID")
195
+ if hotkey is None:
196
+ hotkey = string_prompt("Enter new validator hotkey address")
197
+
198
+ _, keypair = _resolve_signing_keypair(
199
+ coldkey, "sign the validator hotkey update transaction"
200
+ )
201
+ request = ValidatorHotkeyUpdateRequest(
202
+ validator_id=validator_id,
203
+ new_hotkey=hotkey,
204
+ )
205
+
206
+ client = _connect_client()
207
+ if client is None:
208
+ return
209
+
210
+ with HTCLILoadingContext(f"Updating validator {validator_id} hotkey..."):
211
+ response = client.extrinsics.validator.update_validator_hotkey(
212
+ request, keypair
213
+ )
214
+
215
+ display_validator_result(response, "Validator Hotkey Update")
216
+ except Exception as e:
217
+ handle_and_display_error(e, operation="validator update hotkey")
218
+
219
+
220
+ def update_coldkey_handler(
221
+ validator_id: Optional[int] = None,
222
+ new_coldkey: Optional[str] = None,
223
+ coldkey: Optional[str] = None,
224
+ ):
225
+ """Handle validator coldkey updates."""
226
+ try:
227
+ if validator_id is None:
228
+ validator_id = integer_prompt("Enter validator ID")
229
+ if new_coldkey is None:
230
+ new_coldkey = string_prompt("Enter new validator coldkey address")
231
+
232
+ _, keypair = _resolve_signing_keypair(
233
+ coldkey, "sign the validator coldkey update transaction"
234
+ )
235
+ request = ValidatorColdkeyUpdateRequest(
236
+ validator_id=validator_id,
237
+ new_coldkey=new_coldkey,
238
+ )
239
+
240
+ client = _connect_client()
241
+ if client is None:
242
+ return
243
+
244
+ with HTCLILoadingContext(f"Updating validator {validator_id} coldkey..."):
245
+ response = client.extrinsics.validator.update_validator_coldkey(
246
+ request, keypair
247
+ )
248
+
249
+ display_validator_result(response, "Validator Coldkey Update")
250
+ except Exception as e:
251
+ handle_and_display_error(e, operation="validator update coldkey")
252
+
253
+
254
+ def _encode_identity_fields(
255
+ name: Optional[str] = None,
256
+ url: Optional[str] = None,
257
+ image: Optional[str] = None,
258
+ discord: Optional[str] = None,
259
+ x: Optional[str] = None,
260
+ telegram: Optional[str] = None,
261
+ github: Optional[str] = None,
262
+ hugging_face: Optional[str] = None,
263
+ description: Optional[str] = None,
264
+ misc: Optional[str] = None,
265
+ ) -> dict[str, bytes]:
266
+ values = {
267
+ "name": name,
268
+ "url": url,
269
+ "image": image,
270
+ "discord": discord,
271
+ "x": x,
272
+ "telegram": telegram,
273
+ "github": github,
274
+ "hugging_face": hugging_face,
275
+ "description": description,
276
+ "misc": misc,
277
+ }
278
+ if not any(value is not None for value in values.values()):
279
+ raise ValueError("Provide at least one identity field or use --clear")
280
+ return {
281
+ field: ("" if value is None else value).encode("utf-8")
282
+ for field, value in values.items()
283
+ }
284
+
285
+
286
+ def update_identity_handler(
287
+ validator_id: Optional[int] = None,
288
+ name: Optional[str] = None,
289
+ url: Optional[str] = None,
290
+ image: Optional[str] = None,
291
+ discord: Optional[str] = None,
292
+ x: Optional[str] = None,
293
+ telegram: Optional[str] = None,
294
+ github: Optional[str] = None,
295
+ hugging_face: Optional[str] = None,
296
+ description: Optional[str] = None,
297
+ misc: Optional[str] = None,
298
+ clear: bool = False,
299
+ coldkey: Optional[str] = None,
300
+ ):
301
+ """Handle validator identity updates."""
302
+ try:
303
+ if validator_id is None:
304
+ validator_id = integer_prompt("Enter validator ID")
305
+
306
+ identity = None
307
+ if not clear:
308
+ identity = _encode_identity_fields(
309
+ name=name,
310
+ url=url,
311
+ image=image,
312
+ discord=discord,
313
+ x=x,
314
+ telegram=telegram,
315
+ github=github,
316
+ hugging_face=hugging_face,
317
+ description=description,
318
+ misc=misc,
319
+ )
320
+
321
+ _, keypair = _resolve_signing_keypair(
322
+ coldkey, "sign the validator identity update transaction"
323
+ )
324
+ request = ValidatorIdentityUpdateRequest(
325
+ validator_id=validator_id,
326
+ identity=identity,
327
+ )
328
+
329
+ client = _connect_client()
330
+ if client is None:
331
+ return
332
+
333
+ with HTCLILoadingContext(f"Updating validator {validator_id} identity..."):
334
+ response = client.extrinsics.validator.update_validator_identity(
335
+ request, keypair
336
+ )
337
+
338
+ display_validator_result(response, "Validator Identity Update")
339
+ except Exception as e:
340
+ handle_and_display_error(e, operation="validator update identity")