iota_client/node/
address.rs1use crate::{Api, Client, Error, Result};
5
6use bee_message::prelude::{TransactionId, UtxoInput};
7
8use bee_rest_api::types::{
9 body::SuccessBody,
10 responses::{BalanceAddressResponse, OutputsAddressResponse},
11};
12
13use std::convert::TryInto;
14
15const OUTPUT_ID_LENGTH: usize = 68;
16const TRANSACTION_ID_LENGTH: usize = 64;
17
18#[derive(Clone, Debug, Serialize, Deserialize)]
20pub enum OutputType {
21 SignatureLockedSingle,
23 SignatureLockedDustAllowance,
25}
26
27impl From<OutputType> for u16 {
28 fn from(value: OutputType) -> Self {
29 match value {
30 OutputType::SignatureLockedSingle => 0,
31 OutputType::SignatureLockedDustAllowance => 1,
32 }
33 }
34}
35
36#[derive(Default, Clone, Serialize, Deserialize)]
38pub struct OutputsOptions {
39 #[serde(rename = "includeSpent")]
41 pub include_spent: bool,
42 #[serde(rename = "outputType")]
43 pub output_type: Option<OutputType>,
45}
46
47impl OutputsOptions {
48 fn into_query(self) -> Option<String> {
49 let mut params = Vec::new();
50 if self.include_spent {
51 params.push("include-spent=true".to_string());
52 }
53 if let Some(output_type) = self.output_type {
54 params.push(format!("type={}", u16::from(output_type)))
55 }
56 if params.is_empty() {
57 None
58 } else {
59 Some(params.join("&"))
60 }
61 }
62}
63
64pub struct GetAddressBuilder<'a> {
66 client: &'a Client,
67}
68
69impl<'a> GetAddressBuilder<'a> {
70 pub fn new(client: &'a Client) -> Self {
72 Self { client }
73 }
74
75 pub async fn balance(self, address: &str) -> Result<BalanceAddressResponse> {
79 let path = &format!("api/v1/addresses/{address}");
80
81 let resp: SuccessBody<BalanceAddressResponse> = self
82 .client
83 .node_manager
84 .get_request(path, None, self.client.get_timeout(Api::GetBalance))
85 .await?;
86
87 Ok(resp.data)
88 }
89
90 pub async fn outputs(self, address: &str, options: OutputsOptions) -> Result<Box<[UtxoInput]>> {
94 let path = format!("api/v1/addresses/{address}/outputs");
95
96 let resp: SuccessBody<OutputsAddressResponse> = self
97 .client
98 .node_manager
99 .get_request(
100 &path,
101 options.into_query().as_deref(),
102 self.client.get_timeout(Api::GetOutput),
103 )
104 .await?;
105
106 resp.data
107 .output_ids
108 .iter()
109 .map(|s| {
110 if s.len() == OUTPUT_ID_LENGTH {
111 let mut transaction_id = [0u8; 32];
112 hex::decode_to_slice(&s[..TRANSACTION_ID_LENGTH], &mut transaction_id)?;
113 let index = u16::from_le_bytes(
114 hex::decode(&s[TRANSACTION_ID_LENGTH..]).map_err(|_| Error::InvalidParameter("index"))?[..]
115 .try_into()
116 .map_err(|_| Error::InvalidParameter("index"))?,
117 );
118 Ok(UtxoInput::new(TransactionId::new(transaction_id), index)?)
119 } else {
120 Err(Error::OutputError("Invalid output length from API response"))
121 }
122 })
123 .collect::<Result<Box<[UtxoInput]>>>()
124 }
125
126 pub async fn outputs_response(self, address: &str, options: OutputsOptions) -> Result<OutputsAddressResponse> {
130 let path = format!("api/v1/addresses/{address}/outputs");
131
132 let resp: SuccessBody<OutputsAddressResponse> = self
133 .client
134 .node_manager
135 .get_request(
136 &path,
137 options.into_query().as_deref(),
138 self.client.get_timeout(Api::GetOutput),
139 )
140 .await?;
141
142 Ok(resp.data)
143 }
144}