@@ -19,6 +19,9 @@ def unstake_extrinsic(
19
19
amount : Optional [Balance ] = None ,
20
20
wait_for_inclusion : bool = True ,
21
21
wait_for_finalization : bool = False ,
22
+ safe_staking : bool = False ,
23
+ allow_partial_stake : bool = False ,
24
+ rate_threshold : float = 0.005 ,
22
25
) -> bool :
23
26
"""Removes stake into the wallet coldkey from the specified hotkey ``uid``.
24
27
@@ -33,6 +36,9 @@ def unstake_extrinsic(
33
36
returns ``False`` if the extrinsic fails to enter the block within the timeout.
34
37
wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning
35
38
``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%)
36
42
37
43
Returns:
38
44
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(
79
85
return False
80
86
81
87
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
+
94
132
staking_response , err_msg = subtensor .sign_and_send_extrinsic (
95
133
call ,
96
134
wallet ,
@@ -130,7 +168,12 @@ def unstake_extrinsic(
130
168
)
131
169
return True
132
170
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]" )
134
177
return False
135
178
136
179
except NotRegisteredError :
0 commit comments