use std::ops::Range;
use serde::Deserialize;
use crate::{
client::{
api::types::{Bech32Addresses, RawAddresses},
constants::{SHIMMER_COIN_TYPE, SHIMMER_TESTNET_BECH32_HRP},
secret::{GenerateAddressOptions, SecretManage, SecretManager},
Client, Result,
},
types::block::address::Address,
};
#[must_use]
pub struct GetAddressesBuilder<'a> {
client: Option<&'a Client>,
secret_manager: &'a SecretManager,
coin_type: u32,
account_index: u32,
range: Range<u32>,
internal: bool,
bech32_hrp: Option<String>,
options: Option<GenerateAddressOptions>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetAddressesBuilderOptions {
pub coin_type: Option<u32>,
pub account_index: Option<u32>,
pub range: Option<Range<u32>>,
pub internal: Option<bool>,
pub bech32_hrp: Option<String>,
pub options: Option<GenerateAddressOptions>,
}
impl<'a> GetAddressesBuilder<'a> {
pub fn new(manager: &'a SecretManager) -> Self {
Self {
client: None,
secret_manager: manager,
coin_type: SHIMMER_COIN_TYPE,
account_index: 0,
range: 0..super::ADDRESS_GAP_RANGE,
internal: false,
bech32_hrp: None,
options: None,
}
}
pub fn with_client(mut self, client: &'a Client) -> Self {
self.client.replace(client);
self
}
pub fn with_coin_type(mut self, coin_type: u32) -> Self {
self.coin_type = coin_type;
self
}
pub fn with_account_index(mut self, account_index: u32) -> Self {
self.account_index = account_index;
self
}
pub fn with_range(mut self, range: Range<u32>) -> Self {
self.range = range;
self
}
pub fn with_internal_addresses(mut self, internal: bool) -> Self {
self.internal = internal;
self
}
pub fn with_bech32_hrp<T: Into<String>>(mut self, bech32_hrp: T) -> Self {
self.bech32_hrp.replace(bech32_hrp.into());
self
}
pub fn with_options(mut self, options: GenerateAddressOptions) -> Self {
self.options = Some(options);
self
}
pub fn set_options(mut self, options: GetAddressesBuilderOptions) -> Result<Self> {
if let Some(coin_type) = options.coin_type {
self = self.with_coin_type(coin_type);
};
if let Some(account_index) = options.account_index {
self = self.with_account_index(account_index);
}
if let Some(range) = options.range {
self = self.with_range(range);
};
if let Some(internal) = options.internal {
self = self.with_internal_addresses(internal);
};
if let Some(bech32_hrp) = options.bech32_hrp {
self = self.with_bech32_hrp(bech32_hrp);
};
if let Some(options) = options.options {
self = self.with_options(options);
};
Ok(self)
}
pub async fn finish(self) -> Result<Vec<String>> {
let bech32_hrp = match self.bech32_hrp.clone() {
Some(bech32_hrp) => bech32_hrp,
None => match self.client {
Some(client) => client.get_bech32_hrp().await?,
None => SHIMMER_TESTNET_BECH32_HRP.to_string(),
},
};
let addresses = self
.secret_manager
.generate_addresses(
self.coin_type,
self.account_index,
self.range,
self.internal,
self.options.clone(),
)
.await?
.into_iter()
.map(|a| a.to_bech32(&bech32_hrp))
.collect();
Ok(addresses)
}
pub async fn get_raw(self) -> Result<Vec<Address>> {
self.secret_manager
.generate_addresses(
self.coin_type,
self.account_index,
self.range,
false,
self.options.clone(),
)
.await
}
pub async fn get_all(self) -> Result<Bech32Addresses> {
let bech32_hrp = match self.bech32_hrp.clone() {
Some(bech32_hrp) => bech32_hrp,
None => match self.client {
Some(client) => client.get_bech32_hrp().await?,
None => SHIMMER_TESTNET_BECH32_HRP.to_string(),
},
};
let addresses = self.get_all_raw().await?;
Ok(Bech32Addresses {
public: addresses.public.into_iter().map(|a| a.to_bech32(&bech32_hrp)).collect(),
internal: addresses
.internal
.into_iter()
.map(|a| a.to_bech32(&bech32_hrp))
.collect(),
})
}
pub async fn get_all_raw(self) -> Result<RawAddresses> {
let public_addresses = self
.secret_manager
.generate_addresses(
self.coin_type,
self.account_index,
self.range.clone(),
false,
self.options.clone(),
)
.await?;
let internal_addresses = self
.secret_manager
.generate_addresses(
self.coin_type,
self.account_index,
self.range,
true,
self.options.clone(),
)
.await?;
Ok(RawAddresses {
public: public_addresses,
internal: internal_addresses,
})
}
}
pub async fn search_address(
secret_manager: &SecretManager,
bech32_hrp: &str,
coin_type: u32,
account_index: u32,
range: Range<u32>,
address: &Address,
) -> Result<(u32, bool)> {
let addresses = GetAddressesBuilder::new(secret_manager)
.with_coin_type(coin_type)
.with_account_index(account_index)
.with_range(range.clone())
.get_all_raw()
.await?;
for index in 0..addresses.public.len() {
if addresses.public[index] == *address {
return Ok((range.start + index as u32, false));
}
if addresses.internal[index] == *address {
return Ok((range.start + index as u32, true));
}
}
Err(crate::client::Error::InputAddressNotFound {
address: address.to_bech32(bech32_hrp),
range: format!("{range:?}"),
})
}