iota_stardust_types/block/address/
mod.rs

1// Copyright (c) 2026 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4mod alias;
5mod bech32;
6mod ed25519;
7mod nft;
8
9use derive_more::{Display, From};
10
11pub use self::{
12    alias::AliasAddress,
13    bech32::{Bech32Address, Hrp, ToBech32Ext},
14    ed25519::Ed25519Address,
15    nft::NftAddress,
16};
17use crate::block::Error;
18
19/// A generic address supporting different address kinds.
20#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, From, Display, packable::Packable)]
21#[packable(tag_type = u8, with_error = Error::InvalidAddressKind)]
22#[packable(unpack_error = Error)]
23pub enum Address {
24    /// An Ed25519 address.
25    #[packable(tag = Ed25519Address::KIND)]
26    Ed25519(Ed25519Address),
27    /// An alias address.
28    #[packable(tag = AliasAddress::KIND)]
29    Alias(AliasAddress),
30    /// An NFT address.
31    #[packable(tag = NftAddress::KIND)]
32    Nft(NftAddress),
33}
34
35impl core::fmt::Debug for Address {
36    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
37        match self {
38            Self::Ed25519(address) => core::fmt::Display::fmt(address, f),
39            Self::Alias(address) => core::fmt::Display::fmt(address, f),
40            Self::Nft(address) => core::fmt::Display::fmt(address, f),
41        }
42    }
43}
44
45impl Address {
46    /// Returns the address kind of an [`Address`].
47    pub fn kind(&self) -> u8 {
48        match self {
49            Self::Ed25519(_) => Ed25519Address::KIND,
50            Self::Alias(_) => AliasAddress::KIND,
51            Self::Nft(_) => NftAddress::KIND,
52        }
53    }
54
55    /// Returns the address kind of an [`Address`] as a string.
56    pub fn kind_str(&self) -> &str {
57        match self {
58            Self::Ed25519(_) => "Ed25519",
59            Self::Alias(_) => "Alias",
60            Self::Nft(_) => "Nft",
61        }
62    }
63
64    /// Checks whether the address is an [`Ed25519Address`].
65    pub fn is_ed25519(&self) -> bool {
66        matches!(self, Self::Ed25519(_))
67    }
68
69    /// Gets the address as an actual [`Ed25519Address`].
70    /// PANIC: do not call on a non-ed25519 address.
71    pub fn as_ed25519(&self) -> &Ed25519Address {
72        if let Self::Ed25519(address) = self {
73            address
74        } else {
75            panic!("as_ed25519 called on a non-ed25519 address");
76        }
77    }
78
79    /// Checks whether the address is an [`AliasAddress`].
80    pub fn is_alias(&self) -> bool {
81        matches!(self, Self::Alias(_))
82    }
83
84    /// Gets the address as an actual [`AliasAddress`].
85    /// PANIC: do not call on a non-alias address.
86    pub fn as_alias(&self) -> &AliasAddress {
87        if let Self::Alias(address) = self {
88            address
89        } else {
90            panic!("as_alias called on a non-alias address");
91        }
92    }
93
94    /// Checks whether the address is an [`NftAddress`].
95    pub fn is_nft(&self) -> bool {
96        matches!(self, Self::Nft(_))
97    }
98
99    /// Gets the address as an actual [`NftAddress`].
100    /// PANIC: do not call on a non-nft address.
101    pub fn as_nft(&self) -> &NftAddress {
102        if let Self::Nft(address) = self {
103            address
104        } else {
105            panic!("as_nft called on a non-nft address");
106        }
107    }
108
109    /// Tries to create an [`Address`] from a bech32 encoded string.
110    pub fn try_from_bech32(address: impl AsRef<str>) -> Result<Self, Error> {
111        Bech32Address::try_from_str(address).map(|res| res.into_inner())
112    }
113
114    /// Checks if a string is a valid bech32 encoded address.
115    #[must_use]
116    pub fn is_valid_bech32(address: &str) -> bool {
117        Self::try_from_bech32(address).is_ok()
118    }
119}
120
121impl From<&Self> for Address {
122    fn from(value: &Self) -> Self {
123        *value
124    }
125}