nado-protocol 0.1.0__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.
Files changed (78) hide show
  1. nado_protocol/__init__.py +0 -0
  2. nado_protocol/client/__init__.py +200 -0
  3. nado_protocol/client/apis/__init__.py +26 -0
  4. nado_protocol/client/apis/base.py +42 -0
  5. nado_protocol/client/apis/market/__init__.py +23 -0
  6. nado_protocol/client/apis/market/execute.py +192 -0
  7. nado_protocol/client/apis/market/query.py +310 -0
  8. nado_protocol/client/apis/perp/__init__.py +18 -0
  9. nado_protocol/client/apis/perp/query.py +30 -0
  10. nado_protocol/client/apis/rewards/__init__.py +6 -0
  11. nado_protocol/client/apis/rewards/execute.py +131 -0
  12. nado_protocol/client/apis/rewards/query.py +12 -0
  13. nado_protocol/client/apis/spot/__init__.py +23 -0
  14. nado_protocol/client/apis/spot/base.py +32 -0
  15. nado_protocol/client/apis/spot/execute.py +117 -0
  16. nado_protocol/client/apis/spot/query.py +79 -0
  17. nado_protocol/client/apis/subaccount/__init__.py +24 -0
  18. nado_protocol/client/apis/subaccount/execute.py +54 -0
  19. nado_protocol/client/apis/subaccount/query.py +145 -0
  20. nado_protocol/client/context.py +90 -0
  21. nado_protocol/contracts/__init__.py +377 -0
  22. nado_protocol/contracts/abis/Endpoint.json +636 -0
  23. nado_protocol/contracts/abis/FQuerier.json +1909 -0
  24. nado_protocol/contracts/abis/IClearinghouse.json +876 -0
  25. nado_protocol/contracts/abis/IERC20.json +185 -0
  26. nado_protocol/contracts/abis/IEndpoint.json +250 -0
  27. nado_protocol/contracts/abis/IFoundationRewardsAirdrop.json +76 -0
  28. nado_protocol/contracts/abis/IOffchainBook.json +536 -0
  29. nado_protocol/contracts/abis/IPerpEngine.json +931 -0
  30. nado_protocol/contracts/abis/IProductEngine.json +352 -0
  31. nado_protocol/contracts/abis/ISpotEngine.json +813 -0
  32. nado_protocol/contracts/abis/IStaking.json +288 -0
  33. nado_protocol/contracts/abis/IVrtxAirdrop.json +138 -0
  34. nado_protocol/contracts/abis/MockERC20.json +311 -0
  35. nado_protocol/contracts/deployments/deployment.test.json +18 -0
  36. nado_protocol/contracts/eip712/__init__.py +16 -0
  37. nado_protocol/contracts/eip712/domain.py +36 -0
  38. nado_protocol/contracts/eip712/sign.py +79 -0
  39. nado_protocol/contracts/eip712/types.py +154 -0
  40. nado_protocol/contracts/loader.py +55 -0
  41. nado_protocol/contracts/types.py +141 -0
  42. nado_protocol/engine_client/__init__.py +35 -0
  43. nado_protocol/engine_client/execute.py +416 -0
  44. nado_protocol/engine_client/query.py +481 -0
  45. nado_protocol/engine_client/types/__init__.py +113 -0
  46. nado_protocol/engine_client/types/execute.py +680 -0
  47. nado_protocol/engine_client/types/models.py +247 -0
  48. nado_protocol/engine_client/types/query.py +516 -0
  49. nado_protocol/engine_client/types/stream.py +6 -0
  50. nado_protocol/indexer_client/__init__.py +28 -0
  51. nado_protocol/indexer_client/query.py +466 -0
  52. nado_protocol/indexer_client/types/__init__.py +122 -0
  53. nado_protocol/indexer_client/types/models.py +364 -0
  54. nado_protocol/indexer_client/types/query.py +819 -0
  55. nado_protocol/trigger_client/__init__.py +17 -0
  56. nado_protocol/trigger_client/execute.py +118 -0
  57. nado_protocol/trigger_client/query.py +61 -0
  58. nado_protocol/trigger_client/types/__init__.py +7 -0
  59. nado_protocol/trigger_client/types/execute.py +89 -0
  60. nado_protocol/trigger_client/types/models.py +44 -0
  61. nado_protocol/trigger_client/types/query.py +77 -0
  62. nado_protocol/utils/__init__.py +37 -0
  63. nado_protocol/utils/backend.py +111 -0
  64. nado_protocol/utils/bytes32.py +159 -0
  65. nado_protocol/utils/enum.py +6 -0
  66. nado_protocol/utils/exceptions.py +58 -0
  67. nado_protocol/utils/execute.py +403 -0
  68. nado_protocol/utils/expiration.py +45 -0
  69. nado_protocol/utils/interest.py +66 -0
  70. nado_protocol/utils/math.py +67 -0
  71. nado_protocol/utils/model.py +79 -0
  72. nado_protocol/utils/nonce.py +33 -0
  73. nado_protocol/utils/subaccount.py +18 -0
  74. nado_protocol/utils/time.py +21 -0
  75. nado_protocol-0.1.0.dist-info/METADATA +157 -0
  76. nado_protocol-0.1.0.dist-info/RECORD +78 -0
  77. nado_protocol-0.1.0.dist-info/WHEEL +4 -0
  78. nado_protocol-0.1.0.dist-info/entry_points.txt +11 -0
