1use std::collections::{hash_map::Entry, HashMap, HashSet};
2
3use chrono::NaiveDateTime;
4use deepsize::{Context, DeepSizeOf};
5use num_bigint::BigUint;
6use serde::{Deserialize, Serialize};
7use tracing::warn;
8
9use crate::{
10 models::{
11 blockchain::Transaction, Address, AttrStoreKey, Balance, Chain, ChangeType, ComponentId,
12 MergeError, StoreVal, TxHash,
13 },
14 Bytes,
15};
16
17#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
29pub struct ProtocolComponent {
30 pub id: ComponentId,
31 pub protocol_system: String,
32 pub protocol_type_name: String,
33 pub chain: Chain,
34 pub tokens: Vec<Address>,
35 pub contract_addresses: Vec<Address>,
36 pub static_attributes: HashMap<AttrStoreKey, StoreVal>,
37 pub change: ChangeType,
38 pub creation_tx: TxHash,
39 pub created_at: NaiveDateTime,
40}
41
42impl ProtocolComponent {
43 #[allow(clippy::too_many_arguments)]
44 pub fn new(
45 id: &str,
46 protocol_system: &str,
47 protocol_type_name: &str,
48 chain: Chain,
49 tokens: Vec<Address>,
50 contract_addresses: Vec<Address>,
51 static_attributes: HashMap<AttrStoreKey, StoreVal>,
52 change: ChangeType,
53 creation_tx: TxHash,
54 created_at: NaiveDateTime,
55 ) -> Self {
56 Self {
57 id: id.to_string(),
58 protocol_system: protocol_system.to_string(),
59 protocol_type_name: protocol_type_name.to_string(),
60 chain,
61 tokens,
62 contract_addresses,
63 static_attributes,
64 change,
65 creation_tx,
66 created_at,
67 }
68 }
69}
70
71impl DeepSizeOf for ProtocolComponent {
72 fn deep_size_of_children(&self, ctx: &mut Context) -> usize {
73 self.id.deep_size_of_children(ctx) +
74 self.protocol_system
75 .deep_size_of_children(ctx) +
76 self.protocol_type_name
77 .deep_size_of_children(ctx) +
78 self.chain.deep_size_of_children(ctx) +
79 self.tokens.deep_size_of_children(ctx) +
80 self.contract_addresses
81 .deep_size_of_children(ctx) +
82 self.static_attributes
83 .deep_size_of_children(ctx) +
84 self.change.deep_size_of_children(ctx) +
85 self.creation_tx
86 .deep_size_of_children(ctx)
87 }
88}
89
90#[derive(Debug, Clone, PartialEq)]
91pub struct ProtocolComponentState {
92 pub component_id: ComponentId,
93 pub attributes: HashMap<AttrStoreKey, StoreVal>,
94 pub balances: HashMap<Address, Balance>,
96}
97
98impl ProtocolComponentState {
99 pub fn new(
100 component_id: &str,
101 attributes: HashMap<AttrStoreKey, StoreVal>,
102 balances: HashMap<Address, Balance>,
103 ) -> Self {
104 Self { component_id: component_id.to_string(), attributes, balances }
105 }
106
107 pub fn apply_state_delta(
111 &mut self,
112 delta: &ProtocolComponentStateDelta,
113 ) -> Result<(), MergeError> {
114 if self.component_id != delta.component_id {
115 return Err(MergeError::IdMismatch(
116 "ProtocolComponentStates".to_string(),
117 self.component_id.clone(),
118 delta.component_id.clone(),
119 ));
120 }
121 self.attributes
122 .extend(delta.updated_attributes.clone());
123
124 self.attributes
125 .retain(|attr, _| !delta.deleted_attributes.contains(attr));
126
127 Ok(())
128 }
129
130 pub fn apply_balance_delta(
134 &mut self,
135 delta: &HashMap<Bytes, ComponentBalance>,
136 ) -> Result<(), MergeError> {
137 self.balances.extend(
138 delta
139 .iter()
140 .map(|(k, v)| (k.clone(), v.balance.clone())),
141 );
142
143 Ok(())
144 }
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, DeepSizeOf)]
148pub struct ProtocolComponentStateDelta {
149 pub component_id: ComponentId,
150 pub updated_attributes: HashMap<AttrStoreKey, StoreVal>,
151 pub deleted_attributes: HashSet<AttrStoreKey>,
152}
153
154impl ProtocolComponentStateDelta {
155 pub fn new(
156 component_id: &str,
157 updated_attributes: HashMap<AttrStoreKey, StoreVal>,
158 deleted_attributes: HashSet<AttrStoreKey>,
159 ) -> Self {
160 Self { component_id: component_id.to_string(), updated_attributes, deleted_attributes }
161 }
162
163 pub fn merge(&mut self, other: ProtocolComponentStateDelta) -> Result<(), MergeError> {
175 if self.component_id != other.component_id {
176 return Err(MergeError::IdMismatch(
177 "ProtocolComponentStateDeltas".to_string(),
178 self.component_id.clone(),
179 other.component_id.clone(),
180 ));
181 }
182 for attr in &other.deleted_attributes {
183 self.updated_attributes.remove(attr);
184 }
185 for attr in other.updated_attributes.keys() {
186 self.deleted_attributes.remove(attr);
187 }
188 self.updated_attributes
189 .extend(other.updated_attributes);
190 self.deleted_attributes
191 .extend(other.deleted_attributes);
192 Ok(())
193 }
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, DeepSizeOf)]
197pub struct ComponentBalance {
198 pub token: Address,
199 pub balance: Balance,
200 pub balance_float: f64,
201 pub modify_tx: TxHash,
202 pub component_id: ComponentId,
203}
204
205impl ComponentBalance {
206 pub fn new(
207 token: Address,
208 new_balance: Balance,
209 balance_float: f64,
210 modify_tx: TxHash,
211 component_id: &str,
212 ) -> Self {
213 Self {
214 token,
215 balance: new_balance,
216 balance_float,
217 modify_tx,
218 component_id: component_id.to_string(),
219 }
220 }
221}
222
223#[derive(Debug, Clone)]
227pub struct QualityRange {
228 pub min: Option<i32>,
229 pub max: Option<i32>,
230}
231
232impl QualityRange {
233 pub fn new(min: i32, max: i32) -> Self {
234 Self { min: Some(min), max: Some(max) }
235 }
236
237 pub fn min_only(min: i32) -> Self {
238 Self { min: Some(min), max: None }
239 }
240
241 #[allow(non_snake_case)]
242 pub fn None() -> Self {
243 Self { min: None, max: None }
244 }
245}
246
247#[derive(Debug, Clone, PartialEq, Default)]
249pub struct ProtocolChangesWithTx {
250 pub new_protocol_components: HashMap<ComponentId, ProtocolComponent>,
251 pub protocol_states: HashMap<ComponentId, ProtocolComponentStateDelta>,
252 pub balance_changes: HashMap<ComponentId, HashMap<Bytes, ComponentBalance>>,
253 pub tx: Transaction,
254}
255
256impl ProtocolChangesWithTx {
257 pub fn merge(&mut self, other: ProtocolChangesWithTx) -> Result<(), MergeError> {
274 if self.tx.block_hash != other.tx.block_hash {
275 return Err(MergeError::BlockMismatch(
276 "ProtocolChangesWithTx".to_string(),
277 self.tx.block_hash.clone(),
278 other.tx.block_hash,
279 ));
280 }
281 if self.tx.hash == other.tx.hash {
282 return Err(MergeError::SameTransaction(
283 "ProtocolChangesWithTx".to_string(),
284 other.tx.hash,
285 ));
286 }
287 if self.tx.index > other.tx.index {
288 return Err(MergeError::TransactionOrderError(
289 "ProtocolChangesWithTx".to_string(),
290 self.tx.index,
291 other.tx.index,
292 ));
293 }
294 self.tx = other.tx;
295 for (key, value) in other.protocol_states {
297 match self.protocol_states.entry(key) {
298 Entry::Occupied(mut entry) => {
299 entry.get_mut().merge(value)?;
300 }
301 Entry::Vacant(entry) => {
302 entry.insert(value);
303 }
304 }
305 }
306
307 for (component_id, balance_changes) in other.balance_changes {
309 let token_balances = self
310 .balance_changes
311 .entry(component_id)
312 .or_default();
313 for (token, balance) in balance_changes {
314 token_balances.insert(token, balance);
315 }
316 }
317
318 for (key, value) in other.new_protocol_components {
322 match self.new_protocol_components.entry(key) {
323 Entry::Occupied(mut entry) => {
324 warn!(
325 "Overwriting new protocol component for id {} with a new one. This should never happen! Please check logic",
326 entry.get().id
327 );
328 entry.insert(value);
329 }
330 Entry::Vacant(entry) => {
331 entry.insert(value);
332 }
333 }
334 }
335
336 Ok(())
337 }
338}
339
340pub struct GetAmountOutParams {
341 pub amount_in: BigUint,
342 pub token_in: Bytes,
343 pub token_out: Bytes,
344 pub sender: Bytes,
345 pub receiver: Bytes,
346}
347
348#[cfg(test)]
349mod test {
350 use rstest::rstest;
351
352 use super::*;
353 use crate::models::blockchain::fixtures as block_fixtures;
354
355 const HASH_256_0: &str = "0x0000000000000000000000000000000000000000000000000000000000000000";
356 const HASH_256_1: &str = "0x0000000000000000000000000000000000000000000000000000000000000001";
357
358 fn create_state(id: String) -> ProtocolComponentStateDelta {
359 let attributes1: HashMap<String, Bytes> = vec![
360 ("reserve1".to_owned(), Bytes::from(1000u64).lpad(32, 0)),
361 ("reserve2".to_owned(), Bytes::from(500u64).lpad(32, 0)),
362 ("static_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
363 ]
364 .into_iter()
365 .collect();
366 ProtocolComponentStateDelta {
367 component_id: id,
368 updated_attributes: attributes1,
369 deleted_attributes: HashSet::new(),
370 }
371 }
372
373 #[test]
374 fn test_merge_protocol_state_updates() {
375 let mut state_1 = create_state("State1".to_owned());
376 state_1
377 .updated_attributes
378 .insert("to_be_removed".to_owned(), Bytes::from(1u64).lpad(32, 0));
379 state_1.deleted_attributes = vec!["to_add_back".to_owned()]
380 .into_iter()
381 .collect();
382
383 let attributes2: HashMap<String, Bytes> = vec![
384 ("reserve1".to_owned(), Bytes::from(900u64).lpad(32, 0)),
385 ("reserve2".to_owned(), Bytes::from(550u64).lpad(32, 0)),
386 ("new_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
387 ("to_add_back".to_owned(), Bytes::from(200u64).lpad(32, 0)),
388 ]
389 .into_iter()
390 .collect();
391 let del_attributes2: HashSet<String> = vec!["to_be_removed".to_owned()]
392 .into_iter()
393 .collect();
394 let mut state_2 = create_state("State1".to_owned());
395 state_2.updated_attributes = attributes2;
396 state_2.deleted_attributes = del_attributes2;
397
398 let res = state_1.merge(state_2);
399
400 assert!(res.is_ok());
401 let expected_attributes: HashMap<String, Bytes> = vec![
402 ("reserve1".to_owned(), Bytes::from(900u64).lpad(32, 0)),
403 ("reserve2".to_owned(), Bytes::from(550u64).lpad(32, 0)),
404 ("static_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
405 ("new_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
406 ("to_add_back".to_owned(), Bytes::from(200u64).lpad(32, 0)),
407 ]
408 .into_iter()
409 .collect();
410 assert_eq!(state_1.updated_attributes, expected_attributes);
411 let expected_del_attributes: HashSet<String> = vec!["to_be_removed".to_owned()]
412 .into_iter()
413 .collect();
414 assert_eq!(state_1.deleted_attributes, expected_del_attributes);
415 }
416
417 fn protocol_state_with_tx() -> ProtocolChangesWithTx {
418 let state_1 = create_state("State1".to_owned());
419 let state_2 = create_state("State2".to_owned());
420 let states: HashMap<String, ProtocolComponentStateDelta> =
421 vec![(state_1.component_id.clone(), state_1), (state_2.component_id.clone(), state_2)]
422 .into_iter()
423 .collect();
424 ProtocolChangesWithTx {
425 protocol_states: states,
426 tx: block_fixtures::transaction01(),
427 ..Default::default()
428 }
429 }
430
431 #[test]
432 fn test_merge_protocol_state_update_with_tx() {
433 let mut base_state = protocol_state_with_tx();
434
435 let new_attributes: HashMap<String, Bytes> = vec![
436 ("reserve1".to_owned(), Bytes::from(600u64).lpad(32, 0)),
437 ("new_attribute".to_owned(), Bytes::from(10u64).lpad(32, 0)),
438 ]
439 .into_iter()
440 .collect();
441 let new_tx = block_fixtures::create_transaction(HASH_256_1, HASH_256_0, 11);
442 let new_states: HashMap<String, ProtocolComponentStateDelta> = vec![(
443 "State1".to_owned(),
444 ProtocolComponentStateDelta {
445 component_id: "State1".to_owned(),
446 updated_attributes: new_attributes,
447 deleted_attributes: HashSet::new(),
448 },
449 )]
450 .into_iter()
451 .collect();
452
453 let tx_update =
454 ProtocolChangesWithTx { protocol_states: new_states, tx: new_tx, ..Default::default() };
455
456 let res = base_state.merge(tx_update);
457
458 assert!(res.is_ok());
459 assert_eq!(base_state.protocol_states.len(), 2);
460 let expected_attributes: HashMap<String, Bytes> = vec![
461 ("reserve1".to_owned(), Bytes::from(600u64).lpad(32, 0)),
462 ("reserve2".to_owned(), Bytes::from(500u64).lpad(32, 0)),
463 ("static_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
464 ("new_attribute".to_owned(), Bytes::from(10u64).lpad(32, 0)),
465 ]
466 .into_iter()
467 .collect();
468 assert_eq!(
469 base_state
470 .protocol_states
471 .get("State1")
472 .unwrap()
473 .updated_attributes,
474 expected_attributes
475 );
476 }
477
478 #[rstest]
479 #[case::diff_block(
480 block_fixtures::create_transaction(HASH_256_1, HASH_256_1, 11),
481 Err(MergeError::BlockMismatch(
482 "ProtocolChangesWithTx".to_string(),
483 Bytes::zero(32),
484 HASH_256_1.into(),
485 ))
486 )]
487 #[case::same_tx(
488 block_fixtures::create_transaction(HASH_256_0, HASH_256_0, 11),
489 Err(MergeError::SameTransaction(
490 "ProtocolChangesWithTx".to_string(),
491 Bytes::zero(32),
492 ))
493 )]
494 #[case::lower_idx(
495 block_fixtures::create_transaction(HASH_256_1, HASH_256_0, 1),
496 Err(MergeError::TransactionOrderError(
497 "ProtocolChangesWithTx".to_string(),
498 10,
499 1,
500 ))
501 )]
502 fn test_merge_pool_state_update_with_tx_errors(
503 #[case] tx: Transaction,
504 #[case] exp: Result<(), MergeError>,
505 ) {
506 let mut base_state = protocol_state_with_tx();
507
508 let mut new_state = protocol_state_with_tx();
509 new_state.tx = tx;
510
511 let res = base_state.merge(new_state);
512
513 assert_eq!(res, exp);
514 }
515
516 #[test]
517 fn test_merge_protocol_state_update_wrong_id() {
518 let mut state1 = create_state("State1".to_owned());
519 let state2 = create_state("State2".to_owned());
520
521 let res = state1.merge(state2);
522
523 assert_eq!(
524 res,
525 Err(MergeError::IdMismatch(
526 "ProtocolComponentStateDeltas".to_string(),
527 "State1".to_string(),
528 "State2".to_string(),
529 ))
530 );
531 }
532}