mars_core/
cw20_core.rs

1/// cw20_core: Shared functionality for cw20 tokens
2use cosmwasm_std::{DepsMut, StdError, Uint128};
3
4use cw20::{EmbeddedLogo, Logo, LogoInfo, MarketingInfoResponse};
5
6use cw20_base::msg::InstantiateMsg;
7use cw20_base::state::{MinterData, TokenInfo, LOGO, MARKETING_INFO, TOKEN_INFO};
8use cw20_base::ContractError;
9
10pub fn instantiate_token_info_and_marketing(
11    deps: &mut DepsMut,
12    msg: InstantiateMsg,
13    total_supply: Uint128,
14) -> Result<(), ContractError> {
15    if let Some(limit) = msg.get_cap() {
16        if total_supply > limit {
17            return Err(StdError::generic_err("Initial supply greater than cap").into());
18        }
19    }
20
21    let mint = match msg.mint {
22        Some(m) => Some(MinterData {
23            minter: deps.api.addr_validate(&m.minter)?,
24            cap: m.cap,
25        }),
26        None => None,
27    };
28
29    // store token info
30    let data = TokenInfo {
31        name: msg.name,
32        symbol: msg.symbol,
33        decimals: msg.decimals,
34        total_supply,
35        mint,
36    };
37    TOKEN_INFO.save(deps.storage, &data)?;
38
39    if let Some(marketing) = msg.marketing {
40        let logo = if let Some(logo) = marketing.logo {
41            verify_logo(&logo)?;
42            LOGO.save(deps.storage, &logo)?;
43
44            match logo {
45                Logo::Url(url) => Some(LogoInfo::Url(url)),
46                Logo::Embedded(_) => Some(LogoInfo::Embedded),
47            }
48        } else {
49            None
50        };
51
52        let data = MarketingInfoResponse {
53            project: marketing.project,
54            description: marketing.description,
55            marketing: marketing
56                .marketing
57                .map(|addr| deps.api.addr_validate(&addr))
58                .transpose()?,
59            logo,
60        };
61        MARKETING_INFO.save(deps.storage, &data)?;
62    }
63
64    Ok(())
65}
66
67const LOGO_SIZE_CAP: usize = 5 * 1024;
68
69/// Checks if data starts with XML preamble
70fn verify_xml_preamble(data: &[u8]) -> Result<(), ContractError> {
71    // The easiest way to perform this check would be just match on regex, however regex
72    // compilation is heavy and probably not worth it.
73
74    let preamble = data
75        .split_inclusive(|c| *c == b'>')
76        .next()
77        .ok_or(ContractError::InvalidXmlPreamble {})?;
78
79    const PREFIX: &[u8] = b"<?xml ";
80    const POSTFIX: &[u8] = b"?>";
81
82    if !(preamble.starts_with(PREFIX) && preamble.ends_with(POSTFIX)) {
83        Err(ContractError::InvalidXmlPreamble {})
84    } else {
85        Ok(())
86    }
87
88    // Additionally attributes format could be validated as they are well defined, as well as
89    // comments presence inside of preable, but it is probably not worth it.
90}
91
92/// Validates XML logo
93fn verify_xml_logo(logo: &[u8]) -> Result<(), ContractError> {
94    verify_xml_preamble(logo)?;
95
96    if logo.len() > LOGO_SIZE_CAP {
97        Err(ContractError::LogoTooBig {})
98    } else {
99        Ok(())
100    }
101}
102
103/// Validates png logo
104fn verify_png_logo(logo: &[u8]) -> Result<(), ContractError> {
105    // PNG header format:
106    // 0x89 - magic byte, out of ASCII table to fail on 7-bit systems
107    // "PNG" ascii representation
108    // [0x0d, 0x0a] - dos style line ending
109    // 0x1a - dos control character, stop displaying rest of the file
110    // 0x0a - unix style line ending
111    const HEADER: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0d, 0x0a, 0x1a, 0x0a];
112    if logo.len() > LOGO_SIZE_CAP {
113        Err(ContractError::LogoTooBig {})
114    } else if !logo.starts_with(&HEADER) {
115        Err(ContractError::InvalidPngHeader {})
116    } else {
117        Ok(())
118    }
119}
120
121/// Checks if passed logo is correct, and if not, returns an error
122fn verify_logo(logo: &Logo) -> Result<(), ContractError> {
123    match logo {
124        Logo::Embedded(EmbeddedLogo::Svg(logo)) => verify_xml_logo(logo),
125        Logo::Embedded(EmbeddedLogo::Png(logo)) => verify_png_logo(logo),
126        Logo::Url(_) => Ok(()), // Any reasonable url validation would be regex based, probably not worth it
127    }
128}