Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Create Lightning Invoice and Send Payment #192

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
62 changes: 62 additions & 0 deletions src/app/operations/lightning.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use anyhow::Context;
use rust_decimal::Decimal;

use crate::{app::App, client::types::Wallet};

impl App {
pub async fn ln_invoice_create(
&self,
wallet: Wallet,
amount: Decimal,
memo: Option<String>,
) -> anyhow::Result<()> {
let receiving_wallet_id = match wallet {
Wallet::Btc => self.get_user_btc_wallet_id().await?,
Wallet::Usd => self.get_user_usd_wallet_id().await?,
};

match wallet {
Wallet::Btc => {
let data = self
.client
.lightning_invoice_create_btc(receiving_wallet_id, amount, memo)
.await
.context("Error occurred while creating BTC lightning invoice.")?;

println!("{}", serde_json::to_string_pretty(&data.invoice)?);
}
Wallet::Usd => {
let data = self
.client
.lightning_invoice_create_usd(receiving_wallet_id, amount, memo)
.await
.context("Error occurred while creating USD lightning invoice.")?;

println!("{}", serde_json::to_string_pretty(&data.invoice)?);
}
}

Ok(())
}

pub async fn send_lightning(
&self,
ln_payment_request: String,
wallet: Wallet,
memo: Option<String>,
) -> anyhow::Result<()> {
let sender_wallet_id = match wallet {
Wallet::Btc => self.get_user_btc_wallet_id().await?,
Wallet::Usd => self.get_user_usd_wallet_id().await?,
};

self.client
.ln_payment_send(sender_wallet_id, ln_payment_request, memo)
.await
.context("Error occurred while sending payment to lightning invoice")?;

println!("Successfully sent payment to the given lightning invoice",);

Ok(())
}
}
1 change: 1 addition & 0 deletions src/app/operations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod auth;
pub mod batch;
pub mod globals;
pub mod intraledger;
mod lightning;
pub mod onchain;
pub mod request_phone_code;
pub mod user;
Expand Down
27 changes: 24 additions & 3 deletions src/cli/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,25 @@ pub enum Command {
Pay {
#[clap(short, long)]
username: Option<String>,
#[clap(short, long, conflicts_with("username"))]
#[clap(short, long, conflicts_with_all(["username", "ln_payment_request"]))]
onchain_address: Option<String>,
#[clap(short, long, conflicts_with_all(["username", "onchain_address"]))]
ln_payment_request: Option<String>,
#[clap(short, long, value_parser)]
wallet: Wallet,
#[clap(short, long, required_if_eq("wallet", "usd"))]
#[clap(
short,
long,
required_if_eq("wallet", "usd"),
required_unless_present("ln_payment_request")
)]
cents: Option<Decimal>,
#[clap(short, long, required_if_eq("wallet", "btc"))]
#[clap(
short,
long,
required_if_eq("wallet", "btc"),
required_unless_present("ln_payment_request")
)]
sats: Option<Decimal>,
#[clap(short, long)]
memo: Option<String>,
Expand All @@ -80,6 +92,15 @@ pub enum Command {
#[clap(short, long, value_parser)]
via: ReceiveVia,
},
/// Create a lightning invoice
LnInvoice {
#[clap(short, long, value_parser)]
wallet: Wallet,
#[clap(short, long, value_parser)]
amount: Decimal,
#[clap(short, long, value_parser)]
memo: Option<String>,
},
/// execute a batch payment
Batch {
#[clap(short, long = "csv")]
Expand Down
17 changes: 14 additions & 3 deletions src/cli/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,31 @@ pub async fn run() -> anyhow::Result<()> {
cents,
sats,
memo,
} => match (username, onchain_address) {
(Some(username), None) => {
ln_payment_request,
} => match (username, onchain_address, ln_payment_request) {
(Some(username), None, None) => {
app.intraledger_payment(username, wallet, cents, sats, memo)
.await?;
}
(None, Some(onchain_address)) => {
(None, Some(onchain_address), None) => {
app.send_onchain(onchain_address, wallet, cents, sats, memo)
.await?;
}
(None, None, Some(ln_payment_request)) => {
app.send_lightning(ln_payment_request, wallet, memo).await?;
}
_ => {}
},
Command::Receive { wallet, via } => {
app.receive(wallet, via).await?;
}
Command::LnInvoice {
wallet,
amount,
memo,
} => {
app.ln_invoice_create(wallet, amount, memo).await?;
}
Command::Batch {
file,
skip_confirmation,
Expand Down
14 changes: 14 additions & 0 deletions src/client/gql/mutations/ln_invoice_create.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
mutation LnInvoiceCreate($input: LnInvoiceCreateInput!) {
lnInvoiceCreate(input: $input) {
errors {
message
__typename
}
invoice {
paymentHash
paymentRequest
paymentSecret
__typename
}
}
}
9 changes: 9 additions & 0 deletions src/client/gql/mutations/ln_invoice_payment_send.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
mutation LnInvoicePaymentSend($input: LnInvoicePaymentInput!) {
lnInvoicePaymentSend(input: $input) {
errors {
__typename
message
}
status
}
}
14 changes: 14 additions & 0 deletions src/client/gql/mutations/ln_usd_invoice_create.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
mutation LnUsdInvoiceCreate($input: LnUsdInvoiceCreateInput!) {
lnUsdInvoiceCreate(input: $input) {
errors {
message
__typename
}
invoice {
paymentHash
paymentRequest
paymentSecret
__typename
}
}
}
30 changes: 30 additions & 0 deletions src/client/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ type SatAmount = Decimal;
type CentAmount = Decimal;
type Memo = String;
type OnChainAddress = String;
type PaymentHash = String;
type LnPaymentSecret = String;
type LnPaymentRequest = String;

#[derive(GraphQLQuery)]
#[graphql(
Expand Down Expand Up @@ -158,6 +161,33 @@ pub(super) struct OnChainAddressCurrent;
pub use self::on_chain_address_current::OnChainAddressCurrentInput;
pub use self::on_chain_address_current::OnChainAddressCurrentOnChainAddressCurrent;

#[derive(GraphQLQuery)]
#[graphql(
schema_path = "src/client/gql/schema.gql",
query_path = "src/client/gql/mutations/ln_invoice_create.gql",
response_derives = "Debug, Serialize"
)]
pub(super) struct LnInvoiceCreate;
pub use self::ln_invoice_create::LnInvoiceCreateInput;

#[derive(GraphQLQuery)]
#[graphql(
schema_path = "src/client/gql/schema.gql",
query_path = "src/client/gql/mutations/ln_usd_invoice_create.gql",
response_derives = "Debug, Serialize"
)]
pub(super) struct LnUsdInvoiceCreate;
pub use self::ln_usd_invoice_create::LnUsdInvoiceCreateInput;

