namada_ibc/
nft.rs

1use borsh::{BorshDeserialize, BorshSerialize};
2use ibc::apps::nft_transfer::context::{NftClassContext, NftContext};
3use ibc::apps::nft_transfer::types::{
4    ClassData, ClassId, ClassUri, PrefixedClassId, TokenData, TokenId, TokenUri,
5};
6use ibc::core::host::types::error::DecodingError;
7
8/// NFT class
9#[derive(Clone, Debug)]
10pub struct NftClass {
11    /// NFT class ID
12    pub class_id: PrefixedClassId,
13    /// NFT class URI
14    pub class_uri: Option<ClassUri>,
15    /// NFT class data
16    pub class_data: Option<ClassData>,
17}
18
19impl BorshSerialize for NftClass {
20    fn serialize<W: std::io::Write>(
21        &self,
22        writer: &mut W,
23    ) -> std::io::Result<()> {
24        BorshSerialize::serialize(&self.class_id.to_string(), writer)?;
25        match &self.class_uri {
26            Some(uri) => {
27                BorshSerialize::serialize(&true, writer)?;
28                BorshSerialize::serialize(&uri.to_string(), writer)?;
29            }
30            None => BorshSerialize::serialize(&false, writer)?,
31        }
32        match &self.class_data {
33            Some(data) => {
34                BorshSerialize::serialize(&true, writer)?;
35                BorshSerialize::serialize(&data.to_string(), writer)
36            }
37            None => BorshSerialize::serialize(&false, writer),
38        }
39    }
40}
41
42impl BorshDeserialize for NftClass {
43    fn deserialize_reader<R: std::io::Read>(
44        reader: &mut R,
45    ) -> std::io::Result<Self> {
46        use std::io::{Error, ErrorKind};
47        let class_id: String = BorshDeserialize::deserialize_reader(reader)?;
48        let class_id = class_id.parse().map_err(|e: DecodingError| {
49            Error::new(ErrorKind::InvalidData, e.to_string())
50        })?;
51
52        let is_uri: bool = BorshDeserialize::deserialize_reader(reader)?;
53        let class_uri = if is_uri {
54            let uri_str: String = BorshDeserialize::deserialize_reader(reader)?;
55            Some(uri_str.parse().map_err(|e: DecodingError| {
56                Error::new(ErrorKind::InvalidData, e.to_string())
57            })?)
58        } else {
59            None
60        };
61
62        let is_data: bool = BorshDeserialize::deserialize_reader(reader)?;
63        let class_data = if is_data {
64            let data_str: String =
65                BorshDeserialize::deserialize_reader(reader)?;
66            Some(data_str.parse().map_err(|e: DecodingError| {
67                Error::new(ErrorKind::InvalidData, e.to_string())
68            })?)
69        } else {
70            None
71        };
72
73        Ok(Self {
74            class_id,
75            class_uri,
76            class_data,
77        })
78    }
79}
80
81impl NftClassContext for NftClass {
82    fn get_id(&self) -> &ClassId {
83        &self.class_id.base_class_id
84    }
85
86    fn get_uri(&self) -> Option<&ClassUri> {
87        self.class_uri.as_ref()
88    }
89
90    fn get_data(&self) -> Option<&ClassData> {
91        self.class_data.as_ref()
92    }
93}
94
95/// NFT metadata
96#[derive(Clone, Debug)]
97pub struct NftMetadata {
98    /// NFT class ID
99    pub class_id: PrefixedClassId,
100    /// NFT ID
101    pub token_id: TokenId,
102    /// NFT URI
103    pub token_uri: Option<TokenUri>,
104    /// NFT data
105    pub token_data: Option<TokenData>,
106}
107
108impl BorshSerialize for NftMetadata {
109    fn serialize<W: std::io::Write>(
110        &self,
111        writer: &mut W,
112    ) -> std::io::Result<()> {
113        BorshSerialize::serialize(&self.class_id.to_string(), writer)?;
114        BorshSerialize::serialize(&self.token_id.to_string(), writer)?;
115        match &self.token_uri {
116            Some(uri) => {
117                BorshSerialize::serialize(&true, writer)?;
118                BorshSerialize::serialize(&uri.to_string(), writer)?;
119            }
120            None => BorshSerialize::serialize(&false, writer)?,
121        }
122        match &self.token_data {
123            Some(data) => {
124                BorshSerialize::serialize(&true, writer)?;
125                BorshSerialize::serialize(&data.to_string(), writer)
126            }
127            None => BorshSerialize::serialize(&false, writer),
128        }
129    }
130}
131
132impl BorshDeserialize for NftMetadata {
133    fn deserialize_reader<R: std::io::Read>(
134        reader: &mut R,
135    ) -> std::io::Result<Self> {
136        use std::io::{Error, ErrorKind};
137        let class_id: String = BorshDeserialize::deserialize_reader(reader)?;
138        let class_id = class_id.parse().map_err(|e: DecodingError| {
139            Error::new(ErrorKind::InvalidData, e.to_string())
140        })?;
141
142        let token_id: String = BorshDeserialize::deserialize_reader(reader)?;
143        let token_id = token_id.parse().map_err(|e: DecodingError| {
144            Error::new(ErrorKind::InvalidData, e.to_string())
145        })?;
146
147        let is_uri: bool = BorshDeserialize::deserialize_reader(reader)?;
148        let token_uri = if is_uri {
149            let uri_str: String = BorshDeserialize::deserialize_reader(reader)?;
150            Some(uri_str.parse().map_err(|e: DecodingError| {
151                Error::new(ErrorKind::InvalidData, e.to_string())
152            })?)
153        } else {
154            None
155        };
156
157        let is_data: bool = BorshDeserialize::deserialize_reader(reader)?;
158        let token_data = if is_data {
159            let data_str: String =
160                BorshDeserialize::deserialize_reader(reader)?;
161            Some(data_str.parse().map_err(|e: DecodingError| {
162                Error::new(ErrorKind::InvalidData, e.to_string())
163            })?)
164        } else {
165            None
166        };
167
168        Ok(Self {
169            class_id,
170            token_id,
171            token_uri,
172            token_data,
173        })
174    }
175}
176
177impl NftContext for NftMetadata {
178    fn get_class_id(&self) -> &ClassId {
179        &self.class_id.base_class_id
180    }
181
182    fn get_id(&self) -> &TokenId {
183        &self.token_id
184    }
185
186    fn get_uri(&self) -> Option<&TokenUri> {
187        self.token_uri.as_ref()
188    }
189
190    fn get_data(&self) -> Option<&TokenData> {
191        self.token_data.as_ref()
192    }
193}