Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions bindings/lni_nodejs/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ export interface SpeedConfig {
export interface SpeedNode {
config: SpeedConfig
}
export interface CashuConfig {
/** The Cashu mint URL */
mintUrl: string
/** Optional wallet seed (64 bytes as hex string). If not provided, a random seed is generated. */
seed?: string
socks5Proxy?: string
acceptInvalidCerts?: boolean
httpTimeout?: number
}
export interface CashuNode {
config: CashuConfig
}
export interface SparkConfig {
/** 12 or 24 word mnemonic phrase */
mnemonic: string
Expand Down Expand Up @@ -427,6 +439,22 @@ export declare class SpeedNode {
decode(str: string): Promise<string>
onInvoiceEvents(params: OnInvoiceEventParams, callback: (arg0: string, arg1?: Transaction | undefined | null) => void): void
}
export declare class CashuNode {
constructor(config: CashuConfig)
getMintUrl(): string
getConfig(): CashuConfig
getInfo(): Promise<NodeInfo>
createInvoice(params: CreateInvoiceParams): Promise<Transaction>
payInvoice(params: PayInvoiceParams): Promise<PayInvoiceResponse>
createOffer(params: CreateOfferParams): Promise<Offer>
getOffer(search?: string | undefined | null): Promise<Offer>
listOffers(search?: string | undefined | null): Promise<Array<Offer>>
lookupInvoice(params: LookupInvoiceParams): Promise<Transaction>
payOffer(offer: string, amountMsats: number, payerNote?: string | undefined | null): Promise<PayInvoiceResponse>
listTransactions(params: ListTransactionsParams): Promise<Array<Transaction>>
decode(str: string): Promise<string>
onInvoiceEvents(params: OnInvoiceEventParams, callback: (arg0: string, arg1?: Transaction | undefined | null) => void): void
}
/**
* Spark Node wrapper for napi-rs
* Note: SparkNode requires async initialization, so we use a builder pattern
Expand Down
3 changes: 2 additions & 1 deletion bindings/lni_nodejs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

const { InvoiceType, PhoenixdNode, ClnNode, LndNode, BlinkNode, NwcNode, StrikeNode, SpeedNode, SparkNode, generateMnemonic, sayAfterWithTokio } = nativeBinding
const { InvoiceType, PhoenixdNode, ClnNode, LndNode, BlinkNode, NwcNode, StrikeNode, SpeedNode, CashuNode, SparkNode, generateMnemonic, sayAfterWithTokio } = nativeBinding

module.exports.InvoiceType = InvoiceType
module.exports.PhoenixdNode = PhoenixdNode
Expand All @@ -320,6 +320,7 @@ module.exports.BlinkNode = BlinkNode
module.exports.NwcNode = NwcNode
module.exports.StrikeNode = StrikeNode
module.exports.SpeedNode = SpeedNode
module.exports.CashuNode = CashuNode
module.exports.SparkNode = SparkNode
module.exports.generateMnemonic = generateMnemonic
module.exports.sayAfterWithTokio = sayAfterWithTokio
139 changes: 139 additions & 0 deletions bindings/lni_nodejs/src/cashu.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
use lni::{cashu::lib::CashuConfig, CreateInvoiceParams, CreateOfferParams, LookupInvoiceParams, PayInvoiceParams};
use napi::bindgen_prelude::*;
use napi_derive::napi;

#[napi]
pub struct CashuNode {
inner: CashuConfig,
}

#[napi]
impl CashuNode {
#[napi(constructor)]
pub fn new(config: CashuConfig) -> Self {
Self { inner: config }
}

#[napi]
pub fn get_mint_url(&self) -> String {
self.inner.mint_url.clone()
}

#[napi]
pub fn get_config(&self) -> CashuConfig {
self.inner.clone()
}

#[napi]
pub async fn get_info(&self) -> napi::Result<lni::NodeInfo> {
let info = lni::cashu::api::get_info(self.inner.clone())
.await
.map_err(|e| napi::Error::from_reason(e.to_string()))?;
Ok(info)
}

#[napi]
pub async fn create_invoice(
&self,
params: CreateInvoiceParams,
) -> napi::Result<lni::Transaction> {
let txn = lni::cashu::api::create_invoice(self.inner.clone(), params)
.await
.map_err(|e| napi::Error::from_reason(e.to_string()))?;
Ok(txn)
}

#[napi]
pub async fn pay_invoice(
&self,
params: PayInvoiceParams,
) -> Result<lni::types::PayInvoiceResponse> {
let invoice = lni::cashu::api::pay_invoice(self.inner.clone(), params)
.await
.map_err(|e| napi::Error::from_reason(e.to_string()))?;
Ok(invoice)
}

#[napi]
pub async fn create_offer(&self, _params: CreateOfferParams) -> Result<lni::Offer> {
Err(napi::Error::from_reason("Bolt12 not implemented for Cashu".to_string()))
}

#[napi]
pub async fn get_offer(&self, search: Option<String>) -> Result<lni::Offer> {
let offer = lni::cashu::api::get_offer(&self.inner, search)
.map_err(|e| napi::Error::from_reason(e.to_string()))?;
Ok(offer)
}

#[napi]
pub async fn list_offers(&self, search: Option<String>) -> Result<Vec<lni::Offer>> {
let offers = lni::cashu::api::list_offers(&self.inner, search)
.await
.map_err(|e| napi::Error::from_reason(e.to_string()))?;
Ok(offers)
}

#[napi]
pub async fn lookup_invoice(
&self,
params: LookupInvoiceParams,
) -> napi::Result<lni::Transaction> {
let txn =
lni::cashu::api::lookup_invoice(self.inner.clone(), params.payment_hash, None, None, params.search)
.await
.map_err(|e| napi::Error::from_reason(e.to_string()))?;
Ok(txn)
}

#[napi]
pub async fn pay_offer(
&self,
offer: String,
amount_msats: i64,
payer_note: Option<String>,
) -> napi::Result<lni::PayInvoiceResponse> {
let offer = lni::cashu::api::pay_offer(&self.inner, offer, amount_msats, payer_note)
.map_err(|e| napi::Error::from_reason(e.to_string()))?;
Ok(offer)
}

#[napi]
pub async fn list_transactions(
&self,
params: crate::ListTransactionsParams,
) -> napi::Result<Vec<lni::Transaction>> {
let txns =
lni::cashu::api::list_transactions(self.inner.clone(), params.from, params.limit, params.search)
.await
.map_err(|e| napi::Error::from_reason(e.to_string()))?;
Ok(txns)
}

#[napi]
pub async fn decode(&self, str: String) -> Result<String> {
let decoded = lni::cashu::api::decode(&self.inner, str)
.map_err(|e| napi::Error::from_reason(e.to_string()))?;
Ok(decoded)
}

#[napi]
pub fn on_invoice_events<T: Fn(String, Option<lni::Transaction>) -> Result<()>>(
&self,
params: lni::types::OnInvoiceEventParams,
callback: T,
) -> Result<()> {
let config = self.inner.clone();

// Block on the async function in the current thread
tokio::runtime::Runtime::new().unwrap().block_on(async {
lni::cashu::api::poll_invoice_events(config, params, move |status, tx| {
let _ = callback(status.clone(), tx.clone())
.map_err(|err| napi::Error::from_reason(err.to_string()));
})
.await;
});

Ok(())
}
}
2 changes: 2 additions & 0 deletions bindings/lni_nodejs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub use strike::StrikeNode;
mod speed;
pub use speed::SpeedNode;

mod cashu;
pub use cashu::CashuNode;
mod spark;
pub use spark::SparkNode;

Expand Down
3 changes: 3 additions & 0 deletions crates/lni/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ nwc = "0.43.0"
nostr = "0.43.0"
chrono = { version = "0.4", features = ["serde"] }
once_cell = "1.19"
cdk = { version = "0.14.2", default-features = false, features = ["wallet"] }
cdk-sqlite = { version = "0.14.2", default-features = true }
getrandom = "0.2"
breez-sdk-spark = { git = "https://github.com/breez/spark-sdk", tag = "0.6.3", default-features = false, features = ["rustls-tls"] }
bip39 = "2.2.2"

Expand Down
Loading