fedimint_mintv2_client/
ecash.rs1use fedimint_core::config::FederationId;
2use fedimint_core::encoding::{Decodable, Encodable};
3use fedimint_core::invite_code::InviteCode;
4use fedimint_core::util::SafeUrl;
5use fedimint_core::{Amount, PeerId};
6
7use crate::SpendableNote;
8
9#[derive(Clone, Debug, Encodable, Decodable)]
10pub struct ECash(Vec<ECashField>);
11
12#[derive(Clone, Debug, Decodable, Encodable)]
13enum ECashField {
14 Mint(FederationId),
15 Note(SpendableNote),
16 Invite {
20 peer_apis: Vec<(PeerId, SafeUrl)>,
21 federation_id: FederationId,
22 },
23 ApiSecret(String),
24 #[encodable_default]
25 Default {
26 variant: u64,
27 bytes: Vec<u8>,
28 },
29}
30
31impl ECash {
32 pub fn new(mint: FederationId, notes: Vec<SpendableNote>) -> Self {
33 Self(
34 std::iter::once(ECashField::Mint(mint))
35 .chain(notes.into_iter().map(ECashField::Note))
36 .collect(),
37 )
38 }
39
40 pub fn new_with_invite(notes: Vec<SpendableNote>, invite: &InviteCode) -> Self {
41 let mut fields = vec![ECashField::Mint(invite.federation_id())];
42
43 fields.extend(notes.into_iter().map(ECashField::Note));
44
45 fields.push(ECashField::Invite {
46 peer_apis: vec![(invite.peer(), invite.url())],
47 federation_id: invite.federation_id(),
48 });
49
50 if let Some(api_secret) = invite.api_secret() {
51 fields.push(ECashField::ApiSecret(api_secret));
52 }
53
54 Self(fields)
55 }
56
57 pub fn amount(&self) -> Amount {
58 self.0
59 .iter()
60 .filter_map(|field| match field {
61 ECashField::Note(note) => Some(note.amount()),
62 _ => None,
63 })
64 .sum()
65 }
66
67 pub fn mint(&self) -> Option<FederationId> {
68 self.0.iter().find_map(|field| match field {
69 ECashField::Mint(mint) => Some(*mint),
70 _ => None,
71 })
72 }
73
74 pub fn notes(&self) -> Vec<SpendableNote> {
75 self.0
76 .iter()
77 .filter_map(|field| match field {
78 ECashField::Note(note) => Some(note.clone()),
79 _ => None,
80 })
81 .collect()
82 }
83
84 pub fn federation_invite(&self) -> Option<InviteCode> {
87 let api_secret = self.api_secret();
88
89 self.0.iter().find_map(|field| {
90 let ECashField::Invite {
91 peer_apis,
92 federation_id,
93 } = field
94 else {
95 return None;
96 };
97
98 let (peer_id, api) = peer_apis.first().cloned()?;
99
100 Some(InviteCode::new(
101 api,
102 peer_id,
103 *federation_id,
104 api_secret.clone(),
105 ))
106 })
107 }
108
109 fn api_secret(&self) -> Option<String> {
110 self.0.iter().find_map(|field| match field {
111 ECashField::ApiSecret(api_secret) => Some(api_secret.clone()),
112 _ => None,
113 })
114 }
115}