1use pretty_simple_display::{DebugPretty, DisplaySimple};
7use serde::{Deserialize, Serialize};
8use serde_with::skip_serializing_none;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12#[serde(rename_all = "snake_case")]
13#[derive(Default)]
14pub enum TransferState {
15 #[default]
17 Prepared,
18 Confirmed,
20 Cancelled,
22 WaitingForAdmin,
24 InsufficientFunds,
26 WithdrawalLimit,
28}
29
30#[skip_serializing_none]
32#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Serialize, Deserialize)]
33pub struct Transfer {
34 pub id: i64,
36 pub currency: String,
38 pub amount: f64,
40 pub fee: f64,
42 pub address: String,
44 pub transaction_id: Option<String>,
46 pub state: TransferState,
48 pub created_timestamp: i64,
50 pub updated_timestamp: i64,
52 pub confirmed_timestamp: Option<i64>,
54 pub transfer_type: Option<String>,
56}
57
58impl Transfer {
59 pub fn new(
61 id: i64,
62 currency: String,
63 amount: f64,
64 fee: f64,
65 address: String,
66 created_timestamp: i64,
67 ) -> Self {
68 Self {
69 id,
70 currency,
71 amount,
72 fee,
73 address,
74 transaction_id: None,
75 state: TransferState::Prepared,
76 created_timestamp,
77 updated_timestamp: created_timestamp,
78 confirmed_timestamp: None,
79 transfer_type: None,
80 }
81 }
82
83 pub fn with_transaction_id(mut self, tx_id: String) -> Self {
85 self.transaction_id = Some(tx_id);
86 self
87 }
88
89 pub fn with_state(mut self, state: TransferState) -> Self {
91 self.state = state;
92 self
93 }
94
95 pub fn with_type(mut self, transfer_type: String) -> Self {
97 self.transfer_type = Some(transfer_type);
98 self
99 }
100
101 pub fn confirm(&mut self, timestamp: i64) {
103 self.state = TransferState::Confirmed;
104 self.confirmed_timestamp = Some(timestamp);
105 self.updated_timestamp = timestamp;
106 }
107
108 pub fn cancel(&mut self, timestamp: i64) {
110 self.state = TransferState::Cancelled;
111 self.updated_timestamp = timestamp;
112 }
113
114 pub fn is_confirmed(&self) -> bool {
116 matches!(self.state, TransferState::Confirmed)
117 }
118
119 pub fn is_cancelled(&self) -> bool {
121 matches!(self.state, TransferState::Cancelled)
122 }
123
124 pub fn is_pending(&self) -> bool {
126 matches!(
127 self.state,
128 TransferState::Prepared | TransferState::WaitingForAdmin
129 )
130 }
131
132 pub fn net_amount(&self) -> f64 {
134 self.amount - self.fee
135 }
136}
137
138#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Serialize, Deserialize)]
140pub struct Transfers {
141 pub transfers: Vec<Transfer>,
143}
144
145impl Transfers {
146 pub fn new() -> Self {
148 Self {
149 transfers: Vec::new(),
150 }
151 }
152
153 pub fn add(&mut self, transfer: Transfer) {
155 self.transfers.push(transfer);
156 }
157
158 pub fn by_currency(&self, currency: String) -> Vec<&Transfer> {
160 self.transfers
161 .iter()
162 .filter(|t| t.currency == currency)
163 .collect()
164 }
165
166 pub fn by_state(&self, state: TransferState) -> Vec<&Transfer> {
168 self.transfers.iter().filter(|t| t.state == state).collect()
169 }
170
171 pub fn pending(&self) -> Vec<&Transfer> {
173 self.transfers.iter().filter(|t| t.is_pending()).collect()
174 }
175
176 pub fn confirmed(&self) -> Vec<&Transfer> {
178 self.transfers.iter().filter(|t| t.is_confirmed()).collect()
179 }
180
181 pub fn total_amount(&self, currency: String) -> f64 {
183 self.transfers
184 .iter()
185 .filter(|t| t.currency == currency)
186 .map(|t| t.amount)
187 .sum()
188 }
189
190 pub fn total_fees(&self, currency: String) -> f64 {
192 self.transfers
193 .iter()
194 .filter(|t| t.currency == currency)
195 .map(|t| t.fee)
196 .sum()
197 }
198}
199impl Default for Transfers {
200 fn default() -> Self {
201 Self::new()
202 }
203}
204
205#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Serialize, Deserialize)]
207pub struct SubaccountTransfer {
208 pub amount: f64,
210 pub currency: String,
212 pub destination: i64,
214 pub id: i64,
216 pub source: i64,
218 pub state: TransferState,
220 pub timestamp: i64,
222 pub transfer_type: String,
224}
225
226impl SubaccountTransfer {
227 pub fn new(
229 id: i64,
230 amount: f64,
231 currency: String,
232 source: i64,
233 destination: i64,
234 timestamp: i64,
235 ) -> Self {
236 Self {
237 amount,
238 currency,
239 destination,
240 id,
241 source,
242 state: TransferState::Prepared,
243 timestamp,
244 transfer_type: "subaccount".to_string(),
245 }
246 }
247
248 pub fn with_state(mut self, state: TransferState) -> Self {
250 self.state = state;
251 self
252 }
253
254 pub fn with_type(mut self, transfer_type: String) -> Self {
256 self.transfer_type = transfer_type;
257 self
258 }
259
260 pub fn is_main_subaccount_transfer(&self) -> bool {
262 self.source == 0 || self.destination == 0
263 }
264
265 pub fn is_subaccount_to_subaccount(&self) -> bool {
267 self.source != 0 && self.destination != 0
268 }
269}