Skip to content

Commit d33a4e1

Browse files
committed
adds safe unstaking
1 parent 1e76bf2 commit d33a4e1

File tree

1 file changed

+56
-13
lines changed

1 file changed

+56
-13
lines changed

bittensor/core/extrinsics/unstaking.py

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ def unstake_extrinsic(
1919
amount: Optional[Balance] = None,
2020
wait_for_inclusion: bool = True,
2121
wait_for_finalization: bool = False,
22+
safe_staking: bool = False,
23+
allow_partial_stake: bool = False,
24+
rate_threshold: float = 0.005,
2225
) -> bool:
2326
"""Removes stake into the wallet coldkey from the specified hotkey ``uid``.
2427
@@ -33,6 +36,9 @@ def unstake_extrinsic(
3336
returns ``False`` if the extrinsic fails to enter the block within the timeout.
3437
wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning
3538
``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout.
39+
safe_staking: If true, enables price safety checks
40+
allow_partial_stake: If true, allows partial unstaking if price threshold exceeded
41+
rate_threshold: Maximum allowed price decrease percentage (0.005 = 0.5%)
3642
3743
Returns:
3844
success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for
@@ -79,18 +85,50 @@ def unstake_extrinsic(
7985
return False
8086

8187
try:
82-
logging.info(
83-
f"Unstaking [blue]{unstaking_balance}[/blue] from [magenta]{hotkey_ss58}[/magenta] on [blue]{netuid}[/blue]"
84-
)
85-
call = subtensor.substrate.compose_call(
86-
call_module="SubtensorModule",
87-
call_function="remove_stake",
88-
call_params={
89-
"hotkey": hotkey_ss58,
90-
"amount_unstaked": unstaking_balance.rao,
91-
"netuid": netuid,
92-
},
93-
)
88+
if safe_staking:
89+
pool = subtensor.subnet(netuid=netuid)
90+
base_price = pool.price.rao
91+
price_with_tolerance = base_price * (1 - rate_threshold)
92+
93+
# For logging
94+
base_rate = pool.price.tao
95+
rate_with_tolerance = base_rate * (1 - rate_threshold)
96+
97+
logging.info(
98+
f":satellite: [magenta]Safe Unstaking from:[/magenta] "
99+
f"[blue]netuid: {netuid}, amount: {unstaking_balance}, tolerance percentage: {rate_threshold*100}%, "
100+
f"price limit: {rate_with_tolerance}, original price: {base_rate}, with partial unstake: {allow_partial_stake} "
101+
f"on {subtensor.network}[/blue] [magenta]...[/magenta]"
102+
)
103+
104+
call = subtensor.substrate.compose_call(
105+
call_module="SubtensorModule",
106+
call_function="remove_stake_limit",
107+
call_params={
108+
"hotkey": hotkey_ss58,
109+
"netuid": netuid,
110+
"amount_unstaked": unstaking_balance.rao,
111+
"limit_price": price_with_tolerance,
112+
"allow_partial": allow_partial_stake,
113+
},
114+
)
115+
else:
116+
logging.info(
117+
f":satellite: [magenta]Unstaking from:[/magenta] "
118+
f"[blue]netuid: {netuid}, amount: {unstaking_balance} "
119+
f"on {subtensor.network}[/blue] [magenta]...[/magenta]"
120+
)
121+
122+
call = subtensor.substrate.compose_call(
123+
call_module="SubtensorModule",
124+
call_function="remove_stake",
125+
call_params={
126+
"hotkey": hotkey_ss58,
127+
"netuid": netuid,
128+
"amount_unstaked": unstaking_balance.rao,
129+
},
130+
)
131+
94132
staking_response, err_msg = subtensor.sign_and_send_extrinsic(
95133
call,
96134
wallet,
@@ -130,7 +168,12 @@ def unstake_extrinsic(
130168
)
131169
return True
132170
else:
133-
logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]")
171+
if safe_staking and "Custom error: 8" in err_msg:
172+
logging.error(
173+
":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking."
174+
)
175+
else:
176+
logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]")
134177
return False
135178

136179
except NotRegisteredError:

0 commit comments

Comments
 (0)