diff --git a/Cargo.lock b/Cargo.lock index ac44cf6869..5ec1f91fb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -714,6 +714,41 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.38", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.38", +] + [[package]] name = "data-encoding" version = "2.4.0" @@ -763,6 +798,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_setters" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "dialoguer" version = "0.11.0" @@ -1452,6 +1499,12 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.4.0" @@ -1604,6 +1657,7 @@ dependencies = [ "bitflags 2.4.1", "bs58", "derive_more", + "derive_setters", "dotenvy", "fern-logger", "futures", diff --git a/bindings/core/src/method/client.rs b/bindings/core/src/method/client.rs index f436913ebe..b82114b11a 100644 --- a/bindings/core/src/method/client.rs +++ b/bindings/core/src/method/client.rs @@ -5,7 +5,13 @@ use derivative::Derivative; #[cfg(feature = "mqtt")] use iota_sdk::client::mqtt::Topic; use iota_sdk::{ - client::{node_api::indexer::query_parameters::QueryParameter, node_manager::node::NodeAuth}, + client::{ + node_api::indexer::query_parameters::{ + AccountOutputQueryParameters, BasicOutputQueryParameters, FoundryOutputQueryParameters, + NftOutputQueryParameters, OutputQueryParameters, + }, + node_manager::node::NodeAuth, + }, types::block::{ address::{Bech32Address, Hrp}, output::{ @@ -209,19 +215,19 @@ pub enum ClientMethod { #[serde(rename_all = "camelCase")] OutputIds { /// Query parameters for output requests - query_parameters: Vec, + query_parameters: OutputQueryParameters, }, /// Fetch basic output IDs #[serde(rename_all = "camelCase")] BasicOutputIds { /// Query parameters for output requests - query_parameters: Vec, + query_parameters: BasicOutputQueryParameters, }, /// Fetch account output IDs #[serde(rename_all = "camelCase")] AccountOutputIds { /// Query parameters for output requests - query_parameters: Vec, + query_parameters: AccountOutputQueryParameters, }, /// Fetch account output ID #[serde(rename_all = "camelCase")] @@ -233,7 +239,7 @@ pub enum ClientMethod { #[serde(rename_all = "camelCase")] NftOutputIds { /// Query parameters for output requests - query_parameters: Vec, + query_parameters: NftOutputQueryParameters, }, /// Fetch NFT output ID #[serde(rename_all = "camelCase")] @@ -245,7 +251,7 @@ pub enum ClientMethod { #[serde(rename_all = "camelCase")] FoundryOutputIds { /// Query parameters for output requests - query_parameters: Vec, + query_parameters: FoundryOutputQueryParameters, }, /// Fetch foundry Output ID #[serde(rename_all = "camelCase")] diff --git a/bindings/nodejs/examples/client/16-custom-plugin.ts b/bindings/nodejs/examples/client/16-custom-plugin.ts index 67059f0ff2..9119d94fdb 100644 --- a/bindings/nodejs/examples/client/16-custom-plugin.ts +++ b/bindings/nodejs/examples/client/16-custom-plugin.ts @@ -27,7 +27,7 @@ async function run() { // Call our "custom" indexer plugin const outputId = await client.callPluginRoute( - 'api/indexer/v1/', + 'api/indexer/v2/', 'GET', route, undefined, diff --git a/bindings/nodejs/lib/client/client.ts b/bindings/nodejs/lib/client/client.ts index 99e9de6ba0..81bd7aafe4 100644 --- a/bindings/nodejs/lib/client/client.ts +++ b/bindings/nodejs/lib/client/client.ts @@ -865,7 +865,7 @@ export class Client { /** * Extension method which provides request methods for plugins. * - * @param basePluginPath The base path for the plugin eg indexer/v1/ . + * @param basePluginPath The base path for the plugin eg indexer/v2/ . * @param method The http method. * @param endpoint The path for the plugin request. * @param queryParams Additional query params for the request. diff --git a/bindings/python/examples/client/custom_plugin.py b/bindings/python/examples/client/custom_plugin.py index 79292ce7c8..edf75e80b9 100644 --- a/bindings/python/examples/client/custom_plugin.py +++ b/bindings/python/examples/client/custom_plugin.py @@ -28,5 +28,5 @@ # Call our "custom" indexer plugin output_id = client.call_plugin_route( - "api/indexer/v1/", "GET", route, None, None) + "api/indexer/v2/", "GET", route, None, None) print(json.dumps(output_id, indent=4)) diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 2dfaee0368..e0b3d94b03 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -32,6 +32,7 @@ derive_more = { version = "0.99.17", default-features = false, features = [ "add", "add_assign", ] } +derive_setters = { version = "0.1.6", default-features = false } getset = { version = "0.1.2", default-features = false } hashbrown = { version = "0.14.1", default-features = false, features = [ "ahash", @@ -130,6 +131,7 @@ pretty_assertions = { version = "1.4.0", default-features = false, features = [ dotenvy = { version = "0.15.7", default-features = false } fern-logger = { version = "0.5.0", default-features = false } num_cpus = { version = "1.16.0", default-features = false } +once_cell = { version = "1.18.0", default-features = false } tokio = { version = "1.33.0", default-features = false, features = [ "macros", "rt", @@ -563,6 +565,11 @@ name = "client_logger" path = "examples/client/logger.rs" required-features = ["client"] +[[example]] +name = "quorum" +path = "examples/client/quorum.rs" +required-features = ["client"] + [[example]] name = "stronghold" path = "examples/client/stronghold.rs" @@ -658,7 +665,7 @@ required-features = ["wallet", "participation"] [[example]] name = "logger" path = "examples/wallet/logger.rs" -required-features = ["wallet"] +required-features = ["wallet", "storage"] [[example]] name = "recover_accounts" diff --git a/sdk/examples/client/02_address_balance.rs b/sdk/examples/client/02_address_balance.rs index 0f2fdc060c..97c3d992ec 100644 --- a/sdk/examples/client/02_address_balance.rs +++ b/sdk/examples/client/02_address_balance.rs @@ -11,8 +11,8 @@ use iota_sdk::{ client::{ - api::GetAddressesOptions, node_api::indexer::query_parameters::QueryParameter, secret::SecretManager, Client, - Result, + api::GetAddressesOptions, node_api::indexer::query_parameters::BasicOutputQueryParameters, + secret::SecretManager, Client, Result, }, types::block::output::NativeTokensBuilder, }; @@ -43,12 +43,9 @@ async fn main() -> Result<()> { // Get output ids of outputs that can be controlled by this address without further unlock constraints let output_ids_response = client - .basic_output_ids([ - QueryParameter::Address(first_address.clone()), - QueryParameter::HasExpiration(false), - QueryParameter::HasTimelock(false), - QueryParameter::HasStorageDepositReturn(false), - ]) + .basic_output_ids(BasicOutputQueryParameters::only_address_unlock_condition( + first_address.clone(), + )) .await?; // Get the outputs by their id diff --git a/sdk/examples/client/node_api_indexer/02_get_account_outputs.rs b/sdk/examples/client/node_api_indexer/02_get_account_outputs.rs index ba1a6e770d..d708ee22e3 100644 --- a/sdk/examples/client/node_api_indexer/02_get_account_outputs.rs +++ b/sdk/examples/client/node_api_indexer/02_get_account_outputs.rs @@ -12,7 +12,7 @@ //! ``` use iota_sdk::{ - client::{node_api::indexer::query_parameters::QueryParameter, Client, Result}, + client::{node_api::indexer::query_parameters::AccountOutputQueryParameters, Client, Result}, types::block::address::Bech32Address, }; @@ -41,10 +41,7 @@ async fn main() -> Result<()> { // Get output IDs of account outputs that can be controlled by this address. let output_ids_response = client - .account_output_ids([ - QueryParameter::Governor(address.clone()), - QueryParameter::StateController(address), - ]) + .account_output_ids(AccountOutputQueryParameters::new().unlockable_by_address(address)) .await?; println!("Account output IDs: {output_ids_response:#?}"); diff --git a/sdk/examples/client/node_api_indexer/04_get_foundry_outputs.rs b/sdk/examples/client/node_api_indexer/04_get_foundry_outputs.rs index 0cd5aee1ec..dc7b853744 100644 --- a/sdk/examples/client/node_api_indexer/04_get_foundry_outputs.rs +++ b/sdk/examples/client/node_api_indexer/04_get_foundry_outputs.rs @@ -12,7 +12,7 @@ //! ``` use iota_sdk::{ - client::{node_api::indexer::query_parameters::QueryParameter, Client, Result}, + client::{node_api::indexer::query_parameters::FoundryOutputQueryParameters, Client, Result}, types::block::address::Bech32Address, }; @@ -41,7 +41,7 @@ async fn main() -> Result<()> { // Get output IDs of foundry outputs that can be controlled by this address. let output_ids_response = client - .foundry_output_ids([QueryParameter::AccountAddress(address)]) + .foundry_output_ids(FoundryOutputQueryParameters::new().account_address(address)) .await?; println!("Foundry output IDs: {output_ids_response:#?}"); diff --git a/sdk/examples/client/node_api_indexer/06_get_nft_outputs.rs b/sdk/examples/client/node_api_indexer/06_get_nft_outputs.rs index 8c69d356e7..d0210a27b1 100644 --- a/sdk/examples/client/node_api_indexer/06_get_nft_outputs.rs +++ b/sdk/examples/client/node_api_indexer/06_get_nft_outputs.rs @@ -12,7 +12,7 @@ //! ``` use iota_sdk::{ - client::{node_api::indexer::query_parameters::QueryParameter, Client, Result}, + client::{node_api::indexer::query_parameters::NftOutputQueryParameters, Client, Result}, types::block::address::Bech32Address, }; @@ -41,12 +41,7 @@ async fn main() -> Result<()> { // Get output IDs of NFT outputs that can be controlled by this address without further unlock constraints. let output_ids_response = client - .nft_output_ids([ - QueryParameter::Address(address), - QueryParameter::HasExpiration(false), - QueryParameter::HasTimelock(false), - QueryParameter::HasStorageDepositReturn(false), - ]) + .nft_output_ids(NftOutputQueryParameters::only_address_unlock_condition(address)) .await?; println!("NFT output IDs {output_ids_response:#?}"); diff --git a/sdk/examples/client/node_api_indexer/07_get_random_basic_outputs.rs b/sdk/examples/client/node_api_indexer/07_get_random_basic_outputs.rs index a7a599031f..72cc7d2c09 100644 --- a/sdk/examples/client/node_api_indexer/07_get_random_basic_outputs.rs +++ b/sdk/examples/client/node_api_indexer/07_get_random_basic_outputs.rs @@ -11,7 +11,7 @@ //! cargo run --release --example node_api_indexer_get_random_basic_outputs [NODE_URL] //! ``` -use iota_sdk::client::{node_api::indexer::query_parameters::QueryParameter, Client, Result}; +use iota_sdk::client::{node_api::indexer::query_parameters::BasicOutputQueryParameters, Client, Result}; #[tokio::main] async fn main() -> Result<()> { @@ -26,8 +26,10 @@ async fn main() -> Result<()> { // Create a node client. let client = Client::builder().with_node(&node_url)?.finish().await?; - // Get a single page with random output IDs by providing only `QueryParameter::Cursor(_)`. - let output_ids_response = client.basic_output_ids([QueryParameter::Cursor(String::new())]).await?; + // Get a single page with random output IDs by providing only an empty string as cursor. + let output_ids_response = client + .basic_output_ids(BasicOutputQueryParameters::new().cursor(String::new())) + .await?; println!("Basic output IDs from first page {output_ids_response:#?}"); diff --git a/sdk/examples/client/quorum.rs b/sdk/examples/client/quorum.rs index f5ebbf59eb..232de6e647 100644 --- a/sdk/examples/client/quorum.rs +++ b/sdk/examples/client/quorum.rs @@ -14,8 +14,8 @@ //! ``` use iota_sdk::client::{ - api::GetAddressesOptions, node_api::indexer::query_parameters::QueryParameter, secret::SecretManager, Client, - Result, + api::GetAddressesOptions, node_api::indexer::query_parameters::BasicOutputQueryParameters, secret::SecretManager, + Client, Result, }; #[tokio::main] @@ -52,12 +52,9 @@ async fn main() -> Result<()> { // Get output ids of outputs that can be controlled by this address without further unlock constraints let output_ids_response = client - .basic_output_ids([ - QueryParameter::Address(addresses[0]), - QueryParameter::HasExpiration(false), - QueryParameter::HasTimelock(false), - QueryParameter::HasStorageDepositReturn(false), - ]) + .basic_output_ids(BasicOutputQueryParameters::only_address_unlock_condition( + addresses[0].clone(), + )) .await?; println!("Address outputs: {output_ids_response:?}"); diff --git a/sdk/examples/client/send_all.rs b/sdk/examples/client/send_all.rs index 59d99627ae..a17dbb5dc5 100644 --- a/sdk/examples/client/send_all.rs +++ b/sdk/examples/client/send_all.rs @@ -41,12 +41,7 @@ async fn main() -> Result<()> { // Get output ids of outputs that can be controlled by this address without further unlock constraints let output_ids_response = client - .basic_output_ids([ - QueryParameter::Address(address), - QueryParameter::HasExpiration(false), - QueryParameter::HasTimelock(false), - QueryParameter::HasStorageDepositReturn(false), - ]) + .basic_output_ids(BasicOutputQueryParameters::only_address_unlock_condition(address)) .await?; // Get the outputs by their id diff --git a/sdk/examples/how_tos/account_wallet/transaction.rs b/sdk/examples/how_tos/account_wallet/transaction.rs index ec146dfe4d..946a14e297 100644 --- a/sdk/examples/how_tos/account_wallet/transaction.rs +++ b/sdk/examples/how_tos/account_wallet/transaction.rs @@ -7,7 +7,7 @@ //! `cargo run --release --all-features --example account_wallet_transaction` use iota_sdk::{ - client::node_api::indexer::query_parameters::QueryParameter, + client::node_api::indexer::query_parameters::BasicOutputQueryParameters, types::block::address::{AccountAddress, ToBech32Ext}, wallet::{ account::{AliasSyncOptions, SyncOptions, TransactionOptions}, @@ -54,7 +54,9 @@ async fn main() -> Result<()> { // Find first output unlockable by the account address let input = *account .client() - .basic_output_ids([QueryParameter::Address(account_address)]) + .basic_output_ids(BasicOutputQueryParameters::only_address_unlock_condition( + account_address, + )) .await? .items .first() diff --git a/sdk/examples/how_tos/client/get_outputs.rs b/sdk/examples/how_tos/client/get_outputs.rs index fe73db98ae..567dd80cf5 100644 --- a/sdk/examples/how_tos/client/get_outputs.rs +++ b/sdk/examples/how_tos/client/get_outputs.rs @@ -13,7 +13,7 @@ //! ``` use iota_sdk::{ - client::{node_api::indexer::query_parameters::QueryParameter, Client, Result}, + client::{node_api::indexer::query_parameters::BasicOutputQueryParameters, Client, Result}, types::block::address::Bech32Address, }; @@ -40,12 +40,7 @@ async fn main() -> Result<()> { // Get output IDs of basic outputs that can be controlled by this address without further unlock constraints. let output_ids_response = client - .basic_output_ids([ - QueryParameter::Address(address), - QueryParameter::HasExpiration(false), - QueryParameter::HasTimelock(false), - QueryParameter::HasStorageDepositReturn(false), - ]) + .basic_output_ids(BasicOutputQueryParameters::only_address_unlock_condition(address)) .await?; println!("First output of query:"); diff --git a/sdk/src/client/api/high_level.rs b/sdk/src/client/api/high_level.rs index 8334c1d071..352005c372 100644 --- a/sdk/src/client/api/high_level.rs +++ b/sdk/src/client/api/high_level.rs @@ -10,7 +10,7 @@ use crate::{ api::input_selection::Error as InputSelectionError, constants::FIVE_MINUTES_IN_SECONDS, error::{Error, Result}, - node_api::indexer::query_parameters::QueryParameter, + node_api::indexer::query_parameters::BasicOutputQueryParameters, unix_timestamp_now, Client, }, types::block::{ @@ -64,14 +64,7 @@ impl Client { pub async fn find_inputs(&self, addresses: Vec, amount: u64) -> Result> { // Get outputs from node and select inputs let available_outputs = futures::stream::iter(addresses) - .then(|address| { - self.basic_output_ids([ - QueryParameter::Address(address), - QueryParameter::HasExpiration(false), - QueryParameter::HasTimelock(false), - QueryParameter::HasStorageDepositReturn(false), - ]) - }) + .then(|address| self.basic_output_ids(BasicOutputQueryParameters::only_address_unlock_condition(address))) .and_then(|res| async { let items = res.items; self.get_outputs_with_metadata(&items).await diff --git a/sdk/src/client/error.rs b/sdk/src/client/error.rs index 0dfa1178be..e027b7fb04 100644 --- a/sdk/src/client/error.rs +++ b/sdk/src/client/error.rs @@ -12,8 +12,7 @@ use serde::{ }; use crate::{ - client::{api::input_selection::Error as InputSelectionError, node_api::indexer::QueryParameter}, - types::block::semantic::TransactionFailureReason, + client::api::input_selection::Error as InputSelectionError, types::block::semantic::TransactionFailureReason, }; /// Type alias of `Result` in iota-client @@ -136,9 +135,6 @@ pub enum Error { /// The semantic validation of a transaction failed. #[error("the semantic validation of a transaction failed with conflict reason: {} - {0:?}", *.0 as u8)] TransactionSemantic(TransactionFailureReason), - /// An indexer API request contains a query parameter not supported by the endpoint. - #[error("an indexer API request contains a query parameter not supported by the endpoint: {0}.")] - UnsupportedQueryParameter(QueryParameter), /// Unpack error #[error("{0}")] Unpack(#[from] packable::error::UnpackError), diff --git a/sdk/src/client/node_api/indexer/mod.rs b/sdk/src/client/node_api/indexer/mod.rs index 6ff4876411..24d4c9830e 100644 --- a/sdk/src/client/node_api/indexer/mod.rs +++ b/sdk/src/client/node_api/indexer/mod.rs @@ -6,7 +6,7 @@ pub mod query_parameters; pub mod routes; -pub(crate) use self::query_parameters::{QueryParameter, QueryParameters}; +use self::query_parameters::QueryParameter; use crate::{ client::{ClientInner, Result}, types::api::plugins::indexer::OutputIdsResponse, @@ -14,11 +14,11 @@ use crate::{ impl ClientInner { /// Get all output ids for a provided URL route and query parameters. - /// If a `QueryParameter::Cursor(_)` is provided, only a single page will be queried. + /// If an empty cursor is provided, only a single page will be queried. pub async fn get_output_ids( &self, route: &str, - mut query_parameters: QueryParameters, + mut query_parameters: impl QueryParameter, need_quorum: bool, prefer_permanode: bool, ) -> Result { @@ -28,8 +28,11 @@ impl ClientInner { items: Vec::new(), }; - // Return early with only a single page if a `QueryParameter::Cursor(_)` is provided. - let return_early = query_parameters.contains(QueryParameter::Cursor(String::new()).kind()); + let query_string = query_parameters.to_query_string(); + // Return early with only a single page if an empty string is provided as cursor. + let return_early = query_string + .map(|s| s.contains("cursor=&") || s.ends_with("cursor=")) + .unwrap_or(false); while let Some(cursor) = { let output_ids_response = self @@ -51,7 +54,7 @@ impl ClientInner { &merged_output_ids_response.cursor } { - query_parameters.replace(QueryParameter::Cursor(cursor.to_string())); + query_parameters.replace_cursor(cursor.to_string()); } Ok(merged_output_ids_response) diff --git a/sdk/src/client/node_api/indexer/query_parameters.rs b/sdk/src/client/node_api/indexer/query_parameters.rs index 687a870b27..0ad153d415 100644 --- a/sdk/src/client/node_api/indexer/query_parameters.rs +++ b/sdk/src/client/node_api/indexer/query_parameters.rs @@ -3,314 +3,281 @@ //! Query parameters for output_id requests -use std::fmt; - +use derive_setters::Setters; use serde::{Deserialize, Serialize}; -use crate::{ - client::{node_api::query_tuples_to_query_string, Error, Result}, - types::block::{address::Bech32Address, slot::SlotIndex}, -}; +use crate::types::block::{address::Bech32Address, output::TokenId, slot::SlotIndex}; // https://github.com/iotaledger/inx-indexer/tree/develop/pkg/indexer -/// Query parameters for output_id requests. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct QueryParameters(Vec); +pub trait QueryParameter: Serialize + Send + Sync { + /// Converts parameters to a single String. + fn to_query_string(&self) -> Option { + let value = serde_json::to_value(self).unwrap(); + let mut query_string = String::new(); -impl QueryParameters { - /// Creates a hashset from a provided vec of query parameters. - #[must_use] - pub fn new(query_parameters: impl Into>) -> Self { - let mut query_parameters = query_parameters.into(); - query_parameters.sort_unstable_by_key(QueryParameter::kind); - query_parameters.dedup_by_key(|qp| qp.kind()); + for (field, v) in value.as_object().unwrap().iter() { + if !v.is_null() { + if let Some(v_str) = v.as_str() { + if !query_string.is_empty() { + query_string.push('&'); + } + query_string.push_str(&format!("{}={}", field, v_str)); + } + if let Some(v_u64) = v.as_u64() { + if !query_string.is_empty() { + query_string.push('&'); + } + query_string.push_str(&format!("{}={}", field, v_u64)); + } + } + } - Self(query_parameters) + if query_string.is_empty() { + None + } else { + Some(query_string) + } } - /// Creates new empty QueryParameters. - pub fn empty() -> Self { - Self(Vec::new()) - } + fn replace_cursor(&mut self, cursor: String); +} - /// Replaces or inserts an enum variant in the QueryParameters. - pub fn replace(&mut self, query_parameter: QueryParameter) { - match self - .0 - .binary_search_by_key(&query_parameter.kind(), QueryParameter::kind) - { - Ok(pos) => self.0[pos] = query_parameter, - Err(pos) => self.0.insert(pos, query_parameter), +macro_rules! impl_query_parameters_methods { + ($name:ty) => { + impl $name { + pub fn new() -> Self { + Self::default() + } } - } - - /// Returns true if the slice contains an element with the given kind. - pub(crate) fn contains(&self, kind: u8) -> bool { - self.0.iter().any(|q| q.kind() == kind) - } - // Tests if any query parameter matches a predicate. - #[cfg(test)] - pub(crate) fn any bool>(&self, f: F) -> bool { - self.0.iter().any(f) - } + impl QueryParameter for $name { + fn replace_cursor(&mut self, cursor: String) { + self.cursor.replace(cursor); + } + } + }; +} - /// Converts parameters to a single String. - pub fn to_query_string(&self) -> Option { - query_tuples_to_query_string(self.0.iter().map(|q| Some(q.to_query_tuple()))) - } +/// Query parameters for output requests. +#[derive(Setters, Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[setters(strip_option)] +#[serde(rename_all = "camelCase")] +pub struct OutputQueryParameters { + /// Returns outputs that were created after a certain slot index. + created_after: Option, + /// Returns outputs that were created before a certain slot index. + created_before: Option, + /// Starts the search from the cursor (createdSlotIndex+outputId.pageSize). + cursor: Option, + /// Filters outputs based on the presence of a native token. + has_native_token: Option, + /// Filters outputs based on the presence of a specific native token. + native_token: Option, + /// The maximum amount of items returned in one call. If there are more items, a cursor to the next page is + /// returned too. The parameter is ignored when pageSize is defined via the cursor parameter. + page_size: Option, + /// Returns outputs that are unlockable by the bech32 address. + unlockable_by_address: Option, } -/// Query parameter for output requests. -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +impl_query_parameters_methods!(OutputQueryParameters); + +/// Query parameters for basic output requests. +#[derive(Setters, Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[setters(strip_option)] #[serde(rename_all = "camelCase")] -#[non_exhaustive] -pub enum QueryParameter { - /// Bech32-encoded address that should be searched for. - Address(Bech32Address), - /// Filter foundry outputs based on bech32-encoded address of the controlling account. - AccountAddress(Bech32Address), +pub struct BasicOutputQueryParameters { /// Returns outputs that were created after a certain slot index. - CreatedAfter(SlotIndex), + created_after: Option, /// Returns outputs that were created before a certain slot index. - CreatedBefore(SlotIndex), - /// Starts the search from the cursor (confirmationMS+outputId.pageSize). - Cursor(String), + created_before: Option, + /// Starts the search from the cursor (createdSlotIndex+outputId.pageSize). + cursor: Option, + /// Filters outputs based on the presence of a native token. + has_native_token: Option, + /// Filters outputs based on the presence of a specific native token. + native_token: Option, + /// The maximum amount of items returned in one call. If there are more items, a cursor to the next page is + /// returned too. The parameter is ignored when pageSize is defined via the cursor parameter. + page_size: Option, + /// Returns outputs that are unlockable by the bech32 address. + unlockable_by_address: Option, + /// Bech32-encoded address that should be searched for. + address: Option, /// Filters outputs based on the presence of a specific Bech32-encoded return address in the expiration unlock /// condition. - ExpirationReturnAddress(Bech32Address), + expiration_return_address: Option, /// Returns outputs that expire after a certain slot index. - ExpiresAfter(SlotIndex), + expires_after: Option, /// Returns outputs that expire before a certain slot index. - ExpiresBefore(SlotIndex), - /// Filters outputs based on bech32-encoded governor (governance controller) address. - Governor(Bech32Address), + expires_before: Option, /// Filters outputs based on the presence of expiration unlock condition. - HasExpiration(bool), - /// Filters outputs based on the presence of native tokens. - HasNativeTokens(bool), + has_expiration: Option, /// Filters outputs based on the presence of storage deposit return unlock condition. - HasStorageDepositReturn(bool), + has_storage_deposit_return: Option, /// Filters outputs based on the presence of timelock unlock condition. - HasTimelock(bool), - /// Filters outputs based on bech32-encoded issuer address. - Issuer(Bech32Address), - /// Filters outputs that have at most a certain number of distinct native tokens. - MaxNativeTokenCount(u32), - /// Filters outputs that have at least a certain number of distinct native tokens. - MinNativeTokenCount(u32), - /// The maximum amount of items returned in one call. If there are more items, a cursor to the next page is - /// returned too. The parameter is ignored when pageSize is defined via the cursor parameter. - PageSize(usize), + has_timelock: Option, /// Filters outputs based on the presence of validated Sender (bech32 encoded). - Sender(Bech32Address), - /// Filters outputs based on bech32-encoded state controller address. - StateController(Bech32Address), + sender: Option, /// Filters outputs based on the presence of a specific return address in the storage deposit return unlock /// condition. - StorageDepositReturnAddress(Bech32Address), + storage_deposit_return_address: Option, /// Filters outputs based on matching Tag Block. - Tag(String), + tag: Option, /// Returns outputs that are timelocked after a certain slot index. - TimelockedAfter(SlotIndex), + timelocked_after: Option, /// Returns outputs that are timelocked before a certain slot index. - TimelockedBefore(SlotIndex), - /// Returns outputs that are unlockable by the bech32 address. - UnlockableByAddress(Bech32Address), + timelocked_before: Option, } -impl QueryParameter { - fn to_query_tuple(&self) -> (&'static str, String) { - match self { - Self::Address(v) => ("address", v.to_string()), - Self::AccountAddress(v) => ("accountAddress", v.to_string()), - Self::CreatedAfter(v) => ("createdAfter", v.to_string()), - Self::CreatedBefore(v) => ("createdBefore", v.to_string()), - Self::Cursor(v) => ("cursor", v.to_string()), - Self::ExpirationReturnAddress(v) => ("expirationReturnAddress", v.to_string()), - Self::ExpiresAfter(v) => ("expiresAfter", v.to_string()), - Self::ExpiresBefore(v) => ("expiresBefore", v.to_string()), - Self::Governor(v) => ("governor", v.to_string()), - Self::HasExpiration(v) => ("hasExpiration", v.to_string()), - Self::HasNativeTokens(v) => ("hasNativeTokens", v.to_string()), - Self::HasStorageDepositReturn(v) => ("hasStorageDepositReturn", v.to_string()), - Self::HasTimelock(v) => ("hasTimelock", v.to_string()), - Self::Issuer(v) => ("issuer", v.to_string()), - Self::MaxNativeTokenCount(v) => ("maxNativeTokenCount", v.to_string()), - Self::MinNativeTokenCount(v) => ("minNativeTokenCount", v.to_string()), - Self::PageSize(v) => ("pageSize", v.to_string()), - Self::Sender(v) => ("sender", v.to_string()), - Self::StateController(v) => ("stateController", v.to_string()), - Self::StorageDepositReturnAddress(v) => ("storageDepositReturnAddress", v.to_string()), - Self::Tag(v) => ("tag", v.to_string()), - Self::TimelockedAfter(v) => ("timelockedAfter", v.to_string()), - Self::TimelockedBefore(v) => ("timelockedBefore", v.to_string()), - Self::UnlockableByAddress(v) => ("unlockableByAddress", v.to_string()), - } - } - - pub(crate) fn kind(&self) -> u8 { - match self { - Self::Address(_) => 0, - Self::AccountAddress(_) => 1, - Self::CreatedAfter(_) => 2, - Self::CreatedBefore(_) => 3, - Self::Cursor(_) => 4, - Self::ExpirationReturnAddress(_) => 5, - Self::ExpiresAfter(_) => 6, - Self::ExpiresBefore(_) => 7, - Self::Governor(_) => 8, - Self::HasExpiration(_) => 9, - Self::HasNativeTokens(_) => 10, - Self::HasStorageDepositReturn(_) => 11, - Self::HasTimelock(_) => 12, - Self::Issuer(_) => 13, - Self::MaxNativeTokenCount(_) => 14, - Self::MinNativeTokenCount(_) => 15, - Self::PageSize(_) => 16, - Self::Sender(_) => 17, - Self::StateController(_) => 18, - Self::StorageDepositReturnAddress(_) => 19, - Self::Tag(_) => 20, - Self::TimelockedAfter(_) => 21, - Self::TimelockedBefore(_) => 22, - Self::UnlockableByAddress(_) => 23, - } - } -} +impl_query_parameters_methods!(BasicOutputQueryParameters); -impl fmt::Display for QueryParameter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let query_tuple = self.to_query_tuple(); - write!(f, "{}={}", query_tuple.0, query_tuple.1) +impl BasicOutputQueryParameters { + /// Sets `.address(address).has_expiration(false).has_storage_deposit_return(false).has_timelock(false)` to only + /// get outputs that can be unlocked by the address without potential further restrictions. + pub fn only_address_unlock_condition(address: impl Into) -> Self { + Self::default() + .address(address.into()) + .has_expiration(false) + .has_storage_deposit_return(false) + .has_timelock(false) } } -macro_rules! verify_query_parameters { - ($query_parameters:ident, $first:path $(, $rest:path)*) => { - if let Some(qp) = $query_parameters.iter().find(|qp| { - !matches!(qp, $first(_) $(| $rest(_))*) - }) { - Err(Error::UnsupportedQueryParameter(qp.clone())) - } else { - Ok(()) - } - }; +/// Query parameters for account output requests. +#[derive(Setters, Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[setters(strip_option)] +#[serde(rename_all = "camelCase")] +pub struct AccountOutputQueryParameters { + /// Returns outputs that were created after a certain slot index. + created_after: Option, + /// Returns outputs that were created before a certain slot index. + created_before: Option, + /// Starts the search from the cursor (createdSlotIndex+outputId.pageSize). + cursor: Option, + /// The maximum amount of items returned in one call. If there are more items, a cursor to the next page is + /// returned too. The parameter is ignored when pageSize is defined via the cursor parameter. + page_size: Option, + /// Returns outputs that are unlockable by the bech32 address. + unlockable_by_address: Option, + /// Filters outputs based on the presence of validated Sender (bech32 encoded). + sender: Option, + /// Filters outputs based on bech32-encoded issuer address. + issuer: Option, + /// Filters outputs based on bech32-encoded governor (governance controller) address. + governor: Option, + /// Filters outputs based on bech32-encoded state controller address. + state_controller: Option, } -pub(crate) fn verify_query_parameters_outputs(query_parameters: Vec) -> Result { - verify_query_parameters!( - query_parameters, - QueryParameter::HasNativeTokens, - QueryParameter::MinNativeTokenCount, - QueryParameter::MaxNativeTokenCount, - QueryParameter::CreatedBefore, - QueryParameter::CreatedAfter, - QueryParameter::PageSize, - QueryParameter::Cursor, - QueryParameter::UnlockableByAddress - )?; +impl_query_parameters_methods!(AccountOutputQueryParameters); - Ok(QueryParameters::new(query_parameters)) +/// Query parameters for nft output requests. +#[derive(Setters, Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[setters(strip_option)] +#[serde(rename_all = "camelCase")] +pub struct NftOutputQueryParameters { + /// Returns outputs that were created after a certain slot index. + created_after: Option, + /// Returns outputs that were created before a certain slot index. + created_before: Option, + /// Starts the search from the cursor (createdSlotIndex+outputId.pageSize). + cursor: Option, + /// The maximum amount of items returned in one call. If there are more items, a cursor to the next page is + /// returned too. The parameter is ignored when pageSize is defined via the cursor parameter. + page_size: Option, + /// Returns outputs that are unlockable by the bech32 address. + unlockable_by_address: Option, + /// Bech32-encoded address that should be searched for. + address: Option, + /// Filters outputs based on the presence of a specific Bech32-encoded return address in the expiration unlock + /// condition. + expiration_return_address: Option, + /// Returns outputs that expire after a certain slot index. + expires_after: Option, + /// Returns outputs that expire before a certain slot index. + expires_before: Option, + /// Filters outputs based on the presence of expiration unlock condition. + has_expiration: Option, + /// Filters outputs based on the presence of storage deposit return unlock condition. + has_storage_deposit_return: Option, + /// Filters outputs based on the presence of timelock unlock condition. + has_timelock: Option, + /// Filters outputs based on the presence of validated Sender (bech32 encoded). + sender: Option, + /// Filters outputs based on bech32-encoded issuer address. + issuer: Option, + /// Filters outputs based on the presence of a specific return address in the storage deposit return unlock + /// condition. + storage_deposit_return_address: Option, + /// Filters outputs based on matching Tag Block. + tag: Option, + /// Returns outputs that are timelocked after a certain slot index. + timelocked_after: Option, + /// Returns outputs that are timelocked before a certain slot index. + timelocked_before: Option, } -pub(crate) fn verify_query_parameters_basic_outputs(query_parameters: Vec) -> Result { - verify_query_parameters!( - query_parameters, - QueryParameter::Address, - QueryParameter::HasNativeTokens, - QueryParameter::MinNativeTokenCount, - QueryParameter::MaxNativeTokenCount, - QueryParameter::HasStorageDepositReturn, - QueryParameter::StorageDepositReturnAddress, - QueryParameter::HasTimelock, - QueryParameter::TimelockedBefore, - QueryParameter::TimelockedAfter, - QueryParameter::HasExpiration, - QueryParameter::ExpiresBefore, - QueryParameter::ExpiresAfter, - QueryParameter::ExpirationReturnAddress, - QueryParameter::Sender, - QueryParameter::Tag, - QueryParameter::CreatedBefore, - QueryParameter::CreatedAfter, - QueryParameter::PageSize, - QueryParameter::Cursor, - QueryParameter::UnlockableByAddress - )?; +impl_query_parameters_methods!(NftOutputQueryParameters); - Ok(QueryParameters::new(query_parameters)) +impl NftOutputQueryParameters { + /// Sets `.address(address).has_expiration(false).has_storage_deposit_return(false).has_timelock(false)` to only + /// get outputs that can be unlocked by the address without potential further restrictions. + pub fn only_address_unlock_condition(address: impl Into) -> Self { + Self::default() + .address(address.into()) + .has_expiration(false) + .has_storage_deposit_return(false) + .has_timelock(false) + } } -pub(crate) fn verify_query_parameters_account_outputs( - query_parameters: Vec, -) -> Result { - verify_query_parameters!( - query_parameters, - QueryParameter::StateController, - QueryParameter::Governor, - QueryParameter::Issuer, - QueryParameter::Sender, - QueryParameter::HasNativeTokens, - QueryParameter::MinNativeTokenCount, - QueryParameter::MaxNativeTokenCount, - QueryParameter::CreatedBefore, - QueryParameter::CreatedAfter, - QueryParameter::PageSize, - QueryParameter::Cursor, - QueryParameter::UnlockableByAddress - )?; - - Ok(QueryParameters::new(query_parameters)) +/// Query parameters for foundry output requests. +#[derive(Setters, Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[setters(strip_option)] +#[serde(rename_all = "camelCase")] +pub struct FoundryOutputQueryParameters { + /// Returns outputs that were created after a certain slot index. + created_after: Option, + /// Returns outputs that were created before a certain slot index. + created_before: Option, + /// Starts the search from the cursor (createdSlotIndex+outputId.pageSize). + cursor: Option, + /// Filters outputs based on the presence of a native token. + has_native_token: Option, + /// Filters outputs based on the presence of a specific native token. + native_token: Option, + /// The maximum amount of items returned in one call. If there are more items, a cursor to the next page is + /// returned too. The parameter is ignored when pageSize is defined via the cursor parameter. + page_size: Option, + /// Filter foundry outputs based on bech32-encoded address of the controlling account. + account_address: Option, } -pub(crate) fn verify_query_parameters_foundry_outputs( - query_parameters: Vec, -) -> Result { - verify_query_parameters!( - query_parameters, - QueryParameter::AccountAddress, - QueryParameter::HasNativeTokens, - QueryParameter::MinNativeTokenCount, - QueryParameter::MaxNativeTokenCount, - QueryParameter::CreatedBefore, - QueryParameter::CreatedAfter, - QueryParameter::PageSize, - QueryParameter::Cursor - )?; +impl_query_parameters_methods!(FoundryOutputQueryParameters); - Ok(QueryParameters::new(query_parameters)) +/// Query parameters for delegation output requests. +#[derive(Setters, Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[setters(strip_option)] +#[serde(rename_all = "camelCase")] +pub struct DelegationOutputQueryParameters { + /// Returns outputs that were created after a certain slot index. + created_after: Option, + /// Returns outputs that were created before a certain slot index. + created_before: Option, + /// Starts the search from the cursor (createdSlotIndex+outputId.pageSize). + cursor: Option, + /// The maximum amount of items returned in one call. If there are more items, a cursor to the next page is + /// returned too. The parameter is ignored when pageSize is defined via the cursor parameter. + page_size: Option, + /// Bech32-encoded address that should be searched for. + address: Option, + /// Filter foundry outputs based on bech32-encoded address of the validator. + validator: Option, } -pub(crate) fn verify_query_parameters_nft_outputs(query_parameters: Vec) -> Result { - verify_query_parameters!( - query_parameters, - QueryParameter::Address, - QueryParameter::HasNativeTokens, - QueryParameter::MinNativeTokenCount, - QueryParameter::MaxNativeTokenCount, - QueryParameter::HasStorageDepositReturn, - QueryParameter::StorageDepositReturnAddress, - QueryParameter::HasTimelock, - QueryParameter::TimelockedBefore, - QueryParameter::TimelockedAfter, - QueryParameter::HasExpiration, - QueryParameter::ExpiresBefore, - QueryParameter::ExpiresAfter, - QueryParameter::ExpirationReturnAddress, - QueryParameter::Issuer, - QueryParameter::Sender, - QueryParameter::Tag, - QueryParameter::CreatedBefore, - QueryParameter::CreatedAfter, - QueryParameter::PageSize, - QueryParameter::Cursor, - QueryParameter::UnlockableByAddress - )?; - - Ok(QueryParameters::new(query_parameters)) -} +impl_query_parameters_methods!(DelegationOutputQueryParameters); #[cfg(test)] mod tests { @@ -318,28 +285,24 @@ mod tests { #[test] fn query_parameter() { - let address1 = QueryParameter::Address( - Bech32Address::try_from_str("atoi1qzt0nhsf38nh6rs4p6zs5knqp6psgha9wsv74uajqgjmwc75ugupx3y7x0r").unwrap(), - ); - let address2 = QueryParameter::Address( - Bech32Address::try_from_str("atoi1qzt0nhsf38nh6rs4p6zs5knqp6psgha9wsv74uajqgjmwc75ugupx3y7x0r").unwrap(), - ); - let address3 = QueryParameter::Address( - Bech32Address::try_from_str("atoi1qprxpfvaz2peggq6f8k9cj8zfsxuw69e4nszjyv5kuf8yt70t2847shpjak").unwrap(), - ); - let state_controller = QueryParameter::StateController( - Bech32Address::try_from_str("atoi1qzt0nhsf38nh6rs4p6zs5knqp6psgha9wsv74uajqgjmwc75ugupx3y7x0r").unwrap(), + let empty_basic_outputs_query_parameters = BasicOutputQueryParameters::new(); + assert_eq!(empty_basic_outputs_query_parameters.to_query_string(), None); + + let mut basic_outputs_query_parameters = BasicOutputQueryParameters::new() + .address( + Bech32Address::try_from_str("atoi1qzt0nhsf38nh6rs4p6zs5knqp6psgha9wsv74uajqgjmwc75ugupx3y7x0r") + .unwrap(), + ) + .cursor("".into()); + assert_eq!( + basic_outputs_query_parameters.to_query_string(), + Some("address=atoi1qzt0nhsf38nh6rs4p6zs5knqp6psgha9wsv74uajqgjmwc75ugupx3y7x0r&cursor=".into()) ); - let mut query_parameters = QueryParameters::new([address1, address2, state_controller]); - // since address1 and address2 are of the same enum variant, we should only have one - assert!(query_parameters.0.len() == 2); - // since address2 and address3 are of the same enum variant, we should only have one - query_parameters.replace(address3); - assert!(query_parameters.0.len() == 2); - // Contains address query parameter - assert!(query_parameters.any(|param| matches!(param, QueryParameter::Address(_)))); - // Contains no cursor query parameter - assert!(!query_parameters.any(|param| matches!(param, QueryParameter::Cursor(_)))); + basic_outputs_query_parameters.replace_cursor("newCursor".into()); + assert_eq!( + basic_outputs_query_parameters.to_query_string(), + Some("address=atoi1qzt0nhsf38nh6rs4p6zs5knqp6psgha9wsv74uajqgjmwc75ugupx3y7x0r&cursor=newCursor".into()) + ); } } diff --git a/sdk/src/client/node_api/indexer/routes.rs b/sdk/src/client/node_api/indexer/routes.rs index 7da81319aa..d8355d87ac 100644 --- a/sdk/src/client/node_api/indexer/routes.rs +++ b/sdk/src/client/node_api/indexer/routes.rs @@ -5,13 +5,9 @@ use crate::{ client::{ - node_api::indexer::{ - query_parameters::{ - verify_query_parameters_account_outputs, verify_query_parameters_basic_outputs, - verify_query_parameters_foundry_outputs, verify_query_parameters_nft_outputs, - verify_query_parameters_outputs, QueryParameter, - }, - QueryParameters, + node_api::indexer::query_parameters::{ + AccountOutputQueryParameters, BasicOutputQueryParameters, FoundryOutputQueryParameters, + NftOutputQueryParameters, OutputQueryParameters, }, ClientInner, Error, Result, }, @@ -29,14 +25,9 @@ impl ClientInner { /// Query parameters: "hasNativeTokens", "minNativeTokenCount", "maxNativeTokenCount", "unlockableByAddress", /// "createdBefore", "createdAfter", "cursor", "pageSize". /// Returns Err(Node(NotFound) if no results are found. - /// api/indexer/v1/outputs - pub async fn output_ids( - &self, - query_parameters: impl Into> + Send, - ) -> Result { - let route = "api/indexer/v1/outputs"; - - let query_parameters = verify_query_parameters_outputs(query_parameters.into())?; + /// api/indexer/v2/outputs + pub async fn output_ids(&self, query_parameters: OutputQueryParameters) -> Result { + let route = "api/indexer/v2/outputs"; self.get_output_ids(route, query_parameters, true, false).await } @@ -48,14 +39,9 @@ impl ClientInner { /// "timelockedAfter", "sender", "tag", "createdBefore" and "createdAfter". /// Returns Err(Node(NotFound) if no results are found. /// api/indexer/v2/outputs/basic - pub async fn basic_output_ids( - &self, - query_parameters: impl Into> + Send, - ) -> Result { + pub async fn basic_output_ids(&self, query_parameters: BasicOutputQueryParameters) -> Result { let route = "api/indexer/v2/outputs/basic"; - let query_parameters = verify_query_parameters_basic_outputs(query_parameters.into())?; - self.get_output_ids(route, query_parameters, true, false).await } @@ -66,12 +52,10 @@ impl ClientInner { /// api/indexer/v2/outputs/account pub async fn account_output_ids( &self, - query_parameters: impl Into> + Send, + query_parameters: AccountOutputQueryParameters, ) -> Result { let route = "api/indexer/v2/outputs/account"; - let query_parameters = verify_query_parameters_account_outputs(query_parameters.into())?; - self.get_output_ids(route, query_parameters, true, false).await } @@ -81,7 +65,7 @@ impl ClientInner { let route = format!("api/indexer/v2/outputs/account/{account_id}"); Ok(*(self - .get_output_ids(&route, QueryParameters::empty(), true, false) + .get_output_ids(&route, AccountOutputQueryParameters::new(), true, false) .await? .first() .ok_or_else(|| Error::NoOutput(format!("{account_id:?}")))?)) @@ -94,12 +78,10 @@ impl ClientInner { /// api/indexer/v2/outputs/foundry pub async fn foundry_output_ids( &self, - query_parameters: impl Into> + Send, + query_parameters: FoundryOutputQueryParameters, ) -> Result { let route = "api/indexer/v2/outputs/foundry"; - let query_parameters = verify_query_parameters_foundry_outputs(query_parameters.into())?; - self.get_output_ids(route, query_parameters, true, false).await } @@ -109,7 +91,7 @@ impl ClientInner { let route = format!("api/indexer/v2/outputs/foundry/{foundry_id}"); Ok(*(self - .get_output_ids(&route, QueryParameters::empty(), true, false) + .get_output_ids(&route, FoundryOutputQueryParameters::new(), true, false) .await? .first() .ok_or_else(|| Error::NoOutput(format!("{foundry_id:?}")))?)) @@ -121,14 +103,9 @@ impl ClientInner { /// "timelockedAfter", "issuer", "sender", "tag", "createdBefore", "createdAfter" /// Returns Err(Node(NotFound) if no results are found. /// api/indexer/v2/outputs/nft - pub async fn nft_output_ids( - &self, - query_parameters: impl Into> + Send, - ) -> Result { + pub async fn nft_output_ids(&self, query_parameters: NftOutputQueryParameters) -> Result { let route = "api/indexer/v2/outputs/nft"; - let query_parameters = verify_query_parameters_nft_outputs(query_parameters.into())?; - self.get_output_ids(route, query_parameters, true, false).await } @@ -138,7 +115,7 @@ impl ClientInner { let route = format!("api/indexer/v2/outputs/nft/{nft_id}"); Ok(*(self - .get_output_ids(&route, QueryParameters::empty(), true, false) + .get_output_ids(&route, NftOutputQueryParameters::new(), true, false) .await? .first() .ok_or_else(|| Error::NoOutput(format!("{nft_id:?}")))?)) diff --git a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/account_foundry.rs b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/account_foundry.rs index 3cf7a73bec..795ee91c95 100644 --- a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/account_foundry.rs +++ b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/account_foundry.rs @@ -4,7 +4,10 @@ use std::collections::HashSet; use crate::{ - client::{node_api::indexer::query_parameters::QueryParameter, secret::SecretManage}, + client::{ + node_api::indexer::query_parameters::{AccountOutputQueryParameters, FoundryOutputQueryParameters}, + secret::SecretManage, + }, types::{ api::plugins::indexer::OutputIdsResponse, block::{ @@ -34,7 +37,7 @@ where let mut output_ids = self .client() - .account_output_ids([QueryParameter::UnlockableByAddress(bech32_address)]) + .account_output_ids(AccountOutputQueryParameters::new().unlockable_by_address(bech32_address)) .await? .items; @@ -69,7 +72,7 @@ where let client = self.client().clone(); tasks.push(Box::pin(task::spawn(async move { client - .foundry_output_ids([QueryParameter::AccountAddress(account_bech32_address)]) + .foundry_output_ids(FoundryOutputQueryParameters::new().account_address(account_bech32_address)) .await .map_err(From::from) }))); diff --git a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/basic.rs b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/basic.rs index d0e431c026..54178ae70d 100644 --- a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/basic.rs +++ b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/basic.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - client::{node_api::indexer::query_parameters::QueryParameter, secret::SecretManage}, + client::{node_api::indexer::query_parameters::BasicOutputQueryParameters, secret::SecretManage}, types::block::{address::Bech32Address, output::OutputId, ConvertTo}, wallet::Account, }; @@ -17,21 +17,18 @@ where bech32_address: impl ConvertTo, ) -> crate::client::Result> { let bech32_address = bech32_address.convert()?; - // Only request basic outputs with `AddressUnlockCondition` only + Ok(self .client() - .basic_output_ids([ - QueryParameter::Address(bech32_address), - QueryParameter::HasExpiration(false), - QueryParameter::HasTimelock(false), - QueryParameter::HasStorageDepositReturn(false), - ]) + .basic_output_ids(BasicOutputQueryParameters::only_address_unlock_condition( + bech32_address, + )) .await? .items) } - /// Returns output ids of basic outputs that have the address in the `AddressUnlockCondition`, - /// `ExpirationUnlockCondition` or `StorageDepositReturnUnlockCondition` + /// Returns output ids of basic outputs that have the address in the `AddressUnlockCondition` or + /// `ExpirationUnlockCondition` pub(crate) async fn get_basic_output_ids_with_any_unlock_condition( &self, bech32_address: impl ConvertTo, @@ -40,7 +37,7 @@ where Ok(self .client() - .basic_output_ids([QueryParameter::UnlockableByAddress(bech32_address)]) + .basic_output_ids(BasicOutputQueryParameters::new().unlockable_by_address(bech32_address.clone())) .await? .items) } diff --git a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/mod.rs b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/mod.rs index 3efe237de8..f9560262d9 100644 --- a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/mod.rs +++ b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/mod.rs @@ -12,7 +12,10 @@ use futures::FutureExt; use instant::Instant; use crate::{ - client::{node_api::indexer::QueryParameter, secret::SecretManage}, + client::{ + node_api::indexer::query_parameters::{FoundryOutputQueryParameters, OutputQueryParameters}, + secret::SecretManage, + }, types::block::{ address::{Address, Bech32Address}, output::OutputId, @@ -51,7 +54,7 @@ where { return Ok(self .client() - .output_ids([QueryParameter::UnlockableByAddress(bech32_address.clone())]) + .output_ids(OutputQueryParameters::new().unlockable_by_address(bech32_address.clone())) .await? .items); } @@ -160,7 +163,7 @@ where { results.push(Ok(self .client() - .foundry_output_ids([QueryParameter::AccountAddress(bech32_address)]) + .foundry_output_ids(FoundryOutputQueryParameters::new().account_address(bech32_address)) .await? .items)) } @@ -173,7 +176,7 @@ where let client = self.client().clone(); tokio::spawn(async move { Ok(client - .foundry_output_ids([QueryParameter::AccountAddress(bech32_address)]) + .foundry_output_ids(FoundryOutputQueryParameters::new().account_address(bech32_address)) .await? .items) }) diff --git a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/nft.rs b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/nft.rs index 60339732cf..af53f1f4b8 100644 --- a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/nft.rs +++ b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/nft.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - client::{node_api::indexer::query_parameters::QueryParameter, secret::SecretManage}, + client::{node_api::indexer::query_parameters::NftOutputQueryParameters, secret::SecretManage}, types::block::{address::Bech32Address, output::OutputId, ConvertTo}, wallet::Account, }; @@ -11,7 +11,8 @@ impl Account where crate::wallet::Error: From, { - /// Returns output ids of nft outputs that have the address in any unlock condition + /// Returns output ids of NFT outputs that have the address in the `AddressUnlockCondition` or + /// `ExpirationUnlockCondition` pub(crate) async fn get_nft_output_ids_with_any_unlock_condition( &self, bech32_address: impl ConvertTo, @@ -20,7 +21,7 @@ where Ok(self .client() - .nft_output_ids([QueryParameter::UnlockableByAddress(bech32_address)]) + .nft_output_ids(NftOutputQueryParameters::new().unlockable_by_address(bech32_address)) .await? .items) } diff --git a/sdk/tests/client/node_api/core.rs b/sdk/tests/client/node_api/core.rs index c0b3f4f80e..40acaa351d 100644 --- a/sdk/tests/client/node_api/core.rs +++ b/sdk/tests/client/node_api/core.rs @@ -4,7 +4,10 @@ // These are E2E test samples, so they are ignored by default. use iota_sdk::{ - client::{api::GetAddressesOptions, node_api::indexer::query_parameters::QueryParameter, Client, NodeInfoWrapper}, + client::{ + api::GetAddressesOptions, node_api::indexer::query_parameters::BasicOutputQueryParameters, Client, + NodeInfoWrapper, + }, types::{ api::core::TransactionState, block::{ @@ -126,7 +129,7 @@ async fn test_get_address_outputs() { .unwrap(); let output_ids_response = client - .basic_output_ids([QueryParameter::Address(address)]) + .basic_output_ids(BasicOutputQueryParameters::new().address(address)) .await .unwrap(); diff --git a/sdk/tests/client/node_api/mod.rs b/sdk/tests/client/node_api/mod.rs index ee1592052e..8b50f9d377 100644 --- a/sdk/tests/client/node_api/mod.rs +++ b/sdk/tests/client/node_api/mod.rs @@ -9,8 +9,9 @@ mod mqtt; use crypto::keys::bip44::Bip44; use iota_sdk::{ client::{ - api::GetAddressesOptions, constants::IOTA_COIN_TYPE, node_api::indexer::query_parameters::QueryParameter, - request_funds_from_faucet, secret::SecretManager, Client, + api::GetAddressesOptions, constants::IOTA_COIN_TYPE, + node_api::indexer::query_parameters::BasicOutputQueryParameters, request_funds_from_faucet, + secret::SecretManager, Client, }, types::block::{ payload::{signed_transaction::TransactionId, tagged_data::TaggedDataPayload, Payload}, @@ -71,12 +72,9 @@ pub async fn setup_transaction_block(client: &Client) -> (BlockId, TransactionId } tokio::time::sleep(std::time::Duration::from_secs(1)).await; let output_ids_response = client - .basic_output_ids([ - QueryParameter::Address(addresses[0].clone()), - QueryParameter::HasExpiration(false), - QueryParameter::HasTimelock(false), - QueryParameter::HasStorageDepositReturn(false), - ]) + .basic_output_ids(BasicOutputQueryParameters::only_address_unlock_condition( + addresses[0].clone(), + )) .await .unwrap();