use std::{fmt, str::FromStr};
use crate::utils::{Address, ApiKeyIndex, AssetId};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub enum Network {
#[default]
Lighter,
Robinhood,
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("`{0}` is not a Lighter network; expected lighter or robinhood")]
pub struct UnknownNetwork(pub String);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Collateral {
pub symbol: &'static str,
pub token: Address,
pub l1_decimals: u32,
pub asset: AssetId,
pub decimals: u32,
}
pub const USDC: Collateral = Collateral {
symbol: "USDC",
token: address("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
l1_decimals: 6,
asset: AssetId::COLLATERAL,
decimals: 6,
};
pub const USDG: Collateral = Collateral {
symbol: "USDG",
token: address("0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168"),
l1_decimals: 6,
asset: AssetId::COLLATERAL,
decimals: 6,
};
pub const LIGHTER_CONTRACT: Address = address("0x3B4D794a66304F130a4Db8F2551B0070dfCf5ca7");
pub const ROBINHOOD_CONTRACT: Address = address("0x94bAB9693Ba2f6358507eFfcbd372b0660AFfF9d");
const fn address(text: &str) -> Address {
match Address::parse_const(text) {
Some(address) => address,
None => Address::ZERO,
}
}
impl Network {
pub const ALL: [Network; 2] = [Network::Lighter, Network::Robinhood];
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Network::Lighter => "lighter",
Network::Robinhood => "robinhood",
}
}
#[must_use]
pub const fn api_url(self) -> &'static str {
match self {
Network::Lighter => "https://mainnet.zklighter.elliot.ai",
Network::Robinhood => "https://api.rh.lighter.xyz",
}
}
#[must_use]
pub const fn ws_url(self) -> &'static str {
match self {
Network::Lighter => "wss://mainnet.zklighter.elliot.ai/stream",
Network::Robinhood => "wss://api.rh.lighter.xyz/stream",
}
}
#[must_use]
pub const fn ws_readonly_url(self) -> &'static str {
match self {
Network::Lighter => "wss://mainnet.zklighter.elliot.ai/stream?readonly=true",
Network::Robinhood => "wss://api.rh.lighter.xyz/stream?readonly=true",
}
}
#[must_use]
pub const fn chain_id(self) -> u32 {
match self {
Network::Lighter => 304,
Network::Robinhood => 466_324,
}
}
#[must_use]
pub const fn l1_chain_id(self) -> u64 {
match self {
Network::Lighter => 1,
Network::Robinhood => 4663,
}
}
#[must_use]
pub const fn l1_name(self) -> &'static str {
match self {
Network::Lighter => "Ethereum",
Network::Robinhood => "Robinhood Chain",
}
}
#[must_use]
pub const fn l1_rpc_url(self) -> &'static str {
match self {
Network::Lighter => "https://ethereum-rpc.publicnode.com",
Network::Robinhood => "https://rpc.mainnet.chain.robinhood.com",
}
}
#[must_use]
pub const fn l1_explorer_url(self) -> &'static str {
match self {
Network::Lighter => "https://etherscan.io",
Network::Robinhood => "https://robinhoodchain.blockscout.com",
}
}
#[must_use]
pub const fn contract(self) -> Address {
match self {
Network::Lighter => LIGHTER_CONTRACT,
Network::Robinhood => ROBINHOOD_CONTRACT,
}
}
#[must_use]
pub const fn collateral(self) -> Collateral {
match self {
Network::Lighter => USDC,
Network::Robinhood => USDG,
}
}
#[must_use]
pub const fn explorer_api_url(self) -> &'static str {
match self {
Network::Lighter => "https://explorer.elliot.ai/api",
Network::Robinhood => "https://explorerapi.rh.lighter.xyz/api",
}
}
#[must_use]
pub const fn app_url(self) -> &'static str {
match self {
Network::Lighter => "https://app.lighter.xyz",
Network::Robinhood => "https://lighter.xyz",
}
}
#[must_use]
pub const fn reserves_api_key(self, slot: ApiKeyIndex) -> bool {
matches!((self, slot.get()), (_, 0..=3) | (Network::Robinhood, 157))
}
#[must_use]
pub const fn has_binary_options(self) -> bool {
matches!(self, Network::Lighter)
}
#[must_use]
pub const fn fast_withdrawals_per_hour(self) -> u32 {
match self {
Network::Lighter => 10,
Network::Robinhood => 20,
}
}
#[must_use]
pub const fn standard_sub_accounts(self) -> u32 {
match self {
Network::Lighter => 4,
Network::Robinhood => 8,
}
}
}
impl fmt::Display for Network {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
impl FromStr for Network {
type Err = UnknownNetwork;
fn from_str(text: &str) -> Result<Self, Self::Err> {
match text.trim().to_ascii_lowercase().as_str() {
"lighter" | "zklighter" | "mainnet" => Ok(Network::Lighter),
"robinhood" | "lighter-rh" | "rh" => Ok(Network::Robinhood),
_ => Err(UnknownNetwork(text.trim().to_owned())),
}
}
}
#[cfg(test)]
#[path = "tests/network.rs"]
mod tests;