1
2
3pub mod error;
4pub mod manager;
5pub mod update;
6mod payment_method;
7
8pub use self::payment_method::PaymentMethod;
9
10use std::collections::HashMap;
11use std::fmt;
12use std::str::FromStr;
13
14use bitcoin::{Amount, SignedAmount};
15use chrono::DateTime;
16use lightning::offers::offer::Offer;
17use lnurllib::lightning_address::LightningAddress;
18use serde::{Deserialize, Serialize};
19
20use ark::VtxoId;
21use ark::lightning::Invoice;
22
23const MOVEMENT_PENDING: &'static str = "pending";
24const MOVEMENT_SUCCESSFUL: &'static str = "successful";
25const MOVEMENT_FAILED: &'static str = "failed";
26const MOVEMENT_CANCELED: &'static str = "canceled";
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct Movement {
31 pub id: MovementId,
33 pub status: MovementStatus,
35 pub subsystem: MovementSubsystem,
38 pub metadata: HashMap<String, serde_json::Value>,
41 #[serde(with = "bitcoin::amount::serde::as_sat")]
44 pub intended_balance: SignedAmount,
45 #[serde(with = "bitcoin::amount::serde::as_sat")]
49 pub effective_balance: SignedAmount,
50 #[serde(with = "bitcoin::amount::serde::as_sat")]
54 pub offchain_fee: Amount,
55 pub sent_to: Vec<MovementDestination>,
57 pub received_on: Vec<MovementDestination>,
60 pub input_vtxos: Vec<VtxoId>,
63 pub output_vtxos: Vec<VtxoId>,
66 pub exited_vtxos: Vec<VtxoId>,
71 pub time: MovementTimestamp,
73}
74
75impl Movement {
76 pub fn new(
77 id: MovementId,
78 status: MovementStatus,
79 subsystem: &MovementSubsystem,
80 time: DateTime<chrono::Local>,
81 ) -> Self {
82 Self {
83 id,
84 status,
85 subsystem: subsystem.clone(),
86 time: MovementTimestamp {
87 created_at: time,
88 updated_at: time,
89 completed_at: None,
90 },
91 metadata: HashMap::new(),
92 intended_balance: SignedAmount::ZERO,
93 effective_balance: SignedAmount::ZERO,
94 offchain_fee: Amount::ZERO,
95 sent_to: vec![],
96 received_on: vec![],
97 input_vtxos: vec![],
98 output_vtxos: vec![],
99 exited_vtxos: vec![],
100 }
101 }
102}
103
104#[derive(Clone, Copy, Eq, Hash, PartialEq, Deserialize, Serialize)]
106pub struct MovementId(pub u32);
107
108impl MovementId {
109 pub fn new(id: u32) -> Self {
110 Self(id)
111 }
112
113 pub fn to_bytes(&self) -> [u8; 4] {
114 self.0.to_be_bytes()
115 }
116}
117
118impl fmt::Display for MovementId {
119 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120 fmt::Display::fmt(&self.0, f)
121 }
122}
123
124impl fmt::Debug for MovementId {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 fmt::Display::fmt(&self, f)
127 }
128}
129
130#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
135pub enum MovementStatus {
136 Pending,
138 Successful,
141 Failed,
144 Canceled,
147}
148
149impl MovementStatus {
150 pub fn as_str(&self) -> &'static str {
155 match self {
156 Self::Pending => MOVEMENT_PENDING,
157 Self::Successful => MOVEMENT_SUCCESSFUL,
158 Self::Failed => MOVEMENT_FAILED,
159 Self::Canceled => MOVEMENT_CANCELED,
160 }
161 }
162}
163
164impl fmt::Display for MovementStatus {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 f.write_str(self.as_str())
167 }
168}
169
170impl fmt::Debug for MovementStatus {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 fmt::Display::fmt(&self, f)
173 }
174}
175
176impl FromStr for MovementStatus {
177 type Err = anyhow::Error;
178
179 fn from_str(s: &str) -> Result<Self, Self::Err> {
181 match s {
182 MOVEMENT_PENDING => Ok(MovementStatus::Pending),
183 MOVEMENT_SUCCESSFUL => Ok(MovementStatus::Successful),
184 MOVEMENT_FAILED => Ok(MovementStatus::Failed),
185 MOVEMENT_CANCELED => Ok(MovementStatus::Canceled),
186 _ => bail!("Invalid MovementStatus: {}", s),
187 }
188 }
189}
190
191impl Serialize for MovementStatus {
192 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
193 where
194 S: serde::Serializer,
195 {
196 serializer.serialize_str(self.as_str())
197 }
198}
199
200impl<'de> Deserialize<'de> for MovementStatus {
201 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
202 where
203 D: serde::Deserializer<'de>,
204 {
205 let s = String::deserialize(deserializer)?;
206 MovementStatus::from_str(&s).map_err(serde::de::Error::custom)
207 }
208}
209
210#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
213pub struct MovementDestination {
214 pub destination: PaymentMethod,
216 #[serde(with = "bitcoin::amount::serde::as_sat")]
218 pub amount: Amount,
219}
220
221impl MovementDestination {
222 pub fn new(payment_method: PaymentMethod, amount: Amount) -> Self {
223 Self { destination: payment_method, amount }
224 }
225
226 pub fn ark(address: ark::Address, amount: Amount) -> Self {
227 Self::new(address.into(), amount)
228 }
229
230 pub fn bitcoin(address: bitcoin::Address, amount: Amount) -> Self {
231 Self::new(address.into(), amount)
232 }
233
234 pub fn invoice(invoice: Invoice, amount: Amount) -> Self {
235 Self::new(invoice.into(), amount)
236 }
237
238 pub fn offer(offer: Offer, amount: Amount) -> Self {
239 Self::new(offer.into(), amount)
240 }
241
242 pub fn lightning_address(address: LightningAddress, amount: Amount) -> Self {
243 Self::new(address.into(), amount)
244 }
245
246 pub fn custom(destination: String, amount: Amount) -> Self {
247 Self::new(PaymentMethod::Custom(destination), amount)
248 }
249}
250
251#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
254pub struct MovementSubsystem {
255 pub name: String,
257 pub kind: String,
259}
260
261#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
263pub struct MovementTimestamp {
264 pub created_at: DateTime<chrono::Local>,
266 pub updated_at: DateTime<chrono::Local>,
268 pub completed_at: Option<DateTime<chrono::Local>>,
270}