lighter-sdk 0.1.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. lighter/__init__.py +140 -0
  2. lighter/api/__init__.py +11 -0
  3. lighter/api/account_api.py +2371 -0
  4. lighter/api/block_api.py +863 -0
  5. lighter/api/candlestick_api.py +718 -0
  6. lighter/api/info_api.py +282 -0
  7. lighter/api/order_api.py +2734 -0
  8. lighter/api/root_api.py +529 -0
  9. lighter/api/transaction_api.py +3525 -0
  10. lighter/api_client.py +784 -0
  11. lighter/api_response.py +21 -0
  12. lighter/configuration.py +475 -0
  13. lighter/exceptions.py +199 -0
  14. lighter/models/__init__.py +112 -0
  15. lighter/models/account.py +110 -0
  16. lighter/models/account_api_keys.py +104 -0
  17. lighter/models/account_market_stats.py +98 -0
  18. lighter/models/account_metadata.py +94 -0
  19. lighter/models/account_pn_l.py +106 -0
  20. lighter/models/account_position.py +112 -0
  21. lighter/models/account_stats.py +102 -0
  22. lighter/models/accounts.py +106 -0
  23. lighter/models/api_key.py +98 -0
  24. lighter/models/block.py +124 -0
  25. lighter/models/blocks.py +106 -0
  26. lighter/models/bridge_supported_network.py +96 -0
  27. lighter/models/candlestick.py +106 -0
  28. lighter/models/candlesticks.py +106 -0
  29. lighter/models/contract_address.py +94 -0
  30. lighter/models/current_height.py +96 -0
  31. lighter/models/cursor.py +92 -0
  32. lighter/models/deposit_history.py +106 -0
  33. lighter/models/deposit_history_item.py +105 -0
  34. lighter/models/detailed_account.py +152 -0
  35. lighter/models/detailed_accounts.py +106 -0
  36. lighter/models/detailed_candlestick.py +108 -0
  37. lighter/models/enriched_tx.py +129 -0
  38. lighter/models/exchange_stats.py +110 -0
  39. lighter/models/fee_bucket.py +96 -0
  40. lighter/models/funding.py +98 -0
  41. lighter/models/fundings.py +106 -0
  42. lighter/models/is_whitelisted.py +98 -0
  43. lighter/models/l1_provider_info.py +98 -0
  44. lighter/models/layer2_basic_info.py +100 -0
  45. lighter/models/liquidation.py +100 -0
  46. lighter/models/market_info.py +116 -0
  47. lighter/models/next_nonce.py +96 -0
  48. lighter/models/order.py +159 -0
  49. lighter/models/order_book.py +119 -0
  50. lighter/models/order_book_depth.py +115 -0
  51. lighter/models/order_book_detail.py +151 -0
  52. lighter/models/order_book_details.py +104 -0
  53. lighter/models/order_book_orders.py +117 -0
  54. lighter/models/order_book_stats.py +102 -0
  55. lighter/models/order_books.py +104 -0
  56. lighter/models/orders.py +106 -0
  57. lighter/models/pn_l_entry.py +94 -0
  58. lighter/models/position_funding.py +111 -0
  59. lighter/models/price_level.py +94 -0
  60. lighter/models/public_pool.py +128 -0
  61. lighter/models/public_pool_info.py +100 -0
  62. lighter/models/public_pool_share.py +96 -0
  63. lighter/models/public_pools.py +106 -0
  64. lighter/models/req_get_account.py +101 -0
  65. lighter/models/req_get_account_active_orders.py +96 -0
  66. lighter/models/req_get_account_api_keys.py +94 -0
  67. lighter/models/req_get_account_by_l1_address.py +92 -0
  68. lighter/models/req_get_account_inactive_orders.py +103 -0
  69. lighter/models/req_get_account_orders.py +99 -0
  70. lighter/models/req_get_account_pending_txs.py +106 -0
  71. lighter/models/req_get_account_pn_l.py +118 -0
  72. lighter/models/req_get_account_txs.py +111 -0
  73. lighter/models/req_get_block.py +101 -0
  74. lighter/models/req_get_block_txs.py +101 -0
  75. lighter/models/req_get_by_account.py +101 -0
  76. lighter/models/req_get_candlesticks.py +109 -0
  77. lighter/models/req_get_deposit_history.py +106 -0
  78. lighter/models/req_get_fee_bucket.py +92 -0
  79. lighter/models/req_get_fundings.py +107 -0
  80. lighter/models/req_get_l1_tx.py +92 -0
  81. lighter/models/req_get_latest_deposit.py +92 -0
  82. lighter/models/req_get_next_nonce.py +94 -0
  83. lighter/models/req_get_order_book_details.py +92 -0
  84. lighter/models/req_get_order_book_orders.py +95 -0
  85. lighter/models/req_get_order_books.py +92 -0
  86. lighter/models/req_get_public_pools.py +109 -0
  87. lighter/models/req_get_range_with_cursor.py +95 -0
  88. lighter/models/req_get_range_with_index.py +95 -0
  89. lighter/models/req_get_range_with_index_sortable.py +107 -0
  90. lighter/models/req_get_recent_trades.py +95 -0
  91. lighter/models/req_get_trades.py +126 -0
  92. lighter/models/req_get_tx.py +101 -0
  93. lighter/models/req_get_withdraw_history.py +106 -0
  94. lighter/models/req_is_whitelisted.py +92 -0
  95. lighter/models/result_code.py +94 -0
  96. lighter/models/simple_order.py +102 -0
  97. lighter/models/status.py +94 -0
  98. lighter/models/sub_accounts.py +106 -0
  99. lighter/models/ticker.py +103 -0
  100. lighter/models/trade.py +125 -0
  101. lighter/models/trades.py +106 -0
  102. lighter/models/tx.py +121 -0
  103. lighter/models/tx_hash.py +96 -0
  104. lighter/models/tx_hashes.py +96 -0
  105. lighter/models/txs.py +104 -0
  106. lighter/models/validator_info.py +94 -0
  107. lighter/models/withdraw_history.py +110 -0
  108. lighter/models/withdraw_history_cursor.py +94 -0
  109. lighter/models/withdraw_history_item.py +114 -0
  110. lighter/models/zk_lighter_info.py +92 -0
  111. lighter/py.typed +0 -0
  112. lighter/rest.py +215 -0
  113. lighter/signer_client.py +440 -0
  114. lighter/signers/signer-amd64.so +0 -0
  115. lighter/signers/signer-arm64.dylib +0 -0
  116. lighter/transactions/__init__.py +3 -0
  117. lighter/transactions/cancel_order.py +27 -0
  118. lighter/transactions/create_order.py +33 -0
  119. lighter/transactions/withdraw.py +25 -0
  120. lighter/ws_client.py +160 -0
  121. lighter_sdk-0.1.0.dist-info/LICENSE +201 -0
  122. lighter_sdk-0.1.0.dist-info/METADATA +26 -0
  123. lighter_sdk-0.1.0.dist-info/RECORD +125 -0
  124. lighter_sdk-0.1.0.dist-info/WHEEL +5 -0
  125. lighter_sdk-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,440 @@
