1use std::sync::atomic::{AtomicU32, Ordering};
42
43use dashmap::{DashMap, DashSet, mapref::entry::Entry};
44use nautilus_model::identifiers::ClientOrderId;
45use thiserror::Error;
46
47pub const DYDX_BASE_EPOCH: i64 = 1577836800;
50
51pub const DEFAULT_RUST_CLIENT_METADATA: u32 = 4;
54
55pub const MAX_SAFE_CLIENT_ID: u32 = u32::MAX - 1000;
58
59const TRADER_SHIFT: u32 = 22; const STRATEGY_SHIFT: u32 = 12; const COUNT_MASK: u32 = 0xFFF; const TRADER_MASK: u32 = 0x3FF; const STRATEGY_MASK: u32 = 0x3FF; const SEQUENTIAL_METADATA_MARKER: u32 = u32::MAX;
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub struct EncodedClientOrderId {
78 pub client_id: u32,
80 pub client_metadata: u32,
82}
83
84#[derive(Debug, Clone, Error)]
86pub enum EncoderError {
87 #[error(
89 "Client order ID counter overflow: current value {0} exceeds safe limit {MAX_SAFE_CLIENT_ID}"
90 )]
91 CounterOverflow(u32),
92
93 #[error("Failed to parse O-format ClientOrderId: {0}")]
95 ParseError(String),
96
97 #[error("Value overflow in encoding: {0}")]
99 ValueOverflow(String),
100}
101
102#[derive(Debug)]
114pub struct ClientOrderIdEncoder {
115 forward: DashMap<ClientOrderId, EncodedClientOrderId>,
117 reverse: DashMap<(u32, u32), ClientOrderId>,
119 next_id: AtomicU32,
121
122 known_client_ids: DashSet<u32>,
126}
127
128impl Default for ClientOrderIdEncoder {
129 fn default() -> Self {
130 Self::new()
131 }
132}
133
134impl ClientOrderIdEncoder {
135 #[must_use]
137 pub fn new() -> Self {
138 Self {
139 forward: DashMap::new(),
140 reverse: DashMap::new(),
141 next_id: AtomicU32::new(1),
142 known_client_ids: DashSet::new(),
143 }
144 }
145
146 pub fn register_known_client_id(&self, client_id: u32) {
152 self.known_client_ids.insert(client_id);
153 }
154
155 pub fn encode(&self, id: ClientOrderId) -> Result<EncodedClientOrderId, EncoderError> {
169 if let Some(existing) = self.forward.get(&id) {
171 let encoded = *existing.value();
172 return Ok(encoded);
173 }
174
175 let id_str = id.as_str();
176
177 if let Ok(numeric_id) = id_str.parse::<u32>() {
179 let encoded = EncodedClientOrderId {
180 client_id: numeric_id,
181 client_metadata: DEFAULT_RUST_CLIENT_METADATA,
182 };
183 self.forward.insert(id, encoded);
185 self.reverse
186 .insert((encoded.client_id, encoded.client_metadata), id);
187 return Ok(encoded);
188 }
189
190 if id_str.starts_with("O-") {
192 match self.encode_o_format(id_str) {
193 Ok(encoded) => {
194 if self.known_client_ids.contains(&encoded.client_id) {
198 log::warn!(
199 "[ENCODER] client_id {} for '{id}' collides with \
200 reconciled order, falling back to sequential",
201 encoded.client_id,
202 );
203 } else {
204 self.reverse
206 .insert((encoded.client_id, encoded.client_metadata), id);
207 return Ok(encoded);
208 }
209 }
210 Err(e) => {
211 log::warn!(
212 "[ENCODER] O-format parse failed for '{id}': {e}, falling back to sequential",
213 );
214 }
216 }
217 }
218
219 self.allocate_sequential(id)
221 }
222
223 fn encode_o_format(&self, id_str: &str) -> Result<EncodedClientOrderId, EncoderError> {
224 let parts: Vec<&str> = id_str.split('-').collect();
226 if parts.len() != 6 || parts[0] != "O" {
227 return Err(EncoderError::ParseError(format!(
228 "Expected O-YYYYMMDD-HHMMSS-TTT-SSS-CCC, received: {id_str}",
229 )));
230 }
231
232 let date_str = parts[1]; let time_str = parts[2]; let trader_str = parts[3]; let strategy_str = parts[4]; let count_str = parts[5]; if date_str.len() != 8 || time_str.len() != 6 {
240 return Err(EncoderError::ParseError(format!(
241 "Invalid date/time format in: {id_str}"
242 )));
243 }
244
245 let year: i32 = date_str[0..4]
247 .parse()
248 .map_err(|_| EncoderError::ParseError(format!("Invalid year in: {id_str}")))?;
249 let month: u32 = date_str[4..6]
250 .parse()
251 .map_err(|_| EncoderError::ParseError(format!("Invalid month in: {id_str}")))?;
252 let day: u32 = date_str[6..8]
253 .parse()
254 .map_err(|_| EncoderError::ParseError(format!("Invalid day in: {id_str}")))?;
255 let hour: u32 = time_str[0..2]
256 .parse()
257 .map_err(|_| EncoderError::ParseError(format!("Invalid hour in: {id_str}")))?;
258 let minute: u32 = time_str[2..4]
259 .parse()
260 .map_err(|_| EncoderError::ParseError(format!("Invalid minute in: {id_str}")))?;
261 let second: u32 = time_str[4..6]
262 .parse()
263 .map_err(|_| EncoderError::ParseError(format!("Invalid second in: {id_str}")))?;
264
265 let trader: u32 = trader_str
267 .parse()
268 .map_err(|_| EncoderError::ParseError(format!("Invalid trader in: {id_str}")))?;
269 let strategy: u32 = strategy_str
270 .parse()
271 .map_err(|_| EncoderError::ParseError(format!("Invalid strategy in: {id_str}")))?;
272 let count: u32 = count_str
273 .parse()
274 .map_err(|_| EncoderError::ParseError(format!("Invalid count in: {id_str}")))?;
275
276 if trader > TRADER_MASK {
278 return Err(EncoderError::ValueOverflow(format!(
279 "Trader tag {trader} exceeds max {TRADER_MASK}"
280 )));
281 }
282
283 if strategy > STRATEGY_MASK {
284 return Err(EncoderError::ValueOverflow(format!(
285 "Strategy tag {strategy} exceeds max {STRATEGY_MASK}"
286 )));
287 }
288
289 if count > COUNT_MASK {
290 return Err(EncoderError::ValueOverflow(format!(
291 "Count {count} exceeds max {COUNT_MASK}"
292 )));
293 }
294
295 let dt = chrono::NaiveDate::from_ymd_opt(year, month, day)
297 .and_then(|d| d.and_hms_opt(hour, minute, second))
298 .ok_or_else(|| EncoderError::ParseError(format!("Invalid datetime in: {id_str}")))?;
299
300 let timestamp = dt.and_utc().timestamp();
301
302 let seconds_since_epoch = timestamp - DYDX_BASE_EPOCH;
304 if seconds_since_epoch < 0 {
305 return Err(EncoderError::ValueOverflow(format!(
306 "Timestamp {timestamp} is before base epoch {DYDX_BASE_EPOCH}"
307 )));
308 }
309
310 let client_id =
317 (trader << TRADER_SHIFT) | (strategy << STRATEGY_SHIFT) | (count & COUNT_MASK);
318 let client_metadata = seconds_since_epoch as u32;
319
320 Ok(EncodedClientOrderId {
321 client_id,
322 client_metadata,
323 })
324 }
325
326 fn allocate_sequential(&self, id: ClientOrderId) -> Result<EncodedClientOrderId, EncoderError> {
327 let current = self.next_id.load(Ordering::Relaxed);
329 if current >= MAX_SAFE_CLIENT_ID {
330 log::error!(
331 "[ENCODER] allocate_sequential() OVERFLOW: counter {current} >= MAX_SAFE {MAX_SAFE_CLIENT_ID}"
332 );
333 return Err(EncoderError::CounterOverflow(current));
334 }
335
336 match self.forward.entry(id) {
338 Entry::Occupied(entry) => {
339 let encoded = *entry.get();
340 Ok(encoded)
341 }
342 Entry::Vacant(vacant) => {
343 let mut counter = self.next_id.fetch_add(1, Ordering::Relaxed);
346 while self.known_client_ids.contains(&counter) {
347 counter = self.next_id.fetch_add(1, Ordering::Relaxed);
348 }
349
350 if counter >= MAX_SAFE_CLIENT_ID {
351 return Err(EncoderError::CounterOverflow(counter));
352 }
353
354 let encoded = EncodedClientOrderId {
357 client_id: counter,
358 client_metadata: SEQUENTIAL_METADATA_MARKER,
359 };
360 vacant.insert(encoded);
361 self.reverse
362 .insert((encoded.client_id, encoded.client_metadata), id);
363 Ok(encoded)
364 }
365 }
366 }
367
368 #[must_use]
378 pub fn decode(&self, client_id: u32, client_metadata: u32) -> Option<ClientOrderId> {
379 if client_metadata == DEFAULT_RUST_CLIENT_METADATA {
381 let id = ClientOrderId::from(client_id.to_string().as_str());
382 return Some(id);
383 }
384
385 if client_metadata == SEQUENTIAL_METADATA_MARKER {
387 let result = self
388 .reverse
389 .get(&(client_id, client_metadata))
390 .map(|r| *r.value());
391 return result;
392 }
393
394 self.decode_o_format(client_id, client_metadata)
396 }
397
398 #[must_use]
403 pub fn decode_if_known(&self, client_id: u32, client_metadata: u32) -> Option<ClientOrderId> {
404 if let Some(entry) = self.reverse.get(&(client_id, client_metadata)) {
406 return Some(*entry.value());
407 }
408
409 if client_metadata == SEQUENTIAL_METADATA_MARKER {
411 return None;
412 }
413
414 if client_metadata == DEFAULT_RUST_CLIENT_METADATA {
416 return Some(ClientOrderId::from(client_id.to_string().as_str()));
417 }
418
419 self.decode_o_format(client_id, client_metadata)
421 }
422
423 fn decode_o_format(&self, client_id: u32, client_metadata: u32) -> Option<ClientOrderId> {
424 let trader = (client_id >> TRADER_SHIFT) & TRADER_MASK;
426 let strategy = (client_id >> STRATEGY_SHIFT) & STRATEGY_MASK;
427 let count = client_id & COUNT_MASK;
428
429 let timestamp = (client_metadata as i64) + DYDX_BASE_EPOCH;
431
432 let dt = chrono::DateTime::from_timestamp(timestamp, 0)?;
434
435 let id_str = format!(
437 "O-{:04}{:02}{:02}-{:02}{:02}{:02}-{:03}-{:03}-{}",
438 dt.year(),
439 dt.month(),
440 dt.day(),
441 dt.hour(),
442 dt.minute(),
443 dt.second(),
444 trader,
445 strategy,
446 count
447 );
448
449 let id = ClientOrderId::from(id_str.as_str());
450 Some(id)
451 }
452
453 #[must_use]
458 pub fn get(&self, id: &ClientOrderId) -> Option<EncodedClientOrderId> {
459 if let Some(entry) = self.forward.get(id) {
461 return Some(*entry.value());
462 }
463
464 let id_str = id.as_str();
465
466 if let Ok(numeric_id) = id_str.parse::<u32>() {
468 return Some(EncodedClientOrderId {
469 client_id: numeric_id,
470 client_metadata: DEFAULT_RUST_CLIENT_METADATA,
471 });
472 }
473
474 if id_str.starts_with("O-")
476 && let Ok(encoded) = self.encode_o_format(id_str)
477 {
478 return Some(encoded);
479 }
480
481 None
482 }
483
484 pub fn remove(&self, client_id: u32, client_metadata: u32) -> Option<ClientOrderId> {
488 if let Some((_, client_order_id)) = self.reverse.remove(&(client_id, client_metadata)) {
489 self.forward.remove(&client_order_id);
490 return Some(client_order_id);
491 }
492 None
493 }
494
495 pub fn remove_by_client_id(&self, client_id: u32) -> Option<ClientOrderId> {
498 if let result @ Some(_) = self.remove(client_id, DEFAULT_RUST_CLIENT_METADATA) {
500 return result;
501 }
502
503 let key_to_remove = self
505 .reverse
506 .iter()
507 .find(|r| r.key().0 == client_id)
508 .map(|r| *r.key());
509
510 if let Some((cid, meta)) = key_to_remove {
511 return self.remove(cid, meta);
512 }
513
514 None
515 }
516
517 #[must_use]
519 pub fn current_counter(&self) -> u32 {
520 self.next_id.load(Ordering::Relaxed)
521 }
522
523 #[must_use]
525 pub fn len(&self) -> usize {
526 self.forward.len()
527 }
528
529 #[must_use]
531 pub fn is_empty(&self) -> bool {
532 self.forward.is_empty()
533 }
534}
535
536use chrono::{Datelike, Timelike};
538
539#[cfg(test)]
540mod tests {
541 use rstest::rstest;
542
543 use super::*;
544
545 #[rstest]
546 fn test_encode_numeric_id() {
547 let encoder = ClientOrderIdEncoder::new();
548 let id = ClientOrderId::from("12345");
549
550 let result = encoder.encode(id);
551 assert!(result.is_ok());
552 let encoded = result.unwrap();
553 assert_eq!(encoded.client_id, 12345);
554 assert_eq!(encoded.client_metadata, DEFAULT_RUST_CLIENT_METADATA);
555 }
556
557 #[rstest]
558 fn test_encode_o_format() {
559 let encoder = ClientOrderIdEncoder::new();
560 let id = ClientOrderId::from("O-20260131-174827-001-001-1");
561
562 let result = encoder.encode(id);
563 assert!(result.is_ok());
564 let encoded = result.unwrap();
565
566 let expected_client_id = (1 << TRADER_SHIFT) | (1 << STRATEGY_SHIFT) | 1;
572 assert_eq!(encoded.client_id, expected_client_id);
573
574 let expected_timestamp = chrono::NaiveDate::from_ymd_opt(2026, 1, 31)
577 .unwrap()
578 .and_hms_opt(17, 48, 27)
579 .unwrap()
580 .and_utc()
581 .timestamp();
582 let expected_metadata = (expected_timestamp - DYDX_BASE_EPOCH) as u32;
583 assert_eq!(encoded.client_metadata, expected_metadata);
584 }
585
586 #[rstest]
587 fn test_roundtrip_o_format() {
588 let encoder = ClientOrderIdEncoder::new();
589 let id = ClientOrderId::from("O-20260131-174827-001-001-1");
590
591 let encoded = encoder.encode(id).unwrap();
592 let decoded = encoder.decode(encoded.client_id, encoded.client_metadata);
593
594 assert_eq!(decoded, Some(id));
595 }
596
597 #[rstest]
598 fn test_roundtrip_o_format_various() {
599 let encoder = ClientOrderIdEncoder::new();
600 let test_cases = vec![
601 "O-20260131-000000-001-001-1",
602 "O-20260131-235959-999-999-4095",
603 "O-20200101-000000-000-000-0",
604 "O-20251215-123456-123-456-789",
605 ];
606
607 for id_str in test_cases {
608 let id = ClientOrderId::from(id_str);
609 let encoded = encoder.encode(id).unwrap();
610 let decoded = encoder.decode(encoded.client_id, encoded.client_metadata);
611 assert_eq!(decoded, Some(id), "Roundtrip failed for {id_str}");
612 }
613 }
614
615 #[rstest]
616 fn test_roundtrip_numeric() {
617 let encoder = ClientOrderIdEncoder::new();
618 let id = ClientOrderId::from("12345");
619
620 let encoded = encoder.encode(id).unwrap();
621 let decoded = encoder.decode(encoded.client_id, encoded.client_metadata);
622
623 assert_eq!(decoded, Some(id));
624 }
625
626 #[rstest]
627 fn test_encode_non_standard_uses_sequential() {
628 let encoder = ClientOrderIdEncoder::new();
629 let id = ClientOrderId::from("custom-order-id");
630
631 let result = encoder.encode(id);
632 assert!(result.is_ok());
633 let encoded = result.unwrap();
634
635 assert_eq!(
637 encoded.client_metadata, SEQUENTIAL_METADATA_MARKER,
638 "Expected client_metadata == SEQUENTIAL_METADATA_MARKER"
639 );
640 }
641
642 #[rstest]
643 fn test_roundtrip_sequential() {
644 let encoder = ClientOrderIdEncoder::new();
645 let id = ClientOrderId::from("custom-order-id");
646
647 let encoded = encoder.encode(id).unwrap();
648 let decoded = encoder.decode(encoded.client_id, encoded.client_metadata);
649
650 assert_eq!(decoded, Some(id));
651 }
652
653 #[rstest]
654 fn test_sequential_lost_after_restart() {
655 let encoder1 = ClientOrderIdEncoder::new();
657 let id = ClientOrderId::from("custom-order-id");
658
659 let encoded = encoder1.encode(id).unwrap();
660
661 let encoder2 = ClientOrderIdEncoder::new();
663 let decoded = encoder2.decode(encoded.client_id, encoded.client_metadata);
664
665 assert!(decoded.is_none());
667 }
668
669 #[rstest]
670 fn test_o_format_survives_restart() {
671 let encoder1 = ClientOrderIdEncoder::new();
672 let id = ClientOrderId::from("O-20260131-174827-001-001-1");
673
674 let encoded = encoder1.encode(id).unwrap();
675
676 let encoder2 = ClientOrderIdEncoder::new();
678 let decoded = encoder2.decode(encoded.client_id, encoded.client_metadata);
679
680 assert_eq!(decoded, Some(id));
682 }
683
684 #[rstest]
685 fn test_get_without_encode() {
686 let encoder = ClientOrderIdEncoder::new();
687
688 let numeric_id = ClientOrderId::from("12345");
690 let got = encoder.get(&numeric_id);
691 assert_eq!(
692 got,
693 Some(EncodedClientOrderId {
694 client_id: 12345,
695 client_metadata: DEFAULT_RUST_CLIENT_METADATA
696 })
697 );
698
699 let o_id = ClientOrderId::from("O-20260131-174827-001-001-1");
701 let got = encoder.get(&o_id);
702 assert!(got.is_some());
703
704 let custom_id = ClientOrderId::from("custom");
706 let got = encoder.get(&custom_id);
707 assert!(got.is_none());
708 }
709
710 #[rstest]
711 fn test_remove_sequential() {
712 let encoder = ClientOrderIdEncoder::new();
713 let id = ClientOrderId::from("custom-order-id");
714
715 let encoded = encoder.encode(id).unwrap();
716 assert_eq!(encoder.len(), 1);
717
718 let removed = encoder.remove(encoded.client_id, encoded.client_metadata);
719 assert_eq!(removed, Some(id));
720 assert_eq!(encoder.len(), 0);
721 }
722
723 #[rstest]
724 fn test_max_values_o_format() {
725 let encoder = ClientOrderIdEncoder::new();
726 let id = ClientOrderId::from("O-20260131-235959-999-999-4095");
728
729 let result = encoder.encode(id);
730 assert!(result.is_ok());
731
732 let encoded = result.unwrap();
733 let decoded = encoder.decode(encoded.client_id, encoded.client_metadata);
734 assert_eq!(decoded, Some(id));
735 }
736
737 #[rstest]
738 fn test_overflow_trader_tag() {
739 let encoder = ClientOrderIdEncoder::new();
740 let id = ClientOrderId::from("O-20260131-174827-1024-001-1");
742
743 let result = encoder.encode(id);
744 assert!(result.is_ok());
746 assert_eq!(
747 result.unwrap().client_metadata,
748 SEQUENTIAL_METADATA_MARKER,
749 "Overflow should fall back to sequential allocation"
750 );
751 }
752
753 #[rstest]
754 fn test_date_before_base_epoch_falls_back_to_sequential() {
755 let encoder = ClientOrderIdEncoder::new();
756 let id = ClientOrderId::from("O-20191231-235959-001-001-1");
758
759 let result = encoder.encode(id);
760 assert!(result.is_ok());
762 let encoded = result.unwrap();
763 assert_eq!(
764 encoded.client_metadata, SEQUENTIAL_METADATA_MARKER,
765 "Pre-2020 dates should fall back to sequential allocation"
766 );
767
768 let decoded = encoder.decode(encoded.client_id, encoded.client_metadata);
770 assert_eq!(decoded, Some(id));
771 }
772
773 #[rstest]
774 fn test_encode_same_id_returns_same_value() {
775 let encoder = ClientOrderIdEncoder::new();
776 let id = ClientOrderId::from("O-20260131-174827-001-001-1");
777
778 let first = encoder.encode(id).unwrap();
779 let second = encoder.encode(id).unwrap();
780
781 assert_eq!(first, second);
782 }
783
784 #[rstest]
785 fn test_same_second_different_count_has_unique_client_ids() {
786 let encoder = ClientOrderIdEncoder::new();
789
790 let id1 = ClientOrderId::from("O-20260201-084653-001-001-1");
792 let id2 = ClientOrderId::from("O-20260201-084653-001-001-2");
793
794 let encoded1 = encoder.encode(id1).unwrap();
795 let encoded2 = encoder.encode(id2).unwrap();
796
797 assert_ne!(
799 encoded1.client_id, encoded2.client_id,
800 "Orders in the same second must have different client_ids for dYdX"
801 );
802
803 assert_eq!(encoded1.client_metadata, encoded2.client_metadata);
805
806 assert_eq!(
808 encoder.decode(encoded1.client_id, encoded1.client_metadata),
809 Some(id1)
810 );
811 assert_eq!(
812 encoder.decode(encoded2.client_id, encoded2.client_metadata),
813 Some(id2)
814 );
815 }
816
817 #[rstest]
818 fn test_encode_different_ids_returns_different_values() {
819 let encoder = ClientOrderIdEncoder::new();
820 let id1 = ClientOrderId::from("O-20260131-174827-001-001-1");
821 let id2 = ClientOrderId::from("O-20260131-174828-001-001-2");
822
823 let result1 = encoder.encode(id1).unwrap();
824 let result2 = encoder.encode(id2).unwrap();
825
826 assert_ne!(result1, result2);
827 }
828
829 #[rstest]
830 fn test_current_counter() {
831 let encoder = ClientOrderIdEncoder::new();
832 assert_eq!(encoder.current_counter(), 1);
833
834 encoder.encode(ClientOrderId::from("custom-1")).unwrap();
835 assert_eq!(encoder.current_counter(), 2);
836
837 encoder.encode(ClientOrderId::from("custom-2")).unwrap();
838 assert_eq!(encoder.current_counter(), 3);
839
840 encoder
842 .encode(ClientOrderId::from("O-20260131-174827-001-001-1"))
843 .unwrap();
844 assert_eq!(encoder.current_counter(), 3);
845 }
846
847 #[rstest]
848 fn test_is_empty() {
849 let encoder = ClientOrderIdEncoder::new();
850 assert!(encoder.is_empty());
851
852 encoder.encode(ClientOrderId::from("custom")).unwrap();
853 assert!(!encoder.is_empty());
854 }
855
856 #[rstest]
857 fn test_o_format_collision_falls_back_to_sequential() {
858 let encoder = ClientOrderIdEncoder::new();
859 let id = ClientOrderId::from("O-20260220-031943-001-000-51");
860
861 let colliding_client_id = (1 << TRADER_SHIFT) | (0 << STRATEGY_SHIFT) | 51;
863
864 encoder.register_known_client_id(colliding_client_id);
865 let encoded = encoder.encode(id).unwrap();
866 assert_eq!(
867 encoded.client_metadata, SEQUENTIAL_METADATA_MARKER,
868 "Collision should fall back to sequential allocation"
869 );
870 assert_ne!(encoded.client_id, colliding_client_id);
871
872 let decoded = encoder.decode_o_format(colliding_client_id, {
874 let dt = chrono::NaiveDate::from_ymd_opt(2026, 2, 20)
875 .unwrap()
876 .and_hms_opt(3, 19, 43)
877 .unwrap()
878 .and_utc()
879 .timestamp();
880 (dt - DYDX_BASE_EPOCH) as u32
881 });
882 assert_eq!(decoded, Some(id));
883 }
884
885 #[rstest]
886 fn test_sequential_skips_known_client_ids() {
887 let encoder = ClientOrderIdEncoder::new();
888
889 encoder.register_known_client_id(1);
890 encoder.register_known_client_id(2);
891
892 let encoded = encoder.encode(ClientOrderId::from("custom-order")).unwrap();
893 assert_eq!(encoded.client_id, 3);
894 assert_eq!(encoded.client_metadata, SEQUENTIAL_METADATA_MARKER);
895 }
896
897 #[rstest]
898 fn test_sequential_overflow_after_skipping_known_ids() {
899 let encoder = ClientOrderIdEncoder::new();
900
901 let near_limit = MAX_SAFE_CLIENT_ID - 1;
902 encoder.next_id.store(near_limit, Ordering::Relaxed);
903
904 encoder.register_known_client_id(near_limit);
906
907 let result = encoder.encode(ClientOrderId::from("overflow-order"));
908 assert!(
909 matches!(result, Err(EncoderError::CounterOverflow(_))),
910 "Expected CounterOverflow after skipping past MAX_SAFE_CLIENT_ID"
911 );
912 }
913}