Skip to content

Commit bcf4cc2

Browse files
authored
Merge pull request #2939 from opentensor/feat/roman/liquidity-provider
Integrate Liquidity Provider feature
2 parents 48fd826 + 0c747df commit bcf4cc2

File tree

15 files changed

+2639
-11
lines changed

15 files changed

+2639
-11
lines changed

bittensor/core/async_subtensor.py

Lines changed: 378 additions & 4 deletions
Large diffs are not rendered by default.
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
from typing import Optional, TYPE_CHECKING
2+
3+
from bittensor.utils import unlock_key
4+
from bittensor.utils.balance import Balance
5+
from bittensor.utils.btlogging import logging
6+
from bittensor.utils.liquidity import price_to_tick
7+
8+
if TYPE_CHECKING:
9+
from bittensor_wallet import Wallet
10+
from bittensor.core.async_subtensor import AsyncSubtensor
11+
12+
13+
async def add_liquidity_extrinsic(
14+
subtensor: "AsyncSubtensor",
15+
wallet: "Wallet",
16+
netuid: int,
17+
liquidity: Balance,
18+
price_low: Balance,
19+
price_high: Balance,
20+
wait_for_inclusion: bool = True,
21+
wait_for_finalization: bool = False,
22+
period: Optional[int] = None,
23+
) -> tuple[bool, str]:
24+
"""
25+
Adds liquidity to the specified price range.
26+
27+
Arguments:
28+
subtensor: The Subtensor client instance used for blockchain interaction.
29+
wallet: The wallet used to sign the extrinsic (must be unlocked).
30+
netuid: The UID of the target subnet for which the call is being initiated.
31+
liquidity: The amount of liquidity to be added.
32+
price_low: The lower bound of the price tick range.
33+
price_high: The upper bound of the price tick range.
34+
wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True.
35+
wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False.
36+
period: The number of blocks during which the transaction will remain valid after it's submitted. If
37+
the transaction is not included in a block within that number of blocks, it will expire and be rejected.
38+
You can think of it as an expiration date for the transaction.
39+
40+
Returns:
41+
Tuple[bool, str]:
42+
- True and a success message if the extrinsic is successfully submitted or processed.
43+
- False and an error message if the submission fails or the wallet cannot be unlocked.
44+
45+
Note: Adding is allowed even when user liquidity is enabled in specified subnet. Call
46+
`toggle_user_liquidity_extrinsic` to enable/disable user liquidity.
47+
"""
48+
if not (unlock := unlock_key(wallet)).success:
49+
logging.error(unlock.message)
50+
return False, unlock.message
51+
52+
tick_low = price_to_tick(price_low.tao)
53+
tick_high = price_to_tick(price_high.tao)
54+
55+
call = await subtensor.substrate.compose_call(
56+
call_module="Swap",
57+
call_function="add_liquidity",
58+
call_params={
59+
"hotkey": wallet.hotkey.ss58_address,
60+
"netuid": netuid,
61+
"tick_low": tick_low,
62+
"tick_high": tick_high,
63+
"liquidity": liquidity.rao,
64+
},
65+
)
66+
67+
return await subtensor.sign_and_send_extrinsic(
68+
call=call,
69+
wallet=wallet,
70+
wait_for_inclusion=wait_for_inclusion,
71+
wait_for_finalization=wait_for_finalization,
72+
use_nonce=True,
73+
period=period,
74+
)
75+
76+
77+
async def modify_liquidity_extrinsic(
78+
subtensor: "AsyncSubtensor",
79+
wallet: "Wallet",
80+
netuid: int,
81+
position_id: int,
82+
liquidity_delta: Balance,
83+
wait_for_inclusion: bool = True,
84+
wait_for_finalization: bool = False,
85+
period: Optional[int] = None,
86+
) -> tuple[bool, str]:
87+
"""Modifies liquidity in liquidity position by adding or removing liquidity from it.
88+
89+
Arguments:
90+
subtensor: The Subtensor client instance used for blockchain interaction.
91+
wallet: The wallet used to sign the extrinsic (must be unlocked).
92+
netuid: The UID of the target subnet for which the call is being initiated.
93+
position_id: The id of the position record in the pool.
94+
liquidity_delta: The amount of liquidity to be added or removed (add if positive or remove if negative).
95+
wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True.
96+
wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False.
97+
period: The number of blocks during which the transaction will remain valid after it's submitted. If
98+
the transaction is not included in a block within that number of blocks, it will expire and be rejected.
99+
You can think of it as an expiration date for the transaction.
100+
101+
Returns:
102+
Tuple[bool, str]:
103+
- True and a success message if the extrinsic is successfully submitted or processed.
104+
- False and an error message if the submission fails or the wallet cannot be unlocked.
105+
106+
Note: Modifying is allowed even when user liquidity is enabled in specified subnet.
107+
Call `toggle_user_liquidity_extrinsic` to enable/disable user liquidity.
108+
"""
109+
if not (unlock := unlock_key(wallet)).success:
110+
logging.error(unlock.message)
111+
return False, unlock.message
112+
113+
call = await subtensor.substrate.compose_call(
114+
call_module="Swap",
115+
call_function="modify_position",
116+
call_params={
117+
"hotkey": wallet.hotkey.ss58_address,
118+
"netuid": netuid,
119+
"position_id": position_id,
120+
"liquidity_delta": liquidity_delta.rao,
121+
},
122+
)
123+
124+
return await subtensor.sign_and_send_extrinsic(
125+
call=call,
126+
wallet=wallet,
127+
wait_for_inclusion=wait_for_inclusion,
128+
wait_for_finalization=wait_for_finalization,
129+
use_nonce=True,
130+
period=period,
131+
)
132+
133+
134+
async def remove_liquidity_extrinsic(
135+
subtensor: "AsyncSubtensor",
136+
wallet: "Wallet",
137+
netuid: int,
138+
position_id: int,
139+
wait_for_inclusion: bool = True,
140+
wait_for_finalization: bool = False,
141+
period: Optional[int] = None,
142+
) -> tuple[bool, str]:
143+
"""Remove liquidity and credit balances back to wallet's hotkey stake.
144+
145+
Arguments:
146+
subtensor: The Subtensor client instance used for blockchain interaction.
147+
wallet: The wallet used to sign the extrinsic (must be unlocked).
148+
netuid: The UID of the target subnet for which the call is being initiated.
149+
position_id: The id of the position record in the pool.
150+
wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True.
151+
wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False.
152+
period: The number of blocks during which the transaction will remain valid after it's submitted. If
153+
the transaction is not included in a block within that number of blocks, it will expire and be rejected.
154+
You can think of it as an expiration date for the transaction.
155+
156+
Returns:
157+
Tuple[bool, str]:
158+
- True and a success message if the extrinsic is successfully submitted or processed.
159+
- False and an error message if the submission fails or the wallet cannot be unlocked.
160+
161+
Note: Adding is allowed even when user liquidity is enabled in specified subnet.
162+
Call `toggle_user_liquidity_extrinsic` to enable/disable user liquidity.
163+
"""
164+
if not (unlock := unlock_key(wallet)).success:
165+
logging.error(unlock.message)
166+
return False, unlock.message
167+
168+
call = await subtensor.substrate.compose_call(
169+
call_module="Swap",
170+
call_function="remove_liquidity",
171+
call_params={
172+
"hotkey": wallet.hotkey.ss58_address,
173+
"netuid": netuid,
174+
"position_id": position_id,
175+
},
176+
)
177+
178+
return await subtensor.sign_and_send_extrinsic(
179+
call=call,
180+
wallet=wallet,
181+
wait_for_inclusion=wait_for_inclusion,
182+
wait_for_finalization=wait_for_finalization,
183+
use_nonce=True,
184+
period=period,
185+
)
186+
187+
188+
async def toggle_user_liquidity_extrinsic(
189+
subtensor: "AsyncSubtensor",
190+
wallet: "Wallet",
191+
netuid: int,
192+
enable: bool,
193+
wait_for_inclusion: bool = True,
194+
wait_for_finalization: bool = False,
195+
period: Optional[int] = None,
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+
period: The number of blocks during which the transaction will remain valid after it's submitted. If
207+
the transaction is not included in a block within that number of blocks, it will expire and be rejected.
208+
You can think of it as an expiration date for the transaction.
209+
210+
Returns:
211+
Tuple[bool, str]:
212+
- True and a success message if the extrinsic is successfully submitted or processed.
213+
- False and an error message if the submission fails or the wallet cannot be unlocked.
214+
"""
215+
if not (unlock := unlock_key(wallet)).success:
216+
logging.error(unlock.message)
217+
return False, unlock.message
218+
219+
call = await subtensor.substrate.compose_call(
220+
call_module="Swap",
221+
call_function="toggle_user_liquidity",
222+
call_params={"netuid": netuid, "enable": enable},
223+
)
224+
225+
return await subtensor.sign_and_send_extrinsic(
226+
call=call,
227+
wallet=wallet,
228+
wait_for_inclusion=wait_for_inclusion,
229+
wait_for_finalization=wait_for_finalization,
230+
period=period,
231+
)

0 commit comments

Comments
 (0)