Skip to main content

fedimint_mintv2_client/
receive.rs

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