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>,
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 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,
bech32_hrp: None,
options: None,
}
}
pub fn with_client(mut self, client: impl Into<Option<&'a Client>>) -> Self {
self.client = client.into();
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_bech32_hrp<T: Into<String>>(mut self, bech32_hrp: impl Into<Option<T>>) -> Self {
self.bech32_hrp = bech32_hrp.into().map(|b| b.into());
self
}
pub fn with_options(mut self, options: impl Into<Option<GenerateAddressOptions>>) -> Self {
self.options = options.into();
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(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.options)
.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,
self.options.map(|mut o| {
o.internal = false;
o
}),
)
.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(),
self.options.map(|mut o| {
o.internal = false;
o
}),
)
.await?;
let internal_addresses = self
.secret_manager
.generate_addresses(
self.coin_type,
self.account_index,
self.range,
self.options
.map(|mut o| {
o.internal = true;
o
})
.or_else(|| Some(GenerateAddressOptions::internal())),
)
.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:?}"),
})
}