1use std::{collections::HashMap, sync::Arc};
2
3use crate::{types::AssetId, Address, Txid};
4
5#[derive(uniffi::Object, Debug)]
11pub struct PsetDetails {
12 inner: lwk_common::PsetDetails,
13}
14
15impl From<lwk_common::PsetDetails> for PsetDetails {
16 fn from(inner: lwk_common::PsetDetails) -> Self {
17 Self { inner }
18 }
19}
20
21#[uniffi::export]
22impl PsetDetails {
23 pub fn balance(&self) -> Arc<PsetBalance> {
26 Arc::new(self.inner.balance.clone().into())
27 }
28
29 pub fn signatures(&self) -> Vec<Arc<PsetSignatures>> {
31 self.inner
32 .sig_details
33 .clone()
34 .into_iter()
35 .map(|s| Arc::new(s.into()))
36 .collect()
37 }
38
39 pub fn inputs_issuances(&self) -> Vec<Arc<Issuance>> {
41 self.inner
45 .issuances
46 .clone()
47 .into_iter()
48 .map(|e| Arc::new(e.into()))
49 .collect()
50 }
51}
52
53#[derive(uniffi::Object, Debug)]
54pub struct PsetBalance {
55 inner: lwk_common::PsetBalance,
56}
57
58impl From<lwk_common::PsetBalance> for PsetBalance {
59 fn from(inner: lwk_common::PsetBalance) -> Self {
60 Self { inner }
61 }
62}
63
64#[uniffi::export]
65impl PsetBalance {
66 pub fn fee(&self) -> u64 {
67 self.inner.fee
68 }
69
70 pub fn balances(&self) -> HashMap<AssetId, i64> {
71 self.inner
72 .balances
73 .iter()
74 .map(|(k, v)| ((*k).into(), *v))
75 .collect()
76 }
77
78 pub fn recipients(&self) -> Vec<Arc<Recipient>> {
79 self.inner
80 .recipients
81 .clone()
82 .into_iter()
83 .map(|e| Arc::new(e.into()))
84 .collect()
85 }
86}
87
88#[derive(uniffi::Object, Debug)]
89pub struct PsetSignatures {
90 inner: lwk_common::PsetSignatures,
91}
92
93impl From<lwk_common::PsetSignatures> for PsetSignatures {
94 fn from(inner: lwk_common::PsetSignatures) -> Self {
95 Self { inner }
96 }
97}
98
99type PublicKey = String;
100type KeySource = String;
101
102#[uniffi::export]
103impl PsetSignatures {
104 pub fn has_signature(&self) -> HashMap<PublicKey, KeySource> {
105 self.inner
106 .has_signature
107 .iter()
108 .map(|(k, v)| (k.to_string(), key_source_to_string(v)))
109 .collect()
110 }
111
112 pub fn missing_signature(&self) -> HashMap<PublicKey, KeySource> {
113 self.inner
114 .missing_signature
115 .iter()
116 .map(|(k, v)| (k.to_string(), key_source_to_string(v)))
117 .collect()
118 }
119}
120
121fn key_source_to_string(
122 key_source: &(
123 elements::bitcoin::bip32::Fingerprint,
124 elements::bitcoin::bip32::DerivationPath,
125 ),
126) -> String {
127 format!("[{}]{}", key_source.0, key_source.1)
128}
129
130#[derive(uniffi::Object, Debug)]
132
133pub struct Issuance {
134 inner: lwk_common::Issuance,
135}
136
137#[uniffi::export]
138impl Issuance {
139 pub fn asset(&self) -> Option<AssetId> {
141 self.inner.asset().map(Into::into)
142 }
143
144 pub fn token(&self) -> Option<AssetId> {
146 self.inner.token().map(Into::into)
147 }
148
149 pub fn prev_vout(&self) -> Option<u32> {
151 self.inner.prev_vout()
152 }
153
154 pub fn prev_txid(&self) -> Option<Arc<Txid>> {
156 self.inner.prev_txid().map(|e| Arc::new(e.into()))
157 }
158
159 pub fn is_null(&self) -> bool {
161 self.inner.is_null()
162 }
163
164 pub fn is_issuance(&self) -> bool {
166 self.inner.is_issuance()
167 }
168
169 pub fn is_reissuance(&self) -> bool {
171 self.inner.is_reissuance()
172 }
173
174 pub fn is_confidential(&self) -> bool {
176 self.inner.is_confidential()
177 }
178
179 pub fn asset_satoshi(&self) -> Option<u64> {
181 self.inner.asset_satoshi()
182 }
183
184 pub fn token_satoshi(&self) -> Option<u64> {
186 self.inner.token_satoshi()
187 }
188}
189
190impl From<lwk_common::Issuance> for Issuance {
191 fn from(inner: lwk_common::Issuance) -> Self {
192 Self { inner }
193 }
194}
195
196#[derive(uniffi::Object, Debug)]
197pub struct Recipient {
198 inner: lwk_common::Recipient,
199}
200
201impl From<lwk_common::Recipient> for Recipient {
202 fn from(inner: lwk_common::Recipient) -> Self {
203 Self { inner }
204 }
205}
206
207#[uniffi::export]
208impl Recipient {
209 pub fn asset(&self) -> Option<AssetId> {
210 self.inner.asset.map(Into::into)
211 }
212
213 pub fn value(&self) -> Option<u64> {
214 self.inner.value
215 }
216
217 pub fn address(&self) -> Option<Arc<Address>> {
218 self.inner
219 .address
220 .as_ref()
221 .map(|e| Arc::new(e.clone().into()))
222 }
223 pub fn vout(&self) -> u32 {
224 self.inner.vout
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use std::str::FromStr;
231
232 use crate::{types::AssetId, Network, Pset, Wollet, WolletDescriptor};
233
234 #[test]
235 fn pset_details() {
236 let pset = include_str!("../test_data/pset_details/pset.base64");
237 let pset = Pset::new(pset).unwrap();
238
239 let descriptor = include_str!("../test_data/pset_details/desc");
240 let descriptor = WolletDescriptor::new(descriptor).unwrap();
241 let network = Network::regtest_default();
242 let wollet = Wollet::new(&network, &descriptor, None).unwrap();
243
244 let details = wollet.pset_details(&pset).unwrap();
245 assert_eq!(details.balance().fee(), 254);
246
247 let balances = details.balance().balances();
248 assert_eq!(balances.len(), 1);
249 let expected_asset_id = "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225";
250 let asset_id = elements::AssetId::from_str(expected_asset_id).unwrap();
251 let asset_id: AssetId = asset_id.into();
252 let val = balances.get(&asset_id).unwrap();
253 assert_eq!(*val, -1254);
254
255 let signatures = details.signatures();
256 assert_eq!(signatures.len(), 1);
257
258 assert_eq!(format!("{:?}", signatures[0].has_signature()), "{\"02ab89406d9cf32ff1819838136eecb65c07add8e8ef1cd2d6c64bab1d85606453\": \"[6e055509]87'/1'/0'/0/0\"}");
259 assert_eq!(format!("{:?}", signatures[0].missing_signature()), "{\"03c1d0c7ddab5bd5bffbe0bf04a8a570eeabd9b6356358ecaacc242f658c7d5aad\": \"[281e2239]87'/1'/0'/0/0\"}");
260
261 let issuances = details.inputs_issuances();
262 assert_eq!(issuances.len(), 1);
263 assert!(!issuances[0].is_issuance());
264 assert!(!issuances[0].is_reissuance());
265 assert!(issuances[0].is_null());
266 assert!(!issuances[0].is_confidential());
267
268 let recipients = details.balance().recipients();
269 assert_eq!(recipients.len(), 1);
270 assert_eq!(recipients[0].vout(), 0);
271 assert_eq!(
272 recipients[0].asset().unwrap().to_string(),
273 "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225"
274 );
275 assert_eq!(recipients[0].value(), Some(1000));
276 assert_eq!(
277 recipients[0].address().unwrap().to_string(),
278 "AzpoyU5wJFcfdq6sh5ETbqCBA1oLuoLYk5UGJbYLGj3wKMurrVQiX1Djq67JHFAVt1hA5QVq41iNuVmy"
279 );
280 }
281}