Skip to content

Commit

Permalink
Deezy integration
Browse files Browse the repository at this point in the history
This PR allows deezy via lndhubx:

* Swap BTC -> LN
* Swap LN -> BTC

Fix #243
  • Loading branch information
grunch committed Jul 25, 2023
1 parent 4e2d831 commit fd3dce4
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 16 deletions.
57 changes: 57 additions & 0 deletions src/lightning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,38 @@ pub struct CheckPaymentResponse {
paid: bool,
}

/// Swap BTC onchain to Lightning response
#[derive(Debug, Deserialize)]
pub struct SwapBtcLnResponse {
pub address: String,
pub commitment: String,
pub signature: String,
pub secret_access_key: String,
}

/// Speed of the onchain transaction
#[derive(Serialize, Deserialize, Debug)]
pub enum OnchainSpeed {
Fast,
Medium,
Slow,
}

/// Swap Lightning to BTC onchain request
#[derive(Serialize, Deserialize, Debug)]
pub struct SwapLnBTCRequest {
pub amount: u64,
pub address: String,
pub speed: Option<OnchainSpeed>,
}

/// Swap Lightning to BTC onchain response
#[derive(Debug, Deserialize)]
pub struct SwapLnBtcResponse {
pub bolt11_invoice: String,
pub fee_sats: u32,
}

/// Creates a new lightning custodial wallet
pub async fn create_wallet(username: &str, password: &str) -> Result<CreateWalletResponse> {
let endpoint = LNDHUB_ENDPOINT.read().await;
Expand Down Expand Up @@ -220,3 +252,28 @@ pub async fn check_payment(payment_hash: &str) -> Result<bool> {

Ok(r.paid)
}

/// Swap BTC onchain to Lightning
pub async fn swap_btc_ln(token: &str) -> Result<SwapBtcLnResponse> {
let endpoint = LNDHUB_ENDPOINT.read().await;
let url = format!("{endpoint}/get_onchain_address");
let response = get(&url, Some(token)).await?;
let r = serde_json::from_str::<SwapBtcLnResponse>(&response)?;

Ok(r)
}

/// Swap Lightning to BTC onchain
pub async fn swap_ln_btc(address: &str, amount: u64, token: &str) -> Result<SwapLnBtcResponse> {
let endpoint = LNDHUB_ENDPOINT.read().await;
let url = format!("{endpoint}/make_onchain_swap");
let req = SwapLnBTCRequest {
address: address.to_string(),
amount,
speed: Some(OnchainSpeed::Fast),
};
let response = post_json_auth(&url, &Some(req), Some(token)).await?;
let r = serde_json::from_str::<SwapLnBtcResponse>(&response)?;

Ok(r)
}
28 changes: 28 additions & 0 deletions src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,34 @@ pub mod lightning {
}
})
}

#[wasm_bindgen]
pub fn swap_btc_ln(token: String) -> Promise {
set_panic_hook();

future_to_promise(async move {
match crate::lightning::swap_btc_ln(&token).await {
Ok(result) => Ok(JsValue::from_string(
serde_json::to_string(&result).unwrap(),

Check failure on line 679 in src/web.rs

View workflow job for this annotation

GitHub Actions / lint-wasm

error[E0277]: the trait bound `lightning::SwapBtcLnResponse: lightning::_::_serde::Serialize` is not satisfied --> src/web.rs:679:43 | 679 | serde_json::to_string(&result).unwrap(), | --------------------- ^^^^^^^ the trait `lightning::_::_serde::Serialize` is not implemented for `lightning::SwapBtcLnResponse` | | | required by a bound introduced by this call | = help: the following other types implement trait `lightning::_::_serde::Serialize`: &'a T &'a mut T () (T0, T1) (T0, T1, T2) (T0, T1, T2, T3) (T0, T1, T2, T3, T4) (T0, T1, T2, T3, T4, T5) and 780 others note: required by a bound in `serde_json::to_string` --> /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.103/src/ser.rs:2196:17 | 2194 | pub fn to_string<T>(value: &T) -> Result<String> | --------- required by a bound in this function 2195 | where 2196 | T: ?Sized + Serialize, | ^^^^^^^^^ required by this bound in `to_string`

Check failure on line 679 in src/web.rs

View workflow job for this annotation

GitHub Actions / lint-wasm

error[E0277]: the trait bound `lightning::SwapBtcLnResponse: lightning::_::_serde::Serialize` is not satisfied --> src/web.rs:679:43 | 679 | serde_json::to_string(&result).unwrap(), | --------------------- ^^^^^^^ the trait `lightning::_::_serde::Serialize` is not implemented for `lightning::SwapBtcLnResponse` | | | required by a bound introduced by this call | = help: the following other types implement trait `lightning::_::_serde::Serialize`: &'a T &'a mut T () (T0, T1) (T0, T1, T2) (T0, T1, T2, T3) (T0, T1, T2, T3, T4) (T0, T1, T2, T3, T4, T5) and 780 others note: required by a bound in `serde_json::to_string` --> /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.103/src/ser.rs:2196:17 | 2194 | pub fn to_string<T>(value: &T) -> Result<String> | --------- required by a bound in this function 2195 | where 2196 | T: ?Sized + Serialize, | ^^^^^^^^^ required by this bound in `to_string`
)),
Err(err) => Err(JsValue::from_string(err.to_string())),
}
})
}

