@@ -25,6 +25,7 @@ pub mod ui;
|
||||
mod unknowns;
|
||||
mod urls;
|
||||
mod user_account;
|
||||
mod wallet;
|
||||
|
||||
pub use accounts::{AccountData, Accounts, AccountsAction, AddAccountAction, SwitchAccountAction};
|
||||
pub use app::{App, Notedeck};
|
||||
@@ -52,6 +53,7 @@ pub use timecache::TimeCached;
|
||||
pub use unknowns::{get_unknown_note_ids, NoteRefsUnkIdAction, SingleUnkIdAction, UnknownIds};
|
||||
pub use urls::{supported_mime_hosted_at_url, SupportedMimeType, UrlMimes};
|
||||
pub use user_account::UserAccount;
|
||||
pub use wallet::{GlobalWallet, Wallet, WalletError, WalletState, WalletType, WalletUIState};
|
||||
|
||||
// export libs
|
||||
pub use enostr;
|
||||
|
||||
225
crates/notedeck/src/wallet.rs
Normal file
225
crates/notedeck/src/wallet.rs
Normal file
@@ -0,0 +1,225 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use nwc::{
|
||||
nostr::nips::nip47::{NostrWalletConnectURI, PayInvoiceRequest, PayInvoiceResponse},
|
||||
NWC,
|
||||
};
|
||||
use poll_promise::Promise;
|
||||
use tokenator::TokenSerializable;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::{DataPath, TokenHandler};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WalletState<'a> {
|
||||
Wallet {
|
||||
wallet: &'a mut Wallet,
|
||||
can_create_local_wallet: bool,
|
||||
},
|
||||
NoWallet {
|
||||
state: &'a mut WalletUIState,
|
||||
show_local_only: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub enum WalletType {
|
||||
Auto,
|
||||
Local,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct WalletUIState {
|
||||
pub buf: String,
|
||||
pub error_msg: Option<WalletError>,
|
||||
pub for_local_only: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WalletError {
|
||||
InvalidURI,
|
||||
}
|
||||
|
||||
pub struct Wallet {
|
||||
pub uri: String,
|
||||
wallet: Arc<RwLock<NWC>>,
|
||||
balance: Option<Promise<Result<u64, nwc::Error>>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Wallet {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "Wallet({})", self.uri)
|
||||
}
|
||||
}
|
||||
|
||||
impl Wallet {
|
||||
pub fn new(uri: String) -> Result<Self, crate::Error> {
|
||||
let nwc_uri = NostrWalletConnectURI::parse(uri.clone())
|
||||
.map_err(|e| crate::Error::Generic(e.to_string()))?;
|
||||
|
||||
let nwc = NWC::new(nwc_uri);
|
||||
|
||||
Ok(Self {
|
||||
uri,
|
||||
wallet: Arc::new(RwLock::new(nwc)),
|
||||
balance: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_balance(&mut self) -> Option<&Result<u64, nwc::Error>> {
|
||||
if self.balance.is_none() {
|
||||
self.balance = Some(get_balance(self.wallet.clone()));
|
||||
return None;
|
||||
}
|
||||
let promise = self.balance.as_ref().unwrap();
|
||||
|
||||
if let Some(bal) = promise.ready() {
|
||||
Some(bal)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pay_invoice(
|
||||
&mut self,
|
||||
invoice: &str,
|
||||
) -> Promise<Result<PayInvoiceResponse, nwc::Error>> {
|
||||
pay_invoice(
|
||||
self.wallet.clone(),
|
||||
PayInvoiceRequest::new(invoice.to_owned()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_balance(nwc: Arc<RwLock<NWC>>) -> Promise<Result<u64, nwc::Error>> {
|
||||
let (sender, promise) = Promise::new();
|
||||
|
||||
tokio::spawn(async move {
|
||||
sender.send(nwc.read().await.get_balance().await);
|
||||
});
|
||||
|
||||
promise
|
||||
}
|
||||
|
||||
fn pay_invoice(
|
||||
nwc: Arc<RwLock<NWC>>,
|
||||
invoice: PayInvoiceRequest,
|
||||
) -> Promise<Result<PayInvoiceResponse, nwc::Error>> {
|
||||
let (sender, promise) = Promise::new();
|
||||
|
||||
tokio::spawn(async move {
|
||||
sender.send(nwc.read().await.pay_invoice(invoice).await);
|
||||
});
|
||||
|
||||
promise
|
||||
}
|
||||
|
||||
impl TokenSerializable for Wallet {
|
||||
fn parse_from_tokens<'a>(
|
||||
parser: &mut tokenator::TokenParser<'a>,
|
||||
) -> Result<Self, tokenator::ParseError<'a>> {
|
||||
parser.parse_all(|p| {
|
||||
p.parse_token("nwc_uri")?;
|
||||
|
||||
let raw_uri = p.pull_token()?;
|
||||
|
||||
let wallet =
|
||||
Wallet::new(raw_uri.to_owned()).map_err(|_| tokenator::ParseError::DecodeFailed)?;
|
||||
|
||||
Ok(wallet)
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize_tokens(&self, writer: &mut tokenator::TokenWriter) {
|
||||
writer.write_token("nwc_uri");
|
||||
writer.write_token(&self.uri);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct GlobalWallet {
|
||||
pub wallet: Option<Wallet>,
|
||||
pub ui_state: WalletUIState,
|
||||
wallet_handler: TokenHandler,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl GlobalWallet {
|
||||
pub fn new(path: &DataPath) -> Self {
|
||||
let wallet_handler =
|
||||
TokenHandler::new(path, crate::DataPathType::Setting, "global_wallet.txt");
|
||||
let wallet = construct_global_wallet(&wallet_handler);
|
||||
|
||||
Self {
|
||||
wallet,
|
||||
ui_state: WalletUIState::default(),
|
||||
wallet_handler,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_wallet(&self) {
|
||||
let Some(wallet) = &self.wallet else {
|
||||
// saving with no wallet means delete
|
||||
if let Err(e) = self.wallet_handler.clear() {
|
||||
tracing::error!("Could not clear wallet: {e}");
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
match self.wallet_handler.save(wallet, "\t") {
|
||||
Ok(_) => {}
|
||||
Err(e) => tracing::error!("Could not save global wallet: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn construct_global_wallet(wallet_handler: &TokenHandler) -> Option<Wallet> {
|
||||
let Ok(res) = wallet_handler.load::<Wallet>("\t") else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let wallet = match res {
|
||||
Ok(wallet) => wallet,
|
||||
Err(e) => {
|
||||
tracing::error!("Error parsing wallet: {:?}", e);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some(wallet)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use tokenator::{TokenParser, TokenSerializable, TokenWriter};
|
||||
|
||||
use crate::Wallet;
|
||||
|
||||
const URI: &str = "nostr+walletconnect://b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4?relay=wss%3A%2F%2Frelay.damus.io&secret=71a8c14c1407c113601079c4302dab36460f0ccd0ad506f1f2dc73b5100e4f3c&lud16=nostr%40nostr.com";
|
||||
|
||||
#[test]
|
||||
|
||||
fn test_uri() {
|
||||
assert!(Wallet::new(URI.to_owned()).is_ok())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_serialize_deserialize() {
|
||||
let wallet = Wallet::new(URI.to_owned()).unwrap();
|
||||
|
||||
let mut writer = TokenWriter::new("\t");
|
||||
wallet.serialize_tokens(&mut writer);
|
||||
let serialized = writer.str();
|
||||
|
||||
let data = &serialized.split("\t").collect::<Vec<&str>>();
|
||||
let mut parser = TokenParser::new(data);
|
||||
let m_new_wallet = Wallet::parse_from_tokens(&mut parser);
|
||||
|
||||
assert!(m_new_wallet.is_ok());
|
||||
|
||||
let new_wallet = m_new_wallet.unwrap();
|
||||
|
||||
assert_eq!(wallet.uri, new_wallet.uri);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user