diff --git a/.gitignore b/.gitignore index ef7d038..416367f 100644 --- a/.gitignore +++ b/.gitignore @@ -72,4 +72,5 @@ examples/secrets.py # Environment variables .env -.env.* \ No newline at end of file +.env.* +api_key_config.json \ No newline at end of file diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000..87b6ebd --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1 @@ +api_key_config.json \ No newline at end of file diff --git a/examples/README.md b/examples/README.md index b506b7c..13a3e5a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,18 +5,43 @@ - this will require you to enter your Ethereum private key - the eth private key will only be used in the Py SDK to sign a message - the eth private key is not required in order to trade on the platform - - the eth private key is not passed to the binary - - copy the output of the script and post it into `create_cancel_order.py` - - the output should look like -``` -BASE_URL = 'https://testnet.zklighter.elliot.ai' -API_KEY_PRIVATE_KEY = '0xea5d2eca5be67eca056752eaf27b173518b8a5550117c09d2b58c7ea7d306cc4426f913ccf27ab19' -ACCOUNT_INDEX = 595 -API_KEY_INDEX = 1 -``` -- start trading using - - `create_cancel_order.py` has an example which created an order on testnet & cancels it - - you'll need to set up both your account index, api key index & API Key private key + - the eth private key is not passed to the binary + - the API key config is saved in a local file `./api_key_config.json` + +## Start trading on testnet +- `create_modify_cancel_order.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` + - sends TXs over WS instead of HTTP + +- `create_grouped_ioc_with_attached_sl_tp.py` + - creates an ask (sell) IoC order for 0.1 ETH + - along w/ the order, it sets up a Stop Loss (SL) and a Take Profit (TP) order for the whole size of the order + - 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` + - 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 + - the SL/TP orders are canceled when the sign of your position changes + +### On SL/TP orders +SL/TP orders need to be configured beyond just setting the trigger price. When the trigger price is set, +the order will just be executed, like a normal order. This means that a market order, for example, might not have enough slippage! \ +Let's say that you have a 1 BTC long position, and the current price is $110'000. \ +You want to set up a take profit at $120'000 +- order should be an ask (sell) order, to close your position +- the trigger price should be $120'000 + +What about the order types? Just as normal orders, SL/TP orders trigger an order, which can be: +- market order +- limit IOC / GTC ## Setup steps for mainnet - deposit money on Lighter to create an account first diff --git a/examples/create_cancel_order.py b/examples/create_cancel_order.py deleted file mode 100644 index f757296..0000000 --- a/examples/create_cancel_order.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. -# 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 - - -def trim_exception(e: Exception) -> str: - return str(e).strip().split("\n")[-1] - - -async def main(): - 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, - ) - - err = client.check_client() - if err is not None: - print(f"CheckClient error: {trim_exception(err)}") - return - - # create order - tx, tx_hash, err = await client.create_order( - market_index=0, - client_order_index=123, - base_amount=100000, - price=405000, - 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, - ) - print(f"Create Order {tx=} {tx_hash=} {err=}") - if err is not None: - raise Exception(err) - - auth, err = client.create_auth_token_with_expiry(lighter.SignerClient.DEFAULT_10_MIN_AUTH_EXPIRY) - print(f"{auth=}") - if err is not None: - raise Exception(err) - - # cancel order - tx, tx_hash, err = await client.cancel_order( - market_index=0, - order_index=123, - ) - print(f"Cancel Order {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/create_grouped_ioc_with_attached_sl_tp.py b/examples/create_grouped_ioc_with_attached_sl_tp.py new file mode 100644 index 0000000..b08e36f --- /dev/null +++ b/examples/create_grouped_ioc_with_attached_sl_tp.py @@ -0,0 +1,61 @@ +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() + + ioc_order = CreateOrderTxReq( + MarketIndex=0, + ClientOrderIndex=0, + BaseAmount=1000, # 0.1 ETH + Price=300000, # $3000 + IsAsk=1, # sell + Type=lighter.SignerClient.ORDER_TYPE_LIMIT, + TimeInForce=lighter.SignerClient.ORDER_TIME_IN_FORCE_IMMEDIATE_OR_CANCEL, + ReduceOnly=0, + TriggerPrice=0, + OrderExpiry=0, + ) + + # 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_TRIGGERS_A_ONE_CANCELS_THE_OTHER, + orders=[ioc_order, 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_market_order.py b/examples/create_market_order.py index 069d2db..367f89a 100644 --- a/examples/create_market_order.py +++ b/examples/create_market_order.py @@ -1,38 +1,23 @@ 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 serves as an example. -BASE_URL = "https://testnet.zklighter.elliot.ai" -API_KEY_PRIVATE_KEY = "0xed636277f3753b6c0275f7a28c2678a7f3a95655e09deaebec15179b50c5da7f903152e50f594f7b" -ACCOUNT_INDEX = 65 -API_KEY_INDEX = 3 - - -def trim_exception(e: Exception) -> str: - return str(e).strip().split("\n")[-1] +from utils import default_example_setup 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, - ) + client, api_client, _ = default_example_setup() - tx = await client.create_market_order( + tx, tx_hash, err = await client.create_market_order( market_index=0, client_order_index=0, base_amount=1000, # 0.1 ETH avg_execution_price=170000, # $1700 -- worst acceptable price for the order is_ask=True, ) - print("Create Order Tx:", tx) + print(f"Create Order {tx=} {tx_hash=} {err=}") + if err is not None: + raise Exception(err) + await client.close() + await api_client.close() if __name__ == "__main__": diff --git a/examples/create_market_order_max_slippage.py b/examples/create_market_order_max_slippage.py index f219a22..b05fa40 100644 --- a/examples/create_market_order_max_slippage.py +++ b/examples/create_market_order_max_slippage.py @@ -1,33 +1,21 @@ 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 serves 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] +from utils import default_example_setup 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, - ) + client, api_client, _ = default_example_setup() # tx = await client.create_market_order_limited_slippage(market_index=0, client_order_index=0, base_amount=30000000, # max_slippage=0.001, is_ask=True) - tx = await client.create_market_order_if_slippage(market_index=0, client_order_index=0, base_amount=30000000, - max_slippage=0.01, is_ask=True, ideal_price=300000) + tx = await client.create_market_order_if_slippage( + market_index=0, # ETH + client_order_index=0, + base_amount=1000, # 0.1 ETH + max_slippage=0.01, # 1% + is_ask=True, + ideal_price=300000 # $3000 + ) + print("Create Order Tx:", tx) await client.close() diff --git a/examples/create_modify_cancel_order.py b/examples/create_modify_cancel_order.py new file mode 100644 index 0000000..2de927c --- /dev/null +++ b/examples/create_modify_cancel_order.py @@ -0,0 +1,51 @@ +import asyncio +import lighter +from utils import default_example_setup + + +async def main(): + client, api_client, _ = default_example_setup() + + # create order + tx, tx_hash, err = await client.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, + ) + print(f"Create Order {tx=} {tx_hash=} {err=}") + if err is not None: + raise Exception(err) + + # create order + tx, tx_hash, err = await client.modify_order( + market_index=0, + order_index=123, + base_amount=1100, # 0.11 ETH + price=410000, # $4100 + trigger_price=0, + ) + print(f"Modify Order {tx=} {tx_hash=} {err=}") + if err is not None: + raise Exception(err) + + # cancel order + tx, tx_hash, err = await client.cancel_order( + market_index=0, + order_index=123, + ) + print(f"Cancel Order {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/create_grouped_orders.py b/examples/create_position_tied_sl_tl.py similarity index 65% rename from examples/create_grouped_orders.py rename to examples/create_position_tied_sl_tl.py index 6dfa3a5..936f7f9 100644 --- a/examples/create_grouped_orders.py +++ b/examples/create_position_tied_sl_tl.py @@ -1,31 +1,18 @@ import asyncio import lighter -import logging from lighter.signer_client import CreateOrderTxReq +from utils import default_example_setup -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 serves as an example. -BASE_URL = "https://testnet.zklighter.elliot.ai" -API_KEY_PRIVATE_KEY = "0xed636277f3753b6c0275f7a28c2678a7f3a95655e09deaebec15179b50c5da7f903152e50f594f7b" -ACCOUNT_INDEX = 65 -API_KEY_INDEX = 3 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, - ) + 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=1000, + BaseAmount=0, Price=300000, IsAsk=0, Type=lighter.SignerClient.ORDER_TYPE_TAKE_PROFIT_LIMIT, @@ -38,7 +25,7 @@ async def main(): stop_loss_order = CreateOrderTxReq( MarketIndex=0, ClientOrderIndex=0, - BaseAmount=1000, + BaseAmount=0, Price=500000, IsAsk=0, Type=lighter.SignerClient.ORDER_TYPE_STOP_LOSS_LIMIT, @@ -55,6 +42,7 @@ async def main(): print("Create Grouped Order Tx:", transaction) await client.close() + await api_client.close() if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/system_setup.py b/examples/system_setup.py index 0896d7d..bc7dca1 100644 --- a/examples/system_setup.py +++ b/examples/system_setup.py @@ -3,6 +3,7 @@ import time import eth_account import lighter +from utils import save_api_key_config logging.basicConfig(level=logging.DEBUG) @@ -69,14 +70,7 @@ async def main(): if err is not None: raise Exception(err) - print( - f""" -BASE_URL = '{BASE_URL}' -API_KEY_PRIVATE_KEY = '{private_key}' -ACCOUNT_INDEX = {account_index} -API_KEY_INDEX = {API_KEY_INDEX} - """ - ) + save_api_key_config(BASE_URL, private_key, account_index, API_KEY_INDEX) await tx_client.close() await api_client.close() diff --git a/examples/utils.py b/examples/utils.py new file mode 100644 index 0000000..e794537 --- /dev/null +++ b/examples/utils.py @@ -0,0 +1,47 @@ +import logging + +import json +import websockets + +import lighter + + +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"): + 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, + }, f, ensure_ascii=False, indent=2) + + +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"] + + +def default_example_setup(config_file="./api_key_config.json") -> (lighter.ApiClient, lighter.SignerClient, websockets.ClientConnection): + logging.basicConfig(level=logging.DEBUG) + + base_url, api_key_private_key, account_index, api_key_index = 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, + ) + + err = client.check_client() + if err is not None: + print(f"CheckClient error: {trim_exception(err)}") + return + + return client, api_client, websockets.connect(f"{base_url.replace('https', 'wss')}/stream") diff --git a/examples/ws_send_tx.py b/examples/ws_send_tx.py index c120b83..1624839 100644 --- a/examples/ws_send_tx.py +++ b/examples/ws_send_tx.py @@ -1,73 +1,76 @@ -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 = 3 - +import lighter +from utils import default_example_setup -async def ws_flow(tx_type, tx_info): - 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/sendtx", - "data": { - "id": f"my_random_id_{12345678}", # optional, helps id the response - "tx_type": tx_type, - "tx_info": json.loads(tx_info), - }, - } - ) +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.recv()) + 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 = 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) + client, api_client, ws_client_promise = default_example_setup() - next_nonce = await transaction_api.next_nonce( - account_index=ACCOUNT_INDEX, api_key_index=API_KEY_INDEX - ) - nonce_value = next_nonce.nonce + # setup WS client and print connected message + ws_client: websockets.ClientConnection = await ws_client_promise + print("Received:", await ws_client.recv()) - tx_info, error = client.sign_create_order( + # create order + tx_type, tx_info, err = 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, + 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, - nonce=nonce_value, ) - if error is not None: - print(f"Error signing order: {error}") - return + 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 ws_flow(lighter.SignerClient.TX_TYPE_CREATE_ORDER, tx_info) + await client.close() + await api_client.close() + await ws_client.close() if __name__ == "__main__": diff --git a/lighter/signer_client.py b/lighter/signer_client.py index 7812da5..0aac7e5 100644 --- a/lighter/signer_client.py +++ b/lighter/signer_client.py @@ -25,6 +25,7 @@ class ApiKeyResponse(ctypes.Structure): _fields_ = [("privateKey", ctypes.c_char_p), ("publicKey", ctypes.c_char_p), ("err", ctypes.c_char_p)] + class CreateOrderTxReq(ctypes.Structure): _fields_ = [ ("MarketIndex", ctypes.c_uint8), @@ -39,11 +40,15 @@ class CreateOrderTxReq(ctypes.Structure): ("OrderExpiry", ctypes.c_longlong), ] + class StrOrErr(ctypes.Structure): _fields_ = [("str", ctypes.c_char_p), ("err", ctypes.c_char_p)] -def _initialize_signer(): +__signer = None + + +def __get_shared_library(): is_linux = platform.system() == "Linux" is_mac = platform.system() == "Darwin" is_windows = platform.system() == "Windows" @@ -54,11 +59,13 @@ def _initialize_signer(): path_to_signer_folders = os.path.join(current_file_directory, "signers") if is_arm and is_mac: - return ctypes.CDLL(os.path.join(path_to_signer_folders, "signer-arm64.dylib")) + return ctypes.CDLL(os.path.join(path_to_signer_folders, "lighter-signer-darwin-arm64.dylib")) elif is_linux and is_x64: - return ctypes.CDLL(os.path.join(path_to_signer_folders, "signer-amd64.so")) + return ctypes.CDLL(os.path.join(path_to_signer_folders, "lighter-signer-linux-amd64.so")) + elif is_linux and is_arm: + return ctypes.CDLL(os.path.join(path_to_signer_folders, "lighter-signer-linux-arm64.so")) elif is_windows and is_x64: - return ctypes.CDLL(os.path.join(path_to_signer_folders, "signer-amd64.dll")) + return ctypes.CDLL(os.path.join(path_to_signer_folders, "lighter-signer-windows-amd64.dll")) else: raise Exception( f"Unsupported platform/architecture: {platform.system()}/{platform.machine()}. " @@ -66,13 +73,80 @@ def _initialize_signer(): ) -def create_api_key(seed=""): - signer = _initialize_signer() - signer.GenerateAPIKey.argtypes = [ - ctypes.c_char_p, - ] +def __populate_shared_library_functions(signer): + signer.GenerateAPIKey.argtypes = [ctypes.c_char_p] signer.GenerateAPIKey.restype = ApiKeyResponse - result = signer.GenerateAPIKey(ctypes.c_char_p(seed.encode("utf-8"))) + + signer.CreateClient.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_int, ctypes.c_longlong] + signer.CreateClient.restype = ctypes.c_char_p + + 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.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 + + signer.SignCreateGroupedOrders.argtypes = [ctypes.c_uint8, ctypes.POINTER(CreateOrderTxReq), ctypes.c_int, ctypes.c_longlong] + signer.SignCreateGroupedOrders.restype = StrOrErr + + signer.SignCancelOrder.argtypes = [ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong] + signer.SignCancelOrder.restype = StrOrErr + + signer.SignWithdraw.argtypes = [ctypes.c_longlong, ctypes.c_longlong] + signer.SignWithdraw.restype = StrOrErr + + signer.SignCreateSubAccount.argtypes = [ctypes.c_longlong] + signer.SignCreateSubAccount.restype = StrOrErr + + signer.SignCancelAllOrders.argtypes = [ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong] + signer.SignCancelAllOrders.restype = StrOrErr + + 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.SignTransfer.argtypes = [ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_char_p, ctypes.c_longlong] + signer.SignTransfer.restype = StrOrErr + + signer.SignCreatePublicPool.argtypes = [ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong] + signer.SignCreatePublicPool.restype = StrOrErr + + signer.SignUpdatePublicPool.argtypes = [ctypes.c_longlong, ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong] + signer.SignUpdatePublicPool.restype = StrOrErr + + signer.SignMintShares.argtypes = [ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong] + signer.SignMintShares.restype = StrOrErr + + signer.SignBurnShares.argtypes = [ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong] + signer.SignBurnShares.restype = StrOrErr + + signer.SignUpdateLeverage.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_longlong] + signer.SignUpdateLeverage.restype = StrOrErr + + signer.CreateAuthToken.argtypes = [ctypes.c_longlong] + signer.CreateAuthToken.restype = StrOrErr + + signer.SwitchAPIKey.argtypes = [ctypes.c_int] + signer.SwitchAPIKey.restype = ctypes.c_char_p + + +def get_signer(): + # check if singleton exists already + global __signer + if __signer is not None: + return __signer + + # create shared library & populate methods + __signer = __get_shared_library() + __populate_shared_library_functions(__signer) + return __signer + + +def create_api_key(seed=""): + result = get_signer().GenerateAPIKey(ctypes.c_char_p(seed.encode("utf-8"))) private_key_str = result.privateKey.decode("utf-8") if result.privateKey else None public_key_str = result.publicKey.decode("utf-8") if result.publicKey else None @@ -100,7 +174,7 @@ async def wrapper(self, *args, **kwargs): if api_key_index == -1 and nonce == -1: api_key_index, nonce = self.nonce_manager.next_nonce() err = self.switch_api_key(api_key_index) - if err != None: + if err is not None: raise Exception(f"error switching api key: {err}") # Call the original function with modified kwargs @@ -140,6 +214,7 @@ class SignerClient: TX_TYPE_BURN_SHARES = 19 TX_TYPE_UPDATE_LEVERAGE = 20 TX_TYPE_CREATE_GROUP_ORDER = 28 + TX_TYPE_UPDATE_MARGIN = 29 ORDER_TYPE_LIMIT = 0 ORDER_TYPE_MARKET = 1 @@ -163,22 +238,22 @@ class SignerClient: DEFAULT_10_MIN_AUTH_EXPIRY = -1 MINUTE = 60 - CROSS_MARGIN_MODE = 0 + CROSS_MARGIN_MODE = 0 ISOLATED_MARGIN_MODE = 1 GROUPING_TYPE_ONE_TRIGGERS_THE_OTHER = 1 - GROUPING_TYPE_ONE_CANCELS_THE_OTHER = 2 + GROUPING_TYPE_ONE_CANCELS_THE_OTHER = 2 GROUPING_TYPE_ONE_TRIGGERS_A_ONE_CANCELS_THE_OTHER = 3 def __init__( - self, - url, - private_key, - api_key_index, - account_index, - max_api_key_index=-1, - private_keys: Optional[Dict[int, str]] = None, - nonce_management_type=nonce_manager.NonceManagerType.OPTIMISTIC, + self, + url, + private_key, + api_key_index, + account_index, + max_api_key_index=-1, + private_keys: Optional[Dict[int, str]] = None, + nonce_management_type=nonce_manager.NonceManagerType.OPTIMISTIC, ): """ First private key needs to be passed separately for backwards compatibility. @@ -202,7 +277,7 @@ def __init__( self.validate_api_private_keys(private_key, private_keys) self.api_key_dict = self.build_api_key_dict(private_key, private_keys) self.account_index = account_index - self.signer = _initialize_signer() + 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) @@ -216,6 +291,34 @@ def __init__( for api_key in range(self.api_key_index, self.end_api_key_index + 1): self.create_client(api_key) + # === 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 + + @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 + + 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"] + + # sign the message + acct = Account.from_key(eth_private_key) + message = encode_defunct(text=msg_to_sign) + signature = acct.sign_message(message) + 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): @@ -233,15 +336,7 @@ def build_api_key_dict(self, private_key, private_keys): return private_keys def create_client(self, api_key_index=None): - self.signer.CreateClient.argtypes = [ - ctypes.c_char_p, - ctypes.c_char_p, - ctypes.c_int, - ctypes.c_int, - ctypes.c_longlong, - ] api_key_index = api_key_index or self.api_key_index - self.signer.CreateClient.restype = ctypes.c_char_p err = self.signer.CreateClient( self.url.encode("utf-8"), self.api_key_dict[api_key_index].encode("utf-8"), @@ -253,66 +348,35 @@ def create_client(self, api_key_index=None): if err is None: return - err_str = err.decode("utf-8") - raise Exception(err_str) + if err is not None: + raise Exception(err.decode("utf-8")) + + def __signer_check_client( + self, + api_key_index: int, + account_index: int, + ) -> Optional[str]: + err = self.signer.CheckClient(api_key_index, account_index) + if err is None: + return None + + return err.decode("utf-8") # check_client verifies that the given API key associated with (api_key_index, account_index) matches the one on Lighter def check_client(self): - self.signer.CheckClient.argtypes = [ - ctypes.c_int, - ctypes.c_longlong, - ] - self.signer.CheckClient.restype = ctypes.c_char_p - for api_key in range(self.api_key_index, self.end_api_key_index + 1): - result = self.signer.CheckClient(api_key, self.account_index) - if result: - return result.decode("utf-8") + f" on api key {self.api_key_index}" - return result.decode("utf-8") if result else None + 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 None def switch_api_key(self, api_key: int): - self.signer.SwitchAPIKey.argtypes = [ctypes.c_int] - self.signer.CheckClient.restype = ctypes.c_char_p - result = self.signer.SwitchAPIKey(api_key) - return result.decode("utf-8") if result else None + err = self.signer.SwitchAPIKey(api_key) + return err.decode("utf-8") if err else None + @staticmethod def create_api_key(self, seed=""): - self.signer.GenerateAPIKey.argtypes = [ - ctypes.c_char_p, - ] - self.signer.GenerateAPIKey.restype = ApiKeyResponse - result = self.signer.GenerateAPIKey(ctypes.c_char_p(seed.encode("utf-8"))) - - private_key_str = result.str.decode("utf-8") if result.privateKey else None - public_key_str = result.str.decode("utf-8") if result.publicKey else None - error = result.err.decode("utf-8") if result.err else None - - return private_key_str, public_key_str, error - - def sign_change_api_key(self, eth_private_key, new_pubkey: str, nonce: int): - self.signer.SignChangePubKey.argtypes = [ - ctypes.c_char_p, - ctypes.c_longlong, - ] - self.signer.SignChangePubKey.restype = StrOrErr - result = self.signer.SignChangePubKey(ctypes.c_char_p(new_pubkey.encode("utf-8")), nonce) - - tx_info_str = result.str.decode("utf-8") if result.str else None - error = result.err.decode("utf-8") if result.err else None - if error is not None: - return None, error - - # fetch message to sign - tx_info = json.loads(tx_info_str) - msg_to_sign = tx_info["MessageToSign"] - del tx_info["MessageToSign"] - - # sign the message - acct = Account.from_key(eth_private_key) - message = encode_defunct(text=msg_to_sign) - signature = acct.sign_message(message) - tx_info["L1Sig"] = signature.signature.to_0x_hex() - return json.dumps(tx_info), None + 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: @@ -324,36 +388,49 @@ def get_api_key_nonce(self, api_key_index: int, nonce: int) -> Tuple[int, int]: 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): + 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) + + 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( + ctypes.c_char_p(new_pubkey.encode("utf-8")), + nonce + )) + + 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) + if error is not None: + return None, error + + logging.debug(f"Change Pub Key Tx Info: {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}") + return api_response, None + def sign_create_order( - self, - market_index, - client_order_index, - base_amount, - price, - is_ask, - order_type, - time_in_force, - reduce_only, - trigger_price, - order_expiry=DEFAULT_28_DAY_ORDER_EXPIRY, - nonce=-1, + self, + market_index, + client_order_index, + base_amount, + price, + is_ask, + order_type, + time_in_force, + reduce_only=False, + trigger_price=NIL_TRIGGER_PRICE, + order_expiry=DEFAULT_28_DAY_ORDER_EXPIRY, + nonce=-1, ): - self.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, - ] - self.signer.SignCreateOrder.restype = StrOrErr - - result = self.signer.SignCreateOrder( + return self.__decode_tx_info(self.TX_TYPE_CREATE_ORDER, self.signer.SignCreateOrder( market_index, client_order_index, base_amount, @@ -365,258 +442,78 @@ def sign_create_order( trigger_price, order_expiry, nonce, - ) - - tx_info = result.str.decode("utf-8") if result.str else None - error = result.err.decode("utf-8") if result.err else None - - return tx_info, error + )) def sign_create_grouped_orders( - self, - grouping_type: int, - orders: List[CreateOrderTxReq], - nonce=-1, + self, + grouping_type: int, + orders: List[CreateOrderTxReq], + nonce: int = -1, ): arr_type = CreateOrderTxReq * len(orders) orders_arr = arr_type(*orders) - self.signer.SignCreateGroupedOrders.argtypes = [ - ctypes.c_uint8, - ctypes.POINTER(CreateOrderTxReq), - ctypes.c_int, - ctypes.c_longlong, - ] - self.signer.SignCreateGroupedOrders.restype = StrOrErr - - result = self.signer.SignCreateGroupedOrders( + return self.__decode_tx_info(self.TX_TYPE_CREATE_GROUP_ORDER, self.signer.SignCreateGroupedOrders( grouping_type, orders_arr, len(orders), nonce - ) - - tx_info = result.str.decode("utf-8") if result.str else None - error = result.err.decode("utf-8") if result.err else None - return tx_info, error - - def sign_cancel_order(self, market_index, order_index, nonce=-1): - self.signer.SignCancelOrder.argtypes = [ - ctypes.c_int, - ctypes.c_longlong, - ctypes.c_longlong, - ] - self.signer.SignCancelOrder.restype = StrOrErr - - result = self.signer.SignCancelOrder(market_index, order_index, nonce) - - tx_info = result.str.decode("utf-8") if result.str else None - error = result.err.decode("utf-8") if result.err else None - - return tx_info, error + )) - def sign_withdraw(self, usdc_amount, nonce=-1): - self.signer.SignWithdraw.argtypes = [ctypes.c_longlong, ctypes.c_longlong] - self.signer.SignWithdraw.restype = StrOrErr + 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)) - result = self.signer.SignWithdraw(usdc_amount, nonce) - - tx_info = result.str.decode("utf-8") if result.str else None - error = result.err.decode("utf-8") if result.err else None - - return tx_info, error + 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_create_sub_account(self, nonce=-1): - self.signer.SignCreateSubAccount.argtypes = [ctypes.c_longlong] - self.signer.SignCreateSubAccount.restype = StrOrErr - - result = self.signer.SignCreateSubAccount(nonce) - - tx_info = result.str.decode("utf-8") if result.str else None - error = result.err.decode("utf-8") if result.err else None - - return tx_info, error - - def sign_cancel_all_orders(self, time_in_force, time, nonce=-1): - self.signer.SignCancelAllOrders.argtypes = [ - ctypes.c_int, - ctypes.c_longlong, - ctypes.c_longlong, - ] - self.signer.SignCancelAllOrders.restype = StrOrErr - - result = self.signer.SignCancelAllOrders(time_in_force, time, nonce) - - tx_info = result.str.decode("utf-8") if result.str else None - error = result.err.decode("utf-8") if result.err else None - - return tx_info, error - - def sign_modify_order(self, market_index, order_index, base_amount, price, trigger_price, nonce=-1): - self.signer.SignModifyOrder.argtypes = [ - ctypes.c_int, - ctypes.c_longlong, - ctypes.c_longlong, - ctypes.c_longlong, - ctypes.c_longlong, - ctypes.c_longlong, - ] - self.signer.SignModifyOrder.restype = StrOrErr - - result = self.signer.SignModifyOrder(market_index, order_index, base_amount, price, trigger_price, nonce) - - tx_info = result.str.decode("utf-8") if result.str else None - error = result.err.decode("utf-8") if result.err else None - - return tx_info, error - - def sign_transfer(self, eth_private_key, to_account_index, usdc_amount, fee, memo, nonce=-1): - self.signer.SignTransfer.argtypes = [ - ctypes.c_longlong, - ctypes.c_longlong, - ctypes.c_longlong, - ctypes.c_char_p, - ctypes.c_longlong, - ] - self.signer.SignTransfer.restype = StrOrErr - result = self.signer.SignTransfer(to_account_index, usdc_amount, fee, ctypes.c_char_p(memo.encode("utf-8")), nonce) - - 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 self.__decode_tx_info(self.TX_TYPE_CREATE_SUB_ACCOUNT, self.signer.SignCreateSubAccount(nonce)) - if error: - return tx_info_str, error - - # fetch message to sign - tx_info = json.loads(tx_info_str) - msg_to_sign = tx_info["MessageToSign"] - del tx_info["MessageToSign"] + 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)) - # sign the message - acct = Account.from_key(eth_private_key) - message = encode_defunct(text=msg_to_sign) - signature = acct.sign_message(message) - tx_info["L1Sig"] = signature.signature.to_0x_hex() - return json.dumps(tx_info), None + 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_create_public_pool(self, operator_fee, initial_total_shares, min_operator_share_rate, nonce=-1): - self.signer.SignCreatePublicPool.argtypes = [ - ctypes.c_longlong, - ctypes.c_longlong, - ctypes.c_longlong, - ctypes.c_longlong, - ] - self.signer.SignCreatePublicPool.restype = StrOrErr + 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)) - result = self.signer.SignCreatePublicPool(operator_fee, initial_total_shares, min_operator_share_rate, nonce) - - tx_info = result.str.decode("utf-8") if result.str else None - error = result.err.decode("utf-8") if result.err else None + 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)) - return tx_info, error + 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, status, operator_fee, min_operator_share_rate, nonce=-1): - self.signer.SignUpdatePublicPool.argtypes = [ - ctypes.c_longlong, - ctypes.c_int, - ctypes.c_longlong, - ctypes.c_longlong, - ctypes.c_longlong, - ] - self.signer.SignUpdatePublicPool.restype = StrOrErr - - result = self.signer.SignUpdatePublicPool( - public_pool_index, status, operator_fee, min_operator_share_rate, nonce - ) + 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)) - tx_info = result.str.decode("utf-8") if result.str else None - error = result.err.decode("utf-8") if result.err else None - - return tx_info, error - - def sign_mint_shares(self, public_pool_index, share_amount, nonce=-1): - self.signer.SignMintShares.argtypes = [ - ctypes.c_longlong, - ctypes.c_longlong, - ctypes.c_longlong, - ] - self.signer.SignMintShares.restype = StrOrErr - - result = self.signer.SignMintShares(public_pool_index, share_amount, nonce) - - tx_info = result.str.decode("utf-8") if result.str else None - error = result.err.decode("utf-8") if result.err else None - - return tx_info, error - - def sign_burn_shares(self, public_pool_index, share_amount, nonce=-1): - self.signer.SignBurnShares.argtypes = [ - ctypes.c_longlong, - ctypes.c_longlong, - ctypes.c_longlong, - ] - self.signer.SignBurnShares.restype = StrOrErr - - result = self.signer.SignBurnShares(public_pool_index, share_amount, nonce) - - tx_info = result.str.decode("utf-8") if result.str else None - error = result.err.decode("utf-8") if result.err else None - - return tx_info, error - - def sign_update_leverage(self, market_index, fraction, margin_mode, nonce=-1): - self.signer.SignUpdateLeverage.argtypes = [ - ctypes.c_int, - ctypes.c_int, - ctypes.c_int, - ctypes.c_longlong, - ] - self.signer.SignUpdateLeverage.restype = StrOrErr - result = self.signer.SignUpdateLeverage(market_index, fraction, margin_mode, nonce) - - tx_info = result.str.decode("utf-8") if result.str else None - error = result.err.decode("utf-8") if result.err else None - return tx_info, error - - def create_auth_token_with_expiry(self, deadline: int = DEFAULT_10_MIN_AUTH_EXPIRY, *, timestamp: int = None): - if deadline == SignerClient.DEFAULT_10_MIN_AUTH_EXPIRY: - deadline = 10 * SignerClient.MINUTE - if timestamp is None: - timestamp = int(time.time()) - - self.signer.CreateAuthToken.argtypes = [ctypes.c_longlong] - self.signer.CreateAuthToken.restype = StrOrErr - result = self.signer.CreateAuthToken(timestamp + deadline) - - 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 - - async def change_api_key(self, eth_private_key: str, new_pubkey: str, nonce=-1): - tx_info, error = self.sign_change_api_key(eth_private_key, new_pubkey, nonce) - if error is not None: - return None, error + 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)) - logging.debug(f"Change Pub Key Tx Info: {tx_info}") + 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)) - api_response = await self.send_tx(tx_type=self.TX_TYPE_CHANGE_PUB_KEY, tx_info=tx_info) - logging.debug(f"Change Pub Key Send Tx Response: {api_response}") - return api_response, None + 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)) @process_api_key_and_nonce async def create_order( - self, - market_index, - client_order_index, - base_amount, - price, - is_ask, - order_type, - time_in_force, - reduce_only=False, - trigger_price=NIL_TRIGGER_PRICE, - order_expiry=DEFAULT_28_DAY_ORDER_EXPIRY, - nonce=-1, - api_key_index=-1, + self, + market_index, + client_order_index, + base_amount, + price, + is_ask, + order_type, + time_in_force, + 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_info, error = self.sign_create_order( + tx_type, tx_info, error = self.sign_create_order( market_index, client_order_index, base_amount, @@ -631,43 +528,43 @@ async def create_order( ) if error is not None: return None, None, error - logging.debug(f"Create Order Tx Info: {tx_info}") - api_response = await self.send_tx(tx_type=self.TX_TYPE_CREATE_ORDER, tx_info=tx_info) + logging.debug(f"Create Order Tx Info: {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}") return CreateOrder.from_json(tx_info), api_response, None @process_api_key_and_nonce async def create_grouped_orders( - self, - grouping_type: int, - orders: List[CreateOrderTxReq], - nonce=-1, - api_key_index=-1, + self, + grouping_type: int, + orders: List[CreateOrderTxReq], + nonce=-1, + api_key_index=-1, ) -> (CreateGroupedOrders, TxHash, str): - tx_info, error = self.sign_create_grouped_orders( + tx_type, tx_info, error = self.sign_create_grouped_orders( grouping_type, orders, nonce, ) if error is not None: return None, None, error - logging.debug(f"Create Grouped Orders Tx Info: {tx_info}") - api_response = await self.send_tx(tx_type=self.TX_TYPE_CREATE_GROUP_ORDER, tx_info=tx_info) + logging.debug(f"Create Grouped Orders Tx Info: {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}") return CreateGroupedOrders.from_json(tx_info), api_response, None async def create_market_order( - self, - market_index, - client_order_index, - base_amount, - avg_execution_price, - is_ask, - reduce_only: bool = False, - nonce=-1, - api_key_index=-1, + self, + market_index, + client_order_index, + base_amount, + avg_execution_price, + is_ask, + reduce_only: bool = False, + nonce=-1, + api_key_index=-1, ) -> (CreateOrder, TxHash, str): return await self.create_order( market_index, @@ -685,20 +582,21 @@ async def create_market_order( # will only do the amount such that the slippage is limited to the value provided async def create_market_order_limited_slippage( - self, - market_index, - client_order_index, - base_amount, - max_slippage, - is_ask, - reduce_only: bool = False, - nonce=-1, - api_key_index=-1, - ideal_price=None + self, + market_index, + client_order_index, + base_amount, + max_slippage, + is_ask, + reduce_only: bool = False, + nonce=-1, + api_key_index=-1, + ideal_price=None ) -> (CreateOrder, TxHash, str): if ideal_price is None: order_book_orders = await self.order_api.order_book_orders(market_index, 1) - logging.debug("Create market order limited slippage is doing an API call to get the current ideal price. You can also provide it yourself to avoid this.") + logging.debug( + "Create market order limited slippage is doing an API call to get the current ideal price. You can also provide it yourself to avoid this.") ideal_price = int((order_book_orders.bids[0].price if is_ask else order_book_orders.asks[0].price).replace(".", "")) acceptable_execution_price = round(ideal_price * (1 + max_slippage * (-1 if is_ask else 1))) @@ -718,16 +616,16 @@ async def create_market_order_limited_slippage( # will only execute the order if it executes with slippage <= max_slippage async def create_market_order_if_slippage( - self, - market_index, - client_order_index, - base_amount, - max_slippage, - is_ask, - reduce_only: bool = False, - nonce=-1, - api_key_index=-1, - ideal_price=None + self, + market_index, + client_order_index, + base_amount, + max_slippage, + is_ask, + reduce_only: bool = False, + nonce=-1, + api_key_index=-1, + ideal_price=None ) -> (CreateOrder, TxHash, str): order_book_orders = await self.order_api.order_book_orders(market_index, 100) if ideal_price is None: @@ -767,16 +665,18 @@ 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_info, error = self.sign_cancel_order(market_index, order_index, nonce) + tx_type, tx_info, error = self.sign_cancel_order(market_index, order_index, nonce) + if error is not None: return None, None, error - logging.debug(f"Cancel Order Tx Info: {tx_info}") - api_response = await self.send_tx(tx_type=self.TX_TYPE_CANCEL_ORDER, tx_info=tx_info) + logging.debug(f"Cancel Order Tx Info: {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}") 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=-1, + api_key_index=-1) -> (CreateOrder, TxHash, str): return await self.create_order( market_index, client_order_index, @@ -792,7 +692,8 @@ async def create_tp_order(self, market_index, client_order_index, base_amount, t 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=-1, + api_key_index=-1) -> (CreateOrder, TxHash, str): return await self.create_order( market_index, client_order_index, @@ -808,7 +709,8 @@ 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=-1, + api_key_index=-1) -> (CreateOrder, TxHash, str): return await self.create_order( market_index, client_order_index, @@ -824,7 +726,8 @@ async def create_sl_order(self, market_index, client_order_index, base_amount, t 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=-1, + api_key_index=-1) -> (CreateOrder, TxHash, str): return await self.create_order( market_index, client_order_index, @@ -844,46 +747,46 @@ async def create_sl_limit_order(self, market_index, client_order_index, base_amo async def withdraw(self, usdc_amount, nonce=-1, api_key_index=-1) -> (Withdraw, TxHash): usdc_amount = int(usdc_amount * self.USDC_TICKER_SCALE) - tx_info, error = self.sign_withdraw(usdc_amount, nonce) + tx_type, tx_info, error = self.sign_withdraw(usdc_amount, nonce) if error is not None: return None, None, error - logging.debug(f"Withdraw Tx Info: {tx_info}") - api_response = await self.send_tx(tx_type=self.TX_TYPE_WITHDRAW, tx_info=tx_info) + logging.debug(f"Withdraw Tx Info: {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}") return Withdraw.from_json(tx_info), api_response, None async def create_sub_account(self, nonce=-1): - tx_info, error = self.sign_create_sub_account(nonce) + tx_type, tx_info, error = self.sign_create_sub_account(nonce) if error is not None: return None, None, error - logging.debug(f"Create Sub Account Tx Info: {tx_info}") - api_response = await self.send_tx(tx_type=self.TX_TYPE_CREATE_SUB_ACCOUNT, tx_info=tx_info) + logging.debug(f"Create Sub Account Tx Info: {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}") return tx_info, api_response, None @process_api_key_and_nonce - async def cancel_all_orders(self, time_in_force, time, nonce=-1, api_key_index=-1): - tx_info, error = self.sign_cancel_all_orders(time_in_force, time, 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) if error is not None: return None, None, error - logging.debug(f"Cancel All Orders Tx Info: {tx_info}") - api_response = await self.send_tx(tx_type=self.TX_TYPE_CANCEL_ALL_ORDERS, tx_info=tx_info) + logging.debug(f"Cancel All Orders Tx Info: {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}") 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, nonce=-1, api_key_index=-1 + self, market_index, order_index, base_amount, price, trigger_price=NIL_TRIGGER_PRICE, nonce=-1, api_key_index=-1 ): - tx_info, error = self.sign_modify_order(market_index, order_index, base_amount, price, trigger_price, nonce) + tx_type, tx_info, error = self.sign_modify_order(market_index, order_index, base_amount, price, trigger_price, nonce) if error is not None: return None, None, error - logging.debug(f"Modify Order Tx Info: {tx_info}") - api_response = await self.send_tx(tx_type=self.TX_TYPE_MODIFY_ORDER, tx_info=tx_info) + logging.debug(f"Modify Order Tx Info: {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}") return tx_info, api_response, None @@ -891,80 +794,92 @@ async def modify_order( async def transfer(self, eth_private_key: str, to_account_index, usdc_amount, fee, memo, nonce=-1, api_key_index=-1): usdc_amount = int(usdc_amount * self.USDC_TICKER_SCALE) - tx_info, error = self.sign_transfer(eth_private_key, to_account_index, usdc_amount, fee, memo, nonce) + tx_type, tx_info, error = self.sign_transfer(eth_private_key, to_account_index, usdc_amount, fee, memo, nonce) if error is not None: return None, None, error - logging.debug(f"Transfer Tx Info: {tx_info}") - api_response = await self.send_tx(tx_type=self.TX_TYPE_TRANSFER, tx_info=tx_info) + logging.debug(f"Transfer Tx Info: {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}") 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=-1, api_key_index=-1 ): - tx_info, error = self.sign_create_public_pool( + tx_type, tx_info, error = self.sign_create_public_pool( operator_fee, initial_total_shares, min_operator_share_rate, nonce ) if error is not None: return None, None, error - logging.debug(f"Create Public Pool Tx Info: {tx_info}") - api_response = await self.send_tx(tx_type=self.TX_TYPE_CREATE_PUBLIC_POOL, tx_info=tx_info) + logging.debug(f"Create Public Pool Tx Info: {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}") 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=-1, api_key_index=-1 ): - tx_info, error = self.sign_update_public_pool( + tx_type, tx_info, error = self.sign_update_public_pool( public_pool_index, status, operator_fee, min_operator_share_rate, nonce ) if error is not None: return None, None, error - logging.debug(f"Update Public Pool Tx Info: {tx_info}") - api_response = await self.send_tx(tx_type=self.TX_TYPE_UPDATE_PUBLIC_POOL, tx_info=tx_info) + logging.debug(f"Update Public Pool Tx Info: {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}") 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_info, error = self.sign_mint_shares(public_pool_index, share_amount, nonce) + tx_type, tx_info, error = self.sign_mint_shares(public_pool_index, share_amount, nonce) if error is not None: return None, None, error - logging.debug(f"Mint Shares Tx Info: {tx_info}") - api_response = await self.send_tx(tx_type=self.TX_TYPE_MINT_SHARES, tx_info=tx_info) + logging.debug(f"Mint Shares Tx Info: {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}") 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_info, error = self.sign_burn_shares(public_pool_index, share_amount, nonce) + tx_type, tx_info, error = self.sign_burn_shares(public_pool_index, share_amount, nonce) if error is not None: return None, None, error - logging.debug(f"Burn Shares Tx Info: {tx_info}") - api_response = await self.send_tx(tx_type=self.TX_TYPE_BURN_SHARES, tx_info=tx_info) + logging.debug(f"Burn Shares Tx Info: {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}") 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): imf = int(10_000 / leverage) - tx_info, error = self.sign_update_leverage(market_index, imf, margin_mode, nonce) + tx_type, tx_info, error = self.sign_update_leverage(market_index, imf, margin_mode, nonce) if error is not None: return None, None, error - logging.debug(f"Update Leverage Tx Info: {tx_info}") - api_response = await self.send_tx(tx_type=self.TX_TYPE_UPDATE_LEVERAGE, tx_info=tx_info) + logging.debug(f"Update Leverage Tx Info: {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): + usdc_amount = int(usdc_amount * self.USDC_TICKER_SCALE) + tx_type, tx_info, error = self.sign_update_margin(market_index, usdc_amount, direction, nonce) + + if error is not None: + return None, None, error + + logging.debug(f"Update Margin Tx Info: {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 async def send_tx(self, tx_type: StrictInt, tx_info: str) -> RespSendTx: if tx_info[0] != "{": diff --git a/lighter/signers/README.md b/lighter/signers/README.md index f5295bb..3408e2a 100644 --- a/lighter/signers/README.md +++ b/lighter/signers/README.md @@ -2,56 +2,24 @@ This directory contains various signer implementations for the Lighter Protocol. -## Available Signers - -### Native Binary Signers - -- `signer-amd64.so` - Linux AMD64 native signer -- `signer-arm64.dylib` - macOS ARM64 native signer -- `signer-amd64.dll` - Windows AMD64 native signer - -These binaries are compiled from the Go implementation in [lighter-go](https://github.com/elliottech/lighter-go) and -provide high-performance cryptographic operations for the Lighter Protocol. - ## Usage The Python SDK automatically selects the correct native binary signer based on your platform: -- ✅ **Linux (x86_64)**: Uses `signer-amd64.so` -- ✅ **macOS (ARM64)**: Uses `signer-arm64.dylib` -- ✅ **Windows (x86_64)**: Uses `signer-amd64.dll` - -No additional configuration is required - the SDK detects your platform and loads the appropriate signer. - -## Building Signers -All native signers are built from the Go implementation in [lighter-go](https://github.com/elliottech/lighter-go). +| Platform | Architecture | Binary | +|----------|-----------------------|-------------------------------------| +| Linux | x86_64 | `lighter-signer-linux-amd64.so` | +| Linux | ARM64 | `lighter-signer-linux-amd64.so` | +| macOS | ARM64 (Apple Silicon) | `lighter-signer-darwin-arm64.dylib` | +| Windows | x86_64 | `lighter-signer-windows-amd64.dll` | -To build signers locally,Import lighter-go repo: +No additional configuration is required - the SDK detects your platform and loads the appropriate signer. \ +If you encounter issues with missing binaries, ensure the appropriate signer binary is present in this directory. -```bash -# From lighter-go/ directory -just build-linux-local # Linux AMD64 -just build-darwin-local # macOS ARM64 -just build-windows-local # Windows AMD64 -``` - -Or use Docker for cross-compilation: - -```bash -just build-linux-docker -just build-windows-docker -``` - -## Supported Platforms - -The Python SDK supports the following platforms out of the box: - -| Platform | Architecture | Binary | -|----------|-----------------------|----------------------| -| Linux | x86_64 | `signer-amd64.so` | -| macOS | ARM64 (Apple Silicon) | `signer-arm64.dylib` | -| Windows | x86_64 | `signer-amd64.dll` | +## Building Signers -If you encounter issues with missing binaries, ensure the appropriate signer binary is present in this directory. You -can build it from `lighter-go/` using the commands above. \ No newline at end of file +These binaries are compiled from the Go implementation in [lighter-go](https://github.com/elliottech/lighter-go) and +provide high-performance cryptographic operations for the Lighter Protocol. \ +There are `.h` files for easier integrations in other languages, like C, C++, Rust. \ +For building the signers yourself, you can find the steps in the lighter-go repo. diff --git a/lighter/signers/lighter-signer-darwin-arm64.dylib b/lighter/signers/lighter-signer-darwin-arm64.dylib new file mode 100644 index 0000000..ec0ecef Binary files /dev/null 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 new file mode 100644 index 0000000..9e29d07 --- /dev/null +++ b/lighter/signers/lighter-signer-darwin-arm64.h @@ -0,0 +1,129 @@ +/* Code generated by cmd/cgo; DO NOT EDIT. */ + +/* package command-line-arguments */ + + +#line 1 "cgo-builtin-export-prolog" + +#include + +#ifndef GO_CGO_EXPORT_PROLOGUE_H +#define GO_CGO_EXPORT_PROLOGUE_H + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef struct { const char *p; ptrdiff_t n; } _GoString_; +#endif + +#endif + +/* Start of preamble from import "C" comments. */ + + +#line 18 "sharedlib.go" + +#include +#include +typedef struct { + char* str; + char* err; +} StrOrErr; + +typedef struct { + char* privateKey; + char* publicKey; + char* err; +} ApiKeyResponse; + +typedef struct { + uint8_t MarketIndex; + int64_t ClientOrderIndex; + int64_t BaseAmount; + uint32_t Price; + uint8_t IsAsk; + uint8_t Type; + uint8_t TimeInForce; + uint8_t ReduceOnly; + uint32_t TriggerPrice; + int64_t OrderExpiry; +} CreateOrderTxReq; + +#line 1 "cgo-generated-wrapper" + + +/* End of preamble from import "C" comments. */ + + +/* Start of boilerplate cgo prologue. */ +#line 1 "cgo-gcc-export-header-prolog" + +#ifndef GO_CGO_PROLOGUE_H +#define GO_CGO_PROLOGUE_H + +typedef signed char GoInt8; +typedef unsigned char GoUint8; +typedef short GoInt16; +typedef unsigned short GoUint16; +typedef int GoInt32; +typedef unsigned int GoUint32; +typedef long long GoInt64; +typedef unsigned long long GoUint64; +typedef GoInt64 GoInt; +typedef GoUint64 GoUint; +typedef size_t GoUintptr; +typedef float GoFloat32; +typedef double GoFloat64; +#ifdef _MSC_VER +#include +typedef _Fcomplex GoComplex64; +typedef _Dcomplex GoComplex128; +#else +typedef float _Complex GoComplex64; +typedef double _Complex GoComplex128; +#endif + +/* + static assertion to make sure the file is being used on architecture + at least with matching size of GoInt. +*/ +typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef _GoString_ GoString; +#endif +typedef void *GoMap; +typedef void *GoChan; +typedef struct { void *t; void *v; } GoInterface; +typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; + +#endif + +/* End of boilerplate cgo prologue. */ + +#ifdef __cplusplus +extern "C" { +#endif + +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); + +#ifdef __cplusplus +} +#endif diff --git a/lighter/signers/lighter-signer-linux-amd64.h b/lighter/signers/lighter-signer-linux-amd64.h new file mode 100644 index 0000000..5e0df7d --- /dev/null +++ b/lighter/signers/lighter-signer-linux-amd64.h @@ -0,0 +1,129 @@ +/* Code generated by cmd/cgo; DO NOT EDIT. */ + +/* package command-line-arguments */ + + +#line 1 "cgo-builtin-export-prolog" + +#include + +#ifndef GO_CGO_EXPORT_PROLOGUE_H +#define GO_CGO_EXPORT_PROLOGUE_H + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef struct { const char *p; ptrdiff_t n; } _GoString_; +#endif + +#endif + +/* Start of preamble from import "C" comments. */ + + +#line 18 "sharedlib.go" + +#include +#include +typedef struct { + char* str; + char* err; +} StrOrErr; + +typedef struct { + char* privateKey; + char* publicKey; + char* err; +} ApiKeyResponse; + +typedef struct { + uint8_t MarketIndex; + int64_t ClientOrderIndex; + int64_t BaseAmount; + uint32_t Price; + uint8_t IsAsk; + uint8_t Type; + uint8_t TimeInForce; + uint8_t ReduceOnly; + uint32_t TriggerPrice; + int64_t OrderExpiry; +} CreateOrderTxReq; + +#line 1 "cgo-generated-wrapper" + + +/* End of preamble from import "C" comments. */ + + +/* Start of boilerplate cgo prologue. */ +#line 1 "cgo-gcc-export-header-prolog" + +#ifndef GO_CGO_PROLOGUE_H +#define GO_CGO_PROLOGUE_H + +typedef signed char GoInt8; +typedef unsigned char GoUint8; +typedef short GoInt16; +typedef unsigned short GoUint16; +typedef int GoInt32; +typedef unsigned int GoUint32; +typedef long long GoInt64; +typedef unsigned long long GoUint64; +typedef GoInt64 GoInt; +typedef GoUint64 GoUint; +typedef size_t GoUintptr; +typedef float GoFloat32; +typedef double GoFloat64; +#ifdef _MSC_VER +#include +typedef _Fcomplex GoComplex64; +typedef _Dcomplex GoComplex128; +#else +typedef float _Complex GoComplex64; +typedef double _Complex GoComplex128; +#endif + +/* + static assertion to make sure the file is being used on architecture + at least with matching size of GoInt. +*/ +typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef _GoString_ GoString; +#endif +typedef void *GoMap; +typedef void *GoChan; +typedef struct { void *t; void *v; } GoInterface; +typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; + +#endif + +/* End of boilerplate cgo prologue. */ + +#ifdef __cplusplus +extern "C" { +#endif + +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); + +#ifdef __cplusplus +} +#endif diff --git a/lighter/signers/signer-amd64.so b/lighter/signers/lighter-signer-linux-amd64.so similarity index 54% rename from lighter/signers/signer-amd64.so rename to lighter/signers/lighter-signer-linux-amd64.so index 7e3a900..bfe40ed 100644 Binary files a/lighter/signers/signer-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 new file mode 100644 index 0000000..5e0df7d --- /dev/null +++ b/lighter/signers/lighter-signer-linux-arm64.h @@ -0,0 +1,129 @@ +/* Code generated by cmd/cgo; DO NOT EDIT. */ + +/* package command-line-arguments */ + + +#line 1 "cgo-builtin-export-prolog" + +#include + +#ifndef GO_CGO_EXPORT_PROLOGUE_H +#define GO_CGO_EXPORT_PROLOGUE_H + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef struct { const char *p; ptrdiff_t n; } _GoString_; +#endif + +#endif + +/* Start of preamble from import "C" comments. */ + + +#line 18 "sharedlib.go" + +#include +#include +typedef struct { + char* str; + char* err; +} StrOrErr; + +typedef struct { + char* privateKey; + char* publicKey; + char* err; +} ApiKeyResponse; + +typedef struct { + uint8_t MarketIndex; + int64_t ClientOrderIndex; + int64_t BaseAmount; + uint32_t Price; + uint8_t IsAsk; + uint8_t Type; + uint8_t TimeInForce; + uint8_t ReduceOnly; + uint32_t TriggerPrice; + int64_t OrderExpiry; +} CreateOrderTxReq; + +#line 1 "cgo-generated-wrapper" + + +/* End of preamble from import "C" comments. */ + + +/* Start of boilerplate cgo prologue. */ +#line 1 "cgo-gcc-export-header-prolog" + +#ifndef GO_CGO_PROLOGUE_H +#define GO_CGO_PROLOGUE_H + +typedef signed char GoInt8; +typedef unsigned char GoUint8; +typedef short GoInt16; +typedef unsigned short GoUint16; +typedef int GoInt32; +typedef unsigned int GoUint32; +typedef long long GoInt64; +typedef unsigned long long GoUint64; +typedef GoInt64 GoInt; +typedef GoUint64 GoUint; +typedef size_t GoUintptr; +typedef float GoFloat32; +typedef double GoFloat64; +#ifdef _MSC_VER +#include +typedef _Fcomplex GoComplex64; +typedef _Dcomplex GoComplex128; +#else +typedef float _Complex GoComplex64; +typedef double _Complex GoComplex128; +#endif + +/* + static assertion to make sure the file is being used on architecture + at least with matching size of GoInt. +*/ +typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef _GoString_ GoString; +#endif +typedef void *GoMap; +typedef void *GoChan; +typedef struct { void *t; void *v; } GoInterface; +typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; + +#endif + +/* End of boilerplate cgo prologue. */ + +#ifdef __cplusplus +extern "C" { +#endif + +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); + +#ifdef __cplusplus +} +#endif diff --git a/lighter/signers/lighter-signer-linux-arm64.so b/lighter/signers/lighter-signer-linux-arm64.so new file mode 100644 index 0000000..133693d Binary files /dev/null and b/lighter/signers/lighter-signer-linux-arm64.so differ diff --git a/lighter/signers/signer-amd64.dll b/lighter/signers/lighter-signer-windows-amd64.dll similarity index 62% rename from lighter/signers/signer-amd64.dll rename to lighter/signers/lighter-signer-windows-amd64.dll index fdf817a..ed59a72 100644 Binary files a/lighter/signers/signer-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 new file mode 100644 index 0000000..ecd926b --- /dev/null +++ b/lighter/signers/lighter-signer-windows-amd64.h @@ -0,0 +1,129 @@ +/* Code generated by cmd/cgo; DO NOT EDIT. */ + +/* package github.com/elliottech/lighter-go/sharedlib */ + + +#line 1 "cgo-builtin-export-prolog" + +#include + +#ifndef GO_CGO_EXPORT_PROLOGUE_H +#define GO_CGO_EXPORT_PROLOGUE_H + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef struct { const char *p; ptrdiff_t n; } _GoString_; +#endif + +#endif + +/* Start of preamble from import "C" comments. */ + + +#line 18 "sharedlib.go" + +#include +#include +typedef struct { + char* str; + char* err; +} StrOrErr; + +typedef struct { + char* privateKey; + char* publicKey; + char* err; +} ApiKeyResponse; + +typedef struct { + uint8_t MarketIndex; + int64_t ClientOrderIndex; + int64_t BaseAmount; + uint32_t Price; + uint8_t IsAsk; + uint8_t Type; + uint8_t TimeInForce; + uint8_t ReduceOnly; + uint32_t TriggerPrice; + int64_t OrderExpiry; +} CreateOrderTxReq; + +#line 1 "cgo-generated-wrapper" + + +/* End of preamble from import "C" comments. */ + + +/* Start of boilerplate cgo prologue. */ +#line 1 "cgo-gcc-export-header-prolog" + +#ifndef GO_CGO_PROLOGUE_H +#define GO_CGO_PROLOGUE_H + +typedef signed char GoInt8; +typedef unsigned char GoUint8; +typedef short GoInt16; +typedef unsigned short GoUint16; +typedef int GoInt32; +typedef unsigned int GoUint32; +typedef long long GoInt64; +typedef unsigned long long GoUint64; +typedef GoInt64 GoInt; +typedef GoUint64 GoUint; +typedef size_t GoUintptr; +typedef float GoFloat32; +typedef double GoFloat64; +#ifdef _MSC_VER +#include +typedef _Fcomplex GoComplex64; +typedef _Dcomplex GoComplex128; +#else +typedef float _Complex GoComplex64; +typedef double _Complex GoComplex128; +#endif + +/* + static assertion to make sure the file is being used on architecture + at least with matching size of GoInt. +*/ +typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef _GoString_ GoString; +#endif +typedef void *GoMap; +typedef void *GoChan; +typedef struct { void *t; void *v; } GoInterface; +typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; + +#endif + +/* End of boilerplate cgo prologue. */ + +#ifdef __cplusplus +extern "C" { +#endif + +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); + +#ifdef __cplusplus +} +#endif diff --git a/lighter/signers/signer-arm64.dylib b/lighter/signers/signer-arm64.dylib deleted file mode 100644 index 1080f8e..0000000 Binary files a/lighter/signers/signer-arm64.dylib and /dev/null differ