mirror of
https://github.com/aljazceru/breez-sdk-liquid.git
synced 2025-12-24 01:14:22 +01:00
Fix cooperative refund and improve keypair generation (#223)
* feat: add random keypair generation * Encapsulate decode_keypair in SendSwap::get_refund_keypair() * Add refund_tx_id and refund_tx_amount_sat to Payment * fix: remove blocking on refund * fix: change `refund_private_key` order * fix: rebasing * fix: set `next_unused_address` as refund output * Handle refunds in `get_info`, `list_payments` (#226) * Exclude refund txs from payment list * Adjust balance calculation to account for refunds * fix: revert boltz changes and fix locktime * Replace subquery with LEFT JOIN to get refund data * Rewrite locktime check for more clarity * Rewrite locktime check for more clarity * Fix select_payment_query in case of refunds * Include boltz-client fixes (handling of unwraps for failed broadcasts) * Cargo.toml: Use boltz-client branch instead of commit --------- 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
@@ -370,7 +370,7 @@ checksum = "829a082bd3761fde7476dc2ed85ca56c11628948460ece621e4f56fef5046567"
|
||||
[[package]]
|
||||
name = "boltz-client"
|
||||
version = "0.1.3"
|
||||
source = "git+https://github.com/ok300/boltz-rust?branch=ok300-breez-latest-05-21#a6254147a0d00756880f5de38ac6000e49f61560"
|
||||
source = "git+https://github.com/ok300/boltz-rust?branch=ok300-breez-latest-05-27#faecead854e8b0803f744b25bd0c47853cc487da"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"bitcoin 0.31.2",
|
||||
|
||||
2
lib/Cargo.lock
generated
2
lib/Cargo.lock
generated
@@ -490,7 +490,7 @@ checksum = "829a082bd3761fde7476dc2ed85ca56c11628948460ece621e4f56fef5046567"
|
||||
[[package]]
|
||||
name = "boltz-client"
|
||||
version = "0.1.3"
|
||||
source = "git+https://github.com/ok300/boltz-rust?branch=ok300-breez-latest-05-21#a6254147a0d00756880f5de38ac6000e49f61560"
|
||||
source = "git+https://github.com/ok300/boltz-rust?branch=ok300-breez-latest-05-27#faecead854e8b0803f744b25bd0c47853cc487da"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"bitcoin 0.31.2",
|
||||
|
||||
@@ -68,6 +68,8 @@ typedef struct wire_cst_payment {
|
||||
uint64_t amount_sat;
|
||||
uint64_t *fees_sat;
|
||||
struct wire_cst_list_prim_u_8_strict *preimage;
|
||||
struct wire_cst_list_prim_u_8_strict *refund_tx_id;
|
||||
uint64_t *refund_tx_amount_sat;
|
||||
int32_t payment_type;
|
||||
int32_t status;
|
||||
} wire_cst_payment;
|
||||
|
||||
@@ -84,6 +84,8 @@ dictionary Payment {
|
||||
u64 amount_sat;
|
||||
u64? fees_sat = null;
|
||||
string? preimage = null;
|
||||
string? refund_tx_id = null;
|
||||
u64? refund_tx_amount_sat = null;
|
||||
PaymentType payment_type;
|
||||
PaymentState status;
|
||||
};
|
||||
|
||||
@@ -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-05-21" }
|
||||
boltz-client = { git = "https://github.com/ok300/boltz-rust", branch = "ok300-breez-latest-05-27" }
|
||||
flutter_rust_bridge = { version = "=2.0.0-dev.35", features = ["chrono"], optional = true }
|
||||
log = "0.4.20"
|
||||
lwk_common = "0.5.1"
|
||||
|
||||
@@ -254,6 +254,8 @@ impl CstDecode<crate::model::Payment> for wire_cst_payment {
|
||||
amount_sat: self.amount_sat.cst_decode(),
|
||||
fees_sat: self.fees_sat.cst_decode(),
|
||||
preimage: self.preimage.cst_decode(),
|
||||
refund_tx_id: self.refund_tx_id.cst_decode(),
|
||||
refund_tx_amount_sat: self.refund_tx_amount_sat.cst_decode(),
|
||||
payment_type: self.payment_type.cst_decode(),
|
||||
status: self.status.cst_decode(),
|
||||
}
|
||||
@@ -453,6 +455,8 @@ impl NewWithNullPtr for wire_cst_payment {
|
||||
amount_sat: Default::default(),
|
||||
fees_sat: core::ptr::null_mut(),
|
||||
preimage: core::ptr::null_mut(),
|
||||
refund_tx_id: core::ptr::null_mut(),
|
||||
refund_tx_amount_sat: core::ptr::null_mut(),
|
||||
payment_type: Default::default(),
|
||||
status: Default::default(),
|
||||
}
|
||||
@@ -893,6 +897,8 @@ pub struct wire_cst_payment {
|
||||
amount_sat: u64,
|
||||
fees_sat: *mut u64,
|
||||
preimage: *mut wire_cst_list_prim_u_8_strict,
|
||||
refund_tx_id: *mut wire_cst_list_prim_u_8_strict,
|
||||
refund_tx_amount_sat: *mut u64,
|
||||
payment_type: i32,
|
||||
status: i32,
|
||||
}
|
||||
|
||||
@@ -778,6 +778,8 @@ impl SseDecode for crate::model::Payment {
|
||||
let mut var_amountSat = <u64>::sse_decode(deserializer);
|
||||
let mut var_feesSat = <Option<u64>>::sse_decode(deserializer);
|
||||
let mut var_preimage = <Option<String>>::sse_decode(deserializer);
|
||||
let mut var_refundTxId = <Option<String>>::sse_decode(deserializer);
|
||||
let mut var_refundTxAmountSat = <Option<u64>>::sse_decode(deserializer);
|
||||
let mut var_paymentType = <crate::model::PaymentType>::sse_decode(deserializer);
|
||||
let mut var_status = <crate::model::PaymentState>::sse_decode(deserializer);
|
||||
return crate::model::Payment {
|
||||
@@ -787,6 +789,8 @@ impl SseDecode for crate::model::Payment {
|
||||
amount_sat: var_amountSat,
|
||||
fees_sat: var_feesSat,
|
||||
preimage: var_preimage,
|
||||
refund_tx_id: var_refundTxId,
|
||||
refund_tx_amount_sat: var_refundTxAmountSat,
|
||||
payment_type: var_paymentType,
|
||||
status: var_status,
|
||||
};
|
||||
@@ -1172,6 +1176,8 @@ impl flutter_rust_bridge::IntoDart for crate::model::Payment {
|
||||
self.amount_sat.into_into_dart().into_dart(),
|
||||
self.fees_sat.into_into_dart().into_dart(),
|
||||
self.preimage.into_into_dart().into_dart(),
|
||||
self.refund_tx_id.into_into_dart().into_dart(),
|
||||
self.refund_tx_amount_sat.into_into_dart().into_dart(),
|
||||
self.payment_type.into_into_dart().into_dart(),
|
||||
self.status.into_into_dart().into_dart(),
|
||||
]
|
||||
@@ -1577,6 +1583,8 @@ impl SseEncode for crate::model::Payment {
|
||||
<u64>::sse_encode(self.amount_sat, serializer);
|
||||
<Option<u64>>::sse_encode(self.fees_sat, serializer);
|
||||
<Option<String>>::sse_encode(self.preimage, serializer);
|
||||
<Option<String>>::sse_encode(self.refund_tx_id, serializer);
|
||||
<Option<u64>>::sse_encode(self.refund_tx_amount_sat, serializer);
|
||||
<crate::model::PaymentType>::sse_encode(self.payment_type, serializer);
|
||||
<crate::model::PaymentState>::sse_encode(self.status, serializer);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use boltz_client::network::Chain;
|
||||
use boltz_client::swaps::boltzv2::{
|
||||
CreateReverseResponse, CreateSubmarineResponse, Leaf, SwapTree,
|
||||
};
|
||||
use boltz_client::SwapType;
|
||||
use boltz_client::{Keypair, SwapType};
|
||||
use lwk_signer::SwSigner;
|
||||
use lwk_wollet::{ElectrumUrl, ElementsNetwork, WolletDescriptor};
|
||||
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef};
|
||||
@@ -201,8 +201,13 @@ pub(crate) struct SendSwap {
|
||||
pub(crate) refund_tx_id: Option<String>,
|
||||
pub(crate) created_at: u32,
|
||||
pub(crate) state: PaymentState,
|
||||
pub(crate) refund_private_key: String,
|
||||
}
|
||||
impl SendSwap {
|
||||
pub(crate) fn get_refund_keypair(&self) -> Result<Keypair, PaymentError> {
|
||||
utils::decode_keypair(&self.refund_private_key).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn get_boltz_create_response(
|
||||
&self,
|
||||
) -> Result<CreateSubmarineResponse, PaymentError> {
|
||||
@@ -460,6 +465,9 @@ pub struct PaymentSwapData {
|
||||
/// Amount received by the swap receiver
|
||||
pub receiver_amount_sat: u64,
|
||||
|
||||
pub refund_tx_id: Option<String>,
|
||||
pub refund_tx_amount_sat: Option<u64>,
|
||||
|
||||
/// Payment status derived from the swap status
|
||||
pub status: PaymentState,
|
||||
}
|
||||
@@ -495,6 +503,12 @@ pub struct Payment {
|
||||
/// In case of a Send swap, this is the preimage of the paid invoice (proof of payment).
|
||||
pub preimage: Option<String>,
|
||||
|
||||
/// For a Send swap which was refunded, this is the refund tx id
|
||||
pub refund_tx_id: Option<String>,
|
||||
|
||||
/// For a Send swap which was refunded, this is the refund amount
|
||||
pub refund_tx_amount_sat: Option<u64>,
|
||||
|
||||
pub payment_type: PaymentType,
|
||||
|
||||
/// Composite status representing the overall status of the payment.
|
||||
@@ -518,6 +532,8 @@ impl Payment {
|
||||
.as_ref()
|
||||
.map(|s| s.payer_amount_sat - s.receiver_amount_sat),
|
||||
preimage: swap.as_ref().and_then(|s| s.preimage.clone()),
|
||||
refund_tx_id: swap.as_ref().and_then(|s| s.refund_tx_id.clone()),
|
||||
refund_tx_amount_sat: swap.as_ref().and_then(|s| s.refund_tx_amount_sat),
|
||||
payment_type: tx.payment_type,
|
||||
status: match swap {
|
||||
Some(swap) => swap.status,
|
||||
|
||||
@@ -19,6 +19,7 @@ pub(crate) fn current_migrations() -> Vec<&'static str> {
|
||||
payer_amount_sat INTEGER NOT NULL,
|
||||
receiver_amount_sat INTEGER NOT NULL,
|
||||
create_response_json TEXT NOT NULL,
|
||||
refund_private_key TEXT NOT NULL,
|
||||
lockup_tx_id TEXT,
|
||||
refund_tx_id TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
|
||||
@@ -109,15 +109,21 @@ impl Persister {
|
||||
ss.id,
|
||||
ss.created_at,
|
||||
ss.preimage,
|
||||
ss.refund_tx_id,
|
||||
ss.payer_amount_sat,
|
||||
ss.receiver_amount_sat,
|
||||
ss.state
|
||||
FROM payment_tx_data AS ptx
|
||||
LEFT JOIN receive_swaps AS rs
|
||||
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
|
||||
ON ptx.tx_id = rs.claim_tx_id
|
||||
LEFT JOIN send_swaps AS ss
|
||||
LEFT JOIN send_swaps AS ss -- Send Swap data
|
||||
ON ptx.tx_id = ss.lockup_tx_id
|
||||
WHERE {}
|
||||
LEFT JOIN payment_tx_data AS rtx -- Refund tx data
|
||||
ON rtx.tx_id = ss.refund_tx_id
|
||||
WHERE -- Filter out refund txs from Payment tx list
|
||||
ptx.tx_id NOT IN (SELECT refund_tx_id FROM send_swaps WHERE refund_tx_id NOT NULL)
|
||||
AND {}
|
||||
",
|
||||
where_clause.unwrap_or("true")
|
||||
)
|
||||
@@ -137,12 +143,15 @@ impl Persister {
|
||||
let maybe_receive_swap_payer_amount_sat: Option<u64> = row.get(7)?;
|
||||
let maybe_receive_swap_receiver_amount_sat: Option<u64> = row.get(8)?;
|
||||
let maybe_receive_swap_receiver_state: Option<PaymentState> = row.get(9)?;
|
||||
|
||||
let maybe_send_swap_id: Option<String> = row.get(10)?;
|
||||
let maybe_send_swap_created_at: Option<u32> = row.get(11)?;
|
||||
let maybe_send_swap_preimage: Option<String> = row.get(12)?;
|
||||
let maybe_send_swap_payer_amount_sat: Option<u64> = row.get(13)?;
|
||||
let maybe_send_swap_receiver_amount_sat: Option<u64> = row.get(14)?;
|
||||
let maybe_send_swap_state: Option<PaymentState> = row.get(15)?;
|
||||
let maybe_send_swap_refund_tx_id: Option<String> = row.get(13)?;
|
||||
let maybe_send_swap_payer_amount_sat: Option<u64> = row.get(14)?;
|
||||
let maybe_send_swap_receiver_amount_sat: Option<u64> = row.get(15)?;
|
||||
let maybe_send_swap_state: Option<PaymentState> = row.get(16)?;
|
||||
let maybe_send_swap_refund_tx_amount_sat: Option<u64> = row.get(17)?;
|
||||
|
||||
let swap = match maybe_receive_swap_id {
|
||||
Some(receive_swap_id) => Some(PaymentSwapData {
|
||||
@@ -151,6 +160,8 @@ impl Persister {
|
||||
preimage: None,
|
||||
payer_amount_sat: maybe_receive_swap_payer_amount_sat.unwrap_or(0),
|
||||
receiver_amount_sat: maybe_receive_swap_receiver_amount_sat.unwrap_or(0),
|
||||
refund_tx_id: None,
|
||||
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 {
|
||||
@@ -159,6 +170,8 @@ impl Persister {
|
||||
preimage: maybe_send_swap_preimage,
|
||||
payer_amount_sat: maybe_send_swap_payer_amount_sat.unwrap_or(0),
|
||||
receiver_amount_sat: maybe_send_swap_receiver_amount_sat.unwrap_or(0),
|
||||
refund_tx_id: maybe_send_swap_refund_tx_id,
|
||||
refund_tx_amount_sat: maybe_send_swap_refund_tx_amount_sat,
|
||||
status: maybe_send_swap_state.unwrap_or(PaymentState::Created),
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ use rusqlite::{named_params, params, Connection, Row};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
impl Persister {
|
||||
pub(crate) fn insert_receive_swap(&self, receive_swap: ReceiveSwap) -> Result<()> {
|
||||
pub(crate) fn insert_receive_swap(&self, receive_swap: &ReceiveSwap) -> Result<()> {
|
||||
let con = self.get_connection()?;
|
||||
|
||||
let mut stmt = con.prepare(
|
||||
@@ -31,16 +31,16 @@ impl Persister {
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
)?;
|
||||
_ = stmt.execute((
|
||||
receive_swap.id,
|
||||
receive_swap.preimage,
|
||||
receive_swap.create_response_json,
|
||||
receive_swap.invoice,
|
||||
receive_swap.payer_amount_sat,
|
||||
receive_swap.receiver_amount_sat,
|
||||
receive_swap.created_at,
|
||||
receive_swap.claim_fees_sat,
|
||||
receive_swap.claim_tx_id,
|
||||
receive_swap.state,
|
||||
&receive_swap.id,
|
||||
&receive_swap.preimage,
|
||||
&receive_swap.create_response_json,
|
||||
&receive_swap.invoice,
|
||||
&receive_swap.payer_amount_sat,
|
||||
&receive_swap.receiver_amount_sat,
|
||||
&receive_swap.created_at,
|
||||
&receive_swap.claim_fees_sat,
|
||||
&receive_swap.claim_tx_id,
|
||||
&receive_swap.state,
|
||||
))?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::model::*;
|
||||
use crate::persist::Persister;
|
||||
|
||||
impl Persister {
|
||||
pub(crate) fn insert_send_swap(&self, send_swap: SendSwap) -> Result<()> {
|
||||
pub(crate) fn insert_send_swap(&self, send_swap: &SendSwap) -> Result<()> {
|
||||
let con = self.get_connection()?;
|
||||
|
||||
let mut stmt = con.prepare(
|
||||
@@ -22,23 +22,25 @@ impl Persister {
|
||||
payer_amount_sat,
|
||||
receiver_amount_sat,
|
||||
create_response_json,
|
||||
refund_private_key,
|
||||
lockup_tx_id,
|
||||
refund_tx_id,
|
||||
created_at,
|
||||
state
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
)?;
|
||||
_ = stmt.execute((
|
||||
send_swap.id,
|
||||
send_swap.invoice,
|
||||
send_swap.payer_amount_sat,
|
||||
send_swap.receiver_amount_sat,
|
||||
send_swap.create_response_json,
|
||||
send_swap.lockup_tx_id,
|
||||
send_swap.refund_tx_id,
|
||||
send_swap.created_at,
|
||||
send_swap.state,
|
||||
&send_swap.id,
|
||||
&send_swap.invoice,
|
||||
&send_swap.payer_amount_sat,
|
||||
&send_swap.receiver_amount_sat,
|
||||
&send_swap.create_response_json,
|
||||
&send_swap.refund_private_key,
|
||||
&send_swap.lockup_tx_id,
|
||||
&send_swap.refund_tx_id,
|
||||
&send_swap.created_at,
|
||||
&send_swap.state,
|
||||
))?;
|
||||
|
||||
Ok(())
|
||||
@@ -59,6 +61,7 @@ impl Persister {
|
||||
payer_amount_sat,
|
||||
receiver_amount_sat,
|
||||
create_response_json,
|
||||
refund_private_key,
|
||||
lockup_tx_id,
|
||||
refund_tx_id,
|
||||
created_at,
|
||||
@@ -85,10 +88,11 @@ impl Persister {
|
||||
payer_amount_sat: row.get(2)?,
|
||||
receiver_amount_sat: row.get(3)?,
|
||||
create_response_json: row.get(4)?,
|
||||
lockup_tx_id: row.get(5)?,
|
||||
refund_tx_id: row.get(6)?,
|
||||
created_at: row.get(7)?,
|
||||
state: row.get(8)?,
|
||||
refund_private_key: row.get(5)?,
|
||||
lockup_tx_id: row.get(6)?,
|
||||
refund_tx_id: row.get(7)?,
|
||||
created_at: row.get(8)?,
|
||||
state: row.get(9)?,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use boltz_client::network::Chain;
|
||||
use boltz_client::ToHex;
|
||||
use boltz_client::{
|
||||
network::electrum::ElectrumConfig,
|
||||
swaps::{
|
||||
@@ -13,7 +15,7 @@ use log::{debug, error, info, warn};
|
||||
use lwk_common::{singlesig_desc, Signer, Singlesig};
|
||||
use lwk_signer::{AnySigner, SwSigner};
|
||||
use lwk_wollet::bitcoin::Witness;
|
||||
use lwk_wollet::elements::hex::ToHex;
|
||||
use lwk_wollet::elements::{LockTime, LockTime::*};
|
||||
use lwk_wollet::hashes::{sha256, Hash};
|
||||
use lwk_wollet::{
|
||||
elements::{Address, Transaction},
|
||||
@@ -148,23 +150,6 @@ impl LiquidSdk {
|
||||
Ok(descriptor_str.parse()?)
|
||||
}
|
||||
|
||||
fn get_submarine_keys(&self, derivation_index: i32) -> Result<Keypair, PaymentError> {
|
||||
let mnemonic = self
|
||||
.lwk_signer
|
||||
.mnemonic()
|
||||
.ok_or(PaymentError::SignerError {
|
||||
err: "Could not claim: Mnemonic not found".to_string(),
|
||||
})?;
|
||||
let swap_key = SwapKey::from_submarine_account(
|
||||
&mnemonic.to_string(),
|
||||
"",
|
||||
self.network.into(),
|
||||
derivation_index as u64,
|
||||
)?;
|
||||
let lsk = LiquidSwapKey::try_from(swap_key)?;
|
||||
Ok(lsk.keypair)
|
||||
}
|
||||
|
||||
fn validate_state_transition(
|
||||
from_state: PaymentState,
|
||||
to_state: PaymentState,
|
||||
@@ -406,20 +391,12 @@ impl LiquidSdk {
|
||||
.persister
|
||||
.fetch_send_swap(id)?
|
||||
.ok_or(anyhow!("No ongoing Send Swap found for ID {id}"))?;
|
||||
let create_response: CreateSubmarineResponse =
|
||||
ongoing_send_swap.get_boltz_create_response()?;
|
||||
|
||||
let receiver_amount_sat = get_invoice_amount!(ongoing_send_swap.invoice);
|
||||
let keypair = self.get_submarine_keys(0)?;
|
||||
|
||||
match swap_state {
|
||||
SubSwapStates::TransactionClaimPending => {
|
||||
let lockup_tx_id = ongoing_send_swap.lockup_tx_id.ok_or(anyhow!(
|
||||
"Swap-in {id} is pending but no lockup txid is present"
|
||||
))?;
|
||||
|
||||
let keypair = ongoing_send_swap.get_refund_keypair()?;
|
||||
let swap_script = LBtcSwapScriptV2::submarine_from_swap_resp(
|
||||
&create_response,
|
||||
&ongoing_send_swap.get_boltz_create_response()?,
|
||||
keypair.public_key().into(),
|
||||
)
|
||||
.map_err(|e| anyhow!("Could not rebuild refund details for swap-in {id}: {e:?}"))?;
|
||||
@@ -445,21 +422,13 @@ impl LiquidSdk {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// If swap state is unrecoverable, try refunding
|
||||
SubSwapStates::TransactionLockupFailed
|
||||
| SubSwapStates::InvoiceFailedToPay
|
||||
| SubSwapStates::SwapExpired => {
|
||||
warn!("Swap-in {id} is in an unrecoverable state: {swap_state:?}");
|
||||
|
||||
// If swap state is unrecoverable, try refunding
|
||||
let swap_script = LBtcSwapScriptV2::submarine_from_swap_resp(
|
||||
&create_response,
|
||||
keypair.public_key().into(),
|
||||
)
|
||||
.map_err(|e| anyhow!("Could not rebuild refund details for swap-in {id}: {e:?}"))?;
|
||||
|
||||
let refund_tx_id = self
|
||||
.try_refund(id, &swap_script, &keypair, receiver_amount_sat)
|
||||
.await?;
|
||||
let refund_tx_id = self.try_refund(&ongoing_send_swap).await?;
|
||||
info!("Broadcast refund tx for Swap-in {id}. Tx id: {refund_tx_id}");
|
||||
self.try_handle_send_swap_update(id, Pending, None, None, Some(&refund_tx_id))
|
||||
.await?;
|
||||
@@ -495,13 +464,23 @@ impl LiquidSdk {
|
||||
for p in self.list_payments()? {
|
||||
match p.payment_type {
|
||||
PaymentType::Send => match p.status {
|
||||
PaymentState::Complete => confirmed_sent_sat += p.amount_sat,
|
||||
PaymentState::Failed => {}
|
||||
_ => pending_send_sat += p.amount_sat,
|
||||
Complete => confirmed_sent_sat += p.amount_sat,
|
||||
Failed => {
|
||||
confirmed_sent_sat += p.amount_sat;
|
||||
confirmed_received_sat += p.refund_tx_amount_sat.unwrap_or_default();
|
||||
}
|
||||
Pending => match p.refund_tx_amount_sat {
|
||||
Some(refund_tx_amount_sat) => {
|
||||
confirmed_sent_sat += p.amount_sat;
|
||||
pending_receive_sat += refund_tx_amount_sat;
|
||||
}
|
||||
None => pending_send_sat += p.amount_sat,
|
||||
},
|
||||
Created => pending_send_sat += p.amount_sat,
|
||||
},
|
||||
PaymentType::Receive => match p.status {
|
||||
PaymentState::Complete => confirmed_received_sat += p.amount_sat,
|
||||
PaymentState::Failed => {}
|
||||
Complete => confirmed_received_sat += p.amount_sat,
|
||||
Failed => {}
|
||||
_ => pending_receive_sat += p.amount_sat,
|
||||
},
|
||||
}
|
||||
@@ -652,50 +631,118 @@ impl LiquidSdk {
|
||||
|
||||
async fn new_refund_tx(
|
||||
&self,
|
||||
swap_id: &str,
|
||||
swap_script: &LBtcSwapScriptV2,
|
||||
) -> Result<LBtcSwapTxV2, PaymentError> {
|
||||
let wallet = self.lwk_wollet.lock().await;
|
||||
let output_address = wallet.address(Some(0))?.address().to_string();
|
||||
let output_address = self.next_unused_address().await?.to_string();
|
||||
let network_config = self.network_config();
|
||||
Ok(LBtcSwapTxV2::new_refund(
|
||||
swap_script.clone(),
|
||||
&output_address,
|
||||
&network_config,
|
||||
self.boltz_url_v2().to_string(),
|
||||
swap_id.to_string(),
|
||||
)?)
|
||||
}
|
||||
|
||||
async fn try_refund(
|
||||
async fn try_refund_cooperative(
|
||||
&self,
|
||||
swap_id: &str,
|
||||
swap_script: &LBtcSwapScriptV2,
|
||||
keypair: &Keypair,
|
||||
amount_sat: u64,
|
||||
swap: &SendSwap,
|
||||
refund_tx: &LBtcSwapTxV2,
|
||||
broadcast_fees_sat: Amount,
|
||||
is_lowball: Option<(&BoltzApiClientV2, Chain)>,
|
||||
) -> Result<String, PaymentError> {
|
||||
let refund_tx = self.new_refund_tx(swap_script).await?;
|
||||
info!("Initiating cooperative refund for Send Swap {}", &swap.id);
|
||||
let tx = refund_tx.sign_refund(
|
||||
&swap.get_refund_keypair()?,
|
||||
broadcast_fees_sat,
|
||||
Some((&self.boltz_client_v2(), &swap.id)),
|
||||
)?;
|
||||
|
||||
let refund_tx_id = refund_tx.broadcast(&tx, &self.network_config(), is_lowball)?;
|
||||
info!(
|
||||
"Successfully broadcast cooperative refund for Send Swap {}",
|
||||
&swap.id
|
||||
);
|
||||
Ok(refund_tx_id.clone())
|
||||
}
|
||||
|
||||
async fn try_refund_non_cooperative(
|
||||
&self,
|
||||
swap: &SendSwap,
|
||||
swap_script: &LBtcSwapScriptV2,
|
||||
refund_tx: LBtcSwapTxV2,
|
||||
broadcast_fees_sat: Amount,
|
||||
is_lowball: Option<(&BoltzApiClientV2, Chain)>,
|
||||
) -> Result<String, PaymentError> {
|
||||
info!(
|
||||
"Initiating non-cooperative refund for Send Swap {}",
|
||||
&swap.id
|
||||
);
|
||||
|
||||
let current_height = self.lwk_wollet.lock().await.tip().height();
|
||||
let locktime_from_height =
|
||||
LockTime::from_height(current_height).map_err(|e| PaymentError::Generic {
|
||||
err: format!("Cannot convert current block height to lock time: {e:?}"),
|
||||
})?;
|
||||
|
||||
info!("locktime info: locktime_from_height = {locktime_from_height:?}, swap_script.locktime = {:?}", swap_script.locktime);
|
||||
let is_locktime_satisfied = match (locktime_from_height, swap_script.locktime) {
|
||||
(Blocks(n), Blocks(lock_time)) => n >= lock_time,
|
||||
(Seconds(n), Seconds(lock_time)) => n >= lock_time,
|
||||
_ => false, // Not using the same units
|
||||
};
|
||||
if !is_locktime_satisfied {
|
||||
return Err(PaymentError::Generic {
|
||||
err: format!(
|
||||
"Cannot refund non-cooperatively. Lock time not elapsed yet. Current tip: {:?}. Script lock time: {:?}",
|
||||
locktime_from_height, swap_script.locktime
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
let tx = refund_tx.sign_refund(&swap.get_refund_keypair()?, broadcast_fees_sat, None)?;
|
||||
let refund_tx_id = refund_tx.broadcast(&tx, &self.network_config(), is_lowball)?;
|
||||
info!(
|
||||
"Successfully broadcast non-cooperative refund for swap-in {}",
|
||||
swap.id
|
||||
);
|
||||
Ok(refund_tx_id)
|
||||
}
|
||||
|
||||
async fn try_refund(&self, swap: &SendSwap) -> Result<String, PaymentError> {
|
||||
let id = &swap.id;
|
||||
let swap_script = LBtcSwapScriptV2::submarine_from_swap_resp(
|
||||
&swap.get_boltz_create_response()?,
|
||||
swap.get_refund_keypair()?.public_key().into(),
|
||||
)
|
||||
.map_err(|e| anyhow!("Could not rebuild refund details for swap-in {id}: {e:?}"))?;
|
||||
|
||||
let refund_tx = self.new_refund_tx(&swap.id, &swap_script).await?;
|
||||
let amount_sat = get_invoice_amount!(swap.invoice);
|
||||
let broadcast_fees_sat =
|
||||
Amount::from_sat(self.get_broadcast_fee_estimation(amount_sat).await?);
|
||||
let client = self.boltz_client_v2();
|
||||
let is_lowball = Some((&client, boltz_client::network::Chain::from(self.network)));
|
||||
let is_lowball = match self.network {
|
||||
Network::Liquid => None,
|
||||
Network::LiquidTestnet => Some((&client, boltz_client::network::Chain::LiquidTestnet)),
|
||||
};
|
||||
|
||||
match refund_tx.sign_refund(
|
||||
keypair,
|
||||
broadcast_fees_sat,
|
||||
Some((&client, &swap_id.to_string())),
|
||||
) {
|
||||
// Try with cooperative refund
|
||||
Ok(tx) => {
|
||||
let refund_tx_id = refund_tx.broadcast(&tx, &self.network_config(), is_lowball)?;
|
||||
debug!("Successfully broadcast cooperative refund for swap-in {swap_id}");
|
||||
Ok(refund_tx_id)
|
||||
}
|
||||
// Try with non-cooperative refund
|
||||
match self
|
||||
.try_refund_cooperative(swap, &refund_tx, broadcast_fees_sat, is_lowball)
|
||||
.await
|
||||
{
|
||||
Ok(res) => Ok(res),
|
||||
Err(e) => {
|
||||
debug!("Cooperative refund failed: {:?}", e);
|
||||
let tx = refund_tx.sign_refund(keypair, broadcast_fees_sat, None)?;
|
||||
let refund_tx_id = refund_tx.broadcast(&tx, &self.network_config(), is_lowball)?;
|
||||
debug!("Successfully broadcast non-cooperative refund for swap-in {swap_id}");
|
||||
Ok(refund_tx_id)
|
||||
warn!("Cooperative refund failed: {:?}", e);
|
||||
self.try_refund_non_cooperative(
|
||||
swap,
|
||||
&swap_script,
|
||||
refund_tx,
|
||||
broadcast_fees_sat,
|
||||
is_lowball,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -723,7 +770,7 @@ impl LiquidSdk {
|
||||
) -> Result<(), PaymentError> {
|
||||
debug!("Claim is pending for swap-in {swap_id}. Initiating cooperative claim");
|
||||
let client = self.boltz_client_v2();
|
||||
let refund_tx = self.new_refund_tx(swap_script).await?;
|
||||
let refund_tx = self.new_refund_tx(swap_id, swap_script).await?;
|
||||
|
||||
let claim_tx_response = client.get_claim_tx_details(&swap_id.to_string())?;
|
||||
debug!("Received claim tx details: {:?}", &claim_tx_response);
|
||||
@@ -783,7 +830,7 @@ impl LiquidSdk {
|
||||
PaymentError::InvalidOrExpiredFees
|
||||
);
|
||||
|
||||
let keypair = self.get_submarine_keys(0)?;
|
||||
let keypair = utils::generate_keypair();
|
||||
let refund_public_key = boltz_client::PublicKey {
|
||||
compressed: true,
|
||||
inner: keypair.public_key(),
|
||||
@@ -820,7 +867,7 @@ impl LiquidSdk {
|
||||
BoltzStatusStream::mark_swap_as_tracked(swap_id, SwapType::Submarine);
|
||||
|
||||
let payer_amount_sat = req.fees_sat + receiver_amount_sat;
|
||||
self.persister.insert_send_swap(SendSwap {
|
||||
let swap = SendSwap {
|
||||
id: swap_id.clone(),
|
||||
invoice: req.invoice.clone(),
|
||||
payer_amount_sat,
|
||||
@@ -830,7 +877,9 @@ impl LiquidSdk {
|
||||
refund_tx_id: None,
|
||||
created_at: utils::now(),
|
||||
state: PaymentState::Created,
|
||||
})?;
|
||||
refund_private_key: keypair.display_secret().to_string(),
|
||||
};
|
||||
self.persister.insert_send_swap(&swap)?;
|
||||
|
||||
let result;
|
||||
let mut lockup_tx_id = String::new();
|
||||
@@ -913,8 +962,15 @@ impl LiquidSdk {
|
||||
SubSwapStates::InvoiceFailedToPay
|
||||
| SubSwapStates::SwapExpired
|
||||
| SubSwapStates::TransactionLockupFailed => {
|
||||
let refund_tx_id = self
|
||||
.try_refund(swap_id, &swap_script, &keypair, receiver_amount_sat)
|
||||
let refund_tx_id = self.try_refund(&swap).await?;
|
||||
|
||||
self.try_handle_send_swap_update(
|
||||
swap_id,
|
||||
Pending,
|
||||
None,
|
||||
None,
|
||||
Some(&refund_tx_id),
|
||||
)
|
||||
.await?;
|
||||
|
||||
result = Err(PaymentError::Refunded {
|
||||
@@ -1080,7 +1136,7 @@ impl LiquidSdk {
|
||||
&invoice.to_string(),
|
||||
)?;
|
||||
self.persister
|
||||
.insert_receive_swap(ReceiveSwap {
|
||||
.insert_receive_swap(&ReceiveSwap {
|
||||
id: swap_id.clone(),
|
||||
preimage: preimage_str,
|
||||
create_response_json,
|
||||
@@ -1153,7 +1209,7 @@ impl LiquidSdk {
|
||||
info!("Retrieving preimage from non-cooperative claim tx");
|
||||
|
||||
let id = &swap.id;
|
||||
let keypair = self.get_submarine_keys(0)?;
|
||||
let keypair = swap.get_refund_keypair()?;
|
||||
let create_response = swap.get_boltz_create_response()?;
|
||||
let electrum_client = ElectrumClient::new(&self.electrum_url)?;
|
||||
|
||||
|
||||
@@ -44,11 +44,13 @@ pub(crate) fn get_swap_status_v2(
|
||||
|
||||
return match args.first() {
|
||||
Some(update) if update.id == swap_id => {
|
||||
info!("Got new swap status: {}", update.status);
|
||||
info!("Got new swap status for {swap_id}: {}", update.status);
|
||||
|
||||
Ok(update.status.clone())
|
||||
}
|
||||
Some(update) => Err(anyhow!("WS reply has wrong swap ID {update:?}")),
|
||||
Some(update) => Err(anyhow!(
|
||||
"WS reply has wrong swap ID {update:?}. Should be {swap_id}"
|
||||
)),
|
||||
None => Err(anyhow!("WS reply contains no update")),
|
||||
};
|
||||
}
|
||||
@@ -64,7 +66,9 @@ pub(crate) fn get_swap_status_v2(
|
||||
for e in &args {
|
||||
error!("Got error: {} for swap: {}", e.error, e.id);
|
||||
}
|
||||
return Err(anyhow!("Got SwapUpdate errors: {args:?}"));
|
||||
return Err(anyhow!(
|
||||
"Got SwapUpdate errors for swap {swap_id}: {args:?}"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,3 +86,16 @@ pub(crate) fn json_to_pubkey(json: &str) -> Result<boltz_client::PublicKey, Paym
|
||||
err: format!("Failed to deserialize PublicKey: {e:?}"),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn generate_keypair() -> boltz_client::Keypair {
|
||||
let secp = boltz_client::Secp256k1::new();
|
||||
let mut rng = bip39::rand::rngs::OsRng;
|
||||
let secret_key = lwk_wollet::secp256k1::SecretKey::new(&mut rng);
|
||||
boltz_client::Keypair::from_secret_key(&secp, &secret_key)
|
||||
}
|
||||
|
||||
pub(crate) fn decode_keypair(secret_key: &str) -> Result<boltz_client::Keypair, lwk_wollet::Error> {
|
||||
let secp = boltz_client::Secp256k1::new();
|
||||
let secret_key = lwk_wollet::secp256k1::SecretKey::from_str(secret_key)?;
|
||||
Ok(boltz_client::Keypair::from_secret_key(&secp, &secret_key))
|
||||
}
|
||||
|
||||
@@ -669,7 +669,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
Payment dco_decode_payment(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
final arr = raw as List<dynamic>;
|
||||
if (arr.length != 8) throw Exception('unexpected arr length: expect 8 but see ${arr.length}');
|
||||
if (arr.length != 10) throw Exception('unexpected arr length: expect 10 but see ${arr.length}');
|
||||
return Payment(
|
||||
txId: dco_decode_String(arr[0]),
|
||||
swapId: dco_decode_opt_String(arr[1]),
|
||||
@@ -677,8 +677,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
amountSat: dco_decode_u_64(arr[3]),
|
||||
feesSat: dco_decode_opt_box_autoadd_u_64(arr[4]),
|
||||
preimage: dco_decode_opt_String(arr[5]),
|
||||
paymentType: dco_decode_payment_type(arr[6]),
|
||||
status: dco_decode_payment_state(arr[7]),
|
||||
refundTxId: dco_decode_opt_String(arr[6]),
|
||||
refundTxAmountSat: dco_decode_opt_box_autoadd_u_64(arr[7]),
|
||||
paymentType: dco_decode_payment_type(arr[8]),
|
||||
status: dco_decode_payment_state(arr[9]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1090,6 +1092,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
var var_amountSat = sse_decode_u_64(deserializer);
|
||||
var var_feesSat = sse_decode_opt_box_autoadd_u_64(deserializer);
|
||||
var var_preimage = sse_decode_opt_String(deserializer);
|
||||
var var_refundTxId = sse_decode_opt_String(deserializer);
|
||||
var var_refundTxAmountSat = sse_decode_opt_box_autoadd_u_64(deserializer);
|
||||
var var_paymentType = sse_decode_payment_type(deserializer);
|
||||
var var_status = sse_decode_payment_state(deserializer);
|
||||
return Payment(
|
||||
@@ -1099,6 +1103,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
amountSat: var_amountSat,
|
||||
feesSat: var_feesSat,
|
||||
preimage: var_preimage,
|
||||
refundTxId: var_refundTxId,
|
||||
refundTxAmountSat: var_refundTxAmountSat,
|
||||
paymentType: var_paymentType,
|
||||
status: var_status);
|
||||
}
|
||||
@@ -1546,6 +1552,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_u_64(self.amountSat, serializer);
|
||||
sse_encode_opt_box_autoadd_u_64(self.feesSat, serializer);
|
||||
sse_encode_opt_String(self.preimage, serializer);
|
||||
sse_encode_opt_String(self.refundTxId, serializer);
|
||||
sse_encode_opt_box_autoadd_u_64(self.refundTxAmountSat, serializer);
|
||||
sse_encode_payment_type(self.paymentType, serializer);
|
||||
sse_encode_payment_state(self.status, serializer);
|
||||
}
|
||||
|
||||
@@ -569,6 +569,8 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
wireObj.amount_sat = cst_encode_u_64(apiObj.amountSat);
|
||||
wireObj.fees_sat = cst_encode_opt_box_autoadd_u_64(apiObj.feesSat);
|
||||
wireObj.preimage = cst_encode_opt_String(apiObj.preimage);
|
||||
wireObj.refund_tx_id = cst_encode_opt_String(apiObj.refundTxId);
|
||||
wireObj.refund_tx_amount_sat = cst_encode_opt_box_autoadd_u_64(apiObj.refundTxAmountSat);
|
||||
wireObj.payment_type = cst_encode_payment_type(apiObj.paymentType);
|
||||
wireObj.status = cst_encode_payment_state(apiObj.status);
|
||||
}
|
||||
@@ -1369,6 +1371,10 @@ final class wire_cst_payment extends ffi.Struct {
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> preimage;
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> refund_tx_id;
|
||||
|
||||
external ffi.Pointer<ffi.Uint64> refund_tx_amount_sat;
|
||||
|
||||
@ffi.Int32()
|
||||
external int payment_type;
|
||||
|
||||
|
||||
@@ -161,6 +161,12 @@ class Payment {
|
||||
|
||||
/// In case of a Send swap, this is the preimage of the paid invoice (proof of payment).
|
||||
final String? preimage;
|
||||
|
||||
/// For a Send swap which was refunded, this is the refund tx id
|
||||
final String? refundTxId;
|
||||
|
||||
/// For a Send swap which was refunded, this is the refund amount
|
||||
final int? refundTxAmountSat;
|
||||
final PaymentType paymentType;
|
||||
|
||||
/// Composite status representing the overall status of the payment.
|
||||
@@ -177,6 +183,8 @@ class Payment {
|
||||
required this.amountSat,
|
||||
this.feesSat,
|
||||
this.preimage,
|
||||
this.refundTxId,
|
||||
this.refundTxAmountSat,
|
||||
required this.paymentType,
|
||||
required this.status,
|
||||
});
|
||||
@@ -189,6 +197,8 @@ class Payment {
|
||||
amountSat.hashCode ^
|
||||
feesSat.hashCode ^
|
||||
preimage.hashCode ^
|
||||
refundTxId.hashCode ^
|
||||
refundTxAmountSat.hashCode ^
|
||||
paymentType.hashCode ^
|
||||
status.hashCode;
|
||||
|
||||
@@ -203,6 +213,8 @@ class Payment {
|
||||
amountSat == other.amountSat &&
|
||||
feesSat == other.feesSat &&
|
||||
preimage == other.preimage &&
|
||||
refundTxId == other.refundTxId &&
|
||||
refundTxAmountSat == other.refundTxAmountSat &&
|
||||
paymentType == other.paymentType &&
|
||||
status == other.status;
|
||||
}
|
||||
|
||||
@@ -539,6 +539,10 @@ final class wire_cst_payment extends ffi.Struct {
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> preimage;
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> refund_tx_id;
|
||||
|
||||
external ffi.Pointer<ffi.Uint64> refund_tx_amount_sat;
|
||||
|
||||
@ffi.Int32()
|
||||
external int payment_type;
|
||||
|
||||
|
||||
@@ -172,6 +172,8 @@ fun asPayment(payment: ReadableMap): Payment? {
|
||||
val amountSat = payment.getDouble("amountSat").toULong()
|
||||
val feesSat = if (hasNonNullKey(payment, "feesSat")) payment.getDouble("feesSat").toULong() else null
|
||||
val preimage = if (hasNonNullKey(payment, "preimage")) payment.getString("preimage") else null
|
||||
val refundTxId = if (hasNonNullKey(payment, "refundTxId")) payment.getString("refundTxId") else null
|
||||
val refundTxAmountSat = if (hasNonNullKey(payment, "refundTxAmountSat")) payment.getDouble("refundTxAmountSat").toULong() else null
|
||||
val paymentType = payment.getString("paymentType")?.let { asPaymentType(it) }!!
|
||||
val status = payment.getString("status")?.let { asPaymentState(it) }!!
|
||||
return Payment(
|
||||
@@ -181,6 +183,8 @@ fun asPayment(payment: ReadableMap): Payment? {
|
||||
amountSat,
|
||||
feesSat,
|
||||
preimage,
|
||||
refundTxId,
|
||||
refundTxAmountSat,
|
||||
paymentType,
|
||||
status,
|
||||
)
|
||||
@@ -194,6 +198,8 @@ fun readableMapOf(payment: Payment): ReadableMap {
|
||||
"amountSat" to payment.amountSat,
|
||||
"feesSat" to payment.feesSat,
|
||||
"preimage" to payment.preimage,
|
||||
"refundTxId" to payment.refundTxId,
|
||||
"refundTxAmountSat" to payment.refundTxAmountSat,
|
||||
"paymentType" to payment.paymentType.name.lowercase(),
|
||||
"status" to payment.status.name.lowercase(),
|
||||
)
|
||||
|
||||
@@ -198,6 +198,20 @@ enum BreezLiquidSDKMapper {
|
||||
}
|
||||
preimage = preimageTmp
|
||||
}
|
||||
var refundTxId: String?
|
||||
if hasNonNilKey(data: payment, key: "refundTxId") {
|
||||
guard let refundTxIdTmp = payment["refundTxId"] as? String else {
|
||||
throw LiquidSdkError.Generic(message: errUnexpectedValue(fieldName: "refundTxId"))
|
||||
}
|
||||
refundTxId = refundTxIdTmp
|
||||
}
|
||||
var refundTxAmountSat: UInt64?
|
||||
if hasNonNilKey(data: payment, key: "refundTxAmountSat") {
|
||||
guard let refundTxAmountSatTmp = payment["refundTxAmountSat"] as? UInt64 else {
|
||||
throw LiquidSdkError.Generic(message: errUnexpectedValue(fieldName: "refundTxAmountSat"))
|
||||
}
|
||||
refundTxAmountSat = refundTxAmountSatTmp
|
||||
}
|
||||
guard let paymentTypeTmp = payment["paymentType"] as? String else {
|
||||
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "paymentType", typeName: "Payment"))
|
||||
}
|
||||
@@ -215,6 +229,8 @@ enum BreezLiquidSDKMapper {
|
||||
amountSat: amountSat,
|
||||
feesSat: feesSat,
|
||||
preimage: preimage,
|
||||
refundTxId: refundTxId,
|
||||
refundTxAmountSat: refundTxAmountSat,
|
||||
paymentType: paymentType,
|
||||
status: status
|
||||
)
|
||||
@@ -228,6 +244,8 @@ enum BreezLiquidSDKMapper {
|
||||
"amountSat": payment.amountSat,
|
||||
"feesSat": payment.feesSat == nil ? nil : payment.feesSat,
|
||||
"preimage": payment.preimage == nil ? nil : payment.preimage,
|
||||
"refundTxId": payment.refundTxId == nil ? nil : payment.refundTxId,
|
||||
"refundTxAmountSat": payment.refundTxAmountSat == nil ? nil : payment.refundTxAmountSat,
|
||||
"paymentType": valueOf(paymentType: payment.paymentType),
|
||||
"status": valueOf(paymentState: payment.status),
|
||||
]
|
||||
|
||||
@@ -47,6 +47,8 @@ export interface Payment {
|
||||
amountSat: number
|
||||
feesSat?: number
|
||||
preimage?: string
|
||||
refundTxId?: string
|
||||
refundTxAmountSat?: number
|
||||
paymentType: PaymentType
|
||||
status: PaymentState
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user