diff --git a/examples/README.md b/examples/README.md index 13a3e5a..43780cf 100644 --- a/examples/README.md +++ b/examples/README.md @@ -9,14 +9,14 @@ - the API key config is saved in a local file `./api_key_config.json` ## Start trading on testnet -- `create_modify_cancel_order.py` +- `create_modify_cancel_order_http.py` - creates an ask (sell) order for 0.1 ETH @ $4050 - modified the order and increases the size to 0.11 ETH and increases the price to $4100 - cancels the order - Note: all of these operations use the client order index of the order. You can use the order from the exchange as well -- `ws_send_tx.py` - - same flow as `create_modify_cancel_order.py` +- `create_modify_cancel_order_ws.py` + - same flow as `create_modify_cancel_order_http.py` - sends TXs over WS instead of HTTP - `create_grouped_ioc_with_attached_sl_tp.py` @@ -25,7 +25,7 @@ - the size of the SL/TP will be equal to the executed size of the order - the SL/TP orders are canceled when the sign of your position changes -- `create_position_tied_sl_tl.py` +- `create_position_tied_sl_tp.py` - creates a bid (buy) Stop Loss (SL) and a Take Profit (TP) to close your short position - the size of the orders will be for your whole position (because BaseAmount=0) - the orders will grow / shrink as you accumulate more position @@ -43,6 +43,24 @@ What about the order types? Just as normal orders, SL/TP orders trigger an order - market order - limit IOC / GTC +### Modify leverage / Margin Mode (Cross, Isolated) / Add Collateral to isolated-only positions +- `margin_eth_20x_cross_http` + - sets ETH market to 20x leverage and cross-margin mode, using HTTP +- `margin_eth_50x_isolate_ws` + - sets ETH market to 50x leverage and isolated margin mode, using HTTP +- `margin_eth_add_collateral_http.py` + - adds $10.5 USDC to the ETH position (must be opened and in isolated mode) +- `margin_eth_remove_collateral_ws.py` + - removes $5 USDC from the ETH position (must be opened and in isolated mode) + +### Batch orders +- `send_batch_tx_http.py` + - sends multiple orders in a single HTTP request +- `send_batch_tx_ws.py` + - sends multiple orders in a single WS request` + +Batch TXs will be executed back to back, without the possibility of other TXs interfering. + ## Setup steps for mainnet - deposit money on Lighter to create an account first - change the URL to `mainnet.zklighter.elliot.ai` diff --git a/examples/create_grouped_ioc_with_attached_sl_tp.py b/examples/create_grouped_ioc_with_attached_sl_tp.py index b08e36f..4fd9012 100644 --- a/examples/create_grouped_ioc_with_attached_sl_tp.py +++ b/examples/create_grouped_ioc_with_attached_sl_tp.py @@ -1,5 +1,4 @@ import asyncio -import lighter from lighter.signer_client import CreateOrderTxReq from utils import default_example_setup @@ -7,14 +6,22 @@ async def main(): client, api_client, _ = default_example_setup() + # Sell some ETH at $2500 + # The size of the SL/TP orders will be equal to the size of the executed order + + # set SL trigger price at 5000 and limit price at 5050 + # set TP trigger price at 1500 and limit price at 1550 + # Note: set the limit price to be higher than the SL/TP trigger price to ensure the order will be filled + # If the mark price of ETH reaches 1500, there might be no one willing to sell you ETH at 1500, so trying to buy at 1550 would increase the fill rate + ioc_order = CreateOrderTxReq( MarketIndex=0, ClientOrderIndex=0, BaseAmount=1000, # 0.1 ETH - Price=300000, # $3000 + Price=2500_00, # $2500 IsAsk=1, # sell - Type=lighter.SignerClient.ORDER_TYPE_LIMIT, - TimeInForce=lighter.SignerClient.ORDER_TIME_IN_FORCE_IMMEDIATE_OR_CANCEL, + Type=client.ORDER_TYPE_LIMIT, + TimeInForce=client.ORDER_TIME_IN_FORCE_IMMEDIATE_OR_CANCEL, ReduceOnly=0, TriggerPrice=0, OrderExpiry=0, @@ -25,12 +32,12 @@ async def main(): MarketIndex=0, ClientOrderIndex=0, BaseAmount=0, - Price=300000, + Price=1550_00, IsAsk=0, - Type=lighter.SignerClient.ORDER_TYPE_TAKE_PROFIT_LIMIT, - TimeInForce=lighter.SignerClient.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, + Type=client.ORDER_TYPE_TAKE_PROFIT_LIMIT, + TimeInForce=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, ReduceOnly=1, - TriggerPrice=300000, + TriggerPrice=1500_00, OrderExpiry=-1, ) @@ -38,17 +45,17 @@ async def main(): MarketIndex=0, ClientOrderIndex=0, BaseAmount=0, - Price=500000, + Price=5050_00, IsAsk=0, - Type=lighter.SignerClient.ORDER_TYPE_STOP_LOSS_LIMIT, - TimeInForce=lighter.SignerClient.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, + Type=client.ORDER_TYPE_STOP_LOSS_LIMIT, + TimeInForce=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, ReduceOnly=1, - TriggerPrice=500000, + TriggerPrice=5000_00, OrderExpiry=-1, ) transaction = await client.create_grouped_orders( - grouping_type=lighter.SignerClient.GROUPING_TYPE_ONE_TRIGGERS_A_ONE_CANCELS_THE_OTHER, + grouping_type=client.GROUPING_TYPE_ONE_TRIGGERS_A_ONE_CANCELS_THE_OTHER, orders=[ioc_order, take_profit_order, stop_loss_order], ) @@ -57,5 +64,6 @@ async def main(): await client.close() await api_client.close() + if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/create_modify_cancel_order.py b/examples/create_modify_cancel_order_http.py similarity index 58% rename from examples/create_modify_cancel_order.py rename to examples/create_modify_cancel_order_http.py index 2de927c..05637f2 100644 --- a/examples/create_modify_cancel_order.py +++ b/examples/create_modify_cancel_order_http.py @@ -1,43 +1,54 @@ import asyncio -import lighter from utils import default_example_setup async def main(): client, api_client, _ = default_example_setup() + client.check_client() # create order + api_key_index, nonce = client.nonce_manager.next_nonce() tx, tx_hash, err = await client.create_order( market_index=0, client_order_index=123, base_amount=1000, # 0.1 ETH - price=405000, # $4050 + price=4050_00, # $4050 is_ask=True, - order_type=lighter.SignerClient.ORDER_TYPE_LIMIT, - time_in_force=lighter.SignerClient.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, + order_type=client.ORDER_TYPE_LIMIT, + time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, reduce_only=False, trigger_price=0, + nonce=nonce, + api_key_index=api_key_index, ) print(f"Create Order {tx=} {tx_hash=} {err=}") if err is not None: raise Exception(err) - # create order + ## modify order + # use the same API key so the TX goes after the create order TX + api_key_index, nonce = client.nonce_manager.next_nonce(api_key_index) tx, tx_hash, err = await client.modify_order( market_index=0, order_index=123, base_amount=1100, # 0.11 ETH - price=410000, # $4100 + price=4100_00, # $4100 trigger_price=0, + nonce=nonce, + api_key_index=api_key_index, ) print(f"Modify Order {tx=} {tx_hash=} {err=}") if err is not None: raise Exception(err) - # cancel order + ## cancel order + # use the same API key so the TX goes after the modify order TX + api_key_index, nonce = client.nonce_manager.next_nonce(api_key_index) tx, tx_hash, err = await client.cancel_order( market_index=0, order_index=123, + nonce=nonce, + api_key_index=api_key_index, ) print(f"Cancel Order {tx=} {tx_hash=} {err=}") if err is not None: diff --git a/examples/create_modify_cancel_order_ws.py b/examples/create_modify_cancel_order_ws.py new file mode 100644 index 0000000..63e1e73 --- /dev/null +++ b/examples/create_modify_cancel_order_ws.py @@ -0,0 +1,68 @@ +import websockets +import asyncio +from utils import default_example_setup, ws_send_tx + + +# this example does the same thing as the create_modify_cancel_order.py example, but sends the TX over WS instead of HTTP +async def main(): + client, api_client, ws_client_promise = default_example_setup() + + # set up WS client and print a connected message + ws_client: websockets.ClientConnection = await ws_client_promise + print("Received:", await ws_client.recv()) + + # create order + api_key_index, nonce = client.nonce_manager.next_nonce() + tx_type, tx_info, tx_hash, err = client.sign_create_order( + market_index=0, + client_order_index=123, + base_amount=1000, # 0.1 ETH + price=4050_00, # $4050 + is_ask=True, + order_type=client.ORDER_TYPE_LIMIT, + time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, + reduce_only=False, + trigger_price=0, + nonce=nonce, + api_key_index=api_key_index, + ) + if err is not None: + raise Exception(err) + await ws_send_tx(ws_client, tx_type, tx_info, tx_hash) + + ## modify order + # use the same API key so the TX goes after the create order TX + api_key_index, nonce = client.nonce_manager.next_nonce(api_key_index) + tx_type, tx_info, tx_hash, err = client.sign_modify_order( + market_index=0, + order_index=123, + base_amount=1100, # 0.11 ETH + price=4100_00, # $4100 + trigger_price=0, + nonce=nonce, + api_key_index=api_key_index, + ) + if err is not None: + raise Exception(err) + await ws_send_tx(ws_client, tx_type, tx_info, tx_hash) + + ## cancel order + # use the same API key so the TX goes after the modify order TX + api_key_index, nonce = client.nonce_manager.next_nonce(api_key_index) + tx_type, tx_info, tx_hash, err = client.sign_cancel_order( + market_index=0, + order_index=123, + nonce=nonce, + api_key_index=api_key_index, + ) + if err is not None: + raise Exception(err) + await ws_send_tx(ws_client, tx_type, tx_info, tx_hash) + + await client.close() + await api_client.close() + await ws_client.close() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/create_position_tied_sl_tl.py b/examples/create_position_tied_sl_tl.py deleted file mode 100644 index 936f7f9..0000000 --- a/examples/create_position_tied_sl_tl.py +++ /dev/null @@ -1,48 +0,0 @@ -import asyncio -import lighter - -from lighter.signer_client import CreateOrderTxReq -from utils import default_example_setup - - -async def main(): - client, api_client, _ = default_example_setup() - - # Create a One-Cancels-the-Other grouped order with a take-profit and a stop-loss order - take_profit_order = CreateOrderTxReq( - MarketIndex=0, - ClientOrderIndex=0, - BaseAmount=0, - Price=300000, - IsAsk=0, - Type=lighter.SignerClient.ORDER_TYPE_TAKE_PROFIT_LIMIT, - TimeInForce=lighter.SignerClient.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, - ReduceOnly=1, - TriggerPrice=300000, - OrderExpiry=-1, - ) - - stop_loss_order = CreateOrderTxReq( - MarketIndex=0, - ClientOrderIndex=0, - BaseAmount=0, - Price=500000, - IsAsk=0, - Type=lighter.SignerClient.ORDER_TYPE_STOP_LOSS_LIMIT, - TimeInForce=lighter.SignerClient.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, - ReduceOnly=1, - TriggerPrice=500000, - OrderExpiry=-1, - ) - - transaction = await client.create_grouped_orders( - grouping_type=lighter.SignerClient.GROUPING_TYPE_ONE_CANCELS_THE_OTHER, - orders=[take_profit_order, stop_loss_order], - ) - - print("Create Grouped Order Tx:", transaction) - await client.close() - await api_client.close() - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/examples/create_position_tied_sl_tp.py b/examples/create_position_tied_sl_tp.py new file mode 100644 index 0000000..0b691e1 --- /dev/null +++ b/examples/create_position_tied_sl_tp.py @@ -0,0 +1,56 @@ +import asyncio +from lighter.signer_client import CreateOrderTxReq +from utils import default_example_setup + + +async def main(): + client, api_client, _ = default_example_setup() + + # Creates a position tied SL/TP pair + # The SL/TP orders will close your whole position, even if you add/remove from it later on + # if the positions reach 0 or switches from short -> long, the orders are canceled + + # this particular example, sets the SL/TP for a short position + # set SL trigger price at 5000 and limit price at 5050 + # set TP trigger price at 1500 and limit price at 1550 + # Note: set the limit price to be higher than the SL/TP trigger price to ensure the order will be filled + # If the mark price of ETH reaches 1500, there might be no one willing to sell you ETH at 1500, so trying to buy at 1550 would increase the fill rate + + # Create a One-Cancels-the-Other grouped order with a take-profit and a stop-loss order + take_profit_order = CreateOrderTxReq( + MarketIndex=0, + ClientOrderIndex=0, + BaseAmount=0, + Price=1550_00, + IsAsk=0, + Type=client.ORDER_TYPE_TAKE_PROFIT_LIMIT, + TimeInForce=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, + ReduceOnly=1, + TriggerPrice=1500_00, + OrderExpiry=-1, + ) + + stop_loss_order = CreateOrderTxReq( + MarketIndex=0, + ClientOrderIndex=0, + BaseAmount=0, + Price=4050_00, + IsAsk=0, + Type=client.ORDER_TYPE_STOP_LOSS_LIMIT, + TimeInForce=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, + ReduceOnly=1, + TriggerPrice=4000_00, + OrderExpiry=-1, + ) + + transaction = await client.create_grouped_orders( + grouping_type=client.GROUPING_TYPE_ONE_CANCELS_THE_OTHER, + orders=[take_profit_order, stop_loss_order], + ) + + print("Create Grouped Order Tx:", transaction) + await client.close() + await api_client.close() + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/create_sl_tp.py b/examples/create_sl_tp.py deleted file mode 100644 index a6b4b6d..0000000 --- a/examples/create_sl_tp.py +++ /dev/null @@ -1,70 +0,0 @@ -import asyncio -import logging -import lighter - -logging.basicConfig(level=logging.DEBUG) - -# The API_KEY_PRIVATE_KEY provided belongs to a dummy account registered on Testnet. -# It was generated using the setup_system.py script, and servers as an example. -BASE_URL = "https://testnet.zklighter.elliot.ai" -API_KEY_PRIVATE_KEY = "0xe0fa55e11d6b5575d54c0500bd2f3b240221ae90241e3b573f2307e27de20c04ea628de3f1936e56" -ACCOUNT_INDEX = 22 -API_KEY_INDEX = 3 - - -def trim_exception(e: Exception) -> str: - return str(e).strip().split("\n")[-1] - - -async def main(): - client = lighter.SignerClient( - url=BASE_URL, - private_key=API_KEY_PRIVATE_KEY, - account_index=ACCOUNT_INDEX, - api_key_index=API_KEY_INDEX, - ) - - tx = await client.create_tp_order( - market_index=0, - client_order_index=0, - base_amount=1000, # 0.1 ETH - trigger_price=500000, - price=500000, - is_ask=False - ) - print("Create Order Tx:", tx) - - - tx = await client.create_sl_order( - market_index=0, - client_order_index=0, - base_amount=1000, # 0.1 ETH - trigger_price=500000, - price=500000, - is_ask=False - ) - print("Create Order Tx:", tx) - - tx = await client.create_tp_limit_order( - market_index=0, - client_order_index=0, - base_amount=1000, # 0.1 ETH - trigger_price=500000, - price=500000, - is_ask=False - ) - - tx = await client.create_sl_limit_order( - market_index=0, - client_order_index=0, - base_amount=1000, # 0.1 ETH - trigger_price=500000, - price=500000, - is_ask=False - ) - print("Create Order Tx:", tx) - await client.close() - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/examples/create_with_multiple_keys.py b/examples/create_with_multiple_keys.py index 3a99200..94e0b2e 100644 --- a/examples/create_with_multiple_keys.py +++ b/examples/create_with_multiple_keys.py @@ -1,47 +1,30 @@ +import time import asyncio -import lighter - - -BASE_URL = "https://testnet.zklighter.elliot.ai" -# use examples/system_setup.py or the apikeys page (for mainnet) to generate new api keys -KEYS = { - 5: "API_PRIVATE_KEY_5", - 6: "API_PRIVATE_KEY_6", - 7: "API_PRIVATE_KEY_7", -} -ACCOUNT_INDEX = 100 # replace with your account_index +from utils import default_example_setup async def main(): - client = lighter.SignerClient( - url=BASE_URL, - private_key=KEYS[5], - account_index=ACCOUNT_INDEX, - api_key_index=5, - max_api_key_index=7, - private_keys=KEYS, - ) + client, api_client, _ = default_example_setup() - err = client.check_client() - if err is not None: - print(f"CheckClient error: {err}") - return + # create 20 orders. The client will use as many API keys as it was configured. for i in range(20): res_tuple = await client.create_order( market_index=0, client_order_index=123 + i, - base_amount=100000 + i, - price=385000 + i, + base_amount=1000 + i, # 0.1 ETH + dust + price=3850_00 + i, is_ask=True, - order_type=lighter.SignerClient.ORDER_TYPE_LIMIT, - time_in_force=lighter.SignerClient.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, + order_type=client.ORDER_TYPE_LIMIT, + time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, reduce_only=False, trigger_price=0, ) print(res_tuple) - await client.cancel_all_orders(time_in_force=client.CANCEL_ALL_TIF_IMMEDIATE, time=0) + # wait for orders to be created + time.sleep(1) + await client.cancel_all_orders(time_in_force=client.CANCEL_ALL_TIF_IMMEDIATE, timestamp_ms=0) if __name__ == "__main__": diff --git a/examples/margin_eth_20x_cross_http.py b/examples/margin_eth_20x_cross_http.py new file mode 100644 index 0000000..afdb023 --- /dev/null +++ b/examples/margin_eth_20x_cross_http.py @@ -0,0 +1,29 @@ +import asyncio +from utils import default_example_setup + + +async def main(): + client, api_client, _ = default_example_setup() + + # Note: the HTTP method `update_leverage` receives `leverage` as the argument, + # while the WS one that calls `sign_update_leverage` to get the TX to send it directly over WS + # receives `fraction` as the argument, which is 10_000 / leverage + # this was kept this way to not break backwards compatibility. Ideally, they would be consistent. + + tx, tx_hash, err = await client.update_leverage( + market_index=0, + leverage=20, + margin_mode=client.CROSS_MARGIN_MODE + ) + + print(f"Update Leverage {tx=} {tx_hash=} {err=}") + if err is not None: + raise Exception(err) + + await client.close() + await api_client.close() + + +if __name__ == "__main__": + asyncio.run(main()) + diff --git a/examples/margin_eth_50x_isolate_ws.py b/examples/margin_eth_50x_isolate_ws.py new file mode 100644 index 0000000..dd08b4c --- /dev/null +++ b/examples/margin_eth_50x_isolate_ws.py @@ -0,0 +1,33 @@ +import websockets +import asyncio +from utils import default_example_setup, ws_send_tx + + +async def main(): + client, api_client, ws_client_promise = default_example_setup() + + # set up WS client and print a connected message + ws_client: websockets.ClientConnection = await ws_client_promise + print("Received:", await ws_client.recv()) + + # Note: the HTTP method `update_leverage` receives `leverage` as the argument, + # while the WS one that calls `sign_update_leverage` to get the TX to send it directly over WS + # receives `fraction` as the argument, which is 10_000 / leverage + # this was kept this way to not break backwards compatibility. Ideally, they would be consistent. + + tx_type, tx_info, tx_hash, err = client.sign_update_leverage( + market_index=0, + fraction=10_000 // 50, + margin_mode=client.ISOLATED_MARGIN_MODE + ) + if err is not None: + raise Exception(err) + await ws_send_tx(ws_client, tx_type, tx_info, tx_hash) + + await client.close() + await api_client.close() + await ws_client.close() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/margin_eth_add_collateral_http.py b/examples/margin_eth_add_collateral_http.py new file mode 100644 index 0000000..c0d1fed --- /dev/null +++ b/examples/margin_eth_add_collateral_http.py @@ -0,0 +1,29 @@ +import asyncio +from utils import default_example_setup + + +async def main(): + client, api_client, _ = default_example_setup() + + # Note: the HTTP method `update_margin` receives `usdc_amount` (float) as the argument, + # while the WS one that calls `sign_update_margin` to get the TX to send it directly over WS + # receives `usdc_amount` (int) as the argument, which is the float one * 1_000_000 + # this was kept this way to not break backwards compatibility. Ideally, they would be consistent. + + tx, tx_hash, err = await client.update_margin( + market_index=0, + usdc_amount=10.5, + direction=client.ISOLATED_MARGIN_ADD_COLLATERAL + ) + + print(f"Update Margin {tx=} {tx_hash=} {err=}") + if err is not None: + raise Exception(err) + + await client.close() + await api_client.close() + + +if __name__ == "__main__": + asyncio.run(main()) + diff --git a/examples/margin_eth_remove_collateral_ws.py b/examples/margin_eth_remove_collateral_ws.py new file mode 100644 index 0000000..11dbadc --- /dev/null +++ b/examples/margin_eth_remove_collateral_ws.py @@ -0,0 +1,32 @@ +import asyncio +import websockets +from utils import default_example_setup, ws_send_tx + + +async def main(): + client, api_client, ws_client_promise = default_example_setup() + + # set up WS client and print a connected message + ws_client: websockets.ClientConnection = await ws_client_promise + print("Received:", await ws_client.recv()) + + # Note: the HTTP method `update_margin` receives `usdc_amount` (float) as the argument, + # while the WS one that calls `sign_update_margin` to get the TX to send it directly over WS + # receives `usdc_amount` (int) as the argument, which is the float one * 1_000_000 + # this was kept this way to not break backwards compatibility. Ideally, they would be consistent. + + tx_type, tx_info, tx_hash, err = client.sign_update_margin( + market_index=0, + usdc_amount=5_000_000, # 5 USDC + direction=client.ISOLATED_MARGIN_REMOVE_COLLATERAL + ) + if err is not None: + raise Exception(err) + await ws_send_tx(ws_client, tx_type, tx_info, tx_hash) + + await client.close() + await api_client.close() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/send_batch_tx_http.py b/examples/send_batch_tx_http.py new file mode 100644 index 0000000..3264099 --- /dev/null +++ b/examples/send_batch_tx_http.py @@ -0,0 +1,114 @@ +import asyncio +import time +from utils import default_example_setup, trim_exception + + +# this example does the same thing as the send_batch_tx_ws.py example, but sends the TX over HTTP instead of WS +async def main(): + client, api_client, _ = default_example_setup() + + api_key_index, nonce = client.nonce_manager.next_nonce() + ask_tx_type, ask_tx_info, ask_tx_hash, error = client.sign_create_order( + market_index=0, + client_order_index=1001, # Unique identifier for this order + base_amount=1000, # 0.1 ETH + price=5000_00, # $5000 + is_ask=True, + order_type=client.ORDER_TYPE_LIMIT, + time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, + reduce_only=False, + trigger_price=0, + nonce=nonce, + api_key_index=api_key_index, + ) + + if error is not None: + print(f"Error signing ask order (first batch): {trim_exception(error)}") + return + + # intentionally pass api_key_index to the client.nonce_manager so it increases the nonce, without changing the API key. + # in batch TXs, all TXs must come from the same API key. + api_key_index, nonce = client.nonce_manager.next_nonce(api_key_index) + bid_tx_type, bid_tx_info, bid_tx_hash, error = client.sign_create_order( + market_index=0, + client_order_index=1002, # Different unique identifier + base_amount=1000, # 0.1 ETH + price=1500_00, # $1500 + is_ask=False, + order_type=client.ORDER_TYPE_LIMIT, + time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, + reduce_only=False, + trigger_price=0, + nonce=nonce, + api_key_index=api_key_index, + ) + + if error is not None: + print(f"Error signing second order (first batch): {trim_exception(error)}") + return + + tx_types = [ask_tx_type, bid_tx_type] + tx_infos = [ask_tx_info, bid_tx_info] + tx_hashes = [ask_tx_hash, bid_tx_hash] + + try: + response = await client.send_tx_batch(tx_types=tx_types, tx_infos=tx_infos) + print(f"Batch transaction successful: {response} expected: {tx_hashes}") + except Exception as e: + print(f"Error sending batch transaction: {trim_exception(e)}") + + # In case we want to see the changes in the UI, sleep a bit + time.sleep(5) + + # since this is a new batch, we can request a fresh API key + api_key_index, nonce = client.nonce_manager.next_nonce() + cancel_tx_type, cancel_tx_info, cancel_tx_hash, error = client.sign_cancel_order( + market_index=0, + order_index=1001, # the index of the order we want cancelled + nonce=nonce, + api_key_index=api_key_index, + ) + + if error is not None: + print(f"Error signing first order (second batch): {trim_exception(error)}") + return + + # intentionally pass api_key_index to the client.nonce_manager so it increases the nonce, without changing the API key. + # in batch TXs, all TXs must come from the same API key. + api_key_index, nonce = client.nonce_manager.next_nonce(api_key_index) + new_ask_tx_type, new_ask_tx_info, new_ask_tx_hash, error = client.sign_create_order( + market_index=0, + client_order_index=1003, # Different unique identifier + base_amount=2000, # 0.2 ETH + price=5500_00, # $5500 + is_ask=True, + order_type=client.ORDER_TYPE_LIMIT, + time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, + reduce_only=False, + trigger_price=0, + nonce=nonce, + api_key_index=api_key_index, + ) + + if error is not None: + print(f"Error signing second order (second batch): {trim_exception(error)}") + return + + tx_types = [cancel_tx_type, new_ask_tx_type] + tx_infos = [cancel_tx_info, new_ask_tx_info] + tx_hashes = [ask_tx_hash, bid_tx_hash] + + try: + response = await client.send_tx_batch(tx_types=tx_types, tx_infos=tx_infos) + print(f"Batch transaction successful: {response} expected: {tx_hashes}") + except Exception as e: + print(f"Error sending batch transaction: {trim_exception(e)}") + + # Clean up + await client.close() + await api_client.close() + + +# Run the async main function +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/send_batch_tx_ws.py b/examples/send_batch_tx_ws.py new file mode 100644 index 0000000..9fca11d --- /dev/null +++ b/examples/send_batch_tx_ws.py @@ -0,0 +1,112 @@ +import websockets +import asyncio +import time + +from utils import default_example_setup, ws_send_batch_tx, trim_exception + + +# this example does the same thing as the send_batch_tx_http.py example, but sends the TX over WS instead of HTTP +async def main(): + client, api_client, ws_client_promise = default_example_setup() + + # set up WS client and print a connected message + ws_client: websockets.ClientConnection = await ws_client_promise + print("Received:", await ws_client.recv()) + + api_key_index, nonce = client.nonce_manager.next_nonce() + ask_tx_type, ask_tx_info, ask_tx_hash, error = client.sign_create_order( + market_index=0, + client_order_index=1001, # Unique identifier for this order + base_amount=1000, # 0.1 ETH + price=5000_00, # $5000 + is_ask=True, + order_type=client.ORDER_TYPE_LIMIT, + time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, + reduce_only=False, + trigger_price=0, + nonce=nonce, + api_key_index=api_key_index, + ) + + if error is not None: + print(f"Error signing ask order (first batch): {trim_exception(error)}") + return + + # intentionally pass api_key_index to the client.nonce_manager so it increases the nonce, without changing the API key. + # in batch TXs, all TXs must come from the same API key. + api_key_index, nonce = client.nonce_manager.next_nonce(api_key_index) + bid_tx_type, bid_tx_info, bid_tx_hash, error = client.sign_create_order( + market_index=0, + client_order_index=1002, # Different unique identifier + base_amount=1000, # 0.1 ETH + price=1500_00, # $1500 + is_ask=False, + order_type=client.ORDER_TYPE_LIMIT, + time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, + reduce_only=False, + trigger_price=0, + nonce=nonce, + api_key_index=api_key_index, + ) + + if error is not None: + print(f"Error signing second order (first batch): {trim_exception(error)}") + return + + tx_types = [ask_tx_type, bid_tx_type] + tx_infos = [ask_tx_info, bid_tx_info] + tx_hashes = [ask_tx_hash, bid_tx_hash] + + await ws_send_batch_tx(ws_client, tx_types, tx_infos, tx_hashes) + + # In case we want to see the changes in the UI, sleep a bit + time.sleep(5) + + # since this is a new batch, we can request a fresh API key + api_key_index, nonce = client.nonce_manager.next_nonce() + cancel_tx_type, cancel_tx_info, cancel_tx_hash, error = client.sign_cancel_order( + market_index=0, + order_index=1001, # the index of the order we want cancelled + nonce=nonce, + api_key_index=api_key_index, + ) + + if error is not None: + print(f"Error signing first order (second batch): {trim_exception(error)}") + return + + # intentionally pass api_key_index to the client.nonce_manager so it increases the nonce, without changing the API key. + # in batch TXs, all TXs must come from the same API key. + api_key_index, nonce = client.nonce_manager.next_nonce(api_key_index) + new_ask_tx_type, new_ask_tx_info, new_ask_tx_hash, error = client.sign_create_order( + market_index=0, + client_order_index=1003, # Different unique identifier + base_amount=2000, # 0.2 ETH + price=5500_00, # $5500 + is_ask=True, + order_type=client.ORDER_TYPE_LIMIT, + time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, + reduce_only=False, + trigger_price=0, + nonce=nonce, + api_key_index=api_key_index, + ) + + if error is not None: + print(f"Error signing second order (second batch): {trim_exception(error)}") + return + + tx_types = [cancel_tx_type, new_ask_tx_type] + tx_infos = [cancel_tx_info, new_ask_tx_info] + tx_hashes = [cancel_tx_hash, new_ask_tx_hash] + + await ws_send_batch_tx(ws_client, tx_types, tx_infos, tx_hashes) + + # Clean up + await client.close() + await api_client.close() + await ws_client.close() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/send_tx_batch.py b/examples/send_tx_batch.py deleted file mode 100644 index b6bc3e7..0000000 --- a/examples/send_tx_batch.py +++ /dev/null @@ -1,138 +0,0 @@ -import asyncio -import logging -import lighter -import json - -logging.basicConfig(level=logging.DEBUG) - -# The API_KEY_PRIVATE_KEY provided belongs to a dummy account registered on Testnet. -# It was generated using the setup_system.py script, and servers as an example. -BASE_URL = "https://testnet.zklighter.elliot.ai" -API_KEY_PRIVATE_KEY = "0xed636277f3753b6c0275f7a28c2678a7f3a95655e09deaebec15179b50c5da7f903152e50f594f7b" -ACCOUNT_INDEX = 65 -API_KEY_INDEX = 1 - -def trim_exception(e: Exception) -> str: - return str(e).strip().split("\n")[-1] - - -async def main(): - # Initialize configuration and clients - configuration = lighter.Configuration(BASE_URL) - api_client = lighter.ApiClient(configuration) - transaction_api = lighter.TransactionApi(api_client) - - # Initialize signer client - client = lighter.SignerClient( - url=BASE_URL, - private_key=API_KEY_PRIVATE_KEY, - account_index=ACCOUNT_INDEX, - api_key_index=API_KEY_INDEX - ) - - # Check client connection - err = client.check_client() - if err is not None: - print(f"CheckClient error: {trim_exception(err)}") - return - - # use next nonce for getting nonces - next_nonce = await transaction_api.next_nonce(account_index=ACCOUNT_INDEX, api_key_index=API_KEY_INDEX) - nonce_value = next_nonce.nonce - - ask_tx_info, error = client.sign_create_order( - market_index=0, - client_order_index=1001, # Unique identifier for this order - base_amount=100000, - price=280000, - is_ask=True, - order_type=client.ORDER_TYPE_LIMIT, - time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, - reduce_only=False, - trigger_price=0, - nonce=nonce_value - ) - nonce_value += 1 - - if error is not None: - print(f"Error signing first order (first batch): {trim_exception(error)}") - return - - # Sign second order - bid_tx_info, error = client.sign_create_order( - market_index=0, - client_order_index=1002, # Different unique identifier - base_amount=200000, - price=200000, - is_ask=False, - order_type=client.ORDER_TYPE_LIMIT, - time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, - reduce_only=False, - trigger_price=0, - nonce=nonce_value - ) - nonce_value += 1 - - if error is not None: - print(f"Error signing second order (first batch): {trim_exception(error)}") - return - - tx_types = json.dumps([client.TX_TYPE_CREATE_ORDER, client.TX_TYPE_CREATE_ORDER]) - tx_infos = json.dumps([ask_tx_info, bid_tx_info]) - - try: - tx_hashes = await transaction_api.send_tx_batch(tx_types=tx_types, tx_infos=tx_infos) - print(f"Batch transaction successful: {tx_hashes}") - except Exception as e: - print(f"Error sending batch transaction: {trim_exception(e)}") - - # In case we want to see the changes in the UI, sleep a bit - import time - time.sleep(5) - - cancel_tx_info, error = client.sign_cancel_order( - market_index=0, - order_index=1001, # the index of the order we want cancelled - nonce=nonce_value - ) - nonce_value += 1 - - if error is not None: - print(f"Error signing first order (second batch): {trim_exception(error)}") - return - - # Sign second order - new_ask_tx_info, error = client.sign_create_order( - market_index=0, - client_order_index=1003, # Different unique identifier - base_amount=300000, - price=310000, - is_ask=True, - order_type=client.ORDER_TYPE_LIMIT, - time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, - reduce_only=False, - trigger_price=0, - nonce=nonce_value - ) - nonce_value += 1 - - if error is not None: - print(f"Error signing second order (second batch): {trim_exception(error)}") - return - - tx_types = json.dumps([client.TX_TYPE_CANCEL_ORDER, client.TX_TYPE_CREATE_ORDER]) - tx_infos = json.dumps([cancel_tx_info, new_ask_tx_info]) - - try: - tx_hashes = await transaction_api.send_tx_batch(tx_types=tx_types, tx_infos=tx_infos) - print(f"Batch 2 transaction successful: {tx_hashes}") - except Exception as e: - print(f"Error sending batch transaction 2: {trim_exception(e)}") - - # Clean up - await client.close() - await api_client.close() - -# Run the async main function -if __name__ == "__main__": - asyncio.run(main()) \ No newline at end of file diff --git a/examples/system_setup.py b/examples/system_setup.py index bc7dca1..e288bd0 100644 --- a/examples/system_setup.py +++ b/examples/system_setup.py @@ -7,11 +7,12 @@ logging.basicConfig(level=logging.DEBUG) -# this is a dummy private key which is registered on Testnet. +# this is a dummy private key registered on Testnet. # It serves as a good example BASE_URL = "https://testnet.zklighter.elliot.ai" ETH_PRIVATE_KEY = "1234567812345678123456781234567812345678123456781234567812345678" API_KEY_INDEX = 3 +NUM_API_KEYS = 5 async def main(): @@ -43,24 +44,32 @@ async def main(): # create a private/public key pair for the new API key # pass any string to be used as seed for create_api_key like # create_api_key("Hello world random seed to make things more secure") - private_key, public_key, err = lighter.create_api_key() - if err is not None: - raise Exception(err) + + private_keys = {} + public_keys = [] + + for i in range(NUM_API_KEYS): + private_key, public_key, err = lighter.create_api_key() + if err is not None: + raise Exception(err) + public_keys.append(public_key) + private_keys[API_KEY_INDEX + i] = private_key tx_client = lighter.SignerClient( url=BASE_URL, - private_key=private_key, account_index=account_index, - api_key_index=API_KEY_INDEX, + api_private_keys=private_keys, ) - # change the API key - response, err = await tx_client.change_api_key( - eth_private_key=ETH_PRIVATE_KEY, - new_pubkey=public_key, - ) - if err is not None: - raise Exception(err) + # change all API keys + for i in range(NUM_API_KEYS): + response, err = await tx_client.change_api_key( + eth_private_key=ETH_PRIVATE_KEY, + new_pubkey=public_keys[i], + api_key_index=API_KEY_INDEX + i + ) + if err is not None: + raise Exception(err) # wait some time so that we receive the new API key in the response time.sleep(10) @@ -70,7 +79,7 @@ async def main(): if err is not None: raise Exception(err) - save_api_key_config(BASE_URL, private_key, account_index, API_KEY_INDEX) + save_api_key_config(BASE_URL, account_index, private_keys) await tx_client.close() await api_client.close() diff --git a/examples/transfer_update_leverage.py b/examples/transfer.py similarity index 53% rename from examples/transfer_update_leverage.py rename to examples/transfer.py index e5593b7..07f4a34 100644 --- a/examples/transfer_update_leverage.py +++ b/examples/transfer.py @@ -1,34 +1,25 @@ import asyncio import lighter +from utils import default_example_setup - -from examples.secrets import BASE_URL, API_KEY_PRIVATE_KEY, ETH_PRIVATE_KEY - -API_KEY_INDEX = 10 TO_ACCOUNT_INDEX = 9 -ACCOUNT_INDEX = 10 # replace with your account_index +ETH_PRIVATE_KEY = "0x..." async def main(): - client = lighter.SignerClient( - url=BASE_URL, - private_key=API_KEY_PRIVATE_KEY, - account_index=ACCOUNT_INDEX, - api_key_index=API_KEY_INDEX, - ) - api_client = lighter.ApiClient(configuration=lighter.Configuration(host=BASE_URL)) + client, api_client, _ = default_example_setup() info_api = lighter.InfoApi(api_client) - order_api = lighter.OrderApi(api_client) auth_token, _ = client.create_auth_token_with_expiry() - fee_info = await info_api.transfer_fee_info(ACCOUNT_INDEX, authorization=auth_token, auth=auth_token, to_account_index=TO_ACCOUNT_INDEX) + fee_info = await info_api.transfer_fee_info(client.account_index, authorization=auth_token, auth=auth_token, to_account_index=TO_ACCOUNT_INDEX) print(fee_info) err = client.check_client() if err is not None: print(f"CheckClient error: {err}") return - memo = "a"*32 # memo is a user message and it has to be exactly 32 bytes long + + memo = "a"*32 # memo is a user message, and it has to be exactly 32 bytes long transfer_tx, response, err = await client.transfer( ETH_PRIVATE_KEY, usdc_amount=100, # decimals are added by sdk @@ -36,7 +27,7 @@ async def main(): fee=fee_info.transfer_fee_usdc, memo=memo, ) - if err != None: + if err is not None: raise Exception(f"error transferring {err}") print(transfer_tx, response) diff --git a/examples/utils.py b/examples/utils.py index e794537..7210716 100644 --- a/examples/utils.py +++ b/examples/utils.py @@ -1,8 +1,7 @@ +from typing import Tuple, Optional import logging - import json import websockets - import lighter @@ -10,13 +9,12 @@ def trim_exception(e: Exception) -> str: return str(e).strip().split("\n")[-1] -def save_api_key_config(base_url, api_key_private_key, account_index, api_key_index, config_file="./api_key_config.json"): +def save_api_key_config(base_url, account_index, private_keys, config_file="./api_key_config.json"): with open(config_file, "w", encoding="utf-8") as f: json.dump({ "baseUrl": base_url, - "apiKeyPrivateKey": api_key_private_key, "accountIndex": account_index, - "apiKeyIndex": api_key_index, + "privateKeys": private_keys, }, f, ensure_ascii=False, indent=2) @@ -24,19 +22,23 @@ def get_api_key_config(config_file="./api_key_config.json"): with open(config_file) as f: cfg = json.load(f) - return cfg["baseUrl"], cfg["apiKeyPrivateKey"], cfg["accountIndex"], cfg["apiKeyIndex"] + private_keys_original = cfg["privateKeys"] + private_key = {} + for key in private_keys_original.keys(): + private_key[int(key)] = private_keys_original[key] + + return cfg["baseUrl"], cfg["accountIndex"], private_key -def default_example_setup(config_file="./api_key_config.json") -> (lighter.ApiClient, lighter.SignerClient, websockets.ClientConnection): +def default_example_setup(config_file="./api_key_config.json") -> Optional[Tuple[lighter.SignerClient, lighter.ApiClient, websockets.connect]]: logging.basicConfig(level=logging.DEBUG) - base_url, api_key_private_key, account_index, api_key_index = get_api_key_config(config_file) + base_url, account_index, private_keys = get_api_key_config(config_file) api_client = lighter.ApiClient(configuration=lighter.Configuration(host=base_url)) client = lighter.SignerClient( url=base_url, - private_key=api_key_private_key, account_index=account_index, - api_key_index=api_key_index, + api_private_keys=private_keys, ) err = client.check_client() @@ -45,3 +47,45 @@ def default_example_setup(config_file="./api_key_config.json") -> (lighter.ApiCl return return client, api_client, websockets.connect(f"{base_url.replace('https', 'wss')}/stream") + + +async def ws_send_tx(ws_client: websockets.ClientConnection, tx_type, tx_info, tx_hash): + # Note: you have the TX Hash from signing the TX + # You can use this TX Hash to check the status of the TX later on + # if the server generates a different hash, the signature will fail, so the hash will always be correct + # because of this, the hash returned by the server will always be the same + await ws_client.send( + json.dumps( + { + "type": "jsonapi/sendtx", + "data": { + "id": f"my_random_id_{12345678}", # optional helps id the response + "tx_type": tx_type, + "tx_info": json.loads(tx_info), + }, + } + ) + ) + + print(f"expectedHash {tx_hash} response {await ws_client.recv()}") + + +async def ws_send_batch_tx(ws_client: websockets.ClientConnection, tx_types, tx_infos, tx_hashes): + # Note: you have the TX Hash from signing the TX + # You can use this TX Hash to check the status of the TX later on + # if the server generates a different hash, the signature will fail, so the hash will always be correct + # because of this, the hash returned by the server will always be the same + await ws_client.send( + json.dumps( + { + "type": "jsonapi/sendtxbatch", + "data": { + "id": f"my_random_id_{12345678}", # optional helps id the response + "tx_types": json.dumps(tx_types), + "tx_infos": json.dumps(tx_infos), + }, + } + ) + ) + + print(f"expectedHash {tx_hashes} response {await ws_client.recv()}") diff --git a/examples/ws_send_batch_tx.py b/examples/ws_send_batch_tx.py deleted file mode 100644 index 375d8fb..0000000 --- a/examples/ws_send_batch_tx.py +++ /dev/null @@ -1,101 +0,0 @@ -import lighter -import json -import websockets -import asyncio - -# The API_KEY_PRIVATE_KEY provided belongs to a dummy account registered on Testnet. -# It was generated using the setup_system.py script, and servers as an example. -# Alternatively, you can go to https://app.lighter.xyz/apikeys for mainnet api keys -BASE_URL = "https://testnet.zklighter.elliot.ai" -API_KEY_PRIVATE_KEY = ( - "0xed636277f3753b6c0275f7a28c2678a7f3a95655e09deaebec15179b50c5da7f903152e50f594f7b" -) -ACCOUNT_INDEX = 65 -API_KEY_INDEX = 1 - - -async def ws_flow(tx_types, tx_infos): - async with websockets.connect(f"{BASE_URL.replace('https', 'wss')}/stream") as ws: - msg = await ws.recv() - print("Received:", msg) - - await ws.send( - json.dumps( - { - "type": "jsonapi/sendtxbatch", - "data": { - "id": f"my_random_batch_id_{12345678}", # optional, helps id the response - "tx_types": json.dumps(tx_types), - "tx_infos": json.dumps(tx_infos), - }, - } - ) - ) - - print("Response:", await ws.recv()) - - -async def main(): - client = lighter.SignerClient( - url=BASE_URL, - private_key=API_KEY_PRIVATE_KEY, - account_index=ACCOUNT_INDEX, - api_key_index=API_KEY_INDEX, - ) - configuration = lighter.Configuration(BASE_URL) - api_client = lighter.ApiClient(configuration) - transaction_api = lighter.TransactionApi(api_client) - - # use next nonce for getting nonces - next_nonce = await transaction_api.next_nonce( - account_index=ACCOUNT_INDEX, api_key_index=API_KEY_INDEX - ) - nonce_value = next_nonce.nonce - - ask_tx_info, error = client.sign_create_order( - market_index=0, - client_order_index=1001, # Unique identifier for this order - base_amount=100000, - price=280000, - is_ask=True, - order_type=client.ORDER_TYPE_LIMIT, - time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, - reduce_only=False, - trigger_price=0, - nonce=nonce_value, - ) - nonce_value += 1 - - if error is not None: - print(f"Error signing first order (first batch): {error}") - return - - # Sign second order - bid_tx_info, error = client.sign_create_order( - market_index=0, - client_order_index=1002, # Different unique identifier - base_amount=200000, - price=200000, - is_ask=False, - order_type=client.ORDER_TYPE_LIMIT, - time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, - reduce_only=False, - trigger_price=0, - nonce=nonce_value, - ) - - if error is not None: - print(f"Error signing second order (first batch): {error}") - return - - tx_types = [ - lighter.SignerClient.TX_TYPE_CREATE_ORDER, - lighter.SignerClient.TX_TYPE_CREATE_ORDER, - ] - tx_infos = [ask_tx_info, bid_tx_info] - - await ws_flow(tx_types, tx_infos) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/examples/ws_send_tx.py b/examples/ws_send_tx.py deleted file mode 100644 index 1624839..0000000 --- a/examples/ws_send_tx.py +++ /dev/null @@ -1,77 +0,0 @@ -import json -import websockets -import asyncio - -import lighter -from utils import default_example_setup - - -async def ws_send_tx(ws_client: websockets.ClientConnection, tx_type, tx_info): - await ws_client.send( - json.dumps( - { - "type": "jsonapi/sendtx", - "data": { - "id": f"my_random_id_{12345678}", # optional, helps id the response - "tx_type": tx_type, - "tx_info": json.loads(tx_info), - }, - } - ) - ) - - print("Response:", await ws_client.recv()) - - -# this example does the same thing as the create_modify_cancel_order.py example, but sends the TX over WS instead of HTTP -async def main(): - client, api_client, ws_client_promise = default_example_setup() - - # setup WS client and print connected message - ws_client: websockets.ClientConnection = await ws_client_promise - print("Received:", await ws_client.recv()) - - # create order - tx_type, tx_info, err = client.sign_create_order( - market_index=0, - client_order_index=123, - base_amount=1000, # 0.1 ETH - price=405000, # $4050 - is_ask=True, - order_type=lighter.SignerClient.ORDER_TYPE_LIMIT, - time_in_force=lighter.SignerClient.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME, - reduce_only=False, - trigger_price=0, - ) - if err is not None: - raise Exception(err) - await ws_send_tx(ws_client, tx_type, tx_info) - - # modify order - tx_type, tx_info, err = client.sign_modify_order( - market_index=0, - order_index=123, - base_amount=1100, # 0.11 ETH - price=410000, # $4100 - trigger_price=0, - ) - if err is not None: - raise Exception(err) - await ws_send_tx(ws_client, tx_type, tx_info) - - # cancel order - tx_type, tx_info, err = client.sign_cancel_order( - market_index=0, - order_index=123, - ) - if err is not None: - raise Exception(err) - await ws_send_tx(ws_client, tx_type, tx_info) - - await client.close() - await api_client.close() - await ws_client.close() - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/lighter/nonce_manager.py b/lighter/nonce_manager.py index cf4c730..c2c6db6 100644 --- a/lighter/nonce_manager.py +++ b/lighter/nonce_manager.py @@ -1,19 +1,18 @@ import abc import enum -from typing import Optional, Tuple +from typing import Optional, Tuple, List import requests -from lighter import api_client -from lighter.api import transaction_api +from lighter.api_client import ApiClient from lighter.errors import ValidationError -def get_nonce_from_api(client: api_client.ApiClient, account_index: int, api_key_index: int) -> int: +def get_nonce_from_api(client: ApiClient, account_index: int, api_key: int) -> int: # uses request to avoid async initialization req = requests.get( client.configuration.host + "/api/v1/nextNonce", - params={"account_index": account_index, "api_key_index": api_key_index}, + params={"account_index": account_index, "api_key_index": api_key}, ) if req.status_code != 200: raise Exception(f"couldn't get nonce {req.content}") @@ -22,85 +21,80 @@ def get_nonce_from_api(client: api_client.ApiClient, account_index: int, api_key class NonceManager(abc.ABC): def __init__( - self, - account_index: int, - api_client: api_client.ApiClient, - start_api_key: int, - end_api_key: Optional[int] = None, + self, + account_index: int, + api_client: ApiClient, + api_keys_list: List[int], ): - if end_api_key is None: - end_api_key = start_api_key - if start_api_key > end_api_key or start_api_key >= 255 or end_api_key >= 255: - raise ValidationError(f"invalid range {start_api_key=} {end_api_key=}") - self.start_api_key = start_api_key - self.end_api_key = end_api_key - self.current_api_key = end_api_key # start will be used for the first tx + if len(api_keys_list) == 0: + raise ValidationError(f"No API Key provided") + + self.current = 0 # cycle through api keys self.account_index = account_index self.api_client = api_client + self.api_keys_list = api_keys_list self.nonce = { - api_key_index: get_nonce_from_api(api_client, account_index, api_key_index) - 1 - for api_key_index in range(start_api_key, end_api_key + 1) + api_keys_list[i]: get_nonce_from_api(api_client, account_index, api_keys_list[i]) - 1 + for i in range(len(api_keys_list)) } + def refresh_nonce(self, api_key: int) -> int: + self.nonce[api_key] = get_nonce_from_api(self.api_client, self.account_index, api_key) + return self.nonce[api_key] + def hard_refresh_nonce(self, api_key: int): self.nonce[api_key] = get_nonce_from_api(self.api_client, self.account_index, api_key) - 1 @abc.abstractmethod - def next_nonce(self) -> Tuple[int, int]: + def next_nonce(self, api_key: Optional[int] = None) -> Tuple[int, int]: pass - def acknowledge_failure(self, api_key_index: int) -> None: + def acknowledge_failure(self, api_key: int) -> None: pass -def increment_circular(idx: int, start_idx: int, end_idx: int) -> int: - idx += 1 - if idx > end_idx: - return start_idx - return idx - - class OptimisticNonceManager(NonceManager): def __init__( - self, - account_index: int, - api_client: api_client.ApiClient, - start_api_key: int, - end_api_key: Optional[int] = None, + self, + account_index: int, + api_client: ApiClient, + api_keys_list: List[int] ) -> None: - super().__init__(account_index, api_client, start_api_key, end_api_key) + super().__init__(account_index, api_client, api_keys_list) + + def next_nonce(self, api_key: Optional[int] = None) -> Tuple[int, int]: + if api_key is None: + self.current = (self.current + 1) % len(self.api_keys_list) + api_key = self.api_keys_list[self.current] - def next_nonce(self) -> Tuple[int, int]: - self.current_api_key = increment_circular(self.current_api_key, self.start_api_key, self.end_api_key) - self.nonce[self.current_api_key] += 1 - return (self.current_api_key, self.nonce[self.current_api_key]) + self.nonce[api_key] += 1 + return api_key, self.nonce[api_key] - def acknowledge_failure(self, api_key_index: int) -> None: - self.nonce[api_key_index] -= 1 + def acknowledge_failure(self, api_key: int) -> None: + self.nonce[api_key] -= 1 class ApiNonceManager(NonceManager): def __init__( - self, - account_index: int, - api_client: api_client.ApiClient, - start_api_key: int, - end_api_key: Optional[int] = None, + self, + account_index: int, + api_client: ApiClient, + api_keys_list: List[int], ) -> None: - super().__init__(account_index, api_client, start_api_key, end_api_key) + super().__init__(account_index, api_client, api_keys_list) - def next_nonce(self) -> Tuple[int, int]: + def next_nonce(self, api_key: Optional[int] = None) -> Tuple[int, int]: """ It is recommended to wait at least 350ms before using the same api key. Please be mindful of your transaction frequency when using this nonce manager. predicted_execution_time_ms from the response could give you a tighter bound. """ - self.current_api_key = increment_circular(self.current_api_key, self.start_api_key, self.end_api_key) - self.nonce[self.current_api_key] = get_nonce_from_api(self.api_client, self.account_index, self.current_api_key) - return (self.current_api_key, self.nonce[self.current_api_key]) + if api_key is None: + self.current = (self.current + 1) % len(self.api_keys_list) + api_key = self.api_keys_list[self.current] - def refresh_nonce(self, api_key_index: int) -> int: - self.nonce[api_key_index] = get_nonce_from_api(self.api_client, self.start_api_key, self.end_api_key) + nonce = self.refresh_nonce(api_key) + return api_key, nonce class NonceManagerType(enum.Enum): @@ -109,24 +103,21 @@ class NonceManagerType(enum.Enum): def nonce_manager_factory( - nonce_manager_type: NonceManagerType, - account_index: int, - api_client: api_client.ApiClient, - start_api_key: int, - end_api_key: Optional[int] = None, + nonce_manager_type: NonceManagerType, + account_index: int, + api_client: ApiClient, + api_keys_list: List[int], ) -> NonceManager: if nonce_manager_type == NonceManagerType.OPTIMISTIC: return OptimisticNonceManager( account_index=account_index, api_client=api_client, - start_api_key=start_api_key, - end_api_key=end_api_key, + api_keys_list=api_keys_list, ) elif nonce_manager_type == NonceManagerType.API: return ApiNonceManager( account_index=account_index, api_client=api_client, - start_api_key=start_api_key, - end_api_key=end_api_key, + api_keys_list=api_keys_list, ) raise ValidationError("invalid nonce manager type") diff --git a/lighter/signer_client.py b/lighter/signer_client.py index 06bb98d..7f4c209 100644 --- a/lighter/signer_client.py +++ b/lighter/signer_client.py @@ -6,7 +6,7 @@ import logging import os import time -from typing import Dict, List, Optional, Tuple +from typing import Dict, List, Optional, Union, Tuple from eth_account import Account from eth_account.messages import encode_defunct @@ -17,6 +17,7 @@ from lighter.models import TxHash from lighter import nonce_manager from lighter.models.resp_send_tx import RespSendTx +from lighter.models.resp_send_tx_batch import RespSendTxBatch from lighter.transactions import CreateOrder, CancelOrder, Withdraw, CreateGroupedOrders CODE_OK = 200 @@ -45,6 +46,16 @@ class StrOrErr(ctypes.Structure): _fields_ = [("str", ctypes.c_char_p), ("err", ctypes.c_char_p)] +class SignedTxResponse(ctypes.Structure): + _fields_ = [ + ("txType", ctypes.c_uint8), + ("txInfo", ctypes.c_char_p), + ("txHash", ctypes.c_char_p), + ("messageToSign", ctypes.c_char_p), + ("err", ctypes.c_char_p), + ] + + __signer = None @@ -83,54 +94,57 @@ def __populate_shared_library_functions(signer): signer.CheckClient.argtypes = [ctypes.c_int, ctypes.c_longlong] signer.CheckClient.restype = ctypes.c_char_p - signer.SignChangePubKey.argtypes = [ctypes.c_char_p, ctypes.c_longlong] - signer.SignChangePubKey.restype = StrOrErr + signer.SignChangePubKey.argtypes = [ctypes.c_char_p, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignChangePubKey.restype = SignedTxResponse signer.SignCreateOrder.argtypes = [ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, - ctypes.c_int, ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong] - signer.SignCreateOrder.restype = StrOrErr + ctypes.c_int, ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignCreateOrder.restype = SignedTxResponse - signer.SignCreateGroupedOrders.argtypes = [ctypes.c_uint8, ctypes.POINTER(CreateOrderTxReq), ctypes.c_int, ctypes.c_longlong] - signer.SignCreateGroupedOrders.restype = StrOrErr + signer.SignCreateGroupedOrders.argtypes = [ctypes.c_uint8, ctypes.POINTER(CreateOrderTxReq), ctypes.c_int, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignCreateGroupedOrders.restype = SignedTxResponse - signer.SignCancelOrder.argtypes = [ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong] - signer.SignCancelOrder.restype = StrOrErr + signer.SignCancelOrder.argtypes = [ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignCancelOrder.restype = SignedTxResponse - signer.SignWithdraw.argtypes = [ctypes.c_longlong, ctypes.c_longlong] - signer.SignWithdraw.restype = StrOrErr + signer.SignWithdraw.argtypes = [ctypes.c_longlong, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignWithdraw.restype = SignedTxResponse - signer.SignCreateSubAccount.argtypes = [ctypes.c_longlong] - signer.SignCreateSubAccount.restype = StrOrErr + signer.SignCreateSubAccount.argtypes = [ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignCreateSubAccount.restype = SignedTxResponse - signer.SignCancelAllOrders.argtypes = [ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong] - signer.SignCancelAllOrders.restype = StrOrErr + signer.SignCancelAllOrders.argtypes = [ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignCancelAllOrders.restype = SignedTxResponse - signer.SignModifyOrder.argtypes = [ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong] - signer.SignModifyOrder.restype = StrOrErr + signer.SignModifyOrder.argtypes = [ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignModifyOrder.restype = SignedTxResponse - signer.SignTransfer.argtypes = [ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_char_p, ctypes.c_longlong] - signer.SignTransfer.restype = StrOrErr + signer.SignTransfer.argtypes = [ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_char_p, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignTransfer.restype = SignedTxResponse - signer.SignCreatePublicPool.argtypes = [ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong] - signer.SignCreatePublicPool.restype = StrOrErr + signer.SignCreatePublicPool.argtypes = [ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignCreatePublicPool.restype = SignedTxResponse - signer.SignUpdatePublicPool.argtypes = [ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong] - signer.SignUpdatePublicPool.restype = StrOrErr + signer.SignUpdatePublicPool.argtypes = [ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignUpdatePublicPool.restype = SignedTxResponse - signer.SignMintShares.argtypes = [ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong] - signer.SignMintShares.restype = StrOrErr + signer.SignMintShares.argtypes = [ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignMintShares.restype = SignedTxResponse - signer.SignBurnShares.argtypes = [ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong] - signer.SignBurnShares.restype = StrOrErr + signer.SignBurnShares.argtypes = [ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignBurnShares.restype = SignedTxResponse - signer.SignUpdateLeverage.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_longlong] - signer.SignUpdateLeverage.restype = StrOrErr + signer.SignUpdateLeverage.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignUpdateLeverage.restype = SignedTxResponse - signer.CreateAuthToken.argtypes = [ctypes.c_longlong] + signer.CreateAuthToken.argtypes = [ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] signer.CreateAuthToken.restype = StrOrErr - signer.SwitchAPIKey.argtypes = [ctypes.c_int] - signer.SwitchAPIKey.restype = ctypes.c_char_p + # Note: SwitchAPIKey is no longer exported in the new binary + # All functions now take api_key_index directly, so switching is handled via parameters + + signer.SignUpdateMargin.argtypes = [ctypes.c_int, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong] + signer.SignUpdateMargin.restype = SignedTxResponse def get_signer(): @@ -169,13 +183,10 @@ async def wrapper(self, *args, **kwargs): bound_args = sig.bind(self, *args, **kwargs) bound_args.apply_defaults() # Extract api_key_index and nonce from kwargs or use defaults - api_key_index = bound_args.arguments.get("api_key_index", -1) + api_key_index = bound_args.arguments.get("api_key_index", 255) nonce = bound_args.arguments.get("nonce", -1) - if api_key_index == -1 and nonce == -1: + if api_key_index == 255 and nonce == -1: api_key_index, nonce = self.nonce_manager.next_nonce() - err = self.switch_api_key(api_key_index) - if err is not None: - raise Exception(f"error switching api key: {err}") # Call the original function with modified kwargs ret: TxHash @@ -198,23 +209,10 @@ async def wrapper(self, *args, **kwargs): class SignerClient: - USDC_TICKER_SCALE = 1e6 + DEFAULT_NONCE = -1 + DEFAULT_API_KEY_INDEX = 255 - TX_TYPE_CHANGE_PUB_KEY = 8 - TX_TYPE_CREATE_SUB_ACCOUNT = 9 - TX_TYPE_CREATE_PUBLIC_POOL = 10 - TX_TYPE_UPDATE_PUBLIC_POOL = 11 - TX_TYPE_TRANSFER = 12 - TX_TYPE_WITHDRAW = 13 - TX_TYPE_CREATE_ORDER = 14 - TX_TYPE_CANCEL_ORDER = 15 - TX_TYPE_CANCEL_ALL_ORDERS = 16 - TX_TYPE_MODIFY_ORDER = 17 - TX_TYPE_MINT_SHARES = 18 - TX_TYPE_BURN_SHARES = 19 - TX_TYPE_UPDATE_LEVERAGE = 20 - TX_TYPE_CREATE_GROUP_ORDER = 28 - TX_TYPE_UPDATE_MARGIN = 29 + USDC_TICKER_SCALE = 1e6 ORDER_TYPE_LIMIT = 0 ORDER_TYPE_MARKET = 1 @@ -241,6 +239,9 @@ class SignerClient: CROSS_MARGIN_MODE = 0 ISOLATED_MARGIN_MODE = 1 + ISOLATED_MARGIN_REMOVE_COLLATERAL = 0 + ISOLATED_MARGIN_ADD_COLLATERAL = 1 + GROUPING_TYPE_ONE_TRIGGERS_THE_OTHER = 1 GROUPING_TYPE_ONE_CANCELS_THE_OTHER = 2 GROUPING_TYPE_ONE_TRIGGERS_A_ONE_CANCELS_THE_OTHER = 3 @@ -248,95 +249,75 @@ class SignerClient: def __init__( self, url, - private_key, - api_key_index, account_index, - max_api_key_index=-1, - private_keys: Optional[Dict[int, str]] = None, + api_private_keys: Dict[int, str], nonce_management_type=nonce_manager.NonceManagerType.OPTIMISTIC, ): - """ - First private key needs to be passed separately for backwards compatibility. - This may get deprecated in a future version. - """ - chain_id = 304 if "mainnet" in url else 300 - - # api_key_index=0 is generally used by frontend - if private_key.startswith("0x"): - private_key = private_key[2:] self.url = url - self.private_key = private_key - self.chain_id = chain_id - self.api_key_index = api_key_index - if max_api_key_index == -1: - self.end_api_key_index = api_key_index - else: - self.end_api_key_index = max_api_key_index - - private_keys = private_keys or {} - self.validate_api_private_keys(private_key, private_keys) - self.api_key_dict = self.build_api_key_dict(private_key, private_keys) + self.chain_id = 304 if "mainnet" in url else 300 + + self.validate_api_private_keys(api_private_keys) + self.api_key_dict = api_private_keys self.account_index = account_index self.signer = get_signer() self.api_client = lighter.ApiClient(configuration=Configuration(host=url)) self.tx_api = lighter.TransactionApi(self.api_client) self.order_api = lighter.OrderApi(self.api_client) + self.nonce_manager = nonce_manager.nonce_manager_factory( nonce_manager_type=nonce_management_type, account_index=account_index, api_client=self.api_client, - start_api_key=self.api_key_index, - end_api_key=self.end_api_key_index, + api_keys_list=list(api_private_keys.keys()), ) - for api_key in range(self.api_key_index, self.end_api_key_index + 1): - self.create_client(api_key) + for api_key_index in api_private_keys.keys(): + self.create_client(api_key_index) # === signer helpers === @staticmethod - def __decode_tx_info(tx_type: int, result: StrOrErr): - tx_info_str = result.str.decode("utf-8") if result.str else None - error = result.err.decode("utf-8") if result.err else None - - return tx_type, tx_info_str, error + def __decode_tx_info(result: SignedTxResponse) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + if result.err: + error = result.err.decode("utf-8") + return None, None, None, error + + # Use txType from response if available, otherwise use the provided type + tx_type = result.txType + tx_info_str = result.txInfo.decode("utf-8") if result.txInfo else None + tx_hash_str = result.txHash.decode("utf-8") if result.txHash else None + + return tx_type, tx_info_str, tx_hash_str, None @staticmethod - def __decode_and_sign_tx_info(eth_private_key: str, tx_type: int, result: StrOrErr): - tx_info_str = result.str.decode("utf-8") if result.str else None - err = result.err.decode("utf-8") if result.err else None + def __decode_and_sign_tx_info(eth_private_key: str, result: SignedTxResponse) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + if result.err: + err = result.err.decode("utf-8") + return None, None, None, err - if err is not None: - return None, None, err - - # fetch message to sign - tx_info = json.loads(tx_info_str) - msg_to_sign = tx_info["MessageToSign"] - del tx_info["MessageToSign"] + tx_type = result.txType + tx_info_str = result.txInfo.decode("utf-8") if result.txInfo else None + tx_hash_str = result.txHash.decode("utf-8") if result.txHash else None + msg_to_sign = result.messageToSign.decode("utf-8") if result.messageToSign else None # sign the message acct = Account.from_key(eth_private_key) message = encode_defunct(text=msg_to_sign) signature = acct.sign_message(message) + + # add signature to tx_info + tx_info = json.loads(tx_info_str) tx_info["L1Sig"] = signature.signature.to_0x_hex() - return tx_type, json.dumps(tx_info), None - - def validate_api_private_keys(self, initial_private_key: str, private_keys: Dict[int, str]): - if len(private_keys) == self.end_api_key_index - self.api_key_index + 1: - if not self.are_keys_equal(private_keys[self.api_key_index], initial_private_key): - raise ValidationError("inconsistent private keys") - return # this is all we need to check in this case - if len(private_keys) != self.end_api_key_index - self.api_key_index: - raise ValidationError("unexpected number of private keys") - for api_key in range(self.api_key_index + 1, self.end_api_key_index): - if api_key not in private_keys: - raise Exception(f"missing {api_key=} private key!") - - def build_api_key_dict(self, private_key, private_keys): - if len(private_keys) == self.end_api_key_index - self.api_key_index: - private_keys[self.api_key_index] = private_key - return private_keys - - def create_client(self, api_key_index=None): - api_key_index = api_key_index or self.api_key_index + return tx_type, json.dumps(tx_info), tx_hash_str, None + + def validate_api_private_keys(self, private_keys: Dict[int, str]): + if len(private_keys) == 0: + raise ValidationError("No API keys provided") + + # trim 0x + for api_key_index, private_key in private_keys.items(): + if private_key.startswith("0x"): + private_keys[api_key_index] = private_key[2:] + + def create_client(self, api_key_index): err = self.signer.CreateClient( self.url.encode("utf-8"), self.api_key_dict[api_key_index].encode("utf-8"), @@ -364,56 +345,55 @@ def __signer_check_client( # check_client verifies that the given API key associated with (api_key_index, account_index) matches the one on Lighter def check_client(self): - for api_key in range(self.api_key_index, self.end_api_key_index + 1): + for api_key in self.api_key_dict.keys(): err = self.__signer_check_client(api_key, self.account_index) if err is not None: - return err + f" on api key {self.api_key_index}" + return err + f" on api key {api_key}" return None - def switch_api_key(self, api_key: int): - err = self.signer.SwitchAPIKey(api_key) - return err.decode("utf-8") if err else None - @staticmethod def create_api_key(self, seed=""): return create_api_key(seed=seed) def get_api_key_nonce(self, api_key_index: int, nonce: int) -> Tuple[int, int]: - if api_key_index != -1 and nonce != -1: + if api_key_index != self.DEFAULT_API_KEY_INDEX and nonce != self.DEFAULT_NONCE: return api_key_index, nonce - if nonce != -1: - if self.api_key_index == self.end_api_key_index: + + if nonce != self.DEFAULT_NONCE: + if len(self.api_key_dict) == 1: return self.nonce_manager.next_nonce() else: raise Exception("ambiguous api key") return self.nonce_manager.next_nonce() - def create_auth_token_with_expiry(self, deadline: int = DEFAULT_10_MIN_AUTH_EXPIRY, *, timestamp: int = None): + def create_auth_token_with_expiry(self, deadline: int = DEFAULT_10_MIN_AUTH_EXPIRY, *, timestamp: int = None, api_key_index: int = DEFAULT_API_KEY_INDEX): if deadline == SignerClient.DEFAULT_10_MIN_AUTH_EXPIRY: deadline = 10 * SignerClient.MINUTE if timestamp is None: timestamp = int(time.time()) - result = self.signer.CreateAuthToken(deadline+timestamp) + result = self.signer.CreateAuthToken(deadline+timestamp, api_key_index, self.account_index) auth = result.str.decode("utf-8") if result.str else None error = result.err.decode("utf-8") if result.err else None return auth, error - def sign_change_api_key(self, eth_private_key: str, new_pubkey: str, nonce: int = -1): - return self.__decode_and_sign_tx_info(eth_private_key, self.TX_TYPE_CHANGE_PUB_KEY, self.signer.SignChangePubKey( + def sign_change_api_key(self, eth_private_key: str, new_pubkey: str, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + return self.__decode_and_sign_tx_info(eth_private_key, self.signer.SignChangePubKey( ctypes.c_char_p(new_pubkey.encode("utf-8")), - nonce + nonce, + api_key_index, + self.account_index )) - async def change_api_key(self, eth_private_key: str, new_pubkey: str, nonce=-1): - tx_type, tx_info, error = self.sign_change_api_key(eth_private_key, new_pubkey, nonce) + async def change_api_key(self, eth_private_key: str, new_pubkey: str, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX): + tx_type, tx_info, tx_hash, error = self.sign_change_api_key(eth_private_key, new_pubkey, nonce, api_key_index) if error is not None: return None, error - logging.debug(f"Change Pub Key Tx Info: {tx_info}") + logging.debug(f"Change Pub Key TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) - logging.debug(f"Change Pub Key Send Tx Response: {api_response}") + logging.debug(f"Change Pub Key Send. TxResponse: {api_response}") return api_response, None def sign_create_order( @@ -428,9 +408,10 @@ def sign_create_order( reduce_only=False, trigger_price=NIL_TRIGGER_PRICE, order_expiry=DEFAULT_28_DAY_ORDER_EXPIRY, - nonce=-1, - ): - return self.__decode_tx_info(self.TX_TYPE_CREATE_ORDER, self.signer.SignCreateOrder( + nonce: int = DEFAULT_NONCE, + api_key_index: int = DEFAULT_API_KEY_INDEX + ) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + return self.__decode_tx_info(self.signer.SignCreateOrder( market_index, client_order_index, base_amount, @@ -442,60 +423,59 @@ def sign_create_order( trigger_price, order_expiry, nonce, + api_key_index, + self.account_index, )) def sign_create_grouped_orders( self, grouping_type: int, orders: List[CreateOrderTxReq], - nonce: int = -1, - ): + nonce: int = DEFAULT_NONCE, + api_key_index=DEFAULT_API_KEY_INDEX + ) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: arr_type = CreateOrderTxReq * len(orders) orders_arr = arr_type(*orders) - return self.__decode_tx_info(self.TX_TYPE_CREATE_GROUP_ORDER, self.signer.SignCreateGroupedOrders( - grouping_type, orders_arr, len(orders), nonce + return self.__decode_tx_info(self.signer.SignCreateGroupedOrders( + grouping_type, orders_arr, len(orders), nonce, api_key_index, self.account_index )) - def sign_cancel_order(self, market_index: int, order_index: int, nonce: int = -1): - return self.__decode_tx_info(self.TX_TYPE_CANCEL_ORDER, self.signer.SignCancelOrder(market_index, order_index, nonce)) + def sign_cancel_order(self, market_index: int, order_index: int, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + return self.__decode_tx_info(self.signer.SignCancelOrder(market_index, order_index, nonce, api_key_index, self.account_index)) - def sign_withdraw(self, usdc_amount: int, nonce: int = -1): - return self.__decode_tx_info(self.TX_TYPE_WITHDRAW, self.signer.SignWithdraw(usdc_amount, nonce)) + def sign_withdraw(self, usdc_amount: int, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + return self.__decode_tx_info(self.signer.SignWithdraw(usdc_amount, nonce, api_key_index, self.account_index)) - def sign_create_sub_account(self, nonce=-1): - return self.__decode_tx_info(self.TX_TYPE_CREATE_SUB_ACCOUNT, self.signer.SignCreateSubAccount(nonce)) + def sign_create_sub_account(self, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + return self.__decode_tx_info(self.signer.SignCreateSubAccount(nonce, api_key_index, self.account_index)) - def sign_cancel_all_orders(self, time_in_force: int, timestamp_ms: int, nonce: int = -1): - return self.__decode_tx_info(self.TX_TYPE_CANCEL_ALL_ORDERS, self.signer.SignCancelAllOrders(time_in_force, timestamp_ms, nonce)) + def sign_cancel_all_orders(self, time_in_force: int, timestamp_ms: int, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + return self.__decode_tx_info(self.signer.SignCancelAllOrders(time_in_force, timestamp_ms, nonce, api_key_index, self.account_index)) - def sign_modify_order(self, market_index: int, order_index: int, base_amount: int, price: int, trigger_price: int = NIL_TRIGGER_PRICE, nonce: int = -1): - return self.__decode_tx_info(self.TX_TYPE_MODIFY_ORDER, - self.signer.SignModifyOrder(market_index, order_index, base_amount, price, trigger_price, nonce)) + def sign_modify_order(self, market_index: int, order_index: int, base_amount: int, price: int, trigger_price: int = NIL_TRIGGER_PRICE, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + return self.__decode_tx_info(self.signer.SignModifyOrder(market_index, order_index, base_amount, price, trigger_price, nonce, api_key_index, self.account_index)) - def sign_transfer(self, eth_private_key: str, to_account_index: int, usdc_amount: int, fee: int, memo: str, nonce: int = -1): - return self.__decode_and_sign_tx_info(eth_private_key, self.TX_TYPE_TRANSFER, - self.signer.SignTransfer(to_account_index, usdc_amount, fee, ctypes.c_char_p(memo.encode("utf-8")), nonce)) + def sign_transfer(self, eth_private_key: str, to_account_index: int, usdc_amount: int, fee: int, memo: str, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + return self.__decode_and_sign_tx_info(eth_private_key, self.signer.SignTransfer(to_account_index,usdc_amount,fee,ctypes.c_char_p(memo.encode("utf-8")),nonce,api_key_index,self.account_index)) - def sign_create_public_pool(self, operator_fee: int, initial_total_shares: int, min_operator_share_rate: int, nonce: int = -1): - return self.__decode_tx_info(self.TX_TYPE_CREATE_PUBLIC_POOL, - self.signer.SignCreatePublicPool(operator_fee, initial_total_shares, min_operator_share_rate, nonce)) + def sign_create_public_pool(self, operator_fee: int, initial_total_shares: int, min_operator_share_rate: int, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + return self.__decode_tx_info(self.signer.SignCreatePublicPool(operator_fee, initial_total_shares, min_operator_share_rate, nonce, api_key_index, self.account_index)) - def sign_update_public_pool(self, public_pool_index: int, status: int, operator_fee: int, min_operator_share_rate: int, nonce: int = -1): - return self.__decode_tx_info(self.TX_TYPE_UPDATE_PUBLIC_POOL, - self.signer.SignUpdatePublicPool(public_pool_index, status, operator_fee, min_operator_share_rate, nonce)) + def sign_update_public_pool(self, public_pool_index: int, status: int, operator_fee: int, min_operator_share_rate: int, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + return self.__decode_tx_info(self.signer.SignUpdatePublicPool(public_pool_index, status, operator_fee, min_operator_share_rate, nonce, api_key_index, self.account_index)) - def sign_mint_shares(self, public_pool_index: int, share_amount: int, nonce: int = -1): - return self.__decode_tx_info(self.TX_TYPE_MINT_SHARES, self.signer.SignMintShares(public_pool_index, share_amount, nonce)) + def sign_mint_shares(self, public_pool_index: int, share_amount: int, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + return self.__decode_tx_info(self.signer.SignMintShares(public_pool_index, share_amount, nonce, api_key_index, self.account_index)) - def sign_burn_shares(self, public_pool_index: int, share_amount: int, nonce: int = -1): - return self.__decode_tx_info(self.TX_TYPE_BURN_SHARES, self.signer.SignBurnShares(public_pool_index, share_amount, nonce)) + def sign_burn_shares(self, public_pool_index: int, share_amount: int, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + return self.__decode_tx_info(self.signer.SignBurnShares(public_pool_index, share_amount, nonce, api_key_index, self.account_index)) - def sign_update_leverage(self, market_index: int, fraction: int, margin_mode: int, nonce: int = -1): - return self.__decode_tx_info(self.TX_TYPE_UPDATE_LEVERAGE, self.signer.SignUpdateLeverage(market_index, fraction, margin_mode, nonce)) + def sign_update_leverage(self, market_index: int, fraction: int, margin_mode: int, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + return self.__decode_tx_info(self.signer.SignUpdateLeverage(market_index, fraction, margin_mode, nonce, api_key_index, self.account_index)) - def sign_update_margin(self, market_index: int, usdc_amount: int, direction: int, nonce: int = -1): - return self.__decode_tx_info(self.TX_TYPE_UPDATE_MARGIN, self.signer.SignUpdateMargin(market_index, usdc_amount, direction, nonce)) + def sign_update_margin(self, market_index: int, usdc_amount: int, direction: int, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX) -> Union[Tuple[str, str, str, None], Tuple[None, None, None, str]]: + return self.__decode_tx_info(self.signer.SignUpdateMargin(market_index, usdc_amount, direction, nonce, api_key_index, self.account_index)) @process_api_key_and_nonce async def create_order( @@ -510,10 +490,10 @@ async def create_order( reduce_only=False, trigger_price=NIL_TRIGGER_PRICE, order_expiry=DEFAULT_28_DAY_ORDER_EXPIRY, - nonce=-1, - api_key_index=-1, - ) -> (CreateOrder, TxHash, str): - tx_type, tx_info, error = self.sign_create_order( + nonce: int = DEFAULT_NONCE, + api_key_index: int = DEFAULT_API_KEY_INDEX + ) -> Union[Tuple[CreateOrder, RespSendTx, None], Tuple[None, None, str]]: + tx_type, tx_info, tx_hash, error = self.sign_create_order( market_index, client_order_index, base_amount, @@ -521,17 +501,18 @@ async def create_order( int(is_ask), order_type, time_in_force, - int(reduce_only), + reduce_only, trigger_price, order_expiry, nonce, + api_key_index, ) if error is not None: return None, None, error - logging.debug(f"Create Order Tx Info: {tx_info}") + logging.debug(f"Create Order TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) - logging.debug(f"Create Order Send Tx Response: {api_response}") + logging.debug(f"Create Order Send. TxResponse: {api_response}") return CreateOrder.from_json(tx_info), api_response, None @process_api_key_and_nonce @@ -539,20 +520,21 @@ async def create_grouped_orders( self, grouping_type: int, orders: List[CreateOrderTxReq], - nonce=-1, - api_key_index=-1, - ) -> (CreateGroupedOrders, TxHash, str): - tx_type, tx_info, error = self.sign_create_grouped_orders( + nonce: int = DEFAULT_NONCE, + api_key_index: int = DEFAULT_API_KEY_INDEX + ) ->Union[Tuple[CreateGroupedOrders, RespSendTx, None], Tuple[None, None, str]]: + tx_type, tx_info, tx_hash, error = self.sign_create_grouped_orders( grouping_type, orders, nonce, + api_key_index ) if error is not None: return None, None, error - logging.debug(f"Create Grouped Orders Tx Info: {tx_info}") + logging.debug(f"Create Grouped Orders TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) - logging.debug(f"Create Grouped Orders Send Tx Response: {api_response}") + logging.debug(f"Create Grouped Orders Send. TxResponse: {api_response}") return CreateGroupedOrders.from_json(tx_info), api_response, None async def create_market_order( @@ -563,9 +545,9 @@ async def create_market_order( avg_execution_price, is_ask, reduce_only: bool = False, - nonce=-1, - api_key_index=-1, - ) -> (CreateOrder, TxHash, str): + nonce: int = DEFAULT_NONCE, + api_key_index: int = DEFAULT_API_KEY_INDEX + ) -> Union[Tuple[CreateOrder, RespSendTx, None], Tuple[None, None, str]]: return await self.create_order( market_index, client_order_index, @@ -589,10 +571,10 @@ async def create_market_order_limited_slippage( max_slippage, is_ask, reduce_only: bool = False, - nonce=-1, - api_key_index=-1, + nonce: int = DEFAULT_NONCE, + api_key_index: int = DEFAULT_API_KEY_INDEX, ideal_price=None - ) -> (CreateOrder, TxHash, str): + ) -> Union[Tuple[CreateOrder, RespSendTx, None], Tuple[None, None, str]]: if ideal_price is None: order_book_orders = await self.order_api.order_book_orders(market_index, 1) logging.debug( @@ -623,10 +605,10 @@ async def create_market_order_if_slippage( max_slippage, is_ask, reduce_only: bool = False, - nonce=-1, - api_key_index=-1, + nonce: int = DEFAULT_NONCE, + api_key_index: int = DEFAULT_API_KEY_INDEX, ideal_price=None - ) -> (CreateOrder, TxHash, str): + ) -> Union[Tuple[CreateOrder, RespSendTx, None], Tuple[None, None, str]]: order_book_orders = await self.order_api.order_book_orders(market_index, 100) if ideal_price is None: ideal_price = int((order_book_orders.bids[0].price if is_ask else order_book_orders.asks[0].price).replace(".", "")) @@ -664,19 +646,22 @@ async def create_market_order_if_slippage( ) @process_api_key_and_nonce - async def cancel_order(self, market_index, order_index, nonce=-1, api_key_index=-1) -> (CancelOrder, TxHash, str): - tx_type, tx_info, error = self.sign_cancel_order(market_index, order_index, nonce) + async def cancel_order(self, market_index, order_index, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX + ) -> Union[Tuple[CancelOrder, RespSendTx, None], Tuple[None, None, str]]: + tx_type, tx_info, tx_hash, error = self.sign_cancel_order(market_index, order_index, nonce, api_key_index) if error is not None: return None, None, error - logging.debug(f"Cancel Order Tx Info: {tx_info}") + logging.debug(f"Cancel Order TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) - logging.debug(f"Cancel Order Send Tx Response: {api_response}") + logging.debug(f"Cancel Order Send. TxResponse: {api_response}") return CancelOrder.from_json(tx_info), api_response, None - async def create_tp_order(self, market_index, client_order_index, base_amount, trigger_price, price, is_ask, reduce_only=False, nonce=-1, - api_key_index=-1) -> (CreateOrder, TxHash, str): + async def create_tp_order(self, market_index, client_order_index, base_amount, trigger_price, price, is_ask, reduce_only=False, + nonce: int = DEFAULT_NONCE, + api_key_index: int = DEFAULT_API_KEY_INDEX + ) -> Union[Tuple[CreateOrder, RespSendTx, None], Tuple[None, None, str]]: return await self.create_order( market_index, client_order_index, @@ -689,11 +674,13 @@ async def create_tp_order(self, market_index, client_order_index, base_amount, t trigger_price, self.DEFAULT_28_DAY_ORDER_EXPIRY, nonce, - api_key_index=api_key_index, + api_key_index, ) - async def create_tp_limit_order(self, market_index, client_order_index, base_amount, trigger_price, price, is_ask, reduce_only=False, nonce=-1, - api_key_index=-1) -> (CreateOrder, TxHash, str): + async def create_tp_limit_order(self, market_index, client_order_index, base_amount, trigger_price, price, is_ask, reduce_only=False, + nonce: int = DEFAULT_NONCE, + api_key_index: int = DEFAULT_API_KEY_INDEX + ) -> Union[Tuple[CreateOrder, RespSendTx, None], Tuple[None, None, str]]: return await self.create_order( market_index, client_order_index, @@ -709,8 +696,10 @@ async def create_tp_limit_order(self, market_index, client_order_index, base_amo api_key_index, ) - async def create_sl_order(self, market_index, client_order_index, base_amount, trigger_price, price, is_ask, reduce_only=False, nonce=-1, - api_key_index=-1) -> (CreateOrder, TxHash, str): + async def create_sl_order(self, market_index, client_order_index, base_amount, trigger_price, price, is_ask, reduce_only=False, + nonce: int = DEFAULT_NONCE, + api_key_index: int = DEFAULT_API_KEY_INDEX + ) -> Union[Tuple[CreateOrder, RespSendTx, None], Tuple[None, None, str]]: return await self.create_order( market_index, client_order_index, @@ -723,11 +712,13 @@ async def create_sl_order(self, market_index, client_order_index, base_amount, t trigger_price, self.DEFAULT_28_DAY_ORDER_EXPIRY, nonce, - api_key_index=api_key_index, + api_key_index, ) - async def create_sl_limit_order(self, market_index, client_order_index, base_amount, trigger_price, price, is_ask, reduce_only=False, nonce=-1, - api_key_index=-1) -> (CreateOrder, TxHash, str): + async def create_sl_limit_order(self, market_index, client_order_index, base_amount, trigger_price, price, is_ask, reduce_only=False, + nonce: int = DEFAULT_NONCE, + api_key_index: int = DEFAULT_API_KEY_INDEX + ) -> Union[Tuple[CreateOrder, RespSendTx, None], Tuple[None, None, str]]: return await self.create_order( market_index, client_order_index, @@ -744,139 +735,139 @@ async def create_sl_limit_order(self, market_index, client_order_index, base_amo ) @process_api_key_and_nonce - async def withdraw(self, usdc_amount, nonce=-1, api_key_index=-1) -> (Withdraw, TxHash): + async def withdraw(self, usdc_amount, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX) -> Union[Tuple[Withdraw, RespSendTx, None], Tuple[None, None, str]]: usdc_amount = int(usdc_amount * self.USDC_TICKER_SCALE) - tx_type, tx_info, error = self.sign_withdraw(usdc_amount, nonce) + tx_type, tx_info, tx_hash, error = self.sign_withdraw(usdc_amount, nonce, api_key_index) if error is not None: return None, None, error - logging.debug(f"Withdraw Tx Info: {tx_info}") + logging.debug(f"Withdraw TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) - logging.debug(f"Withdraw Send Tx Response: {api_response}") + logging.debug(f"Withdraw Send. TxResponse: {api_response}") return Withdraw.from_json(tx_info), api_response, None - async def create_sub_account(self, nonce=-1): - tx_type, tx_info, error = self.sign_create_sub_account(nonce) + async def create_sub_account(self, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX): + tx_type, tx_info, tx_hash, error = self.sign_create_sub_account(nonce, api_key_index) if error is not None: return None, None, error - logging.debug(f"Create Sub Account Tx Info: {tx_info}") + logging.debug(f"Create Sub Account TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) - logging.debug(f"Create Sub Account Send Tx Response: {api_response}") + logging.debug(f"Create Sub Account Send. TxResponse: {api_response}") return tx_info, api_response, None @process_api_key_and_nonce - async def cancel_all_orders(self, time_in_force, timestamp_ms, nonce=-1, api_key_index=-1): - tx_type, tx_info, error = self.sign_cancel_all_orders(time_in_force, timestamp_ms, nonce) + async def cancel_all_orders(self, time_in_force, timestamp_ms, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX)-> Union[Tuple[Withdraw, RespSendTx, None], Tuple[None, None, str]]: + tx_type, tx_info, tx_hash, error = self.sign_cancel_all_orders(time_in_force, timestamp_ms, nonce, api_key_index) if error is not None: return None, None, error - logging.debug(f"Cancel All Orders Tx Info: {tx_info}") + logging.debug(f"Cancel All Orders TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) - logging.debug(f"Cancel All Orders Send Tx Response: {api_response}") + logging.debug(f"Cancel All Orders Send. TxResponse: {api_response}") return tx_info, api_response, None @process_api_key_and_nonce async def modify_order( - self, market_index, order_index, base_amount, price, trigger_price=NIL_TRIGGER_PRICE, nonce=-1, api_key_index=-1 + self, market_index, order_index, base_amount, price, trigger_price=NIL_TRIGGER_PRICE, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX ): - tx_type, tx_info, error = self.sign_modify_order(market_index, order_index, base_amount, price, trigger_price, nonce) + tx_type, tx_info, tx_hash, error = self.sign_modify_order(market_index, order_index, base_amount, price, trigger_price, nonce, api_key_index) if error is not None: return None, None, error - logging.debug(f"Modify Order Tx Info: {tx_info}") + logging.debug(f"Modify Order TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) - logging.debug(f"Modify Order Send Tx Response: {api_response}") + logging.debug(f"Modify Order Send. TxResponse: {api_response}") return tx_info, api_response, None @process_api_key_and_nonce - async def transfer(self, eth_private_key: str, to_account_index, usdc_amount, fee, memo, nonce=-1, api_key_index=-1): + async def transfer(self, eth_private_key: str, to_account_index, usdc_amount, fee, memo, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX): usdc_amount = int(usdc_amount * self.USDC_TICKER_SCALE) - tx_type, tx_info, error = self.sign_transfer(eth_private_key, to_account_index, usdc_amount, fee, memo, nonce) + tx_type, tx_info, tx_hash, error = self.sign_transfer(eth_private_key, to_account_index, usdc_amount, fee, memo, nonce, api_key_index) if error is not None: return None, None, error - logging.debug(f"Transfer Tx Info: {tx_info}") + logging.debug(f"Transfer TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) - logging.debug(f"Transfer Send Tx Response: {api_response}") + logging.debug(f"Transfer Send. TxResponse: {api_response}") return tx_info, api_response, None @process_api_key_and_nonce async def create_public_pool( - self, operator_fee, initial_total_shares, min_operator_share_rate, nonce=-1, api_key_index=-1 + self, operator_fee, initial_total_shares, min_operator_share_rate, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX ): - tx_type, tx_info, error = self.sign_create_public_pool( - operator_fee, initial_total_shares, min_operator_share_rate, nonce + tx_type, tx_info, tx_hash, error = self.sign_create_public_pool( + operator_fee, initial_total_shares, min_operator_share_rate, nonce, api_key_index ) if error is not None: return None, None, error - logging.debug(f"Create Public Pool Tx Info: {tx_info}") + logging.debug(f"Create Public Pool TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) - logging.debug(f"Create Public Pool Send Tx Response: {api_response}") + logging.debug(f"Create Public Pool Send. TxResponse: {api_response}") return tx_info, api_response, None @process_api_key_and_nonce async def update_public_pool( - self, public_pool_index, status, operator_fee, min_operator_share_rate, nonce=-1, api_key_index=-1 + self, public_pool_index, status, operator_fee, min_operator_share_rate, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX ): - tx_type, tx_info, error = self.sign_update_public_pool( - public_pool_index, status, operator_fee, min_operator_share_rate, nonce + tx_type, tx_info, tx_hash, error = self.sign_update_public_pool( + public_pool_index, status, operator_fee, min_operator_share_rate, nonce, api_key_index ) if error is not None: return None, None, error - logging.debug(f"Update Public Pool Tx Info: {tx_info}") + logging.debug(f"Update Public Pool TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) - logging.debug(f"Update Public Pool Send Tx Response: {api_response}") + logging.debug(f"Update Public Pool Send. TxResponse: {api_response}") return tx_info, api_response, None @process_api_key_and_nonce - async def mint_shares(self, public_pool_index, share_amount, nonce=-1, api_key_index=-1): - tx_type, tx_info, error = self.sign_mint_shares(public_pool_index, share_amount, nonce) + async def mint_shares(self, public_pool_index, share_amount, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX): + tx_type, tx_info, tx_hash, error = self.sign_mint_shares(public_pool_index, share_amount, nonce, api_key_index) if error is not None: return None, None, error - logging.debug(f"Mint Shares Tx Info: {tx_info}") + logging.debug(f"Mint Shares TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) - logging.debug(f"Mint Shares Send Tx Response: {api_response}") + logging.debug(f"Mint Shares Send. TxResponse: {api_response}") return tx_info, api_response, None @process_api_key_and_nonce - async def burn_shares(self, public_pool_index, share_amount, nonce=-1, api_key_index=-1): - tx_type, tx_info, error = self.sign_burn_shares(public_pool_index, share_amount, nonce) + async def burn_shares(self, public_pool_index, share_amount, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX): + tx_type, tx_info, tx_hash, error = self.sign_burn_shares(public_pool_index, share_amount, nonce, api_key_index) if error is not None: return None, None, error - logging.debug(f"Burn Shares Tx Info: {tx_info}") + logging.debug(f"Burn Shares TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) - logging.debug(f"Burn Shares Send Tx Response: {api_response}") + logging.debug(f"Burn Shares Send. TxResponse: {api_response}") return tx_info, api_response, None @process_api_key_and_nonce - async def update_leverage(self, market_index, margin_mode, leverage, nonce=-1, api_key_index=-1): + async def update_leverage(self, market_index, margin_mode, leverage, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX): imf = int(10_000 / leverage) - tx_type, tx_info, error = self.sign_update_leverage(market_index, imf, margin_mode, nonce) + tx_type, tx_info, tx_hash, error = self.sign_update_leverage(market_index, imf, margin_mode, nonce, api_key_index) if error is not None: return None, None, error - logging.debug(f"Update Leverage Tx Info: {tx_info}") + logging.debug(f"Update Leverage TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) logging.debug(f"Update Leverage Tx Response: {api_response}") return tx_info, api_response, None @process_api_key_and_nonce - async def update_margin(self, market_index: int, usdc_amount: float, direction: int, nonce: int = -1): + async def update_margin(self, market_index: int, usdc_amount: float, direction: int, nonce: int = DEFAULT_NONCE, api_key_index: int = DEFAULT_API_KEY_INDEX): usdc_amount = int(usdc_amount * self.USDC_TICKER_SCALE) - tx_type, tx_info, error = self.sign_update_margin(market_index, usdc_amount, direction, nonce) + tx_type, tx_info, tx_hash, error = self.sign_update_margin(market_index, usdc_amount, direction, nonce, api_key_index) if error is not None: return None, None, error - logging.debug(f"Update Margin Tx Info: {tx_info}") + logging.debug(f"Update Margin TxHash: {tx_hash} TxInfo: {tx_info}") api_response = await self.send_tx(tx_type=tx_type, tx_info=tx_info) logging.debug(f"Update Margin Tx Response: {api_response}") return tx_info, api_response, None @@ -886,6 +877,16 @@ async def send_tx(self, tx_type: StrictInt, tx_info: str) -> RespSendTx: raise Exception(tx_info) return await self.tx_api.send_tx(tx_type=tx_type, tx_info=tx_info) + async def send_tx_batch(self, tx_types: List[StrictInt], tx_infos: List[str]) -> RespSendTxBatch: + if len(tx_types) != len(tx_infos): + raise Exception("Tx types and tx infos must be of same length") + if len(tx_types) == 0: + raise Exception("Empty tx types and tx infos") + + if tx_infos[0][0] != "{": + raise Exception(tx_infos) + return await self.tx_api.send_tx_batch(tx_types=json.dumps(tx_types), tx_infos=json.dumps(tx_infos)) + async def close(self): await self.api_client.close() diff --git a/lighter/signers/lighter-signer-darwin-arm64.dylib b/lighter/signers/lighter-signer-darwin-arm64.dylib index ec0ecef..6d164ae 100644 Binary files a/lighter/signers/lighter-signer-darwin-arm64.dylib and b/lighter/signers/lighter-signer-darwin-arm64.dylib differ diff --git a/lighter/signers/lighter-signer-darwin-arm64.h b/lighter/signers/lighter-signer-darwin-arm64.h index 9e29d07..6361a6f 100644 --- a/lighter/signers/lighter-signer-darwin-arm64.h +++ b/lighter/signers/lighter-signer-darwin-arm64.h @@ -19,7 +19,7 @@ typedef struct { const char *p; ptrdiff_t n; } _GoString_; /* Start of preamble from import "C" comments. */ -#line 18 "sharedlib.go" +#line 15 "main.go" #include #include @@ -28,6 +28,14 @@ typedef struct { char* err; } StrOrErr; +typedef struct { + uint8_t txType; + char* txInfo; + char* txHash; + char* messageToSign; + char* err; +} SignedTxResponse; + typedef struct { char* privateKey; char* publicKey; @@ -106,23 +114,22 @@ extern "C" { extern ApiKeyResponse GenerateAPIKey(char* cSeed); extern char* CreateClient(char* cUrl, char* cPrivateKey, int cChainId, int cApiKeyIndex, long long cAccountIndex); extern char* CheckClient(int cApiKeyIndex, long long cAccountIndex); -extern StrOrErr SignChangePubKey(char* cPubKey, long long cNonce); -extern StrOrErr SignCreateOrder(int cMarketIndex, long long cClientOrderIndex, long long cBaseAmount, int cPrice, int cIsAsk, int cOrderType, int cTimeInForce, int cReduceOnly, int cTriggerPrice, long long cOrderExpiry, long long cNonce); -extern StrOrErr SignCreateGroupedOrders(uint8_t cGroupingType, CreateOrderTxReq* cOrders, int cLen, long long cNonce); -extern StrOrErr SignCancelOrder(int cMarketIndex, long long cOrderIndex, long long cNonce); -extern StrOrErr SignWithdraw(long long cUSDCAmount, long long cNonce); -extern StrOrErr SignCreateSubAccount(long long cNonce); -extern StrOrErr SignCancelAllOrders(int cTimeInForce, long long cTime, long long cNonce); -extern StrOrErr SignModifyOrder(int cMarketIndex, long long cIndex, long long cBaseAmount, long long cPrice, long long cTriggerPrice, long long cNonce); -extern StrOrErr SignTransfer(long long cToAccountIndex, long long cUSDCAmount, long long cFee, char* cMemo, long long cNonce); -extern StrOrErr SignCreatePublicPool(long long cOperatorFee, long long cInitialTotalShares, long long cMinOperatorShareRate, long long cNonce); -extern StrOrErr SignUpdatePublicPool(long long cPublicPoolIndex, int cStatus, long long cOperatorFee, long long cMinOperatorShareRate, long long cNonce); -extern StrOrErr SignMintShares(long long cPublicPoolIndex, long long cShareAmount, long long cNonce); -extern StrOrErr SignBurnShares(long long cPublicPoolIndex, long long cShareAmount, long long cNonce); -extern StrOrErr SignUpdateLeverage(int cMarketIndex, int cInitialMarginFraction, int cMarginMode, long long cNonce); -extern StrOrErr CreateAuthToken(long long cDeadline); -extern char* SwitchAPIKey(int c); -extern StrOrErr SignUpdateMargin(int cMarketIndex, long long cUSDCAmount, int cDirection, long long cNonce); +extern SignedTxResponse SignChangePubKey(char* cPubKey, long long cNonce, int cApiKeyIndex, long long cAccountIndex); +extern SignedTxResponse SignCreateOrder(int cMarketIndex, long long cClientOrderIndex, long long cBaseAmount, int cPrice, int cIsAsk, int cOrderType, int cTimeInForce, int cReduceOnly, int cTriggerPrice, long long cOrderExpiry, long long cNonce, int cApiKeyIndex, long long cAccountIndex); +extern SignedTxResponse SignCreateGroupedOrders(uint8_t cGroupingType, CreateOrderTxReq* cOrders, int cLen, long long cNonce, int cApiKeyIndex, long long cAccountIndex); +extern SignedTxResponse SignCancelOrder(int cMarketIndex, long long cOrderIndex, long long cNonce, int cApiKeyIndex, long long cAccountIndex); +extern SignedTxResponse SignWithdraw(long long cUSDCAmount, long long cNonce, int cApiKeyIndex, long long cAccountIndex); +extern SignedTxResponse SignCreateSubAccount(long long cNonce, int cApiKeyIndex, long long cAccountIndex); +extern SignedTxResponse SignCancelAllOrders(int cTimeInForce, long long cTime, long long cNonce, int cApiKeyIndex, long long cAccountIndex); +extern SignedTxResponse SignModifyOrder(int cMarketIndex, long long cIndex, long long cBaseAmount, long long cPrice, long long cTriggerPrice, long long cNonce, int cApiKeyIndex, long long cAccountIndex); +extern SignedTxResponse SignTransfer(long long cToAccountIndex, long long cUSDCAmount, long long cFee, char* cMemo, long long cNonce, int cApiKeyIndex, long long cAccountIndex); +extern SignedTxResponse SignCreatePublicPool(long long cOperatorFee, long long cInitialTotalShares, long long cMinOperatorShareRate, long long cNonce, int cApiKeyIndex, long long cAccountIndex); +extern SignedTxResponse SignUpdatePublicPool(long long cPublicPoolIndex, int cStatus, long long cOperatorFee, long long cMinOperatorShareRate, long long cNonce, int cApiKeyIndex, long long cAccountIndex); +extern SignedTxResponse SignMintShares(long long cPublicPoolIndex, long long cShareAmount, long long cNonce, int cApiKeyIndex, long long cAccountIndex); +extern SignedTxResponse SignBurnShares(long long cPublicPoolIndex, long long cShareAmount, long long cNonce, int cApiKeyIndex, long long cAccountIndex); +extern SignedTxResponse SignUpdateLeverage(int cMarketIndex, int cInitialMarginFraction, int cMarginMode, long long cNonce, int cApiKeyIndex, long long cAccountIndex); +extern StrOrErr CreateAuthToken(long long cDeadline, int cApiKeyIndex, long long cAccountIndex); +extern SignedTxResponse SignUpdateMargin(int cMarketIndex, long long cUSDCAmount, int cDirection, long long cNonce, int cApiKeyIndex, long long cAccountIndex); #ifdef __cplusplus } diff --git a/lighter/signers/lighter-signer-linux-amd64.h b/lighter/signers/lighter-signer-linux-amd64.h index 5e0df7d..e68289d 100644 --- a/lighter/signers/lighter-signer-linux-amd64.h +++ b/lighter/signers/lighter-signer-linux-amd64.h @@ -1,6 +1,6 @@ /* Code generated by cmd/cgo; DO NOT EDIT. */ -/* package command-line-arguments */ +/* package github.com/elliottech/lighter-go/sharedlib */ #line 1 "cgo-builtin-export-prolog" @@ -19,7 +19,7 @@ typedef struct { const char *p; ptrdiff_t n; } _GoString_; /* Start of preamble from import "C" comments. */ -#line 18 "sharedlib.go" +#line 15 "main.go" #include #include @@ -28,6 +28,14 @@ typedef struct { char* err; } StrOrErr; +typedef struct { + uint8_t txType; + char* txInfo; + char* txHash; + char* messageToSign; + char* err; +} SignedTxResponse; + typedef struct { char* privateKey; char* publicKey; @@ -106,23 +114,22 @@ extern "C" { extern ApiKeyResponse GenerateAPIKey(char* cSeed); extern char* CreateClient(char* cUrl, char* cPrivateKey, int cChainId, int cApiKeyIndex, long long int cAccountIndex); extern char* CheckClient(int cApiKeyIndex, long long int cAccountIndex); -extern StrOrErr SignChangePubKey(char* cPubKey, long long int cNonce); -extern StrOrErr SignCreateOrder(int cMarketIndex, long long int cClientOrderIndex, long long int cBaseAmount, int cPrice, int cIsAsk, int cOrderType, int cTimeInForce, int cReduceOnly, int cTriggerPrice, long long int cOrderExpiry, long long int cNonce); -extern StrOrErr SignCreateGroupedOrders(uint8_t cGroupingType, CreateOrderTxReq* cOrders, int cLen, long long int cNonce); -extern StrOrErr SignCancelOrder(int cMarketIndex, long long int cOrderIndex, long long int cNonce); -extern StrOrErr SignWithdraw(long long int cUSDCAmount, long long int cNonce); -extern StrOrErr SignCreateSubAccount(long long int cNonce); -extern StrOrErr SignCancelAllOrders(int cTimeInForce, long long int cTime, long long int cNonce); -extern StrOrErr SignModifyOrder(int cMarketIndex, long long int cIndex, long long int cBaseAmount, long long int cPrice, long long int cTriggerPrice, long long int cNonce); -extern StrOrErr SignTransfer(long long int cToAccountIndex, long long int cUSDCAmount, long long int cFee, char* cMemo, long long int cNonce); -extern StrOrErr SignCreatePublicPool(long long int cOperatorFee, long long int cInitialTotalShares, long long int cMinOperatorShareRate, long long int cNonce); -extern StrOrErr SignUpdatePublicPool(long long int cPublicPoolIndex, int cStatus, long long int cOperatorFee, long long int cMinOperatorShareRate, long long int cNonce); -extern StrOrErr SignMintShares(long long int cPublicPoolIndex, long long int cShareAmount, long long int cNonce); -extern StrOrErr SignBurnShares(long long int cPublicPoolIndex, long long int cShareAmount, long long int cNonce); -extern StrOrErr SignUpdateLeverage(int cMarketIndex, int cInitialMarginFraction, int cMarginMode, long long int cNonce); -extern StrOrErr CreateAuthToken(long long int cDeadline); -extern char* SwitchAPIKey(int c); -extern StrOrErr SignUpdateMargin(int cMarketIndex, long long int cUSDCAmount, int cDirection, long long int cNonce); +extern SignedTxResponse SignChangePubKey(char* cPubKey, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignCreateOrder(int cMarketIndex, long long int cClientOrderIndex, long long int cBaseAmount, int cPrice, int cIsAsk, int cOrderType, int cTimeInForce, int cReduceOnly, int cTriggerPrice, long long int cOrderExpiry, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignCreateGroupedOrders(uint8_t cGroupingType, CreateOrderTxReq* cOrders, int cLen, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignCancelOrder(int cMarketIndex, long long int cOrderIndex, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignWithdraw(long long int cUSDCAmount, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignCreateSubAccount(long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignCancelAllOrders(int cTimeInForce, long long int cTime, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignModifyOrder(int cMarketIndex, long long int cIndex, long long int cBaseAmount, long long int cPrice, long long int cTriggerPrice, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignTransfer(long long int cToAccountIndex, long long int cUSDCAmount, long long int cFee, char* cMemo, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignCreatePublicPool(long long int cOperatorFee, long long int cInitialTotalShares, long long int cMinOperatorShareRate, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignUpdatePublicPool(long long int cPublicPoolIndex, int cStatus, long long int cOperatorFee, long long int cMinOperatorShareRate, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignMintShares(long long int cPublicPoolIndex, long long int cShareAmount, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignBurnShares(long long int cPublicPoolIndex, long long int cShareAmount, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignUpdateLeverage(int cMarketIndex, int cInitialMarginFraction, int cMarginMode, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern StrOrErr CreateAuthToken(long long int cDeadline, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignUpdateMargin(int cMarketIndex, long long int cUSDCAmount, int cDirection, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); #ifdef __cplusplus } diff --git a/lighter/signers/lighter-signer-linux-amd64.so b/lighter/signers/lighter-signer-linux-amd64.so index bfe40ed..66944dc 100644 Binary files a/lighter/signers/lighter-signer-linux-amd64.so and b/lighter/signers/lighter-signer-linux-amd64.so differ diff --git a/lighter/signers/lighter-signer-linux-arm64.h b/lighter/signers/lighter-signer-linux-arm64.h index 5e0df7d..e68289d 100644 --- a/lighter/signers/lighter-signer-linux-arm64.h +++ b/lighter/signers/lighter-signer-linux-arm64.h @@ -1,6 +1,6 @@ /* Code generated by cmd/cgo; DO NOT EDIT. */ -/* package command-line-arguments */ +/* package github.com/elliottech/lighter-go/sharedlib */ #line 1 "cgo-builtin-export-prolog" @@ -19,7 +19,7 @@ typedef struct { const char *p; ptrdiff_t n; } _GoString_; /* Start of preamble from import "C" comments. */ -#line 18 "sharedlib.go" +#line 15 "main.go" #include #include @@ -28,6 +28,14 @@ typedef struct { char* err; } StrOrErr; +typedef struct { + uint8_t txType; + char* txInfo; + char* txHash; + char* messageToSign; + char* err; +} SignedTxResponse; + typedef struct { char* privateKey; char* publicKey; @@ -106,23 +114,22 @@ extern "C" { extern ApiKeyResponse GenerateAPIKey(char* cSeed); extern char* CreateClient(char* cUrl, char* cPrivateKey, int cChainId, int cApiKeyIndex, long long int cAccountIndex); extern char* CheckClient(int cApiKeyIndex, long long int cAccountIndex); -extern StrOrErr SignChangePubKey(char* cPubKey, long long int cNonce); -extern StrOrErr SignCreateOrder(int cMarketIndex, long long int cClientOrderIndex, long long int cBaseAmount, int cPrice, int cIsAsk, int cOrderType, int cTimeInForce, int cReduceOnly, int cTriggerPrice, long long int cOrderExpiry, long long int cNonce); -extern StrOrErr SignCreateGroupedOrders(uint8_t cGroupingType, CreateOrderTxReq* cOrders, int cLen, long long int cNonce); -extern StrOrErr SignCancelOrder(int cMarketIndex, long long int cOrderIndex, long long int cNonce); -extern StrOrErr SignWithdraw(long long int cUSDCAmount, long long int cNonce); -extern StrOrErr SignCreateSubAccount(long long int cNonce); -extern StrOrErr SignCancelAllOrders(int cTimeInForce, long long int cTime, long long int cNonce); -extern StrOrErr SignModifyOrder(int cMarketIndex, long long int cIndex, long long int cBaseAmount, long long int cPrice, long long int cTriggerPrice, long long int cNonce); -extern StrOrErr SignTransfer(long long int cToAccountIndex, long long int cUSDCAmount, long long int cFee, char* cMemo, long long int cNonce); -extern StrOrErr SignCreatePublicPool(long long int cOperatorFee, long long int cInitialTotalShares, long long int cMinOperatorShareRate, long long int cNonce); -extern StrOrErr SignUpdatePublicPool(long long int cPublicPoolIndex, int cStatus, long long int cOperatorFee, long long int cMinOperatorShareRate, long long int cNonce); -extern StrOrErr SignMintShares(long long int cPublicPoolIndex, long long int cShareAmount, long long int cNonce); -extern StrOrErr SignBurnShares(long long int cPublicPoolIndex, long long int cShareAmount, long long int cNonce); -extern StrOrErr SignUpdateLeverage(int cMarketIndex, int cInitialMarginFraction, int cMarginMode, long long int cNonce); -extern StrOrErr CreateAuthToken(long long int cDeadline); -extern char* SwitchAPIKey(int c); -extern StrOrErr SignUpdateMargin(int cMarketIndex, long long int cUSDCAmount, int cDirection, long long int cNonce); +extern SignedTxResponse SignChangePubKey(char* cPubKey, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignCreateOrder(int cMarketIndex, long long int cClientOrderIndex, long long int cBaseAmount, int cPrice, int cIsAsk, int cOrderType, int cTimeInForce, int cReduceOnly, int cTriggerPrice, long long int cOrderExpiry, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignCreateGroupedOrders(uint8_t cGroupingType, CreateOrderTxReq* cOrders, int cLen, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignCancelOrder(int cMarketIndex, long long int cOrderIndex, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignWithdraw(long long int cUSDCAmount, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignCreateSubAccount(long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignCancelAllOrders(int cTimeInForce, long long int cTime, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignModifyOrder(int cMarketIndex, long long int cIndex, long long int cBaseAmount, long long int cPrice, long long int cTriggerPrice, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignTransfer(long long int cToAccountIndex, long long int cUSDCAmount, long long int cFee, char* cMemo, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignCreatePublicPool(long long int cOperatorFee, long long int cInitialTotalShares, long long int cMinOperatorShareRate, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignUpdatePublicPool(long long int cPublicPoolIndex, int cStatus, long long int cOperatorFee, long long int cMinOperatorShareRate, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignMintShares(long long int cPublicPoolIndex, long long int cShareAmount, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignBurnShares(long long int cPublicPoolIndex, long long int cShareAmount, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignUpdateLeverage(int cMarketIndex, int cInitialMarginFraction, int cMarginMode, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern StrOrErr CreateAuthToken(long long int cDeadline, int cApiKeyIndex, long long int cAccountIndex); +extern SignedTxResponse SignUpdateMargin(int cMarketIndex, long long int cUSDCAmount, int cDirection, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); #ifdef __cplusplus } diff --git a/lighter/signers/lighter-signer-linux-arm64.so b/lighter/signers/lighter-signer-linux-arm64.so index 133693d..d4d5c60 100644 Binary files a/lighter/signers/lighter-signer-linux-arm64.so and b/lighter/signers/lighter-signer-linux-arm64.so differ diff --git a/lighter/signers/lighter-signer-windows-amd64.dll b/lighter/signers/lighter-signer-windows-amd64.dll index ed59a72..1601cc6 100644 Binary files a/lighter/signers/lighter-signer-windows-amd64.dll and b/lighter/signers/lighter-signer-windows-amd64.dll differ diff --git a/lighter/signers/lighter-signer-windows-amd64.h b/lighter/signers/lighter-signer-windows-amd64.h index ecd926b..f923486 100644 --- a/lighter/signers/lighter-signer-windows-amd64.h +++ b/lighter/signers/lighter-signer-windows-amd64.h @@ -19,7 +19,7 @@ typedef struct { const char *p; ptrdiff_t n; } _GoString_; /* Start of preamble from import "C" comments. */ -#line 18 "sharedlib.go" +#line 15 "main.go" #include #include @@ -28,6 +28,14 @@ typedef struct { char* err; } StrOrErr; +typedef struct { + uint8_t txType; + char* txInfo; + char* txHash; + char* messageToSign; + char* err; +} SignedTxResponse; + typedef struct { char* privateKey; char* publicKey; @@ -106,23 +114,22 @@ extern "C" { extern __declspec(dllexport) ApiKeyResponse GenerateAPIKey(char* cSeed); extern __declspec(dllexport) char* CreateClient(char* cUrl, char* cPrivateKey, int cChainId, int cApiKeyIndex, long long int cAccountIndex); extern __declspec(dllexport) char* CheckClient(int cApiKeyIndex, long long int cAccountIndex); -extern __declspec(dllexport) StrOrErr SignChangePubKey(char* cPubKey, long long int cNonce); -extern __declspec(dllexport) StrOrErr SignCreateOrder(int cMarketIndex, long long int cClientOrderIndex, long long int cBaseAmount, int cPrice, int cIsAsk, int cOrderType, int cTimeInForce, int cReduceOnly, int cTriggerPrice, long long int cOrderExpiry, long long int cNonce); -extern __declspec(dllexport) StrOrErr SignCreateGroupedOrders(uint8_t cGroupingType, CreateOrderTxReq* cOrders, int cLen, long long int cNonce); -extern __declspec(dllexport) StrOrErr SignCancelOrder(int cMarketIndex, long long int cOrderIndex, long long int cNonce); -extern __declspec(dllexport) StrOrErr SignWithdraw(long long int cUSDCAmount, long long int cNonce); -extern __declspec(dllexport) StrOrErr SignCreateSubAccount(long long int cNonce); -extern __declspec(dllexport) StrOrErr SignCancelAllOrders(int cTimeInForce, long long int cTime, long long int cNonce); -extern __declspec(dllexport) StrOrErr SignModifyOrder(int cMarketIndex, long long int cIndex, long long int cBaseAmount, long long int cPrice, long long int cTriggerPrice, long long int cNonce); -extern __declspec(dllexport) StrOrErr SignTransfer(long long int cToAccountIndex, long long int cUSDCAmount, long long int cFee, char* cMemo, long long int cNonce); -extern __declspec(dllexport) StrOrErr SignCreatePublicPool(long long int cOperatorFee, long long int cInitialTotalShares, long long int cMinOperatorShareRate, long long int cNonce); -extern __declspec(dllexport) StrOrErr SignUpdatePublicPool(long long int cPublicPoolIndex, int cStatus, long long int cOperatorFee, long long int cMinOperatorShareRate, long long int cNonce); -extern __declspec(dllexport) StrOrErr SignMintShares(long long int cPublicPoolIndex, long long int cShareAmount, long long int cNonce); -extern __declspec(dllexport) StrOrErr SignBurnShares(long long int cPublicPoolIndex, long long int cShareAmount, long long int cNonce); -extern __declspec(dllexport) StrOrErr SignUpdateLeverage(int cMarketIndex, int cInitialMarginFraction, int cMarginMode, long long int cNonce); -extern __declspec(dllexport) StrOrErr CreateAuthToken(long long int cDeadline); -extern __declspec(dllexport) char* SwitchAPIKey(int c); -extern __declspec(dllexport) StrOrErr SignUpdateMargin(int cMarketIndex, long long int cUSDCAmount, int cDirection, long long int cNonce); +extern __declspec(dllexport) SignedTxResponse SignChangePubKey(char* cPubKey, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) SignedTxResponse SignCreateOrder(int cMarketIndex, long long int cClientOrderIndex, long long int cBaseAmount, int cPrice, int cIsAsk, int cOrderType, int cTimeInForce, int cReduceOnly, int cTriggerPrice, long long int cOrderExpiry, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) SignedTxResponse SignCreateGroupedOrders(uint8_t cGroupingType, CreateOrderTxReq* cOrders, int cLen, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) SignedTxResponse SignCancelOrder(int cMarketIndex, long long int cOrderIndex, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) SignedTxResponse SignWithdraw(long long int cUSDCAmount, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) SignedTxResponse SignCreateSubAccount(long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) SignedTxResponse SignCancelAllOrders(int cTimeInForce, long long int cTime, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) SignedTxResponse SignModifyOrder(int cMarketIndex, long long int cIndex, long long int cBaseAmount, long long int cPrice, long long int cTriggerPrice, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) SignedTxResponse SignTransfer(long long int cToAccountIndex, long long int cUSDCAmount, long long int cFee, char* cMemo, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) SignedTxResponse SignCreatePublicPool(long long int cOperatorFee, long long int cInitialTotalShares, long long int cMinOperatorShareRate, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) SignedTxResponse SignUpdatePublicPool(long long int cPublicPoolIndex, int cStatus, long long int cOperatorFee, long long int cMinOperatorShareRate, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) SignedTxResponse SignMintShares(long long int cPublicPoolIndex, long long int cShareAmount, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) SignedTxResponse SignBurnShares(long long int cPublicPoolIndex, long long int cShareAmount, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) SignedTxResponse SignUpdateLeverage(int cMarketIndex, int cInitialMarginFraction, int cMarginMode, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) StrOrErr CreateAuthToken(long long int cDeadline, int cApiKeyIndex, long long int cAccountIndex); +extern __declspec(dllexport) SignedTxResponse SignUpdateMargin(int cMarketIndex, long long int cUSDCAmount, int cDirection, long long int cNonce, int cApiKeyIndex, long long int cAccountIndex); #ifdef __cplusplus }