1use std::collections::{hash_map::Entry, HashMap, HashSet};
2
3use chrono::NaiveDateTime;
4use num_bigint::BigUint;
5use serde::{Deserialize, Serialize};
6use tracing::warn;
7
8use crate::{
9 models::{
10 blockchain::Transaction, Address, AttrStoreKey, Balance, Chain, ChangeType, ComponentId,
11 MergeError, StoreVal, TxHash,
12 },
13 Bytes,
14};
15
16#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
28pub struct ProtocolComponent {
29 pub id: ComponentId,
30 pub protocol_system: String,
31 pub protocol_type_name: String,
32 pub chain: Chain,
33 pub tokens: Vec<Address>,
34 pub contract_addresses: Vec<Address>,
35 pub static_attributes: HashMap<AttrStoreKey, StoreVal>,
36 pub change: ChangeType,
37 pub creation_tx: TxHash,
38 pub created_at: NaiveDateTime,
39}
40
41impl ProtocolComponent {
42 #[allow(clippy::too_many_arguments)]
43 pub fn new(
44 id: &str,
45 protocol_system: &str,
46 protocol_type_name: &str,
47 chain: Chain,
48 tokens: Vec<Address>,
49 contract_addresses: Vec<Address>,
50 static_attributes: HashMap<AttrStoreKey, StoreVal>,
51 change: ChangeType,
52 creation_tx: TxHash,
53 created_at: NaiveDateTime,
54 ) -> Self {
55 Self {
56 id: id.to_string(),
57 protocol_system: protocol_system.to_string(),
58 protocol_type_name: protocol_type_name.to_string(),
59 chain,
60 tokens,
61 contract_addresses,
62 static_attributes,
63 change,
64 creation_tx,
65 created_at,
66 }
67 }
68}
69
70#[derive(Debug, Clone, PartialEq)]
71pub struct ProtocolComponentState {
72 pub component_id: ComponentId,
73 pub attributes: HashMap<AttrStoreKey, StoreVal>,
74 pub balances: HashMap<Address, Balance>,
76}
77
78impl ProtocolComponentState {
79 pub fn new(
80 component_id: &str,
81 attributes: HashMap<AttrStoreKey, StoreVal>,
82 balances: HashMap<Address, Balance>,
83 ) -> Self {
84 Self { component_id: component_id.to_string(), attributes, balances }
85 }
86
87 pub fn apply_state_delta(
91 &mut self,
92 delta: &ProtocolComponentStateDelta,
93 ) -> Result<(), MergeError> {
94 if self.component_id != delta.component_id {
95 return Err(MergeError::IdMismatch(
96 "ProtocolComponentStates".to_string(),
97 self.component_id.clone(),
98 delta.component_id.clone(),
99 ));
100 }
101 self.attributes
102 .extend(delta.updated_attributes.clone());
103
104 self.attributes
105 .retain(|attr, _| !delta.deleted_attributes.contains(attr));
106
107 Ok(())
108 }
109
110 pub fn apply_balance_delta(
114 &mut self,
115 delta: &HashMap<Bytes, ComponentBalance>,
116 ) -> Result<(), MergeError> {
117 self.balances.extend(
118 delta
119 .iter()
120 .map(|(k, v)| (k.clone(), v.balance.clone())),
121 );
122
123 Ok(())
124 }
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
128pub struct ProtocolComponentStateDelta {
129 pub component_id: ComponentId,
130 pub updated_attributes: HashMap<AttrStoreKey, StoreVal>,
131 pub deleted_attributes: HashSet<AttrStoreKey>,
132}
133
134impl ProtocolComponentStateDelta {
135 pub fn new(
136 component_id: &str,
137 updated_attributes: HashMap<AttrStoreKey, StoreVal>,
138 deleted_attributes: HashSet<AttrStoreKey>,
139 ) -> Self {
140 Self { component_id: component_id.to_string(), updated_attributes, deleted_attributes }
141 }
142
143 pub fn merge(&mut self, other: ProtocolComponentStateDelta) -> Result<(), MergeError> {
155 if self.component_id != other.component_id {
156 return Err(MergeError::IdMismatch(
157 "ProtocolComponentStateDeltas".to_string(),
158 self.component_id.clone(),
159 other.component_id.clone(),
160 ));
161 }
162 for attr in &other.deleted_attributes {
163 self.updated_attributes.remove(attr);
164 }
165 for attr in other.updated_attributes.keys() {
166 self.deleted_attributes.remove(attr);
167 }
168 self.updated_attributes
169 .extend(other.updated_attributes);
170 self.deleted_attributes
171 .extend(other.deleted_attributes);
172 Ok(())
173 }
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
177pub struct ComponentBalance {
178 pub token: Address,
179 pub balance: Balance,
180 pub balance_float: f64,
181 pub modify_tx: TxHash,
182 pub component_id: ComponentId,
183}
184
185impl ComponentBalance {
186 pub fn new(
187 token: Address,
188 new_balance: Balance,
189 balance_float: f64,
190 modify_tx: TxHash,
191 component_id: &str,
192 ) -> Self {
193 Self {
194 token,
195 balance: new_balance,
196 balance_float,
197 modify_tx,
198 component_id: component_id.to_string(),
199 }
200 }
201}
202
203#[derive(Debug, Clone)]
207pub struct QualityRange {
208 pub min: Option<i32>,
209 pub max: Option<i32>,
210}
211
212impl QualityRange {
213 pub fn new(min: i32, max: i32) -> Self {
214 Self { min: Some(min), max: Some(max) }
215 }
216
217 pub fn min_only(min: i32) -> Self {
218 Self { min: Some(min), max: None }
219 }
220
221 #[allow(non_snake_case)]
222 pub fn None() -> Self {
223 Self { min: None, max: None }
224 }
225}
226
227#[derive(Debug, Clone, PartialEq, Default)]
229pub struct ProtocolChangesWithTx {
230 pub new_protocol_components: HashMap<ComponentId, ProtocolComponent>,
231 pub protocol_states: HashMap<ComponentId, ProtocolComponentStateDelta>,
232 pub balance_changes: HashMap<ComponentId, HashMap<Bytes, ComponentBalance>>,
233 pub tx: Transaction,
234}
235
236impl ProtocolChangesWithTx {
237 pub fn merge(&mut self, other: ProtocolChangesWithTx) -> Result<(), MergeError> {
254 if self.tx.block_hash != other.tx.block_hash {
255 return Err(MergeError::BlockMismatch(
256 "ProtocolChangesWithTx".to_string(),
257 self.tx.block_hash.clone(),
258 other.tx.block_hash,
259 ));
260 }
261 if self.tx.hash == other.tx.hash {
262 return Err(MergeError::SameTransaction(
263 "ProtocolChangesWithTx".to_string(),
264 other.tx.hash,
265 ));
266 }
267 if self.tx.index > other.tx.index {
268 return Err(MergeError::TransactionOrderError(
269 "ProtocolChangesWithTx".to_string(),
270 self.tx.index,
271 other.tx.index,
272 ));
273 }
274 self.tx = other.tx;
275 for (key, value) in other.protocol_states {
277 match self.protocol_states.entry(key) {
278 Entry::Occupied(mut entry) => {
279 entry.get_mut().merge(value)?;
280 }
281 Entry::Vacant(entry) => {
282 entry.insert(value);
283 }
284 }
285 }
286
287 for (component_id, balance_changes) in other.balance_changes {
289 let token_balances = self
290 .balance_changes
291 .entry(component_id)
292 .or_default();
293 for (token, balance) in balance_changes {
294 token_balances.insert(token, balance);
295 }
296 }
297
298 for (key, value) in other.new_protocol_components {
302 match self.new_protocol_components.entry(key) {
303 Entry::Occupied(mut entry) => {
304 warn!(
305 "Overwriting new protocol component for id {} with a new one. This should never happen! Please check logic",
306 entry.get().id
307 );
308 entry.insert(value);
309 }
310 Entry::Vacant(entry) => {
311 entry.insert(value);
312 }
313 }
314 }
315
316 Ok(())
317 }
318}
319
320pub struct GetAmountOutParams {
321 pub amount_in: BigUint,
322 pub token_in: Bytes,
323 pub token_out: Bytes,
324 pub sender: Bytes,
325 pub receiver: Bytes,
326}
327
328#[cfg(test)]
329mod test {
330 use rstest::rstest;
331
332 use super::*;
333 use crate::models::blockchain::fixtures as block_fixtures;
334
335 const HASH_256_0: &str = "0x0000000000000000000000000000000000000000000000000000000000000000";
336 const HASH_256_1: &str = "0x0000000000000000000000000000000000000000000000000000000000000001";
337
338 fn create_state(id: String) -> ProtocolComponentStateDelta {
339 let attributes1: HashMap<String, Bytes> = vec![
340 ("reserve1".to_owned(), Bytes::from(1000u64).lpad(32, 0)),
341 ("reserve2".to_owned(), Bytes::from(500u64).lpad(32, 0)),
342 ("static_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
343 ]
344 .into_iter()
345 .collect();
346 ProtocolComponentStateDelta {
347 component_id: id,
348 updated_attributes: attributes1,
349 deleted_attributes: HashSet::new(),
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 fn protocol_state_with_tx() -> ProtocolChangesWithTx {
398 let state_1 = create_state("State1".to_owned());
399 let state_2 = create_state("State2".to_owned());
400 let states: HashMap<String, ProtocolComponentStateDelta> =
401 vec![(state_1.component_id.clone(), state_1), (state_2.component_id.clone(), state_2)]
402 .into_iter()
403 .collect();
404 ProtocolChangesWithTx {
405 protocol_states: states,
406 tx: block_fixtures::transaction01(),
407 ..Default::default()
408 }
409 }
410
411 #[test]
412 fn test_merge_protocol_state_update_with_tx() {
413 let mut base_state = protocol_state_with_tx();
414
415 let new_attributes: HashMap<String, Bytes> = vec![
416 ("reserve1".to_owned(), Bytes::from(600u64).lpad(32, 0)),
417 ("new_attribute".to_owned(), Bytes::from(10u64).lpad(32, 0)),
418 ]
419 .into_iter()
420 .collect();
421 let new_tx = block_fixtures::create_transaction(HASH_256_1, HASH_256_0, 11);
422 let new_states: HashMap<String, ProtocolComponentStateDelta> = vec![(
423 "State1".to_owned(),
424 ProtocolComponentStateDelta {
425 component_id: "State1".to_owned(),
426 updated_attributes: new_attributes,
427 deleted_attributes: HashSet::new(),
428 },
429 )]
430 .into_iter()
431 .collect();
432
433 let tx_update =
434 ProtocolChangesWithTx { protocol_states: new_states, tx: new_tx, ..Default::default() };
435
436 let res = base_state.merge(tx_update);
437
438 assert!(res.is_ok());
439 assert_eq!(base_state.protocol_states.len(), 2);
440 let expected_attributes: HashMap<String, Bytes> = vec![
441 ("reserve1".to_owned(), Bytes::from(600u64).lpad(32, 0)),
442 ("reserve2".to_owned(), Bytes::from(500u64).lpad(32, 0)),
443 ("static_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
444 ("new_attribute".to_owned(), Bytes::from(10u64).lpad(32, 0)),
445 ]
446 .into_iter()
447 .collect();
448 assert_eq!(
449 base_state
450 .protocol_states
451 .get("State1")
452 .unwrap()
453 .updated_attributes,
454 expected_attributes
455 );
456 }
457
458 #[rstest]
459 #[case::diff_block(
460 block_fixtures::create_transaction(HASH_256_1, HASH_256_1, 11),
461 Err(MergeError::BlockMismatch(
462 "ProtocolChangesWithTx".to_string(),
463 Bytes::zero(32),
464 HASH_256_1.into(),
465 ))
466 )]
467 #[case::same_tx(
468 block_fixtures::create_transaction(HASH_256_0, HASH_256_0, 11),
469 Err(MergeError::SameTransaction(
470 "ProtocolChangesWithTx".to_string(),
471 Bytes::zero(32),
472 ))
473 )]
474 #[case::lower_idx(
475 block_fixtures::create_transaction(HASH_256_1, HASH_256_0, 1),
476 Err(MergeError::TransactionOrderError(
477 "ProtocolChangesWithTx".to_string(),
478 10,
479 1,
480 ))
481 )]
482 fn test_merge_pool_state_update_with_tx_errors(
483 #[case] tx: Transaction,
484 #[case] exp: Result<(), MergeError>,
485 ) {
486 let mut base_state = protocol_state_with_tx();
487
488 let mut new_state = protocol_state_with_tx();
489 new_state.tx = tx;
490
491 let res = base_state.merge(new_state);
492
493 assert_eq!(res, exp);
494 }
495
496 #[test]
497 fn test_merge_protocol_state_update_wrong_id() {
498 let mut state1 = create_state("State1".to_owned());
499 let state2 = create_state("State2".to_owned());
500
501 let res = state1.merge(state2);
502
503 assert_eq!(
504 res,
505 Err(MergeError::IdMismatch(
506 "ProtocolComponentStateDeltas".to_string(),
507 "State1".to_string(),
508 "State2".to_string(),
509 ))
510 );
511 }
512}