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 pub origin: Option<Bytes>,
284}
285
286impl From<dto::ProtocolStateDelta> for ProtocolComponentStateDelta {
287 fn from(value: dto::ProtocolStateDelta) -> Self {
288 Self {
289 component_id: value.component_id,
290 updated_attributes: value.updated_attributes,
291 deleted_attributes: value.deleted_attributes,
292 created_attributes: HashSet::new(),
293 }
294 }
295}
296
297impl From<dto::ComponentBalance> for ComponentBalance {
298 fn from(value: dto::ComponentBalance) -> Self {
299 Self {
300 token: value.token,
301 balance: value.balance,
302 balance_float: value.balance_float,
303 modify_tx: value.modify_tx,
304 component_id: value.component_id,
305 }
306 }
307}
308
309impl From<dto::ProtocolComponent> for ProtocolComponent {
310 fn from(value: dto::ProtocolComponent) -> Self {
311 Self {
312 id: value.id,
313 protocol_system: value.protocol_system,
314 protocol_type_name: value.protocol_type_name,
315 chain: value.chain.into(),
316 tokens: value.tokens,
317 contract_addresses: value.contract_ids,
318 static_attributes: value.static_attributes,
319 change: value.change.into(),
320 creation_tx: value.creation_tx,
321 created_at: value.created_at,
322 }
323 }
324}
325
326impl From<dto::ResponseProtocolState> for ProtocolComponentState {
327 fn from(value: dto::ResponseProtocolState) -> Self {
328 Self {
329 component_id: value.component_id,
330 attributes: value.attributes,
331 balances: value.balances,
332 }
333 }
334}
335
336#[cfg(test)]
337mod test {
338 use super::*;
339
340 fn create_state(id: String) -> ProtocolComponentStateDelta {
341 let attributes1: HashMap<String, Bytes> = vec![
342 ("reserve1".to_owned(), Bytes::from(1000u64).lpad(32, 0)),
343 ("reserve2".to_owned(), Bytes::from(500u64).lpad(32, 0)),
344 ("static_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
345 ]
346 .into_iter()
347 .collect();
348 ProtocolComponentStateDelta {
349 component_id: id,
350 updated_attributes: attributes1,
351 deleted_attributes: HashSet::new(),
352 ..Default::default()
353 }
354 }
355
356 #[test]
357 fn test_merge_protocol_state_updates() {
358 let mut state_1 = create_state("State1".to_owned());
359 state_1
360 .updated_attributes
361 .insert("to_be_removed".to_owned(), Bytes::from(1u64).lpad(32, 0));
362 state_1.deleted_attributes = vec!["to_add_back".to_owned()]
363 .into_iter()
364 .collect();
365
366 let attributes2: HashMap<String, Bytes> = vec![
367 ("reserve1".to_owned(), Bytes::from(900u64).lpad(32, 0)),
368 ("reserve2".to_owned(), Bytes::from(550u64).lpad(32, 0)),
369 ("new_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
370 ("to_add_back".to_owned(), Bytes::from(200u64).lpad(32, 0)),
371 ]
372 .into_iter()
373 .collect();
374 let del_attributes2: HashSet<String> = vec!["to_be_removed".to_owned()]
375 .into_iter()
376 .collect();
377 let mut state_2 = create_state("State1".to_owned());
378 state_2.updated_attributes = attributes2;
379 state_2.deleted_attributes = del_attributes2;
380
381 let res = state_1.merge(state_2);
382
383 assert!(res.is_ok());
384 let expected_attributes: HashMap<String, Bytes> = vec![
385 ("reserve1".to_owned(), Bytes::from(900u64).lpad(32, 0)),
386 ("reserve2".to_owned(), Bytes::from(550u64).lpad(32, 0)),
387 ("static_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
388 ("new_attribute".to_owned(), Bytes::from(1u64).lpad(32, 0)),
389 ("to_add_back".to_owned(), Bytes::from(200u64).lpad(32, 0)),
390 ]
391 .into_iter()
392 .collect();
393 assert_eq!(state_1.updated_attributes, expected_attributes);
394 let expected_del_attributes: HashSet<String> = vec!["to_be_removed".to_owned()]
395 .into_iter()
396 .collect();
397 assert_eq!(state_1.deleted_attributes, expected_del_attributes);
398 }
399
400 #[test]
401 fn test_merge_protocol_state_update_wrong_id() {
402 let mut state1 = create_state("State1".to_owned());
403 let state2 = create_state("State2".to_owned());
404
405 let res = state1.merge(state2);
406
407 assert_eq!(
408 res,
409 Err(MergeError::IdMismatch(
410 "ProtocolComponentStateDeltas".to_string(),
411 "State1".to_string(),
412 "State2".to_string(),
413 ))
414 );
415 }
416}