dip721_rs/
data.rs

1use candid::{CandidType, Deserialize, Int, Nat, Principal};
2use serde::Serialize;
3
4/// NFT token identifier
5pub type TokenIdentifier = Nat;
6
7/// Properties value representation for a token
8#[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), // motoko only support f64
25    NestedContent(Vec<(String, GenericValue)>),
26}
27
28/// Metadata for a DIP721 token
29#[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}