use std::ops::Range;
use serde::{Deserialize, Serialize};
use super::ADDRESS_GAP_RANGE;
use crate::{
client::{
constants::{SHIMMER_COIN_TYPE, SHIMMER_TESTNET_BECH32_HRP},
secret::{GenerateAddressOptions, SecretManage, SecretManager},
Client, Result,
},
types::block::{
address::{Address, Bech32Address, Hrp, ToBech32Ext},
ConvertTo,
},
};
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(default)]
pub struct GetAddressesOptions {
pub coin_type: u32,
pub account_index: u32,
pub range: Range<u32>,
pub bech32_hrp: Hrp,
pub options: Option<GenerateAddressOptions>,
}
impl GetAddressesOptions {
pub async fn from_client(client: &Client) -> Result<Self> {
Ok(Self::default().with_bech32_hrp(client.get_bech32_hrp().await?))
}
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(mut self, bech32_hrp: Hrp) -> Self {
self.bech32_hrp = bech32_hrp;
self
}
pub fn try_with_bech32_hrp(mut self, bech32_hrp: impl ConvertTo<Hrp>) -> Result<Self> {
self.bech32_hrp = bech32_hrp.convert()?;
Ok(self)
}
pub fn internal(mut self) -> Self {
match &mut self.options {
Some(o) => {
o.internal = true;
self
}
None => self.with_options(GenerateAddressOptions::internal()),
}
}
pub fn with_options(mut self, options: impl Into<Option<GenerateAddressOptions>>) -> Self {
self.options = options.into();
self
}
}
impl Default for GetAddressesOptions {
fn default() -> Self {
Self {
coin_type: SHIMMER_COIN_TYPE,
account_index: 0,
range: 0..ADDRESS_GAP_RANGE,
bech32_hrp: SHIMMER_TESTNET_BECH32_HRP,
options: Default::default(),
}
}
}
impl SecretManager {
pub async fn generate_ed25519_addresses(
&self,
GetAddressesOptions {
coin_type,
account_index,
range,
bech32_hrp,
options,
}: GetAddressesOptions,
) -> Result<Vec<Bech32Address>> {
Ok(
SecretManage::generate_ed25519_addresses(self, coin_type, account_index, range, options)
.await?
.into_iter()
.map(|a| a.to_bech32(bech32_hrp))
.collect(),
)
}
pub async fn generate_evm_addresses(
&self,
GetAddressesOptions {
coin_type,
account_index,
range,
options,
..
}: GetAddressesOptions,
) -> Result<Vec<String>> {
Ok(
SecretManage::generate_evm_addresses(self, coin_type, account_index, range, options)
.await?
.into_iter()
.map(|a| prefix_hex::encode(a.as_ref()))
.collect(),
)
}
}
pub async fn search_address(
secret_manager: &SecretManager,
bech32_hrp: Hrp,
coin_type: u32,
account_index: u32,
range: Range<u32>,
address: &Address,
) -> Result<(u32, bool)> {
let opts = GetAddressesOptions::default()
.with_coin_type(coin_type)
.with_account_index(account_index)
.with_range(range.clone());
let public = secret_manager.generate_ed25519_addresses(opts.clone()).await?;
let internal = secret_manager.generate_ed25519_addresses(opts.internal()).await?;
for index in 0..public.len() {
if public[index].inner == *address {
return Ok((range.start + index as u32, false));
}
if internal[index].inner == *address {
return Ok((range.start + index as u32, true));
}
}
Err(crate::client::Error::InputAddressNotFound {
address: address.to_bech32(bech32_hrp).to_string(),
range: format!("{range:?}"),
})
}