Skip to main content

fedimint_walletv2_client/
receive_sm.rs

1use fedimint_client_module::DynGlobalClientContext;
2use fedimint_client_module::sm::{ClientSMDatabaseTransaction, State, StateTransition};
3use fedimint_core::TransactionId;
4use fedimint_core::core::OperationId;
5use fedimint_core::encoding::{Decodable, Encodable};
6
7use crate::WalletClientContext;
8use crate::events::{ReceivePaymentStatus, ReceivePaymentStatusEvent};
9
10#[derive(Debug, Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
11pub struct ReceiveStateMachine {
12    pub common: ReceiveSMCommon,
13    pub state: ReceiveSMState,
14}
15
16impl ReceiveStateMachine {
17    pub fn update(&self, state: ReceiveSMState) -> Self {
18        Self {
19            common: self.common.clone(),
20            state,
21        }
22    }
23}
24
25#[derive(Debug, Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
26pub struct ReceiveSMCommon {
27    pub operation_id: OperationId,
28    pub txid: TransactionId,
29    pub amount: bitcoin::Amount,
30    pub fee: bitcoin::Amount,
31}
32
33#[derive(Debug, Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
34pub enum ReceiveSMState {
35    Funding,
36    Success,
37    Aborted(String),
38}
39
40impl State for ReceiveStateMachine {
41    type ModuleContext = WalletClientContext;
42
43    fn transitions(
44        &self,
45        context: &Self::ModuleContext,
46        global_context: &DynGlobalClientContext,
47    ) -> Vec<StateTransition<Self>> {
48        let ctx = context.clone();
49
50        match &self.state {
51            ReceiveSMState::Funding => {
52                vec![StateTransition::new(
53                    Self::await_funding(global_context.clone(), self.common.txid),
54                    move |dbtx, result, old_state| {
55                        Box::pin(Self::transition_funding(
56                            ctx.clone(),
57                            dbtx,
58                            result,
59                            old_state,
60                        ))
61                    },
62                )]
63            }
64            ReceiveSMState::Success | ReceiveSMState::Aborted(_) => {
65                vec![]
66            }
67        }
68    }
69
70    fn operation_id(&self) -> OperationId {
71        self.common.operation_id
72    }
73}
74
75impl ReceiveStateMachine {
76    async fn await_funding(
77        global_context: DynGlobalClientContext,
78        txid: TransactionId,
79    ) -> Result<(), String> {
80        global_context.await_tx_accepted(txid).await
81    }
82
83    async fn transition_funding(
84        context: WalletClientContext,
85        dbtx: &mut ClientSMDatabaseTransaction<'_, '_>,
86        result: Result<(), String>,
87        old_state: ReceiveStateMachine,
88    ) -> ReceiveStateMachine {
89        match result {
90            Ok(()) => {
91                context
92                    .client_ctx
93                    .log_event(
94                        &mut dbtx.module_tx(),
95                        ReceivePaymentStatusEvent {
96                            operation_id: old_state.common.operation_id,
97                            status: ReceivePaymentStatus::Success,
98                        },
99                    )
100                    .await;
101
102                old_state.update(ReceiveSMState::Success)
103            }
104            Err(error) => {
105                context
106                    .client_ctx
107                    .log_event(
108                        &mut dbtx.module_tx(),
109                        ReceivePaymentStatusEvent {
110                            operation_id: old_state.common.operation_id,
111                            status: ReceivePaymentStatus::Aborted,
112                        },
113                    )
114                    .await;
115
116                old_state.update(ReceiveSMState::Aborted(error))
117            }
118        }
119    }
120}