multiversx_chain_vm/system_sc/
system_sc_issue.rs

1use num_bigint::BigUint;
2
3use crate::{
4    crypto_functions::keccak256,
5    host::context::{BlockchainUpdate, TxCache, TxInput, TxResult},
6    types::{top_decode_u64, VMTokenType},
7};
8
9/// Issues a new fungible token.
10#[allow(unused_variables)]
11pub fn issue(tx_input: TxInput, tx_cache: TxCache) -> (TxResult, BlockchainUpdate) {
12    if tx_input.args.len() < 4 {
13        let tx_result = TxResult::from_vm_error("not enough arguments");
14        return (tx_result, BlockchainUpdate::empty());
15    }
16    let name = tx_input.args[0].clone();
17    let ticker = tx_input.args[1].clone();
18    let total_supply = BigUint::from_bytes_be(tx_input.args[2].clone().as_ref());
19    let decimals = top_decode_u64(tx_input.args[3].clone().as_ref()) as u32;
20
21    register_and_set_roles(tx_input, tx_cache, ticker, VMTokenType::Fungible)
22}
23
24/// Issues a new semi-fungible token.
25#[allow(unused_variables)]
26pub fn issue_semi_fungible(tx_input: TxInput, tx_cache: TxCache) -> (TxResult, BlockchainUpdate) {
27    if tx_input.args.len() < 2 {
28        let tx_result = TxResult::from_vm_error("not enough arguments");
29        return (tx_result, BlockchainUpdate::empty());
30    }
31    let name = tx_input.args[0].clone();
32    let ticker = tx_input.args[1].clone();
33
34    register_and_set_roles(tx_input, tx_cache, ticker, VMTokenType::SemiFungible)
35}
36
37/// Issues a new non-fungible token.
38#[allow(unused_variables)]
39pub fn issue_non_fungible(tx_input: TxInput, tx_cache: TxCache) -> (TxResult, BlockchainUpdate) {
40    if tx_input.args.len() < 2 {
41        let tx_result = TxResult::from_vm_error("not enough arguments");
42        return (tx_result, BlockchainUpdate::empty());
43    }
44    let name = tx_input.args[0].clone();
45    let ticker = tx_input.args[1].clone();
46
47    register_and_set_roles(tx_input, tx_cache, ticker, VMTokenType::NonFungible)
48}
49
50// Issues a new token and sets all roles for its type.
51#[allow(unused_variables)]
52pub fn register_and_set_all_roles(
53    tx_input: TxInput,
54    tx_cache: TxCache,
55) -> (TxResult, BlockchainUpdate) {
56    if tx_input.args.len() < 4 {
57        let tx_result = TxResult::from_vm_error("not enough arguments");
58        return (tx_result, BlockchainUpdate::empty());
59    }
60
61    let name = tx_input.args[0].clone();
62    let ticker = tx_input.args[1].clone();
63    let token_type = VMTokenType::from_system_sc_arg(&tx_input.args[2]);
64    let decimals = top_decode_u64(tx_input.args[3].clone().as_ref()) as u32;
65
66    register_and_set_roles(tx_input, tx_cache, ticker, token_type)
67}
68
69fn register_and_set_roles(
70    tx_input: TxInput,
71    tx_cache: TxCache,
72    ticker: Vec<u8>,
73    token_type: VMTokenType,
74) -> (TxResult, BlockchainUpdate) {
75    let mut new_token_identifiers = tx_cache.get_new_token_identifiers();
76
77    let token_identifier = if let Some((i, ti)) =
78        first_token_identifier_with_ticker(&new_token_identifiers, &ticker)
79    {
80        new_token_identifiers.remove(i);
81        ti.into_bytes()
82    } else {
83        generate_token_identifier_from_ticker(&tx_input, &tx_cache, &ticker)
84    };
85
86    tx_cache.with_account_mut(&tx_input.from, |account| {
87        account
88            .esdt
89            .register_and_set_roles(&token_identifier, token_type);
90    });
91    tx_cache.set_new_token_identifiers(new_token_identifiers);
92
93    let tx_result = TxResult {
94        result_values: vec![token_identifier],
95        ..Default::default()
96    };
97
98    (tx_result, tx_cache.into_blockchain_updates())
99}
100
101fn first_token_identifier_with_ticker(
102    token_identifiers: &[String],
103    ticker: &[u8],
104) -> Option<(usize, String)> {
105    let extract_ticker =
106        |ti: &String| -> String { ti.split('-').map(|x| x.to_string()).next().unwrap() };
107
108    token_identifiers
109        .iter()
110        .position(|x| extract_ticker(x).as_bytes() == ticker)
111        .map(|i| (i, token_identifiers[i].clone()))
112}
113
114fn generate_token_identifier_from_ticker(
115    tx_input: &TxInput,
116    tx_cache: &TxCache,
117    ticker: &[u8],
118) -> Vec<u8> {
119    let new_random_base = [
120        tx_input.from.as_bytes(),
121        tx_cache
122            .blockchain_ref()
123            .block_config
124            .current_block_info
125            .block_random_seed
126            .as_slice(),
127    ]
128    .concat();
129    let new_random = keccak256(&new_random_base);
130    let new_random_for_ticker = &new_random[..3];
131
132    let token_identifier = [
133        ticker,
134        "-".as_bytes(),
135        hex::encode(new_random_for_ticker).as_bytes(),
136    ]
137    .concat();
138
139    token_identifier
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_first_token_identifier_with_ticker_ok() {
148        let ticker = String::from("BBBB").into_bytes();
149        let new_token_indetifiers = vec![
150            "AAAA-0123".to_string(),
151            "BBBB-4567".to_string(),
152            "BBBB-0123".to_string(),
153            "CCCC-4567".to_string(),
154        ];
155
156        let ti = first_token_identifier_with_ticker(&new_token_indetifiers, &ticker);
157        let expected = b"BBBB-4567".as_slice();
158        assert_eq!(expected, ti.unwrap().1.into_bytes());
159    }
160
161    #[test]
162    fn test_first_token_identifier_with_ticker_is_none() {
163        let ticker = String::from("BBBB").into_bytes();
164        let new_token_indetifiers = vec!["AAAA-0123".to_string()];
165
166        let i = first_token_identifier_with_ticker(&new_token_indetifiers, &ticker);
167        let expected = None;
168        assert_eq!(expected, i);
169    }
170}