use std::sync::Arc;
use anyhow::Result;
use flutter_rust_bridge::frb;
use log::{Level, LevelFilter, Metadata, Record, SetLoggerError};
pub use sdk_common::prelude::*;
use crate::{error::*, frb_generated::StreamSink, model::*, sdk::LiquidSdk};
pub struct BindingEventListener {
pub stream: StreamSink<SdkEvent>,
}
impl EventListener for BindingEventListener {
fn on_event(&self, e: SdkEvent) {
let _ = self.stream.add(e);
}
}
struct DartBindingLogger {
log_stream: StreamSink<LogEntry>,
}
impl DartBindingLogger {
fn init(log_stream: StreamSink<LogEntry>) -> Result<(), SetLoggerError> {
let binding_logger: DartBindingLogger = DartBindingLogger { log_stream };
log::set_boxed_logger(Box::new(binding_logger))
.map(|_| log::set_max_level(LevelFilter::Trace))
}
}
impl log::Log for DartBindingLogger {
fn enabled(&self, m: &Metadata) -> bool {
m.level() <= Level::Trace
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
let _ = self.log_stream.add(LogEntry {
line: record.args().to_string(),
level: record.level().as_str().to_string(),
});
}
}
fn flush(&self) {}
}
pub async fn connect(req: ConnectRequest) -> Result<BindingLiquidSdk, SdkError> {
let ln_sdk = LiquidSdk::connect(req).await?;
Ok(BindingLiquidSdk { sdk: ln_sdk })
}
pub fn breez_log_stream(s: StreamSink<LogEntry>) -> Result<()> {
DartBindingLogger::init(s).map_err(|_| SdkError::generic("Log stream already created"))?;
Ok(())
}
#[frb(sync)]
pub fn default_config(
network: LiquidNetwork,
breez_api_key: Option<String>,
) -> Result<Config, SdkError> {
LiquidSdk::default_config(network, breez_api_key)
}
#[frb(sync)]
pub fn parse_invoice(input: String) -> Result<LNInvoice, PaymentError> {
LiquidSdk::parse_invoice(&input)
}
pub struct BindingLiquidSdk {
sdk: Arc<LiquidSdk>,
}
impl BindingLiquidSdk {
pub async fn get_info(&self) -> Result<GetInfoResponse, SdkError> {
self.sdk.get_info().await.map_err(Into::into)
}
#[frb(sync)]
pub fn sign_message(&self, req: SignMessageRequest) -> Result<SignMessageResponse, SdkError> {
self.sdk.sign_message(&req)
}
#[frb(sync)]
pub fn check_message(
&self,
req: CheckMessageRequest,
) -> Result<CheckMessageResponse, SdkError> {
self.sdk.check_message(&req)
}
pub async fn parse(&self, input: String) -> Result<InputType, PaymentError> {
self.sdk.parse(&input).await
}
pub async fn add_event_listener(
&self,
listener: StreamSink<SdkEvent>,
) -> Result<String, SdkError> {
self.sdk
.add_event_listener(Box::new(BindingEventListener { stream: listener }))
.await
}
pub async fn prepare_send_payment(
&self,
req: PrepareSendRequest,
) -> Result<PrepareSendResponse, PaymentError> {
self.sdk.prepare_send_payment(&req).await
}
pub async fn send_payment(
&self,
req: SendPaymentRequest,
) -> Result<SendPaymentResponse, PaymentError> {
self.sdk.send_payment(&req).await
}
pub async fn prepare_receive_payment(
&self,
req: PrepareReceiveRequest,
) -> Result<PrepareReceiveResponse, PaymentError> {
self.sdk.prepare_receive_payment(&req).await
}
pub async fn receive_payment(
&self,
req: ReceivePaymentRequest,
) -> Result<ReceivePaymentResponse, PaymentError> {
self.sdk.receive_payment(&req).await
}
pub async fn fetch_lightning_limits(
&self,
) -> Result<LightningPaymentLimitsResponse, PaymentError> {
self.sdk.fetch_lightning_limits().await
}
pub async fn fetch_onchain_limits(&self) -> Result<OnchainPaymentLimitsResponse, PaymentError> {
self.sdk.fetch_onchain_limits().await
}
pub async fn prepare_pay_onchain(
&self,
req: PreparePayOnchainRequest,
) -> Result<PreparePayOnchainResponse, PaymentError> {
self.sdk.prepare_pay_onchain(&req).await
}
pub async fn pay_onchain(
&self,
req: PayOnchainRequest,
) -> Result<SendPaymentResponse, PaymentError> {
self.sdk.pay_onchain(&req).await
}
pub async fn prepare_buy_bitcoin(
&self,
req: PrepareBuyBitcoinRequest,
) -> Result<PrepareBuyBitcoinResponse, PaymentError> {
self.sdk.prepare_buy_bitcoin(&req).await
}
pub async fn buy_bitcoin(&self, req: BuyBitcoinRequest) -> Result<String, PaymentError> {
self.sdk.buy_bitcoin(&req).await
}
pub async fn list_payments(
&self,
req: ListPaymentsRequest,
) -> Result<Vec<Payment>, PaymentError> {
self.sdk.list_payments(&req).await
}
pub async fn get_payment(
&self,
req: GetPaymentRequest,
) -> Result<Option<Payment>, PaymentError> {
self.sdk.get_payment(&req).await
}
pub async fn prepare_lnurl_pay(
&self,
req: PrepareLnUrlPayRequest,
) -> Result<PrepareLnUrlPayResponse, duplicates::LnUrlPayError> {
self.sdk.prepare_lnurl_pay(req).await.map_err(Into::into)
}
pub async fn lnurl_pay(
&self,
req: crate::model::LnUrlPayRequest,
) -> Result<LnUrlPayResult, duplicates::LnUrlPayError> {
self.sdk.lnurl_pay(req).await.map_err(Into::into)
}
pub async fn lnurl_withdraw(
&self,
req: LnUrlWithdrawRequest,
) -> Result<duplicates::LnUrlWithdrawResult, duplicates::LnUrlWithdrawError> {
self.sdk
.lnurl_withdraw(req)
.await
.map(Into::into)
.map_err(Into::into)
}
pub async fn lnurl_auth(
&self,
req_data: LnUrlAuthRequestData,
) -> Result<duplicates::LnUrlCallbackStatus, duplicates::LnUrlAuthError> {
self.sdk
.lnurl_auth(req_data)
.await
.map(Into::into)
.map_err(Into::into)
}
pub async fn register_webhook(&self, webhook_url: String) -> Result<(), SdkError> {
self.sdk.register_webhook(webhook_url).await
}
pub async fn unregister_webhook(&self) -> Result<(), SdkError> {
self.sdk.unregister_webhook().await
}
pub async fn fetch_fiat_rates(&self) -> Result<Vec<Rate>, SdkError> {
self.sdk.fetch_fiat_rates().await
}
pub async fn list_fiat_currencies(&self) -> Result<Vec<FiatCurrency>, SdkError> {
self.sdk.list_fiat_currencies().await
}
pub async fn list_refundables(&self) -> Result<Vec<RefundableSwap>, SdkError> {
self.sdk.list_refundables().await
}
pub async fn prepare_refund(
&self,
req: PrepareRefundRequest,
) -> Result<PrepareRefundResponse, SdkError> {
self.sdk.prepare_refund(&req).await
}
pub async fn refund(&self, req: RefundRequest) -> Result<RefundResponse, PaymentError> {
self.sdk.refund(&req).await
}
pub async fn rescan_onchain_swaps(&self) -> Result<(), SdkError> {
self.sdk.rescan_onchain_swaps().await
}
#[frb(name = "sync")]
pub async fn sync(&self) -> Result<(), SdkError> {
self.sdk.sync().await.map_err(Into::into)
}
pub async fn recommended_fees(&self) -> Result<RecommendedFees, SdkError> {
self.sdk.recommended_fees().await.map_err(Into::into)
}
#[frb(sync)]
pub fn empty_wallet_cache(&self) -> Result<(), SdkError> {
self.sdk.empty_wallet_cache().map_err(Into::into)
}
#[frb(sync)]
pub fn backup(&self, req: BackupRequest) -> Result<(), SdkError> {
self.sdk.backup(req).map_err(Into::into)
}
#[frb(sync)]
pub fn restore(&self, req: RestoreRequest) -> Result<(), SdkError> {
self.sdk.restore(req).map_err(Into::into)
}
pub async fn disconnect(&self) -> Result<(), SdkError> {
self.sdk.disconnect().await
}
}
#[frb(mirror(Network))]
pub enum _Network {
Bitcoin,
Testnet,
Signet,
Regtest,
}
#[frb(mirror(ExternalInputParser))]
pub struct _ExternalInputParser {
pub provider_id: String,
pub input_regex: String,
pub parser_url: String,
}
#[frb(mirror(LNInvoice))]
pub struct _LNInvoice {
pub bolt11: String,
pub network: Network,
pub payee_pubkey: String,
pub payment_hash: String,
pub description: Option<String>,
pub description_hash: Option<String>,
pub amount_msat: Option<u64>,
pub timestamp: u64,
pub expiry: u64,
pub routing_hints: Vec<RouteHint>,
pub payment_secret: Vec<u8>,
pub min_final_cltv_expiry_delta: u64,
}
#[frb(mirror(RouteHint))]
pub struct _RouteHint {
pub hops: Vec<RouteHintHop>,
}
#[frb(mirror(RouteHintHop))]
pub struct _RouteHintHop {
pub src_node_id: String,
pub short_channel_id: String,
pub fees_base_msat: u32,
pub fees_proportional_millionths: u32,
pub cltv_expiry_delta: u64,
pub htlc_minimum_msat: Option<u64>,
pub htlc_maximum_msat: Option<u64>,
}
#[frb(mirror(Amount))]
pub enum _Amount {
Bitcoin {
amount_msat: u64,
},
Currency {
iso4217_code: String,
fractional_amount: u64,
},
}
#[frb(mirror(LnOfferBlindedPath))]
pub struct _LnOfferBlindedPath {
pub blinded_hops: Vec<String>,
}
#[frb(mirror(LNOffer))]
pub struct _LNOffer {
pub offer: String,
pub chains: Vec<String>,
pub min_amount: Option<Amount>,
pub description: Option<String>,
pub absolute_expiry: Option<u64>,
pub issuer: Option<String>,
pub signing_pubkey: Option<String>,
pub paths: Vec<LnOfferBlindedPath>,
}
#[frb(mirror(InputType))]
pub enum _InputType {
BitcoinAddress { address: BitcoinAddressData },
LiquidAddress { address: LiquidAddressData },
Bolt11 { invoice: LNInvoice },
Bolt12Offer { offer: LNOffer },
NodeId { node_id: String },
Url { url: String },
LnUrlPay { data: LnUrlPayRequestData },
LnUrlWithdraw { data: LnUrlWithdrawRequestData },
LnUrlAuth { data: LnUrlAuthRequestData },
LnUrlError { data: LnUrlErrorData },
}
#[frb(mirror(BitcoinAddressData))]
pub struct _BitcoinAddressData {
pub address: String,
pub network: sdk_common::prelude::Network,
pub amount_sat: Option<u64>,
pub label: Option<String>,
pub message: Option<String>,
}
#[frb(mirror(LiquidAddressData))]
pub struct _LiquidAddressData {
pub address: String,
pub network: Network,
pub asset_id: Option<String>,
pub amount_sat: Option<u64>,
pub label: Option<String>,
pub message: Option<String>,
}
#[frb(mirror(LnUrlPayRequestData))]
pub struct _LnUrlPayRequestData {
pub callback: String,
pub min_sendable: u64,
pub max_sendable: u64,
pub metadata_str: String,
pub comment_allowed: u16,
pub domain: String,
pub allows_nostr: bool,
pub nostr_pubkey: Option<String>,
pub ln_address: Option<String>,
}
#[frb(mirror(SuccessAction))]
pub enum _SuccessAction {
Aes { data: AesSuccessActionData },
Message { data: MessageSuccessActionData },
Url { data: UrlSuccessActionData },
}
#[frb(mirror(SuccessActionProcessed))]
pub enum _SuccessActionProcessed {
Aes { result: AesSuccessActionDataResult },
Message { data: MessageSuccessActionData },
Url { data: UrlSuccessActionData },
}
#[frb(mirror(AesSuccessActionData))]
pub struct _AesSuccessActionData {
pub description: String,
pub ciphertext: String,
pub iv: String,
}
#[frb(mirror(AesSuccessActionDataResult))]
pub enum _AesSuccessActionDataResult {
Decrypted { data: AesSuccessActionDataDecrypted },
ErrorStatus { reason: String },
}
#[frb(mirror(AesSuccessActionDataDecrypted))]
pub struct _AesSuccessActionDataDecrypted {
pub description: String,
pub plaintext: String,
}
#[frb(mirror(MessageSuccessActionData))]
pub struct _MessageSuccessActionData {
pub message: String,
}
#[frb(mirror(UrlSuccessActionData))]
pub struct _UrlSuccessActionData {
pub description: String,
pub url: String,
pub matches_callback_domain: bool,
}
#[frb(mirror(LnUrlPayErrorData))]
pub struct _LnUrlPayErrorData {
pub payment_hash: String,
pub reason: String,
}
#[frb(mirror(LnUrlWithdrawRequestData))]
pub struct _LnUrlWithdrawRequestData {
pub callback: String,
pub k1: String,
pub default_description: String,
pub min_withdrawable: u64,
pub max_withdrawable: u64,
}
#[frb(mirror(LnUrlAuthRequestData))]
pub struct _LnUrlAuthRequestData {
pub k1: String,
pub action: Option<String>,
pub domain: String,
pub url: String,
}
#[frb(mirror(LnUrlErrorData))]
pub struct _LnUrlErrorData {
pub reason: String,
}
#[frb(mirror(LnUrlWithdrawRequest))]
pub struct _LnUrlWithdrawRequest {
pub data: LnUrlWithdrawRequestData,
pub amount_msat: u64,
pub description: Option<String>,
}
#[frb(mirror(Rate))]
pub struct _Rate {
pub coin: String,
pub value: f64,
}
#[frb(mirror(FiatCurrency))]
pub struct _FiatCurrency {
pub id: String,
pub info: CurrencyInfo,
}
#[frb(mirror(CurrencyInfo))]
pub struct _CurrencyInfo {
pub name: String,
pub fraction_size: u32,
pub spacing: Option<u32>,
pub symbol: Option<Symbol>,
pub uniq_symbol: Option<Symbol>,
pub localized_name: Vec<LocalizedName>,
pub locale_overrides: Vec<LocaleOverrides>,
}
#[frb(mirror(LocaleOverrides))]
pub struct _LocaleOverrides {
pub locale: String,
pub spacing: Option<u32>,
pub symbol: Symbol,
}
#[frb(mirror(LocalizedName))]
pub struct _LocalizedName {
pub locale: String,
pub name: String,
}
#[frb(mirror(Symbol))]
pub struct _Symbol {
pub grapheme: Option<String>,
pub template: Option<String>,
pub rtl: Option<bool>,
pub position: Option<u32>,
}
pub mod duplicates {
use sdk_common::prelude::*;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::error::PaymentError;
#[derive(Clone, Debug, Error)]
pub enum LnUrlPayError {
#[error("Invoice already paid")]
AlreadyPaid,
#[error("Generic: {err}")]
Generic { err: String },
#[error("Invalid amount: {err}")]
InvalidAmount { err: String },
#[error("Invalid invoice: {err}")]
InvalidInvoice { err: String },
#[error("Invalid network: {err}")]
InvalidNetwork { err: String },
#[error("Invalid uri: {err}")]
InvalidUri { err: String },
#[error("Invoice expired: {err}")]
InvoiceExpired { err: String },
#[error("Payment failed: {err}")]
PaymentFailed { err: String },
#[error("Payment timeout: {err}")]
PaymentTimeout { err: String },
#[error("Route not found: {err}")]
RouteNotFound { err: String },
#[error("Route too expensive: {err}")]
RouteTooExpensive { err: String },
#[error("Service connectivity: {err}")]
ServiceConnectivity { err: String },
}
impl From<sdk_common::prelude::LnUrlPayError> for LnUrlPayError {
fn from(value: sdk_common::prelude::LnUrlPayError) -> Self {
match value {
sdk_common::prelude::LnUrlPayError::AlreadyPaid => Self::AlreadyPaid,
sdk_common::prelude::LnUrlPayError::Generic { err } => Self::Generic { err },
sdk_common::prelude::LnUrlPayError::InvalidAmount { err } => {
Self::InvalidAmount { err }
}
sdk_common::prelude::LnUrlPayError::InvalidInvoice { err } => {
Self::InvalidInvoice { err }
}
sdk_common::prelude::LnUrlPayError::InvalidNetwork { err } => {
Self::InvalidNetwork { err }
}
sdk_common::prelude::LnUrlPayError::InvalidUri { err } => Self::InvalidUri { err },
sdk_common::prelude::LnUrlPayError::InvoiceExpired { err } => {
Self::InvoiceExpired { err }
}
sdk_common::prelude::LnUrlPayError::PaymentFailed { err } => {
Self::PaymentFailed { err }
}
sdk_common::prelude::LnUrlPayError::PaymentTimeout { err } => {
Self::PaymentTimeout { err }
}
sdk_common::prelude::LnUrlPayError::RouteNotFound { err } => {
Self::RouteNotFound { err }
}
sdk_common::prelude::LnUrlPayError::RouteTooExpensive { err } => {
Self::RouteTooExpensive { err }
}
sdk_common::prelude::LnUrlPayError::ServiceConnectivity { err } => {
Self::ServiceConnectivity { err }
}
}
}
}
impl From<PaymentError> for sdk_common::prelude::LnUrlPayError {
fn from(value: PaymentError) -> Self {
Self::Generic {
err: format!("{value}"),
}
}
}
#[derive(Debug, Error)]
pub enum LnUrlWithdrawError {
#[error("Generic: {err}")]
Generic { err: String },
#[error("Invalid amount: {err}")]
InvalidAmount { err: String },
#[error("Invalid invoice: {err}")]
InvalidInvoice { err: String },
#[error("Invalid uri: {err}")]
InvalidUri { err: String },
#[error("No routing hints: {err}")]
InvoiceNoRoutingHints { err: String },
#[error("Service connectivity: {err}")]
ServiceConnectivity { err: String },
}
impl From<sdk_common::prelude::LnUrlWithdrawError> for LnUrlWithdrawError {
fn from(value: sdk_common::prelude::LnUrlWithdrawError) -> Self {
match value {
sdk_common::prelude::LnUrlWithdrawError::Generic { err } => Self::Generic { err },
sdk_common::prelude::LnUrlWithdrawError::InvalidAmount { err } => {
Self::InvalidAmount { err }
}
sdk_common::prelude::LnUrlWithdrawError::InvalidInvoice { err } => {
Self::InvalidInvoice { err }
}
sdk_common::prelude::LnUrlWithdrawError::InvalidUri { err } => {
Self::InvalidUri { err }
}
sdk_common::prelude::LnUrlWithdrawError::InvoiceNoRoutingHints { err } => {
Self::InvoiceNoRoutingHints { err }
}
sdk_common::prelude::LnUrlWithdrawError::ServiceConnectivity { err } => {
Self::ServiceConnectivity { err }
}
}
}
}
impl From<PaymentError> for sdk_common::prelude::LnUrlWithdrawError {
fn from(value: PaymentError) -> Self {
Self::Generic {
err: format!("{value}"),
}
}
}
#[derive(Clone, Serialize)]
pub enum LnUrlWithdrawResult {
Ok { data: LnUrlWithdrawSuccessData },
Timeout { data: LnUrlWithdrawSuccessData },
ErrorStatus { data: LnUrlErrorData },
}
impl From<sdk_common::prelude::LnUrlWithdrawResult> for LnUrlWithdrawResult {
fn from(value: sdk_common::prelude::LnUrlWithdrawResult) -> Self {
match value {
sdk_common::prelude::LnUrlWithdrawResult::Ok { data } => {
Self::Ok { data: data.into() }
}
sdk_common::prelude::LnUrlWithdrawResult::Timeout { data } => {
Self::Timeout { data: data.into() }
}
sdk_common::prelude::LnUrlWithdrawResult::ErrorStatus { data } => {
Self::ErrorStatus { data }
}
}
}
}
#[derive(Clone, Deserialize, Debug, Serialize)]
pub struct LnUrlWithdrawSuccessData {
pub invoice: LNInvoice,
}
impl From<sdk_common::prelude::LnUrlWithdrawSuccessData> for LnUrlWithdrawSuccessData {
fn from(value: sdk_common::prelude::LnUrlWithdrawSuccessData) -> Self {
Self {
invoice: value.invoice,
}
}
}
#[derive(Debug, Error)]
pub enum LnUrlAuthError {
#[error("Generic: {err}")]
Generic { err: String },
#[error("Invalid uri: {err}")]
InvalidUri { err: String },
#[error("Service connectivity: {err}")]
ServiceConnectivity { err: String },
}
impl From<sdk_common::prelude::LnUrlAuthError> for LnUrlAuthError {
fn from(value: prelude::LnUrlAuthError) -> Self {
match value {
sdk_common::prelude::LnUrlAuthError::Generic { err } => Self::Generic { err },
sdk_common::prelude::LnUrlAuthError::InvalidUri { err } => Self::InvalidUri { err },
sdk_common::prelude::LnUrlAuthError::ServiceConnectivity { err } => {
Self::ServiceConnectivity { err }
}
}
}
}
#[derive(Clone, Deserialize, Debug, Serialize)]
#[serde(rename_all = "UPPERCASE")]
#[serde(tag = "status")]
pub enum LnUrlCallbackStatus {
Ok,
#[serde(rename = "ERROR")]
ErrorStatus {
#[serde(flatten)]
data: LnUrlErrorData,
},
}
impl From<sdk_common::prelude::LnUrlCallbackStatus> for LnUrlCallbackStatus {
fn from(value: prelude::LnUrlCallbackStatus) -> Self {
match value {
sdk_common::prelude::LnUrlCallbackStatus::Ok => Self::Ok,
sdk_common::prelude::LnUrlCallbackStatus::ErrorStatus { data } => {
Self::ErrorStatus { data }
}
}
}
}
}