1
+ import ctypes
2
+ import platform
3
+ import logging
4
+ import os
5
+ import time
6
+
7
+ from eth_account import Account
8
+ import lighter
9
+ from lighter.configuration import Configuration
10
+ from lighter.models import TxHash
11
+ from lighter.transactions import CreateOrder, CancelOrder, Withdraw
12
+
13
+ logging.basicConfig(level=logging.DEBUG)
14
+
15
+
16
+ class SignerClient:
17
+ USDC_TICKER_SCALE = 1e6
18
+
19
+ TX_TYPE_CREATE_SUB_ACCOUNT = 9
20
+ TX_TYPE_CREATE_PUBLIC_POOL = 10
21
+ TX_TYPE_UPDATE_PUBLIC_POOL = 11
22
+ TX_TYPE_TRANSFER = 12
23
+ TX_TYPE_WITHDRAW = 13
24
+ TX_TYPE_CREATE_ORDER = 14
25
+ TX_TYPE_CANCEL_ORDER = 15
26
+ TX_TYPE_CANCEL_ALL_ORDERS = 16
27
+ TX_TYPE_MODIFY_ORDER = 17
28
+ TX_TYPE_MINT_SHARES = 18
29
+ TX_TYPE_BURN_SHARES = 19
30
+
31
+ ORDER_TYPE_LIMIT = 0
32
+ ORDER_TYPE_MARKET = 1
33
+ ORDER_TYPE_STOP_LOSS = 2
34
+ ORDER_TYPE_STOP_LOSS_LIMIT = 3
35
+ ORDER_TYPE_TAKE_PROFIT = 4
36
+ ORDER_TYPE_TAKE_PROFIT_LIMIT = 5
37
+ ORDER_TYPE_TWAP = 6
38
+
39
+ ORDER_TIME_IN_FORCE_IMMEDIATE_OR_CANCEL = 0
40
+ ORDER_TIME_IN_FORCE_GOOD_TILL_TIME = 1
41
+ ORDER_TIME_IN_FORCE_POST_ONLY = 2
42
+
43
+ NIL_TRIGGER_PRICE = 0
44
+ DEFAULT_28_DAY_ORDER_EXPIRY = -1
45
+ DEFAULT_10_MIN_AUTH_EXPIRY = -1
46
+ MINUTE = 60
47
+
48
+ def __init__(
49
+ self, url, private_key, chain_id=300, api_key_index=0, account_index=-1
50
+ ):
51
+ if private_key.startswith("0x"):
52
+ private_key = private_key[2:]
53
+ self.url = url
54
+ self.private_key = private_key
55
+ self.chain_id = chain_id
56
+ self.api_key_index = api_key_index
57
+ self.account_index = account_index
58
+ self.signer = self._initialize_signer()
59
+ self.api_client = lighter.ApiClient(configuration=Configuration(host=url))
60
+ self.tx_api = lighter.TransactionApi(self.api_client)
61
+ self.create_client()
62
+ self.l1_address = Account.from_key(private_key).address
63
+
64
+ def _initialize_signer(self):
65
+ is_linux = platform.system() == "Linux"
66
+ is_mac = platform.system() == "Darwin"
67
+ is_x64 = platform.machine().lower() in ("amd64", "x86_64")
68
+ is_arm = platform.machine().lower() == "arm64"
69
+
70
+ current_file_directory = os.path.dirname(os.path.abspath(__file__))
71
+ path_to_signer_folders = os.path.join(current_file_directory, "signers")
72
+
73
+ if is_arm and is_mac:
74
+ logging.debug("Detected ARM architecture on macOS.")
75
+ return ctypes.CDLL(
76
+ os.path.join(path_to_signer_folders, "signer-arm64.dylib")
77
+ )
78
+ elif is_linux and is_x64:
79
+ logging.debug("Detected x64/amd architecture on Linux.")
80
+ return ctypes.CDLL(os.path.join(path_to_signer_folders, "signer-amd64.so"))
81
+ else:
82
+ raise Exception(
83
+ f"Unsupported platform/architecture: {platform.system()}/{platform.machine()} only supports Linux(x86) and Darwin(arm64)"
84
+ )
85
+
86
+ async def set_account_index(self) -> int:
87
+ if self.account_index != -1:
88
+ return self.account_index
89
+
90
+ accounts = await lighter.AccountApi(self.api_client).accounts_by_l1_address(
91
+ l1_address=self.l1_address
92
+ )
93
+ if len(accounts.sub_accounts) > 0:
94
+ self.account_index = min(acc.index for acc in accounts.sub_accounts)
95
+ return self.account_index
96
+
97
+ return -1
98
+
99
+ def create_client(self):
100
+ self.signer.CreateClient.argtypes = [
101
+ ctypes.c_char_p,
102
+ ctypes.c_char_p,
103
+ ctypes.c_int,
104
+ ctypes.c_int,
105
+ ctypes.c_longlong,
106
+ ]
107
+ self.signer.CreateClient.restype = ctypes.c_void_p
108
+ self.signer.CreateClient(
109
+ self.url.encode("utf-8"),
110
+ self.private_key.encode("utf-8"),
111
+ self.chain_id,
112
+ self.api_key_index,
113
+ self.account_index,
114
+ )
115
+
116
+ def sign_create_order(
117
+ self,
118
+ market_index,
119
+ client_order_index,
120
+ base_amount,
121
+ price,
122
+ is_ask,
123
+ order_type,
124
+ time_in_force,
125
+ reduce_only,
126
+ trigger_price,
127
+ order_expiry=DEFAULT_28_DAY_ORDER_EXPIRY,
128
+ nonce=-1,
129
+ ):
130
+ self.signer.SignCreateOrder.argtypes = [
131
+ ctypes.c_int,
132
+ ctypes.c_longlong,
133
+ ctypes.c_longlong,
134
+ ctypes.c_int,
135
+ ctypes.c_int,
136
+ ctypes.c_int,
137
+ ctypes.c_int,
138
+ ctypes.c_int,
139
+ ctypes.c_int,
140
+ ctypes.c_longlong,
141
+ ctypes.c_longlong,
142
+ ]
143
+ self.signer.SignCreateOrder.restype = ctypes.c_char_p
144
+ return self.signer.SignCreateOrder(
145
+ market_index,
146
+ client_order_index,
147
+ base_amount,
148
+ price,
149
+ int(is_ask),
150
+ order_type,
151
+ time_in_force,
152
+ reduce_only,
153
+ trigger_price,
154
+ order_expiry,
155
+ nonce,
156
+ ).decode("utf-8")
157
+
158
+ def sign_cancel_order(self, market_index, order_index, nonce=-1):
159
+ self.signer.SignCancelOrder.argtypes = [
160
+ ctypes.c_int,
161
+ ctypes.c_longlong,
162
+ ctypes.c_longlong,
163
+ ]
164
+ self.signer.SignCancelOrder.restype = ctypes.c_char_p
165
+ return self.signer.SignCancelOrder(market_index, order_index, nonce).decode(
166
+ "utf-8"
167
+ )
168
+
169
+ def sign_withdraw(self, usdc_amount, nonce=-1):
170
+ self.signer.SignWithdraw.argtypes = [ctypes.c_longlong, ctypes.c_longlong]
171
+ self.signer.SignWithdraw.restype = ctypes.c_char_p
172
+ return self.signer.SignWithdraw(usdc_amount, nonce).decode("utf-8")
173
+
174
+ def sign_create_sub_account(self, nonce=-1):
175
+ self.signer.SignCreateSubAccount.argtypes = [ctypes.c_longlong]
176
+ self.signer.SignCreateSubAccount.restype = ctypes.c_char_p
177
+ return self.signer.SignCreateSubAccount(nonce).decode("utf-8")
178
+
179
+ def sign_cancel_all_orders(self, time_in_force, time, nonce=-1):
180
+ self.signer.SignCancelAllOrders.argtypes = [
181
+ ctypes.c_int,
182
+ ctypes.c_longlong,
183
+ ctypes.c_longlong,
184
+ ]
185
+ self.signer.SignCancelAllOrders.restype = ctypes.c_char_p
186
+ return self.signer.SignCancelAllOrders(time_in_force, time, nonce).decode(
187
+ "utf-8"
188
+ )
189
+
190
+ def sign_modify_order(
191
+ self, market_index, order_index, base_amount, price, trigger_price, nonce=-1
192
+ ):
193
+ self.signer.SignModifyOrder.argtypes = [
194
+ ctypes.c_int,
195
+ ctypes.c_longlong,
196
+ ctypes.c_longlong,
197
+ ctypes.c_longlong,
198
+ ctypes.c_longlong,
199
+ ctypes.c_longlong,
200
+ ]
201
+ self.signer.SignModifyOrder.restype = ctypes.c_char_p
202
+ return self.signer.SignModifyOrder(
203
+ market_index, order_index, base_amount, price, trigger_price, nonce
204
+ ).decode("utf-8")
205
+
206
+ def sign_transfer(self, to_account_index, usdc_amount, nonce=-1):
207
+ self.signer.SignTransfer.argtypes = [
208
+ ctypes.c_longlong,
209
+ ctypes.c_longlong,
210
+ ctypes.c_longlong,
211
+ ]
212
+ self.signer.SignTransfer.restype = ctypes.c_char_p
213
+ return self.signer.SignTransfer(to_account_index, usdc_amount, nonce).decode(
214
+ "utf-8"
215
+ )
216
+
217
+ def sign_create_public_pool(
218
+ self, operator_fee, initial_total_shares, min_operator_share_rate, nonce=-1
219
+ ):
220
+ self.signer.SignCreatePublicPool.argtypes = [
221
+ ctypes.c_longlong,
222
+ ctypes.c_longlong,
223
+ ctypes.c_longlong,
224
+ ctypes.c_longlong,
225
+ ]
226
+ self.signer.SignCreatePublicPool.restype = ctypes.c_char_p
227
+ return self.signer.SignCreatePublicPool(
228
+ operator_fee, initial_total_shares, min_operator_share_rate, nonce
229
+ ).decode("utf-8")
230
+
231
+ def sign_update_public_pool(
232
+ self, public_pool_index, status, operator_fee, min_operator_share_rate, nonce=-1
233
+ ):
234
+ self.signer.SignUpdatePublicPool.argtypes = [
235
+ ctypes.c_longlong,
236
+ ctypes.c_int,
237
+ ctypes.c_longlong,
238
+ ctypes.c_longlong,
239
+ ctypes.c_longlong,
240
+ ]
241
+ self.signer.SignUpdatePublicPool.restype = ctypes.c_char_p
242
+ return self.signer.SignUpdatePublicPool(
243
+ public_pool_index, status, operator_fee, min_operator_share_rate, nonce
244
+ ).decode("utf-8")
245
+
246
+ def sign_mint_shares(self, public_pool_index, share_amount, nonce=-1):
247
+ self.signer.SignMintShares.argtypes = [
248
+ ctypes.c_longlong,
249
+ ctypes.c_longlong,
250
+ ctypes.c_longlong,
251
+ ]
252
+ self.signer.SignMintShares.restype = ctypes.c_char_p
253
+ return self.signer.SignMintShares(
254
+ public_pool_index, share_amount, nonce
255
+ ).decode("utf-8")
256
+
257
+ def sign_burn_shares(self, public_pool_index, share_amount, nonce=-1):
258
+ self.signer.SignBurnShares.argtypes = [
259
+ ctypes.c_longlong,
260
+ ctypes.c_longlong,
261
+ ctypes.c_longlong,
262
+ ]
263
+ self.signer.SignBurnShares.restype = ctypes.c_char_p
264
+ return self.signer.SignBurnShares(
265
+ public_pool_index, share_amount, nonce
266
+ ).decode("utf-8")
267
+
268
+ def sign_update_leverage(self, market_index, leverage, nonce=-1):
269
+ self.signer.SignUpdateLeverage.argtypes = [
270
+ ctypes.c_int,
271
+ ctypes.c_int,
272
+ ctypes.c_longlong,
273
+ ]
274
+ self.signer.SignUpdateLeverage.restype = ctypes.c_char_p
275
+ return self.signer.SignUpdateLeverage(market_index, leverage, nonce).decode(
276
+ "utf-8"
277
+ )
278
+
279
+ def create_auth_token_with_expiry(self, deadline: int=DEFAULT_10_MIN_AUTH_EXPIRY):
280
+ if deadline == SignerClient.DEFAULT_10_MIN_AUTH_EXPIRY:
281
+ deadline = int(time.time() + 10 * SignerClient.MINUTE)
282
+ self.signer.CreateAuthToken.argtypes = [ctypes.c_longlong]
283
+ self.signer.CreateAuthToken.restype = ctypes.c_char_p
284
+ return self.signer.CreateAuthToken(deadline).decode("utf-8")
285
+
286
+ async def create_order(
287
+ self,
288
+ market_index,
289
+ client_order_index,
290
+ base_amount,
291
+ price,
292
+ is_ask,
293
+ order_type,
294
+ time_in_force,
295
+ reduce_only=False,
296
+ trigger_price=NIL_TRIGGER_PRICE,
297
+ order_expiry=-1,
298
+ nonce=-1,
299
+ ) -> (CreateOrder, TxHash):
300
+ tx_info = self.sign_create_order(
301
+ market_index,
302
+ client_order_index,
303
+ base_amount,
304
+ price,
305
+ int(is_ask),
306
+ order_type,
307
+ time_in_force,
308
+ int(reduce_only),
309
+ trigger_price,
310
+ order_expiry,
311
+ nonce,
312
+ )
313
+ logging.debug(f"Create Order Tx Info: {tx_info}")
314
+
315
+ api_response = await self.tx_api.send_tx(
316
+ tx_type=self.TX_TYPE_CREATE_ORDER, tx_info=tx_info
317
+ )
318
+ logging.debug(f"Create Order Send Tx Response: {api_response}")
319
+ return CreateOrder.from_json(tx_info), api_response
320
+
321
+ async def cancel_order(
322
+ self, market_index, order_index, nonce=-1
323
+ ) -> (CancelOrder, TxHash):
324
+ tx_info = self.sign_cancel_order(market_index, order_index, nonce)
325
+ logging.debug(f"Cancel Order Tx Info: {tx_info}")
326
+
327
+ api_response = await self.tx_api.send_tx(
328
+ tx_type=self.TX_TYPE_CANCEL_ORDER, tx_info=tx_info
329
+ )
330
+ logging.debug(f"Cancel Order Send Tx Response: {api_response}")
331
+ return CancelOrder.from_json(tx_info), api_response
332
+
333
+ async def withdraw(self, usdc_amount, nonce=-1) -> (Withdraw, TxHash):
334
+ usdc_amount = int(usdc_amount * self.USDC_TICKER_SCALE)
335
+
336
+ tx_info = self.sign_withdraw(usdc_amount, nonce)
337
+ logging.debug(f"Withdraw Tx Info: {tx_info}")
338
+
339
+ api_response = await self.tx_api.send_tx(
340
+ tx_type=self.TX_TYPE_WITHDRAW, tx_info=tx_info
341
+ )
342
+ logging.debug(f"Withdraw Send Tx Response: {api_response}")
343
+ return Withdraw.from_json(tx_info), api_response
344
+
345
+ async def create_sub_account(self, nonce=-1):
346
+ tx_info = self.sign_create_sub_account(nonce)
347
+ logging.debug(f"Create Sub Account Tx Info: {tx_info}")
348
+
349
+ api_response = await self.tx_api.send_tx(
350
+ tx_type=self.TX_TYPE_CREATE_SUB_ACCOUNT, tx_info=tx_info
351
+ )
352
+ logging.debug(f"Create Sub Account Send Tx Response: {api_response}")
353
+ return api_response
354
+
355
+ async def cancel_all_orders(self, time_in_force, time, nonce=-1):
356
+ tx_info = self.sign_cancel_all_orders(time_in_force, time, nonce)
357
+ logging.debug(f"Cancel All Orders Tx Info: {tx_info}")
358
+
359
+ api_response = await self.tx_api.send_tx(
360
+ tx_type=self.TX_TYPE_CANCEL_ALL_ORDERS, tx_info=tx_info
361
+ )
362
+ logging.debug(f"Cancel All Orders Send Tx Response: {api_response}")
363
+ return api_response
364
+
365
+ async def modify_order(
366
+ self, market_index, order_index, base_amount, price, trigger_price, nonce=-1
367
+ ):
368
+ tx_info = self.sign_modify_order(
369
+ market_index, order_index, base_amount, price, trigger_price, nonce
370
+ )
371
+ logging.debug(f"Modify Order Tx Info: {tx_info}")
372
+
373
+ api_response = await self.tx_api.send_tx(
374
+ tx_type=self.TX_TYPE_MODIFY_ORDER, tx_info=tx_info
375
+ )
376
+ logging.debug(f"Modify Order Send Tx Response: {api_response}")
377
+ return api_response
378
+
379
+ async def transfer(self, to_account_index, usdc_amount, nonce=-1):
380
+ usdc_amount = int(usdc_amount * self.USDC_TICKER_SCALE)
381
+
382
+ tx_info = self.sign_transfer(to_account_index, usdc_amount, nonce)
383
+ logging.debug(f"Transfer Tx Info: {tx_info}")
384
+
385
+ api_response = await self.tx_api.send_tx(
386
+ tx_type=self.TX_TYPE_TRANSFER, tx_info=tx_info
387
+ )
388
+ logging.debug(f"Transfer Send Tx Response: {api_response}")
389
+ return api_response
390
+
391
+ async def create_public_pool(
392
+ self, operator_fee, initial_total_shares, min_operator_share_rate, nonce=-1
393
+ ):
394
+ tx_info = self.sign_create_public_pool(
395
+ operator_fee, initial_total_shares, min_operator_share_rate, nonce
396
+ )
397
+ logging.debug(f"Create Public Pool Tx Info: {tx_info}")
398
+
399
+ api_response = await self.tx_api.send_tx(
400
+ tx_type=self.TX_TYPE_CREATE_PUBLIC_POOL, tx_info=tx_info
401
+ )
402
+ logging.debug(f"Create Public Pool Send Tx Response: {api_response}")
403
+ return api_response
404
+
405
+ async def update_public_pool(
406
+ self, public_pool_index, status, operator_fee, min_operator_share_rate, nonce=-1
407
+ ):
408
+ tx_info = self.sign_update_public_pool(
409
+ public_pool_index, status, operator_fee, min_operator_share_rate, nonce
410
+ )
411
+ logging.debug(f"Update Public Pool Tx Info: {tx_info}")
412
+
413
+ api_response = await self.tx_api.send_tx(
414
+ tx_type=self.TX_TYPE_UPDATE_PUBLIC_POOL, tx_info=tx_info
415
+ )
416
+ logging.debug(f"Update Public Pool Send Tx Response: {api_response}")
417
+ return api_response
418
+
419
+ async def mint_shares(self, public_pool_index, share_amount, nonce=-1):
420
+ tx_info = self.sign_mint_shares(public_pool_index, share_amount, nonce)
421
+ logging.debug(f"Mint Shares Tx Info: {tx_info}")
422
+
423
+ api_response = await self.tx_api.send_tx(
424
+ tx_type=self.TX_TYPE_MINT_SHARES, tx_info=tx_info
425
+ )
426
+ logging.debug(f"Mint Shares Send Tx Response: {api_response}")
427
+ return api_response
428
+
429
+ async def burn_shares(self, public_pool_index, share_amount, nonce=-1):
430
+ tx_info = self.sign_burn_shares(public_pool_index, share_amount, nonce)
431
+ logging.debug(f"Burn Shares Tx Info: {tx_info}")
432
+
433
+ api_response = await self.tx_api.send_tx(
434
+ tx_type=self.TX_TYPE_BURN_SHARES, tx_info=tx_info
435
+ )
436
+ logging.debug(f"Burn Shares Send Tx Response: {api_response}")
437
+ return api_response
438
+
439
+ async def close(self):
440
+ await self.api_client.close()
Binary file
Binary file
@@ -0,0 +1,3 @@
1
+ from lighter.transactions.cancel_order import CancelOrder
2
+ from lighter.transactions.create_order import CreateOrder
3
+ from lighter.transactions.withdraw import Withdraw
@@ -0,0 +1,27 @@
1
+ import json
2
+ from typing import Optional
3
+
4
+
5
+ class CancelOrder:
6
+ def __init__(self):
7
+ self.account_index: Optional[int] = None
8
+ self.order_book_index: Optional[int] = None
9
+ self.order_nonce: Optional[int] = None
10
+ self.expired_at: Optional[int] = None
11
+ self.nonce: Optional[int] = None
12
+ self.sig: Optional[str] = None
13
+
14
+ @classmethod
15
+ def from_json(cls, json_str: str) -> 'CancelOrder':
16
+ params = json.loads(json_str)
17
+ self = cls()
18
+ self.account_index = params.get('AccountIndex')
19
+ self.order_book_index = params.get('OrderBookIndex')
20
+ self.order_nonce = params.get('OrderNonce')
21
+ self.expired_at = params.get('ExpiredAt')
22
+ self.nonce = params.get('Nonce')
23
+ self.sig = params.get('Sig')
24
+ return self
25
+
26
+ def to_json(self) -> str:
27
+ return json.dumps(self.__dict__, default=str)
@@ -0,0 +1,33 @@
1
+ import json
2
+ from typing import Optional
3
+
4
+
5
+ class CreateOrder:
6
+ def __init__(self):
7
+ self.account_index: Optional[int] = None
8
+ self.order_book_index: Optional[int] = None
9
+ self.base_amount: Optional[int] = None
10
+ self.price: Optional[int] = None
11
+ self.is_ask: Optional[int] = None
12
+ self.order_type: Optional[int] = None
13
+ self.expired_at: Optional[int] = None
14
+ self.nonce: Optional[int] = None
15
+ self.sig: Optional[str] = None
16
+
17
+ @classmethod
18
+ def from_json(cls, json_str: str) -> 'CreateOrder':
19
+ params = json.loads(json_str)
20
+ self = cls()
21
+ self.account_index = params.get('AccountIndex')
22
+ self.order_book_index = params.get('OrderBookIndex')
23
+ self.base_amount = params.get('BaseAmount')
24
+ self.price = params.get('Price')
25
+ self.is_ask = params.get('IsAsk')
26
+ self.order_type = params.get('OrderType')
27
+ self.expired_at = params.get('ExpiredAt')
28
+ self.nonce = params.get('Nonce')
29
+ self.sig = params.get('Sig')
30
+ return self
31
+
32
+ def to_json(self) -> str:
33
+ return json.dumps(self.__dict__, default=str)
@@ -0,0 +1,25 @@
1
+ import json
2
+ from typing import Optional
3
+
4
+
5
+ class Withdraw:
6
+ def __init__(self):
7
+ self.from_account_index: Optional[int] = None
8
+ self.collateral_amount: Optional[int] = None
9
+ self.expired_at: Optional[int] = None
10
+ self.nonce: Optional[int] = None
11
+ self.sig: Optional[str] = None
12
+
13
+ @classmethod
14
+ def from_json(cls, json_str: str) -> 'Withdraw':
15
+ params = json.loads(json_str)
16
+ instance = cls()
17
+ instance.from_account_index = params.get('FromAccountIndex')
18
+ instance.collateral_amount = params.get('CollateralAmount')
19
+ instance.expired_at = params.get('ExpiredAt')
20
+ instance.nonce = params.get('Nonce')
21
+ instance.sig = params.get('Sig')
22
+ return instance
23
+
24
+ def to_json(self) -> str:
25
+ return json.dumps(self.__dict__, default=str)
lighter/ws_client.py ADDED
@@ -0,0 +1,160 @@
1
+ import json
2
+ from websockets.sync.client import connect
3
+ from websockets.client import connect as connect_async
4
+ from lighter.configuration import Configuration
5
+
6
+
7
+ class WsClient:
8
+ def __init__(
9
+ self,
10
+ host=None,
11
+ path="/stream",
12
+ order_book_ids=[],
13
+ account_ids=[],
14
+ on_order_book_update=print,
15
+ on_account_update=print,
16
+ ):
17
+ if host is None:
18
+ host = Configuration.get_default().host.replace("https://", "")
19
+
20
+ self.base_url = f"wss://{host}{path}"
21
+
22
+ self.subscriptions = {
23
+ "order_books": order_book_ids,
24
+ "accounts": account_ids,
25
+ }
26
+
27
+ if len(order_book_ids) == 0 and len(account_ids) == 0:
28
+ raise Exception("No subscriptions provided.")
29
+
30
+ self.order_book_states = {}
31
+ self.account_states = {}
32
+
33
+ self.on_order_book_update = on_order_book_update
34
+ self.on_account_update = on_account_update
35
+
36
+ self.ws = None
37
+
38
+ def on_message(self, ws, message):
39
+ if isinstance(message, str):
40
+ message = json.loads(message)
41
+
42
+ message_type = message.get("type")
43
+
44
+ if message_type == "connected":
45
+ self.handle_connected(ws)
46
+ elif message_type == "subscribed/order_book":
47
+ self.handle_subscribed_order_book(message)
48
+ elif message_type == "update/order_book":
49
+ self.handle_update_order_book(message)
50
+ elif message_type == "subscribed/account_all":
51
+ self.handle_subscribed_account(message)
52
+ elif message_type == "update/account_all":
53
+ self.handle_update_account(message)
54
+ else:
55
+ self.handle_unhandled_message(message)
56
+
57
+ async def on_message_async(self, ws, message):
58
+ message = json.loads(message)
59
+ message_type = message.get("type")
60
+
61
+ if message_type == "connected":
62
+ await self.handle_connected_async(ws)
63
+ else:
64
+ self.on_message(ws, message)
65
+
66
+ def handle_connected(self, ws):
67
+ for market_id in self.subscriptions["order_books"]:
68
+ ws.send(
69
+ json.dumps({"type": "subscribe", "channel": f"order_book/{market_id}"})
70
+ )
71
+ for account_id in self.subscriptions["accounts"]:
72
+ ws.send(
73
+ json.dumps(
74
+ {"type": "subscribe", "channel": f"account_all/{account_id}"}
75
+ )
76
+ )
77
+
78
+ async def handle_connected_async(self, ws):
79
+ for market_id in self.subscriptions["order_books"]:
80
+ await ws.send(
81
+ json.dumps({"type": "subscribe", "channel": f"order_book/{market_id}"})
82
+ )
83
+ for account_id in self.subscriptions["accounts"]:
84
+ await ws.send(
85
+ json.dumps(
86
+ {"type": "subscribe", "channel": f"account_all/{account_id}"}
87
+ )
88
+ )
89
+
90
+ def handle_subscribed_order_book(self, message):
91
+ market_id = message["channel"].split(":")[1]
92
+ self.order_book_states[market_id] = message["order_book"]
93
+ if self.on_order_book_update:
94
+ self.on_order_book_update(market_id, self.order_book_states[market_id])
95
+
96
+ def handle_update_order_book(self, message):
97
+ market_id = message["channel"].split(":")[1]
98
+ self.update_order_book_state(market_id, message["order_book"])
99
+ if self.on_order_book_update:
100
+ self.on_order_book_update(market_id, self.order_book_states[market_id])
101
+
102
+ def update_order_book_state(self, market_id, order_book):
103
+ self.update_orders(
104
+ order_book["asks"], self.order_book_states[market_id]["asks"]
105
+ )
106
+ self.update_orders(
107
+ order_book["bids"], self.order_book_states[market_id]["bids"]
108
+ )
109
+
110
+ def update_orders(self, new_orders, existing_orders):
111
+ for new_order in new_orders:
112
+ is_new_order = True
113
+ for existing_order in existing_orders:
114
+ if new_order["price"] == existing_order["price"]:
115
+ is_new_order = False
116
+ existing_order["size"] = new_order["size"]
117
+ if float(new_order["size"]) == 0:
118
+ existing_orders.remove(existing_order)
119
+ break
120
+ if is_new_order:
121
+ existing_orders.append(new_order)
122
+
123
+ existing_orders = [
124
+ order for order in existing_orders if float(order["size"]) > 0
125
+ ]
126
+
127
+ def handle_subscribed_account(self, message):
128
+ account_id = message["channel"].split(":")[1]
129
+ self.account_states[account_id] = message
130
+ if self.on_account_update:
131
+ self.on_account_update(account_id, self.account_states[account_id])
132
+
133
+ def handle_update_account(self, message):
134
+ account_id = message["channel"].split(":")[1]
135
+ self.account_states[account_id] = message
136
+ if self.on_account_update:
137
+ self.on_account_update(account_id, self.account_states[account_id])
138
+
139
+ def handle_unhandled_message(self, message):
140
+ raise Exception(f"Unhandled message: {message}")
141
+
142
+ def on_error(self, ws, error):
143
+ raise Exception(f"Error: {error}")
144
+
145
+ def on_close(self, ws, close_status_code, close_msg):
146
+ raise Exception(f"Closed: {close_status_code} {close_msg}")
147
+
148
+ def run(self):
149
+ ws = connect(self.base_url)
150
+ self.ws = ws
151
+
152
+ for message in ws:
153
+ self.on_message(ws, message)
154
+
155
+ async def run_async(self):
156
+ ws = await connect_async(self.base_url)
157
+ self.ws = ws
158
+
159
+ async for message in ws:
160
+ await self.on_message_async(ws, message)