@@ -0,0 +1,141 @@
1
+ from typing import Optional
2
+ from pydantic import AnyUrl, Field
3
+ from nado_protocol.utils.enum import StrEnum
4
+
5
+ from nado_protocol.utils.model import NadoBaseModel
6
+
7
+
8
+ class NadoNetwork(StrEnum):
9
+ """
10
+ Enumeration representing various network environments for the Nado protocol.
11
+ """
12
+
13
+ # dev
14
+ HARDHAT = "localhost"
15
+ TESTING = "test"
16
+
17
+
18
+ class NadoAbiName(StrEnum):
19
+ """
20
+ Enumeration representing various contract names for which the ABI can be loaded in the Nado protocol.
21
+ """
22
+
23
+ ENDPOINT = "Endpoint"
24
+ FQUERIER = "FQuerier"
25
+ ICLEARINGHOUSE = "IClearinghouse"
26
+ IENDPOINT = "IEndpoint"
27
+ IOFFCHAIN_BOOK = "IOffchainBook"
28
+ IPERP_ENGINE = "IPerpEngine"
29
+ ISPOT_ENGINE = "ISpotEngine"
30
+ MOCK_ERC20 = "MockERC20"
31
+ ISTAKING = "IStaking"
32
+ IVRTX_AIRDROP = "IVrtxAirdrop"
33
+ IFOUNDATION_REWARDS_AIRDROP = "IFoundationRewardsAirdrop"
34
+
35
+
36
+ class NadoDeployment(NadoBaseModel):
37
+ """
38
+ Class representing deployment data for Nado protocol contracts.
39
+
40
+ Attributes:
41
+ node_url (AnyUrl): The URL of the node.
42
+
43
+ quote_addr (str): The address of the quote contract.
44
+
45
+ querier_addr (str): The address of the querier contract.
46
+
47
+ clearinghouse_addr (str): The address of the clearinghouse contract.
48
+
49
+ endpoint_addr (str): The address of the endpoint contract.
50
+
51
+ spot_engine_addr (str): The address of the spot engine contract.
52
+
53
+ perp_engine_addr (str): The address of the perpetual engine contract.
54
+
55
+ vrtx_airdrop_addr (str): The address of the VRTX airdrop contract.
56
+
57
+ vrtx_staking_addr (str): The address of the VRTX staking contract.
58
+
59
+ foundation_rewards_airdrop_addr (str): The address of Foundation Rewards airdrop contract for the corresponding chain (e.g: Arb airdrop for Arbitrum).
60
+ """
61
+
62
+ node_url: AnyUrl = Field(alias="publicNodeUrl")
63
+ quote_addr: str = Field(alias="quote")
64
+ querier_addr: str = Field(alias="querier")
65
+ clearinghouse_addr: str = Field(alias="clearinghouse")
66
+ endpoint_addr: str = Field(alias="endpoint")
67
+ spot_engine_addr: str = Field(alias="spotEngine")
68
+ perp_engine_addr: str = Field(alias="perpEngine")
69
+ vrtx_airdrop_addr: str = Field(alias="vrtxAirdrop")
70
+ vrtx_staking_addr: str = Field(alias="vrtxStaking")
71
+ foundation_rewards_airdrop_addr: str = Field(alias="foundationRewardsAirdrop")
72
+
73
+
74
+ class DepositCollateralParams(NadoBaseModel):
75
+ """
76
+ Class representing parameters for depositing collateral in the Nado protocol.
77
+
78
+ Attributes:
79
+ subaccount_name (str): The name of the subaccount.
80
+
81
+ product_id (int): The ID of the spot product to deposit collateral for.
82
+
83
+ amount (int): The amount of collateral to be deposited.
84
+
85
+ referral_code (Optional[str]): Use this to indicate you were referred by existing member.
86
+ """
87
+
88
+ subaccount_name: str
89
+ product_id: int
90
+ amount: int
91
+ referral_code: Optional[str]
92
+
93
+
94
+ class ClaimVrtxParams(NadoBaseModel):
95
+ epoch: int
96
+ amount: Optional[int]
97
+ claim_all: Optional[bool]
98
+
99
+
100
+ class ClaimVrtxContractParams(NadoBaseModel):
101
+ epoch: int
102
+ amount_to_claim: int
103
+ total_claimable_amount: int
104
+ merkle_proof: list[str]
105
+
106
+
107
+ class ClaimFoundationRewardsProofStruct(NadoBaseModel):
108
+ totalAmount: int
109
+ week: int
110
+ proof: list[str]
111
+
112
+
113
+ class ClaimFoundationRewardsContractParams(NadoBaseModel):
114
+ claim_proofs: list[ClaimFoundationRewardsProofStruct]
115
+
116
+
117
+ class NadoExecuteType(StrEnum):
118
+ """
119
+ Enumeration of possible actions to execute in Nado.
120
+ """
121
+
122
+ PLACE_ORDER = "place_order"
123
+ PLACE_ISOLATED_ORDER = "place_isolated_order"
124
+ CANCEL_ORDERS = "cancel_orders"
125
+ CANCEL_PRODUCT_ORDERS = "cancel_product_orders"
126
+ CANCEL_AND_PLACE = "cancel_and_place"
127
+ WITHDRAW_COLLATERAL = "withdraw_collateral"
128
+ LIQUIDATE_SUBACCOUNT = "liquidate_subaccount"
129
+ MINT_LP = "mint_lp"
130
+ BURN_LP = "burn_lp"
131
+ LINK_SIGNER = "link_signer"
132
+
133
+
134
+ NadoTxType = StrEnum(
135
+ "NadoTxType",
136
+ {
137
+ **{name: member.value for name, member in NadoExecuteType.__members__.items()},
138
+ **{"AUTHENTICATE_STREAM": "authenticate"},
139
+ **{"LIST_TRIGGER_ORDERS": "list_trigger_orders"},
140
+ },
141
+ ) # type: ignore
@@ -0,0 +1,35 @@
1
+ from nado_protocol.engine_client.types import EngineClientOpts
2
+ from nado_protocol.engine_client.execute import EngineExecuteClient
3
+ from nado_protocol.engine_client.query import EngineQueryClient
4
+
5
+
6
+ class EngineClient(EngineQueryClient, EngineExecuteClient): # type: ignore
7
+ """
8
+ Client for interacting with the engine service.
9
+
10
+ It allows users to both query data from and execute commands on the engine service.
11
+
12
+ Attributes:
13
+ opts (EngineClientOpts): Client configuration options for connecting and interacting with the engine service.
14
+
15
+ Methods:
16
+ __init__: Initializes the `EngineClient` with the provided options.
17
+ """
18
+
19
+ def __init__(self, opts: EngineClientOpts):
20
+ """
21
+ Initializes the EngineClient with the provided options.
22
+
23
+ Args:
24
+ opts (EngineClientOpts): Client configuration options for connecting and interacting with the engine service.
25
+ """
26
+ EngineQueryClient.__init__(self, opts)
27
+ EngineExecuteClient.__init__(self, opts)
28
+
29
+
30
+ __all__ = [
31
+ "EngineClient",
32
+ "EngineClientOpts",
33
+ "EngineExecuteClient",
34
+ "EngineQueryClient",
35
+ ]
@@ -0,0 +1,416 @@
1
+ import time
2
+ import requests
3
+ from functools import singledispatchmethod
4
+
5
+ from typing import Optional, Union
6
+ from nado_protocol.engine_client.query import EngineQueryClient
7
+ from nado_protocol.engine_client.types import (
8
+ EngineClientOpts,
9
+ )
10
+ from nado_protocol.engine_client.types.execute import (
11
+ BurnLpParams,
12
+ CancelAndPlaceParams,
13
+ CancelOrdersParams,
14
+ CancelProductOrdersParams,
15
+ ExecuteParams,
16
+ ExecuteRequest,
17
+ ExecuteResponse,
18
+ LinkSignerParams,
19
+ LiquidateSubaccountParams,
20
+ MintLpParams,
21
+ OrderParams,
22
+ PlaceIsolatedOrderParams,
23
+ PlaceMarketOrderParams,
24
+ PlaceOrderParams,
25
+ WithdrawCollateralParams,
26
+ to_execute_request,
27
+ )
28
+ from nado_protocol.contracts.types import NadoExecuteType
29
+ from nado_protocol.engine_client.types.models import MarketLiquidity
30
+ from nado_protocol.utils.bytes32 import subaccount_to_hex
31
+
32
+ from nado_protocol.utils.exceptions import (
33
+ BadStatusCodeException,
34
+ ExecuteFailedException,
35
+ )
36
+ from nado_protocol.utils.expiration import OrderType, get_expiration_timestamp
37
+ from nado_protocol.utils.math import mul_x18, round_x18, to_x18
38
+ from nado_protocol.utils.model import NadoBaseModel, is_instance_of_union
39
+ from nado_protocol.utils.subaccount import Subaccount, SubaccountParams
40
+ from nado_protocol.utils.execute import NadoBaseExecute
41
+
42
+
43
+ class EngineExecuteClient(NadoBaseExecute):
44
+ """
45
+ Client class for executing operations against the off-chain engine.
46
+ """
47
+
48
+ def __init__(
49
+ self, opts: EngineClientOpts, querier: Optional[EngineQueryClient] = None
50
+ ):
51
+ """
52
+ Initialize the EngineExecuteClient with provided options.
53
+
54
+ Args:
55
+ opts (EngineClientOpts): Options for the client.
56
+
57
+ querier (EngineQueryClient, optional): An EngineQueryClient instance. If not provided, a new one is created.
58
+ """
59
+ super().__init__(opts)
60
+ self._querier = querier or EngineQueryClient(opts)
61
+ self._opts: EngineClientOpts = EngineClientOpts.parse_obj(opts)
62
+ self.url: str = self._opts.url
63
+ self.session = requests.Session()
64
+
65
+ def tx_nonce(self, sender: str) -> int:
66
+ """
67
+ Get the transaction nonce. Used to perform executes such as `withdraw_collateral`.
68
+
69
+ Returns:
70
+ int: The transaction nonce.
71
+ """
72
+ return int(self._querier.get_nonces(sender[:42]).tx_nonce)
73
+
74
+ @singledispatchmethod
75
+ def execute(self, params: Union[ExecuteParams, ExecuteRequest]) -> ExecuteResponse:
76
+ """
77
+ Executes the operation defined by the provided parameters.
78
+
79
+ Args:
80
+ params (ExecuteParams): The parameters for the operation to execute. This can represent a variety of operations, such as placing orders, cancelling orders, and more.
81
+
82
+ Returns:
83
+ ExecuteResponse: The response from the executed operation.
84
+ """
85
+ req: ExecuteRequest = (
86
+ params if is_instance_of_union(params, ExecuteRequest) else to_execute_request(params) # type: ignore
87
+ )
88
+ return self._execute(req)
89
+
90
+ @execute.register
91
+ def _(self, req: dict) -> ExecuteResponse:
92
+ """
93
+ Overloaded method to execute the operation defined by the provided request.
94
+
95
+ Args:
96
+ req (dict): The request data for the operation to execute. Can be a dictionary or an instance of ExecuteRequest.
97
+
98
+ Returns:
99
+ ExecuteResponse: The response from the executed operation.
100
+ """
101
+ parsed_req: ExecuteRequest = NadoBaseModel.parse_obj(req) # type: ignore
102
+ return self._execute(parsed_req)
103
+
104
+ def _execute(self, req: ExecuteRequest) -> ExecuteResponse:
105
+ """
106
+ Internal method to execute the operation. Sends request to the server.
107
+
108
+ Args:
109
+ req (ExecuteRequest): The request data for the operation to execute.
110
+
111
+ Returns:
112
+ ExecuteResponse: The response from the executed operation.
113
+
114
+ Raises:
115
+ BadStatusCodeException: If the server response status code is not 200.
116
+ ExecuteFailedException: If there's an error in the execution or the response status is not "success".
117
+ """
118
+ res = self.session.post(f"{self.url}/execute", json=req.dict())
119
+ if res.status_code != 200:
120
+ raise BadStatusCodeException(res.text)
121
+ try:
122
+ execute_res = ExecuteResponse(**res.json(), req=req.dict())
123
+ except Exception:
124
+ raise ExecuteFailedException(res.text)
125
+ if execute_res.status != "success":
126
+ raise ExecuteFailedException(res.text)
127
+ return execute_res
128
+
129
+ def _assert_book_not_empty(
130
+ self, bids: list[MarketLiquidity], asks: list[MarketLiquidity], is_bid: bool
131
+ ):
132
+ book_is_empty = (is_bid and len(bids) == 0) or (not is_bid and len(asks) == 0)
133
+ if book_is_empty:
134
+ raise Exception("Orderbook is empty.")
135
+
136
+ def place_order(self, params: PlaceOrderParams) -> ExecuteResponse:
137
+ """
138
+ Execute a place order operation.
139
+
140
+ Args:
141
+ params (PlaceOrderParams): Parameters required for placing an order.
142
+ The parameters include the order details and the product_id.
143
+
144
+ Returns:
145
+ ExecuteResponse: Response of the execution, including status and potential error message.
146
+ """
147
+ params = PlaceOrderParams.parse_obj(params)
148
+ params.order = self.prepare_execute_params(params.order, True)
149
+ params.signature = params.signature or self._sign(
150
+ NadoExecuteType.PLACE_ORDER, params.order.dict(), params.product_id
151
+ )
152
+ return self.execute(params)
153
+
154
+ def place_isolated_order(self, params: PlaceIsolatedOrderParams) -> ExecuteResponse:
155
+ """
156
+ Execute a place isolated order operation.
157
+
158
+ Args:
159
+ params (PlaceIsolatedOrderParams): Parameters required for placing an isolated order.
160
+ The parameters include the isolated order details.
161
+
162
+ Returns:
163
+ ExecuteResponse: Response of the execution, including status and potential error message.
164
+ """
165
+ params = PlaceIsolatedOrderParams.parse_obj(params)
166
+ params.isolated_order = self.prepare_execute_params(params.isolated_order, True)
167
+ params.signature = params.signature or self._sign(
168
+ NadoExecuteType.PLACE_ISOLATED_ORDER,
169
+ params.isolated_order.dict(),
170
+ params.product_id,
171
+ )
172
+ return self.execute(params)
173
+
174
+ def place_market_order(self, params: PlaceMarketOrderParams) -> ExecuteResponse:
175
+ """
176
+ Places an FOK order using top of the book price with provided slippage.
177
+
178
+ Args:
179
+ params (PlaceMarketOrderParams): Parameters required for placing a market order.
180
+
181
+ Returns:
182
+ ExecuteResponse: Response of the execution, including status and potential error message.
183
+ """
184
+ orderbook = self._querier.get_market_liquidity(params.product_id, 1)
185
+ is_bid = int(params.market_order.amount) > 0
186
+ self._assert_book_not_empty(orderbook.bids, orderbook.asks, is_bid)
187
+ slippage = to_x18(params.slippage or 0.005) # defaults to 0.5%
188
+ market_price_x18 = (
189
+ mul_x18(orderbook.bids[0][0], to_x18(1) + slippage)
190
+ if is_bid
191
+ else mul_x18(orderbook.asks[0][0], to_x18(1) - slippage)
192
+ )
193
+ price_increment_x18 = self._querier._get_subaccount_product_position(
194
+ subaccount_to_hex(params.market_order.sender), params.product_id
195
+ ).product.book_info.price_increment_x18
196
+ order = OrderParams(
197
+ sender=params.market_order.sender,
198
+ amount=params.market_order.amount,
199
+ nonce=params.market_order.nonce,
200
+ priceX18=round_x18(market_price_x18, price_increment_x18),
201
+ expiration=get_expiration_timestamp(
202
+ OrderType.FOK, int(time.time()) + 1000, bool(params.reduce_only)
203
+ ),
204
+ )
205
+ return self.place_order(
206
+ PlaceOrderParams( # type: ignore
207
+ product_id=params.product_id,
208
+ order=order,
209
+ spot_leverage=params.spot_leverage,
210
+ signature=params.signature,
211
+ )
212
+ )
213
+
214
+ def cancel_orders(self, params: CancelOrdersParams) -> ExecuteResponse:
215
+ """
216
+ Execute a cancel orders operation.
217
+
218
+ Args:
219
+ params (CancelOrdersParams): Parameters required for canceling orders.
220
+ The parameters include the order digests to be cancelled.
221
+
222
+ Returns:
223
+ ExecuteResponse: Response of the execution, including status and potential error message.
224
+ """
225
+ params = self.prepare_execute_params(CancelOrdersParams.parse_obj(params), True)
226
+ params.signature = params.signature or self._sign(
227
+ NadoExecuteType.CANCEL_ORDERS, params.dict()
228
+ )
229
+ return self.execute(params)
230
+
231
+ def cancel_product_orders(
232
+ self, params: CancelProductOrdersParams
233
+ ) -> ExecuteResponse:
234
+ """
235
+ Execute a cancel product orders operation.
236
+
237
+ Args:
238
+ params (CancelProductOrdersParams): Parameters required for bulk canceling orders of specific products.
239
+ The parameters include a list of product ids to bulk cancel orders for.
240
+
241
+ Returns:
242
+ ExecuteResponse: Response of the execution, including status and potential error message.
243
+ """
244
+ params = self.prepare_execute_params(
245
+ CancelProductOrdersParams.parse_obj(params), True
246
+ )
247
+ params.signature = params.signature or self._sign(
248
+ NadoExecuteType.CANCEL_PRODUCT_ORDERS, params.dict()
249
+ )
250
+ return self.execute(params)
251
+
252
+ def cancel_and_place(self, params: CancelAndPlaceParams) -> ExecuteResponse:
253
+ """
254
+ Execute a cancel and place operation.
255
+
256
+ Args:
257
+ params (CancelAndPlaceParams): Parameters required for cancel and place.
258
+
259
+ Returns:
260
+ ExecuteResponse: Response of the execution, including status and potential error message.
261
+ """
262
+ cancel_orders: CancelOrdersParams = self.prepare_execute_params(
263
+ CancelOrdersParams.parse_obj(params.cancel_orders), True
264
+ )
265
+ cancel_orders.signature = cancel_orders.signature or self._sign(
266
+ NadoExecuteType.CANCEL_ORDERS, cancel_orders.dict()
267
+ )
268
+ place_order: PlaceOrderParams = PlaceOrderParams.parse_obj(params.place_order)
269
+ place_order.order = self.prepare_execute_params(place_order.order, True)
270
+ place_order.signature = place_order.signature or self._sign(
271
+ NadoExecuteType.PLACE_ORDER,
272
+ place_order.order.dict(),
273
+ place_order.product_id,
274
+ )
275
+ return self.execute(
276
+ CancelAndPlaceParams(cancel_orders=cancel_orders, place_order=place_order)
277
+ )
278
+
279
+ def withdraw_collateral(self, params: WithdrawCollateralParams) -> ExecuteResponse:
280
+ """
281
+ Execute a withdraw collateral operation.
282
+
283
+ Args:
284
+ params (WithdrawCollateralParams): Parameters required for withdrawing collateral.
285
+ The parameters include the collateral details.
286
+
287
+ Returns:
288
+ ExecuteResponse: Response of the execution, including status and potential error message.
289
+ """
290
+ params = self.prepare_execute_params(
291
+ WithdrawCollateralParams.parse_obj(params), False
292
+ )
293
+ params.signature = params.signature or self._sign(
294
+ NadoExecuteType.WITHDRAW_COLLATERAL, params.dict()
295
+ )
296
+ return self.execute(params)
297
+
298
+ def liquidate_subaccount(
299
+ self, params: LiquidateSubaccountParams
300
+ ) -> ExecuteResponse:
301
+ """
302
+ Execute a liquidate subaccount operation.
303
+
304
+ Args:
305
+ params (LiquidateSubaccountParams): Parameters required for liquidating a subaccount.
306
+ The parameters include the liquidatee details.
307
+
308
+ Returns:
309
+ ExecuteResponse: Response of the execution, including status and potential error message.
310
+ """
311
+ params = self.prepare_execute_params(
312
+ LiquidateSubaccountParams.parse_obj(params), False
313
+ )
314
+ params.signature = params.signature or self._sign(
315
+ NadoExecuteType.LIQUIDATE_SUBACCOUNT,
316
+ params.dict(),
317
+ )
318
+ return self.execute(params)
319
+
320
+ def mint_lp(self, params: MintLpParams) -> ExecuteResponse:
321
+ """
322
+ Execute a mint LP tokens operation.
323
+
324
+ Args:
325
+ params (MintLpParams): Parameters required for minting LP tokens.
326
+ The parameters include the LP details.
327
+
328
+ Returns:
329
+ ExecuteResponse: Response of the execution, including status and potential error message.
330
+ """
331
+ params = self.prepare_execute_params(MintLpParams.parse_obj(params), False)
332
+ params.signature = params.signature or self._sign(
333
+ NadoExecuteType.MINT_LP,
334
+ params.dict(),
335
+ )
336
+ return self.execute(params)
337
+
338
+ def burn_lp(self, params: BurnLpParams) -> ExecuteResponse:
339
+ """
340
+ Execute a burn LP tokens operation.
341
+
342
+ Args:
343
+ params (BurnLpParams): Parameters required for burning LP tokens.
344
+ The parameters include the LP details.
345
+
346
+ Returns:
347
+ ExecuteResponse: Response of the execution, including status and potential error message.
348
+ """
349
+ params = self.prepare_execute_params(BurnLpParams.parse_obj(params), False)
350
+ params.signature = params.signature or self._sign(
351
+ NadoExecuteType.BURN_LP,
352
+ params.dict(),
353
+ )
354
+ return self.execute(params)
355
+
356
+ def link_signer(self, params: LinkSignerParams) -> ExecuteResponse:
357
+ """
358
+ Execute a link signer operation.
359
+
360
+ Args:
361
+ params (LinkSignerParams): Parameters required for linking a signer.
362
+ The parameters include the signer details.
363
+
364
+ Returns:
365
+ ExecuteResponse: Response of the execution, including status and potential error message.
366
+ """
367
+ params = self.prepare_execute_params(LinkSignerParams.parse_obj(params), False)
368
+ params.signature = params.signature or self._sign(
369
+ NadoExecuteType.LINK_SIGNER,
370
+ params.dict(),
371
+ )
372
+ return self.execute(params)
373
+
374
+ def close_position(
375
+ self, subaccount: Subaccount, product_id: int
376
+ ) -> ExecuteResponse:
377
+ """
378
+ Execute a place order operation to close a position for the provided `product_id`.
379
+
380
+ Attributes:
381
+ subaccount (Subaccount): The subaccount to close position for.
382
+ product_id (int): The ID of the product to close position for.
383
+
384
+ Returns:
385
+ ExecuteResponse: Response of the execution, including status and potential error message.
386
+ """
387
+ subaccount = subaccount_to_hex(subaccount)
388
+ position = self._querier._get_subaccount_product_position(
389
+ subaccount, product_id
390
+ )
391
+ balance, product = position.balance, position.product
392
+ closing_spread_x18 = to_x18(0.005)
393
+ closing_price_x18 = (
394
+ mul_x18(product.oracle_price_x18, to_x18(1) - closing_spread_x18)
395
+ if int(balance.balance.amount) > 0
396
+ else mul_x18(product.oracle_price_x18, to_x18(1) + closing_spread_x18)
397
+ )
398
+ return self.place_order(
399
+ PlaceOrderParams( # type: ignore
400
+ product_id=product_id,
401
+ order=OrderParams( # type: ignore
402
+ sender=subaccount,
403
+ amount=-round_x18(
404
+ balance.balance.amount,
405
+ product.book_info.size_increment,
406
+ ),
407
+ priceX18=round_x18(
408
+ closing_price_x18,
409
+ product.book_info.price_increment_x18,
410
+ ),
411
+ expiration=get_expiration_timestamp(
412
+ OrderType.FOK, int(time.time()) + 1000, reduce_only=True
413
+ ),
414
+ ),
415
+ )
416
+ )