1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use anyhow::{anyhow, Result};
use bdk::{database::AnyDatabase, wallet::tx_builder::TxOrdering, FeeRate, Wallet};
use bitcoin::{consensus::serialize, Transaction};
use payjoin::{PjUri, PjUriExt};
use crate::{
data::structs::SatsInvoice,
debug, info,
operations::bitcoin::{
balance::synchronize_wallet,
psbt::{sign_original_psbt, sign_psbt},
},
};
pub async fn create_transaction(
invoices: Vec<SatsInvoice>,
wallet: &Wallet<AnyDatabase>,
fee_rate: Option<FeeRate>,
) -> Result<Transaction> {
synchronize_wallet(wallet).await?;
let (psbt, details) = {
let mut builder = wallet.build_tx();
for invoice in invoices {
builder.add_recipient(invoice.address.script_pubkey(), invoice.amount);
}
builder.ordering(TxOrdering::Untouched); builder.enable_rbf().fee_rate(fee_rate.unwrap_or_default());
builder.finish()?
};
debug!(format!("Create transaction: {details:#?}"));
debug!("Unsigned PSBT:", base64::encode(&serialize(&psbt)));
let tx = sign_psbt(wallet, psbt).await?;
info!("PSBT successfully signed");
Ok(tx)
}
pub async fn create_payjoin(
invoices: Vec<SatsInvoice>,
wallet: &Wallet<AnyDatabase>,
fee_rate: Option<FeeRate>,
pj_uri: PjUri<'_>, ) -> Result<Transaction> {
let (psbt, details) = {
let mut builder = wallet.build_tx();
for invoice in invoices {
builder.add_recipient(invoice.address.script_pubkey(), invoice.amount);
}
builder.enable_rbf().fee_rate(fee_rate.unwrap_or_default());
builder.finish()?
};
debug!(format!("Request PayJoin transaction: {details:#?}"));
debug!("Unsigned Original PSBT:", base64::encode(&serialize(&psbt)));
let original_psbt = sign_original_psbt(wallet, psbt).await?;
info!("Original PSBT successfully signed");
let pj_params = payjoin::sender::Params::non_incentivizing();
let (req, ctx) = pj_uri.create_pj_request(original_psbt, pj_params)?;
info!("Built PayJoin request");
let response = reqwest::Client::new()
.post(req.url)
.header("Content-Type", "text/plain")
.body(reqwest::Body::from(req.body))
.send()
.await?;
info!("Got PayJoin response");
let res = response.text().await?;
info!(format!("Response: {res}"));
if res.contains("errorCode") {
return Err(anyhow!("Error performing payjoin: {res}"));
}
let payjoin_psbt = ctx.process_response(res.as_bytes())?;
debug!(
"Proposed PayJoin PSBT:",
base64::encode(&serialize(&payjoin_psbt))
);
let tx = sign_psbt(wallet, payjoin_psbt).await?;
Ok(tx)
}