df_lighter_core/client/
network.rs1use std::{fmt, str::FromStr};
12
13use crate::utils::{Address, ApiKeyIndex, AssetId};
14
15#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
16pub enum Network {
17 #[default]
19 Lighter,
20 Robinhood,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
26#[error("`{0}` is not a Lighter network; expected lighter or robinhood")]
27pub struct UnknownNetwork(pub String);
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31pub struct Collateral {
32 pub symbol: &'static str,
33 pub token: Address,
35 pub l1_decimals: u32,
37 pub asset: AssetId,
39 pub decimals: u32,
41}
42
43pub const USDC: Collateral = Collateral {
45 symbol: "USDC",
46 token: address("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
47 l1_decimals: 6,
48 asset: AssetId::COLLATERAL,
49 decimals: 6,
50};
51
52pub const USDG: Collateral = Collateral {
54 symbol: "USDG",
55 token: address("0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168"),
56 l1_decimals: 6,
57 asset: AssetId::COLLATERAL,
58 decimals: 6,
59};
60
61pub const LIGHTER_CONTRACT: Address = address("0x3B4D794a66304F130a4Db8F2551B0070dfCf5ca7");
63
64pub const ROBINHOOD_CONTRACT: Address = address("0x94bAB9693Ba2f6358507eFfcbd372b0660AFfF9d");
66
67const fn address(text: &str) -> Address {
68 match Address::parse_const(text) {
69 Some(address) => address,
70 None => Address::ZERO,
71 }
72}
73
74impl Network {
75 pub const ALL: [Network; 2] = [Network::Lighter, Network::Robinhood];
76
77 #[must_use]
78 pub const fn name(self) -> &'static str {
79 match self {
80 Network::Lighter => "lighter",
81 Network::Robinhood => "robinhood",
82 }
83 }
84
85 #[must_use]
87 pub const fn api_url(self) -> &'static str {
88 match self {
89 Network::Lighter => "https://mainnet.zklighter.elliot.ai",
90 Network::Robinhood => "https://api.rh.lighter.xyz",
91 }
92 }
93
94 #[must_use]
96 pub const fn ws_url(self) -> &'static str {
97 match self {
98 Network::Lighter => "wss://mainnet.zklighter.elliot.ai/stream",
99 Network::Robinhood => "wss://api.rh.lighter.xyz/stream",
100 }
101 }
102
103 #[must_use]
105 pub const fn ws_readonly_url(self) -> &'static str {
106 match self {
107 Network::Lighter => "wss://mainnet.zklighter.elliot.ai/stream?readonly=true",
108 Network::Robinhood => "wss://api.rh.lighter.xyz/stream?readonly=true",
109 }
110 }
111
112 #[must_use]
114 pub const fn chain_id(self) -> u32 {
115 match self {
116 Network::Lighter => 304,
117 Network::Robinhood => 466_324,
118 }
119 }
120
121 #[must_use]
123 pub const fn l1_chain_id(self) -> u64 {
124 match self {
125 Network::Lighter => 1,
126 Network::Robinhood => 4663,
127 }
128 }
129
130 #[must_use]
131 pub const fn l1_name(self) -> &'static str {
132 match self {
133 Network::Lighter => "Ethereum",
134 Network::Robinhood => "Robinhood Chain",
135 }
136 }
137
138 #[must_use]
141 pub const fn l1_rpc_url(self) -> &'static str {
142 match self {
143 Network::Lighter => "https://ethereum-rpc.publicnode.com",
144 Network::Robinhood => "https://rpc.mainnet.chain.robinhood.com",
145 }
146 }
147
148 #[must_use]
150 pub const fn l1_explorer_url(self) -> &'static str {
151 match self {
152 Network::Lighter => "https://etherscan.io",
153 Network::Robinhood => "https://robinhoodchain.blockscout.com",
154 }
155 }
156
157 #[must_use]
159 pub const fn contract(self) -> Address {
160 match self {
161 Network::Lighter => LIGHTER_CONTRACT,
162 Network::Robinhood => ROBINHOOD_CONTRACT,
163 }
164 }
165
166 #[must_use]
167 pub const fn collateral(self) -> Collateral {
168 match self {
169 Network::Lighter => USDC,
170 Network::Robinhood => USDG,
171 }
172 }
173
174 #[must_use]
176 pub const fn explorer_api_url(self) -> &'static str {
177 match self {
178 Network::Lighter => "https://explorer.elliot.ai/api",
179 Network::Robinhood => "https://explorerapi.rh.lighter.xyz/api",
180 }
181 }
182
183 #[must_use]
185 pub const fn app_url(self) -> &'static str {
186 match self {
187 Network::Lighter => "https://app.lighter.xyz",
188 Network::Robinhood => "https://lighter.xyz",
189 }
190 }
191
192 #[must_use]
195 pub const fn reserves_api_key(self, slot: ApiKeyIndex) -> bool {
196 matches!((self, slot.get()), (_, 0..=3) | (Network::Robinhood, 157))
197 }
198
199 #[must_use]
201 pub const fn has_binary_options(self) -> bool {
202 matches!(self, Network::Lighter)
203 }
204
205 #[must_use]
207 pub const fn fast_withdrawals_per_hour(self) -> u32 {
208 match self {
209 Network::Lighter => 10,
210 Network::Robinhood => 20,
211 }
212 }
213
214 #[must_use]
216 pub const fn standard_sub_accounts(self) -> u32 {
217 match self {
218 Network::Lighter => 4,
219 Network::Robinhood => 8,
220 }
221 }
222}
223
224impl fmt::Display for Network {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 f.write_str(self.name())
227 }
228}
229
230impl FromStr for Network {
231 type Err = UnknownNetwork;
232
233 fn from_str(text: &str) -> Result<Self, Self::Err> {
234 match text.trim().to_ascii_lowercase().as_str() {
235 "lighter" | "zklighter" | "mainnet" => Ok(Network::Lighter),
236 "robinhood" | "lighter-rh" | "rh" => Ok(Network::Robinhood),
237 _ => Err(UnknownNetwork(text.trim().to_owned())),
238 }
239 }
240}
241
242#[cfg(test)]
243#[path = "tests/network.rs"]
244mod tests;