Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions examples/system_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
import eth_account
import lighter
from utils import save_api_key_config
from lighter import get_endpoint_profile, MAINNET, ROBINHOOD, TESTNET

logging.basicConfig(level=logging.DEBUG)

# this is a dummy private key registered on Testnet.
# It serves as a good example
ENDPOINT_PROFILE = TESTNET
BASE_URL = "https://testnet.zklighter.elliot.ai"
ETH_PRIVATE_KEY = "1234567812345678123456781234567812345678123456781234567812345678"
API_KEY_INDEX = 3
Expand All @@ -21,7 +23,9 @@

async def main():
# verify that the account exists & fetch account index
api_client = lighter.ApiClient(configuration=lighter.Configuration(host=BASE_URL))
api_client = lighter.ApiClient(
configuration=lighter.Configuration(host=ENDPOINT_PROFILE.api_url)
)
eth_acc = eth_account.Account.from_key(ETH_PRIVATE_KEY)
eth_address = eth_acc.address

Expand Down Expand Up @@ -63,17 +67,18 @@ async def main():
private_keys[API_KEY_INDEX + i] = private_key

tx_client = lighter.SignerClient(
url=BASE_URL,
url=ENDPOINT_PROFILE.api_url,
account_index=account_index,
api_private_keys=private_keys,
chain_id=ENDPOINT_PROFILE.chain_id,
)

# 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
api_key_index=API_KEY_INDEX + i,
)
if err is not None:
raise Exception(err)
Expand All @@ -86,7 +91,7 @@ async def main():
if err is not None:
raise Exception(err)

save_api_key_config(BASE_URL, account_index, private_keys)
save_api_key_config(ENDPOINT_PROFILE, account_index, private_keys)

await tx_client.close()
await api_client.close()
Expand Down
43 changes: 33 additions & 10 deletions examples/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ def trim_exception(e: Exception) -> str:
return str(e).strip().split("\n")[-1]


def save_api_key_config(base_url, account_index, private_keys, config_file="./api_key_config.json"):
def save_api_key_config(endpoint_profile, 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,
"endpointProfile": endpoint_profile.name,
"accountIndex": account_index,
"privateKeys": private_keys,
}, f, ensure_ascii=False, indent=2)
Expand All @@ -27,28 +27,51 @@ def get_api_key_config(config_file="./api_key_config.json"):
for key in private_keys_original.keys():
private_key[int(key)] = private_keys_original[key]

return cfg["baseUrl"], cfg["accountIndex"], private_key
profile_name = cfg.get("endpointProfile")
if profile_name:
profile = lighter.get_endpoint_profile(profile_name)
else:
# Backward compat: build a profile from the raw baseUrl
base_url = cfg["baseUrl"]
profile = lighter.EndpointProfile(
name="custom",
api_url=base_url,
ws_url=base_url.replace("https", "wss") + "/stream",
chain_id=304 if ("mainnet" in base_url or "api" in base_url) else 300,
)

return profile, cfg["accountIndex"], private_key


def default_example_setup(config_file="./api_key_config.json", nonce_management_type=lighter.nonce_manager.NonceManagerType.OPTIMISTIC) -> Optional[Tuple[lighter.SignerClient, lighter.ApiClient, websockets.connect]]:
def default_example_setup(config_file="./api_key_config.json",nonce_management_type=lighter.nonce_manager.NonceManagerType.OPTIMISTIC,endpoint_profile=None,) -> Optional[Tuple[lighter.SignerClient, lighter.ApiClient, websockets.connect]]:
logging.basicConfig(level=logging.DEBUG)

base_url, account_index, private_keys = get_api_key_config(config_file)
api_client = lighter.ApiClient(configuration=lighter.Configuration(host=base_url))
config_endpoint, account_index, private_keys = get_api_key_config(config_file)

profile = endpoint_profile or config_endpoint

api_url = profile.api_url
chain_id = profile.chain_id
ws_url = profile.ws_url

api_client = lighter.ApiClient(
configuration=lighter.Configuration(host=api_url)
)

