Skip to main content

miden_protocol/asset/
asset_composition.rs

1use alloc::string::ToString;
2
3use crate::errors::AssetError;
4use crate::utils::serde::{
5    ByteReader,
6    ByteWriter,
7    Deserializable,
8    DeserializationError,
9    Serializable,
10};
11
12/// Indicates how an asset is composed (i.e. how its merge/split semantics are defined).
13///
14/// The composition is encoded in the metadata byte of an
15/// [`AssetVaultKey`](super::AssetVaultKey) and determines how the asset is handled by the
16/// asset vault, account delta and faucet logic.
17#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
18#[repr(u8)]
19pub enum AssetComposition {
20    /// Instances of the asset do not compose. In other words, they are non-fungible.
21    #[default]
22    None = Self::NONE,
23
24    /// Instances of the asset compose according to the rules of the standard fungible asset.
25    Fungible = Self::FUNGIBLE,
26
27    /// Instances of the asset have a custom, faucet-defined composition logic.
28    ///
29    /// Exists for future use; currently effectively disabled.
30    Custom = Self::CUSTOM,
31}
32
33impl AssetComposition {
34    const NONE: u8 = 0;
35    const FUNGIBLE: u8 = 1;
36    const CUSTOM: u8 = 2;
37
38    /// The serialized size of an [`AssetComposition`] in bytes.
39    pub const SERIALIZED_SIZE: usize = core::mem::size_of::<AssetComposition>();
40
41    /// Encodes the composition as a `u8`.
42    pub const fn as_u8(&self) -> u8 {
43        *self as u8
44    }
45
46    /// Returns true if the composition is [`AssetComposition::None`].
47    pub const fn is_none(&self) -> bool {
48        matches!(self, Self::None)
49    }
50
51    /// Returns true if the composition is [`AssetComposition::Fungible`].
52    pub const fn is_fungible(&self) -> bool {
53        matches!(self, Self::Fungible)
54    }
55
56    /// Returns true if the composition is [`AssetComposition::Custom`].
57    pub const fn is_custom(&self) -> bool {
58        matches!(self, Self::Custom)
59    }
60}
61
62impl TryFrom<u8> for AssetComposition {
63    type Error = AssetError;
64
65    /// Decodes a composition from a `u8`.
66    ///
67    /// # Errors
68    ///
69    /// Returns an error if the value is not a valid composition encoding.
70    fn try_from(value: u8) -> Result<Self, Self::Error> {
71        match value {
72            Self::NONE => Ok(Self::None),
73            Self::FUNGIBLE => Ok(Self::Fungible),
74            Self::CUSTOM => Ok(Self::Custom),
75            _ => Err(AssetError::UnknownAssetComposition(value)),
76        }
77    }
78}
79
80impl core::fmt::Display for AssetComposition {
81    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
82        let string = match self {
83            AssetComposition::None => "none",
84            AssetComposition::Fungible => "fungible",
85            AssetComposition::Custom => "custom",
86        };
87
88        f.write_str(string)
89    }
90}
91
92impl Serializable for AssetComposition {
93    fn write_into<W: ByteWriter>(&self, target: &mut W) {
94        target.write_u8(self.as_u8());
95    }
96
97    fn get_size_hint(&self) -> usize {
98        AssetComposition::SERIALIZED_SIZE
99    }
100}
101
102impl Deserializable for AssetComposition {
103    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
104        Self::try_from(source.read_u8()?)
105            .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
106    }
107}