use std::{collections::HashMap, ops::Deref, sync::Arc};
use serde::{Deserialize, Serialize};
use tokio::sync::{Mutex, RwLock};
#[cfg(feature = "events")]
use crate::wallet::events::EventEmitter;
#[cfg(feature = "storage")]
use crate::wallet::storage::manager::StorageManagerHandle;
use crate::{
client::{secret::SecretManager, Client},
types::block::{
output::{FoundryId, Output, OutputId, TokenId},
payload::transaction::TransactionId,
},
wallet::{
account::{
types::{
address::{AccountAddress, AddressWithUnspentOutputs},
OutputData, Transaction,
},
Account,
},
Result,
},
};
#[derive(Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub struct FilterOptions {
#[serde(rename = "lowerBoundBookedTimestamp")]
pub lower_bound_booked_timestamp: Option<u32>,
#[serde(rename = "upperBoundBookedTimestamp")]
pub upper_bound_booked_timestamp: Option<u32>,
#[serde(rename = "outputTypes")]
pub output_types: Option<Vec<u8>>,
}
#[derive(Debug, Clone)]
pub struct AccountHandle {
account: Arc<RwLock<Account>>,
pub(crate) client: Client,
pub(crate) secret_manager: Arc<RwLock<SecretManager>>,
pub(crate) last_synced: Arc<Mutex<u128>>,
#[cfg(feature = "events")]
pub(crate) event_emitter: Arc<Mutex<EventEmitter>>,
#[cfg(feature = "storage")]
pub(crate) storage_manager: StorageManagerHandle,
}
impl AccountHandle {
pub(crate) fn new(
account: Account,
client: Client,
secret_manager: Arc<RwLock<SecretManager>>,
#[cfg(feature = "events")] event_emitter: Arc<Mutex<EventEmitter>>,
#[cfg(feature = "storage")] storage_manager: StorageManagerHandle,
) -> Self {
Self {
account: Arc::new(RwLock::new(account)),
client,
secret_manager,
last_synced: Default::default(),
#[cfg(feature = "events")]
event_emitter,
#[cfg(feature = "storage")]
storage_manager,
}
}
pub async fn alias(&self) -> String {
self.read().await.alias.clone()
}
pub fn client(&self) -> &Client {
&self.client
}
pub async fn get_output(&self, output_id: &OutputId) -> Option<OutputData> {
let account = self.read().await;
account.outputs().get(output_id).cloned()
}
pub async fn get_foundry_output(&self, native_token_id: TokenId) -> Result<Output> {
let foundry_id = FoundryId::from(native_token_id);
for output_data in self.read().await.outputs().values() {
if let Output::Foundry(foundry_output) = &output_data.output {
if foundry_output.id() == foundry_id {
return Ok(output_data.output.clone());
}
}
}
let foundry_output_id = self.client.foundry_output_id(foundry_id).await?;
let output_response = self.client.get_output(&foundry_output_id).await?;
Ok(Output::try_from_dto(
&output_response.output,
self.client.get_token_supply().await?,
)?)
}
pub async fn get_transaction(&self, transaction_id: &TransactionId) -> Option<Transaction> {
let account = self.read().await;
account.transactions().get(transaction_id).cloned()
}
pub async fn get_incoming_transaction_data(&self, transaction_id: &TransactionId) -> Option<Transaction> {
let account = self.read().await;
account.incoming_transactions().get(transaction_id).cloned()
}
pub async fn addresses(&self) -> Result<Vec<AccountAddress>> {
let account = self.read().await;
let mut all_addresses = account.public_addresses().clone();
all_addresses.extend(account.internal_addresses().clone());
Ok(all_addresses.to_vec())
}
pub(crate) async fn public_addresses(&self) -> Vec<AccountAddress> {
let account = self.read().await;
account.public_addresses().to_vec()
}
pub async fn addresses_with_unspent_outputs(&self) -> Result<Vec<AddressWithUnspentOutputs>> {
let account = self.read().await;
Ok(account.addresses_with_unspent_outputs().to_vec())
}
pub async fn outputs(&self, filter: Option<FilterOptions>) -> Result<Vec<OutputData>> {
let mut outputs = Vec::new();
for output in self.read().await.outputs.values() {
if let Some(filter_options) = &filter {
if let Some(lower_bound_booked_timestamp) = filter_options.lower_bound_booked_timestamp {
if output.metadata.milestone_timestamp_booked < lower_bound_booked_timestamp {
continue;
}
}
if let Some(upper_bound_booked_timestamp) = filter_options.upper_bound_booked_timestamp {
if output.metadata.milestone_timestamp_booked > upper_bound_booked_timestamp {
continue;
}
}
if let Some(output_types) = &filter_options.output_types {
if !output_types.contains(&output.output.kind()) {
continue;
}
}
}
outputs.push(output.clone());
}
Ok(outputs)
}
pub async fn unspent_outputs(&self, filter: Option<FilterOptions>) -> Result<Vec<OutputData>> {
let mut outputs = Vec::new();
for output in self.read().await.unspent_outputs.values() {
if let Some(filter_options) = &filter {
if let Some(lower_bound_booked_timestamp) = filter_options.lower_bound_booked_timestamp {
if output.metadata.milestone_timestamp_booked < lower_bound_booked_timestamp {
continue;
}
}
if let Some(upper_bound_booked_timestamp) = filter_options.upper_bound_booked_timestamp {
if output.metadata.milestone_timestamp_booked > upper_bound_booked_timestamp {
continue;
}
}
if let Some(output_types) = &filter_options.output_types {
if !output_types.contains(&output.output.kind()) {
continue;
}
}
}
outputs.push(output.clone());
}
Ok(outputs)
}
pub async fn incoming_transactions(&self) -> Result<HashMap<TransactionId, Transaction>> {
Ok(self.read().await.incoming_transactions.clone())
}
pub async fn transactions(&self) -> Result<Vec<Transaction>> {
Ok(self.read().await.transactions.values().cloned().collect())
}
pub async fn pending_transactions(&self) -> Result<Vec<Transaction>> {
let mut transactions = Vec::new();
let account = self.read().await;
for transaction_id in &account.pending_transactions {
if let Some(transaction) = account.transactions.get(transaction_id) {
transactions.push(transaction.clone());
}
}
Ok(transactions)
}
#[cfg(feature = "storage")]
pub(crate) async fn save(&self, updated_account: Option<&Account>) -> Result<()> {
log::debug!("[save] saving account to database");
match updated_account {
Some(account) => {
let mut storage_manager = self.storage_manager.lock().await;
storage_manager.save_account(account).await?;
drop(storage_manager);
}
None => {
let account = self.read().await;
let mut storage_manager = self.storage_manager.lock().await;
storage_manager.save_account(&account).await?;
drop(storage_manager);
drop(account);
}
}
Ok(())
}
}
impl Deref for AccountHandle {
type Target = RwLock<Account>;
fn deref(&self) -> &Self::Target {
self.account.deref()
}
}