multiversx_sc_modules/token_merge/
mod.rs1multiversx_sc::imports!();
2multiversx_sc::derive_imports!();
3
4pub mod custom_merged_token_attributes;
5pub mod merged_token_instances;
6pub mod merged_token_setup;
7
8use merged_token_instances::{MergedTokenInstances, MAX_MERGED_TOKENS};
9
10use self::custom_merged_token_attributes::MergedTokenAttributesCreator;
11
12static SC_DOES_NOT_OWN_NFT_PARTS_ERR_MSG: &[u8] = b"NFT parts belong to another merging SC";
13
14const MIN_MERGE_PAYMENTS: usize = 2;
15
16#[multiversx_sc::module]
17pub trait TokenMergeModule:
18 merged_token_setup::MergedTokenSetupModule
19 + crate::default_issue_callbacks::DefaultIssueCallbacksModule
20 + crate::pause::PauseModule
21{
22 fn merge_tokens<AttributesCreator: MergedTokenAttributesCreator<ScType = Self>>(
23 &self,
24 payments: &ManagedVec<EsdtTokenPayment>,
25 attr_creator: &AttributesCreator,
26 ) -> EsdtTokenPayment {
27 self.require_not_paused();
28 require!(
29 payments.len() >= MIN_MERGE_PAYMENTS,
30 "Must send at least 2 tokens"
31 );
32
33 let merged_token_id = self.merged_token().get_token_id();
34 let sc_address = self.blockchain().get_sc_address();
35 let token_whitelist = self.mergeable_tokens_whitelist();
36
37 let mut already_merged_tokens = ArrayVec::<_, MAX_MERGED_TOKENS>::new();
38 let mut single_tokens = ArrayVec::<_, MAX_MERGED_TOKENS>::new();
39 for token in payments {
40 if token.token_identifier == merged_token_id {
41 let token_data = self.blockchain().get_esdt_token_data(
42 &sc_address,
43 &token.token_identifier,
44 token.token_nonce,
45 );
46
47 require!(
48 token_data.creator == sc_address,
49 SC_DOES_NOT_OWN_NFT_PARTS_ERR_MSG
50 );
51
52 let merged_instances =
53 MergedTokenInstances::decode_from_first_uri(&token_data.uris);
54 already_merged_tokens.push(merged_instances);
55 } else {
56 require!(
57 token_whitelist.contains(&token.token_identifier),
58 "Token {} cannot be merged",
59 (token.token_identifier)
60 );
61
62 single_tokens.push(token);
63 }
64 }
65
66 let mut all_merged_instances = MergedTokenInstances::new();
67 for already_merged in already_merged_tokens {
68 all_merged_instances.merge_with_other(already_merged);
69 }
70
71 for single_token_instance in single_tokens {
72 all_merged_instances.add_or_update_instance(single_token_instance.clone());
73 }
74
75 let merged_token_payment =
76 self.create_merged_token(merged_token_id, &all_merged_instances, attr_creator);
77
78 self.tx()
79 .to(ToCaller)
80 .payment(&merged_token_payment)
81 .transfer_if_not_empty();
82
83 merged_token_payment
84 }
85
86 fn split_tokens(
87 &self,
88 payments: &ManagedVec<EsdtTokenPayment>,
89 ) -> ManagedVec<EsdtTokenPayment> {
90 self.require_not_paused();
91 require!(!payments.is_empty(), "No payments");
92
93 let merged_token_id = self.merged_token().get_token_id();
94 let sc_address = self.blockchain().get_sc_address();
95
96 let mut output_payments = ManagedVec::new();
97 for token in payments {
98 require!(
99 token.token_identifier == merged_token_id,
100 "Invalid token to split"
101 );
102
103 let token_data = self.blockchain().get_esdt_token_data(
104 &sc_address,
105 &token.token_identifier,
106 token.token_nonce,
107 );
108 require!(
109 token_data.creator == sc_address,
110 SC_DOES_NOT_OWN_NFT_PARTS_ERR_MSG
111 );
112
113 let previously_merged_instance_attributes =
114 MergedTokenInstances::decode_from_first_uri(&token_data.uris);
115 for inst in previously_merged_instance_attributes.into_instances() {
116 output_payments.push(inst);
117 }
118
119 self.send()
120 .esdt_local_burn(&token.token_identifier, token.token_nonce, &token.amount);
121 }
122
123 self.tx().to(ToCaller).payment(&output_payments).transfer();
124
125 output_payments
126 }
127
128 fn split_token_partial<AttributesCreator: MergedTokenAttributesCreator<ScType = Self>>(
129 &self,
130 merged_token: EsdtTokenPayment,
131 mut tokens_to_remove: ManagedVec<EsdtTokenPayment>,
132 attr_creator: &AttributesCreator,
133 ) -> ManagedVec<EsdtTokenPayment> {
134 self.require_not_paused();
135 self.merged_token()
136 .require_same_token(&merged_token.token_identifier);
137
138 let sc_address = self.blockchain().get_sc_address();
139 let merged_token_data = self.blockchain().get_esdt_token_data(
140 &sc_address,
141 &merged_token.token_identifier,
142 merged_token.token_nonce,
143 );
144 require!(
145 merged_token_data.creator == sc_address,
146 SC_DOES_NOT_OWN_NFT_PARTS_ERR_MSG
147 );
148
149 let mut merged_attributes =
150 MergedTokenInstances::decode_from_first_uri(&merged_token_data.uris);
151 for token in &tokens_to_remove {
152 merged_attributes.deduct_balance_for_instance(&token);
153 }
154
155 self.send().esdt_local_burn(
156 &merged_token.token_identifier,
157 merged_token.token_nonce,
158 &merged_token.amount,
159 );
160
161 let new_merged_token = self.create_merged_token(
163 merged_token.token_identifier,
164 &merged_attributes,
165 attr_creator,
166 );
167 tokens_to_remove.push(new_merged_token);
168
169 self.tx().to(ToCaller).payment(&tokens_to_remove).transfer();
170
171 tokens_to_remove
172 }
173}