Skip to main content

tycho_simulation/protocol/
models.rs

1//! Pair Properties and ProtocolState
2//!
3//! This module contains the `ProtocolComponent` struct, which represents the
4//! properties of a trading pair. It also contains the `Pair` struct, which
5//! represents a trading pair with its properties and corresponding state.
6//!
7//! Additionally, it contains the `GetAmountOutResult` struct, which
8//! represents the result of getting the amount out of a trading pair.
9//!
10//! The `ProtocolComponent` struct has two fields: `address` and `tokens`.
11//! `address` is the address of the trading pair and `tokens` is a vector
12//! of `ERC20Token` representing the tokens of the trading pair.
13//!
14//! Generally this struct contains immutable properties of the pair. These
15//! are attributes that will never change - not even through governance.
16//!
17//! This is in contrast to `ProtocolState`, which includes ideally only
18//! attributes that can change.
19//!
20//! The `Pair` struct combines the former two: `ProtocolComponent` and
21//! `ProtocolState` into a single struct.
22//!
23//! # Note:
24//! It's worth emphasizing that although the term "pair" used in this
25//! module refers to a trading pair, it does not necessarily imply two
26//! tokens only. Some pairs might have more than two tokens.
27use std::{collections::HashMap, default::Default, future::Future};
28
29use chrono::NaiveDateTime;
30use serde::{Deserialize, Serialize};
31use tokio::sync::watch;
32use tycho_client::feed::{HeaderLike, SynchronizerState};
33use tycho_common::{
34    models::{token::Token, Chain},
35    simulation::protocol_sim::ProtocolSim,
36    Bytes,
37};
38
39use crate::evm::override_stream::OverrideSnapshot;
40
41/// Context struct containing attributes for decoders
42///
43/// This struct can be extended to include additional attributes for other decoders in the future
44#[derive(Debug, Clone)]
45pub struct DecoderContext {
46    pub adapter_path: Option<String>,
47    pub vm_traces: Option<bool>,
48    /// Live per-block VM state override channel, wired into the pool at construction time.
49    ///
50    /// Set internally by the decoder from its registered override providers; not part of the
51    /// public API. External consumers never set this — overrides are fully handled by the library.
52    pub(crate) live_override: Option<watch::Receiver<OverrideSnapshot>>,
53}
54
55impl DecoderContext {
56    pub fn new() -> Self {
57        Self { adapter_path: None, vm_traces: None, live_override: None }
58    }
59
60    pub fn vm_adapter_path<S: Into<String>>(mut self, path: S) -> Self {
61        self.adapter_path = Some(path.into());
62        self
63    }
64
65    pub fn vm_traces(mut self, trace: bool) -> Self {
66        self.vm_traces = Some(trace);
67        self
68    }
69}
70
71impl Default for DecoderContext {
72    fn default() -> Self {
73        Self::new()
74    }
75}
76
77/// ProtocolComponent struct represents the properties of a trading pair
78///
79/// # Fields
80///
81/// * `address`: String, the address of the trading pair
82/// * `tokens`: `Vec<ERC20Token>`, the tokens of the trading pair
83#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
84pub struct ProtocolComponent {
85    #[deprecated(since = "0.73.0", note = "Use `id` instead")]
86    pub address: Bytes,
87    pub id: Bytes,
88    pub tokens: Vec<Token>,
89    pub protocol_system: String,
90    pub protocol_type_name: String,
91    pub chain: Chain,
92    pub contract_ids: Vec<Bytes>,
93    pub static_attributes: HashMap<String, Bytes>,
94    pub creation_tx: Bytes,
95    pub created_at: NaiveDateTime,
96}
97
98impl ProtocolComponent {
99    #[allow(deprecated)]
100    #[allow(clippy::too_many_arguments)]
101    pub fn new(
102        id: Bytes,
103        protocol_system: String,
104        protocol_type_name: String,
105        chain: Chain,
106        tokens: Vec<Token>,
107        contract_ids: Vec<Bytes>,
108        static_attributes: HashMap<String, Bytes>,
109        creation_tx: Bytes,
110        created_at: NaiveDateTime,
111    ) -> Self {
112        ProtocolComponent {
113            address: Default::default(),
114            id,
115            tokens,
116            protocol_system,
117            protocol_type_name,
118            chain,
119            contract_ids,
120            static_attributes,
121            creation_tx,
122            created_at,
123        }
124    }
125
126    pub fn from_with_tokens(
127        core_model: tycho_common::models::protocol::ProtocolComponent,
128        tokens: Vec<Token>,
129    ) -> Self {
130        let id = Bytes::from(core_model.id.as_str());
131        ProtocolComponent::new(
132            id.clone(),
133            core_model.protocol_system,
134            core_model.protocol_type_name,
135            core_model.chain,
136            tokens,
137            core_model.contract_addresses,
138            core_model.static_attributes,
139            core_model.creation_tx,
140            core_model.created_at,
141        )
142    }
143}
144
145impl From<ProtocolComponent> for tycho_common::models::protocol::ProtocolComponent {
146    fn from(component: ProtocolComponent) -> Self {
147        tycho_common::models::protocol::ProtocolComponent {
148            id: hex::encode(component.id),
149            protocol_system: component.protocol_system,
150            protocol_type_name: component.protocol_type_name,
151            chain: component.chain,
152            tokens: component
153                .tokens
154                .into_iter()
155                .map(|t| t.address)
156                .collect(),
157            static_attributes: component.static_attributes,
158            change: Default::default(),
159            creation_tx: component.creation_tx,
160            created_at: component.created_at,
161            contract_addresses: component.contract_ids,
162        }
163    }
164}
165
166pub trait TryFromWithBlock<T, H>
167where
168    H: HeaderLike,
169{
170    type Error;
171
172    fn try_from_with_header(
173        value: T,
174        block: H,
175        account_balances: &HashMap<Bytes, HashMap<Bytes, Bytes>>,
176        all_tokens: &HashMap<Bytes, Token>,
177        decoder_context: &DecoderContext,
178    ) -> impl Future<Output = Result<Self, Self::Error>> + Send + Sync
179    where
180        Self: Sized;
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct Update {
185    pub block_number_or_timestamp: u64,
186    /// True when this update is for a partial (pre-confirmation) block, false for full blocks.
187    #[serde(default)]
188    pub is_partial: bool,
189    /// Synchronization state per protocol
190    pub sync_states: HashMap<String, SynchronizerState>,
191    /// The new and updated states of this block.
192    /// VM-backed states that can't be serialized are silently skipped during
193    /// serialization and will be absent after a roundtrip.
194    #[serde(with = "crate::serde_helpers::protocol_states")]
195    pub states: HashMap<String, Box<dyn ProtocolSim>>,
196    /// The new pairs that were added in this block
197    pub new_pairs: HashMap<String, ProtocolComponent>,
198    /// The pairs that were removed in this block
199    pub removed_pairs: HashMap<String, ProtocolComponent>,
200}
201
202impl Update {
203    pub fn new(
204        block_number: u64,
205        states: HashMap<String, Box<dyn ProtocolSim>>,
206        new_pairs: HashMap<String, ProtocolComponent>,
207    ) -> Self {
208        Update {
209            block_number_or_timestamp: block_number,
210            is_partial: false,
211            sync_states: HashMap::new(),
212            states,
213            new_pairs,
214            removed_pairs: HashMap::new(),
215        }
216    }
217
218    pub fn set_is_partial(mut self, is_partial: bool) -> Self {
219        self.is_partial = is_partial;
220        self
221    }
222
223    pub fn set_removed_pairs(mut self, pairs: HashMap<String, ProtocolComponent>) -> Self {
224        self.removed_pairs = pairs;
225        self
226    }
227
228    pub fn set_sync_states(mut self, sync_states: HashMap<String, SynchronizerState>) -> Self {
229        self.sync_states = sync_states;
230        self
231    }
232
233    pub fn merge(mut self, other: Update) -> Self {
234        self.states.extend(other.states);
235        self.new_pairs.extend(other.new_pairs);
236        self.removed_pairs
237            .extend(other.removed_pairs);
238        self
239    }
240}