#[wasm_bindgen]
pub fn swap_ln_btc(address: String, amount: u64, token: String) -> Promise {
set_panic_hook();

future_to_promise(async move {
match crate::lightning::swap_ln_btc(&address, amount, &token).await {
Ok(result) => Ok(JsValue::from_string(
serde_json::to_string(&result).unwrap(),

Check failure on line 693 in src/web.rs

View workflow job for this annotation

GitHub Actions / lint-wasm

error[E0277]: the trait bound `lightning::SwapLnBtcResponse: lightning::_::_serde::Serialize` is not satisfied --> src/web.rs:693:43 | 693 | serde_json::to_string(&result).unwrap(), | --------------------- ^^^^^^^ the trait `lightning::_::_serde::Serialize` is not implemented for `lightning::SwapLnBtcResponse` | | | required by a bound introduced by this call | = help: the following other types implement trait `lightning::_::_serde::Serialize`: &'a T &'a mut T () (T0, T1) (T0, T1, T2) (T0, T1, T2, T3) (T0, T1, T2, T3, T4) (T0, T1, T2, T3, T4, T5) and 780 others note: required by a bound in `serde_json::to_string` --> /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.103/src/ser.rs:2196:17 | 2194 | pub fn to_string<T>(value: &T) -> Result<String> | --------- required by a bound in this function 2195 | where 2196 | T: ?Sized + Serialize, | ^^^^^^^^^ required by this bound in `to_string`

Check failure on line 693 in src/web.rs

View workflow job for this annotation

GitHub Actions / lint-wasm

error[E0277]: the trait bound `lightning::SwapLnBtcResponse: lightning::_::_serde::Serialize` is not satisfied --> src/web.rs:693:43 | 693 | serde_json::to_string(&result).unwrap(), | --------------------- ^^^^^^^ the trait `lightning::_::_serde::Serialize` is not implemented for `lightning::SwapLnBtcResponse` | | | required by a bound introduced by this call | = help: the following other types implement trait `lightning::_::_serde::Serialize`: &'a T &'a mut T () (T0, T1) (T0, T1, T2) (T0, T1, T2, T3) (T0, T1, T2, T3, T4) (T0, T1, T2, T3, T4, T5) and 780 others note: required by a bound in `serde_json::to_string` --> /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.103/src/ser.rs:2196:17 | 2194 | pub fn to_string<T>(value: &T) -> Result<String> | --------- required by a bound in this function 2195 | where 2196 | T: ?Sized + Serialize, | ^^^^^^^^^ required by this bound in `to_string`
)),
Err(err) => Err(JsValue::from_string(err.to_string())),
}
})
}
}

