Skip to main content

nautilus_lighter/common/
consts.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Venue identifiers and tuning constants for the Lighter adapter.
17
18use std::{sync::LazyLock, time::Duration};
19
20use nautilus_model::identifiers::{ClientId, Venue};
21use ustr::Ustr;
22
23pub(crate) const SCHEDULED_CANCEL_MIN_DELAY_SECS: u64 = 5 * 60;
24pub(crate) const SCHEDULED_CANCEL_MAX_DELAY_SECS: u64 = 15 * 24 * 60 * 60;
25pub(crate) const SCHEDULED_CANCEL_REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
26pub(crate) const SCHEDULED_CANCEL_CLOCK_HEADROOM_SECS: u64 = 60;
27
28/// Venue name string for Lighter.
29pub const LIGHTER: &str = "LIGHTER";
30
31/// Venue name string for Robinhood Chain.
32pub const LIGHTER_ROBINHOOD: &str = "LIGHTER_ROBINHOOD";
33
34/// Lighter venue identifier.
35pub static LIGHTER_VENUE: LazyLock<Venue> = LazyLock::new(|| Venue::new(Ustr::from(LIGHTER)));
36
37/// Robinhood Chain venue identifier.
38pub static LIGHTER_ROBINHOOD_VENUE: LazyLock<Venue> =
39    LazyLock::new(|| Venue::new(Ustr::from(LIGHTER_ROBINHOOD)));
40
41/// Static client ID instance.
42pub static LIGHTER_CLIENT_ID: LazyLock<ClientId> =
43    LazyLock::new(|| ClientId::new(Ustr::from(LIGHTER)));
44
45/// Robinhood Chain client ID instance.
46pub static LIGHTER_ROBINHOOD_CLIENT_ID: LazyLock<ClientId> =
47    LazyLock::new(|| ClientId::new(Ustr::from(LIGHTER_ROBINHOOD)));
48
49/// L2 chain id for Lighter Mainnet.
50///
51/// Mirrors the upstream `lighter-go` constant. Used as the first element of
52/// the L2 transaction hash preimage.
53pub const LIGHTER_MAINNET_CHAIN_ID: u32 = 304;
54
55/// L2 chain id for Lighter Testnet.
56///
57/// Mirrors `lighter-go`'s testnet chain id and matches the value the oracle
58/// generator emits.
59pub const LIGHTER_TESTNET_CHAIN_ID: u32 = 300;
60
61/// L2 chain id for Robinhood Mainnet.
62pub const LIGHTER_ROBINHOOD_CHAIN_ID: u32 = 466_324;
63
64/// Nautilus integrator account index on Lighter.
65pub const LIGHTER_NAUTILUS_INTEGRATOR_ACCOUNT_INDEX: u64 = 723_813;
66
67/// Nautilus integrator account index on Robinhood Chain.
68pub const LIGHTER_ROBINHOOD_NAUTILUS_INTEGRATOR_ACCOUNT_INDEX: u64 = 17_725;
69
70/// Nautilus referral code shared by both mainnets.
71pub const LIGHTER_NAUTILUS_REFERRAL_CODE: &str = "NAUTILUS";
72
73/// Venue error code for missing integrator approval.
74pub const LIGHTER_ERROR_CODE_INTEGRATOR_NOT_APPROVED: u64 = 21_149;
75
76/// Venue error code for an invalid (non-contiguous) transaction nonce.
77pub const LIGHTER_ERROR_CODE_INVALID_NONCE: i64 = 21_104;
78
79/// Venue error code for an idempotent duplicate WebSocket subscription.
80pub const LIGHTER_ERROR_CODE_ALREADY_SUBSCRIBED: u64 = 30_003;
81
82/// Venue error code for WebSocket request rate limiting.
83pub const LIGHTER_ERROR_CODE_WS_RATE_LIMITED: u64 = 30_009;
84
85/// Venue error code for a failed WebSocket subscription open.
86pub const LIGHTER_ERROR_CODE_WS_SUBSCRIBE_FAILED: u64 = 30_012;
87
88/// Venue error-code range for L2 transaction failures.
89///
90/// Observed codes follow a domain split: `20xxx` request validation, `21xxx`
91/// transaction processing (21104 invalid nonce, 21149 integrator not
92/// approved), `30xxx` WebSocket subscription state (30003 "Already
93/// Subscribed"). Bare error frames are attributed to in-flight `sendTx`
94/// requests only when the code falls in this range.
95pub const LIGHTER_ERROR_CODE_TX_RANGE: std::ops::Range<u64> = 21_000..22_000;
96
97/// Public docs anchor for integrator approval.
98pub const LIGHTER_INTEGRATOR_APPROVAL_DOCS_URL: &str =
99    "https://nautilustrader.io/docs/nightly/integrations/lighter/#integrator-attribution";
100
101/// Maximum batch size for `sendTxBatch` on the WebSocket transport.
102pub const LIGHTER_MAX_BATCH_TX: usize = 15;
103
104/// Maximum auth-token expiry permitted by the venue (8 hours).
105pub const LIGHTER_AUTH_TOKEN_MAX_TTL: Duration = Duration::from_secs(8 * 60 * 60);
106
107/// Default refresh window before an auth token expires.
108///
109/// The adapter rotates the auth token this far ahead of expiry to avoid races
110/// during long-running WebSocket sessions.
111pub const LIGHTER_AUTH_TOKEN_REFRESH_LEAD: Duration = Duration::from_secs(15 * 60);
112
113/// Default WebSocket heartbeat interval.
114///
115/// Lighter requires a frame at least every 2 minutes; we send well below that.
116pub const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(30);
117
118/// Teardown window for a WebSocket carrying no inbound frame of any kind.
119///
120/// Liveness rests on the venue still sending frames rather than on market data
121/// arriving: a quiet market is legitimate, and the pong answering every
122/// [`HEARTBEAT_INTERVAL`] ping refreshes this window even when nothing trades.
123/// Three heartbeat cycles tolerate two lost replies before teardown.
124pub const HEARTBEAT_TIMEOUT: Duration =
125    Duration::from_secs(HEARTBEAT_INTERVAL.as_secs().saturating_mul(3));
126
127const _: () = assert!(
128    HEARTBEAT_TIMEOUT.as_secs() > HEARTBEAT_INTERVAL.as_secs(),
129    "heartbeat timeout must exceed the heartbeat interval, or every connection tears down \
130     before its first pong is due"
131);
132
133/// Base reconnect backoff for the WebSocket client.
134pub const RECONNECT_BASE_BACKOFF: Duration = Duration::from_millis(250);
135
136/// Maximum reconnect backoff for the WebSocket client.
137pub const RECONNECT_MAX_BACKOFF: Duration = Duration::from_secs(30);
138
139/// Default HTTP request timeout.
140pub const HTTP_TIMEOUT: Duration = Duration::from_secs(10);
141
142/// Grace period a client waits for its background tasks to finish during
143/// teardown before aborting them.
144pub const DISCONNECT_TIMEOUT: Duration = Duration::from_secs(2);
145
146/// Maximum subscribe messages awaiting venue acknowledgement at once.
147///
148/// Held below Lighter's 50-per-IP inflight cap; see the WebSocket rate-limit
149/// strategy in [`crate::common::rate_limit`].
150pub const SUBSCRIBE_INFLIGHT_MAX: usize = 35;
151
152/// Maximum venue-level retries for one WebSocket subscription request.
153pub const SUBSCRIBE_RETRY_MAX: u8 = 5;
154
155/// Initial backoff after a venue-level WebSocket subscription rejection.
156pub const SUBSCRIBE_RETRY_BASE_BACKOFF: Duration = Duration::from_millis(250);
157
158/// Outbound command queue depth before backpressure kicks in.
159pub const QUEUE_MAX: usize = 1000;