Skip to content

Commit

Permalink
Add WebLN::keysend
Browse files Browse the repository at this point in the history
  • Loading branch information
yukibtc committed Jan 19, 2024
1 parent f4c9b14 commit 21762ba
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 4 deletions.
34 changes: 34 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 38 additions & 0 deletions webln-js/src/keysend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2024 Yuki Kishimoto
// Distributed under the MIT software license

use std::ops::Deref;
use std::str::FromStr;

use wasm_bindgen::prelude::*;
use webln::secp256k1::PublicKey;
use webln::KeysendArgs;

use crate::error::{into_err, Result};

#[wasm_bindgen(js_name = KeysendArgs)]
pub struct JsKeysendArgs {
inner: KeysendArgs,
}

impl Deref for JsKeysendArgs {
type Target = KeysendArgs;

fn deref(&self) -> &Self::Target {
&self.inner
}
}

#[wasm_bindgen(js_class = KeysendArgs)]
impl JsKeysendArgs {
pub fn new(destination: String, amount: u32) -> Result<JsKeysendArgs> {
let destination: PublicKey = PublicKey::from_str(&destination).map_err(into_err)?;
let amount: u64 = amount as u64;
Ok(Self {
inner: KeysendArgs {
destination,
amount,
},
})
}
}
19 changes: 18 additions & 1 deletion webln-js/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@
#![allow(non_snake_case)]
#![allow(clippy::new_without_default)]

use get_info::JsGetInfoResponse;
use std::ops::Deref;

use wasm_bindgen::prelude::*;
use webln::WebLN;

pub mod error;
pub mod get_info;
pub mod keysend;
pub mod send_payment;

use self::error::{into_err, Result};
use self::get_info::JsGetInfoResponse;
use self::keysend::JsKeysendArgs;
use self::send_payment::JsSendPaymentResponse;

#[wasm_bindgen(start)]
pub fn start() {
Expand Down Expand Up @@ -54,4 +60,15 @@ impl JsWebLN {
pub async fn get_info(&self) -> Result<JsGetInfoResponse> {
Ok(self.inner.get_info().await.map_err(into_err)?.into())
}

/// Request the user to send a keysend payment.
/// This is a spontaneous payment that does not require an invoice and only needs a destination public key and and amount.
pub async fn keysend(&self, args: &JsKeysendArgs) -> Result<JsSendPaymentResponse> {
Ok(self
.inner
.keysend(args.deref())
.await
.map_err(into_err)?
.into())
}
}
24 changes: 24 additions & 0 deletions webln-js/src/send_payment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2024 Yuki Kishimoto
// Distributed under the MIT software license

use wasm_bindgen::prelude::*;
use webln::SendPaymentResponse;

#[wasm_bindgen(js_name = SendPaymentResponse)]
pub struct JsSendPaymentResponse {
inner: SendPaymentResponse,
}

impl From<SendPaymentResponse> for JsSendPaymentResponse {
fn from(inner: SendPaymentResponse) -> Self {
Self { inner }
}
}

#[wasm_bindgen(js_class = SendPaymentResponse)]
impl JsSendPaymentResponse {
#[wasm_bindgen(getter)]
pub fn preimage(&self) -> String {
self.inner.preimage.clone()
}
}
3 changes: 2 additions & 1 deletion webln/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ keywords = ["webln", "lightning", "bitcoin"]

[dependencies]
js-sys.workspace = true
wasm-bindgen.workspace = true
secp256k1 = { version = "0.27", default-features = false, features = ["std"] }
wasm-bindgen = { workspace = true, features = ["std"]}
wasm-bindgen-futures.workspace = true
web-sys = { version = "0.3", default-features = false, features = ["Window"] }
54 changes: 52 additions & 2 deletions webln/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@

//! WebLN - Lightning Web Standard

pub extern crate secp256k1;

use core::fmt;

use js_sys::{Array, Function, Object, Promise, Reflect};
use secp256k1::PublicKey;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use web_sys::Window;

pub const IS_ENABLED: &str = "isEnabled";
pub const ENABLE: &str = "enable";
pub const GET_INFO: &str = "getInfo";
pub const KEYSEND: &str = "keysend";

/// WebLN error
#[derive(Debug)]
Expand Down Expand Up @@ -84,7 +88,7 @@ where
IS_ENABLED => Self::IsEnabled,
ENABLE => Self::Enable,
GET_INFO => Self::GetInfo,
"keysend" => Self::Keysend,
KEYSEND => Self::Keysend,
"makeInvoice" => Self::MakeInvoice,
"sendPayment" => Self::SendPayment,
"sendPaymentAsync" => Self::SendPaymentAsync,
Expand All @@ -106,7 +110,7 @@ impl fmt::Display for GetInfoMethod {
Self::IsEnabled => write!(f, "{IS_ENABLED}"),
Self::Enable => write!(f, "{ENABLE}"),
Self::GetInfo => write!(f, "{GET_INFO}"),
Self::Keysend => write!(f, "keysend"),
Self::Keysend => write!(f, "{KEYSEND}"),
Self::MakeInvoice => write!(f, "makeInvoice"),
Self::SendPayment => write!(f, "sendPayment"),
Self::SendPaymentAsync => write!(f, "sendPaymentAsync"),
Expand All @@ -128,6 +132,23 @@ pub struct GetInfoResponse {
pub methods: Vec<GetInfoMethod>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KeysendArgs {
/// Public key of the destination node.
pub destination: PublicKey,
/// Amount in SAT
pub amount: u64,
// TODO: add TLVRegistry enum
// The key should be a stringified integer from the <https://github.com/satoshisstream/satoshis.stream/blob/main/TLV_registry.md>.
// The value should be an unencoded, plain string.
// pub custom: Option<HashMap<String, String>>,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SendPaymentResponse {
pub preimage: String,
}

/// WebLN instance
#[derive(Debug, Clone)]
pub struct WebLN {
Expand Down Expand Up @@ -214,4 +235,33 @@ impl WebLN {
methods,
})
}

/// Request the user to send a keysend payment.
/// This is a spontaneous payment that does not require an invoice and only needs a destination public key and and amount.
pub async fn keysend(&self, args: &KeysendArgs) -> Result<SendPaymentResponse, Error> {
let func: Function = self.get_func(&self.webln_obj, KEYSEND)?;

let keysend_obj = Object::new();
Reflect::set(
&keysend_obj,
&JsValue::from_str("destination"),
&args.destination.to_string().into(),
)?;
Reflect::set(
&keysend_obj,
&JsValue::from_str("amount"),
&args.amount.to_string().into(),
)?;

let promise: Promise = Promise::resolve(&func.call1(&self.webln_obj, &keysend_obj.into())?);
let result: JsValue = JsFuture::from(promise).await?;
let send_payment_obj: Object = result.dyn_into()?;

Ok(SendPaymentResponse {
preimage: self
.get_value_by_key(&send_payment_obj, "preimage")?
.as_string()
.ok_or_else(|| Error::TypeMismatch(String::from("expected a string [preimage]")))?,
})
}
}

0 comments on commit 21762ba

Please sign in to comment.