mirror of
https://github.com/aljazceru/breez-sdk-liquid.git
synced 2026-01-04 14:54:21 +01:00
Receive: Switch payment to pending state when lockup is in the mempool (#301)
* feat: switch to pending state when receive lockup is in the mempool * rebasing * fix: move socket update logic to sub-crate * Update payments query, to avoid duplicate Receive Swaps This can happen if the app is stopped before the temporary lockup tx is removed from the DB. The Receive Swap would then forever result in two payments in list_payments. * Add comments to clarify use of temp lockup txid * Re-generate flutter bridge bindings * feat: set Payment `tx_id` as optional and change `list_payments` logic * fix: debug typo * fix: undo `remove_temporary_tx` changes * fix: switch to full join rather than manual filtering * fix: bindings * fix: improve error handling when tx data is not present * fix: RN bindings * fix: exclude Created receives from the list * fix: fixing nits * Re-generate FRB bindings --------- Co-authored-by: ok300 <106775972+ok300@users.noreply.github.com>
This commit is contained in:
2
cli/Cargo.lock
generated
2
cli/Cargo.lock
generated
@@ -381,7 +381,7 @@ checksum = "829a082bd3761fde7476dc2ed85ca56c11628948460ece621e4f56fef5046567"
|
||||
[[package]]
|
||||
name = "boltz-client"
|
||||
version = "0.1.3"
|
||||
source = "git+https://github.com/ok300/boltz-rust?branch=ok300-breez-latest-06-05#9a0b5fdcb71271d804aa755870fc079e9605e2c4"
|
||||
source = "git+https://github.com/hydra-yse/boltz-rust?branch=yse-breez-latest#66cdf65ba889a25a5274af3d27f5f52a2d4e3cc9"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"bitcoin 0.31.2",
|
||||
|
||||
2
lib/Cargo.lock
generated
2
lib/Cargo.lock
generated
@@ -501,7 +501,7 @@ checksum = "829a082bd3761fde7476dc2ed85ca56c11628948460ece621e4f56fef5046567"
|
||||
[[package]]
|
||||
name = "boltz-client"
|
||||
version = "0.1.3"
|
||||
source = "git+https://github.com/ok300/boltz-rust?branch=ok300-breez-latest-06-05#9a0b5fdcb71271d804aa755870fc079e9605e2c4"
|
||||
source = "git+https://github.com/hydra-yse/boltz-rust?branch=yse-breez-latest#66cdf65ba889a25a5274af3d27f5f52a2d4e3cc9"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"bitcoin 0.31.2",
|
||||
|
||||
@@ -14,12 +14,6 @@ void store_dart_post_cobject(DartPostCObjectFnType ptr);
|
||||
// EXTRA END
|
||||
typedef struct _Dart_Handle* Dart_Handle;
|
||||
|
||||
/**
|
||||
* Claim tx feerate, in sats per vbyte.
|
||||
* Since the Liquid blocks are consistently empty for now, we hardcode the minimum feerate.
|
||||
*/
|
||||
#define LIQUID_CLAIM_TX_FEERATE_MSAT 100.0
|
||||
|
||||
typedef struct wire_cst_list_prim_u_8_strict {
|
||||
uint8_t *ptr;
|
||||
int32_t len;
|
||||
|
||||
@@ -117,7 +117,7 @@ dictionary LNInvoice {
|
||||
};
|
||||
|
||||
dictionary Payment {
|
||||
string tx_id;
|
||||
string? tx_id = null;
|
||||
string? swap_id = null;
|
||||
u32 timestamp;
|
||||
u64 amount_sat;
|
||||
|
||||
@@ -15,7 +15,7 @@ frb = ["dep:flutter_rust_bridge"]
|
||||
anyhow = { workspace = true }
|
||||
bip39 = { version = "2.0.0", features = ["serde"] }
|
||||
#boltz-client = { git = "https://github.com/SatoshiPortal/boltz-rust", rev = "a05731cc33030ada9ae14afcafe0cded22842ba6" }
|
||||
boltz-client = { git = "https://github.com/ok300/boltz-rust", branch = "ok300-breez-latest-06-05" }
|
||||
boltz-client = { git = "https://github.com/hydra-yse/boltz-rust", branch = "yse-breez-latest" }
|
||||
chrono = "0.4"
|
||||
env_logger = "0.11"
|
||||
flutter_rust_bridge = { version = "=2.0.0-dev.38", features = ["chrono"], optional = true }
|
||||
|
||||
@@ -1028,7 +1028,7 @@ impl SseDecode for Option<u64> {
|
||||
impl SseDecode for crate::model::Payment {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
let mut var_txId = <String>::sse_decode(deserializer);
|
||||
let mut var_txId = <Option<String>>::sse_decode(deserializer);
|
||||
let mut var_swapId = <Option<String>>::sse_decode(deserializer);
|
||||
let mut var_timestamp = <u32>::sse_decode(deserializer);
|
||||
let mut var_amountSat = <u64>::sse_decode(deserializer);
|
||||
@@ -2091,7 +2091,7 @@ impl SseEncode for Option<u64> {
|
||||
impl SseEncode for crate::model::Payment {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
<String>::sse_encode(self.tx_id, serializer);
|
||||
<Option<String>>::sse_encode(self.tx_id, serializer);
|
||||
<Option<String>>::sse_encode(self.swap_id, serializer);
|
||||
<u32>::sse_encode(self.timestamp, serializer);
|
||||
<u64>::sse_encode(self.amount_sat, serializer);
|
||||
|
||||
@@ -288,6 +288,8 @@ pub(crate) struct ReceiveSwap {
|
||||
pub(crate) claim_fees_sat: u64,
|
||||
/// Persisted as soon as a claim tx is broadcast
|
||||
pub(crate) claim_tx_id: Option<String>,
|
||||
/// Until the lockup tx is seen in the mempool, it contains the swap creation time.
|
||||
/// Afterwards, it shows the lockup tx creation time.
|
||||
pub(crate) created_at: u32,
|
||||
pub(crate) state: PaymentState,
|
||||
}
|
||||
@@ -530,8 +532,7 @@ pub struct PaymentSwapData {
|
||||
/// By default, this is an onchain tx. It may represent a swap, if swap metadata is available.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
pub struct Payment {
|
||||
/// The tx ID of the onchain transaction
|
||||
pub tx_id: String,
|
||||
pub tx_id: Option<String>,
|
||||
|
||||
/// The swap ID, if any swap is associated with this payment
|
||||
pub swap_id: Option<String>,
|
||||
@@ -582,9 +583,29 @@ pub struct Payment {
|
||||
pub status: PaymentState,
|
||||
}
|
||||
impl Payment {
|
||||
pub(crate) fn from(tx: PaymentTxData, swap: Option<PaymentSwapData>) -> Payment {
|
||||
pub(crate) fn from_pending_swap(swap: PaymentSwapData, payment_type: PaymentType) -> Payment {
|
||||
let amount_sat = match payment_type {
|
||||
PaymentType::Receive => swap.receiver_amount_sat,
|
||||
PaymentType::Send => swap.payer_amount_sat,
|
||||
};
|
||||
|
||||
Payment {
|
||||
tx_id: tx.tx_id,
|
||||
tx_id: None,
|
||||
swap_id: Some(swap.swap_id),
|
||||
timestamp: swap.created_at,
|
||||
amount_sat,
|
||||
fees_sat: swap.payer_amount_sat - swap.receiver_amount_sat,
|
||||
preimage: swap.preimage,
|
||||
refund_tx_id: swap.refund_tx_id,
|
||||
refund_tx_amount_sat: swap.refund_tx_amount_sat,
|
||||
payment_type,
|
||||
status: swap.status,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_tx_data(tx: PaymentTxData, swap: Option<PaymentSwapData>) -> Payment {
|
||||
Payment {
|
||||
tx_id: Some(tx.tx_id),
|
||||
swap_id: swap.as_ref().map(|s| s.swap_id.clone()),
|
||||
timestamp: match swap {
|
||||
Some(ref swap) => swap.created_at,
|
||||
|
||||
@@ -11,6 +11,7 @@ pub(crate) fn current_migrations() -> Vec<&'static str> {
|
||||
created_at INTEGER NOT NULL,
|
||||
claim_fees_sat INTEGER NOT NULL,
|
||||
claim_tx_id TEXT,
|
||||
lockup_tx_id TEXT,
|
||||
state INTEGER NOT NULL
|
||||
) STRICT;",
|
||||
"CREATE TABLE IF NOT EXISTS send_swaps (
|
||||
|
||||
@@ -3,7 +3,7 @@ mod migrations;
|
||||
pub(crate) mod receive;
|
||||
pub(crate) mod send;
|
||||
|
||||
use std::{collections::HashMap, fs::create_dir_all, path::PathBuf, str::FromStr};
|
||||
use std::{fs::create_dir_all, path::PathBuf, str::FromStr};
|
||||
|
||||
use anyhow::Result;
|
||||
use migrations::current_migrations;
|
||||
@@ -118,7 +118,10 @@ impl Persister {
|
||||
ss.state,
|
||||
rtx.amount_sat
|
||||
FROM payment_tx_data AS ptx -- Payment tx (each tx results in a Payment)
|
||||
LEFT JOIN receive_swaps AS rs -- Receive Swap data
|
||||
FULL JOIN (
|
||||
SELECT * FROM receive_swaps
|
||||
WHERE claim_tx_id IS NOT NULL OR lockup_tx_id IS NOT NULL
|
||||
) rs -- Receive Swap data (by claim)
|
||||
ON ptx.tx_id = rs.claim_tx_id
|
||||
LEFT JOIN send_swaps AS ss -- Send Swap data
|
||||
ON ptx.tx_id = ss.lockup_tx_id
|
||||
@@ -133,13 +136,17 @@ impl Persister {
|
||||
}
|
||||
|
||||
fn sql_row_to_payment(&self, row: &Row) -> Result<Payment, rusqlite::Error> {
|
||||
let tx = PaymentTxData {
|
||||
tx_id: row.get(0)?,
|
||||
let maybe_tx_tx_id: Result<String, rusqlite::Error> = row.get(0);
|
||||
let tx = match maybe_tx_tx_id {
|
||||
Ok(ref tx_id) => Some(PaymentTxData {
|
||||
tx_id: tx_id.to_string(),
|
||||
timestamp: row.get(1)?,
|
||||
amount_sat: row.get(2)?,
|
||||
fees_sat: row.get(3)?,
|
||||
payment_type: row.get(4)?,
|
||||
is_confirmed: row.get(5)?,
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let maybe_receive_swap_id: Option<String> = row.get(6)?;
|
||||
@@ -157,8 +164,9 @@ impl Persister {
|
||||
let maybe_send_swap_state: Option<PaymentState> = row.get(17)?;
|
||||
let maybe_send_swap_refund_tx_amount_sat: Option<u64> = row.get(18)?;
|
||||
|
||||
let swap = match maybe_receive_swap_id {
|
||||
Some(receive_swap_id) => Some(PaymentSwapData {
|
||||
let (swap, payment_type) = match maybe_receive_swap_id {
|
||||
Some(receive_swap_id) => (
|
||||
Some(PaymentSwapData {
|
||||
swap_id: receive_swap_id,
|
||||
created_at: maybe_receive_swap_created_at.unwrap_or(utils::now()),
|
||||
preimage: None,
|
||||
@@ -168,7 +176,10 @@ impl Persister {
|
||||
refund_tx_amount_sat: None,
|
||||
status: maybe_receive_swap_receiver_state.unwrap_or(PaymentState::Created),
|
||||
}),
|
||||
None => maybe_send_swap_id.map(|send_swap_id| PaymentSwapData {
|
||||
PaymentType::Receive,
|
||||
),
|
||||
None => (
|
||||
maybe_send_swap_id.map(|send_swap_id| PaymentSwapData {
|
||||
swap_id: send_swap_id,
|
||||
created_at: maybe_send_swap_created_at.unwrap_or(utils::now()),
|
||||
preimage: maybe_send_swap_preimage,
|
||||
@@ -178,9 +189,16 @@ impl Persister {
|
||||
refund_tx_amount_sat: maybe_send_swap_refund_tx_amount_sat,
|
||||
status: maybe_send_swap_state.unwrap_or(PaymentState::Created),
|
||||
}),
|
||||
PaymentType::Send,
|
||||
),
|
||||
};
|
||||
|
||||
Ok(Payment::from(tx, swap))
|
||||
match (tx, swap.clone()) {
|
||||
(None, None) => Err(maybe_tx_tx_id.err().unwrap()),
|
||||
(None, Some(swap)) => Ok(Payment::from_pending_swap(swap, payment_type)),
|
||||
(Some(tx), None) => Ok(Payment::from_tx_data(tx, None)),
|
||||
(Some(tx), Some(swap)) => Ok(Payment::from_tx_data(tx, Some(swap))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_payment(&self, id: String) -> Result<Option<Payment>> {
|
||||
@@ -194,18 +212,15 @@ impl Persister {
|
||||
.optional()?)
|
||||
}
|
||||
|
||||
pub fn get_payments(&self) -> Result<HashMap<String, Payment>> {
|
||||
pub fn get_payments(&self) -> Result<Vec<Payment>> {
|
||||
let con = self.get_connection()?;
|
||||
|
||||
// Assumes there is no swap chaining (send swap lockup tx = receive swap claim tx)
|
||||
let mut stmt = con.prepare(&self.select_payment_query(None))?;
|
||||
|
||||
let data = stmt
|
||||
.query_map(params![], |row| {
|
||||
self.sql_row_to_payment(row).map(|p| (p.tx_id.clone(), p))
|
||||
})?
|
||||
let payments: Vec<Payment> = stmt
|
||||
.query_map(params![], |row| self.sql_row_to_payment(row))?
|
||||
.map(|i| i.unwrap())
|
||||
.collect();
|
||||
Ok(data)
|
||||
Ok(payments)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,8 +178,9 @@ impl Persister {
|
||||
swap_id: &str,
|
||||
to_state: PaymentState,
|
||||
claim_tx_id: Option<&str>,
|
||||
lockup_tx_id: Option<&str>,
|
||||
) -> Result<(), PaymentError> {
|
||||
// Do not overwrite claim_tx_id
|
||||
// Do not overwrite claim_tx_id or lockup_tx_id
|
||||
let con: Connection = self.get_connection()?;
|
||||
con.execute(
|
||||
"UPDATE receive_swaps
|
||||
@@ -189,12 +190,17 @@ impl Persister {
|
||||
WHEN claim_tx_id IS NULL THEN :claim_tx_id
|
||||
ELSE claim_tx_id
|
||||
END,
|
||||
|
||||
lockup_tx_id =
|
||||
CASE
|
||||
WHEN lockup_tx_id IS NULL THEN :lockup_tx_id
|
||||
ELSE lockup_tx_id
|
||||
END,
|
||||
state = :state
|
||||
WHERE
|
||||
id = :id",
|
||||
named_params! {
|
||||
":id": swap_id,
|
||||
":lockup_tx_id": lockup_tx_id,
|
||||
":claim_tx_id": claim_tx_id,
|
||||
":state": to_state,
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::{str::FromStr, sync::Arc};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use boltz_client::swaps::boltz::RevSwapStates;
|
||||
use boltz_client::swaps::boltzv2;
|
||||
use log::{debug, error, info, warn};
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
@@ -40,7 +41,10 @@ impl ReceiveSwapStateHandler {
|
||||
}
|
||||
|
||||
/// Handles status updates from Boltz for Receive swaps
|
||||
pub(crate) async fn on_new_status(&self, swap_state: &str, id: &str) -> Result<()> {
|
||||
pub(crate) async fn on_new_status(&self, update: &boltzv2::Update) -> Result<()> {
|
||||
let id = update.id();
|
||||
let swap_state = update.status();
|
||||
|
||||
let receive_swap = self
|
||||
.persister
|
||||
.fetch_receive_swap(id)?
|
||||
@@ -49,33 +53,41 @@ impl ReceiveSwapStateHandler {
|
||||
info!("Handling Receive Swap transition to {swap_state:?} for swap {id}");
|
||||
|
||||
match RevSwapStates::from_str(swap_state) {
|
||||
Ok(RevSwapStates::SwapExpired
|
||||
Ok(
|
||||
RevSwapStates::SwapExpired
|
||||
| RevSwapStates::InvoiceExpired
|
||||
| RevSwapStates::TransactionFailed
|
||||
| RevSwapStates::TransactionRefunded) => {
|
||||
| RevSwapStates::TransactionRefunded,
|
||||
) => {
|
||||
error!("Swap {id} entered into an unrecoverable state: {swap_state:?}");
|
||||
self.update_swap_info(id, Failed, None).await?;
|
||||
self.update_swap_info(id, Failed, None, None).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// The lockup tx is in the mempool and we accept 0-conf => try to claim
|
||||
// TODO Add 0-conf preconditions check: https://github.com/breez/breez-liquid-sdk/issues/187
|
||||
Ok(RevSwapStates::TransactionMempool
|
||||
// The lockup tx is confirmed => try to claim
|
||||
| RevSwapStates::TransactionConfirmed) => {
|
||||
Ok(RevSwapStates::TransactionMempool) => {
|
||||
let boltzv2::Update::TransactionMempool { transaction, .. } = update else {
|
||||
return Err(anyhow!("Unexpected payload from Boltz status stream"));
|
||||
};
|
||||
let lockup_tx_id = &transaction.id;
|
||||
self.update_swap_info(id, Pending, None, Some(lockup_tx_id))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
Ok(RevSwapStates::TransactionConfirmed) => {
|
||||
match receive_swap.claim_tx_id {
|
||||
Some(claim_tx_id) => {
|
||||
warn!("Claim tx for Receive Swap {id} was already broadcast: txid {claim_tx_id}")
|
||||
}
|
||||
None => {
|
||||
self.update_swap_info(&receive_swap.id, Pending, None)
|
||||
self.update_swap_info(&receive_swap.id, Pending, None, None)
|
||||
.await?;
|
||||
match self.claim(&receive_swap).await {
|
||||
Ok(_) => {}
|
||||
Err(err) => match err {
|
||||
PaymentError::AlreadyClaimed => warn!("Funds already claimed for Receive Swap {id}"),
|
||||
_ => error!("Claim for Receive Swap {id} failed: {err}")
|
||||
PaymentError::AlreadyClaimed => {
|
||||
warn!("Funds already claimed for Receive Swap {id}")
|
||||
}
|
||||
_ => error!("Claim for Receive Swap {id} failed: {err}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,9 +97,11 @@ impl ReceiveSwapStateHandler {
|
||||
Ok(_) => {
|
||||
debug!("Unhandled state for Receive Swap {id}: {swap_state}");
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
|
||||
_ => Err(anyhow!("Invalid RevSwapState for Receive Swap {id}: {swap_state}")),
|
||||
_ => Err(anyhow!(
|
||||
"Invalid RevSwapState for Receive Swap {id}: {swap_state}"
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,9 +111,10 @@ impl ReceiveSwapStateHandler {
|
||||
swap_id: &str,
|
||||
to_state: PaymentState,
|
||||
claim_tx_id: Option<&str>,
|
||||
lockup_tx_id: Option<&str>,
|
||||
) -> Result<(), PaymentError> {
|
||||
info!(
|
||||
"Transitioning Receive swap {swap_id} to {to_state:?} (claim_tx_id = {claim_tx_id:?})"
|
||||
"Transitioning Receive swap {swap_id} to {to_state:?} (claim_tx_id = {claim_tx_id:?}, lockup_tx_id = {lockup_tx_id:?})"
|
||||
);
|
||||
|
||||
let swap = self
|
||||
@@ -109,11 +124,18 @@ impl ReceiveSwapStateHandler {
|
||||
.ok_or(PaymentError::Generic {
|
||||
err: format!("Receive Swap not found {swap_id}"),
|
||||
})?;
|
||||
let payment_id = claim_tx_id.map(|c| c.to_string()).or(swap.claim_tx_id);
|
||||
let payment_id = claim_tx_id
|
||||
.or(lockup_tx_id)
|
||||
.map(|id| id.to_string())
|
||||
.or(swap.claim_tx_id);
|
||||
|
||||
Self::validate_state_transition(swap.state, to_state)?;
|
||||
self.persister
|
||||
.try_handle_receive_swap_update(swap_id, to_state, claim_tx_id)?;
|
||||
self.persister.try_handle_receive_swap_update(
|
||||
swap_id,
|
||||
to_state,
|
||||
claim_tx_id,
|
||||
lockup_tx_id,
|
||||
)?;
|
||||
|
||||
if let Some(payment_id) = payment_id {
|
||||
let _ = self.subscription_notifier.send(payment_id);
|
||||
@@ -143,7 +165,7 @@ impl ReceiveSwapStateHandler {
|
||||
is_confirmed: false,
|
||||
})?;
|
||||
|
||||
self.update_swap_info(swap_id, Pending, Some(&claim_tx_id))
|
||||
self.update_swap_info(swap_id, Pending, Some(&claim_tx_id), None)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -11,7 +11,6 @@ use std::{
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use boltz_client::lightning_invoice::Bolt11InvoiceDescription;
|
||||
use boltz_client::swaps::boltzv2;
|
||||
use boltz_client::ToHex;
|
||||
use boltz_client::{swaps::boltzv2::*, util::secrets::Preimage, Amount, Bolt11Invoice};
|
||||
use futures_util::stream::select_all;
|
||||
@@ -41,10 +40,6 @@ use crate::{
|
||||
utils,
|
||||
};
|
||||
|
||||
/// Claim tx feerate, in sats per vbyte.
|
||||
/// Since the Liquid blocks are consistently empty for now, we hardcode the minimum feerate.
|
||||
pub const LIQUID_CLAIM_TX_FEERATE_MSAT: f32 = 100.0;
|
||||
|
||||
pub const DEFAULT_DATA_DIR: &str = ".data";
|
||||
|
||||
pub(crate) trait ChainService: Send + Sync + BlockchainBackend {}
|
||||
@@ -224,19 +219,20 @@ impl LiquidSdk {
|
||||
}
|
||||
}
|
||||
update = updates_stream.recv() => match update {
|
||||
Ok(boltzv2::Update { id, status }) => {
|
||||
Ok(update) => {
|
||||
let _ = cloned.sync().await;
|
||||
match cloned.persister.fetch_send_swap_by_id(&id) {
|
||||
let id = update.id();
|
||||
match cloned.persister.fetch_send_swap_by_id(id) {
|
||||
Ok(Some(_)) => {
|
||||
match cloned.send_swap_state_handler.on_new_status(&status, &id).await {
|
||||
match cloned.send_swap_state_handler.on_new_status(&update).await {
|
||||
Ok(_) => info!("Successfully handled Send Swap {id} update"),
|
||||
Err(e) => error!("Failed to handle Send Swap {id} update: {e}")
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
match cloned.persister.fetch_receive_swap(&id) {
|
||||
match cloned.persister.fetch_receive_swap(id) {
|
||||
Ok(Some(_)) => {
|
||||
match cloned.receive_swap_state_handler.on_new_status(&status, &id).await {
|
||||
match cloned.receive_swap_state_handler.on_new_status(&update).await {
|
||||
Ok(_) => info!("Successfully handled Receive Swap {id} update"),
|
||||
Err(e) => error!("Failed to handle Receive Swap {id} update: {e}")
|
||||
}
|
||||
@@ -685,7 +681,7 @@ impl LiquidSdk {
|
||||
self.emit_payment_updated(Some(tx_id)).await?; // Emit Pending event
|
||||
|
||||
Ok(SendPaymentResponse {
|
||||
payment: Payment::from(tx_data, None),
|
||||
payment: Payment::from_tx_data(tx_data, None),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -958,7 +954,10 @@ impl LiquidSdk {
|
||||
.list_payments()
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|p| (p.tx_id.clone(), p))
|
||||
.filter_map(|payment| {
|
||||
let tx_id = payment.tx_id.clone();
|
||||
tx_id.map(|tx_id| (tx_id, payment))
|
||||
})
|
||||
.collect();
|
||||
if with_scan {
|
||||
self.onchain_wallet.full_scan().await?;
|
||||
@@ -989,7 +988,7 @@ impl LiquidSdk {
|
||||
if let Some(swap) = pending_receive_swaps_by_claim_tx_id.get(&tx_id) {
|
||||
if is_tx_confirmed {
|
||||
self.receive_swap_state_handler
|
||||
.update_swap_info(&swap.id, Complete, None)
|
||||
.update_swap_info(&swap.id, Complete, None, None)
|
||||
.await?;
|
||||
}
|
||||
} else if let Some(swap) = pending_send_swaps_by_refund_tx_id.get(&tx_id) {
|
||||
@@ -1027,7 +1026,7 @@ impl LiquidSdk {
|
||||
pub async fn list_payments(&self) -> Result<Vec<Payment>, PaymentError> {
|
||||
self.ensure_is_started().await?;
|
||||
|
||||
let mut payments: Vec<Payment> = self.persister.get_payments()?.values().cloned().collect();
|
||||
let mut payments: Vec<Payment> = self.persister.get_payments()?;
|
||||
payments.sort_by_key(|p| p.timestamp);
|
||||
Ok(payments)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use boltz_client::swaps::boltzv2;
|
||||
use boltz_client::swaps::{boltz::SubSwapStates, boltzv2::CreateSubmarineResponse};
|
||||
use boltz_client::util::secrets::Preimage;
|
||||
use boltz_client::{Amount, Bolt11Invoice, ToHex};
|
||||
@@ -55,7 +56,9 @@ impl SendSwapStateHandler {
|
||||
}
|
||||
|
||||
/// Handles status updates from Boltz for Send swaps
|
||||
pub(crate) async fn on_new_status(&self, swap_state: &str, id: &str) -> Result<()> {
|
||||
pub(crate) async fn on_new_status(&self, update: &boltzv2::Update) -> Result<()> {
|
||||
let id = update.id();
|
||||
let swap_state = update.status();
|
||||
let swap = self
|
||||
.persister
|
||||
.fetch_send_swap_by_id(id)?
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use std::str::FromStr;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use anyhow::Result;
|
||||
use lwk_wollet::elements::{LockTime, LockTime::*};
|
||||
|
||||
use crate::error::PaymentError;
|
||||
use anyhow::Result;
|
||||
use lwk_wollet::elements::LockTime::{self, *};
|
||||
|
||||
pub(crate) fn now() -> u32 {
|
||||
SystemTime::now()
|
||||
|
||||
@@ -849,7 +849,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
final arr = raw as List<dynamic>;
|
||||
if (arr.length != 10) throw Exception('unexpected arr length: expect 10 but see ${arr.length}');
|
||||
return Payment(
|
||||
txId: dco_decode_String(arr[0]),
|
||||
txId: dco_decode_opt_String(arr[0]),
|
||||
swapId: dco_decode_opt_String(arr[1]),
|
||||
timestamp: dco_decode_u_32(arr[2]),
|
||||
amountSat: dco_decode_u_64(arr[3]),
|
||||
@@ -1401,7 +1401,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
@protected
|
||||
Payment sse_decode_payment(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
var var_txId = sse_decode_String(deserializer);
|
||||
var var_txId = sse_decode_opt_String(deserializer);
|
||||
var var_swapId = sse_decode_opt_String(deserializer);
|
||||
var var_timestamp = sse_decode_u_32(deserializer);
|
||||
var var_amountSat = sse_decode_u_64(deserializer);
|
||||
@@ -1968,7 +1968,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
@protected
|
||||
void sse_encode_payment(Payment self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_String(self.txId, serializer);
|
||||
sse_encode_opt_String(self.txId, serializer);
|
||||
sse_encode_opt_String(self.swapId, serializer);
|
||||
sse_encode_u_32(self.timestamp, serializer);
|
||||
sse_encode_u_64(self.amountSat, serializer);
|
||||
|
||||
@@ -714,7 +714,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
|
||||
@protected
|
||||
void cst_api_fill_to_wire_payment(Payment apiObj, wire_cst_payment wireObj) {
|
||||
wireObj.tx_id = cst_encode_String(apiObj.txId);
|
||||
wireObj.tx_id = cst_encode_opt_String(apiObj.txId);
|
||||
wireObj.swap_id = cst_encode_opt_String(apiObj.swapId);
|
||||
wireObj.timestamp = cst_encode_u_32(apiObj.timestamp);
|
||||
wireObj.amount_sat = cst_encode_u_64(apiObj.amountSat);
|
||||
@@ -1935,5 +1935,3 @@ final class wire_cst_receive_payment_response extends ffi.Struct {
|
||||
final class wire_cst_send_payment_response extends ffi.Struct {
|
||||
external wire_cst_payment payment;
|
||||
}
|
||||
|
||||
const double LIQUID_CLAIM_TX_FEERATE_MSAT = 100.0;
|
||||
|
||||
@@ -245,8 +245,7 @@ enum Network {
|
||||
///
|
||||
/// By default, this is an onchain tx. It may represent a swap, if swap metadata is available.
|
||||
class Payment {
|
||||
/// The tx ID of the onchain transaction
|
||||
final String txId;
|
||||
final String? txId;
|
||||
|
||||
/// The swap ID, if any swap is associated with this payment
|
||||
final String? swapId;
|
||||
@@ -296,7 +295,7 @@ class Payment {
|
||||
final PaymentState status;
|
||||
|
||||
const Payment({
|
||||
required this.txId,
|
||||
this.txId,
|
||||
this.swapId,
|
||||
required this.timestamp,
|
||||
required this.amountSat,
|
||||
|
||||
@@ -898,5 +898,3 @@ final class wire_cst_send_payment_response extends ffi.Struct {
|
||||
|
||||
/// EXTRA BEGIN
|
||||
typedef WireSyncRust2DartDco = ffi.Pointer<DartCObject>;
|
||||
|
||||
const double LIQUID_CLAIM_TX_FEERATE_MSAT = 100.0;
|
||||
|
||||
@@ -274,7 +274,6 @@ fun asPayment(payment: ReadableMap): Payment? {
|
||||
if (!validateMandatoryFields(
|
||||
payment,
|
||||
arrayOf(
|
||||
"txId",
|
||||
"timestamp",
|
||||
"amountSat",
|
||||
"feesSat",
|
||||
@@ -285,7 +284,7 @@ fun asPayment(payment: ReadableMap): Payment? {
|
||||
) {
|
||||
return null
|
||||
}
|
||||
val txId = payment.getString("txId")!!
|
||||
val txId = if (hasNonNullKey(payment, "txId")) payment.getString("txId") else null
|
||||
val swapId = if (hasNonNullKey(payment, "swapId")) payment.getString("swapId") else null
|
||||
val timestamp = payment.getInt("timestamp").toUInt()
|
||||
val amountSat = payment.getDouble("amountSat").toULong()
|
||||
|
||||
@@ -324,8 +324,12 @@ enum BreezLiquidSDKMapper {
|
||||
}
|
||||
|
||||
static func asPayment(payment: [String: Any?]) throws -> Payment {
|
||||
guard let txId = payment["txId"] as? String else {
|
||||
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "txId", typeName: "Payment"))
|
||||
var txId: String?
|
||||
if hasNonNilKey(data: payment, key: "txId") {
|
||||
guard let txIdTmp = payment["txId"] as? String else {
|
||||
throw LiquidSdkError.Generic(message: errUnexpectedValue(fieldName: "txId"))
|
||||
}
|
||||
txId = txIdTmp
|
||||
}
|
||||
var swapId: String?
|
||||
if hasNonNilKey(data: payment, key: "swapId") {
|
||||
@@ -390,7 +394,7 @@ enum BreezLiquidSDKMapper {
|
||||
|
||||
static func dictionaryOf(payment: Payment) -> [String: Any?] {
|
||||
return [
|
||||
"txId": payment.txId,
|
||||
"txId": payment.txId == nil ? nil : payment.txId,
|
||||
"swapId": payment.swapId == nil ? nil : payment.swapId,
|
||||
"timestamp": payment.timestamp,
|
||||
"amountSat": payment.amountSat,
|
||||
|
||||
@@ -64,7 +64,7 @@ export interface LogEntry {
|
||||
}
|
||||
|
||||
export interface Payment {
|
||||
txId: string
|
||||
txId?: string
|
||||
swapId?: string
|
||||
timestamp: number
|
||||
amountSat: number
|
||||
|
||||
Reference in New Issue
Block a user