Skip to main content

nautilus_derive/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//! Static constants for the Derive adapter.
17//!
18//! EIP-712 domain separators, the Action typehash, and Trade module addresses
19//! are sourced from the v3 action-signing reference at
20//! <https://docs.derive.xyz/authentication/action-signing>. Both mainnet and
21//! testnet values are populated; per-instance overrides on
22//! [`crate::config::DeriveExecutionClientConfig`] take precedence.
23
24use std::{sync::LazyLock, time::Duration};
25
26use nautilus_model::identifiers::{ClientId, Venue};
27use ustr::Ustr;
28
29use crate::common::enums::DeriveEnvironment;
30
31/// Venue identifier string.
32pub const DERIVE: &str = "DERIVE";
33
34/// Static venue instance.
35pub static DERIVE_VENUE: LazyLock<Venue> = LazyLock::new(|| Venue::new(Ustr::from(DERIVE)));
36
37/// Static client ID instance.
38pub static DERIVE_CLIENT_ID: LazyLock<ClientId> =
39    LazyLock::new(|| ClientId::new(Ustr::from(DERIVE)));
40
41/// Referral code for order attribution (zero additional fees),
42/// see <https://docs.derive.xyz/reference/api-broker> for further details.
43pub const DERIVE_NAUTILUS_REFERRAL_CODE: &str = "nautilus";
44
45pub const REST_URL_MAINNET: &str = "https://api.derive.xyz/v3";
46pub const REST_URL_TESTNET: &str = "https://testnet.api.derive.xyz/v3";
47pub const WS_URL_MAINNET: &str = "wss://api.derive.xyz/v3/ws";
48pub const WS_URL_TESTNET: &str = "wss://testnet.api.derive.xyz/v3/ws";
49
50pub const DERIVE_TRADES_PAGE_SIZE: u32 = 1000;
51pub const DERIVE_CANDLES_DEFAULT_LIMIT: usize = 1000;
52pub const DERIVE_CANDLES_MAX_PAGES: usize = 100;
53
54/// EIP-712 domain separator (Ethereum chain 1).
55pub const DOMAIN_SEPARATOR_MAINNET: &str =
56    "0xda616dfabb88681b08e1592820a41d55ddc62d68de110e327ae99d734506fe19";
57
58/// EIP-712 domain separator (Sepolia chain 11155111).
59pub const DOMAIN_SEPARATOR_TESTNET: &str =
60    "0x24d674cd5f2b9d564691c51e9d88f649b99246a2244dd74ce27b96578d773e85";
61
62/// EIP-712 action typehash, identical across networks in the v3 action-signing reference.
63pub const ACTION_TYPEHASH: &str =
64    "0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17";
65
66/// Trade module contract address (mainnet).
67pub const TRADE_MODULE_ADDRESS_MAINNET: &str = "0xB8D20c2B7a1Ad2EE33Bc50eF10876eD3035b5e7b";
68
69/// Trade module contract address (testnet).
70pub const TRADE_MODULE_ADDRESS_TESTNET: &str = TRADE_MODULE_ADDRESS_MAINNET;
71
72/// Withdrawal module contract address (mainnet).
73pub const WITHDRAW_MODULE_ADDRESS_MAINNET: &str = "0x9d0E8f5b25384C7310CB8C6aE32C8fbeb645d083";
74
75/// Withdrawal module contract address (testnet).
76pub const WITHDRAW_MODULE_ADDRESS_TESTNET: &str = "0xe850641C5207dc5E9423fB15f89ae6031A05fd92";
77
78/// Transfer module contract address (mainnet).
79pub const TRANSFER_MODULE_ADDRESS_MAINNET: &str = "0x01259207A40925b794C8ac320456F7F6c8FE2636";
80
81/// Transfer module contract address (testnet).
82pub const TRANSFER_MODULE_ADDRESS_TESTNET: &str = "0x0CFC1a4a90741aB242cAfaCD798b409E12e68926";
83
84/// Deposit module contract address (mainnet).
85pub const DEPOSIT_MODULE_ADDRESS_MAINNET: &str = "0x9B3FE5E5a3bcEa5df4E08c41Ce89C4e3Ff01Ace3";
86
87/// Deposit module contract address (testnet).
88pub const DEPOSIT_MODULE_ADDRESS_TESTNET: &str = "0x43223Db33AdA0575D2E100829543f8B04A37a1ec";
89
90/// Returns the EIP-712 domain separator constant for the configured environment.
91#[must_use]
92pub const fn domain_separator_for(environment: DeriveEnvironment) -> &'static str {
93    match environment {
94        DeriveEnvironment::Mainnet => DOMAIN_SEPARATOR_MAINNET,
95        DeriveEnvironment::Testnet => DOMAIN_SEPARATOR_TESTNET,
96    }
97}
98
99/// Returns the Trade module contract address constant for the configured environment.
100#[must_use]
101pub const fn trade_module_address_for(environment: DeriveEnvironment) -> &'static str {
102    match environment {
103        DeriveEnvironment::Mainnet => TRADE_MODULE_ADDRESS_MAINNET,
104        DeriveEnvironment::Testnet => TRADE_MODULE_ADDRESS_TESTNET,
105    }
106}
107
108/// REST authentication header carrying the owner's EOA or multisig address.
109pub const HEADER_DERIVE_WALLET: &str = "X-DeriveWallet";
110
111/// REST authentication header carrying the signed timestamp.
112pub const HEADER_DERIVE_TIMESTAMP: &str = "X-DeriveTimestamp";
113
114/// REST authentication header carrying the session-key signature.
115pub const HEADER_DERIVE_SIGNATURE: &str = "X-DeriveSignature";
116
117/// Minimum signature TTL the adapter uses for self-custodial orders.
118///
119/// The v3 minimum order signature lifetime is ten seconds.
120pub const MIN_SIGNATURE_TTL: Duration = Duration::from_secs(10);
121
122/// Signature TTL used for Derive trigger orders.
123///
124/// The 31-day lifetime stays below the v3 120-day maximum for non-MMP orders.
125pub const TRIGGER_ORDER_SIGNATURE_TTL: Duration = Duration::from_secs(31 * 24 * 60 * 60);
126
127/// Fixed-point scale used by all on-chain decimal fields (1e18).
128pub const DECIMAL_SCALE: u128 = 1_000_000_000_000_000_000;
129
130/// Maximum fractional precision accepted for v3 financial inputs.
131pub const DECIMAL_PRECISION: u32 = 12;
132
133pub const HTTP_TIMEOUT: Duration = Duration::from_secs(10);
134
135/// Default timeout for blocking on account registration during execution
136/// client `connect()`.
137pub const DERIVE_ACCOUNT_REGISTRATION_TIMEOUT_SECS: f64 = 30.0;
138
139pub const WS_HEARTBEAT_SECS: u64 = 30;
140pub const WS_HEARTBEAT_TIMEOUT: Duration = Duration::from_mins(1);
141
142pub const RECONNECT_BASE_BACKOFF: Duration = Duration::from_millis(250);
143pub const RECONNECT_MAX_BACKOFF: Duration = Duration::from_secs(30);
144pub const RECONNECT_JITTER_MS: u64 = 200;
145pub const RECONNECT_BACKOFF_FACTOR: f64 = 2.0;
146pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(15);
147
148pub const WS_DISCONNECT_TIMEOUT: Duration = Duration::from_secs(5);
149
150/// Maximum wait for a JSON-RPC response on the WebSocket transport before the
151/// request outcome is treated as unknown, so a silent venue cannot hang
152/// `connect()` or an order write. Matches [`HTTP_TIMEOUT`].
153pub const WS_REQUEST_TIMEOUT: Duration = Duration::from_secs(10);
154
155#[cfg(test)]
156mod tests {
157    use rstest::rstest;
158
159    use super::*;
160
161    #[rstest]
162    fn test_venue_constant() {
163        assert_eq!(DERIVE_VENUE.as_str(), DERIVE);
164    }
165
166    #[rstest]
167    fn test_url_constants_have_expected_schemes() {
168        assert!(REST_URL_MAINNET.starts_with("https://"));
169        assert!(REST_URL_TESTNET.starts_with("https://"));
170        assert!(WS_URL_MAINNET.starts_with("wss://"));
171        assert!(WS_URL_TESTNET.starts_with("wss://"));
172    }
173
174    #[rstest]
175    fn test_decimal_scale_is_1e18() {
176        assert_eq!(DECIMAL_SCALE, 10u128.pow(18));
177    }
178
179    #[rstest]
180    fn test_min_signature_ttl_is_ten_seconds() {
181        assert_eq!(MIN_SIGNATURE_TTL, Duration::from_secs(10));
182    }
183
184    #[rstest]
185    fn test_trigger_order_signature_ttl_is_thirty_one_days() {
186        assert_eq!(
187            TRIGGER_ORDER_SIGNATURE_TTL,
188            Duration::from_secs(31 * 24 * 60 * 60),
189        );
190    }
191
192    #[rstest]
193    fn test_domain_separator_for_routes_per_environment() {
194        assert_eq!(
195            domain_separator_for(DeriveEnvironment::Mainnet),
196            DOMAIN_SEPARATOR_MAINNET,
197        );
198        assert_eq!(
199            domain_separator_for(DeriveEnvironment::Testnet),
200            DOMAIN_SEPARATOR_TESTNET,
201        );
202    }
203
204    #[rstest]
205    fn test_trade_module_address_for_routes_per_environment() {
206        assert_eq!(
207            trade_module_address_for(DeriveEnvironment::Mainnet),
208            TRADE_MODULE_ADDRESS_MAINNET,
209        );
210        assert_eq!(
211            trade_module_address_for(DeriveEnvironment::Testnet),
212            TRADE_MODULE_ADDRESS_TESTNET,
213        );
214    }
215}