bittensor-cli 9.9.0__py3-none-any.whl → 9.10.1__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.
- bittensor_cli/cli.py +174 -58
- bittensor_cli/src/__init__.py +4 -4
- bittensor_cli/src/bittensor/chain_data.py +2 -0
- bittensor_cli/src/bittensor/extrinsics/registration.py +29 -25
- bittensor_cli/src/bittensor/extrinsics/root.py +6 -5
- bittensor_cli/src/bittensor/extrinsics/transfer.py +1 -1
- bittensor_cli/src/bittensor/subtensor_interface.py +2 -1
- bittensor_cli/src/bittensor/utils.py +16 -2
- bittensor_cli/src/commands/stake/add.py +4 -3
- bittensor_cli/src/commands/stake/children_hotkeys.py +16 -14
- bittensor_cli/src/commands/stake/move.py +6 -4
- bittensor_cli/src/commands/stake/remove.py +5 -4
- bittensor_cli/src/commands/subnets/price.py +126 -30
- bittensor_cli/src/commands/subnets/subnets.py +3 -2
- bittensor_cli/src/commands/sudo.py +36 -32
- bittensor_cli/src/commands/wallets.py +73 -19
- bittensor_cli/src/commands/weights.py +2 -1
- {bittensor_cli-9.9.0.dist-info → bittensor_cli-9.10.1.dist-info}/METADATA +13 -5
- {bittensor_cli-9.9.0.dist-info → bittensor_cli-9.10.1.dist-info}/RECORD +22 -22
- {bittensor_cli-9.9.0.dist-info → bittensor_cli-9.10.1.dist-info}/WHEEL +0 -0
- {bittensor_cli-9.9.0.dist-info → bittensor_cli-9.10.1.dist-info}/entry_points.txt +0 -0
- {bittensor_cli-9.9.0.dist-info → bittensor_cli-9.10.1.dist-info}/top_level.txt +0 -0
@@ -10,6 +10,7 @@ import plotille
|
|
10
10
|
import plotly.graph_objects as go
|
11
11
|
|
12
12
|
from bittensor_cli.src import COLOR_PALETTE
|
13
|
+
from bittensor_cli.src.bittensor.chain_data import DynamicInfo
|
13
14
|
from bittensor_cli.src.bittensor.utils import (
|
14
15
|
console,
|
15
16
|
err_console,
|
@@ -27,7 +28,8 @@ async def price(
|
|
27
28
|
subtensor: "SubtensorInterface",
|
28
29
|
netuids: list[int],
|
29
30
|
all_netuids: bool = False,
|
30
|
-
interval_hours: int =
|
31
|
+
interval_hours: int = 4,
|
32
|
+
current_only: bool = False,
|
31
33
|
html_output: bool = False,
|
32
34
|
log_scale: bool = False,
|
33
35
|
json_output: bool = False,
|
@@ -41,45 +43,96 @@ async def price(
|
|
41
43
|
blocks_per_hour = int(3600 / 12) # ~300 blocks per hour
|
42
44
|
total_blocks = blocks_per_hour * interval_hours
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
|
46
|
+
if not current_only:
|
47
|
+
with console.status(":chart_increasing: Fetching historical price data..."):
|
48
|
+
current_block_hash = await subtensor.substrate.get_chain_head()
|
49
|
+
current_block = await subtensor.substrate.get_block_number(
|
50
|
+
current_block_hash
|
51
|
+
)
|
47
52
|
|
48
|
-
|
49
|
-
|
50
|
-
|
53
|
+
step = 300
|
54
|
+
start_block = max(0, current_block - total_blocks)
|
55
|
+
block_numbers = list(range(start_block, current_block + 1, step))
|
51
56
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
+
# Block hashes
|
58
|
+
block_hash_cors = [
|
59
|
+
subtensor.substrate.get_block_hash(bn) for bn in block_numbers
|
60
|
+
]
|
61
|
+
block_hashes = await asyncio.gather(*block_hash_cors)
|
57
62
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
# We fetch all subnets when there is more than one netuid
|
64
|
+
if all_netuids or len(netuids) > 1:
|
65
|
+
subnet_info_cors = [subtensor.all_subnets(bh) for bh in block_hashes]
|
66
|
+
else:
|
67
|
+
# If there is only one netuid, we fetch the subnet info for that netuid
|
68
|
+
netuid = netuids[0]
|
69
|
+
subnet_info_cors = [subtensor.subnet(netuid, bh) for bh in block_hashes]
|
70
|
+
all_subnet_infos = await asyncio.gather(*subnet_info_cors)
|
66
71
|
|
67
72
|
subnet_data = _process_subnet_data(
|
68
73
|
block_numbers, all_subnet_infos, netuids, all_netuids
|
69
74
|
)
|
75
|
+
if not subnet_data:
|
76
|
+
err_console.print("[red]No valid price data found for any subnet[/red]")
|
77
|
+
return
|
70
78
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
79
|
+
if html_output:
|
80
|
+
await _generate_html_output(
|
81
|
+
subnet_data, block_numbers, interval_hours, log_scale
|
82
|
+
)
|
83
|
+
elif json_output:
|
84
|
+
json_console.print(json.dumps(_generate_json_output(subnet_data)))
|
85
|
+
else:
|
86
|
+
_generate_cli_output(subnet_data, block_numbers, interval_hours, log_scale)
|
87
|
+
else:
|
88
|
+
with console.status("Fetching current price data..."):
|
89
|
+
if all_netuids or len(netuids) > 1:
|
90
|
+
all_subnet_info = await subtensor.all_subnets()
|
91
|
+
else:
|
92
|
+
all_subnet_info = [await subtensor.subnet(netuid=netuids[0])]
|
93
|
+
subnet_data = _process_current_subnet_data(
|
94
|
+
all_subnet_info, netuids, all_netuids
|
78
95
|
)
|
79
|
-
|
80
|
-
|
96
|
+
if json_output:
|
97
|
+
json_console.print(json.dumps(_generate_json_output(subnet_data)))
|
98
|
+
else:
|
99
|
+
_generate_cli_output_current(subnet_data)
|
100
|
+
|
101
|
+
|
102
|
+
def _process_current_subnet_data(subnet_infos: list[DynamicInfo], netuids, all_netuids):
|
103
|
+
subnet_data = {}
|
104
|
+
if all_netuids or len(netuids) > 1:
|
105
|
+
# Most recent data for statistics
|
106
|
+
for subnet_info in subnet_infos:
|
107
|
+
stats = {
|
108
|
+
"current_price": subnet_info.price,
|
109
|
+
"supply": subnet_info.alpha_in.tao + subnet_info.alpha_out.tao,
|
110
|
+
"market_cap": subnet_info.price.tao
|
111
|
+
* (subnet_info.alpha_in.tao + subnet_info.alpha_out.tao),
|
112
|
+
"emission": subnet_info.emission.tao,
|
113
|
+
"stake": subnet_info.alpha_out.tao,
|
114
|
+
"symbol": subnet_info.symbol,
|
115
|
+
"name": get_subnet_name(subnet_info),
|
116
|
+
}
|
117
|
+
subnet_data[subnet_info.netuid] = {
|
118
|
+
"stats": stats,
|
119
|
+
}
|
81
120
|
else:
|
82
|
-
|
121
|
+
subnet_info = subnet_infos[0]
|
122
|
+
stats = {
|
123
|
+
"current_price": subnet_info.price.tao,
|
124
|
+
"supply": subnet_info.alpha_in.tao + subnet_info.alpha_out.tao,
|
125
|
+
"market_cap": subnet_info.price.tao
|
126
|
+
* (subnet_info.alpha_in.tao + subnet_info.alpha_out.tao),
|
127
|
+
"emission": subnet_info.emission.tao,
|
128
|
+
"stake": subnet_info.alpha_out.tao,
|
129
|
+
"symbol": subnet_info.symbol,
|
130
|
+
"name": get_subnet_name(subnet_info),
|
131
|
+
}
|
132
|
+
subnet_data[subnet_info.netuid] = {
|
133
|
+
"stats": stats,
|
134
|
+
}
|
135
|
+
return subnet_data
|
83
136
|
|
84
137
|
|
85
138
|
def _process_subnet_data(block_numbers, all_subnet_infos, netuids, all_netuids):
|
@@ -626,3 +679,46 @@ def _generate_cli_output(subnet_data, block_numbers, interval_hours, log_scale):
|
|
626
679
|
)
|
627
680
|
|
628
681
|
console.print(stats_text)
|
682
|
+
|
683
|
+
|
684
|
+
def _generate_cli_output_current(subnet_data):
|
685
|
+
for netuid, data in subnet_data.items():
|
686
|
+
stats = data["stats"]
|
687
|
+
|
688
|
+
if netuid != 0:
|
689
|
+
console.print(
|
690
|
+
f"\n[{COLOR_PALETTE.G.SYM}]Subnet {netuid} - {stats['symbol']} "
|
691
|
+
f"[cyan]{stats['name']}[/cyan][/{COLOR_PALETTE.G.SYM}]\n"
|
692
|
+
f"Current: [blue]{stats['current_price']:.6f}{stats['symbol']}[/blue]\n"
|
693
|
+
)
|
694
|
+
else:
|
695
|
+
console.print(
|
696
|
+
f"\n[{COLOR_PALETTE.G.SYM}]Subnet {netuid} - {stats['symbol']} "
|
697
|
+
f"[cyan]{stats['name']}[/cyan][/{COLOR_PALETTE.G.SYM}]\n"
|
698
|
+
f"Current: [blue]{stats['symbol']} {stats['current_price']:.6f}[/blue]\n"
|
699
|
+
)
|
700
|
+
|
701
|
+
if netuid != 0:
|
702
|
+
stats_text = (
|
703
|
+
"\nLatest stats:\n"
|
704
|
+
f"Supply: [{COLOR_PALETTE.P.ALPHA_IN}]"
|
705
|
+
f"{stats['supply']:,.2f} {stats['symbol']}[/{COLOR_PALETTE.P.ALPHA_IN}]\n"
|
706
|
+
f"Market Cap: [steel_blue3]{stats['market_cap']:,.2f} {stats['symbol']} / 21M[/steel_blue3]\n"
|
707
|
+
f"Emission: [{COLOR_PALETTE.P.EMISSION}]"
|
708
|
+
f"{stats['emission']:,.2f} {stats['symbol']}[/{COLOR_PALETTE.P.EMISSION}]\n"
|
709
|
+
f"Stake: [{COLOR_PALETTE.S.TAO}]"
|
710
|
+
f"{stats['stake']:,.2f} {stats['symbol']}[/{COLOR_PALETTE.S.TAO}]"
|
711
|
+
)
|
712
|
+
else:
|
713
|
+
stats_text = (
|
714
|
+
"\nLatest stats:\n"
|
715
|
+
f"Supply: [{COLOR_PALETTE.P.ALPHA_IN}]"
|
716
|
+
f"{stats['symbol']} {stats['supply']:,.2f}[/{COLOR_PALETTE.P.ALPHA_IN}]\n"
|
717
|
+
f"Market Cap: [steel_blue3]{stats['symbol']} {stats['market_cap']:,.2f} / 21M[/steel_blue3]\n"
|
718
|
+
f"Emission: [{COLOR_PALETTE.P.EMISSION}]"
|
719
|
+
f"{stats['symbol']} {stats['emission']:,.2f}[/{COLOR_PALETTE.P.EMISSION}]\n"
|
720
|
+
f"Stake: [{COLOR_PALETTE.S.TAO}]"
|
721
|
+
f"{stats['symbol']} {stats['stake']:,.2f}[/{COLOR_PALETTE.S.TAO}]"
|
722
|
+
)
|
723
|
+
|
724
|
+
console.print(stats_text)
|
@@ -36,6 +36,7 @@ from bittensor_cli.src.bittensor.utils import (
|
|
36
36
|
unlock_key,
|
37
37
|
blocks_to_duration,
|
38
38
|
json_console,
|
39
|
+
get_hotkey_pub_ss58,
|
39
40
|
)
|
40
41
|
|
41
42
|
if TYPE_CHECKING:
|
@@ -114,7 +115,7 @@ async def register_subnetwork_extrinsic(
|
|
114
115
|
return False, None
|
115
116
|
|
116
117
|
call_params = {
|
117
|
-
"hotkey": wallet
|
118
|
+
"hotkey": get_hotkey_pub_ss58(wallet),
|
118
119
|
"mechid": 1,
|
119
120
|
}
|
120
121
|
call_function = "register_network"
|
@@ -1654,7 +1655,7 @@ async def register(
|
|
1654
1655
|
str(netuid),
|
1655
1656
|
f"{Balance.get_unit(netuid)}",
|
1656
1657
|
f"τ {current_recycle.tao:.4f}",
|
1657
|
-
f"{wallet
|
1658
|
+
f"{get_hotkey_pub_ss58(wallet)}",
|
1658
1659
|
f"{wallet.coldkeypub.ss58_address}",
|
1659
1660
|
)
|
1660
1661
|
console.print(table)
|
@@ -26,6 +26,7 @@ from bittensor_cli.src.bittensor.utils import (
|
|
26
26
|
json_console,
|
27
27
|
string_to_u16,
|
28
28
|
string_to_u64,
|
29
|
+
get_hotkey_pub_ss58,
|
29
30
|
)
|
30
31
|
|
31
32
|
if TYPE_CHECKING:
|
@@ -176,7 +177,7 @@ async def set_hyperparameter_extrinsic(
|
|
176
177
|
wait_for_inclusion: bool = False,
|
177
178
|
wait_for_finalization: bool = True,
|
178
179
|
prompt: bool = True,
|
179
|
-
) -> bool:
|
180
|
+
) -> tuple[bool, str]:
|
180
181
|
"""Sets a hyperparameter for a specific subnetwork.
|
181
182
|
|
182
183
|
:param subtensor: initialized SubtensorInterface object
|
@@ -199,13 +200,14 @@ async def set_hyperparameter_extrinsic(
|
|
199
200
|
params=[netuid],
|
200
201
|
)
|
201
202
|
if subnet_owner != wallet.coldkeypub.ss58_address:
|
202
|
-
|
203
|
+
err_msg = (
|
203
204
|
":cross_mark: [red]This wallet doesn't own the specified subnet.[/red]"
|
204
205
|
)
|
205
|
-
|
206
|
+
err_console.print(err_msg)
|
207
|
+
return False, err_msg
|
206
208
|
|
207
|
-
if not unlock_key(wallet).success:
|
208
|
-
return False
|
209
|
+
if not (ulw := unlock_key(wallet)).success:
|
210
|
+
return False, ulw.message
|
209
211
|
|
210
212
|
arbitrary_extrinsic = False
|
211
213
|
|
@@ -217,15 +219,14 @@ async def set_hyperparameter_extrinsic(
|
|
217
219
|
)
|
218
220
|
extrinsic = parameter
|
219
221
|
if not arbitrary_extrinsic:
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
return False
|
222
|
+
err_msg = ":cross_mark: [red]Invalid hyperparameter specified.[/red]"
|
223
|
+
err_console.print(err_msg)
|
224
|
+
return False, err_msg
|
224
225
|
if sudo_ and prompt:
|
225
226
|
if not Confirm.ask(
|
226
227
|
"This hyperparam is only settable by root sudo users. If you are not, this will fail. Please confirm"
|
227
228
|
):
|
228
|
-
return False
|
229
|
+
return False, "This hyperparam is only settable by root sudo users"
|
229
230
|
|
230
231
|
substrate = subtensor.substrate
|
231
232
|
msg_value = value if not arbitrary_extrinsic else call_params
|
@@ -253,10 +254,11 @@ async def set_hyperparameter_extrinsic(
|
|
253
254
|
]
|
254
255
|
|
255
256
|
if len(value) < len(non_netuid_fields):
|
256
|
-
|
257
|
+
err_msg = (
|
257
258
|
"Not enough values provided in the list for all parameters"
|
258
259
|
)
|
259
|
-
|
260
|
+
err_console.print(err_msg)
|
261
|
+
return False, err_msg
|
260
262
|
|
261
263
|
call_params.update(
|
262
264
|
{name: val for name, val in zip(non_netuid_fields, value)}
|
@@ -289,20 +291,20 @@ async def set_hyperparameter_extrinsic(
|
|
289
291
|
)
|
290
292
|
if not success:
|
291
293
|
err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
|
292
|
-
return False
|
294
|
+
return False, err_msg
|
293
295
|
elif arbitrary_extrinsic:
|
294
296
|
console.print(
|
295
297
|
f":white_heavy_check_mark: "
|
296
298
|
f"[dark_sea_green3]Hyperparameter {parameter} values changed to {call_params}[/dark_sea_green3]"
|
297
299
|
)
|
298
|
-
return True
|
300
|
+
return True, ""
|
299
301
|
# Successful registration, final check for membership
|
300
302
|
else:
|
301
303
|
console.print(
|
302
304
|
f":white_heavy_check_mark: "
|
303
305
|
f"[dark_sea_green3]Hyperparameter {parameter} changed to {value}[/dark_sea_green3]"
|
304
306
|
)
|
305
|
-
return True
|
307
|
+
return True, ""
|
306
308
|
|
307
309
|
|
308
310
|
async def _get_senate_members(
|
@@ -497,7 +499,7 @@ async def vote_senate_extrinsic(
|
|
497
499
|
call_module="SubtensorModule",
|
498
500
|
call_function="vote",
|
499
501
|
call_params={
|
500
|
-
"hotkey": wallet
|
502
|
+
"hotkey": get_hotkey_pub_ss58(wallet),
|
501
503
|
"proposal": proposal_hash,
|
502
504
|
"index": proposal_idx,
|
503
505
|
"approve": vote,
|
@@ -513,9 +515,10 @@ async def vote_senate_extrinsic(
|
|
513
515
|
# Successful vote, final check for data
|
514
516
|
else:
|
515
517
|
if vote_data := await subtensor.get_vote_data(proposal_hash):
|
518
|
+
hotkey_ss58 = get_hotkey_pub_ss58(wallet)
|
516
519
|
if (
|
517
|
-
vote_data.ayes.count(
|
518
|
-
or vote_data.nays.count(
|
520
|
+
vote_data.ayes.count(hotkey_ss58) > 0
|
521
|
+
or vote_data.nays.count(hotkey_ss58) > 0
|
519
522
|
):
|
520
523
|
console.print(":white_heavy_check_mark: [green]Vote cast.[/green]")
|
521
524
|
return True
|
@@ -617,25 +620,26 @@ async def sudo_set_hyperparameter(
|
|
617
620
|
param_value: Optional[str],
|
618
621
|
prompt: bool,
|
619
622
|
json_output: bool,
|
620
|
-
):
|
623
|
+
) -> tuple[bool, str]:
|
621
624
|
"""Set subnet hyperparameters."""
|
622
625
|
is_allowed_value, value = allowed_value(param_name, param_value)
|
623
626
|
if not is_allowed_value:
|
624
|
-
|
627
|
+
err_msg = (
|
625
628
|
f"Hyperparameter [dark_orange]{param_name}[/dark_orange] value is not within bounds. "
|
626
629
|
f"Value is {param_value} but must be {value}"
|
627
630
|
)
|
628
|
-
|
629
|
-
|
631
|
+
err_console.print(err_msg)
|
632
|
+
return False, err_msg
|
633
|
+
success, err_msg = await set_hyperparameter_extrinsic(
|
630
634
|
subtensor, wallet, netuid, param_name, value, prompt=prompt
|
631
635
|
)
|
632
636
|
if json_output:
|
633
|
-
return success
|
637
|
+
return success, err_msg
|
634
638
|
if success:
|
635
639
|
console.print("\n")
|
636
640
|
print_verbose("Fetching hyperparameters")
|
637
641
|
await get_hyperparameters(subtensor, netuid=netuid)
|
638
|
-
return success
|
642
|
+
return success, err_msg
|
639
643
|
|
640
644
|
|
641
645
|
async def get_hyperparameters(
|
@@ -859,10 +863,9 @@ async def senate_vote(
|
|
859
863
|
return False
|
860
864
|
|
861
865
|
print_verbose(f"Fetching senate status of {wallet.hotkey_str}")
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
)
|
866
|
+
hotkey_ss58 = get_hotkey_pub_ss58(wallet)
|
867
|
+
if not await _is_senate_member(subtensor, hotkey_ss58=hotkey_ss58):
|
868
|
+
err_console.print(f"Aborting: Hotkey {hotkey_ss58} isn't a senate member.")
|
866
869
|
return False
|
867
870
|
|
868
871
|
# Unlock the wallet.
|
@@ -890,7 +893,7 @@ async def senate_vote(
|
|
890
893
|
|
891
894
|
|
892
895
|
async def get_current_take(subtensor: "SubtensorInterface", wallet: Wallet):
|
893
|
-
current_take = await subtensor.current_take(wallet
|
896
|
+
current_take = await subtensor.current_take(get_hotkey_pub_ss58(wallet))
|
894
897
|
return current_take
|
895
898
|
|
896
899
|
|
@@ -912,12 +915,13 @@ async def set_take(
|
|
912
915
|
return False
|
913
916
|
|
914
917
|
block_hash = await subtensor.substrate.get_chain_head()
|
918
|
+
hotkey_ss58 = get_hotkey_pub_ss58(wallet)
|
915
919
|
netuids_registered = await subtensor.get_netuids_for_hotkey(
|
916
|
-
|
920
|
+
hotkey_ss58, block_hash=block_hash
|
917
921
|
)
|
918
922
|
if not len(netuids_registered) > 0:
|
919
923
|
err_console.print(
|
920
|
-
f"Hotkey [{COLOR_PALETTE.G.HK}]{
|
924
|
+
f"Hotkey [{COLOR_PALETTE.G.HK}]{hotkey_ss58}[/{COLOR_PALETTE.G.HK}] is not registered to"
|
921
925
|
f" any subnet. Please register using [{COLOR_PALETTE.G.SUBHEAD}]`btcli subnets register`"
|
922
926
|
f"[{COLOR_PALETTE.G.SUBHEAD}] and try again."
|
923
927
|
)
|
@@ -926,7 +930,7 @@ async def set_take(
|
|
926
930
|
result: bool = await set_take_extrinsic(
|
927
931
|
subtensor=subtensor,
|
928
932
|
wallet=wallet,
|
929
|
-
delegate_ss58=
|
933
|
+
delegate_ss58=hotkey_ss58,
|
930
934
|
take=take,
|
931
935
|
)
|
932
936
|
|
@@ -3,7 +3,7 @@ import itertools
|
|
3
3
|
import json
|
4
4
|
import os
|
5
5
|
from collections import defaultdict
|
6
|
-
from typing import Generator, Optional
|
6
|
+
from typing import Generator, Optional, Union
|
7
7
|
|
8
8
|
import aiohttp
|
9
9
|
from bittensor_wallet import Wallet, Keypair
|
@@ -48,6 +48,7 @@ from bittensor_cli.src.bittensor.utils import (
|
|
48
48
|
WalletLike,
|
49
49
|
blocks_to_duration,
|
50
50
|
decode_account_id,
|
51
|
+
get_hotkey_pub_ss58,
|
51
52
|
)
|
52
53
|
|
53
54
|
|
@@ -159,7 +160,7 @@ async def regen_coldkey(
|
|
159
160
|
"name": new_wallet.name,
|
160
161
|
"path": new_wallet.path,
|
161
162
|
"hotkey": new_wallet.hotkey_str,
|
162
|
-
"hotkey_ss58": new_wallet
|
163
|
+
"hotkey_ss58": get_hotkey_pub_ss58(new_wallet),
|
163
164
|
"coldkey_ss58": new_wallet.coldkeypub.ss58_address,
|
164
165
|
},
|
165
166
|
"error": "",
|
@@ -209,7 +210,7 @@ async def regen_coldkey_pub(
|
|
209
210
|
"name": new_coldkeypub.name,
|
210
211
|
"path": new_coldkeypub.path,
|
211
212
|
"hotkey": new_coldkeypub.hotkey_str,
|
212
|
-
"hotkey_ss58": new_coldkeypub
|
213
|
+
"hotkey_ss58": get_hotkey_pub_ss58(new_coldkeypub),
|
213
214
|
"coldkey_ss58": new_coldkeypub.coldkeypub.ss58_address,
|
214
215
|
},
|
215
216
|
"error": "",
|
@@ -255,7 +256,7 @@ async def regen_hotkey(
|
|
255
256
|
console.print(
|
256
257
|
"\n✅ [dark_sea_green]Regenerated hotkey successfully!\n",
|
257
258
|
f"[dark_sea_green]Wallet name: ({new_hotkey_.name}), path: ({new_hotkey_.path}), "
|
258
|
-
f"hotkey ss58: ({new_hotkey_.
|
259
|
+
f"hotkey ss58: ({new_hotkey_.hotkeypub.ss58_address})",
|
259
260
|
)
|
260
261
|
if json_output:
|
261
262
|
json_console.print(
|
@@ -266,7 +267,7 @@ async def regen_hotkey(
|
|
266
267
|
"name": new_hotkey_.name,
|
267
268
|
"path": new_hotkey_.path,
|
268
269
|
"hotkey": new_hotkey_.hotkey_str,
|
269
|
-
"hotkey_ss58": new_hotkey_.
|
270
|
+
"hotkey_ss58": new_hotkey_.hotkeypub.ss58_address,
|
270
271
|
"coldkey_ss58": new_hotkey_.coldkeypub.ss58_address,
|
271
272
|
},
|
272
273
|
"error": "",
|
@@ -287,6 +288,50 @@ async def regen_hotkey(
|
|
287
288
|
)
|
288
289
|
|
289
290
|
|
291
|
+
async def regen_hotkey_pub(
|
292
|
+
wallet: Wallet,
|
293
|
+
ss58_address: str,
|
294
|
+
public_key_hex: str,
|
295
|
+
overwrite: Optional[bool] = False,
|
296
|
+
json_output: bool = False,
|
297
|
+
):
|
298
|
+
"""Creates a new hotkeypub under this wallet."""
|
299
|
+
try:
|
300
|
+
new_hotkeypub = wallet.regenerate_hotkeypub(
|
301
|
+
ss58_address=ss58_address,
|
302
|
+
public_key=public_key_hex,
|
303
|
+
overwrite=overwrite,
|
304
|
+
)
|
305
|
+
if isinstance(new_hotkeypub, Wallet):
|
306
|
+
console.print(
|
307
|
+
"\n✅ [dark_sea_green]Regenerated coldkeypub successfully!\n",
|
308
|
+
f"[dark_sea_green]Wallet name: ({new_hotkeypub.name}), path: ({new_hotkeypub.path}), "
|
309
|
+
f"coldkey ss58: ({new_hotkeypub.coldkeypub.ss58_address})",
|
310
|
+
)
|
311
|
+
if json_output:
|
312
|
+
json_console.print(
|
313
|
+
json.dumps(
|
314
|
+
{
|
315
|
+
"success": True,
|
316
|
+
"data": {
|
317
|
+
"name": new_hotkeypub.name,
|
318
|
+
"path": new_hotkeypub.path,
|
319
|
+
"hotkey": new_hotkeypub.hotkey_str,
|
320
|
+
"hotkey_ss58": new_hotkeypub.hotkeypub.ss58_address,
|
321
|
+
"coldkey_ss58": new_hotkeypub.coldkeypub.ss58_address,
|
322
|
+
},
|
323
|
+
"error": "",
|
324
|
+
}
|
325
|
+
)
|
326
|
+
)
|
327
|
+
except KeyFileError:
|
328
|
+
print_error("KeyFileError: File is not writable")
|
329
|
+
if json_output:
|
330
|
+
json_console.print(
|
331
|
+
'{"success": false, "error": "Keyfile is not writable", "data": null}'
|
332
|
+
)
|
333
|
+
|
334
|
+
|
290
335
|
async def new_hotkey(
|
291
336
|
wallet: Wallet,
|
292
337
|
n_words: int,
|
@@ -323,7 +368,7 @@ async def new_hotkey(
|
|
323
368
|
"name": wallet.name,
|
324
369
|
"path": wallet.path,
|
325
370
|
"hotkey": wallet.hotkey_str,
|
326
|
-
"hotkey_ss58": wallet
|
371
|
+
"hotkey_ss58": get_hotkey_pub_ss58(wallet),
|
327
372
|
"coldkey_ss58": wallet.coldkeypub.ss58_address,
|
328
373
|
},
|
329
374
|
"error": "",
|
@@ -402,19 +447,26 @@ async def wallet_create(
|
|
402
447
|
json_output: bool = False,
|
403
448
|
):
|
404
449
|
"""Creates a new wallet."""
|
405
|
-
output_dict
|
450
|
+
output_dict: dict[str, Optional[Union[bool, str, dict]]] = {
|
451
|
+
"success": False,
|
452
|
+
"error": "",
|
453
|
+
"data": None,
|
454
|
+
}
|
455
|
+
|
406
456
|
if uri:
|
407
457
|
try:
|
408
458
|
keypair = Keypair.create_from_uri(uri)
|
409
|
-
wallet.set_coldkey(keypair=keypair, encrypt=False, overwrite=
|
410
|
-
wallet.set_coldkeypub(keypair=keypair, encrypt=False, overwrite=
|
411
|
-
wallet.set_hotkey(keypair=keypair, encrypt=False, overwrite=
|
459
|
+
wallet.set_coldkey(keypair=keypair, encrypt=False, overwrite=overwrite)
|
460
|
+
wallet.set_coldkeypub(keypair=keypair, encrypt=False, overwrite=overwrite)
|
461
|
+
wallet.set_hotkey(keypair=keypair, encrypt=False, overwrite=overwrite)
|
462
|
+
wallet.set_hotkeypub(keypair=keypair, encrypt=False, overwrite=overwrite)
|
463
|
+
wallet.set_coldkeypub(keypair=keypair, encrypt=False, overwrite=overwrite)
|
412
464
|
output_dict["success"] = True
|
413
465
|
output_dict["data"] = {
|
414
466
|
"name": wallet.name,
|
415
467
|
"path": wallet.path,
|
416
468
|
"hotkey": wallet.hotkey_str,
|
417
|
-
"hotkey_ss58": wallet.
|
469
|
+
"hotkey_ss58": wallet.hotkeypub.ss58_address,
|
418
470
|
"coldkey_ss58": wallet.coldkeypub.ss58_address,
|
419
471
|
}
|
420
472
|
except Exception as e:
|
@@ -455,7 +507,7 @@ async def wallet_create(
|
|
455
507
|
"name": wallet.name,
|
456
508
|
"path": wallet.path,
|
457
509
|
"hotkey": wallet.hotkey_str,
|
458
|
-
"hotkey_ss58": wallet.
|
510
|
+
"hotkey_ss58": wallet.hotkeypub.ss58_address,
|
459
511
|
}
|
460
512
|
except KeyFileError as error:
|
461
513
|
err = str(error)
|
@@ -794,13 +846,14 @@ async def wallet_list(wallet_path: str, json_output: bool):
|
|
794
846
|
data = f"[bold red]Hotkey[/bold red][green] {hkey}[/green] (?)"
|
795
847
|
hk_data = {"name": hkey.name, "ss58_address": "?"}
|
796
848
|
if hkey:
|
849
|
+
hkey_ss58 = get_hotkey_pub_ss58(hkey)
|
797
850
|
try:
|
798
851
|
data = (
|
799
852
|
f"[bold red]Hotkey[/bold red] [green]{hkey.hotkey_str}[/green] "
|
800
|
-
f"ss58_address [green]{
|
853
|
+
f"ss58_address [green]{hkey_ss58}[/green]\n"
|
801
854
|
)
|
802
855
|
hk_data["name"] = hkey.hotkey_str
|
803
|
-
hk_data["ss58_address"] =
|
856
|
+
hk_data["ss58_address"] = hkey_ss58
|
804
857
|
except UnicodeDecodeError:
|
805
858
|
pass
|
806
859
|
wallet_tree.add(data)
|
@@ -1253,7 +1306,7 @@ def _get_hotkeys(
|
|
1253
1306
|
|
1254
1307
|
def is_hotkey_matched(wallet: Wallet, item: str) -> bool:
|
1255
1308
|
if is_valid_ss58_address(item):
|
1256
|
-
return wallet
|
1309
|
+
return get_hotkey_pub_ss58(wallet) == item
|
1257
1310
|
else:
|
1258
1311
|
return wallet.hotkey_str == item
|
1259
1312
|
|
@@ -1285,9 +1338,10 @@ def _get_key_address(all_hotkeys: list[Wallet]) -> tuple[list[str], dict[str, Wa
|
|
1285
1338
|
hotkey_coldkey_to_hotkey_wallet = {}
|
1286
1339
|
for hotkey_wallet in all_hotkeys:
|
1287
1340
|
if hotkey_wallet.coldkeypub:
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1341
|
+
hotkey_ss58 = get_hotkey_pub_ss58(hotkey_wallet)
|
1342
|
+
if hotkey_ss58 not in hotkey_coldkey_to_hotkey_wallet:
|
1343
|
+
hotkey_coldkey_to_hotkey_wallet[hotkey_ss58] = {}
|
1344
|
+
hotkey_coldkey_to_hotkey_wallet[hotkey_ss58][
|
1291
1345
|
hotkey_wallet.coldkeypub.ss58_address
|
1292
1346
|
] = hotkey_wallet
|
1293
1347
|
else:
|
@@ -1471,7 +1525,7 @@ async def inspect(
|
|
1471
1525
|
if hotkey_names := [
|
1472
1526
|
w.hotkey_str
|
1473
1527
|
for w in hotkeys
|
1474
|
-
if w
|
1528
|
+
if get_hotkey_pub_ss58(w) == n.hotkey
|
1475
1529
|
]:
|
1476
1530
|
hotkey_name = f"{hotkey_names[0]}-"
|
1477
1531
|
yield [""] * 5 + [
|
@@ -15,6 +15,7 @@ from bittensor_cli.src.bittensor.utils import (
|
|
15
15
|
console,
|
16
16
|
format_error_message,
|
17
17
|
json_console,
|
18
|
+
get_hotkey_pub_ss58,
|
18
19
|
)
|
19
20
|
from bittensor_cli.src.bittensor.extrinsics.root import (
|
20
21
|
convert_weights_and_uids_for_emit,
|
@@ -128,7 +129,7 @@ class SetWeightsExtrinsic:
|
|
128
129
|
|
129
130
|
# Generate the hash of the weights
|
130
131
|
commit_hash = generate_weight_hash(
|
131
|
-
address=self.wallet
|
132
|
+
address=get_hotkey_pub_ss58(self.wallet),
|
132
133
|
netuid=self.netuid,
|
133
134
|
uids=uids,
|
134
135
|
values=weights,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: bittensor-cli
|
3
|
-
Version: 9.
|
3
|
+
Version: 9.10.1
|
4
4
|
Summary: Bittensor CLI
|
5
5
|
Author: bittensor.com
|
6
6
|
Project-URL: homepage, https://github.com/opentensor/btcli
|
@@ -11,7 +11,6 @@ Requires-Dist: wheel
|
|
11
11
|
Requires-Dist: async-substrate-interface>=1.4.2
|
12
12
|
Requires-Dist: aiohttp~=3.10.2
|
13
13
|
Requires-Dist: backoff~=2.2.1
|
14
|
-
Requires-Dist: click<8.2.0
|
15
14
|
Requires-Dist: GitPython>=3.0.0
|
16
15
|
Requires-Dist: netaddr~=1.3.0
|
17
16
|
Requires-Dist: numpy<3.0.0,>=2.0.1
|
@@ -20,8 +19,9 @@ Requires-Dist: pycryptodome<4.0.0,>=3.0.0
|
|
20
19
|
Requires-Dist: PyYAML~=6.0.1
|
21
20
|
Requires-Dist: rich<15.0,>=13.7
|
22
21
|
Requires-Dist: scalecodec==1.2.11
|
23
|
-
Requires-Dist: typer
|
24
|
-
Requires-Dist: bittensor-wallet>=
|
22
|
+
Requires-Dist: typer>=0.16
|
23
|
+
Requires-Dist: bittensor-wallet>=4.0.0
|
24
|
+
Requires-Dist: packaging
|
25
25
|
Requires-Dist: plotille>=5.0.0
|
26
26
|
Requires-Dist: plotly>=6.0.0
|
27
27
|
Provides-Extra: cuda
|
@@ -71,7 +71,15 @@ Installation steps are described below. For a full documentation on how to use `
|
|
71
71
|
|
72
72
|
## Install on macOS and Linux
|
73
73
|
|
74
|
-
You can install `btcli` on your local machine directly from source, PyPI, or Homebrew.
|
74
|
+
You can install `btcli` on your local machine directly from source, PyPI, or Homebrew.
|
75
|
+
**Make sure you verify your installation after you install**.
|
76
|
+
|
77
|
+
### For macOS users
|
78
|
+
Note that the macOS preinstalled CPython installation is compiled with LibreSSL instead of OpenSSL. There are a number
|
79
|
+
of issues with LibreSSL, and as such is not fully supported by the libraries used by btcli. Thus we highly recommend, if
|
80
|
+
you are using a Mac, to first install Python from [Homebrew](https://brew.sh/). Additionally, the Rust FFI bindings
|
81
|
+
[if installing from precompiled wheels (default)] require the Homebrew-installed OpenSSL pacakge. If you choose to use
|
82
|
+
the preinstalled Python version from macOS, things may not work completely.
|
75
83
|
|
76
84
|
|
77
85
|
### Install from [PyPI](https://pypi.org/project/bittensor/)
|