#[derive(GraphQLQuery)]
#[graphql(
schema_path = "src/client/gql/schema.gql",
query_path = "src/client/gql/mutations/ln_invoice_payment_send.gql",
response_derives = "Debug, Serialize"
)]
pub(super) struct LnInvoicePaymentSend;
pub use self::ln_invoice_payment_send::LnInvoicePaymentInput;

#[derive(GraphQLQuery)]
#[graphql(
schema_path = "src/client/gql/schema.gql",
Expand Down
103 changes: 103 additions & 0 deletions src/client/requests/lightning.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use graphql_client::reqwest::post_graphql;
use rust_decimal::Decimal;

use crate::client::{
errors::{api_error::ApiError, ClientError},
queries::{
ln_invoice_create::{self, LnInvoiceCreateLnInvoiceCreate},
ln_invoice_payment_send,
ln_usd_invoice_create::{self, LnUsdInvoiceCreateLnUsdInvoiceCreate},
LnInvoiceCreate, LnInvoicePaymentInput, LnInvoicePaymentSend, LnUsdInvoiceCreate,
},
GaloyClient,
};

impl GaloyClient {
pub async fn lightning_invoice_create_btc(
&self,
receiving_wallet_id: String,
amount: Decimal,
memo: Option<String>,
) -> Result<LnInvoiceCreateLnInvoiceCreate, ClientError> {
let input = ln_invoice_create::LnInvoiceCreateInput {
wallet_id: receiving_wallet_id,
amount,
memo,
};
let variables = ln_invoice_create::Variables { input };

let response_body =
post_graphql::<LnInvoiceCreate, _>(&self.graphql_client, &self.api, variables)
.await
.map_err(|err| ApiError::IssueGettingResponse(anyhow::Error::new(err)))?;

let response_data = response_body.data.ok_or(ApiError::IssueParsingResponse)?;

let result = response_data.ln_invoice_create;

Ok(result)
}

pub async fn lightning_invoice_create_usd(
&self,
receiving_wallet_id: String,
amount: Decimal,
memo: Option<String>,
) -> Result<LnUsdInvoiceCreateLnUsdInvoiceCreate, ClientError> {
let input = ln_usd_invoice_create::LnUsdInvoiceCreateInput {
wallet_id: receiving_wallet_id,
amount,
memo,
};
let variables = ln_usd_invoice_create::Variables { input };

let response_body =
post_graphql::<LnUsdInvoiceCreate, _>(&self.graphql_client, &self.api, variables)
.await
.map_err(|err| ApiError::IssueGettingResponse(anyhow::Error::new(err)))?;

let response_data = response_body.data.ok_or(ApiError::IssueParsingResponse)?;

let result = response_data.ln_usd_invoice_create;

Ok(result)
}

pub async fn ln_payment_send(
&self,
sender_wallet_id: String,
payment_request: String,
memo: Option<String>,
) -> Result<(), ClientError> {
let input = LnInvoicePaymentInput {
wallet_id: sender_wallet_id,
payment_request,
memo,
};

let variables = ln_invoice_payment_send::Variables { input };

let response_body =
post_graphql::<LnInvoicePaymentSend, _>(&self.graphql_client, &self.api, variables)
.await
.map_err(|err| ApiError::IssueGettingResponse(anyhow::Error::new(err)))?;

let response_data = response_body.data.ok_or(ApiError::IssueParsingResponse)?;

if !response_data.ln_invoice_payment_send.errors.is_empty() {
let error_string: String = response_data
.ln_invoice_payment_send
.errors
.iter()
.map(|error| format!("{:?}", error))
.collect::<Vec<String>>()
.join(", ");

return Err(ClientError::ApiError(ApiError::RequestFailedWithError(
error_string,
)));
} else {
Ok(())
}
}
}
1 change: 1 addition & 0 deletions src/client/requests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod auth;
mod default_wallet;
mod globals;
mod intraledger;
mod lightning;
mod me;
mod onchain;
mod set_username;