bittensor-cli 9.10.2__py3-none-any.whl → 9.11.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 +541 -104
- bittensor_cli/src/__init__.py +4 -1
- bittensor_cli/src/bittensor/chain_data.py +17 -0
- bittensor_cli/src/bittensor/extrinsics/transfer.py +3 -1
- bittensor_cli/src/bittensor/subtensor_interface.py +110 -61
- bittensor_cli/src/bittensor/utils.py +20 -3
- bittensor_cli/src/commands/stake/add.py +8 -13
- bittensor_cli/src/commands/stake/move.py +15 -21
- bittensor_cli/src/commands/stake/remove.py +9 -27
- bittensor_cli/src/commands/subnets/price.py +11 -1
- bittensor_cli/src/commands/subnets/subnets.py +68 -0
- bittensor_cli/src/commands/sudo.py +5 -4
- bittensor_cli/src/commands/wallets.py +16 -3
- {bittensor_cli-9.10.2.dist-info → bittensor_cli-9.11.1.dist-info}/METADATA +31 -5
- {bittensor_cli-9.10.2.dist-info → bittensor_cli-9.11.1.dist-info}/RECORD +18 -18
- {bittensor_cli-9.10.2.dist-info → bittensor_cli-9.11.1.dist-info}/WHEEL +0 -0
- {bittensor_cli-9.10.2.dist-info → bittensor_cli-9.11.1.dist-info}/entry_points.txt +0 -0
- {bittensor_cli-9.10.2.dist-info → bittensor_cli-9.11.1.dist-info}/top_level.txt +0 -0
bittensor_cli/src/__init__.py
CHANGED
@@ -23,6 +23,7 @@ class Constants:
|
|
23
23
|
dev_entrypoint = "wss://dev.chain.opentensor.ai:443"
|
24
24
|
local_entrypoint = "ws://127.0.0.1:9944"
|
25
25
|
latent_lite_entrypoint = "wss://lite.sub.latent.to:443"
|
26
|
+
lite_nodes = [finney_entrypoint, subvortex_entrypoint, latent_lite_entrypoint]
|
26
27
|
network_map = {
|
27
28
|
"finney": finney_entrypoint,
|
28
29
|
"test": finney_test_entrypoint,
|
@@ -88,12 +89,14 @@ class Defaults:
|
|
88
89
|
class config:
|
89
90
|
base_path = "~/.bittensor"
|
90
91
|
path = "~/.bittensor/config.yml"
|
92
|
+
debug_file_path = "~/.bittensor/debug.txt"
|
91
93
|
dictionary = {
|
92
94
|
"network": None,
|
93
95
|
"wallet_path": None,
|
94
96
|
"wallet_name": None,
|
95
97
|
"wallet_hotkey": None,
|
96
98
|
"use_cache": True,
|
99
|
+
"disk_cache": False,
|
97
100
|
"metagraph_cols": {
|
98
101
|
"UID": True,
|
99
102
|
"GLOBAL_STAKE": True,
|
@@ -638,7 +641,7 @@ HYPERPARAMS = {
|
|
638
641
|
"adjustment_interval": ("sudo_set_adjustment_interval", True),
|
639
642
|
"activity_cutoff": ("sudo_set_activity_cutoff", False),
|
640
643
|
"target_regs_per_interval": ("sudo_set_target_registrations_per_interval", True),
|
641
|
-
"min_burn": ("sudo_set_min_burn",
|
644
|
+
"min_burn": ("sudo_set_min_burn", False),
|
642
645
|
"max_burn": ("sudo_set_max_burn", True),
|
643
646
|
"bonds_moving_avg": ("sudo_set_bonds_moving_average", False),
|
644
647
|
"max_regs_per_block": ("sudo_set_max_registrations_per_block", True),
|
@@ -1193,3 +1193,20 @@ class MetagraphInfo(InfoBase):
|
|
1193
1193
|
for adphk in decoded["alpha_dividends_per_hotkey"]
|
1194
1194
|
],
|
1195
1195
|
)
|
1196
|
+
|
1197
|
+
|
1198
|
+
@dataclass
|
1199
|
+
class SimSwapResult:
|
1200
|
+
tao_amount: Balance
|
1201
|
+
alpha_amount: Balance
|
1202
|
+
tao_fee: Balance
|
1203
|
+
alpha_fee: Balance
|
1204
|
+
|
1205
|
+
@classmethod
|
1206
|
+
def from_dict(cls, d: dict, netuid: int) -> "SimSwapResult":
|
1207
|
+
return cls(
|
1208
|
+
tao_amount=Balance.from_rao(d["tao_amount"]).set_unit(0),
|
1209
|
+
alpha_amount=Balance.from_rao(d["alpha_amount"]).set_unit(netuid),
|
1210
|
+
tao_fee=Balance.from_rao(d["tao_fee"]).set_unit(0),
|
1211
|
+
alpha_fee=Balance.from_rao(d["alpha_fee"]).set_unit(netuid),
|
1212
|
+
)
|
@@ -175,7 +175,9 @@ async def transfer_extrinsic(
|
|
175
175
|
f" amount: [bright_cyan]{amount if not transfer_all else account_balance}[/bright_cyan]\n"
|
176
176
|
f" from: [light_goldenrod2]{wallet.name}[/light_goldenrod2] : "
|
177
177
|
f"[bright_magenta]{wallet.coldkey.ss58_address}\n[/bright_magenta]"
|
178
|
-
f" to: [bright_magenta]{destination}[/bright_magenta]\n for fee: [bright_cyan]{fee}[/bright_cyan]"
|
178
|
+
f" to: [bright_magenta]{destination}[/bright_magenta]\n for fee: [bright_cyan]{fee}[/bright_cyan]\n"
|
179
|
+
f":warning:[bright_yellow]Transferring is not the same as staking. To instead stake, use "
|
180
|
+
f"[dark_orange]btcli stake add[/dark_orange] instead[/bright_yellow]:warning:"
|
179
181
|
):
|
180
182
|
return False
|
181
183
|
|
@@ -1,21 +1,22 @@
|
|
1
1
|
import asyncio
|
2
2
|
import os
|
3
|
+
import time
|
3
4
|
from typing import Optional, Any, Union, TypedDict, Iterable
|
4
5
|
|
5
6
|
import aiohttp
|
7
|
+
from async_substrate_interface.async_substrate import (
|
8
|
+
DiskCachedAsyncSubstrateInterface,
|
9
|
+
AsyncSubstrateInterface,
|
10
|
+
)
|
11
|
+
from async_substrate_interface.errors import SubstrateRequestException
|
6
12
|
from async_substrate_interface.utils.storage import StorageKey
|
7
13
|
from bittensor_wallet import Wallet
|
8
14
|
from bittensor_wallet.bittensor_wallet import Keypair
|
9
15
|
from bittensor_wallet.utils import SS58_FORMAT
|
10
16
|
from scalecodec import GenericCall
|
11
|
-
from async_substrate_interface.errors import SubstrateRequestException
|
12
17
|
import typer
|
18
|
+
import websockets
|
13
19
|
|
14
|
-
|
15
|
-
from async_substrate_interface.async_substrate import (
|
16
|
-
DiskCachedAsyncSubstrateInterface,
|
17
|
-
AsyncSubstrateInterface,
|
18
|
-
)
|
19
20
|
from bittensor_cli.src.bittensor.chain_data import (
|
20
21
|
DelegateInfo,
|
21
22
|
StakeInfo,
|
@@ -27,6 +28,7 @@ from bittensor_cli.src.bittensor.chain_data import (
|
|
27
28
|
DynamicInfo,
|
28
29
|
SubnetState,
|
29
30
|
MetagraphInfo,
|
31
|
+
SimSwapResult,
|
30
32
|
)
|
31
33
|
from bittensor_cli.src import DelegatesDetails
|
32
34
|
from bittensor_cli.src.bittensor.balances import Balance, fixed_to_float
|
@@ -42,12 +44,6 @@ from bittensor_cli.src.bittensor.utils import (
|
|
42
44
|
get_hotkey_pub_ss58,
|
43
45
|
)
|
44
46
|
|
45
|
-
SubstrateClass = (
|
46
|
-
DiskCachedAsyncSubstrateInterface
|
47
|
-
if os.getenv("DISK_CACHE", "0") == "1"
|
48
|
-
else AsyncSubstrateInterface
|
49
|
-
)
|
50
|
-
|
51
47
|
|
52
48
|
class ParamWithTypes(TypedDict):
|
53
49
|
name: str # Name of the parameter.
|
@@ -81,7 +77,7 @@ class SubtensorInterface:
|
|
81
77
|
Thin layer for interacting with Substrate Interface. Mostly a collection of frequently-used calls.
|
82
78
|
"""
|
83
79
|
|
84
|
-
def __init__(self, network):
|
80
|
+
def __init__(self, network, use_disk_cache: bool = False):
|
85
81
|
if network in Constants.network_map:
|
86
82
|
self.chain_endpoint = Constants.network_map[network]
|
87
83
|
self.network = network
|
@@ -111,8 +107,12 @@ class SubtensorInterface:
|
|
111
107
|
)
|
112
108
|
self.chain_endpoint = Constants.network_map[defaults.subtensor.network]
|
113
109
|
self.network = defaults.subtensor.network
|
114
|
-
|
115
|
-
|
110
|
+
substrate_class = (
|
111
|
+
DiskCachedAsyncSubstrateInterface
|
112
|
+
if (use_disk_cache or os.getenv("DISK_CACHE", "0") == "1")
|
113
|
+
else AsyncSubstrateInterface
|
114
|
+
)
|
115
|
+
self.substrate = substrate_class(
|
116
116
|
url=self.chain_endpoint,
|
117
117
|
ss58_format=SS58_FORMAT,
|
118
118
|
type_registry=TYPE_REGISTRY,
|
@@ -1503,59 +1503,79 @@ class SubtensorInterface:
|
|
1503
1503
|
fee_dict = await self.substrate.get_payment_info(call, keypair)
|
1504
1504
|
return Balance.from_rao(fee_dict["partial_fee"])
|
1505
1505
|
|
1506
|
-
async def
|
1506
|
+
async def sim_swap(
|
1507
1507
|
self,
|
1508
|
-
|
1509
|
-
|
1510
|
-
origin_coldkey_ss58: str,
|
1511
|
-
destination_hotkey_ss58: Optional[str],
|
1512
|
-
destination_netuid: Optional[int],
|
1513
|
-
destination_coldkey_ss58: str,
|
1508
|
+
origin_netuid: int,
|
1509
|
+
destination_netuid: int,
|
1514
1510
|
amount: int,
|
1515
1511
|
block_hash: Optional[str] = None,
|
1516
|
-
) ->
|
1512
|
+
) -> SimSwapResult:
|
1517
1513
|
"""
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
:param origin_netuid: Netuid of source subnet (None for new stake)
|
1522
|
-
:param origin_coldkey_ss58: SS58 address of source coldkey
|
1523
|
-
:param destination_hotkey_ss58: SS58 address of destination hotkey (None for removing stake)
|
1524
|
-
:param destination_netuid: Netuid of destination subnet (None for removing stake)
|
1525
|
-
:param destination_coldkey_ss58: SS58 address of destination coldkey
|
1526
|
-
:param amount: Amount of stake to transfer in RAO
|
1527
|
-
:param block_hash: Optional block hash at which to perform the calculation
|
1528
|
-
|
1529
|
-
:return: The calculated stake fee as a Balance object
|
1530
|
-
|
1531
|
-
When to use None:
|
1532
|
-
|
1533
|
-
1. Adding new stake (default fee):
|
1534
|
-
- origin_hotkey_ss58 = None
|
1535
|
-
- origin_netuid = None
|
1536
|
-
- All other fields required
|
1514
|
+
Hits the SimSwap Runtime API to calculate the fee and result for a given transaction. This should be used
|
1515
|
+
instead of get_stake_fee for staking fee calculations. The SimSwapResult contains the staking fees and expected
|
1516
|
+
returned amounts of a given transaction. This does not include the transaction (extrinsic) fee.
|
1537
1517
|
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1518
|
+
Args:
|
1519
|
+
origin_netuid: Netuid of the source subnet (0 if new stake)
|
1520
|
+
destination_netuid: Netuid of the destination subnet
|
1521
|
+
amount: Amount to transfer in Rao
|
1522
|
+
block_hash: The hash of the blockchain block number for the query.
|
1542
1523
|
|
1543
|
-
|
1544
|
-
|
1545
|
-
4. Moving between hotkeys
|
1546
|
-
5. Moving between coldkeys
|
1524
|
+
Returns:
|
1525
|
+
SimSwapResult object representing the result
|
1547
1526
|
"""
|
1548
|
-
|
1549
|
-
if origin_netuid
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1527
|
+
block_hash = block_hash or await self.substrate.get_chain_head()
|
1528
|
+
if origin_netuid > 0 and destination_netuid > 0:
|
1529
|
+
# for cross-subnet moves where neither origin nor destination is root
|
1530
|
+
intermediate_result_, sn_price = await asyncio.gather(
|
1531
|
+
self.query_runtime_api(
|
1532
|
+
"SwapRuntimeApi",
|
1533
|
+
"sim_swap_alpha_for_tao",
|
1534
|
+
params={"netuid": origin_netuid, "alpha": amount},
|
1535
|
+
block_hash=block_hash,
|
1536
|
+
),
|
1537
|
+
self.get_subnet_price(origin_netuid, block_hash=block_hash),
|
1538
|
+
)
|
1539
|
+
intermediate_result = SimSwapResult.from_dict(
|
1540
|
+
intermediate_result_, origin_netuid
|
1541
|
+
)
|
1542
|
+
result = SimSwapResult.from_dict(
|
1543
|
+
await self.query_runtime_api(
|
1544
|
+
"SwapRuntimeApi",
|
1545
|
+
"sim_swap_tao_for_alpha",
|
1546
|
+
params={
|
1547
|
+
"netuid": destination_netuid,
|
1548
|
+
"tao": intermediate_result.tao_amount,
|
1549
|
+
},
|
1550
|
+
block_hash=block_hash,
|
1551
|
+
),
|
1552
|
+
destination_netuid,
|
1553
|
+
)
|
1554
|
+
secondary_fee = (result.tao_fee * sn_price).set_unit(origin_netuid)
|
1555
|
+
result.alpha_fee = result.alpha_fee + secondary_fee
|
1556
|
+
return result
|
1557
|
+
elif origin_netuid > 0:
|
1558
|
+
# dynamic to tao
|
1559
|
+
return SimSwapResult.from_dict(
|
1560
|
+
await self.query_runtime_api(
|
1561
|
+
"SwapRuntimeApi",
|
1562
|
+
"sim_swap_alpha_for_tao",
|
1563
|
+
params={"netuid": origin_netuid, "alpha": amount},
|
1564
|
+
block_hash=block_hash,
|
1565
|
+
),
|
1566
|
+
origin_netuid,
|
1567
|
+
)
|
1568
|
+
else:
|
1569
|
+
# tao to dynamic or unstaked to staked tao (SN0)
|
1570
|
+
return SimSwapResult.from_dict(
|
1571
|
+
await self.query_runtime_api(
|
1572
|
+
"SwapRuntimeApi",
|
1573
|
+
"sim_swap_tao_for_alpha",
|
1574
|
+
params={"netuid": destination_netuid, "tao": amount},
|
1575
|
+
block_hash=block_hash,
|
1576
|
+
),
|
1577
|
+
destination_netuid,
|
1578
|
+
)
|
1559
1579
|
|
1560
1580
|
async def get_scheduled_coldkey_swap(
|
1561
1581
|
self,
|
@@ -1656,3 +1676,32 @@ class SubtensorInterface:
|
|
1656
1676
|
map_[netuid_] = Balance.from_rao(int(current_price * 1e9))
|
1657
1677
|
|
1658
1678
|
return map_
|
1679
|
+
|
1680
|
+
|
1681
|
+
async def best_connection(networks: list[str]):
|
1682
|
+
"""
|
1683
|
+
Basic function to compare the latency of a given list of websocket endpoints
|
1684
|
+
Args:
|
1685
|
+
networks: list of network URIs
|
1686
|
+
|
1687
|
+
Returns:
|
1688
|
+
{network_name: [end_to_end_latency, single_request_latency, chain_head_request_latency]}
|
1689
|
+
|
1690
|
+
"""
|
1691
|
+
results = {}
|
1692
|
+
for network in networks:
|
1693
|
+
try:
|
1694
|
+
t1 = time.monotonic()
|
1695
|
+
async with websockets.connect(network) as websocket:
|
1696
|
+
pong = await websocket.ping()
|
1697
|
+
latency = await pong
|
1698
|
+
pt1 = time.monotonic()
|
1699
|
+
await websocket.send(
|
1700
|
+
"{'jsonrpc': '2.0', 'method': 'chain_getHead', 'params': [], 'id': '82'}"
|
1701
|
+
)
|
1702
|
+
await websocket.recv()
|
1703
|
+
t2 = time.monotonic()
|
1704
|
+
results[network] = [t2 - t1, latency, t2 - pt1]
|
1705
|
+
except Exception as e:
|
1706
|
+
err_console.print(f"Error attempting network {network}: {e}")
|
1707
|
+
return results
|
@@ -266,13 +266,29 @@ def get_hotkey_wallets_for_wallet(
|
|
266
266
|
hotkeys_path = wallet_path / wallet.name / "hotkeys"
|
267
267
|
try:
|
268
268
|
hotkeys = [entry.name for entry in hotkeys_path.iterdir()]
|
269
|
-
except FileNotFoundError:
|
269
|
+
except (FileNotFoundError, NotADirectoryError):
|
270
270
|
hotkeys = []
|
271
271
|
for h_name in hotkeys:
|
272
|
-
|
272
|
+
if h_name.endswith("pub.txt"):
|
273
|
+
if h_name.split("pub.txt")[0] in hotkeys:
|
274
|
+
continue
|
275
|
+
else:
|
276
|
+
hotkey_for_name = Wallet(
|
277
|
+
path=str(wallet_path),
|
278
|
+
name=wallet.name,
|
279
|
+
hotkey=h_name.split("pub.txt")[0],
|
280
|
+
)
|
281
|
+
else:
|
282
|
+
hotkey_for_name = Wallet(
|
283
|
+
path=str(wallet_path), name=wallet.name, hotkey=h_name
|
284
|
+
)
|
273
285
|
try:
|
286
|
+
exists = (
|
287
|
+
hotkey_for_name.hotkey_file.exists_on_device()
|
288
|
+
or hotkey_for_name.hotkeypub_file.exists_on_device()
|
289
|
+
)
|
274
290
|
if (
|
275
|
-
|
291
|
+
exists
|
276
292
|
and not hotkey_for_name.hotkey_file.is_encrypted()
|
277
293
|
# and hotkey_for_name.coldkeypub.ss58_address
|
278
294
|
and get_hotkey_pub_ss58(hotkey_for_name)
|
@@ -291,6 +307,7 @@ def get_hotkey_wallets_for_wallet(
|
|
291
307
|
AttributeError,
|
292
308
|
TypeError,
|
293
309
|
KeyFileError,
|
310
|
+
ValueError,
|
294
311
|
): # usually an unrelated file like .DS_Store
|
295
312
|
continue
|
296
313
|
|
@@ -347,17 +347,6 @@ async def stake_add(
|
|
347
347
|
return False
|
348
348
|
remaining_wallet_balance -= amount_to_stake
|
349
349
|
|
350
|
-
# TODO this should be asyncio gathered before the for loop
|
351
|
-
stake_fee = await subtensor.get_stake_fee(
|
352
|
-
origin_hotkey_ss58=None,
|
353
|
-
origin_netuid=None,
|
354
|
-
origin_coldkey_ss58=wallet.coldkeypub.ss58_address,
|
355
|
-
destination_hotkey_ss58=hotkey[1],
|
356
|
-
destination_netuid=netuid,
|
357
|
-
destination_coldkey_ss58=wallet.coldkeypub.ss58_address,
|
358
|
-
amount=amount_to_stake.rao,
|
359
|
-
)
|
360
|
-
|
361
350
|
# Calculate slippage
|
362
351
|
# TODO: Update for V3, slippage calculation is significantly different in v3
|
363
352
|
# try:
|
@@ -409,7 +398,13 @@ async def stake_add(
|
|
409
398
|
safe_staking_=safe_staking,
|
410
399
|
)
|
411
400
|
row_extension = []
|
412
|
-
|
401
|
+
# TODO this should be asyncio gathered before the for loop
|
402
|
+
sim_swap = await subtensor.sim_swap(
|
403
|
+
origin_netuid=0,
|
404
|
+
destination_netuid=netuid,
|
405
|
+
amount=(amount_to_stake - extrinsic_fee).rao,
|
406
|
+
)
|
407
|
+
received_amount = sim_swap.alpha_amount
|
413
408
|
# Add rows for the table
|
414
409
|
base_row = [
|
415
410
|
str(netuid), # netuid
|
@@ -418,7 +413,7 @@ async def stake_add(
|
|
418
413
|
str(rate)
|
419
414
|
+ f" {Balance.get_unit(netuid)}/{Balance.get_unit(0)} ", # rate
|
420
415
|
str(received_amount.set_unit(netuid)), # received
|
421
|
-
str(
|
416
|
+
str(sim_swap.tao_fee), # fee
|
422
417
|
str(extrinsic_fee),
|
423
418
|
# str(slippage_pct), # slippage
|
424
419
|
] + row_extension
|
@@ -520,14 +520,10 @@ async def move_stake(
|
|
520
520
|
"alpha_amount": amount_to_move_as_balance.rao,
|
521
521
|
},
|
522
522
|
)
|
523
|
-
|
524
|
-
subtensor.
|
525
|
-
origin_hotkey_ss58=origin_hotkey,
|
523
|
+
sim_swap, extrinsic_fee = await asyncio.gather(
|
524
|
+
subtensor.sim_swap(
|
526
525
|
origin_netuid=origin_netuid,
|
527
|
-
origin_coldkey_ss58=wallet.coldkeypub.ss58_address,
|
528
|
-
destination_hotkey_ss58=destination_hotkey,
|
529
526
|
destination_netuid=destination_netuid,
|
530
|
-
destination_coldkey_ss58=wallet.coldkeypub.ss58_address,
|
531
527
|
amount=amount_to_move_as_balance.rao,
|
532
528
|
),
|
533
529
|
subtensor.get_extrinsic_fee(call, wallet.coldkeypub),
|
@@ -543,7 +539,9 @@ async def move_stake(
|
|
543
539
|
origin_hotkey=origin_hotkey,
|
544
540
|
destination_hotkey=destination_hotkey,
|
545
541
|
amount_to_move=amount_to_move_as_balance,
|
546
|
-
stake_fee=
|
542
|
+
stake_fee=sim_swap.alpha_fee
|
543
|
+
if origin_netuid != 0
|
544
|
+
else sim_swap.tao_fee,
|
547
545
|
extrinsic_fee=extrinsic_fee,
|
548
546
|
)
|
549
547
|
except ValueError:
|
@@ -709,14 +707,10 @@ async def transfer_stake(
|
|
709
707
|
"alpha_amount": amount_to_transfer.rao,
|
710
708
|
},
|
711
709
|
)
|
712
|
-
|
713
|
-
subtensor.
|
714
|
-
origin_hotkey_ss58=origin_hotkey,
|
710
|
+
sim_swap, extrinsic_fee = await asyncio.gather(
|
711
|
+
subtensor.sim_swap(
|
715
712
|
origin_netuid=origin_netuid,
|
716
|
-
origin_coldkey_ss58=wallet.coldkeypub.ss58_address,
|
717
|
-
destination_hotkey_ss58=origin_hotkey,
|
718
713
|
destination_netuid=dest_netuid,
|
719
|
-
destination_coldkey_ss58=dest_coldkey_ss58,
|
720
714
|
amount=amount_to_transfer.rao,
|
721
715
|
),
|
722
716
|
subtensor.get_extrinsic_fee(call, wallet.coldkeypub),
|
@@ -732,7 +726,9 @@ async def transfer_stake(
|
|
732
726
|
origin_hotkey=origin_hotkey,
|
733
727
|
destination_hotkey=origin_hotkey,
|
734
728
|
amount_to_move=amount_to_transfer,
|
735
|
-
stake_fee=
|
729
|
+
stake_fee=sim_swap.alpha_fee
|
730
|
+
if origin_netuid != 0
|
731
|
+
else sim_swap.tao_fee,
|
736
732
|
extrinsic_fee=extrinsic_fee,
|
737
733
|
)
|
738
734
|
except ValueError:
|
@@ -880,14 +876,10 @@ async def swap_stake(
|
|
880
876
|
"alpha_amount": amount_to_swap.rao,
|
881
877
|
},
|
882
878
|
)
|
883
|
-
|
884
|
-
subtensor.
|
885
|
-
origin_hotkey_ss58=hotkey_ss58,
|
879
|
+
sim_swap, extrinsic_fee = await asyncio.gather(
|
880
|
+
subtensor.sim_swap(
|
886
881
|
origin_netuid=origin_netuid,
|
887
|
-
origin_coldkey_ss58=wallet.coldkeypub.ss58_address,
|
888
|
-
destination_hotkey_ss58=hotkey_ss58,
|
889
882
|
destination_netuid=destination_netuid,
|
890
|
-
destination_coldkey_ss58=wallet.coldkeypub.ss58_address,
|
891
883
|
amount=amount_to_swap.rao,
|
892
884
|
),
|
893
885
|
subtensor.get_extrinsic_fee(call, wallet.coldkeypub),
|
@@ -903,7 +895,9 @@ async def swap_stake(
|
|
903
895
|
origin_hotkey=hotkey_ss58,
|
904
896
|
destination_hotkey=hotkey_ss58,
|
905
897
|
amount_to_move=amount_to_swap,
|
906
|
-
stake_fee=
|
898
|
+
stake_fee=sim_swap.alpha_fee
|
899
|
+
if origin_netuid != 0
|
900
|
+
else sim_swap.tao_fee,
|
907
901
|
extrinsic_fee=extrinsic_fee,
|
908
902
|
)
|
909
903
|
except ValueError:
|
@@ -200,16 +200,6 @@ async def unstake(
|
|
200
200
|
)
|
201
201
|
continue # Skip to the next subnet - useful when single amount is specified for all subnets
|
202
202
|
|
203
|
-
stake_fee = await subtensor.get_stake_fee(
|
204
|
-
origin_hotkey_ss58=staking_address_ss58,
|
205
|
-
origin_netuid=netuid,
|
206
|
-
origin_coldkey_ss58=wallet.coldkeypub.ss58_address,
|
207
|
-
destination_hotkey_ss58=None,
|
208
|
-
destination_netuid=None,
|
209
|
-
destination_coldkey_ss58=wallet.coldkeypub.ss58_address,
|
210
|
-
amount=amount_to_unstake_as_balance.rao,
|
211
|
-
)
|
212
|
-
|
213
203
|
try:
|
214
204
|
current_price = subnet_info.price.tao
|
215
205
|
if safe_staking:
|
@@ -240,10 +230,10 @@ async def unstake(
|
|
240
230
|
netuid=netuid,
|
241
231
|
amount=amount_to_unstake_as_balance,
|
242
232
|
)
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
233
|
+
sim_swap = await subtensor.sim_swap(
|
234
|
+
netuid, 0, amount_to_unstake_as_balance.rao
|
235
|
+
)
|
236
|
+
received_amount = sim_swap.tao_amount - extrinsic_fee
|
247
237
|
except ValueError:
|
248
238
|
continue
|
249
239
|
total_received_amount += received_amount
|
@@ -266,7 +256,7 @@ async def unstake(
|
|
266
256
|
str(amount_to_unstake_as_balance), # Amount to Unstake
|
267
257
|
f"{subnet_info.price.tao:.6f}"
|
268
258
|
+ f"(τ/{Balance.get_unit(netuid)})", # Rate
|
269
|
-
str(
|
259
|
+
str(sim_swap.alpha_fee), # Fee
|
270
260
|
str(extrinsic_fee), # Extrinsic fee
|
271
261
|
str(received_amount), # Received Amount
|
272
262
|
# slippage_pct, # Slippage Percent
|
@@ -361,6 +351,7 @@ async def unstake(
|
|
361
351
|
)
|
362
352
|
if json_output:
|
363
353
|
json_console.print(json.dumps(successes))
|
354
|
+
return True
|
364
355
|
|
365
356
|
|
366
357
|
async def unstake_all(
|
@@ -493,15 +484,6 @@ async def unstake_all(
|
|
493
484
|
hotkey_display = hotkey_names.get(stake.hotkey_ss58, stake.hotkey_ss58)
|
494
485
|
subnet_info = all_sn_dynamic_info.get(stake.netuid)
|
495
486
|
stake_amount = stake.stake
|
496
|
-
stake_fee = await subtensor.get_stake_fee(
|
497
|
-
origin_hotkey_ss58=stake.hotkey_ss58,
|
498
|
-
origin_netuid=stake.netuid,
|
499
|
-
origin_coldkey_ss58=wallet.coldkeypub.ss58_address,
|
500
|
-
destination_hotkey_ss58=None,
|
501
|
-
destination_netuid=None,
|
502
|
-
destination_coldkey_ss58=wallet.coldkeypub.ss58_address,
|
503
|
-
amount=stake_amount.rao,
|
504
|
-
)
|
505
487
|
|
506
488
|
try:
|
507
489
|
current_price = subnet_info.price.tao
|
@@ -514,8 +496,8 @@ async def unstake_all(
|
|
514
496
|
subtensor,
|
515
497
|
hotkey_ss58=stake.hotkey_ss58,
|
516
498
|
)
|
517
|
-
|
518
|
-
received_amount =
|
499
|
+
sim_swap = await subtensor.sim_swap(stake.netuid, 0, stake_amount.rao)
|
500
|
+
received_amount = sim_swap.tao_amount - extrinsic_fee
|
519
501
|
|
520
502
|
if received_amount < Balance.from_tao(0):
|
521
503
|
print_error("Not enough Alpha to pay the transaction fee.")
|
@@ -531,7 +513,7 @@ async def unstake_all(
|
|
531
513
|
str(stake_amount),
|
532
514
|
f"{float(subnet_info.price):.6f}"
|
533
515
|
+ f"({Balance.get_unit(0)}/{Balance.get_unit(stake.netuid)})",
|
534
|
-
str(
|
516
|
+
str(sim_swap.alpha_fee),
|
535
517
|
str(extrinsic_fee),
|
536
518
|
str(received_amount),
|
537
519
|
)
|
@@ -52,7 +52,17 @@ async def price(
|
|
52
52
|
|
53
53
|
step = 300
|
54
54
|
start_block = max(0, current_block - total_blocks)
|
55
|
-
|
55
|
+
|
56
|
+
# snap start block down to nearest multiple of 10
|
57
|
+
start_block -= start_block % 10
|
58
|
+
|
59
|
+
block_numbers = []
|
60
|
+
for b in range(start_block, current_block + 1, step):
|
61
|
+
if b == current_block:
|
62
|
+
block_numbers.append(b) # exact current block
|
63
|
+
else:
|
64
|
+
block_numbers.append(b - (b % 5)) # snap down to multiple of 10
|
65
|
+
block_numbers = sorted(set(block_numbers))
|
56
66
|
|
57
67
|
# Block hashes
|
58
68
|
block_hash_cors = [
|
@@ -2448,3 +2448,71 @@ async def start_subnet(
|
|
2448
2448
|
await get_start_schedule(subtensor, netuid)
|
2449
2449
|
print_error(f":cross_mark: Failed to start subnet: {error_msg}")
|
2450
2450
|
return False
|
2451
|
+
|
2452
|
+
|
2453
|
+
async def set_symbol(
|
2454
|
+
wallet: "Wallet",
|
2455
|
+
subtensor: "SubtensorInterface",
|
2456
|
+
netuid: int,
|
2457
|
+
symbol: str,
|
2458
|
+
prompt: bool = False,
|
2459
|
+
json_output: bool = False,
|
2460
|
+
) -> bool:
|
2461
|
+
"""
|
2462
|
+
Set a subtensor's symbol, given the netuid and symbol.
|
2463
|
+
|
2464
|
+
The symbol must be a symbol that subtensor recognizes as available
|
2465
|
+
(defined in https://github.com/opentensor/subtensor/blob/main/pallets/subtensor/src/subnets/symbols.rs#L8)
|
2466
|
+
"""
|
2467
|
+
if not await subtensor.subnet_exists(netuid):
|
2468
|
+
err = f"Subnet {netuid} does not exist."
|
2469
|
+
if json_output:
|
2470
|
+
json_console.print_json(data={"success": False, "message": err})
|
2471
|
+
else:
|
2472
|
+
err_console.print(err)
|
2473
|
+
return False
|
2474
|
+
|
2475
|
+
if prompt and not json_output:
|
2476
|
+
sn_info = await subtensor.subnet(netuid=netuid)
|
2477
|
+
if not Confirm.ask(
|
2478
|
+
f"Your current subnet symbol for SN{netuid} is {sn_info.symbol}. Do you want to update it to {symbol}?"
|
2479
|
+
):
|
2480
|
+
return False
|
2481
|
+
|
2482
|
+
if not (unlock_status := unlock_key(wallet, print_out=False)).success:
|
2483
|
+
err = unlock_status.message
|
2484
|
+
if json_output:
|
2485
|
+
json_console.print_json(data={"success": False, "message": err})
|
2486
|
+
else:
|
2487
|
+
console.print(err)
|
2488
|
+
return False
|
2489
|
+
|
2490
|
+
start_call = await subtensor.substrate.compose_call(
|
2491
|
+
call_module="SubtensorModule",
|
2492
|
+
call_function="update_symbol",
|
2493
|
+
call_params={"netuid": netuid, "symbol": symbol.encode("utf-8")},
|
2494
|
+
)
|
2495
|
+
|
2496
|
+
signed_ext = await subtensor.substrate.create_signed_extrinsic(
|
2497
|
+
call=start_call,
|
2498
|
+
keypair=wallet.coldkey,
|
2499
|
+
)
|
2500
|
+
|
2501
|
+
response = await subtensor.substrate.submit_extrinsic(
|
2502
|
+
extrinsic=signed_ext,
|
2503
|
+
wait_for_inclusion=True,
|
2504
|
+
)
|
2505
|
+
if await response.is_success:
|
2506
|
+
message = f"Successfully updated SN{netuid}'s symbol to {symbol}."
|
2507
|
+
if json_output:
|
2508
|
+
json_console.print_json(data={"success": True, "message": message})
|
2509
|
+
else:
|
2510
|
+
console.print(f":white_heavy_check_mark:[dark_sea_green3] {message}\n")
|
2511
|
+
return True
|
2512
|
+
else:
|
2513
|
+
err = format_error_message(await response.error_message)
|
2514
|
+
if json_output:
|
2515
|
+
json_console.print_json(data={"success": False, "message": err})
|
2516
|
+
else:
|
2517
|
+
err_console.print(f":cross_mark: [red]Failed[/red]: {err}")
|
2518
|
+
return False
|
@@ -34,6 +34,7 @@ if TYPE_CHECKING:
|
|
34
34
|
SubtensorInterface,
|
35
35
|
ProposalVoteData,
|
36
36
|
)
|
37
|
+
from scalecodec.types import GenericMetadataVersioned
|
37
38
|
|
38
39
|
|
39
40
|
# helpers and extrinsics
|
@@ -91,8 +92,8 @@ def search_metadata(
|
|
91
92
|
param_name: str,
|
92
93
|
value: Union[str, bool, float, list[float]],
|
93
94
|
netuid: int,
|
94
|
-
metadata,
|
95
|
-
|
95
|
+
metadata: "GenericMetadataVersioned",
|
96
|
+
pallet_name: str = DEFAULT_PALLET,
|
96
97
|
) -> tuple[bool, Optional[dict]]:
|
97
98
|
"""
|
98
99
|
Searches the substrate metadata AdminUtils pallet for a given parameter name. Crafts a response dict to be used
|
@@ -103,7 +104,7 @@ def search_metadata(
|
|
103
104
|
value: the value to set the hyperparameter
|
104
105
|
netuid: the specified netuid
|
105
106
|
metadata: the subtensor.substrate.metadata
|
106
|
-
|
107
|
+
pallet_name: the name of the module to use for the query. If not set, the default value is DEFAULT_PALLET
|
107
108
|
|
108
109
|
Returns:
|
109
110
|
(success, dict of call params)
|
@@ -125,7 +126,7 @@ def search_metadata(
|
|
125
126
|
|
126
127
|
call_crafter = {"netuid": netuid}
|
127
128
|
|
128
|
-
pallet = metadata.get_metadata_pallet(
|
129
|
+
pallet = metadata.get_metadata_pallet(pallet_name)
|
129
130
|
for call in pallet.calls:
|
130
131
|
if call.name == param_name:
|
131
132
|
if "netuid" not in [x.name for x in call.args]:
|