pub mod carbonado {
Expand Down
81 changes: 65 additions & 16 deletions tests/lightning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,17 @@ use bitmask_core::{
info,
lightning::{
auth, check_payment, create_invoice, create_wallet, decode_invoice, get_balance, get_txs,
pay_invoice, AuthResponse, CreateWalletResponse, Transaction,
pay_invoice, swap_btc_ln, swap_ln_btc, AuthResponse, CreateWalletResponse, Transaction,
},
util::init_logging,
};
use std::{thread, time};

async fn new_wallet() -> Result<CreateWalletResponse> {
// we generate a random string to be used as username and password
let random_number = bip39::rand::random::<u64>();
let s = hex::encode(random_number.to_le_bytes());
// We put to sleep the test to avoid hit too fast the API
thread::sleep(time::Duration::from_secs(1));
let res = create_wallet(&s, &s).await?;
async fn static_wallet() -> Result<CreateWalletResponse> {
// We have a static username and password for a wallet
let res = CreateWalletResponse::Username {
username: "556bcb9f4cea5b6a".to_string(),
};

Ok(res)
}
Expand All @@ -26,7 +24,11 @@ async fn new_wallet() -> Result<CreateWalletResponse> {
pub async fn create_wallet_test() -> Result<()> {
init_logging("lightning=debug");

let res = new_wallet().await?;
// We create a new wallet only for this test
// we generate a random string to be used as username and password
let random_number = bip39::rand::random::<u64>();
let s = hex::encode(random_number.to_le_bytes());
let res = create_wallet(&s, &s).await?;
let mut uname = String::new();
if let CreateWalletResponse::Username { username } = res {
uname = username;
Expand All @@ -41,7 +43,7 @@ pub async fn create_wallet_test() -> Result<()> {
pub async fn auth_test() -> Result<()> {
init_logging("lightning=warn");

let res = new_wallet().await?;
let res = static_wallet().await?;
let mut uname = String::new();
if let CreateWalletResponse::Username { username } = res {
uname = username;
Expand Down Expand Up @@ -71,7 +73,7 @@ pub async fn auth_failed_test() -> Result<()> {
pub async fn create_decode_invoice_test() -> Result<()> {
init_logging("lightning=warn");

let res = new_wallet().await?;
let res = static_wallet().await?;
let mut uname = String::new();
if let CreateWalletResponse::Username { username } = res {
uname = username;
Expand All @@ -97,7 +99,7 @@ pub async fn create_decode_invoice_test() -> Result<()> {
pub async fn get_balance_test() -> Result<()> {
init_logging("lightning=warn");

let res = new_wallet().await?;
let res = static_wallet().await?;
let mut uname = String::new();
if let CreateWalletResponse::Username { username } = res {
uname = username;
Expand All @@ -118,7 +120,7 @@ pub async fn pay_invoice_error_test() -> Result<()> {
init_logging("tests=debug");

info!("We create user Alice");
let res = new_wallet().await?;
let res = static_wallet().await?;
let mut alice = String::new();
if let CreateWalletResponse::Username { username } = res {
alice = username;
Expand All @@ -129,7 +131,7 @@ pub async fn pay_invoice_error_test() -> Result<()> {
info!("Alice invoice");
let invoice = create_invoice("testing pay alice invoice", 33, &token).await?;
info!("We create user Bob");
let res = new_wallet().await?;
let res = static_wallet().await?;
let mut bob = String::new();
if let CreateWalletResponse::Username { username } = res {
bob = username;
Expand All @@ -150,7 +152,7 @@ pub async fn pay_invoice_error_test() -> Result<()> {
pub async fn get_txs_test() -> Result<()> {
init_logging("lightning=warn");

let res = new_wallet().await?;
let res = static_wallet().await?;
let mut uname = String::new();
if let CreateWalletResponse::Username { username } = res {
uname = username;
Expand All @@ -169,7 +171,7 @@ pub async fn get_txs_test() -> Result<()> {
pub async fn check_payment_test() -> Result<()> {
init_logging("lightning=warn");

let res = new_wallet().await?;
let res = static_wallet().await?;
let mut uname = String::new();
if let CreateWalletResponse::Username { username } = res {
uname = username;
Expand All @@ -188,3 +190,50 @@ pub async fn check_payment_test() -> Result<()> {

Ok(())
}

#[tokio::test]
pub async fn swap_btc_ln_test() -> Result<()> {
init_logging("lightning=warn");

let res = static_wallet().await?;
let mut uname = String::new();
if let CreateWalletResponse::Username { username } = res {
uname = username;
}
let response = auth(&uname, &uname).await?;
thread::sleep(time::Duration::from_secs(1));
if let AuthResponse::Result { refresh: _, token } = response {
let response = swap_btc_ln(&token).await?;
assert_eq!(response.secret_access_key.len(), 64);
} else {
panic!("Auth failed");
}

Ok(())
}

#[tokio::test]
pub async fn swap_ln_btc_test() -> Result<()> {
init_logging("lightning=warn");

let res = static_wallet().await?;
let mut uname = String::new();
if let CreateWalletResponse::Username { username } = res {
uname = username;
}
let response = auth(&uname, &uname).await?;
thread::sleep(time::Duration::from_secs(1));
if let AuthResponse::Result { refresh: _, token } = response {
let amount = 500000;
let response =
swap_ln_btc("bc1q6m6efx4gzlltgcr9hgrke0je4z3hvsyzzazl8z", amount, &token).await?;
let decoded_invoice = decode_invoice(&response.bolt11_invoice)?;
let invoice_amount = decoded_invoice.amount_milli_satoshis().unwrap() / 1000;
let total_amount = amount + response.fee_sats as u64;
assert_eq!(total_amount, invoice_amount);
} else {
panic!("Auth failed");
}

Ok(())
}

0 comments on commit fd3dce4

Please sign in to comment.