bittensor-cli 9.8.6__tar.gz → 9.9.0__tar.gz
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-9.8.6 → bittensor_cli-9.9.0}/PKG-INFO +2 -2
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/cli.py +64 -1
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/extrinsics/transfer.py +34 -17
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/subtensor_interface.py +32 -14
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/utils.py +19 -14
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/stake/add.py +82 -27
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/stake/move.py +101 -81
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/stake/remove.py +114 -7
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/wallets.py +91 -3
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli.egg-info/PKG-INFO +2 -2
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli.egg-info/requires.txt +1 -1
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/pyproject.toml +2 -2
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/MANIFEST.in +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/README.md +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/__init__.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/doc_generation_helper.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/__init__.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/__init__.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/balances.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/chain_data.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/extrinsics/__init__.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/extrinsics/registration.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/extrinsics/root.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/minigraph.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/networking.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/templates/main-filters.j2 +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/templates/main-header.j2 +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/templates/neuron-details.j2 +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/templates/price-multi.j2 +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/templates/price-single.j2 +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/templates/subnet-details-header.j2 +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/templates/subnet-details.j2 +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/templates/subnet-metrics.j2 +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/templates/subnets-table.j2 +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/templates/table.j2 +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/templates/view.css +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/templates/view.j2 +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/templates/view.js +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/__init__.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/liquidity/__init__.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/liquidity/liquidity.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/liquidity/utils.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/stake/__init__.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/stake/children_hotkeys.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/stake/list.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/subnets/__init__.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/subnets/price.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/subnets/subnets.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/sudo.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/view.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/commands/weights.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/version.py +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli.egg-info/SOURCES.txt +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli.egg-info/dependency_links.txt +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli.egg-info/entry_points.txt +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli.egg-info/top_level.txt +0 -0
- {bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: bittensor-cli
|
3
|
-
Version: 9.
|
3
|
+
Version: 9.9.0
|
4
4
|
Summary: Bittensor CLI
|
5
5
|
Author: bittensor.com
|
6
6
|
Project-URL: homepage, https://github.com/opentensor/btcli
|
@@ -8,7 +8,7 @@ Project-URL: Repository, https://github.com/opentensor/btcli
|
|
8
8
|
Requires-Python: <3.14,>=3.9
|
9
9
|
Description-Content-Type: text/markdown
|
10
10
|
Requires-Dist: wheel
|
11
|
-
Requires-Dist: async-substrate-interface>=1.
|
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
14
|
Requires-Dist: click<8.2.0
|
@@ -817,6 +817,9 @@ class CLIManager:
|
|
817
817
|
self.wallet_app.command(
|
818
818
|
"sign", rich_help_panel=HELP_PANELS["WALLET"]["OPERATIONS"]
|
819
819
|
)(self.wallet_sign)
|
820
|
+
self.wallet_app.command(
|
821
|
+
"verify", rich_help_panel=HELP_PANELS["WALLET"]["OPERATIONS"]
|
822
|
+
)(self.wallet_verify)
|
820
823
|
|
821
824
|
# stake commands
|
822
825
|
self.stake_app.command(
|
@@ -1889,6 +1892,12 @@ class CLIManager:
|
|
1889
1892
|
transfer_all: bool = typer.Option(
|
1890
1893
|
False, "--all", prompt=False, help="Transfer all available balance."
|
1891
1894
|
),
|
1895
|
+
allow_death: bool = typer.Option(
|
1896
|
+
False,
|
1897
|
+
"--allow-death",
|
1898
|
+
prompt=False,
|
1899
|
+
help="Transfer balance even if the resulting balance falls below the existential deposit.",
|
1900
|
+
),
|
1892
1901
|
period: int = Options.period,
|
1893
1902
|
wallet_name: str = Options.wallet_name,
|
1894
1903
|
wallet_path: str = Options.wallet_path,
|
@@ -1932,7 +1941,7 @@ class CLIManager:
|
|
1932
1941
|
subtensor = self.initialize_chain(network)
|
1933
1942
|
if transfer_all and amount:
|
1934
1943
|
print_error("Cannot specify an amount and '--all' flag.")
|
1935
|
-
|
1944
|
+
return False
|
1936
1945
|
elif transfer_all:
|
1937
1946
|
amount = 0
|
1938
1947
|
elif not amount:
|
@@ -1944,6 +1953,7 @@ class CLIManager:
|
|
1944
1953
|
destination=destination_ss58_address,
|
1945
1954
|
amount=amount,
|
1946
1955
|
transfer_all=transfer_all,
|
1956
|
+
allow_death=allow_death,
|
1947
1957
|
era=period,
|
1948
1958
|
prompt=prompt,
|
1949
1959
|
json_output=json_output,
|
@@ -3091,6 +3101,59 @@ class CLIManager:
|
|
3091
3101
|
|
3092
3102
|
return self._run_command(wallets.sign(wallet, message, use_hotkey, json_output))
|
3093
3103
|
|
3104
|
+
def wallet_verify(
|
3105
|
+
self,
|
3106
|
+
message: Optional[str] = typer.Option(
|
3107
|
+
None, "--message", "-m", help="The message that was signed"
|
3108
|
+
),
|
3109
|
+
signature: Optional[str] = typer.Option(
|
3110
|
+
None, "--signature", "-s", help="The signature to verify (hex format)"
|
3111
|
+
),
|
3112
|
+
public_key_or_ss58: Optional[str] = typer.Option(
|
3113
|
+
None,
|
3114
|
+
"--address",
|
3115
|
+
"-a",
|
3116
|
+
"--public-key",
|
3117
|
+
"-p",
|
3118
|
+
help="SS58 address or public key (hex) of the signer",
|
3119
|
+
),
|
3120
|
+
quiet: bool = Options.quiet,
|
3121
|
+
verbose: bool = Options.verbose,
|
3122
|
+
json_output: bool = Options.json_output,
|
3123
|
+
):
|
3124
|
+
"""
|
3125
|
+
Verify a message signature using the signer's public key or SS58 address.
|
3126
|
+
|
3127
|
+
This command allows you to verify that a message was signed by the owner of a specific address.
|
3128
|
+
|
3129
|
+
USAGE
|
3130
|
+
|
3131
|
+
Provide the original message, the signature (in hex format), and either the SS58 address
|
3132
|
+
or public key of the signer to verify the signature.
|
3133
|
+
|
3134
|
+
EXAMPLES
|
3135
|
+
|
3136
|
+
[green]$[/green] btcli wallet verify --message "Hello world" --signature "0xabc123..." --address "5GrwvaEF..."
|
3137
|
+
|
3138
|
+
[green]$[/green] btcli wallet verify -m "Test message" -s "0xdef456..." -p "0x1234abcd..."
|
3139
|
+
"""
|
3140
|
+
self.verbosity_handler(quiet, verbose, json_output)
|
3141
|
+
|
3142
|
+
if not public_key_or_ss58:
|
3143
|
+
public_key_or_ss58 = Prompt.ask(
|
3144
|
+
"Enter the [blue]address[/blue] (SS58 or hex format)"
|
3145
|
+
)
|
3146
|
+
|
3147
|
+
if not message:
|
3148
|
+
message = Prompt.ask("Enter the [blue]message[/blue]")
|
3149
|
+
|
3150
|
+
if not signature:
|
3151
|
+
signature = Prompt.ask("Enter the [blue]signature[/blue]")
|
3152
|
+
|
3153
|
+
return self._run_command(
|
3154
|
+
wallets.verify(message, signature, public_key_or_ss58, json_output)
|
3155
|
+
)
|
3156
|
+
|
3094
3157
|
def wallet_swap_coldkey(
|
3095
3158
|
self,
|
3096
3159
|
wallet_name: Optional[str] = Options.wallet_name,
|
{bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/extrinsics/transfer.py
RENAMED
@@ -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
|
|
{bittensor_cli-9.8.6 → bittensor_cli-9.9.0}/bittensor_cli/src/bittensor/subtensor_interface.py
RENAMED
@@ -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
|
@@ -881,9 +883,10 @@ class SubtensorInterface:
|
|
881
883
|
storage_function="IdentitiesV2",
|
882
884
|
block_hash=block_hash,
|
883
885
|
reuse_block_hash=reuse_block,
|
886
|
+
fully_exhaust=True,
|
884
887
|
)
|
885
888
|
all_identities = {}
|
886
|
-
|
889
|
+
for ss58_address, identity in identities.records:
|
887
890
|
all_identities[decode_account_id(ss58_address[0])] = decode_hex_identity(
|
888
891
|
identity.value
|
889
892
|
)
|
@@ -939,22 +942,22 @@ class SubtensorInterface:
|
|
939
942
|
:param reuse_block: Whether to reuse the last-used blockchain block hash.
|
940
943
|
:return: Dict with 'coldkeys' and 'hotkeys' as keys.
|
941
944
|
"""
|
942
|
-
|
943
|
-
|
945
|
+
if block_hash is None:
|
946
|
+
block_hash = await self.substrate.get_chain_head()
|
947
|
+
coldkey_identities = await self.query_all_identities(block_hash=block_hash)
|
944
948
|
identities = {"coldkeys": {}, "hotkeys": {}}
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
reuse_block_hash=reuse_block,
|
953
|
-
)
|
949
|
+
sks = [
|
950
|
+
await self.substrate.create_storage_key(
|
951
|
+
"SubtensorModule", "OwnedHotkeys", [ck], block_hash=block_hash
|
952
|
+
)
|
953
|
+
for ck in coldkey_identities.keys()
|
954
|
+
]
|
955
|
+
query = await self.substrate.query_multi(sks, block_hash=block_hash)
|
954
956
|
|
955
|
-
|
957
|
+
storage_key: StorageKey
|
958
|
+
for storage_key, hotkeys in query:
|
959
|
+
coldkey_ss58 = storage_key.params[0]
|
956
960
|
coldkey_identity = coldkey_identities.get(coldkey_ss58)
|
957
|
-
hotkeys = [decode_account_id(hotkey[0]) for hotkey in hotkeys or []]
|
958
961
|
|
959
962
|
identities["coldkeys"][coldkey_ss58] = {
|
960
963
|
"identity": coldkey_identity,
|
@@ -1455,6 +1458,8 @@ class SubtensorInterface:
|
|
1455
1458
|
),
|
1456
1459
|
self.get_subnet_price(netuid=netuid, block_hash=block_hash),
|
1457
1460
|
)
|
1461
|
+
if not result:
|
1462
|
+
raise ValueError(f"Subnet {netuid} not found")
|
1458
1463
|
subnet_ = DynamicInfo.from_any(result)
|
1459
1464
|
subnet_.price = price
|
1460
1465
|
return subnet_
|
@@ -1484,6 +1489,19 @@ class SubtensorInterface:
|
|
1484
1489
|
|
1485
1490
|
return [decode_account_id(hotkey[0]) for hotkey in owned_hotkeys or []]
|
1486
1491
|
|
1492
|
+
async def get_extrinsic_fee(self, call: GenericCall, keypair: Keypair) -> Balance:
|
1493
|
+
"""
|
1494
|
+
Determines the fee for the extrinsic call.
|
1495
|
+
Args:
|
1496
|
+
call: Created extrinsic call
|
1497
|
+
keypair: The keypair that would sign the extrinsic (usually you would just want to use the *pub for this)
|
1498
|
+
|
1499
|
+
Returns:
|
1500
|
+
Balance object representing the fee for this extrinsic.
|
1501
|
+
"""
|
1502
|
+
fee_dict = await self.substrate.get_payment_info(call, keypair)
|
1503
|
+
return Balance.from_rao(fee_dict["partial_fee"])
|
1504
|
+
|
1487
1505
|
async def get_stake_fee(
|
1488
1506
|
self,
|
1489
1507
|
origin_hotkey_ss58: Optional[str],
|
@@ -622,20 +622,23 @@ def decode_hex_identity_dict(info_dictionary) -> dict[str, Any]:
|
|
622
622
|
|
623
623
|
Examples:
|
624
624
|
input_dict = {
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
625
|
+
"name": {"value": "0x6a6f686e"},
|
626
|
+
"additional": [
|
627
|
+
{"data1": "0x64617461"},
|
628
|
+
("data2", "0x64617461")
|
629
|
+
]
|
630
|
+
}
|
630
631
|
decode_hex_identity_dict(input_dict)
|
631
|
-
{'name': 'john', 'additional': [('data', 'data')]}
|
632
|
+
{'name': 'john', 'additional': [('data1', 'data'), ('data2', 'data')]}
|
632
633
|
"""
|
633
634
|
|
634
|
-
def get_decoded(data: str) -> str:
|
635
|
+
def get_decoded(data: Optional[str]) -> str:
|
635
636
|
"""Decodes a hex-encoded string."""
|
637
|
+
if data is None:
|
638
|
+
return ""
|
636
639
|
try:
|
637
640
|
return hex_to_bytes(data).decode()
|
638
|
-
except UnicodeDecodeError:
|
641
|
+
except (UnicodeDecodeError, ValueError):
|
639
642
|
print(f"Could not decode: {key}: {item}")
|
640
643
|
|
641
644
|
for key, value in info_dictionary.items():
|
@@ -651,12 +654,14 @@ def decode_hex_identity_dict(info_dictionary) -> dict[str, Any]:
|
|
651
654
|
if key == "additional":
|
652
655
|
additional = []
|
653
656
|
for item in value:
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
)
|
659
|
-
|
657
|
+
if isinstance(item, dict):
|
658
|
+
for k, v in item.items():
|
659
|
+
additional.append((k, get_decoded(v)))
|
660
|
+
else:
|
661
|
+
if isinstance(item, (tuple, list)) and len(item) == 2:
|
662
|
+
k_, v = item
|
663
|
+
k = k_ if k_ is not None else ""
|
664
|
+
additional.append((k, get_decoded(v)))
|
660
665
|
info_dictionary[key] = additional
|
661
666
|
|
662
667
|
return info_dictionary
|
@@ -65,6 +65,45 @@ async def stake_add(
|
|
65
65
|
bool: True if stake operation is successful, False otherwise
|
66
66
|
"""
|
67
67
|
|
68
|
+
async def get_stake_extrinsic_fee(
|
69
|
+
netuid_: int,
|
70
|
+
amount_: Balance,
|
71
|
+
staking_address_: str,
|
72
|
+
safe_staking_: bool,
|
73
|
+
price_limit: Optional[Balance] = None,
|
74
|
+
):
|
75
|
+
"""
|
76
|
+
Quick method to get the extrinsic fee for adding stake depending on the args supplied.
|
77
|
+
Args:
|
78
|
+
netuid_: The netuid where the stake will be added
|
79
|
+
amount_: the amount of stake to add
|
80
|
+
staking_address_: the hotkey ss58 to stake to
|
81
|
+
safe_staking_: whether to use safe staking
|
82
|
+
price_limit: rate with tolerance
|
83
|
+
|
84
|
+
Returns:
|
85
|
+
Balance object representing the extrinsic fee for adding stake.
|
86
|
+
"""
|
87
|
+
call_fn = "add_stake" if not safe_staking_ else "add_stake_limit"
|
88
|
+
call_params = {
|
89
|
+
"hotkey": staking_address_,
|
90
|
+
"netuid": netuid_,
|
91
|
+
"amount_staked": amount_.rao,
|
92
|
+
}
|
93
|
+
if safe_staking_:
|
94
|
+
call_params.update(
|
95
|
+
{
|
96
|
+
"limit_price": price_limit,
|
97
|
+
"allow_partial": allow_partial_stake,
|
98
|
+
}
|
99
|
+
)
|
100
|
+
call = await subtensor.substrate.compose_call(
|
101
|
+
call_module="SubtensorModule",
|
102
|
+
call_function=call_fn,
|
103
|
+
call_params=call_params,
|
104
|
+
)
|
105
|
+
return await subtensor.get_extrinsic_fee(call, wallet.coldkeypub)
|
106
|
+
|
68
107
|
async def safe_stake_extrinsic(
|
69
108
|
netuid_: int,
|
70
109
|
amount_: Balance,
|
@@ -87,7 +126,7 @@ async def stake_add(
|
|
87
126
|
"hotkey": hotkey_ss58_,
|
88
127
|
"netuid": netuid_,
|
89
128
|
"amount_staked": amount_.rao,
|
90
|
-
"limit_price": price_limit,
|
129
|
+
"limit_price": price_limit.rao,
|
91
130
|
"allow_partial": allow_partial_stake,
|
92
131
|
},
|
93
132
|
),
|
@@ -332,19 +371,6 @@ async def stake_add(
|
|
332
371
|
# Temporary workaround - calculations without slippage
|
333
372
|
current_price_float = float(subnet_info.price.tao)
|
334
373
|
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
374
|
|
349
375
|
# If we are staking safe, add price tolerance
|
350
376
|
if safe_staking:
|
@@ -356,21 +382,45 @@ async def stake_add(
|
|
356
382
|
rate_with_tolerance = f"{_rate_with_tolerance:.4f}"
|
357
383
|
price_with_tolerance = Balance.from_tao(
|
358
384
|
price_with_tolerance
|
359
|
-
)
|
385
|
+
) # Actual price to pass to extrinsic
|
360
386
|
else:
|
361
387
|
rate_with_tolerance = "1"
|
362
388
|
price_with_tolerance = Balance.from_rao(1)
|
389
|
+
extrinsic_fee = await get_stake_extrinsic_fee(
|
390
|
+
netuid_=netuid,
|
391
|
+
amount_=amount_to_stake,
|
392
|
+
staking_address_=hotkey[1],
|
393
|
+
safe_staking_=safe_staking,
|
394
|
+
price_limit=price_with_tolerance,
|
395
|
+
)
|
363
396
|
prices_with_tolerance.append(price_with_tolerance)
|
364
|
-
|
365
|
-
|
366
|
-
[
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
397
|
+
row_extension = [
|
398
|
+
f"{rate_with_tolerance} {Balance.get_unit(netuid)}/{Balance.get_unit(0)} ",
|
399
|
+
f"[{'dark_sea_green3' if allow_partial_stake else 'red'}]"
|
400
|
+
# safe staking
|
401
|
+
f"{allow_partial_stake}[/{'dark_sea_green3' if allow_partial_stake else 'red'}]",
|
402
|
+
]
|
403
|
+
else:
|
404
|
+
extrinsic_fee = await get_stake_extrinsic_fee(
|
405
|
+
netuid_=netuid,
|
406
|
+
amount_=amount_to_stake,
|
407
|
+
staking_address_=hotkey[1],
|
408
|
+
safe_staking_=safe_staking,
|
372
409
|
)
|
373
|
-
|
410
|
+
row_extension = []
|
411
|
+
received_amount = rate * (amount_to_stake - stake_fee - extrinsic_fee)
|
412
|
+
# Add rows for the table
|
413
|
+
base_row = [
|
414
|
+
str(netuid), # netuid
|
415
|
+
f"{hotkey[1]}", # hotkey
|
416
|
+
str(amount_to_stake), # amount
|
417
|
+
str(rate)
|
418
|
+
+ f" {Balance.get_unit(netuid)}/{Balance.get_unit(0)} ", # rate
|
419
|
+
str(received_amount.set_unit(netuid)), # received
|
420
|
+
str(stake_fee), # fee
|
421
|
+
str(extrinsic_fee),
|
422
|
+
# str(slippage_pct), # slippage
|
423
|
+
] + row_extension
|
374
424
|
rows.append(tuple(base_row))
|
375
425
|
|
376
426
|
# Define and print stake table + slippage warning
|
@@ -569,17 +619,17 @@ def _define_stake_table(
|
|
569
619
|
"Hotkey", justify="center", style=COLOR_PALETTE["GENERAL"]["HOTKEY"]
|
570
620
|
)
|
571
621
|
table.add_column(
|
572
|
-
|
622
|
+
"Amount (τ)",
|
573
623
|
justify="center",
|
574
624
|
style=COLOR_PALETTE["POOLS"]["TAO"],
|
575
625
|
)
|
576
626
|
table.add_column(
|
577
|
-
|
627
|
+
"Rate (per τ)",
|
578
628
|
justify="center",
|
579
629
|
style=COLOR_PALETTE["POOLS"]["RATE"],
|
580
630
|
)
|
581
631
|
table.add_column(
|
582
|
-
"Received",
|
632
|
+
"Est. Received",
|
583
633
|
justify="center",
|
584
634
|
style=COLOR_PALETTE["POOLS"]["TAO_EQUIV"],
|
585
635
|
)
|
@@ -588,6 +638,11 @@ def _define_stake_table(
|
|
588
638
|
justify="center",
|
589
639
|
style=COLOR_PALETTE["STAKE"]["STAKE_AMOUNT"],
|
590
640
|
)
|
641
|
+
table.add_column(
|
642
|
+
"Extrinsic Fee (τ)",
|
643
|
+
justify="center",
|
644
|
+
style=COLOR_PALETTE.STAKE.TAO,
|
645
|
+
)
|
591
646
|
# TODO: Uncomment when slippage is reimplemented for v3
|
592
647
|
# table.add_column(
|
593
648
|
# "Slippage", justify="center", style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"]
|