bittensor-cli 9.7.1__py3-none-any.whl → 9.8.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 +340 -24
- bittensor_cli/src/__init__.py +8 -0
- bittensor_cli/src/bittensor/chain_data.py +81 -33
- bittensor_cli/src/bittensor/subtensor_interface.py +111 -26
- bittensor_cli/src/bittensor/utils.py +9 -0
- bittensor_cli/src/commands/liquidity/__init__.py +0 -0
- bittensor_cli/src/commands/liquidity/liquidity.py +628 -0
- bittensor_cli/src/commands/liquidity/utils.py +200 -0
- bittensor_cli/src/commands/stake/add.py +34 -22
- bittensor_cli/src/commands/stake/list.py +35 -48
- bittensor_cli/src/commands/stake/move.py +18 -44
- bittensor_cli/src/commands/stake/remove.py +33 -95
- bittensor_cli/src/commands/subnets/subnets.py +6 -0
- bittensor_cli/src/commands/sudo.py +21 -8
- bittensor_cli/src/commands/wallets.py +20 -48
- {bittensor_cli-9.7.1.dist-info → bittensor_cli-9.8.1.dist-info}/METADATA +1 -1
- {bittensor_cli-9.7.1.dist-info → bittensor_cli-9.8.1.dist-info}/RECORD +20 -17
- {bittensor_cli-9.7.1.dist-info → bittensor_cli-9.8.1.dist-info}/WHEEL +0 -0
- {bittensor_cli-9.7.1.dist-info → bittensor_cli-9.8.1.dist-info}/entry_points.txt +0 -0
- {bittensor_cli-9.7.1.dist-info → bittensor_cli-9.8.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,628 @@
|
|
1
|
+
import asyncio
|
2
|
+
import json
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
4
|
+
|
5
|
+
from rich.prompt import Confirm
|
6
|
+
from rich.table import Column, Table
|
7
|
+
|
8
|
+
from bittensor_cli.src import COLORS
|
9
|
+
from bittensor_cli.src.bittensor.utils import (
|
10
|
+
unlock_key,
|
11
|
+
console,
|
12
|
+
err_console,
|
13
|
+
json_console,
|
14
|
+
)
|
15
|
+
from bittensor_cli.src.bittensor.balances import Balance, fixed_to_float
|
16
|
+
from bittensor_cli.src.commands.liquidity.utils import (
|
17
|
+
LiquidityPosition,
|
18
|
+
calculate_fees,
|
19
|
+
get_fees,
|
20
|
+
price_to_tick,
|
21
|
+
tick_to_price,
|
22
|
+
)
|
23
|
+
|
24
|
+
if TYPE_CHECKING:
|
25
|
+
from bittensor_wallet import Wallet
|
26
|
+
from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
|
27
|
+
|
28
|
+
|
29
|
+
async def add_liquidity_extrinsic(
|
30
|
+
subtensor: "SubtensorInterface",
|
31
|
+
wallet: "Wallet",
|
32
|
+
hotkey_ss58: str,
|
33
|
+
netuid: int,
|
34
|
+
liquidity: Balance,
|
35
|
+
price_low: Balance,
|
36
|
+
price_high: Balance,
|
37
|
+
wait_for_inclusion: bool = True,
|
38
|
+
wait_for_finalization: bool = False,
|
39
|
+
) -> tuple[bool, str]:
|
40
|
+
"""
|
41
|
+
Adds liquidity to the specified price range.
|
42
|
+
|
43
|
+
Arguments:
|
44
|
+
subtensor: The Subtensor client instance used for blockchain interaction.
|
45
|
+
wallet: The wallet used to sign the extrinsic (must be unlocked).
|
46
|
+
hotkey_ss58: the SS58 of the hotkey to use for this transaction.
|
47
|
+
netuid: The UID of the target subnet for which the call is being initiated.
|
48
|
+
liquidity: The amount of liquidity to be added.
|
49
|
+
price_low: The lower bound of the price tick range.
|
50
|
+
price_high: The upper bound of the price tick range.
|
51
|
+
wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True.
|
52
|
+
wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False.
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
Tuple[bool, str]:
|
56
|
+
- True and a success message if the extrinsic is successfully submitted or processed.
|
57
|
+
- False and an error message if the submission fails or the wallet cannot be unlocked.
|
58
|
+
|
59
|
+
Note: Adding is allowed even when user liquidity is enabled in specified subnet. Call
|
60
|
+
`toggle_user_liquidity_extrinsic` to enable/disable user liquidity.
|
61
|
+
"""
|
62
|
+
if not (unlock := unlock_key(wallet)).success:
|
63
|
+
return False, unlock.message
|
64
|
+
|
65
|
+
tick_low = price_to_tick(price_low.tao)
|
66
|
+
tick_high = price_to_tick(price_high.tao)
|
67
|
+
|
68
|
+
call = await subtensor.substrate.compose_call(
|
69
|
+
call_module="Swap",
|
70
|
+
call_function="add_liquidity",
|
71
|
+
call_params={
|
72
|
+
"hotkey": hotkey_ss58,
|
73
|
+
"netuid": netuid,
|
74
|
+
"tick_low": tick_low,
|
75
|
+
"tick_high": tick_high,
|
76
|
+
"liquidity": liquidity.rao,
|
77
|
+
},
|
78
|
+
)
|
79
|
+
|
80
|
+
return await subtensor.sign_and_send_extrinsic(
|
81
|
+
call=call,
|
82
|
+
wallet=wallet,
|
83
|
+
wait_for_inclusion=wait_for_inclusion,
|
84
|
+
wait_for_finalization=wait_for_finalization,
|
85
|
+
)
|
86
|
+
|
87
|
+
|
88
|
+
async def modify_liquidity_extrinsic(
|
89
|
+
subtensor: "SubtensorInterface",
|
90
|
+
wallet: "Wallet",
|
91
|
+
hotkey_ss58: str,
|
92
|
+
netuid: int,
|
93
|
+
position_id: int,
|
94
|
+
liquidity_delta: Balance,
|
95
|
+
wait_for_inclusion: bool = True,
|
96
|
+
wait_for_finalization: bool = False,
|
97
|
+
) -> tuple[bool, str]:
|
98
|
+
"""Modifies liquidity in liquidity position by adding or removing liquidity from it.
|
99
|
+
|
100
|
+
Arguments:
|
101
|
+
subtensor: The Subtensor client instance used for blockchain interaction.
|
102
|
+
wallet: The wallet used to sign the extrinsic (must be unlocked).
|
103
|
+
hotkey_ss58: the SS58 of the hotkey to use for this transaction.
|
104
|
+
netuid: The UID of the target subnet for which the call is being initiated.
|
105
|
+
position_id: The id of the position record in the pool.
|
106
|
+
liquidity_delta: The amount of liquidity to be added or removed (add if positive or remove if negative).
|
107
|
+
wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True.
|
108
|
+
wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False.
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
Tuple[bool, str]:
|
112
|
+
- True and a success message if the extrinsic is successfully submitted or processed.
|
113
|
+
- False and an error message if the submission fails or the wallet cannot be unlocked.
|
114
|
+
|
115
|
+
Note: Modifying is allowed even when user liquidity is enabled in specified subnet.
|
116
|
+
Call `toggle_user_liquidity_extrinsic` to enable/disable user liquidity.
|
117
|
+
"""
|
118
|
+
if not (unlock := unlock_key(wallet)).success:
|
119
|
+
return False, unlock.message
|
120
|
+
|
121
|
+
call = await subtensor.substrate.compose_call(
|
122
|
+
call_module="Swap",
|
123
|
+
call_function="modify_position",
|
124
|
+
call_params={
|
125
|
+
"hotkey": hotkey_ss58,
|
126
|
+
"netuid": netuid,
|
127
|
+
"position_id": position_id,
|
128
|
+
"liquidity_delta": liquidity_delta.rao,
|
129
|
+
},
|
130
|
+
)
|
131
|
+
|
132
|
+
return await subtensor.sign_and_send_extrinsic(
|
133
|
+
call=call,
|
134
|
+
wallet=wallet,
|
135
|
+
wait_for_inclusion=wait_for_inclusion,
|
136
|
+
wait_for_finalization=wait_for_finalization,
|
137
|
+
)
|
138
|
+
|
139
|
+
|
140
|
+
async def remove_liquidity_extrinsic(
|
141
|
+
subtensor: "SubtensorInterface",
|
142
|
+
wallet: "Wallet",
|
143
|
+
hotkey_ss58: str,
|
144
|
+
netuid: int,
|
145
|
+
position_id: int,
|
146
|
+
wait_for_inclusion: bool = True,
|
147
|
+
wait_for_finalization: bool = False,
|
148
|
+
) -> tuple[bool, str]:
|
149
|
+
"""Remove liquidity and credit balances back to wallet's hotkey stake.
|
150
|
+
|
151
|
+
Arguments:
|
152
|
+
subtensor: The Subtensor client instance used for blockchain interaction.
|
153
|
+
wallet: The wallet used to sign the extrinsic (must be unlocked).
|
154
|
+
hotkey_ss58: the SS58 of the hotkey to use for this transaction.
|
155
|
+
netuid: The UID of the target subnet for which the call is being initiated.
|
156
|
+
position_id: The id of the position record in the pool.
|
157
|
+
wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True.
|
158
|
+
wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False.
|
159
|
+
|
160
|
+
Returns:
|
161
|
+
Tuple[bool, str]:
|
162
|
+
- True and a success message if the extrinsic is successfully submitted or processed.
|
163
|
+
- False and an error message if the submission fails or the wallet cannot be unlocked.
|
164
|
+
|
165
|
+
Note: Adding is allowed even when user liquidity is enabled in specified subnet.
|
166
|
+
Call `toggle_user_liquidity_extrinsic` to enable/disable user liquidity.
|
167
|
+
"""
|
168
|
+
if not (unlock := unlock_key(wallet)).success:
|
169
|
+
return False, unlock.message
|
170
|
+
|
171
|
+
call = await subtensor.substrate.compose_call(
|
172
|
+
call_module="Swap",
|
173
|
+
call_function="remove_liquidity",
|
174
|
+
call_params={
|
175
|
+
"hotkey": hotkey_ss58,
|
176
|
+
"netuid": netuid,
|
177
|
+
"position_id": position_id,
|
178
|
+
},
|
179
|
+
)
|
180
|
+
|
181
|
+
return await subtensor.sign_and_send_extrinsic(
|
182
|
+
call=call,
|
183
|
+
wallet=wallet,
|
184
|
+
wait_for_inclusion=wait_for_inclusion,
|
185
|
+
wait_for_finalization=wait_for_finalization,
|
186
|
+
)
|
187
|
+
|
188
|
+
|
189
|
+
async def toggle_user_liquidity_extrinsic(
|
190
|
+
subtensor: "SubtensorInterface",
|
191
|
+
wallet: "Wallet",
|
192
|
+
netuid: int,
|
193
|
+
enable: bool,
|
194
|
+
wait_for_inclusion: bool = True,
|
195
|
+
wait_for_finalization: bool = False,
|
196
|
+
) -> tuple[bool, str]:
|
197
|
+
"""Allow to toggle user liquidity for specified subnet.
|
198
|
+
|
199
|
+
Arguments:
|
200
|
+
subtensor: The Subtensor client instance used for blockchain interaction.
|
201
|
+
wallet: The wallet used to sign the extrinsic (must be unlocked).
|
202
|
+
netuid: The UID of the target subnet for which the call is being initiated.
|
203
|
+
enable: Boolean indicating whether to enable user liquidity.
|
204
|
+
wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True.
|
205
|
+
wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False.
|
206
|
+
|
207
|
+
Returns:
|
208
|
+
Tuple[bool, str]:
|
209
|
+
- True and a success message if the extrinsic is successfully submitted or processed.
|
210
|
+
- False and an error message if the submission fails or the wallet cannot be unlocked.
|
211
|
+
"""
|
212
|
+
if not (unlock := unlock_key(wallet)).success:
|
213
|
+
return False, unlock.message
|
214
|
+
|
215
|
+
call = await subtensor.substrate.compose_call(
|
216
|
+
call_module="Swap",
|
217
|
+
call_function="toggle_user_liquidity",
|
218
|
+
call_params={"netuid": netuid, "enable": enable},
|
219
|
+
)
|
220
|
+
|
221
|
+
return await subtensor.sign_and_send_extrinsic(
|
222
|
+
call=call,
|
223
|
+
wallet=wallet,
|
224
|
+
wait_for_inclusion=wait_for_inclusion,
|
225
|
+
wait_for_finalization=wait_for_finalization,
|
226
|
+
)
|
227
|
+
|
228
|
+
|
229
|
+
# Command
|
230
|
+
async def add_liquidity(
|
231
|
+
subtensor: "SubtensorInterface",
|
232
|
+
wallet: "Wallet",
|
233
|
+
hotkey_ss58: str,
|
234
|
+
netuid: Optional[int],
|
235
|
+
liquidity: Optional[float],
|
236
|
+
price_low: Optional[float],
|
237
|
+
price_high: Optional[float],
|
238
|
+
prompt: bool,
|
239
|
+
json_output: bool,
|
240
|
+
) -> tuple[bool, str]:
|
241
|
+
"""Add liquidity position to provided subnet."""
|
242
|
+
# Check wallet access
|
243
|
+
if not unlock_key(wallet).success:
|
244
|
+
return False
|
245
|
+
|
246
|
+
# Check that the subnet exists.
|
247
|
+
if not await subtensor.subnet_exists(netuid=netuid):
|
248
|
+
return False, f"Subnet with netuid: {netuid} does not exist in {subtensor}."
|
249
|
+
|
250
|
+
if prompt:
|
251
|
+
console.print(
|
252
|
+
"You are about to add a LiquidityPosition with:\n"
|
253
|
+
f"\tliquidity: {liquidity}\n"
|
254
|
+
f"\tprice low: {price_low}\n"
|
255
|
+
f"\tprice high: {price_high}\n"
|
256
|
+
f"\tto SN: {netuid}\n"
|
257
|
+
f"\tusing wallet with name: {wallet.name}"
|
258
|
+
)
|
259
|
+
|
260
|
+
if not Confirm.ask("Would you like to continue?"):
|
261
|
+
return False, "User cancelled operation."
|
262
|
+
|
263
|
+
success, message = await add_liquidity_extrinsic(
|
264
|
+
subtensor=subtensor,
|
265
|
+
wallet=wallet,
|
266
|
+
hotkey_ss58=hotkey_ss58,
|
267
|
+
netuid=netuid,
|
268
|
+
liquidity=liquidity,
|
269
|
+
price_low=price_low,
|
270
|
+
price_high=price_high,
|
271
|
+
)
|
272
|
+
if json_output:
|
273
|
+
json_console.print(json.dumps({"success": success, "message": message}))
|
274
|
+
else:
|
275
|
+
if success:
|
276
|
+
console.print(
|
277
|
+
"[green]LiquidityPosition has been successfully added.[/green]"
|
278
|
+
)
|
279
|
+
else:
|
280
|
+
err_console.print(f"[red]Error: {message}[/red]")
|
281
|
+
|
282
|
+
|
283
|
+
async def get_liquidity_list(
|
284
|
+
subtensor: "SubtensorInterface",
|
285
|
+
wallet: "Wallet",
|
286
|
+
netuid: Optional[int],
|
287
|
+
) -> tuple[bool, str, list]:
|
288
|
+
"""
|
289
|
+
Args:
|
290
|
+
wallet: wallet object
|
291
|
+
subtensor: SubtensorInterface object
|
292
|
+
netuid: the netuid to stake to (None indicates all subnets)
|
293
|
+
|
294
|
+
Returns:
|
295
|
+
Tuple of (success, error message, liquidity list)
|
296
|
+
"""
|
297
|
+
|
298
|
+
if not await subtensor.subnet_exists(netuid=netuid):
|
299
|
+
return False, f"Subnet with netuid: {netuid} does not exist in {subtensor}.", []
|
300
|
+
|
301
|
+
if not await subtensor.is_subnet_active(netuid=netuid):
|
302
|
+
return False, f"Subnet with netuid: {netuid} is not active in {subtensor}.", []
|
303
|
+
|
304
|
+
block_hash = await subtensor.substrate.get_chain_head()
|
305
|
+
(
|
306
|
+
positions_response,
|
307
|
+
fee_global_tao,
|
308
|
+
fee_global_alpha,
|
309
|
+
current_sqrt_price,
|
310
|
+
) = await asyncio.gather(
|
311
|
+
subtensor.substrate.query_map(
|
312
|
+
module="Swap",
|
313
|
+
storage_function="Positions",
|
314
|
+
params=[netuid, wallet.coldkeypub.ss58_address],
|
315
|
+
block_hash=block_hash,
|
316
|
+
),
|
317
|
+
subtensor.query(
|
318
|
+
module="Swap",
|
319
|
+
storage_function="FeeGlobalTao",
|
320
|
+
params=[netuid],
|
321
|
+
block_hash=block_hash,
|
322
|
+
),
|
323
|
+
subtensor.query(
|
324
|
+
module="Swap",
|
325
|
+
storage_function="FeeGlobalAlpha",
|
326
|
+
params=[netuid],
|
327
|
+
block_hash=block_hash,
|
328
|
+
),
|
329
|
+
subtensor.query(
|
330
|
+
module="Swap",
|
331
|
+
storage_function="AlphaSqrtPrice",
|
332
|
+
params=[netuid],
|
333
|
+
block_hash=block_hash,
|
334
|
+
),
|
335
|
+
)
|
336
|
+
|
337
|
+
current_sqrt_price = fixed_to_float(current_sqrt_price)
|
338
|
+
fee_global_tao = fixed_to_float(fee_global_tao)
|
339
|
+
fee_global_alpha = fixed_to_float(fee_global_alpha)
|
340
|
+
|
341
|
+
current_price = current_sqrt_price * current_sqrt_price
|
342
|
+
current_tick = price_to_tick(current_price)
|
343
|
+
|
344
|
+
preprocessed_positions = []
|
345
|
+
positions_futures = []
|
346
|
+
|
347
|
+
async for _, p in positions_response:
|
348
|
+
position = p.value
|
349
|
+
tick_index_low = position.get("tick_low")[0]
|
350
|
+
tick_index_high = position.get("tick_high")[0]
|
351
|
+
preprocessed_positions.append((position, tick_index_low, tick_index_high))
|
352
|
+
|
353
|
+
# Get ticks for the position (for below/above fees)
|
354
|
+
positions_futures.append(
|
355
|
+
asyncio.gather(
|
356
|
+
subtensor.query(
|
357
|
+
module="Swap",
|
358
|
+
storage_function="Ticks",
|
359
|
+
params=[netuid, tick_index_low],
|
360
|
+
block_hash=block_hash,
|
361
|
+
),
|
362
|
+
subtensor.query(
|
363
|
+
module="Swap",
|
364
|
+
storage_function="Ticks",
|
365
|
+
params=[netuid, tick_index_high],
|
366
|
+
block_hash=block_hash,
|
367
|
+
),
|
368
|
+
)
|
369
|
+
)
|
370
|
+
|
371
|
+
awaited_futures = await asyncio.gather(*positions_futures)
|
372
|
+
|
373
|
+
positions = []
|
374
|
+
|
375
|
+
for (position, tick_index_low, tick_index_high), (tick_low, tick_high) in zip(
|
376
|
+
preprocessed_positions, awaited_futures
|
377
|
+
):
|
378
|
+
tao_fees_below_low = get_fees(
|
379
|
+
current_tick=current_tick,
|
380
|
+
tick=tick_low,
|
381
|
+
tick_index=tick_index_low,
|
382
|
+
quote=True,
|
383
|
+
global_fees_tao=fee_global_tao,
|
384
|
+
global_fees_alpha=fee_global_alpha,
|
385
|
+
above=False,
|
386
|
+
)
|
387
|
+
tao_fees_above_high = get_fees(
|
388
|
+
current_tick=current_tick,
|
389
|
+
tick=tick_high,
|
390
|
+
tick_index=tick_index_high,
|
391
|
+
quote=True,
|
392
|
+
global_fees_tao=fee_global_tao,
|
393
|
+
global_fees_alpha=fee_global_alpha,
|
394
|
+
above=True,
|
395
|
+
)
|
396
|
+
alpha_fees_below_low = get_fees(
|
397
|
+
current_tick=current_tick,
|
398
|
+
tick=tick_low,
|
399
|
+
tick_index=tick_index_low,
|
400
|
+
quote=False,
|
401
|
+
global_fees_tao=fee_global_tao,
|
402
|
+
global_fees_alpha=fee_global_alpha,
|
403
|
+
above=False,
|
404
|
+
)
|
405
|
+
alpha_fees_above_high = get_fees(
|
406
|
+
current_tick=current_tick,
|
407
|
+
tick=tick_high,
|
408
|
+
tick_index=tick_index_high,
|
409
|
+
quote=False,
|
410
|
+
global_fees_tao=fee_global_tao,
|
411
|
+
global_fees_alpha=fee_global_alpha,
|
412
|
+
above=True,
|
413
|
+
)
|
414
|
+
|
415
|
+
# Get position accrued fees
|
416
|
+
fees_tao, fees_alpha = calculate_fees(
|
417
|
+
position=position,
|
418
|
+
global_fees_tao=fee_global_tao,
|
419
|
+
global_fees_alpha=fee_global_alpha,
|
420
|
+
tao_fees_below_low=tao_fees_below_low,
|
421
|
+
tao_fees_above_high=tao_fees_above_high,
|
422
|
+
alpha_fees_below_low=alpha_fees_below_low,
|
423
|
+
alpha_fees_above_high=alpha_fees_above_high,
|
424
|
+
netuid=netuid,
|
425
|
+
)
|
426
|
+
|
427
|
+
lp = LiquidityPosition(
|
428
|
+
**{
|
429
|
+
"id": position.get("id")[0],
|
430
|
+
"price_low": Balance.from_tao(
|
431
|
+
tick_to_price(position.get("tick_low")[0])
|
432
|
+
),
|
433
|
+
"price_high": Balance.from_tao(
|
434
|
+
tick_to_price(position.get("tick_high")[0])
|
435
|
+
),
|
436
|
+
"liquidity": Balance.from_rao(position.get("liquidity")),
|
437
|
+
"fees_tao": fees_tao,
|
438
|
+
"fees_alpha": fees_alpha,
|
439
|
+
"netuid": position.get("netuid"),
|
440
|
+
}
|
441
|
+
)
|
442
|
+
positions.append(lp)
|
443
|
+
|
444
|
+
return True, "", positions
|
445
|
+
|
446
|
+
|
447
|
+
async def show_liquidity_list(
|
448
|
+
subtensor: "SubtensorInterface",
|
449
|
+
wallet: "Wallet",
|
450
|
+
netuid: int,
|
451
|
+
json_output: bool = False,
|
452
|
+
):
|
453
|
+
current_price_, (success, err_msg, positions) = await asyncio.gather(
|
454
|
+
subtensor.subnet(netuid=netuid), get_liquidity_list(subtensor, wallet, netuid)
|
455
|
+
)
|
456
|
+
if not success:
|
457
|
+
if json_output:
|
458
|
+
json_console.print(
|
459
|
+
json.dumps({"success": success, "err_msg": err_msg, "positions": []})
|
460
|
+
)
|
461
|
+
return False
|
462
|
+
else:
|
463
|
+
err_console.print(f"Error: {err_msg}")
|
464
|
+
return False
|
465
|
+
liquidity_table = Table(
|
466
|
+
Column("ID", justify="center"),
|
467
|
+
Column("Liquidity", justify="center"),
|
468
|
+
Column("Alpha", justify="center"),
|
469
|
+
Column("Tao", justify="center"),
|
470
|
+
Column("Price low", justify="center"),
|
471
|
+
Column("Price high", justify="center"),
|
472
|
+
Column("Fee TAO", justify="center"),
|
473
|
+
Column("Fee Alpha", justify="center"),
|
474
|
+
title=f"\n[{COLORS.G.HEADER}]{'Liquidity Positions of '}{wallet.name} wallet in SN #{netuid}\n"
|
475
|
+
"Alpha and Tao columns are respective portions of liquidity.",
|
476
|
+
show_footer=False,
|
477
|
+
show_edge=True,
|
478
|
+
header_style="bold white",
|
479
|
+
border_style="bright_black",
|
480
|
+
style="bold",
|
481
|
+
title_justify="center",
|
482
|
+
show_lines=False,
|
483
|
+
pad_edge=True,
|
484
|
+
)
|
485
|
+
json_table = []
|
486
|
+
current_price = current_price_.price
|
487
|
+
lp: LiquidityPosition
|
488
|
+
for lp in positions:
|
489
|
+
alpha, tao = lp.to_token_amounts(current_price)
|
490
|
+
liquidity_table.add_row(
|
491
|
+
str(lp.id),
|
492
|
+
str(lp.liquidity.tao),
|
493
|
+
str(alpha),
|
494
|
+
str(tao),
|
495
|
+
str(lp.price_low),
|
496
|
+
str(lp.price_high),
|
497
|
+
str(lp.fees_tao),
|
498
|
+
str(lp.fees_alpha),
|
499
|
+
)
|
500
|
+
json_table.append(
|
501
|
+
{
|
502
|
+
"id": lp.id,
|
503
|
+
"liquidity": lp.liquidity.tao,
|
504
|
+
"token_amounts": {"alpha": alpha.tao, "tao": tao.tao},
|
505
|
+
"price_low": lp.price_low.tao,
|
506
|
+
"price_high": lp.price_high.tao,
|
507
|
+
"fees_tao": lp.fees_tao.tao,
|
508
|
+
"fees_alpha": lp.fees_alpha.tao,
|
509
|
+
"netuid": lp.netuid,
|
510
|
+
}
|
511
|
+
)
|
512
|
+
if not json_output:
|
513
|
+
console.print(liquidity_table)
|
514
|
+
else:
|
515
|
+
json_console.print(
|
516
|
+
json.dumps({"success": True, "err_msg": "", "positions": json_table})
|
517
|
+
)
|
518
|
+
|
519
|
+
|
520
|
+
async def remove_liquidity(
|
521
|
+
subtensor: "SubtensorInterface",
|
522
|
+
wallet: "Wallet",
|
523
|
+
hotkey_ss58: str,
|
524
|
+
netuid: int,
|
525
|
+
position_id: Optional[int] = None,
|
526
|
+
prompt: Optional[bool] = None,
|
527
|
+
all_liquidity_ids: Optional[bool] = None,
|
528
|
+
json_output: bool = False,
|
529
|
+
) -> tuple[bool, str]:
|
530
|
+
"""Remove liquidity position from provided subnet."""
|
531
|
+
if not await subtensor.subnet_exists(netuid=netuid):
|
532
|
+
return False, f"Subnet with netuid: {netuid} does not exist in {subtensor}."
|
533
|
+
|
534
|
+
if all_liquidity_ids:
|
535
|
+
success, msg, positions = await get_liquidity_list(subtensor, wallet, netuid)
|
536
|
+
if not success:
|
537
|
+
if json_output:
|
538
|
+
return json_console.print(
|
539
|
+
{"success": False, "err_msg": msg, "positions": positions}
|
540
|
+
)
|
541
|
+
else:
|
542
|
+
return err_console.print(f"Error: {msg}")
|
543
|
+
else:
|
544
|
+
position_ids = [p.id for p in positions]
|
545
|
+
else:
|
546
|
+
position_ids = [position_id]
|
547
|
+
|
548
|
+
if prompt:
|
549
|
+
console.print("You are about to remove LiquidityPositions with:")
|
550
|
+
console.print(f"\tSubnet: {netuid}")
|
551
|
+
console.print(f"\tWallet name: {wallet.name}")
|
552
|
+
for pos in position_ids:
|
553
|
+
console.print(f"\tPosition id: {pos}")
|
554
|
+
|
555
|
+
if not Confirm.ask("Would you like to continue?"):
|
556
|
+
return False, "User cancelled operation."
|
557
|
+
|
558
|
+
results = await asyncio.gather(
|
559
|
+
*[
|
560
|
+
remove_liquidity_extrinsic(
|
561
|
+
subtensor=subtensor,
|
562
|
+
wallet=wallet,
|
563
|
+
hotkey_ss58=hotkey_ss58,
|
564
|
+
netuid=netuid,
|
565
|
+
position_id=pos_id,
|
566
|
+
)
|
567
|
+
for pos_id in position_ids
|
568
|
+
]
|
569
|
+
)
|
570
|
+
if not json_output:
|
571
|
+
for (success, msg), posid in zip(results, position_ids):
|
572
|
+
if success:
|
573
|
+
console.print(f"[green] Position {posid} has been removed.")
|
574
|
+
else:
|
575
|
+
err_console.print(f"[red] Error removing {posid}: {msg}")
|
576
|
+
else:
|
577
|
+
json_table = {}
|
578
|
+
for (success, msg), posid in zip(results, position_ids):
|
579
|
+
json_table[posid] = {"success": success, "err_msg": msg}
|
580
|
+
json_console.print(json.dumps(json_table))
|
581
|
+
|
582
|
+
|
583
|
+
async def modify_liquidity(
|
584
|
+
subtensor: "SubtensorInterface",
|
585
|
+
wallet: "Wallet",
|
586
|
+
hotkey_ss58: str,
|
587
|
+
netuid: int,
|
588
|
+
position_id: int,
|
589
|
+
liquidity_delta: Optional[float],
|
590
|
+
prompt: Optional[bool] = None,
|
591
|
+
json_output: bool = False,
|
592
|
+
) -> bool:
|
593
|
+
"""Modify liquidity position in provided subnet."""
|
594
|
+
if not await subtensor.subnet_exists(netuid=netuid):
|
595
|
+
err_msg = f"Subnet with netuid: {netuid} does not exist in {subtensor}."
|
596
|
+
if json_output:
|
597
|
+
json_console.print(json.dumps({"success": False, "err_msg": err_msg}))
|
598
|
+
else:
|
599
|
+
err_console.print(err_msg)
|
600
|
+
return False
|
601
|
+
|
602
|
+
if prompt:
|
603
|
+
console.print(
|
604
|
+
"You are about to modify a LiquidityPosition with:"
|
605
|
+
f"\tSubnet: {netuid}\n"
|
606
|
+
f"\tPosition id: {position_id}\n"
|
607
|
+
f"\tWallet name: {wallet.name}\n"
|
608
|
+
f"\tLiquidity delta: {liquidity_delta}"
|
609
|
+
)
|
610
|
+
|
611
|
+
if not Confirm.ask("Would you like to continue?"):
|
612
|
+
return False
|
613
|
+
|
614
|
+
success, msg = await modify_liquidity_extrinsic(
|
615
|
+
subtensor=subtensor,
|
616
|
+
wallet=wallet,
|
617
|
+
hotkey_ss58=hotkey_ss58,
|
618
|
+
netuid=netuid,
|
619
|
+
position_id=position_id,
|
620
|
+
liquidity_delta=liquidity_delta,
|
621
|
+
)
|
622
|
+
if json_output:
|
623
|
+
json_console.print(json.dumps({"success": success, "err_msg": msg}))
|
624
|
+
else:
|
625
|
+
if success:
|
626
|
+
console.print(f"[green] Position {position_id} has been modified.")
|
627
|
+
else:
|
628
|
+
err_console.print(f"[red] Error modifying {position_id}: {msg}")
|