1use candid::{CandidType, Deserialize, Int, Nat, Principal};
2use serde::Serialize;
3
4pub type TokenIdentifier = Nat;
6
7#[derive(CandidType, Deserialize, Serialize, Clone, Debug, PartialEq)]
9pub enum GenericValue {
10 BoolContent(bool),
11 TextContent(String),
12 BlobContent(Vec<u8>),
13 Principal(Principal),
14 Nat8Content(u8),
15 Nat16Content(u16),
16 Nat32Content(u32),
17 Nat64Content(u64),
18 NatContent(Nat),
19 Int8Content(i8),
20 Int16Content(i16),
21 Int32Content(i32),
22 Int64Content(i64),
23 IntContent(Int),
24 FloatContent(f64), NestedContent(Vec<(String, GenericValue)>),
26}
27
28#[derive(CandidType, Deserialize, Serialize, Clone, Debug, PartialEq)]
30pub struct TokenMetadata {
31 pub approved_at: Option<u64>,
32 pub approved_by: Option<Principal>,
33 pub burned_at: Option<u64>,
34 pub burned_by: Option<Principal>,
35 pub is_burned: bool,
36 pub minted_at: u64,
37 pub minted_by: Principal,
38 pub operator: Option<Principal>,
39 pub owner: Option<Principal>,
40 pub properties: Vec<(String, GenericValue)>,
41 pub token_identifier: TokenIdentifier,
42 pub transferred_at: Option<u64>,
43 pub transferred_by: Option<Principal>,
44}
45
46#[cfg(feature = "ic-stable-structures")]
47impl ic_stable_structures::Storable for TokenMetadata {
48 const BOUND: ic_stable_structures::storable::Bound =
49 ic_stable_structures::storable::Bound::Unbounded;
50
51 fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
52 use candid::Encode;
53 Encode!(&self).unwrap().into()
54 }
55
56 fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
57 use candid::Decode;
58 Decode!(&bytes, Self).unwrap()
59 }
60}
61
62#[cfg(test)]
63mod test {
64
65 use pretty_assertions::assert_eq;
66
67 use super::*;
68
69 #[test]
70 #[cfg(feature = "ic-stable-structures")]
71 fn test_should_encode_and_decode_nft() {
72 use ic_stable_structures::Storable as _;
73 let token_metadata = TokenMetadata {
74 approved_at: Some(1),
75 approved_by: Some(Principal::management_canister()),
76 burned_at: Some(3),
77 burned_by: Some(Principal::management_canister()),
78 is_burned: true,
79 minted_at: 5,
80 minted_by: Principal::management_canister(),
81 operator: Some(Principal::management_canister()),
82 owner: Some(Principal::management_canister()),
83 properties: vec![(
84 "key".to_string(),
85 GenericValue::TextContent("value".to_string()),
86 )],
87 token_identifier: 9_u64.into(),
88 transferred_at: Some(10),
89 transferred_by: Some(Principal::management_canister()),
90 };
91
92 let bytes = token_metadata.to_bytes();
93 let decoded = TokenMetadata::from_bytes(bytes);
94
95 assert_eq!(token_metadata, decoded);
96 }
97}