bittensor-cli 9.8.7__py3-none-any.whl → 9.10.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.
- bittensor_cli/cli.py +229 -55
- 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 +35 -18
- bittensor_cli/src/bittensor/subtensor_interface.py +34 -15
- bittensor_cli/src/bittensor/utils.py +13 -1
- bittensor_cli/src/commands/stake/add.py +86 -30
- bittensor_cli/src/commands/stake/children_hotkeys.py +16 -14
- bittensor_cli/src/commands/stake/move.py +106 -84
- bittensor_cli/src/commands/stake/remove.py +119 -11
- bittensor_cli/src/commands/subnets/price.py +126 -30
- bittensor_cli/src/commands/subnets/subnets.py +3 -2
- bittensor_cli/src/commands/sudo.py +13 -11
- bittensor_cli/src/commands/wallets.py +159 -19
- bittensor_cli/src/commands/weights.py +2 -1
- {bittensor_cli-9.8.7.dist-info → bittensor_cli-9.10.0.dist-info}/METADATA +14 -6
- {bittensor_cli-9.8.7.dist-info → bittensor_cli-9.10.0.dist-info}/RECORD +20 -20
- {bittensor_cli-9.8.7.dist-info → bittensor_cli-9.10.0.dist-info}/WHEEL +0 -0
- {bittensor_cli-9.8.7.dist-info → bittensor_cli-9.10.0.dist-info}/entry_points.txt +0 -0
- {bittensor_cli-9.8.7.dist-info → bittensor_cli-9.10.0.dist-info}/top_level.txt +0 -0
@@ -39,6 +39,7 @@ from bittensor_cli.src.bittensor.utils import (
|
|
39
39
|
print_error,
|
40
40
|
unlock_key,
|
41
41
|
hex_to_bytes,
|
42
|
+
get_hotkey_pub_ss58,
|
42
43
|
)
|
43
44
|
|
44
45
|
if typing.TYPE_CHECKING:
|
@@ -490,7 +491,7 @@ async def register_extrinsic(
|
|
490
491
|
|
491
492
|
async def get_neuron_for_pubkey_and_subnet():
|
492
493
|
uid = await subtensor.query(
|
493
|
-
"SubtensorModule", "Uids", [netuid, wallet
|
494
|
+
"SubtensorModule", "Uids", [netuid, get_hotkey_pub_ss58(wallet)]
|
494
495
|
)
|
495
496
|
if uid is None:
|
496
497
|
return NeuronInfo.get_null_neuron()
|
@@ -525,7 +526,7 @@ async def register_extrinsic(
|
|
525
526
|
if not Confirm.ask(
|
526
527
|
f"Continue Registration?\n"
|
527
528
|
f" hotkey [{COLOR_PALETTE.G.HK}]({wallet.hotkey_str})[/{COLOR_PALETTE.G.HK}]:"
|
528
|
-
f"\t[{COLOR_PALETTE.G.HK}]{wallet
|
529
|
+
f"\t[{COLOR_PALETTE.G.HK}]{get_hotkey_pub_ss58(wallet)}[/{COLOR_PALETTE.G.HK}]\n"
|
529
530
|
f" coldkey [{COLOR_PALETTE.G.CK}]({wallet.name})[/{COLOR_PALETTE.G.CK}]:"
|
530
531
|
f"\t[{COLOR_PALETTE.G.CK}]{wallet.coldkeypub.ss58_address}[/{COLOR_PALETTE.G.CK}]\n"
|
531
532
|
f" network:\t\t[{COLOR_PALETTE.G.LINKS}]{subtensor.network}[/{COLOR_PALETTE.G.LINKS}]\n"
|
@@ -577,7 +578,7 @@ async def register_extrinsic(
|
|
577
578
|
if not pow_result:
|
578
579
|
# might be registered already on this subnet
|
579
580
|
is_registered = await is_hotkey_registered(
|
580
|
-
subtensor, netuid=netuid, hotkey_ss58=wallet
|
581
|
+
subtensor, netuid=netuid, hotkey_ss58=get_hotkey_pub_ss58(wallet)
|
581
582
|
)
|
582
583
|
if is_registered:
|
583
584
|
err_console.print(
|
@@ -598,7 +599,7 @@ async def register_extrinsic(
|
|
598
599
|
"block_number": pow_result.block_number,
|
599
600
|
"nonce": pow_result.nonce,
|
600
601
|
"work": [int(byte_) for byte_ in pow_result.seal],
|
601
|
-
"hotkey": wallet
|
602
|
+
"hotkey": get_hotkey_pub_ss58(wallet),
|
602
603
|
"coldkey": wallet.coldkeypub.ss58_address,
|
603
604
|
},
|
604
605
|
)
|
@@ -639,7 +640,7 @@ async def register_extrinsic(
|
|
639
640
|
is_registered = await is_hotkey_registered(
|
640
641
|
subtensor,
|
641
642
|
netuid=netuid,
|
642
|
-
hotkey_ss58=wallet
|
643
|
+
hotkey_ss58=get_hotkey_pub_ss58(wallet),
|
643
644
|
)
|
644
645
|
if is_registered:
|
645
646
|
console.print(
|
@@ -704,7 +705,7 @@ async def burned_register_extrinsic(
|
|
704
705
|
spinner="aesthetic",
|
705
706
|
) as status:
|
706
707
|
my_uid = await subtensor.query(
|
707
|
-
"SubtensorModule", "Uids", [netuid, wallet
|
708
|
+
"SubtensorModule", "Uids", [netuid, get_hotkey_pub_ss58(wallet)]
|
708
709
|
)
|
709
710
|
block_hash = await subtensor.substrate.get_chain_head()
|
710
711
|
|
@@ -751,7 +752,7 @@ async def burned_register_extrinsic(
|
|
751
752
|
call_function="burned_register",
|
752
753
|
call_params={
|
753
754
|
"netuid": netuid,
|
754
|
-
"hotkey": wallet
|
755
|
+
"hotkey": get_hotkey_pub_ss58(wallet),
|
755
756
|
},
|
756
757
|
)
|
757
758
|
success, err_msg = await subtensor.sign_and_send_extrinsic(
|
@@ -773,10 +774,10 @@ async def burned_register_extrinsic(
|
|
773
774
|
reuse_block=False,
|
774
775
|
),
|
775
776
|
subtensor.get_netuids_for_hotkey(
|
776
|
-
wallet
|
777
|
+
get_hotkey_pub_ss58(wallet), block_hash=block_hash
|
777
778
|
),
|
778
779
|
subtensor.query(
|
779
|
-
"SubtensorModule", "Uids", [netuid, wallet
|
780
|
+
"SubtensorModule", "Uids", [netuid, get_hotkey_pub_ss58(wallet)]
|
780
781
|
),
|
781
782
|
)
|
782
783
|
|
@@ -1146,7 +1147,7 @@ async def _block_solver(
|
|
1146
1147
|
|
1147
1148
|
timeout = 0.15 if cuda else 0.15
|
1148
1149
|
while netuid == -1 or not await is_hotkey_registered(
|
1149
|
-
subtensor, netuid, wallet
|
1150
|
+
subtensor, netuid, get_hotkey_pub_ss58(wallet)
|
1150
1151
|
):
|
1151
1152
|
# Wait until a solver finds a solution
|
1152
1153
|
try:
|
@@ -1755,37 +1756,39 @@ async def swap_hotkey_extrinsic(
|
|
1755
1756
|
:return: Success
|
1756
1757
|
"""
|
1757
1758
|
block_hash = await subtensor.substrate.get_chain_head()
|
1759
|
+
hk_ss58 = get_hotkey_pub_ss58(wallet)
|
1758
1760
|
netuids_registered = await subtensor.get_netuids_for_hotkey(
|
1759
|
-
|
1761
|
+
hk_ss58, block_hash=block_hash
|
1760
1762
|
)
|
1761
1763
|
netuids_registered_new_hotkey = await subtensor.get_netuids_for_hotkey(
|
1762
|
-
|
1764
|
+
hk_ss58, block_hash=block_hash
|
1763
1765
|
)
|
1764
1766
|
|
1765
1767
|
if netuid is not None and netuid not in netuids_registered:
|
1766
1768
|
err_console.print(
|
1767
|
-
f":cross_mark: [red]Failed[/red]: Original hotkey {
|
1769
|
+
f":cross_mark: [red]Failed[/red]: Original hotkey {hk_ss58} is not registered on subnet {netuid}"
|
1768
1770
|
)
|
1769
1771
|
return False
|
1770
1772
|
|
1771
1773
|
elif not len(netuids_registered) > 0:
|
1772
1774
|
err_console.print(
|
1773
|
-
f"Original hotkey [dark_orange]{
|
1775
|
+
f"Original hotkey [dark_orange]{hk_ss58}[/dark_orange] is not registered on any subnet. "
|
1774
1776
|
f"Please register and try again"
|
1775
1777
|
)
|
1776
1778
|
return False
|
1777
1779
|
|
1780
|
+
new_hk_ss58 = get_hotkey_pub_ss58(new_wallet)
|
1778
1781
|
if netuid is not None:
|
1779
1782
|
if netuid in netuids_registered_new_hotkey:
|
1780
1783
|
err_console.print(
|
1781
|
-
f":cross_mark: [red]Failed[/red]: New hotkey {
|
1784
|
+
f":cross_mark: [red]Failed[/red]: New hotkey {new_hk_ss58} "
|
1782
1785
|
f"is already registered on subnet {netuid}"
|
1783
1786
|
)
|
1784
1787
|
return False
|
1785
1788
|
else:
|
1786
1789
|
if len(netuids_registered_new_hotkey) > 0:
|
1787
1790
|
err_console.print(
|
1788
|
-
f":cross_mark: [red]Failed[/red]: New hotkey {
|
1791
|
+
f":cross_mark: [red]Failed[/red]: New hotkey {new_hk_ss58} "
|
1789
1792
|
f"is already registered on subnet(s) {netuids_registered_new_hotkey}"
|
1790
1793
|
)
|
1791
1794
|
return False
|
@@ -1798,28 +1801,28 @@ async def swap_hotkey_extrinsic(
|
|
1798
1801
|
if netuid is not None:
|
1799
1802
|
confirm_message = (
|
1800
1803
|
f"Do you want to swap [dark_orange]{wallet.name}[/dark_orange] hotkey \n\t"
|
1801
|
-
f"[dark_orange]{
|
1802
|
-
f"[dark_orange]{
|
1804
|
+
f"[dark_orange]{hk_ss58} ({wallet.hotkey_str})[/dark_orange] with hotkey \n\t"
|
1805
|
+
f"[dark_orange]{new_hk_ss58} ({new_wallet.hotkey_str})[/dark_orange] on subnet {netuid}\n"
|
1803
1806
|
"This operation will cost [bold cyan]1 TAO (recycled)[/bold cyan]"
|
1804
1807
|
)
|
1805
1808
|
else:
|
1806
1809
|
confirm_message = (
|
1807
1810
|
f"Do you want to swap [dark_orange]{wallet.name}[/dark_orange] hotkey \n\t"
|
1808
|
-
f"[dark_orange]{
|
1809
|
-
f"[dark_orange]{
|
1811
|
+
f"[dark_orange]{hk_ss58} ({wallet.hotkey_str})[/dark_orange] with hotkey \n\t"
|
1812
|
+
f"[dark_orange]{new_hk_ss58} ({new_wallet.hotkey_str})[/dark_orange] on all subnets\n"
|
1810
1813
|
"This operation will cost [bold cyan]1 TAO (recycled)[/bold cyan]"
|
1811
1814
|
)
|
1812
1815
|
|
1813
1816
|
if not Confirm.ask(confirm_message):
|
1814
1817
|
return False
|
1815
1818
|
print_verbose(
|
1816
|
-
f"Swapping {wallet.name}'s hotkey ({
|
1817
|
-
f"{new_wallet.name}'s hotkey ({
|
1819
|
+
f"Swapping {wallet.name}'s hotkey ({hk_ss58} - {wallet.hotkey_str}) with "
|
1820
|
+
f"{new_wallet.name}'s hotkey ({new_hk_ss58} - {new_wallet.hotkey_str})"
|
1818
1821
|
)
|
1819
1822
|
with console.status(":satellite: Swapping hotkeys...", spinner="aesthetic"):
|
1820
1823
|
call_params = {
|
1821
|
-
"hotkey":
|
1822
|
-
"new_hotkey":
|
1824
|
+
"hotkey": hk_ss58,
|
1825
|
+
"new_hotkey": new_hk_ss58,
|
1823
1826
|
"netuid": netuid,
|
1824
1827
|
}
|
1825
1828
|
|
@@ -1832,7 +1835,8 @@ async def swap_hotkey_extrinsic(
|
|
1832
1835
|
|
1833
1836
|
if success:
|
1834
1837
|
console.print(
|
1835
|
-
f"Hotkey {
|
1838
|
+
f"Hotkey {hk_ss58} ({wallet.hotkey_str}) swapped for new hotkey: "
|
1839
|
+
f"{new_hk_ss58} ({new_wallet.hotkey_str})"
|
1836
1840
|
)
|
1837
1841
|
return True
|
1838
1842
|
else:
|
@@ -37,6 +37,7 @@ from bittensor_cli.src.bittensor.utils import (
|
|
37
37
|
print_verbose,
|
38
38
|
format_error_message,
|
39
39
|
unlock_key,
|
40
|
+
get_hotkey_pub_ss58,
|
40
41
|
)
|
41
42
|
|
42
43
|
if TYPE_CHECKING:
|
@@ -310,7 +311,7 @@ async def root_register_extrinsic(
|
|
310
311
|
|
311
312
|
print_verbose(f"Checking if hotkey ({wallet.hotkey_str}) is registered on root")
|
312
313
|
is_registered = await is_hotkey_registered(
|
313
|
-
subtensor, netuid=0, hotkey_ss58=wallet
|
314
|
+
subtensor, netuid=0, hotkey_ss58=get_hotkey_pub_ss58(wallet)
|
314
315
|
)
|
315
316
|
if is_registered:
|
316
317
|
console.print(
|
@@ -322,7 +323,7 @@ async def root_register_extrinsic(
|
|
322
323
|
call = await subtensor.substrate.compose_call(
|
323
324
|
call_module="SubtensorModule",
|
324
325
|
call_function="root_register",
|
325
|
-
call_params={"hotkey": wallet
|
326
|
+
call_params={"hotkey": get_hotkey_pub_ss58(wallet)},
|
326
327
|
)
|
327
328
|
success, err_msg = await subtensor.sign_and_send_extrinsic(
|
328
329
|
call,
|
@@ -341,7 +342,7 @@ async def root_register_extrinsic(
|
|
341
342
|
uid = await subtensor.query(
|
342
343
|
module="SubtensorModule",
|
343
344
|
storage_function="Uids",
|
344
|
-
params=[0, wallet
|
345
|
+
params=[0, get_hotkey_pub_ss58(wallet)],
|
345
346
|
)
|
346
347
|
if uid is not None:
|
347
348
|
console.print(
|
@@ -391,7 +392,7 @@ async def set_root_weights_extrinsic(
|
|
391
392
|
"weights": weight_vals,
|
392
393
|
"netuid": 0,
|
393
394
|
"version_key": version_key,
|
394
|
-
"hotkey": wallet
|
395
|
+
"hotkey": get_hotkey_pub_ss58(wallet),
|
395
396
|
},
|
396
397
|
)
|
397
398
|
# Period dictates how long the extrinsic will stay as part of waiting pool
|
@@ -415,7 +416,7 @@ async def set_root_weights_extrinsic(
|
|
415
416
|
return False, await response.error_message
|
416
417
|
|
417
418
|
my_uid = await subtensor.query(
|
418
|
-
"SubtensorModule", "Uids", [0, wallet
|
419
|
+
"SubtensorModule", "Uids", [0, get_hotkey_pub_ss58(wallet)]
|
419
420
|
)
|
420
421
|
|
421
422
|
if my_uid is None:
|
@@ -26,9 +26,9 @@ async def transfer_extrinsic(
|
|
26
26
|
amount: Balance,
|
27
27
|
era: int = 3,
|
28
28
|
transfer_all: bool = False,
|
29
|
+
allow_death: bool = False,
|
29
30
|
wait_for_inclusion: bool = True,
|
30
31
|
wait_for_finalization: bool = False,
|
31
|
-
keep_alive: bool = True,
|
32
32
|
prompt: bool = False,
|
33
33
|
) -> bool:
|
34
34
|
"""Transfers funds from this wallet to the destination public key address.
|
@@ -39,11 +39,11 @@ async def transfer_extrinsic(
|
|
39
39
|
:param amount: Amount to stake as Bittensor balance.
|
40
40
|
:param era: Length (in blocks) for which the transaction should be valid.
|
41
41
|
:param transfer_all: Whether to transfer all funds from this wallet to the destination address.
|
42
|
+
:param allow_death: Whether to allow for falling below the existential deposit when performing this transfer.
|
42
43
|
:param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`,
|
43
44
|
or returns `False` if the extrinsic fails to enter the block within the timeout.
|
44
45
|
:param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning
|
45
46
|
`True`, or returns `False` if the extrinsic fails to be finalized within the timeout.
|
46
|
-
:param keep_alive: If set, keeps the account alive by keeping the balance above the existential deposit.
|
47
47
|
:param prompt: If `True`, the call waits for confirmation from the user before proceeding.
|
48
48
|
:return: success: Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for
|
49
49
|
finalization / inclusion, the response is `True`, regardless of its inclusion.
|
@@ -57,8 +57,8 @@ async def transfer_extrinsic(
|
|
57
57
|
"""
|
58
58
|
call = await subtensor.substrate.compose_call(
|
59
59
|
call_module="Balances",
|
60
|
-
call_function=
|
61
|
-
call_params=
|
60
|
+
call_function=call_function,
|
61
|
+
call_params=call_params,
|
62
62
|
)
|
63
63
|
|
64
64
|
try:
|
@@ -82,8 +82,8 @@ async def transfer_extrinsic(
|
|
82
82
|
"""
|
83
83
|
call = await subtensor.substrate.compose_call(
|
84
84
|
call_module="Balances",
|
85
|
-
call_function=
|
86
|
-
call_params=
|
85
|
+
call_function=call_function,
|
86
|
+
call_params=call_params,
|
87
87
|
)
|
88
88
|
extrinsic = await subtensor.substrate.create_signed_extrinsic(
|
89
89
|
call=call, keypair=wallet.coldkey, era={"period": era}
|
@@ -115,6 +115,20 @@ async def transfer_extrinsic(
|
|
115
115
|
if not unlock_key(wallet).success:
|
116
116
|
return False
|
117
117
|
|
118
|
+
call_params = {"dest": destination}
|
119
|
+
if transfer_all:
|
120
|
+
call_function = "transfer_all"
|
121
|
+
if allow_death:
|
122
|
+
call_params["keep_alive"] = False
|
123
|
+
else:
|
124
|
+
call_params["keep_alive"] = True
|
125
|
+
else:
|
126
|
+
call_params["value"] = amount.rao
|
127
|
+
if allow_death:
|
128
|
+
call_function = "transfer_allow_death"
|
129
|
+
else:
|
130
|
+
call_function = "transfer_keep_alive"
|
131
|
+
|
118
132
|
# Check balance.
|
119
133
|
with console.status(
|
120
134
|
f":satellite: Checking balance and fees on chain [white]{subtensor.network}[/white]",
|
@@ -131,23 +145,26 @@ async def transfer_extrinsic(
|
|
131
145
|
)
|
132
146
|
fee = await get_transfer_fee()
|
133
147
|
|
134
|
-
if
|
135
|
-
# Check if the transfer should
|
148
|
+
if allow_death:
|
149
|
+
# Check if the transfer should keep alive the account
|
136
150
|
existential_deposit = Balance(0)
|
137
151
|
|
138
|
-
|
139
|
-
if transfer_all is True:
|
140
|
-
amount = account_balance - fee - existential_deposit
|
141
|
-
if amount < Balance(0):
|
142
|
-
print_error("Not enough balance to transfer")
|
143
|
-
return False
|
144
|
-
|
145
|
-
if account_balance < (amount + fee + existential_deposit):
|
152
|
+
if account_balance < (amount + fee + existential_deposit) and not allow_death:
|
146
153
|
err_console.print(
|
147
154
|
":cross_mark: [bold red]Not enough balance[/bold red]:\n\n"
|
148
155
|
f" balance: [bright_cyan]{account_balance}[/bright_cyan]\n"
|
149
156
|
f" amount: [bright_cyan]{amount}[/bright_cyan]\n"
|
150
|
-
f" for fee: [bright_cyan]{fee}[/bright_cyan]"
|
157
|
+
f" for fee: [bright_cyan]{fee}[/bright_cyan]\n"
|
158
|
+
f" would bring you under the existential deposit: [bright_cyan]{existential_deposit}[/bright_cyan].\n"
|
159
|
+
f"You can try again with `--allow-death`."
|
160
|
+
)
|
161
|
+
return False
|
162
|
+
elif account_balance < (amount + fee) and allow_death:
|
163
|
+
print_error(
|
164
|
+
":cross_mark: [bold red]Not enough balance[/bold red]:\n\n"
|
165
|
+
f" balance: [bright_red]{account_balance}[/bright_red]\n"
|
166
|
+
f" amount: [bright_red]{amount}[/bright_red]\n"
|
167
|
+
f" for fee: [bright_red]{fee}[/bright_red]"
|
151
168
|
)
|
152
169
|
return False
|
153
170
|
|
@@ -155,7 +172,7 @@ async def transfer_extrinsic(
|
|
155
172
|
if prompt:
|
156
173
|
if not Confirm.ask(
|
157
174
|
"Do you want to transfer:[bold white]\n"
|
158
|
-
f" amount: [bright_cyan]{amount}[/bright_cyan]\n"
|
175
|
+
f" amount: [bright_cyan]{amount if not transfer_all else account_balance}[/bright_cyan]\n"
|
159
176
|
f" from: [light_goldenrod2]{wallet.name}[/light_goldenrod2] : "
|
160
177
|
f"[bright_magenta]{wallet.coldkey.ss58_address}\n[/bright_magenta]"
|
161
178
|
f" to: [bright_magenta]{destination}[/bright_magenta]\n for fee: [bright_cyan]{fee}[/bright_cyan]"
|
@@ -3,7 +3,9 @@ import os
|
|
3
3
|
from typing import Optional, Any, Union, TypedDict, Iterable
|
4
4
|
|
5
5
|
import aiohttp
|
6
|
+
from async_substrate_interface.utils.storage import StorageKey
|
6
7
|
from bittensor_wallet import Wallet
|
8
|
+
from bittensor_wallet.bittensor_wallet import Keypair
|
7
9
|
from bittensor_wallet.utils import SS58_FORMAT
|
8
10
|
from scalecodec import GenericCall
|
9
11
|
from async_substrate_interface.errors import SubstrateRequestException
|
@@ -37,6 +39,7 @@ from bittensor_cli.src.bittensor.utils import (
|
|
37
39
|
validate_chain_endpoint,
|
38
40
|
u16_normalized_float,
|
39
41
|
U16_MAX,
|
42
|
+
get_hotkey_pub_ss58,
|
40
43
|
)
|
41
44
|
|
42
45
|
SubstrateClass = (
|
@@ -664,7 +667,7 @@ class SubtensorInterface:
|
|
664
667
|
for sublist in await asyncio.gather(
|
665
668
|
*[
|
666
669
|
self.get_netuids_for_hotkey(
|
667
|
-
wallet
|
670
|
+
get_hotkey_pub_ss58(wallet),
|
668
671
|
reuse_block=reuse_block,
|
669
672
|
block_hash=block_hash,
|
670
673
|
)
|
@@ -881,9 +884,10 @@ class SubtensorInterface:
|
|
881
884
|
storage_function="IdentitiesV2",
|
882
885
|
block_hash=block_hash,
|
883
886
|
reuse_block_hash=reuse_block,
|
887
|
+
fully_exhaust=True,
|
884
888
|
)
|
885
889
|
all_identities = {}
|
886
|
-
|
890
|
+
for ss58_address, identity in identities.records:
|
887
891
|
all_identities[decode_account_id(ss58_address[0])] = decode_hex_identity(
|
888
892
|
identity.value
|
889
893
|
)
|
@@ -939,22 +943,22 @@ class SubtensorInterface:
|
|
939
943
|
:param reuse_block: Whether to reuse the last-used blockchain block hash.
|
940
944
|
:return: Dict with 'coldkeys' and 'hotkeys' as keys.
|
941
945
|
"""
|
942
|
-
|
943
|
-
|
946
|
+
if block_hash is None:
|
947
|
+
block_hash = await self.substrate.get_chain_head()
|
948
|
+
coldkey_identities = await self.query_all_identities(block_hash=block_hash)
|
944
949
|
identities = {"coldkeys": {}, "hotkeys": {}}
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
reuse_block_hash=reuse_block,
|
953
|
-
)
|
950
|
+
sks = [
|
951
|
+
await self.substrate.create_storage_key(
|
952
|
+
"SubtensorModule", "OwnedHotkeys", [ck], block_hash=block_hash
|
953
|
+
)
|
954
|
+
for ck in coldkey_identities.keys()
|
955
|
+
]
|
956
|
+
query = await self.substrate.query_multi(sks, block_hash=block_hash)
|
954
957
|
|
955
|
-
|
958
|
+
storage_key: StorageKey
|
959
|
+
for storage_key, hotkeys in query:
|
960
|
+
coldkey_ss58 = storage_key.params[0]
|
956
961
|
coldkey_identity = coldkey_identities.get(coldkey_ss58)
|
957
|
-
hotkeys = [decode_account_id(hotkey[0]) for hotkey in hotkeys or []]
|
958
962
|
|
959
963
|
identities["coldkeys"][coldkey_ss58] = {
|
960
964
|
"identity": coldkey_identity,
|
@@ -1455,6 +1459,8 @@ class SubtensorInterface:
|
|
1455
1459
|
),
|
1456
1460
|
self.get_subnet_price(netuid=netuid, block_hash=block_hash),
|
1457
1461
|
)
|
1462
|
+
if not result:
|
1463
|
+
raise ValueError(f"Subnet {netuid} not found")
|
1458
1464
|
subnet_ = DynamicInfo.from_any(result)
|
1459
1465
|
subnet_.price = price
|
1460
1466
|
return subnet_
|
@@ -1484,6 +1490,19 @@ class SubtensorInterface:
|
|
1484
1490
|
|
1485
1491
|
return [decode_account_id(hotkey[0]) for hotkey in owned_hotkeys or []]
|
1486
1492
|
|
1493
|
+
async def get_extrinsic_fee(self, call: GenericCall, keypair: Keypair) -> Balance:
|
1494
|
+
"""
|
1495
|
+
Determines the fee for the extrinsic call.
|
1496
|
+
Args:
|
1497
|
+
call: Created extrinsic call
|
1498
|
+
keypair: The keypair that would sign the extrinsic (usually you would just want to use the *pub for this)
|
1499
|
+
|
1500
|
+
Returns:
|
1501
|
+
Balance object representing the fee for this extrinsic.
|
1502
|
+
"""
|
1503
|
+
fee_dict = await self.substrate.get_payment_info(call, keypair)
|
1504
|
+
return Balance.from_rao(fee_dict["partial_fee"])
|
1505
|
+
|
1487
1506
|
async def get_stake_fee(
|
1488
1507
|
self,
|
1489
1508
|
origin_hotkey_ss58: Optional[str],
|
@@ -275,7 +275,7 @@ def get_hotkey_wallets_for_wallet(
|
|
275
275
|
(exists := hotkey_for_name.hotkey_file.exists_on_device())
|
276
276
|
and not hotkey_for_name.hotkey_file.is_encrypted()
|
277
277
|
# and hotkey_for_name.coldkeypub.ss58_address
|
278
|
-
and hotkey_for_name
|
278
|
+
and get_hotkey_pub_ss58(hotkey_for_name)
|
279
279
|
):
|
280
280
|
hotkey_wallets.append(hotkey_for_name)
|
281
281
|
elif (
|
@@ -1431,3 +1431,15 @@ def blocks_to_duration(blocks: int) -> str:
|
|
1431
1431
|
results.append(f"{unit_count}{unit}")
|
1432
1432
|
# Return only the first two non-zero units
|
1433
1433
|
return " ".join(results[:2]) or "0s"
|
1434
|
+
|
1435
|
+
|
1436
|
+
def get_hotkey_pub_ss58(wallet: Wallet) -> str:
|
1437
|
+
"""
|
1438
|
+
Helper fn to retrieve the hotkeypub ss58 of a wallet that may have been created before
|
1439
|
+
bt-wallet 3.1.1 and thus not have a wallet hotkeypub. In this case, it will return the hotkey
|
1440
|
+
SS58.
|
1441
|
+
"""
|
1442
|
+
try:
|
1443
|
+
return wallet.hotkeypub.ss58_address
|
1444
|
+
except KeyFileError:
|
1445
|
+
return wallet.hotkey.ss58_address
|
@@ -20,6 +20,7 @@ from bittensor_cli.src.bittensor.utils import (
|
|
20
20
|
print_verbose,
|
21
21
|
unlock_key,
|
22
22
|
json_console,
|
23
|
+
get_hotkey_pub_ss58,
|
23
24
|
)
|
24
25
|
from bittensor_wallet import Wallet
|
25
26
|
|
@@ -65,6 +66,45 @@ async def stake_add(
|
|
65
66
|
bool: True if stake operation is successful, False otherwise
|
66
67
|
"""
|
67
68
|
|
69
|
+
async def get_stake_extrinsic_fee(
|
70
|
+
netuid_: int,
|
71
|
+
amount_: Balance,
|
72
|
+
staking_address_: str,
|
73
|
+
safe_staking_: bool,
|
74
|
+
price_limit: Optional[Balance] = None,
|
75
|
+
):
|
76
|
+
"""
|
77
|
+
Quick method to get the extrinsic fee for adding stake depending on the args supplied.
|
78
|
+
Args:
|
79
|
+
netuid_: The netuid where the stake will be added
|
80
|
+
amount_: the amount of stake to add
|
81
|
+
staking_address_: the hotkey ss58 to stake to
|
82
|
+
safe_staking_: whether to use safe staking
|
83
|
+
price_limit: rate with tolerance
|
84
|
+
|
85
|
+
Returns:
|
86
|
+
Balance object representing the extrinsic fee for adding stake.
|
87
|
+
"""
|
88
|
+
call_fn = "add_stake" if not safe_staking_ else "add_stake_limit"
|
89
|
+
call_params = {
|
90
|
+
"hotkey": staking_address_,
|
91
|
+
"netuid": netuid_,
|
92
|
+
"amount_staked": amount_.rao,
|
93
|
+
}
|
94
|
+
if safe_staking_:
|
95
|
+
call_params.update(
|
96
|
+
{
|
97
|
+
"limit_price": price_limit,
|
98
|
+
"allow_partial": allow_partial_stake,
|
99
|
+
}
|
100
|
+
)
|
101
|
+
call = await subtensor.substrate.compose_call(
|
102
|
+
call_module="SubtensorModule",
|
103
|
+
call_function=call_fn,
|
104
|
+
call_params=call_params,
|
105
|
+
)
|
106
|
+
return await subtensor.get_extrinsic_fee(call, wallet.coldkeypub)
|
107
|
+
|
68
108
|
async def safe_stake_extrinsic(
|
69
109
|
netuid_: int,
|
70
110
|
amount_: Balance,
|
@@ -87,7 +127,7 @@ async def stake_add(
|
|
87
127
|
"hotkey": hotkey_ss58_,
|
88
128
|
"netuid": netuid_,
|
89
129
|
"amount_staked": amount_.rao,
|
90
|
-
"limit_price": price_limit,
|
130
|
+
"limit_price": price_limit.rao,
|
91
131
|
"allow_partial": allow_partial_stake,
|
92
132
|
},
|
93
133
|
),
|
@@ -332,19 +372,6 @@ async def stake_add(
|
|
332
372
|
# Temporary workaround - calculations without slippage
|
333
373
|
current_price_float = float(subnet_info.price.tao)
|
334
374
|
rate = 1.0 / current_price_float
|
335
|
-
received_amount = rate * amount_to_stake
|
336
|
-
|
337
|
-
# Add rows for the table
|
338
|
-
base_row = [
|
339
|
-
str(netuid), # netuid
|
340
|
-
f"{hotkey[1]}", # hotkey
|
341
|
-
str(amount_to_stake), # amount
|
342
|
-
str(rate)
|
343
|
-
+ f" {Balance.get_unit(netuid)}/{Balance.get_unit(0)} ", # rate
|
344
|
-
str(received_amount.set_unit(netuid)), # received
|
345
|
-
str(stake_fee), # fee
|
346
|
-
# str(slippage_pct), # slippage
|
347
|
-
]
|
348
375
|
|
349
376
|
# If we are staking safe, add price tolerance
|
350
377
|
if safe_staking:
|
@@ -356,21 +383,45 @@ async def stake_add(
|
|
356
383
|
rate_with_tolerance = f"{_rate_with_tolerance:.4f}"
|
357
384
|
price_with_tolerance = Balance.from_tao(
|
358
385
|
price_with_tolerance
|
359
|
-
)
|
386
|
+
) # Actual price to pass to extrinsic
|
360
387
|
else:
|
361
388
|
rate_with_tolerance = "1"
|
362
389
|
price_with_tolerance = Balance.from_rao(1)
|
390
|
+
extrinsic_fee = await get_stake_extrinsic_fee(
|
391
|
+
netuid_=netuid,
|
392
|
+
amount_=amount_to_stake,
|
393
|
+
staking_address_=hotkey[1],
|
394
|
+
safe_staking_=safe_staking,
|
395
|
+
price_limit=price_with_tolerance,
|
396
|
+
)
|
363
397
|
prices_with_tolerance.append(price_with_tolerance)
|
364
|
-
|
365
|
-
|
366
|
-
[
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
398
|
+
row_extension = [
|
399
|
+
f"{rate_with_tolerance} {Balance.get_unit(netuid)}/{Balance.get_unit(0)} ",
|
400
|
+
f"[{'dark_sea_green3' if allow_partial_stake else 'red'}]"
|
401
|
+
# safe staking
|
402
|
+
f"{allow_partial_stake}[/{'dark_sea_green3' if allow_partial_stake else 'red'}]",
|
403
|
+
]
|
404
|
+
else:
|
405
|
+
extrinsic_fee = await get_stake_extrinsic_fee(
|
406
|
+
netuid_=netuid,
|
407
|
+
amount_=amount_to_stake,
|
408
|
+
staking_address_=hotkey[1],
|
409
|
+
safe_staking_=safe_staking,
|
372
410
|
)
|
373
|
-
|
411
|
+
row_extension = []
|
412
|
+
received_amount = rate * (amount_to_stake - stake_fee - extrinsic_fee)
|
413
|
+
# Add rows for the table
|
414
|
+
base_row = [
|
415
|
+
str(netuid), # netuid
|
416
|
+
f"{hotkey[1]}", # hotkey
|
417
|
+
str(amount_to_stake), # amount
|
418
|
+
str(rate)
|
419
|
+
+ f" {Balance.get_unit(netuid)}/{Balance.get_unit(0)} ", # rate
|
420
|
+
str(received_amount.set_unit(netuid)), # received
|
421
|
+
str(stake_fee), # fee
|
422
|
+
str(extrinsic_fee),
|
423
|
+
# str(slippage_pct), # slippage
|
424
|
+
] + row_extension
|
374
425
|
rows.append(tuple(base_row))
|
375
426
|
|
376
427
|
# Define and print stake table + slippage warning
|
@@ -502,7 +553,7 @@ def _get_hotkeys_to_stake_to(
|
|
502
553
|
# Stake to all hotkeys except excluded ones
|
503
554
|
all_hotkeys_: list[Wallet] = get_hotkey_wallets_for_wallet(wallet=wallet)
|
504
555
|
return [
|
505
|
-
(wallet.hotkey_str, wallet
|
556
|
+
(wallet.hotkey_str, get_hotkey_pub_ss58(wallet))
|
506
557
|
for wallet in all_hotkeys_
|
507
558
|
if wallet.hotkey_str not in (exclude_hotkeys or [])
|
508
559
|
]
|
@@ -522,7 +573,7 @@ def _get_hotkeys_to_stake_to(
|
|
522
573
|
name=wallet.name,
|
523
574
|
hotkey=hotkey_ss58_or_hotkey_name,
|
524
575
|
)
|
525
|
-
hotkeys.append((wallet_.hotkey_str, wallet_
|
576
|
+
hotkeys.append((wallet_.hotkey_str, get_hotkey_pub_ss58(wallet_)))
|
526
577
|
|
527
578
|
return hotkeys
|
528
579
|
|
@@ -531,7 +582,7 @@ def _get_hotkeys_to_stake_to(
|
|
531
582
|
f"Staking to hotkey: ({wallet.hotkey_str}) in wallet: ({wallet.name})"
|
532
583
|
)
|
533
584
|
assert wallet.hotkey is not None
|
534
|
-
return [(None, wallet
|
585
|
+
return [(None, get_hotkey_pub_ss58(wallet))]
|
535
586
|
|
536
587
|
|
537
588
|
def _define_stake_table(
|
@@ -569,17 +620,17 @@ def _define_stake_table(
|
|
569
620
|
"Hotkey", justify="center", style=COLOR_PALETTE["GENERAL"]["HOTKEY"]
|
570
621
|
)
|
571
622
|
table.add_column(
|
572
|
-
|
623
|
+
"Amount (τ)",
|
573
624
|
justify="center",
|
574
625
|
style=COLOR_PALETTE["POOLS"]["TAO"],
|
575
626
|
)
|
576
627
|
table.add_column(
|
577
|
-
|
628
|
+
"Rate (per τ)",
|
578
629
|
justify="center",
|
579
630
|
style=COLOR_PALETTE["POOLS"]["RATE"],
|
580
631
|
)
|
581
632
|
table.add_column(
|
582
|
-
"Received",
|
633
|
+
"Est. Received",
|
583
634
|
justify="center",
|
584
635
|
style=COLOR_PALETTE["POOLS"]["TAO_EQUIV"],
|
585
636
|
)
|
@@ -588,6 +639,11 @@ def _define_stake_table(
|
|
588
639
|
justify="center",
|
589
640
|
style=COLOR_PALETTE["STAKE"]["STAKE_AMOUNT"],
|
590
641
|
)
|
642
|
+
table.add_column(
|
643
|
+
"Extrinsic Fee (τ)",
|
644
|
+
justify="center",
|
645
|
+
style=COLOR_PALETTE.STAKE.TAO,
|
646
|
+
)
|
591
647
|
# TODO: Uncomment when slippage is reimplemented for v3
|
592
648
|
# table.add_column(
|
593
649
|
# "Slippage", justify="center", style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"]
|