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.
- lighter/__init__.py +140 -0
- lighter/api/__init__.py +11 -0
- lighter/api/account_api.py +2371 -0
- lighter/api/block_api.py +863 -0
- lighter/api/candlestick_api.py +718 -0
- lighter/api/info_api.py +282 -0
- lighter/api/order_api.py +2734 -0
- lighter/api/root_api.py +529 -0
- lighter/api/transaction_api.py +3525 -0
- lighter/api_client.py +784 -0
- lighter/api_response.py +21 -0
- lighter/configuration.py +475 -0
- lighter/exceptions.py +199 -0
- lighter/models/__init__.py +112 -0
- lighter/models/account.py +110 -0
- lighter/models/account_api_keys.py +104 -0
- lighter/models/account_market_stats.py +98 -0
- lighter/models/account_metadata.py +94 -0
- lighter/models/account_pn_l.py +106 -0
- lighter/models/account_position.py +112 -0
- lighter/models/account_stats.py +102 -0
- lighter/models/accounts.py +106 -0
- lighter/models/api_key.py +98 -0
- lighter/models/block.py +124 -0
- lighter/models/blocks.py +106 -0
- lighter/models/bridge_supported_network.py +96 -0
- lighter/models/candlestick.py +106 -0
- lighter/models/candlesticks.py +106 -0
- lighter/models/contract_address.py +94 -0
- lighter/models/current_height.py +96 -0
- lighter/models/cursor.py +92 -0
- lighter/models/deposit_history.py +106 -0
- lighter/models/deposit_history_item.py +105 -0
- lighter/models/detailed_account.py +152 -0
- lighter/models/detailed_accounts.py +106 -0
- lighter/models/detailed_candlestick.py +108 -0
- lighter/models/enriched_tx.py +129 -0
- lighter/models/exchange_stats.py +110 -0
- lighter/models/fee_bucket.py +96 -0
- lighter/models/funding.py +98 -0
- lighter/models/fundings.py +106 -0
- lighter/models/is_whitelisted.py +98 -0
- lighter/models/l1_provider_info.py +98 -0
- lighter/models/layer2_basic_info.py +100 -0
- lighter/models/liquidation.py +100 -0
- lighter/models/market_info.py +116 -0
- lighter/models/next_nonce.py +96 -0
- lighter/models/order.py +159 -0
- lighter/models/order_book.py +119 -0
- lighter/models/order_book_depth.py +115 -0
- lighter/models/order_book_detail.py +151 -0
- lighter/models/order_book_details.py +104 -0
- lighter/models/order_book_orders.py +117 -0
- lighter/models/order_book_stats.py +102 -0
- lighter/models/order_books.py +104 -0
- lighter/models/orders.py +106 -0
- lighter/models/pn_l_entry.py +94 -0
- lighter/models/position_funding.py +111 -0
- lighter/models/price_level.py +94 -0
- lighter/models/public_pool.py +128 -0
- lighter/models/public_pool_info.py +100 -0
- lighter/models/public_pool_share.py +96 -0
- lighter/models/public_pools.py +106 -0
- lighter/models/req_get_account.py +101 -0
- lighter/models/req_get_account_active_orders.py +96 -0
- lighter/models/req_get_account_api_keys.py +94 -0
- lighter/models/req_get_account_by_l1_address.py +92 -0
- lighter/models/req_get_account_inactive_orders.py +103 -0
- lighter/models/req_get_account_orders.py +99 -0
- lighter/models/req_get_account_pending_txs.py +106 -0
- lighter/models/req_get_account_pn_l.py +118 -0
- lighter/models/req_get_account_txs.py +111 -0
- lighter/models/req_get_block.py +101 -0
- lighter/models/req_get_block_txs.py +101 -0
- lighter/models/req_get_by_account.py +101 -0
- lighter/models/req_get_candlesticks.py +109 -0
- lighter/models/req_get_deposit_history.py +106 -0
- lighter/models/req_get_fee_bucket.py +92 -0
- lighter/models/req_get_fundings.py +107 -0
- lighter/models/req_get_l1_tx.py +92 -0
- lighter/models/req_get_latest_deposit.py +92 -0
- lighter/models/req_get_next_nonce.py +94 -0
- lighter/models/req_get_order_book_details.py +92 -0
- lighter/models/req_get_order_book_orders.py +95 -0
- lighter/models/req_get_order_books.py +92 -0
- lighter/models/req_get_public_pools.py +109 -0
- lighter/models/req_get_range_with_cursor.py +95 -0
- lighter/models/req_get_range_with_index.py +95 -0
- lighter/models/req_get_range_with_index_sortable.py +107 -0
- lighter/models/req_get_recent_trades.py +95 -0
- lighter/models/req_get_trades.py +126 -0
- lighter/models/req_get_tx.py +101 -0
- lighter/models/req_get_withdraw_history.py +106 -0
- lighter/models/req_is_whitelisted.py +92 -0
- lighter/models/result_code.py +94 -0
- lighter/models/simple_order.py +102 -0
- lighter/models/status.py +94 -0
- lighter/models/sub_accounts.py +106 -0
- lighter/models/ticker.py +103 -0
- lighter/models/trade.py +125 -0
- lighter/models/trades.py +106 -0
- lighter/models/tx.py +121 -0
- lighter/models/tx_hash.py +96 -0
- lighter/models/tx_hashes.py +96 -0
- lighter/models/txs.py +104 -0
- lighter/models/validator_info.py +94 -0
- lighter/models/withdraw_history.py +110 -0
- lighter/models/withdraw_history_cursor.py +94 -0
- lighter/models/withdraw_history_item.py +114 -0
- lighter/models/zk_lighter_info.py +92 -0
- lighter/py.typed +0 -0
- lighter/rest.py +215 -0
- lighter/signer_client.py +440 -0
- lighter/signers/signer-amd64.so +0 -0
- lighter/signers/signer-arm64.dylib +0 -0
- lighter/transactions/__init__.py +3 -0
- lighter/transactions/cancel_order.py +27 -0
- lighter/transactions/create_order.py +33 -0
- lighter/transactions/withdraw.py +25 -0
- lighter/ws_client.py +160 -0
- lighter_sdk-0.1.0.dist-info/LICENSE +201 -0
- lighter_sdk-0.1.0.dist-info/METADATA +26 -0
- lighter_sdk-0.1.0.dist-info/RECORD +125 -0
- lighter_sdk-0.1.0.dist-info/WHEEL +5 -0
- lighter_sdk-0.1.0.dist-info/top_level.txt +1 -0
lighter/signer_client.py
ADDED
@@ -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,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)
|