multiversx_sc_modules/token_merge/
merged_token_setup.rs

1multiversx_sc::imports!();
2
3use super::{
4    custom_merged_token_attributes::MergedTokenAttributesCreator,
5    merged_token_instances::{MergedTokenInstances, MAX_MERGED_TOKENS},
6};
7
8const NFT_AMOUNT: u64 = 1;
9pub static DIFFERENT_CREATOR_ERR_MSG: &[u8] = b"All merged tokens must have the same creator";
10
11#[multiversx_sc::module]
12pub trait MergedTokenSetupModule {
13    #[only_owner]
14    #[payable("EGLD")]
15    #[endpoint(issueMergedToken)]
16    fn issue_merged_token(&self, token_display_name: ManagedBuffer, token_ticker: ManagedBuffer) {
17        let payment_amount = self.call_value().egld();
18        self.merged_token().issue_and_set_all_roles(
19            EsdtTokenType::NonFungible,
20            payment_amount.clone(),
21            token_display_name,
22            token_ticker,
23            0,
24            None,
25        );
26    }
27
28    #[only_owner]
29    #[endpoint(addMergeableTokensToWhitelist)]
30    fn add_mergeable_tokens_to_whitelist(&self, tokens: MultiValueEncoded<TokenIdentifier>) {
31        let mut whitelist = self.mergeable_tokens_whitelist();
32        for token in tokens {
33            let _ = whitelist.insert(token);
34        }
35    }
36
37    #[only_owner]
38    #[endpoint(removeMergeableTokensFromWhitelist)]
39    fn remove_mergeable_tokens_from_whitelist(&self, tokens: MultiValueEncoded<TokenIdentifier>) {
40        let mut whitelist = self.mergeable_tokens_whitelist();
41        for token in tokens {
42            let _ = whitelist.swap_remove(&token);
43        }
44    }
45
46    fn create_merged_token<AttributesCreator: MergedTokenAttributesCreator<ScType = Self>>(
47        &self,
48        merged_token_id: TokenIdentifier,
49        merged_instances: &MergedTokenInstances<Self::Api>,
50        attr_creator: &AttributesCreator,
51    ) -> EsdtTokenPayment<Self::Api> {
52        let nft_amount = BigUint::from(NFT_AMOUNT);
53        let empty_buffer = ManagedBuffer::new();
54
55        let all_token_data = self.collect_token_data(merged_instances);
56        self.require_all_parts_same_creator(&all_token_data);
57
58        let royalties = self.get_max_royalties(&all_token_data);
59        let uri = self.create_uri_for_merged_token(merged_instances);
60        let attributes =
61            attr_creator.get_merged_token_attributes(self, &merged_token_id, merged_instances);
62        let merged_token_nonce = self.send().esdt_nft_create(
63            &merged_token_id,
64            &nft_amount,
65            &empty_buffer,
66            &royalties,
67            &empty_buffer,
68            &attributes,
69            &ManagedVec::from_single_item(uri),
70        );
71
72        EsdtTokenPayment::new(merged_token_id, merged_token_nonce, nft_amount)
73    }
74
75    fn create_uri_for_merged_token(
76        &self,
77        merged_instances: &MergedTokenInstances<Self::Api>,
78    ) -> ManagedBuffer {
79        let mut tokens_list = ManagedVec::<Self::Api, _>::new();
80        for inst in merged_instances.get_instances() {
81            tokens_list.push(inst.clone());
82        }
83
84        let mut encoded = ManagedBuffer::new();
85        let _ = tokens_list.top_encode(&mut encoded);
86
87        encoded
88    }
89
90    fn collect_token_data(
91        &self,
92        merged_instances: &MergedTokenInstances<Self::Api>,
93    ) -> ArrayVec<EsdtTokenData<Self::Api>, MAX_MERGED_TOKENS> {
94        let mut all_token_data = ArrayVec::new();
95        let own_sc_address = self.blockchain().get_sc_address();
96        for inst in merged_instances.get_instances() {
97            let token_data = self.blockchain().get_esdt_token_data(
98                &own_sc_address,
99                &inst.token_identifier,
100                inst.token_nonce,
101            );
102            unsafe {
103                all_token_data.push_unchecked(token_data);
104            }
105        }
106
107        all_token_data
108    }
109
110    fn require_all_parts_same_creator(
111        &self,
112        all_token_data: &ArrayVec<EsdtTokenData<Self::Api>, MAX_MERGED_TOKENS>,
113    ) {
114        if all_token_data.is_empty() {
115            return;
116        }
117
118        let first_creator = unsafe { &all_token_data.get_unchecked(0).creator };
119        for token_data in &all_token_data.as_slice()[1..] {
120            require!(
121                &token_data.creator == first_creator,
122                DIFFERENT_CREATOR_ERR_MSG
123            );
124        }
125    }
126
127    fn get_max_royalties(
128        &self,
129        all_token_data: &ArrayVec<EsdtTokenData<Self::Api>, MAX_MERGED_TOKENS>,
130    ) -> BigUint {
131        let zero = BigUint::zero();
132        let mut max_ref = &zero;
133        for token_data in all_token_data {
134            if &token_data.royalties > max_ref {
135                max_ref = &token_data.royalties;
136            }
137        }
138
139        max_ref.clone()
140    }
141
142    #[view(getMergedTokenId)]
143    #[storage_mapper("mergedToken")]
144    fn merged_token(&self) -> NonFungibleTokenMapper;
145
146    #[view(getMergeableTokensWhitelist)]
147    #[storage_mapper("mergeableTokensWhitelist")]
148    fn mergeable_tokens_whitelist(&self) -> UnorderedSetMapper<TokenIdentifier>;
149}