multiversx_sc_modules/
esdt.rs

1multiversx_sc::imports!();
2
3/// Standard smart contract module for managing a single ESDT.
4///
5/// When added to a smart contract offers basic ESDT usage.
6/// A lot of contracts use an owned ESDT for various purposes.
7/// This module is used to offer a standard way of performing the basic operations.  
8///
9/// It provides endpoints for:
10/// * issuing of an ESDT
11/// * setting local roles
12/// * minting/burning
13///
14#[multiversx_sc::module]
15pub trait EsdtModule {
16    /*
17        EsdtTokenType is an enum (u8):
18        0 - Fungible,
19        1 - NonFungible,
20        2 - SemiFungible,
21        3 - Meta,
22
23        Note: Only Fungible and Meta tokens have decimals
24    */
25    #[payable("EGLD")]
26    #[only_owner]
27    #[endpoint(issueToken)]
28    fn issue_token(
29        &self,
30        token_display_name: ManagedBuffer,
31        token_ticker: ManagedBuffer,
32        token_type: EsdtTokenType,
33        opt_num_decimals: OptionalValue<usize>,
34    ) {
35        require!(self.token_id().is_empty(), "Token already issued");
36
37        let issue_cost = self.call_value().egld().clone();
38        let num_decimals = match opt_num_decimals {
39            OptionalValue::Some(d) => d,
40            OptionalValue::None => 0,
41        };
42
43        self.send()
44            .esdt_system_sc_proxy()
45            .issue_and_set_all_roles(
46                issue_cost,
47                token_display_name,
48                token_ticker,
49                token_type,
50                num_decimals,
51            )
52            .with_callback(self.callbacks().issue_callback())
53            .async_call_and_exit()
54    }
55
56    #[callback]
57    fn issue_callback(&self, #[call_result] result: ManagedAsyncCallResult<TokenIdentifier>) {
58        match result {
59            ManagedAsyncCallResult::Ok(token_id) => {
60                self.token_id().set(&token_id);
61            },
62            ManagedAsyncCallResult::Err(_) => {
63                // return payment to initial caller
64                let initial_caller = self.blockchain().get_owner_address();
65                let egld_returned = self.call_value().egld();
66                self.tx()
67                    .to(&initial_caller)
68                    .egld(egld_returned)
69                    .transfer_if_not_empty();
70            },
71        }
72    }
73
74    fn mint(&self, token_nonce: u64, amount: &BigUint) {
75        let token_id = self.token_id().get();
76        self.send().esdt_local_mint(&token_id, token_nonce, amount);
77    }
78
79    fn burn(&self, token_nonce: u64, amount: &BigUint) {
80        let token_id = self.token_id().get();
81        self.send().esdt_local_burn(&token_id, token_nonce, amount);
82    }
83
84    fn nft_create<T: TopEncode>(&self, amount: &BigUint, attributes: &T) -> u64 {
85        let token_id = self.token_id().get();
86        let empty_buffer = ManagedBuffer::new();
87
88        // sneakily reuses the same handle
89        let empty_vec = unsafe { ManagedRef::wrap_handle(empty_buffer.get_handle()) };
90
91        self.send().esdt_nft_create(
92            &token_id,
93            amount,
94            &empty_buffer,
95            &BigUint::zero(),
96            &empty_buffer,
97            &attributes,
98            &empty_vec,
99        )
100    }
101
102    fn get_token_attributes<T: TopDecode>(&self, token_nonce: u64) -> T {
103        let own_sc_address = self.blockchain().get_sc_address();
104        let token_id = self.token_id().get();
105        let token_data =
106            self.blockchain()
107                .get_esdt_token_data(&own_sc_address, &token_id, token_nonce);
108
109        token_data.decode_attributes()
110    }
111
112    fn require_token_issued(&self) {
113        require!(!self.token_id().is_empty(), "Token must be issued first");
114    }
115
116    // Note: to issue another token, you have to clear this storage
117    #[storage_mapper("token_id")]
118    fn token_id(&self) -> SingleValueMapper<TokenIdentifier>;
119}