client = lighter.SignerClient(
url=base_url,
url=api_url,
account_index=account_index,
api_private_keys=private_keys,
nonce_management_type=nonce_management_type,
chain_id=chain_id,
)

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")
return None

return client, api_client, websockets.connect(ws_url)

async def ws_ping(ws_client: websockets.ClientConnection):
await ws_client.send(json.dumps({"type": "pong"}))
Expand Down
1 change: 1 addition & 0 deletions lighter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
from lighter.models.withdraw_history import WithdrawHistory
from lighter.models.withdraw_history_item import WithdrawHistoryItem
from lighter.models.zk_lighter_info import ZkLighterInfo
from lighter.endpoint_profiles import (EndpointProfile, MAINNET, ROBINHOOD, TESTNET, DEFAULT_ENDPOINT_PROFILE, ENDPOINT_PROFILES, get_endpoint_profile)

# manual additions
from lighter.ws_client import WsClient
Expand Down
61 changes: 61 additions & 0 deletions lighter/endpoint_profiles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# lighter/endpoint_profiles.py

from dataclasses import dataclass


def normalize_base_url(url: str) -> str:
return url.rstrip("/")


def join_url(base_url: str, path: str) -> str:
return f"{base_url.rstrip('/')}/{path.lstrip('/')}"


@dataclass(frozen=True)
class EndpointProfile:
name: str
api_url: str
ws_url: str
chain_id: int

def __post_init__(self):
object.__setattr__(self, "api_url", normalize_base_url(self.api_url))
object.__setattr__(self, "ws_url", normalize_base_url(self.ws_url))


MAINNET = EndpointProfile(
name="mainnet",
api_url="https://mainnet.zklighter.elliot.ai",
ws_url="wss://mainnet.zklighter.elliot.ai/stream",
chain_id=304,
)

ROBINHOOD = EndpointProfile(
name="robinhood",
api_url="https://api.rh.lighter.xyz",
ws_url="wss://api.rh.lighter.xyz/stream",
chain_id=466324,
)


TESTNET = EndpointProfile(
name="testnet",
api_url="https://testnet.zklighter.elliot.ai",
ws_url="wss://testnet.zklighter.elliot.ai/stream",
chain_id=300,
)

DEFAULT_ENDPOINT_PROFILE = MAINNET

ENDPOINT_PROFILES = {
MAINNET.name: MAINNET,
ROBINHOOD.name: ROBINHOOD,
TESTNET.name: TESTNET,
}


def get_endpoint_profile(name: str) -> EndpointProfile:
try:
return ENDPOINT_PROFILES[name]
except KeyError as exc:
raise ValueError(f"Unknown endpoint profile: {name}") from exc
6 changes: 4 additions & 2 deletions lighter/signer_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,10 +333,12 @@ def __init__(
account_index,
api_private_keys: Dict[int, str],
nonce_management_type=nonce_manager.NonceManagerType.OPTIMISTIC,
chain_id: Optional[int] = None,
):
self.url = url
self.chain_id = 304 if ("mainnet" in url or "api" in url) else 300

self.chain_id = chain_id if chain_id is not None else (
304 if ("mainnet" in self.url or "api" in self.url) else 300
)
self.validate_api_private_keys(api_private_keys)
self.api_key_dict = api_private_keys
self.account_index = account_index
Expand Down
12 changes: 8 additions & 4 deletions lighter/ws_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from websockets.sync.client import connect
from websockets.client import connect as connect_async
from lighter.configuration import Configuration
from lighter.endpoint_profiles import join_url

class WsClient:
def __init__(
Expand All @@ -12,11 +13,14 @@ def __init__(
account_ids=[],
on_order_book_update=print,
on_account_update=print,
ws_url=None,
):
if host is None:
host = Configuration.get_default().host.replace("https://", "")

self.base_url = f"wss://{host}{path}"
if ws_url is not None:
self.base_url = ws_url.rstrip("/")
else:
if host is None:
host = Configuration.get_default().host.replace("https://", "")
self.base_url = join_url(f"wss://{host}", path)

self.subscriptions = {
"order_books": order_book_ids,
Expand Down
Loading