1use std::{
2 collections::{HashMap, HashSet},
3 sync::Arc,
4};
5
6use chrono::NaiveDateTime;
7use deepsize::{Context, DeepSizeOf};
8use num_bigint::BigUint;
9use serde::{Deserialize, Serialize};
10
11use crate::{
12 dto,
13 models::{
14 token::Token, Address, AttrStoreKey, Balance, Chain, ChangeType, ComponentId, MergeError,
15 StoreVal, TxHash,
16 },
17 Bytes,
18};
19
20#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
32pub struct ProtocolComponent<Token: Into<Address> + Clone = Address> {
33 pub id: ComponentId,
34 pub protocol_system: String,
35 pub protocol_type_name: String,
36 pub chain: Chain,
37 pub tokens: Vec<Token>,
38 pub contract_addresses: Vec<Address>,
39 pub static_attributes: HashMap<AttrStoreKey, StoreVal>,
40 pub change: ChangeType,
41 pub creation_tx: TxHash,
42 pub created_at: NaiveDateTime,
43}
44
45impl<T> ProtocolComponent<T>
46where
47 T: Into<Address> + Clone,
48{
49 #[allow(clippy::too_many_arguments)]
50 pub fn new(
51 id: &str,
52 protocol_system: &str,
53 protocol_type_name: &str,
54 chain: Chain,
55 tokens: Vec<T>,
56 contract_addresses: Vec<Address>,
57 static_attributes: HashMap<AttrStoreKey, StoreVal>,
58 change: ChangeType,
59 creation_tx: TxHash,
60 created_at: NaiveDateTime,
61 ) -> Self {
62 Self {
63 id: id.to_string(),
64 protocol_system: protocol_system.to_string(),
65 protocol_type_name: protocol_type_name.to_string(),
66 chain,
67 tokens,
68 contract_addresses,
69 static_attributes,
70 change,
71 creation_tx,
72 created_at,
73 }
74 }
75}
76
77impl ProtocolComponent<Arc<Token>> {
78 pub fn get_token(&self, address: &Address) -> Option<Arc<Token>> {
79 self.tokens
80 .iter()
81 .find(|t| &t.address == address)
82 .map(Arc::clone)
83 }
84}
85
86impl DeepSizeOf for ProtocolComponent {
87 fn deep_size_of_children(&self, ctx: &mut Context) -> usize {
88 self.id.deep_size_of_children(ctx) +
89 self.protocol_system
90 .deep_size_of_children(ctx) +
91 self.protocol_type_name
92 .deep_size_of_children(ctx) +
93 self.chain.deep_size_of_children(ctx) +
94 self.tokens.deep_size_of_children(ctx) +
95 self.contract_addresses
96 .deep_size_of_children(ctx) +
97 self.static_attributes
98 .deep_size_of_children(ctx) +
99 self.change.deep_size_of_children(ctx) +
100 self.creation_tx
101 .deep_size_of_children(ctx)
102 }
103}
104
105#[derive(Debug, Clone, PartialEq)]
106pub struct ProtocolComponentState {
107 pub component_id: ComponentId,
108 pub attributes: HashMap<AttrStoreKey, StoreVal>,
109 pub balances: HashMap<Address, Balance>,
111}
112
113impl ProtocolComponentState {
114 pub fn new(
115 component_id: &str,
116 attributes: HashMap<AttrStoreKey, StoreVal>,
117 balances: HashMap<Address, Balance>,
118 ) -> Self {
119 Self { component_id: component_id.to_string(), attributes, balances }
120 }
121
122 pub fn apply_state_delta(
126 &mut self,
127 delta: &ProtocolComponentStateDelta,
128 ) -> Result<(), MergeError> {
129 if self.component_id != delta.component_id {
130 return Err(MergeError::IdMismatch(
131 "ProtocolComponentStates".to_string(),
132 self.component_id.clone(),
133 delta.component_id.clone(),
134 ));
135 }
136 self.attributes
137 .extend(delta.updated_attributes.clone());
138
139 self.attributes
140 .retain(|attr, _| !delta.deleted_attributes.contains(attr));
141
142 Ok(())
143 }
144
145 pub fn apply_balance_delta(
149 &mut self,
150 delta: &HashMap<Bytes, ComponentBalance>,
151 ) -> Result<(), MergeError> {
152 self.balances.extend(
153 delta
154 .iter()
155 .map(|(k, v)| (k.clone(), v.balance.clone())),
156 );
157
158 Ok(())
159 }
160}
161
162#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, DeepSizeOf)]
163pub struct ProtocolComponentStateDelta {
164 pub component_id: ComponentId,
165 pub updated_attributes: HashMap<AttrStoreKey, StoreVal>,
166 pub deleted_attributes: HashSet<AttrStoreKey>,
167 #[serde(skip)]
171 pub created_attributes: HashSet<AttrStoreKey>,
172}
173
174impl ProtocolComponentStateDelta {
175 pub fn new(
176 component_id: &str,
177 updated_attributes: HashMap<AttrStoreKey, StoreVal>,
178 deleted_attributes: HashSet<AttrStoreKey>,
179 ) -> Self {
180 Self {
181 component_id: component_id.to_string(),
182 updated_attributes,
183 deleted_attributes,
184 created_attributes: HashSet::new(),
185 }
186 }
187
188 pub fn merge(&mut self, other: ProtocolComponentStateDelta) -> Result<(), MergeError> {
200 if self.component_id != other.component_id {
201 return Err(MergeError::IdMismatch(
202 "ProtocolComponentStateDeltas".to_string(),
203 self.component_id.clone(),
204 other.component_id.clone(),
205 ));
206 }
207 for attr in &other.deleted_attributes {
208 self.updated_attributes.remove(attr);
209 self.created_attributes.remove(attr);
210 }
211 for attr in other.updated_attributes.keys() {
212 self.deleted_attributes.remove(attr);
213 }
214 self.updated_attributes
215 .extend(other.updated_attributes);
216 self.deleted_attributes
217 .extend(other.deleted_attributes);
218 self.created_attributes
219 .extend(other.created_attributes);
220 Ok(())
221 }
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, DeepSizeOf)]
225pub struct ComponentBalance {
226 pub token: Address,
227 pub balance: Balance,
228 pub balance_float: f64,
229 pub modify_tx: TxHash,
230 pub component_id: ComponentId,
231}
232
233impl ComponentBalance {
234 pub fn new(
235 token: Address,
236 new_balance: Balance,
237 balance_float: f64,
238 modify_tx: TxHash,
239 component_id: &str,
240 ) -> Self {
241 Self {
242 token,
243 balance: new_balance,
244 balance_float,
245 modify_tx,
246 component_id: component_id.to_string(),
247 }
248 }
249}
250
251#[derive(Debug, Clone)]
255pub struct QualityRange {
256 pub min: Option<i32>,
257 pub max: Option<i32>,
258}
259
260impl QualityRange {
261 pub fn new(min: i32, max: i32) -> Self {
262 Self { min: Some(min), max: Some(max) }
263 }
264
265 pub fn min_only(min: i32) -> Self {
266 Self { min: Some(min), max: None }
267 }
268
269 #[allow(non_snake_case)]
270 pub fn None() -> Self {
271 Self { min: None, max: None }
272 }
273}
274
275pub struct GetAmountOutParams {
276 pub amount_in: BigUint,
277 pub token_in: Bytes,
278 pub token_out: Bytes,
279 pub sender: Bytes,
280 pub receiver: Bytes,
281}
282
283impl From<dto::ProtocolStateDelta> for ProtocolComponentStateDelta {
284 fn from(value: dto::ProtocolStateDelta) -> Self {
285 Self {
286 component_id: value.component_id,
287 updated_attributes: value.updated_attributes,
288 deleted_attributes: value.deleted_attributes,
289 created_attributes: HashSet::new(),
290 }
291 }
292}
293
294impl From<dto::ComponentBalance> for ComponentBalance {
295 fn from(value: dto::ComponentBalance) -> Self {
296 Self {
297 token: value.token,
298 balance: value.balance,
299 balance_float: value.balance_float,
300 modify_tx: value.modify_tx,
301 component_id: value.component_id,
302 }
303 }
304}
305
306impl From<dto::ProtocolComponent> for ProtocolComponent {
307 fn from(value: dto::ProtocolComponent) -> Self {
308 Self {
309 id: value.id,
310 protocol_system: value.protocol_system,
311 protocol_type_name: value.protocol_type_name,
312 chain: value.chain.into(),
313 tokens: value.tokens,
314 contract_addresses: value.contract_ids,
315 static_attributes: value.static_attributes,
316 change: value.change.into(),
317 creation_tx: value.creation_tx,
318 created_at: value.created_at,
319 }
320 }
321}
322
323impl From<dto::ResponseProtocolState> for ProtocolComponentState {
324 fn from(value: dto::ResponseProtocolState) -> Self {
325 Self {
326 component_id: value.component_id,
327 attributes: value.attributes,
328 balances: value.balances,
329 }
330 }
331}
332
333#[cfg(test)]
334mod test {
335 use super::*;
336
337 fn create_state(id: String) -> ProtocolComponentStateDelta {
338 let attributes1: HashMap<String, Bytes> = vec![
339 ("reserve1".to_owned(), Bytes::from(1000u64).lpad(32, 0)),
340 ("reserve2".to_owned(), Bytes::from(500u64).lpad(32, 0)),
341 ("static_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
342 ]
343 .into_iter()
344 .collect();
345 ProtocolComponentStateDelta {
346 component_id: id,
347 updated_attributes: attributes1,
348 deleted_attributes: HashSet::new(),
349 ..Default::default()
350 }
351 }
352
353 #[test]
354 fn test_merge_protocol_state_updates() {
355 let mut state_1 = create_state("State1".to_owned());
356 state_1
357 .updated_attributes
358 .insert("to_be_removed".to_owned(), Bytes::from(1u64).lpad(32, 0));
359 state_1.deleted_attributes = vec!["to_add_back".to_owned()]
360 .into_iter()
361 .collect();
362
363 let attributes2: HashMap<String, Bytes> = vec![
364 ("reserve1".to_owned(), Bytes::from(900u64).lpad(32, 0)),
365 ("reserve2".to_owned(), Bytes::from(550u64).lpad(32, 0)),
366 ("new_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
367 ("to_add_back".to_owned(), Bytes::from(200u64).lpad(32, 0)),
368 ]
369 .into_iter()
370 .collect();
371 let del_attributes2: HashSet<String> = vec!["to_be_removed".to_owned()]
372 .into_iter()
373 .collect();
374 let mut state_2 = create_state("State1".to_owned());
375 state_2.updated_attributes = attributes2;
376 state_2.deleted_attributes = del_attributes2;
377
378 let res = state_1.merge(state_2);
379
380 assert!(res.is_ok());
381 let expected_attributes: HashMap<String, Bytes> = vec![
382 ("reserve1".to_owned(), Bytes::from(900u64).lpad(32, 0)),
383 ("reserve2".to_owned(), Bytes::from(550u64).lpad(32, 0)),
384 ("static_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
385 ("new_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
386 ("to_add_back".to_owned(), Bytes::from(200u64).lpad(32, 0)),
387 ]
388 .into_iter()
389 .collect();
390 assert_eq!(state_1.updated_attributes, expected_attributes);
391 let expected_del_attributes: HashSet<String> = vec!["to_be_removed".to_owned()]
392 .into_iter()
393 .collect();
394 assert_eq!(state_1.deleted_attributes, expected_del_attributes);
395 }
396
397 #[test]
398 fn test_merge_protocol_state_update_wrong_id() {
399 let mut state1 = create_state("State1".to_owned());
400 let state2 = create_state("State2".to_owned());
401
402 let res = state1.merge(state2);
403
404 assert_eq!(
405 res,
406 Err(MergeError::IdMismatch(
407 "ProtocolComponentStateDeltas".to_string(),
408 "State1".to_string(),
409 "State2".to_string(),
410 ))
411 );
412 }
413}