Skip to main content

nautilus_common/messages/data/
mod.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//! Data specific messages such as subscriptions and requests.
17
18use std::{any::Any, sync::Arc};
19
20use nautilus_core::{Params, UUID4, UnixNanos};
21use nautilus_model::{
22    data::BarType,
23    identifiers::{ClientId, Venue},
24};
25
26pub mod request;
27pub mod response;
28pub mod subscribe;
29pub mod unsubscribe;
30
31// Re-exports
32pub use request::{
33    RequestBars, RequestBookDepth, RequestBookSnapshot, RequestCustomData, RequestForwardPrices,
34    RequestFundingRates, RequestInstrument, RequestInstruments, RequestQuotes, RequestTrades,
35};
36pub use response::{
37    BarsResponse, BookResponse, CustomDataResponse, ForwardPricesResponse, FundingRatesResponse,
38    InstrumentResponse, InstrumentsResponse, QuotesResponse, TradesResponse,
39};
40pub use subscribe::{
41    SubscribeBars, SubscribeBookDeltas, SubscribeBookDepth10, SubscribeBookSnapshots,
42    SubscribeCustomData, SubscribeFundingRates, SubscribeIndexPrices, SubscribeInstrument,
43    SubscribeInstrumentClose, SubscribeInstrumentStatus, SubscribeInstruments, SubscribeMarkPrices,
44    SubscribeOptionChain, SubscribeOptionGreeks, SubscribeQuotes, SubscribeTrades,
45};
46pub use unsubscribe::{
47    UnsubscribeBars, UnsubscribeBookDeltas, UnsubscribeBookDepth10, UnsubscribeBookSnapshots,
48    UnsubscribeCustomData, UnsubscribeFundingRates, UnsubscribeIndexPrices, UnsubscribeInstrument,
49    UnsubscribeInstrumentClose, UnsubscribeInstrumentStatus, UnsubscribeInstruments,
50    UnsubscribeMarkPrices, UnsubscribeOptionChain, UnsubscribeOptionGreeks, UnsubscribeQuotes,
51    UnsubscribeTrades,
52};
53
54#[cfg(feature = "defi")]
55use crate::messages::defi::{DefiRequestCommand, DefiSubscribeCommand, DefiUnsubscribeCommand};
56
57#[non_exhaustive]
58#[derive(Clone, Debug, PartialEq)]
59pub enum DataCommand {
60    Request(RequestCommand),
61    Subscribe(SubscribeCommand),
62    Unsubscribe(UnsubscribeCommand),
63    #[cfg(feature = "defi")]
64    DefiRequest(DefiRequestCommand),
65    #[cfg(feature = "defi")]
66    DefiSubscribe(DefiSubscribeCommand),
67    #[cfg(feature = "defi")]
68    DefiUnsubscribe(DefiUnsubscribeCommand),
69}
70
71impl DataCommand {
72    /// Converts the command to a dyn Any trait object for messaging.
73    pub fn as_any(&self) -> &dyn Any {
74        self
75    }
76}
77
78#[derive(Clone, Debug)]
79pub enum SubscribeCommand {
80    Data(SubscribeCustomData),
81    Instrument(SubscribeInstrument),
82    Instruments(SubscribeInstruments),
83    BookDeltas(SubscribeBookDeltas),
84    BookDepth10(SubscribeBookDepth10),
85    BookSnapshots(SubscribeBookSnapshots),
86    Quotes(SubscribeQuotes),
87    Trades(SubscribeTrades),
88    Bars(SubscribeBars),
89    MarkPrices(SubscribeMarkPrices),
90    IndexPrices(SubscribeIndexPrices),
91    FundingRates(SubscribeFundingRates),
92    InstrumentStatus(SubscribeInstrumentStatus),
93    InstrumentClose(SubscribeInstrumentClose),
94    OptionGreeks(SubscribeOptionGreeks),
95    OptionChain(SubscribeOptionChain),
96}
97
98impl PartialEq for SubscribeCommand {
99    fn eq(&self, other: &Self) -> bool {
100        self.command_id() == other.command_id()
101    }
102}
103
104impl SubscribeCommand {
105    /// Converts the command to a dyn Any trait object for messaging.
106    pub fn as_any(&self) -> &dyn Any {
107        self
108    }
109
110    pub fn command_id(&self) -> UUID4 {
111        match self {
112            Self::Data(cmd) => cmd.command_id,
113            Self::Instrument(cmd) => cmd.command_id,
114            Self::Instruments(cmd) => cmd.command_id,
115            Self::BookDeltas(cmd) => cmd.command_id,
116            Self::BookDepth10(cmd) => cmd.command_id,
117            Self::BookSnapshots(cmd) => cmd.command_id,
118            Self::Quotes(cmd) => cmd.command_id,
119            Self::Trades(cmd) => cmd.command_id,
120            Self::Bars(cmd) => cmd.command_id,
121            Self::MarkPrices(cmd) => cmd.command_id,
122            Self::IndexPrices(cmd) => cmd.command_id,
123            Self::FundingRates(cmd) => cmd.command_id,
124            Self::InstrumentStatus(cmd) => cmd.command_id,
125            Self::InstrumentClose(cmd) => cmd.command_id,
126            Self::OptionGreeks(cmd) => cmd.command_id,
127            Self::OptionChain(cmd) => cmd.command_id,
128        }
129    }
130
131    pub fn client_id(&self) -> Option<&ClientId> {
132        match self {
133            Self::Data(cmd) => cmd.client_id.as_ref(),
134            Self::Instrument(cmd) => cmd.client_id.as_ref(),
135            Self::Instruments(cmd) => cmd.client_id.as_ref(),
136            Self::BookDeltas(cmd) => cmd.client_id.as_ref(),
137            Self::BookDepth10(cmd) => cmd.client_id.as_ref(),
138            Self::BookSnapshots(cmd) => cmd.client_id.as_ref(),
139            Self::Quotes(cmd) => cmd.client_id.as_ref(),
140            Self::Trades(cmd) => cmd.client_id.as_ref(),
141            Self::MarkPrices(cmd) => cmd.client_id.as_ref(),
142            Self::IndexPrices(cmd) => cmd.client_id.as_ref(),
143            Self::FundingRates(cmd) => cmd.client_id.as_ref(),
144            Self::Bars(cmd) => cmd.client_id.as_ref(),
145            Self::InstrumentStatus(cmd) => cmd.client_id.as_ref(),
146            Self::InstrumentClose(cmd) => cmd.client_id.as_ref(),
147            Self::OptionGreeks(cmd) => cmd.client_id.as_ref(),
148            Self::OptionChain(cmd) => cmd.client_id.as_ref(),
149        }
150    }
151
152    pub fn venue(&self) -> Option<&Venue> {
153        match self {
154            Self::Data(cmd) => cmd.venue.as_ref(),
155            Self::Instrument(cmd) => cmd.venue.as_ref(),
156            Self::Instruments(cmd) => Some(&cmd.venue),
157            Self::BookDeltas(cmd) => cmd.venue.as_ref(),
158            Self::BookDepth10(cmd) => cmd.venue.as_ref(),
159            Self::BookSnapshots(cmd) => cmd.venue.as_ref(),
160            Self::Quotes(cmd) => cmd.venue.as_ref(),
161            Self::Trades(cmd) => cmd.venue.as_ref(),
162            Self::MarkPrices(cmd) => cmd.venue.as_ref(),
163            Self::IndexPrices(cmd) => cmd.venue.as_ref(),
164            Self::FundingRates(cmd) => cmd.venue.as_ref(),
165            Self::Bars(cmd) => cmd.venue.as_ref(),
166            Self::InstrumentStatus(cmd) => cmd.venue.as_ref(),
167            Self::InstrumentClose(cmd) => cmd.venue.as_ref(),
168            Self::OptionGreeks(cmd) => cmd.venue.as_ref(),
169            Self::OptionChain(cmd) => cmd.venue.as_ref(),
170        }
171    }
172
173    pub fn ts_init(&self) -> UnixNanos {
174        match self {
175            Self::Data(cmd) => cmd.ts_init,
176            Self::Instrument(cmd) => cmd.ts_init,
177            Self::Instruments(cmd) => cmd.ts_init,
178            Self::BookDeltas(cmd) => cmd.ts_init,
179            Self::BookDepth10(cmd) => cmd.ts_init,
180            Self::BookSnapshots(cmd) => cmd.ts_init,
181            Self::Quotes(cmd) => cmd.ts_init,
182            Self::Trades(cmd) => cmd.ts_init,
183            Self::MarkPrices(cmd) => cmd.ts_init,
184            Self::IndexPrices(cmd) => cmd.ts_init,
185            Self::FundingRates(cmd) => cmd.ts_init,
186            Self::Bars(cmd) => cmd.ts_init,
187            Self::InstrumentStatus(cmd) => cmd.ts_init,
188            Self::InstrumentClose(cmd) => cmd.ts_init,
189            Self::OptionGreeks(cmd) => cmd.ts_init,
190            Self::OptionChain(cmd) => cmd.ts_init,
191        }
192    }
193
194    pub fn correlation_id(&self) -> Option<UUID4> {
195        match self {
196            Self::Data(cmd) => cmd.correlation_id,
197            Self::Instrument(cmd) => cmd.correlation_id,
198            Self::Instruments(cmd) => cmd.correlation_id,
199            Self::BookDeltas(cmd) => cmd.correlation_id,
200            Self::BookDepth10(cmd) => cmd.correlation_id,
201            Self::BookSnapshots(cmd) => cmd.correlation_id,
202            Self::Quotes(cmd) => cmd.correlation_id,
203            Self::Trades(cmd) => cmd.correlation_id,
204            Self::MarkPrices(cmd) => cmd.correlation_id,
205            Self::IndexPrices(cmd) => cmd.correlation_id,
206            Self::FundingRates(cmd) => cmd.correlation_id,
207            Self::Bars(cmd) => cmd.correlation_id,
208            Self::InstrumentStatus(cmd) => cmd.correlation_id,
209            Self::InstrumentClose(cmd) => cmd.correlation_id,
210            Self::OptionGreeks(cmd) => cmd.correlation_id,
211            Self::OptionChain(_) => None,
212        }
213    }
214
215    pub fn params(&self) -> Option<&Params> {
216        match self {
217            Self::Data(cmd) => cmd.params.as_ref(),
218            Self::Instrument(cmd) => cmd.params.as_ref(),
219            Self::Instruments(cmd) => cmd.params.as_ref(),
220            Self::BookDeltas(cmd) => cmd.params.as_ref(),
221            Self::BookDepth10(cmd) => cmd.params.as_ref(),
222            Self::BookSnapshots(cmd) => cmd.params.as_ref(),
223            Self::Quotes(cmd) => cmd.params.as_ref(),
224            Self::Trades(cmd) => cmd.params.as_ref(),
225            Self::Bars(cmd) => cmd.params.as_ref(),
226            Self::MarkPrices(cmd) => cmd.params.as_ref(),
227            Self::IndexPrices(cmd) => cmd.params.as_ref(),
228            Self::FundingRates(cmd) => cmd.params.as_ref(),
229            Self::InstrumentStatus(cmd) => cmd.params.as_ref(),
230            Self::InstrumentClose(cmd) => cmd.params.as_ref(),
231            Self::OptionGreeks(cmd) => cmd.params.as_ref(),
232            Self::OptionChain(cmd) => cmd.params.as_ref(),
233        }
234    }
235}
236
237#[derive(Clone, Debug)]
238pub enum UnsubscribeCommand {
239    Data(UnsubscribeCustomData),
240    Instrument(UnsubscribeInstrument),
241    Instruments(UnsubscribeInstruments),
242    BookDeltas(UnsubscribeBookDeltas),
243    BookDepth10(UnsubscribeBookDepth10),
244    BookSnapshots(UnsubscribeBookSnapshots),
245    Quotes(UnsubscribeQuotes),
246    Trades(UnsubscribeTrades),
247    Bars(UnsubscribeBars),
248    MarkPrices(UnsubscribeMarkPrices),
249    IndexPrices(UnsubscribeIndexPrices),
250    FundingRates(UnsubscribeFundingRates),
251    InstrumentStatus(UnsubscribeInstrumentStatus),
252    InstrumentClose(UnsubscribeInstrumentClose),
253    OptionGreeks(UnsubscribeOptionGreeks),
254    OptionChain(UnsubscribeOptionChain),
255}
256
257impl PartialEq for UnsubscribeCommand {
258    fn eq(&self, other: &Self) -> bool {
259        self.command_id() == other.command_id()
260    }
261}
262
263impl UnsubscribeCommand {
264    /// Converts the command to a dyn Any trait object for messaging.
265    pub fn as_any(&self) -> &dyn Any {
266        self
267    }
268
269    pub fn command_id(&self) -> UUID4 {
270        match self {
271            Self::Data(cmd) => cmd.command_id,
272            Self::Instrument(cmd) => cmd.command_id,
273            Self::Instruments(cmd) => cmd.command_id,
274            Self::BookDeltas(cmd) => cmd.command_id,
275            Self::BookDepth10(cmd) => cmd.command_id,
276            Self::BookSnapshots(cmd) => cmd.command_id,
277            Self::Quotes(cmd) => cmd.command_id,
278            Self::Trades(cmd) => cmd.command_id,
279            Self::Bars(cmd) => cmd.command_id,
280            Self::MarkPrices(cmd) => cmd.command_id,
281            Self::IndexPrices(cmd) => cmd.command_id,
282            Self::FundingRates(cmd) => cmd.command_id,
283            Self::InstrumentStatus(cmd) => cmd.command_id,
284            Self::InstrumentClose(cmd) => cmd.command_id,
285            Self::OptionGreeks(cmd) => cmd.command_id,
286            Self::OptionChain(cmd) => cmd.command_id,
287        }
288    }
289
290    pub fn client_id(&self) -> Option<&ClientId> {
291        match self {
292            Self::Data(cmd) => cmd.client_id.as_ref(),
293            Self::Instrument(cmd) => cmd.client_id.as_ref(),
294            Self::Instruments(cmd) => cmd.client_id.as_ref(),
295            Self::BookDeltas(cmd) => cmd.client_id.as_ref(),
296            Self::BookDepth10(cmd) => cmd.client_id.as_ref(),
297            Self::BookSnapshots(cmd) => cmd.client_id.as_ref(),
298            Self::Quotes(cmd) => cmd.client_id.as_ref(),
299            Self::Trades(cmd) => cmd.client_id.as_ref(),
300            Self::Bars(cmd) => cmd.client_id.as_ref(),
301            Self::MarkPrices(cmd) => cmd.client_id.as_ref(),
302            Self::IndexPrices(cmd) => cmd.client_id.as_ref(),
303            Self::FundingRates(cmd) => cmd.client_id.as_ref(),
304            Self::InstrumentStatus(cmd) => cmd.client_id.as_ref(),
305            Self::InstrumentClose(cmd) => cmd.client_id.as_ref(),
306            Self::OptionGreeks(cmd) => cmd.client_id.as_ref(),
307            Self::OptionChain(cmd) => cmd.client_id.as_ref(),
308        }
309    }
310
311    pub fn venue(&self) -> Option<&Venue> {
312        match self {
313            Self::Data(cmd) => cmd.venue.as_ref(),
314            Self::Instrument(cmd) => cmd.venue.as_ref(),
315            Self::Instruments(cmd) => Some(&cmd.venue),
316            Self::BookDeltas(cmd) => cmd.venue.as_ref(),
317            Self::BookDepth10(cmd) => cmd.venue.as_ref(),
318            Self::BookSnapshots(cmd) => cmd.venue.as_ref(),
319            Self::Quotes(cmd) => cmd.venue.as_ref(),
320            Self::Trades(cmd) => cmd.venue.as_ref(),
321            Self::Bars(cmd) => cmd.venue.as_ref(),
322            Self::MarkPrices(cmd) => cmd.venue.as_ref(),
323            Self::IndexPrices(cmd) => cmd.venue.as_ref(),
324            Self::FundingRates(cmd) => cmd.venue.as_ref(),
325            Self::InstrumentStatus(cmd) => cmd.venue.as_ref(),
326            Self::InstrumentClose(cmd) => cmd.venue.as_ref(),
327            Self::OptionGreeks(cmd) => cmd.venue.as_ref(),
328            Self::OptionChain(cmd) => cmd.venue.as_ref(),
329        }
330    }
331
332    pub fn ts_init(&self) -> UnixNanos {
333        match self {
334            Self::Data(cmd) => cmd.ts_init,
335            Self::Instrument(cmd) => cmd.ts_init,
336            Self::Instruments(cmd) => cmd.ts_init,
337            Self::BookDeltas(cmd) => cmd.ts_init,
338            Self::BookDepth10(cmd) => cmd.ts_init,
339            Self::BookSnapshots(cmd) => cmd.ts_init,
340            Self::Quotes(cmd) => cmd.ts_init,
341            Self::Trades(cmd) => cmd.ts_init,
342            Self::MarkPrices(cmd) => cmd.ts_init,
343            Self::IndexPrices(cmd) => cmd.ts_init,
344            Self::FundingRates(cmd) => cmd.ts_init,
345            Self::Bars(cmd) => cmd.ts_init,
346            Self::InstrumentStatus(cmd) => cmd.ts_init,
347            Self::InstrumentClose(cmd) => cmd.ts_init,
348            Self::OptionGreeks(cmd) => cmd.ts_init,
349            Self::OptionChain(cmd) => cmd.ts_init,
350        }
351    }
352
353    pub fn correlation_id(&self) -> Option<UUID4> {
354        match self {
355            Self::Data(cmd) => cmd.correlation_id,
356            Self::Instrument(cmd) => cmd.correlation_id,
357            Self::Instruments(cmd) => cmd.correlation_id,
358            Self::BookDeltas(cmd) => cmd.correlation_id,
359            Self::BookDepth10(cmd) => cmd.correlation_id,
360            Self::BookSnapshots(cmd) => cmd.correlation_id,
361            Self::Quotes(cmd) => cmd.correlation_id,
362            Self::Trades(cmd) => cmd.correlation_id,
363            Self::MarkPrices(cmd) => cmd.correlation_id,
364            Self::IndexPrices(cmd) => cmd.correlation_id,
365            Self::FundingRates(cmd) => cmd.correlation_id,
366            Self::Bars(cmd) => cmd.correlation_id,
367            Self::InstrumentStatus(cmd) => cmd.correlation_id,
368            Self::InstrumentClose(cmd) => cmd.correlation_id,
369            Self::OptionGreeks(cmd) => cmd.correlation_id,
370            Self::OptionChain(_) => None,
371        }
372    }
373}
374
375fn check_client_id_or_venue(client_id: &Option<ClientId>, venue: &Option<Venue>) {
376    assert!(
377        client_id.is_some() || venue.is_some(),
378        "Both `client_id` and `venue` were None"
379    );
380}
381
382#[derive(Clone, Debug)]
383pub enum RequestCommand {
384    Data(RequestCustomData),
385    Instrument(RequestInstrument),
386    Instruments(RequestInstruments),
387    BookSnapshot(RequestBookSnapshot),
388    BookDepth(RequestBookDepth),
389    Quotes(RequestQuotes),
390    Trades(RequestTrades),
391    FundingRates(RequestFundingRates),
392    ForwardPrices(RequestForwardPrices),
393    Bars(RequestBars),
394}
395
396impl PartialEq for RequestCommand {
397    fn eq(&self, other: &Self) -> bool {
398        self.request_id() == other.request_id()
399    }
400}
401
402impl RequestCommand {
403    /// Converts the command to a dyn Any trait object for messaging.
404    pub fn as_any(&self) -> &dyn Any {
405        self
406    }
407
408    pub fn request_id(&self) -> &UUID4 {
409        match self {
410            Self::Data(cmd) => &cmd.request_id,
411            Self::Instrument(cmd) => &cmd.request_id,
412            Self::Instruments(cmd) => &cmd.request_id,
413            Self::BookSnapshot(cmd) => &cmd.request_id,
414            Self::BookDepth(cmd) => &cmd.request_id,
415            Self::Quotes(cmd) => &cmd.request_id,
416            Self::Trades(cmd) => &cmd.request_id,
417            Self::FundingRates(cmd) => &cmd.request_id,
418            Self::ForwardPrices(cmd) => &cmd.request_id,
419            Self::Bars(cmd) => &cmd.request_id,
420        }
421    }
422
423    pub fn client_id(&self) -> Option<&ClientId> {
424        match self {
425            Self::Data(cmd) => Some(&cmd.client_id),
426            Self::Instrument(cmd) => cmd.client_id.as_ref(),
427            Self::Instruments(cmd) => cmd.client_id.as_ref(),
428            Self::BookSnapshot(cmd) => cmd.client_id.as_ref(),
429            Self::BookDepth(cmd) => cmd.client_id.as_ref(),
430            Self::Quotes(cmd) => cmd.client_id.as_ref(),
431            Self::Trades(cmd) => cmd.client_id.as_ref(),
432            Self::FundingRates(cmd) => cmd.client_id.as_ref(),
433            Self::ForwardPrices(cmd) => cmd.client_id.as_ref(),
434            Self::Bars(cmd) => cmd.client_id.as_ref(),
435        }
436    }
437
438    pub fn venue(&self) -> Option<&Venue> {
439        match self {
440            Self::Data(_) => None,
441            Self::Instrument(cmd) => Some(&cmd.instrument_id.venue),
442            Self::Instruments(cmd) => cmd.venue.as_ref(),
443            Self::BookSnapshot(cmd) => Some(&cmd.instrument_id.venue),
444            Self::BookDepth(cmd) => Some(&cmd.instrument_id.venue),
445            Self::Quotes(cmd) => Some(&cmd.instrument_id.venue),
446            Self::Trades(cmd) => Some(&cmd.instrument_id.venue),
447            Self::FundingRates(cmd) => Some(&cmd.instrument_id.venue),
448            Self::ForwardPrices(cmd) => Some(&cmd.venue),
449            // TODO: Extract the below somewhere
450            Self::Bars(cmd) => match &cmd.bar_type {
451                BarType::Standard { instrument_id, .. } => Some(&instrument_id.venue),
452                BarType::Composite { instrument_id, .. } => Some(&instrument_id.venue),
453            },
454        }
455    }
456
457    pub fn ts_init(&self) -> UnixNanos {
458        match self {
459            Self::Data(cmd) => cmd.ts_init,
460            Self::Instrument(cmd) => cmd.ts_init,
461            Self::Instruments(cmd) => cmd.ts_init,
462            Self::BookSnapshot(cmd) => cmd.ts_init,
463            Self::BookDepth(cmd) => cmd.ts_init,
464            Self::Quotes(cmd) => cmd.ts_init,
465            Self::Trades(cmd) => cmd.ts_init,
466            Self::FundingRates(cmd) => cmd.ts_init,
467            Self::ForwardPrices(cmd) => cmd.ts_init,
468            Self::Bars(cmd) => cmd.ts_init,
469        }
470    }
471}
472
473#[derive(Clone, Debug)]
474pub enum DataResponse {
475    Data(CustomDataResponse),
476    Instrument(Box<InstrumentResponse>),
477    Instruments(InstrumentsResponse),
478    Book(BookResponse),
479    Quotes(QuotesResponse),
480    Trades(TradesResponse),
481    FundingRates(FundingRatesResponse),
482    ForwardPrices(ForwardPricesResponse),
483    Bars(BarsResponse),
484}
485
486impl DataResponse {
487    /// Converts the command to a dyn Any trait object for messaging.
488    pub fn as_any(&self) -> &dyn Any {
489        self
490    }
491
492    pub fn correlation_id(&self) -> &UUID4 {
493        match self {
494            Self::Data(resp) => &resp.correlation_id,
495            Self::Instrument(resp) => &resp.correlation_id,
496            Self::Instruments(resp) => &resp.correlation_id,
497            Self::Book(resp) => &resp.correlation_id,
498            Self::Quotes(resp) => &resp.correlation_id,
499            Self::Trades(resp) => &resp.correlation_id,
500            Self::FundingRates(resp) => &resp.correlation_id,
501            Self::ForwardPrices(resp) => &resp.correlation_id,
502            Self::Bars(resp) => &resp.correlation_id,
503        }
504    }
505}
506
507pub type Payload = Arc<dyn Any + Send + Sync>;