multiversx_sc_modules/token_merge/
merged_token_setup.rs

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
multiversx_sc::imports!();

use super::{
    custom_merged_token_attributes::MergedTokenAttributesCreator,
    merged_token_instances::{MergedTokenInstances, MAX_MERGED_TOKENS},
};

const NFT_AMOUNT: u64 = 1;
pub static DIFFERENT_CREATOR_ERR_MSG: &[u8] = b"All merged tokens must have the same creator";

#[multiversx_sc::module]
pub trait MergedTokenSetupModule {
    #[only_owner]
    #[payable("EGLD")]
    #[endpoint(issueMergedToken)]
    fn issue_merged_token(&self, token_display_name: ManagedBuffer, token_ticker: ManagedBuffer) {
        let payment_amount = self.call_value().egld();
        self.merged_token().issue_and_set_all_roles(
            EsdtTokenType::NonFungible,
            payment_amount.clone(),
            token_display_name,
            token_ticker,
            0,
            None,
        );
    }

    #[only_owner]
    #[endpoint(addMergeableTokensToWhitelist)]
    fn add_mergeable_tokens_to_whitelist(&self, tokens: MultiValueEncoded<TokenIdentifier>) {
        let mut whitelist = self.mergeable_tokens_whitelist();
        for token in tokens {
            let _ = whitelist.insert(token);
        }
    }

    #[only_owner]
    #[endpoint(removeMergeableTokensFromWhitelist)]
    fn remove_mergeable_tokens_from_whitelist(&self, tokens: MultiValueEncoded<TokenIdentifier>) {
        let mut whitelist = self.mergeable_tokens_whitelist();
        for token in tokens {
            let _ = whitelist.swap_remove(&token);
        }
    }

    fn create_merged_token<AttributesCreator: MergedTokenAttributesCreator<ScType = Self>>(
        &self,
        merged_token_id: TokenIdentifier,
        merged_instances: &MergedTokenInstances<Self::Api>,
        attr_creator: &AttributesCreator,
    ) -> EsdtTokenPayment<Self::Api> {
        let nft_amount = BigUint::from(NFT_AMOUNT);
        let empty_buffer = ManagedBuffer::new();

        let all_token_data = self.collect_token_data(merged_instances);
        self.require_all_parts_same_creator(&all_token_data);

        let royalties = self.get_max_royalties(&all_token_data);
        let uri = self.create_uri_for_merged_token(merged_instances);
        let attributes =
            attr_creator.get_merged_token_attributes(self, &merged_token_id, merged_instances);
        let merged_token_nonce = self.send().esdt_nft_create(
            &merged_token_id,
            &nft_amount,
            &empty_buffer,
            &royalties,
            &empty_buffer,
            &attributes,
            &ManagedVec::from_single_item(uri),
        );

        EsdtTokenPayment::new(merged_token_id, merged_token_nonce, nft_amount)
    }

    fn create_uri_for_merged_token(
        &self,
        merged_instances: &MergedTokenInstances<Self::Api>,
    ) -> ManagedBuffer {
        let mut tokens_list = ManagedVec::<Self::Api, _>::new();
        for inst in merged_instances.get_instances() {
            tokens_list.push(inst.clone());
        }

        let mut encoded = ManagedBuffer::new();
        let _ = tokens_list.top_encode(&mut encoded);

        encoded
    }

    fn collect_token_data(
        &self,
        merged_instances: &MergedTokenInstances<Self::Api>,
    ) -> ArrayVec<EsdtTokenData<Self::Api>, MAX_MERGED_TOKENS> {
        let mut all_token_data = ArrayVec::new();
        let own_sc_address = self.blockchain().get_sc_address();
        for inst in merged_instances.get_instances() {
            let token_data = self.blockchain().get_esdt_token_data(
                &own_sc_address,
                &inst.token_identifier,
                inst.token_nonce,
            );
            unsafe {
                all_token_data.push_unchecked(token_data);
            }
        }

        all_token_data
    }

    fn require_all_parts_same_creator(
        &self,
        all_token_data: &ArrayVec<EsdtTokenData<Self::Api>, MAX_MERGED_TOKENS>,
    ) {
        if all_token_data.is_empty() {
            return;
        }

        let first_creator = unsafe { &all_token_data.get_unchecked(0).creator };
        for token_data in &all_token_data.as_slice()[1..] {
            require!(
                &token_data.creator == first_creator,
                DIFFERENT_CREATOR_ERR_MSG
            );
        }
    }

    fn get_max_royalties(
        &self,
        all_token_data: &ArrayVec<EsdtTokenData<Self::Api>, MAX_MERGED_TOKENS>,
    ) -> BigUint {
        let zero = BigUint::zero();
        let mut max_ref = &zero;
        for token_data in all_token_data {
            if &token_data.royalties > max_ref {
                max_ref = &token_data.royalties;
            }
        }

        max_ref.clone()
    }

    #[view(getMergedTokenId)]
    #[storage_mapper("mergedToken")]
    fn merged_token(&self) -> NonFungibleTokenMapper;

    #[view(getMergeableTokensWhitelist)]
    #[storage_mapper("mergeableTokensWhitelist")]
    fn mergeable_tokens_whitelist(&self) -> UnorderedSetMapper<TokenIdentifier>;
}