1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_aux::prelude::*;
use crate::meta::*;
#[derive(Serialize, PartialEq, Eq, Deserialize, Debug, Clone)]
pub struct JettonMetaData {
///Optional. UTF8 string. The name of the token - e.g. "Example Coin".
pub name: Option<String>,
///Optional. Used by "Semi-chain content layout". ASCII string. A URI pointing to JSON document with metadata.
pub uri: Option<String>,
///Optional. UTF8 string. The symbol of the token - e.g. "XMPL". Used in the form "You received 99 XMPL".
pub symbol: Option<String>,
///Optional. UTF8 string. Describes the token - e.g. "This is an example jetton for the TON network".
pub description: Option<String>,
///Optional. ASCII string. A URI pointing to a jetton icon with mime type image.
pub image: Option<String>,
///Optional. Either binary representation of the image for onchain layout or base64 for offchain layout.
pub image_data: Option<Vec<u8>>,
///Optional. If not specified, 9 is used by default. UTF8 encoded string with number from 0 to 255.
///The number of decimals the token uses - e.g. 8, means to divide the token amount by 100000000
///to get its user representation, while 0 means that tokens are indivisible:
///user representation of token number should correspond to token amount in wallet-contract storage.
///
///In case you specify decimals, it is highly recommended that you specify this parameter
///on-chain and that the smart contract code ensures that this parameter is immutable.
#[serde(default, deserialize_with = "deserialize_option_number_from_string")]
pub decimals: Option<u8>,
}
#[async_trait]
impl LoadMeta<JettonMetaData> for MetaLoader<JettonMetaData> {
async fn load(&self, content: &MetaDataContent) -> Result<JettonMetaData, MetaLoaderError> {
match content {
MetaDataContent::External { uri } => self.load_meta_from_uri(uri.as_str()).await,
MetaDataContent::Internal { dict } => {
if dict.contains_key(&META_URI.key) {
let uri = String::from_utf8_lossy(dict.get(&META_URI.key).unwrap()).to_string();
let result = self.load_meta_from_uri(uri.as_str()).await;
match result {
Ok(external_meta) => Ok(JettonMetaData {
name: META_NAME.use_string_or(external_meta.name, dict),
uri: META_URI.use_string_or(external_meta.uri, dict),
symbol: META_SYMBOL.use_string_or(external_meta.symbol, dict),
description: META_DESCRIPTION
.use_string_or(external_meta.description, dict),
image: META_IMAGE.use_string_or(external_meta.image, dict),
image_data: external_meta
.image_data
.or(dict.get(&META_IMAGE_DATA.key).cloned()),
decimals: META_DECIMALS
.use_string_or(None, dict)
.map(|v| v.parse::<u8>().unwrap()),
}),
Err(_) => Ok(dict.try_into()?),
}
} else {
dict.try_into()
}
}
content => Err(MetaLoaderError::ContentLayoutUnsupported(content.clone())),
}
}
}
impl TryFrom<&SnakeFormatDict> for JettonMetaData {
type Error = MetaLoaderError;
fn try_from(dict: &SnakeFormatDict) -> Result<Self, Self::Error> {
let decimals = META_DECIMALS
.use_string_or(None, dict)
.map(|v| v.parse::<u8>())
.transpose()?;
Ok(JettonMetaData {
name: META_NAME.use_string_or(None, dict),
uri: META_URI.use_string_or(None, dict),
symbol: META_SYMBOL.use_string_or(None, dict),
description: META_DESCRIPTION.use_string_or(None, dict),
image: META_IMAGE.use_string_or(None, dict),
image_data: dict.get(&META_IMAGE_DATA.key).cloned(),
decimals,
})
}
}