fedimint_walletv2_client/
receive_sm.rs1use 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}