diff --git a/bindings/core/src/method_handler/wallet.rs b/bindings/core/src/method_handler/wallet.rs index b83cda4e7c..58169a8b80 100644 --- a/bindings/core/src/method_handler/wallet.rs +++ b/bindings/core/src/method_handler/wallet.rs @@ -211,7 +211,7 @@ pub(crate) async fn call_wallet_method_internal(wallet: &Wallet, method: WalletM Response::Transactions(transactions.iter().map(TransactionWithMetadataDto::from).collect()) } WalletMethod::Outputs { filter_options } => { - let outputs = wallet.outputs(filter_options).await?; + let outputs = wallet.outputs(filter_options).await; Response::OutputsData(outputs.iter().map(OutputDataDto::from).collect()) } WalletMethod::PendingTransactions => { @@ -394,7 +394,7 @@ pub(crate) async fn call_wallet_method_internal(wallet: &Wallet, method: WalletM Response::Transactions(transactions.iter().map(TransactionWithMetadataDto::from).collect()) } WalletMethod::UnspentOutputs { filter_options } => { - let outputs = wallet.unspent_outputs(filter_options).await?; + let outputs = wallet.unspent_outputs(filter_options).await; Response::OutputsData(outputs.iter().map(OutputDataDto::from).collect()) } }; diff --git a/cli/src/wallet_cli/mod.rs b/cli/src/wallet_cli/mod.rs index 5044ba6311..987e7204e6 100644 --- a/cli/src/wallet_cli/mod.rs +++ b/cli/src/wallet_cli/mod.rs @@ -654,7 +654,7 @@ pub async fn output_command(wallet: &Wallet, selector: OutputSelector) -> Result let output = match selector { OutputSelector::Id(id) => wallet.get_output(&id).await, OutputSelector::Index(index) => { - let mut outputs = wallet.outputs(None).await?; + let mut outputs = wallet.outputs(None).await; outputs.sort_unstable_by(outputs_ordering); outputs.into_iter().nth(index) } @@ -671,7 +671,7 @@ pub async fn output_command(wallet: &Wallet, selector: OutputSelector) -> Result /// `outputs` command pub async fn outputs_command(wallet: &Wallet) -> Result<(), Error> { - print_outputs(wallet.outputs(None).await?, "Outputs:").await + print_outputs(wallet.outputs(None).await, "Outputs:").await } // `send` command @@ -828,7 +828,7 @@ pub async fn transactions_command(wallet: &Wallet, show_details: bool) -> Result /// `unspent-outputs` command pub async fn unspent_outputs_command(wallet: &Wallet) -> Result<(), Error> { - print_outputs(wallet.unspent_outputs(None).await?, "Unspent outputs:").await + print_outputs(wallet.unspent_outputs(None).await, "Unspent outputs:").await } pub async fn vote_command(wallet: &Wallet, event_id: ParticipationEventId, answers: Vec) -> Result<(), Error> { @@ -917,7 +917,7 @@ async fn print_wallet_address(wallet: &Wallet) -> Result<(), Error> { address.inner() ); - let unspent_outputs = wallet.unspent_outputs(None).await?; + let unspent_outputs = wallet.unspent_outputs(None).await; let slot_index = wallet.client().get_slot_index().await?; let mut output_ids = Vec::new(); diff --git a/sdk/examples/how_tos/accounts_and_addresses/consolidate_outputs.rs b/sdk/examples/how_tos/accounts_and_addresses/consolidate_outputs.rs index 08fb4a564d..bb08f13690 100644 --- a/sdk/examples/how_tos/accounts_and_addresses/consolidate_outputs.rs +++ b/sdk/examples/how_tos/accounts_and_addresses/consolidate_outputs.rs @@ -42,7 +42,7 @@ async fn main() -> Result<()> { // unlock condition and it is an `AddressUnlockCondition`, and so they are valid for consolidation. They have the // same `AddressUnlockCondition`(the address of the wallet), so they will be consolidated into one // output. - let outputs = wallet.unspent_outputs(None).await?; + let outputs = wallet.unspent_outputs(None).await; println!("Outputs BEFORE consolidation:"); outputs.iter().enumerate().for_each(|(i, output_data)| { println!("OUTPUT #{i}"); @@ -78,7 +78,7 @@ async fn main() -> Result<()> { println!("Wallet synced"); // Outputs after consolidation - let outputs = wallet.unspent_outputs(None).await?; + let outputs = wallet.unspent_outputs(None).await; println!("Outputs AFTER consolidation:"); outputs.iter().enumerate().for_each(|(i, output_data)| { println!("OUTPUT #{i}"); diff --git a/sdk/examples/how_tos/accounts_and_addresses/list_outputs.rs b/sdk/examples/how_tos/accounts_and_addresses/list_outputs.rs index b125b8a051..9c732c4831 100644 --- a/sdk/examples/how_tos/accounts_and_addresses/list_outputs.rs +++ b/sdk/examples/how_tos/accounts_and_addresses/list_outputs.rs @@ -25,13 +25,13 @@ async fn main() -> Result<()> { // Print output ids println!("Output ids:"); - for output in wallet.outputs(None).await? { + for output in wallet.outputs(None).await { println!("{}", output.output_id); } // Print unspent output ids println!("Unspent output ids:"); - for output in wallet.unspent_outputs(None).await? { + for output in wallet.unspent_outputs(None).await { println!("{}", output.output_id); } diff --git a/sdk/examples/wallet/spammer.rs b/sdk/examples/wallet/spammer.rs index d16c621fea..9387836fd1 100644 --- a/sdk/examples/wallet/spammer.rs +++ b/sdk/examples/wallet/spammer.rs @@ -74,7 +74,7 @@ async fn main() -> Result<()> { output_types: Some(vec![BasicOutput::KIND]), ..Default::default() }) - .await? + .await .iter() .filter(|data| data.output.amount() >= SEND_AMOUNT) .count(); diff --git a/sdk/src/types/block/output/mod.rs b/sdk/src/types/block/output/mod.rs index d6bd867569..cf3621c937 100644 --- a/sdk/src/types/block/output/mod.rs +++ b/sdk/src/types/block/output/mod.rs @@ -117,14 +117,14 @@ pub enum Output { Basic(BasicOutput), /// An account output. Account(AccountOutput), + /// An anchor output. + Anchor(AnchorOutput), /// A foundry output. Foundry(FoundryOutput), /// An NFT output. Nft(NftOutput), /// A delegation output. Delegation(DelegationOutput), - /// An anchor output. - Anchor(AnchorOutput), } impl core::fmt::Debug for Output { diff --git a/sdk/src/wallet/core/mod.rs b/sdk/src/wallet/core/mod.rs index 44fb61d0df..3314b6b0f9 100644 --- a/sdk/src/wallet/core/mod.rs +++ b/sdk/src/wallet/core/mod.rs @@ -33,7 +33,10 @@ use crate::{ types::{ block::{ address::{Address, Bech32Address, Hrp, ImplicitAccountCreationAddress}, - output::{dto::FoundryOutputDto, AccountId, FoundryId, FoundryOutput, NftId, Output, OutputId, TokenId}, + output::{ + dto::FoundryOutputDto, AccountId, AnchorId, DelegationId, FoundryId, FoundryOutput, NftId, Output, + OutputId, TokenId, + }, payload::signed_transaction::{dto::TransactionDto, Transaction, TransactionId}, }, TryFromDto, @@ -296,7 +299,7 @@ where &self, outputs: impl Iterator, filter: impl Into>, - ) -> Result> { + ) -> Vec { let filter = filter.into(); if let Some(filter) = filter { @@ -304,15 +307,24 @@ where for output in outputs { match &output.output { - Output::Account(alias) => { + Output::Account(account) => { if let Some(account_ids) = &filter.account_ids { - let account_id = alias.account_id_non_null(&output.output_id); + let account_id = account.account_id_non_null(&output.output_id); if account_ids.contains(&account_id) { filtered_outputs.push(output.clone()); continue; } } } + Output::Anchor(anchor) => { + if let Some(anchor_ids) = &filter.anchor_ids { + let anchor_id = anchor.anchor_id_non_null(&output.output_id); + if anchor_ids.contains(&anchor_id) { + filtered_outputs.push(output.clone()); + continue; + } + } + } Output::Foundry(foundry) => { if let Some(foundry_ids) = &filter.foundry_ids { let foundry_id = foundry.id(); @@ -331,6 +343,15 @@ where } } } + Output::Delegation(delegation) => { + if let Some(delegation_ids) = &filter.delegation_ids { + let delegation_id = delegation.delegation_id_non_null(&output.output_id); + if delegation_ids.contains(&delegation_id) { + filtered_outputs.push(output.clone()); + continue; + } + } + } _ => {} } @@ -352,56 +373,86 @@ where } } - // If ids are provided, only return them and no other outputs. - if filter.account_ids.is_none() && filter.foundry_ids.is_none() && filter.nft_ids.is_none() { + // Include the output if we're not filtering by IDs. + if filter.account_ids.is_none() + && filter.anchor_ids.is_none() + && filter.foundry_ids.is_none() + && filter.nft_ids.is_none() + && filter.delegation_ids.is_none() + { filtered_outputs.push(output.clone()); } } - Ok(filtered_outputs) + filtered_outputs } else { - Ok(outputs.cloned().collect()) + outputs.cloned().collect() } } /// Returns outputs of the wallet. - pub async fn outputs(&self, filter: impl Into> + Send) -> Result> { + pub async fn outputs(&self, filter: impl Into> + Send) -> Vec { self.filter_outputs(self.data().await.outputs.values(), filter) } /// Returns unspent outputs of the wallet. - pub async fn unspent_outputs(&self, filter: impl Into> + Send) -> Result> { + pub async fn unspent_outputs(&self, filter: impl Into> + Send) -> Vec { self.filter_outputs(self.data().await.unspent_outputs.values(), filter) } /// Gets the unspent account output matching the given ID. - pub async fn unspent_account_output(&self, account_id: &AccountId) -> Result> { + pub async fn unspent_account_output(&self, account_id: &AccountId) -> Option { self.unspent_outputs(FilterOptions { account_ids: Some([*account_id].into()), ..Default::default() }) .await - .map(|res| res.get(0).cloned()) + .first() + .cloned() + } + + /// Gets the unspent anchor output matching the given ID. + pub async fn unspent_anchor_output(&self, anchor_id: &AnchorId) -> Option { + self.unspent_outputs(FilterOptions { + anchor_ids: Some([*anchor_id].into()), + ..Default::default() + }) + .await + .first() + .cloned() } /// Gets the unspent foundry output matching the given ID. - pub async fn unspent_foundry_output(&self, foundry_id: &FoundryId) -> Result> { + pub async fn unspent_foundry_output(&self, foundry_id: &FoundryId) -> Option { self.unspent_outputs(FilterOptions { foundry_ids: Some([*foundry_id].into()), ..Default::default() }) .await - .map(|res| res.get(0).cloned()) + .first() + .cloned() } /// Gets the unspent nft output matching the given ID. - pub async fn unspent_nft_output(&self, nft_id: &NftId) -> Result> { + pub async fn unspent_nft_output(&self, nft_id: &NftId) -> Option { self.unspent_outputs(FilterOptions { nft_ids: Some([*nft_id].into()), ..Default::default() }) .await - .map(|res| res.get(0).cloned()) + .first() + .cloned() + } + + /// Gets the unspent delegation output matching the given ID. + pub async fn unspent_delegation_output(&self, delegation_id: &DelegationId) -> Option { + self.unspent_outputs(FilterOptions { + delegation_ids: Some([*delegation_id].into()), + ..Default::default() + }) + .await + .first() + .cloned() } /// Returns all incoming transactions of the wallet diff --git a/sdk/src/wallet/mod.rs b/sdk/src/wallet/mod.rs index 07160d4e38..258d5be150 100644 --- a/sdk/src/wallet/mod.rs +++ b/sdk/src/wallet/mod.rs @@ -73,7 +73,7 @@ use crate::{ types::{ api::core::OutputWithMetadataResponse, block::{ - output::{AccountId, FoundryId, NftId}, + output::{AccountId, AnchorId, DelegationId, FoundryId, NftId}, payload::signed_transaction::{SignedTransactionPayload, TransactionId}, }, }, @@ -95,10 +95,14 @@ pub struct FilterOptions { pub output_types: Option>, /// Return all account outputs matching these IDs. pub account_ids: Option>, + /// Return all anchor outputs matching these IDs. + pub anchor_ids: Option>, /// Return all foundry outputs matching these IDs. pub foundry_ids: Option>, /// Return all nft outputs matching these IDs. pub nft_ids: Option>, + /// Return all delegation outputs matching these IDs. + pub delegation_ids: Option>, } pub(crate) fn build_transaction_from_payload_and_inputs( diff --git a/sdk/src/wallet/operations/participation/mod.rs b/sdk/src/wallet/operations/participation/mod.rs index 499135b53b..cd7154580d 100644 --- a/sdk/src/wallet/operations/participation/mod.rs +++ b/sdk/src/wallet/operations/participation/mod.rs @@ -71,7 +71,7 @@ where "[get_participation_overview] restored_spent_cached_outputs_len: {}", restored_spent_cached_outputs_len ); - let outputs = self.outputs(None).await?; + let outputs = self.outputs(None).await; let participation_outputs = outputs .into_iter() .filter(|output_data| { @@ -230,7 +230,7 @@ where log::debug!("[get_voting_output]"); Ok(self .unspent_outputs(None) - .await? + .await .iter() .filter(|output_data| is_valid_participation_output(&output_data.output)) .max_by_key(|output_data| output_data.output.amount()) diff --git a/sdk/src/wallet/operations/syncing/mod.rs b/sdk/src/wallet/operations/syncing/mod.rs index 8757ff084e..abbbbb4d4e 100644 --- a/sdk/src/wallet/operations/syncing/mod.rs +++ b/sdk/src/wallet/operations/syncing/mod.rs @@ -99,7 +99,7 @@ where address: self.address().await, output_ids: self .unspent_outputs(None) - .await? + .await .into_iter() .map(|data| data.output_id) .collect::>(), diff --git a/sdk/src/wallet/operations/transaction/high_level/send_nft.rs b/sdk/src/wallet/operations/transaction/high_level/send_nft.rs index 72560a5599..11e045c813 100644 --- a/sdk/src/wallet/operations/transaction/high_level/send_nft.rs +++ b/sdk/src/wallet/operations/transaction/high_level/send_nft.rs @@ -95,7 +95,7 @@ where { log::debug!("[TRANSACTION] prepare_send_nft"); - let unspent_outputs = self.unspent_outputs(None).await?; + let unspent_outputs = self.unspent_outputs(None).await; let token_supply = self.client().get_token_supply().await?; let mut outputs = Vec::new(); diff --git a/sdk/src/wallet/operations/transaction/prepare_output.rs b/sdk/src/wallet/operations/transaction/prepare_output.rs index 70c2e3a612..3c0507d4c5 100644 --- a/sdk/src/wallet/operations/transaction/prepare_output.rs +++ b/sdk/src/wallet/operations/transaction/prepare_output.rs @@ -253,7 +253,7 @@ where ) } else { // Transition an existing NFT output - let unspent_nft_output = self.unspent_nft_output(nft_id).await?; + let unspent_nft_output = self.unspent_nft_output(nft_id).await; // Find nft output from the inputs let mut first_output_builder = if let Some(nft_output_data) = &unspent_nft_output { diff --git a/sdk/tests/wallet/consolidation.rs b/sdk/tests/wallet/consolidation.rs index 69f292f8d2..64b92e0753 100644 --- a/sdk/tests/wallet/consolidation.rs +++ b/sdk/tests/wallet/consolidation.rs @@ -31,7 +31,7 @@ async fn consolidation() -> Result<()> { let balance = wallet_1.sync(None).await.unwrap(); assert_eq!(balance.base_coin().available(), 10 * amount); - assert_eq!(wallet_1.unspent_outputs(None).await?.len(), 10); + assert_eq!(wallet_1.unspent_outputs(None).await.len(), 10); let tx = wallet_1 .consolidate_outputs(ConsolidationParams::new().with_force(true)) @@ -44,7 +44,7 @@ async fn consolidation() -> Result<()> { // Balance still the same assert_eq!(balance.base_coin().available(), 10 * amount); // Only one unspent output - assert_eq!(wallet_1.unspent_outputs(None).await?.len(), 1); + assert_eq!(wallet_1.unspent_outputs(None).await.len(), 1); tear_down(storage_path_0)?; tear_down(storage_path_1)?; diff --git a/sdk/tests/wallet/output_preparation.rs b/sdk/tests/wallet/output_preparation.rs index 5b0957c0ba..38063b95db 100644 --- a/sdk/tests/wallet/output_preparation.rs +++ b/sdk/tests/wallet/output_preparation.rs @@ -766,7 +766,7 @@ async fn prepare_output_only_single_nft() -> Result<()> { let balance = wallet_1.sync(None).await?; assert_eq!(balance.nfts().len(), 1); - let nft_data = &wallet_1.unspent_outputs(None).await?[0]; + let nft_data = &wallet_1.unspent_outputs(None).await[0]; let nft_id = *balance.nfts().first().unwrap(); // Send NFT back to first wallet let output = wallet_1