multiversx_sc/storage/mappers/token/
fungible_token_mapper.rs1use multiversx_chain_core::types::EsdtLocalRole;
2
3use crate::{
4 abi::{TypeAbi, TypeAbiFrom},
5 api::ErrorApiImpl,
6 codec::{EncodeErrorHandler, TopEncodeMulti, TopEncodeMultiOutput},
7 storage::mappers::{
8 StorageMapperFromAddress,
9 source::{CurrentStorage, StorageAddress},
10 },
11 storage_clear, storage_get, storage_get_len, storage_set,
12 types::{
13 ESDTSystemSCAddress, ManagedRef, Tx,
14 system_proxy::{ESDTSystemSCProxy, FungibleTokenProperties},
15 },
16};
17
18use super::{
19 super::StorageMapper,
20 TokenMapperState,
21 error::{
22 INVALID_PAYMENT_TOKEN_ERR_MSG, INVALID_TOKEN_ID_ERR_MSG, MUST_SET_TOKEN_ID_ERR_MSG,
23 PENDING_ERR_MSG, TOKEN_ID_ALREADY_SET_ERR_MSG,
24 },
25};
26use crate::{
27 abi::TypeName,
28 api::{CallTypeApi, StorageMapperApi},
29 contract_base::{BlockchainWrapper, SendWrapper},
30 storage::StorageKey,
31 types::{
32 BigUint, CallbackClosure, EsdtTokenIdentifier, EsdtTokenPayment, EsdtTokenType,
33 ManagedAddress, ManagedBuffer, ManagedType, ManagedVec,
34 },
35};
36
37pub(crate) const DEFAULT_ISSUE_CALLBACK_NAME: &str = "default_issue_cb";
38pub(crate) const DEFAULT_ISSUE_WITH_INIT_SUPPLY_CALLBACK_NAME: &str =
39 "default_issue_init_supply_cb";
40
41pub struct FungibleTokenMapper<SA, A = CurrentStorage>
81where
82 SA: StorageMapperApi + CallTypeApi,
83 A: StorageAddress<SA>,
84{
85 key: StorageKey<SA>,
86 token_state: TokenMapperState<SA>,
87 address: A,
88}
89
90impl<SA> StorageMapper<SA> for FungibleTokenMapper<SA, CurrentStorage>
91where
92 SA: StorageMapperApi + CallTypeApi,
93{
94 fn new(base_key: StorageKey<SA>) -> Self {
95 Self {
96 token_state: storage_get(base_key.as_ref()),
97 key: base_key,
98 address: CurrentStorage,
99 }
100 }
101}
102
103impl<SA> StorageMapperFromAddress<SA> for FungibleTokenMapper<SA, ManagedAddress<SA>>
104where
105 SA: StorageMapperApi + CallTypeApi,
106{
107 fn new_from_address(address: ManagedAddress<SA>, base_key: StorageKey<SA>) -> Self {
108 Self {
109 token_state: storage_get(base_key.as_ref()),
110 key: base_key,
111 address,
112 }
113 }
114}
115
116impl<SA> FungibleTokenMapper<SA, CurrentStorage>
117where
118 SA: StorageMapperApi + CallTypeApi,
119{
120 pub fn issue(
139 &self,
140 issue_cost: BigUint<SA>,
141 token_display_name: ManagedBuffer<SA>,
142 token_ticker: ManagedBuffer<SA>,
143 initial_supply: BigUint<SA>,
144 num_decimals: usize,
145 opt_callback: Option<CallbackClosure<SA>>,
146 ) -> ! {
147 self.check_not_set();
148
149 let callback = match opt_callback {
150 Some(cb) => cb,
151 None => self.default_callback_closure_obj(&initial_supply),
152 };
153 let properties = FungibleTokenProperties {
154 num_decimals,
155 ..Default::default()
156 };
157
158 storage_set(self.get_storage_key(), &TokenMapperState::<SA>::Pending);
159 Tx::new_tx_from_sc()
160 .to(ESDTSystemSCAddress)
161 .typed(ESDTSystemSCProxy)
162 .issue_fungible(
163 issue_cost,
164 &token_display_name,
165 &token_ticker,
166 &initial_supply,
167 properties,
168 )
169 .callback(callback)
170 .async_call_and_exit()
171 }
172
173 pub fn issue_and_set_all_roles(
192 &self,
193 issue_cost: BigUint<SA>,
194 token_display_name: ManagedBuffer<SA>,
195 token_ticker: ManagedBuffer<SA>,
196 num_decimals: usize,
197 opt_callback: Option<CallbackClosure<SA>>,
198 ) -> ! {
199 self.check_not_set();
200
201 let callback = match opt_callback {
202 Some(cb) => cb,
203 None => self.default_callback_closure_obj(&BigUint::zero()),
204 };
205
206 storage_set(self.get_storage_key(), &TokenMapperState::<SA>::Pending);
207 Tx::new_tx_from_sc()
208 .to(ESDTSystemSCAddress)
209 .typed(ESDTSystemSCProxy)
210 .issue_and_set_all_roles(
211 issue_cost,
212 token_display_name,
213 token_ticker,
214 EsdtTokenType::Fungible,
215 num_decimals,
216 )
217 .callback(callback)
218 .async_call_and_exit();
219 }
220
221 pub fn clear(&mut self) {
222 let state: TokenMapperState<SA> = storage_get(self.key.as_ref());
223 if state.is_pending() {
224 storage_clear(self.key.as_ref());
225 }
226 }
227
228 pub fn mint(&self, amount: BigUint<SA>) -> EsdtTokenPayment<SA> {
229 let send_wrapper = SendWrapper::<SA>::new();
230 let token_id = self.get_token_id();
231
232 send_wrapper.esdt_local_mint(&token_id, 0, &amount);
233
234 EsdtTokenPayment::new(token_id, 0, amount)
235 }
236
237 pub fn mint_and_send(
238 &self,
239 to: &ManagedAddress<SA>,
240 amount: BigUint<SA>,
241 ) -> EsdtTokenPayment<SA> {
242 let payment = self.mint(amount);
243 self.send_payment(to, &payment);
244
245 payment
246 }
247
248 pub fn burn(&self, amount: &BigUint<SA>) {
249 let send_wrapper = SendWrapper::<SA>::new();
250 let token_id = self.get_token_id_ref();
251
252 send_wrapper.esdt_local_burn(token_id, 0, amount);
253 }
254
255 pub fn send_payment(&self, to: &ManagedAddress<SA>, payment: &EsdtTokenPayment<SA>) {
256 Tx::new_tx_from_sc()
257 .to(to)
258 .single_esdt(&payment.token_identifier, 0, &payment.amount)
259 .transfer();
260 }
261
262 pub fn set_if_empty(&mut self, token_id: EsdtTokenIdentifier<SA>) {
263 if self.is_empty() {
264 self.set_token_id(token_id);
265 }
266 }
267
268 pub fn set_local_roles(
269 &self,
270 roles: &[EsdtLocalRole],
271 opt_callback: Option<CallbackClosure<SA>>,
272 ) -> ! {
273 let own_sc_address = Self::get_sc_address();
274 self.set_local_roles_for_address(&own_sc_address, roles, opt_callback);
275 }
276
277 pub fn set_local_roles_for_address(
278 &self,
279 address: &ManagedAddress<SA>,
280 roles: &[EsdtLocalRole],
281 opt_callback: Option<CallbackClosure<SA>>,
282 ) -> ! {
283 self.require_issued_or_set();
284
285 let token_id = self.get_token_id_ref();
286 Tx::new_tx_from_sc()
287 .to(ESDTSystemSCAddress)
288 .typed(ESDTSystemSCProxy)
289 .set_special_roles(address, token_id, roles[..].iter().cloned())
290 .callback(opt_callback)
291 .async_call_and_exit()
292 }
293
294 pub fn set_token_id(&mut self, token_id: EsdtTokenIdentifier<SA>) {
295 self.store_token_id(&token_id);
296 self.token_state = TokenMapperState::Token(token_id);
297 }
298
299 pub(crate) fn store_token_id(&self, token_id: &EsdtTokenIdentifier<SA>) {
300 if self.get_token_state().is_set() {
301 SA::error_api_impl().signal_error(TOKEN_ID_ALREADY_SET_ERR_MSG);
302 }
303 if !token_id.is_valid_esdt_identifier() {
304 SA::error_api_impl().signal_error(INVALID_TOKEN_ID_ERR_MSG);
305 }
306 storage_set(
307 self.get_storage_key(),
308 &TokenMapperState::Token(token_id.clone()),
309 );
310 }
311
312 pub fn get_balance(&self) -> BigUint<SA> {
313 let b_wrapper = BlockchainWrapper::new();
314 let own_sc_address = Self::get_sc_address();
315 let token_id = self.get_token_id_ref();
316
317 b_wrapper.get_esdt_balance(&own_sc_address, token_id, 0)
318 }
319
320 pub fn get_sc_address() -> ManagedAddress<SA> {
321 let b_wrapper = BlockchainWrapper::new();
322 b_wrapper.get_sc_address()
323 }
324}
325
326impl<SA, A> FungibleTokenMapper<SA, A>
327where
328 SA: StorageMapperApi + CallTypeApi,
329 A: StorageAddress<SA>,
330{
331 pub fn get_storage_key(&self) -> ManagedRef<'_, SA, StorageKey<SA>> {
332 self.key.as_ref()
333 }
334
335 pub fn get_token_state(&self) -> TokenMapperState<SA> {
336 self.token_state.clone()
337 }
338
339 pub fn get_token_id(&self) -> EsdtTokenIdentifier<SA> {
340 if let TokenMapperState::Token(token) = &self.token_state {
341 token.clone()
342 } else {
343 SA::error_api_impl().signal_error(INVALID_TOKEN_ID_ERR_MSG)
344 }
345 }
346
347 pub fn get_token_id_ref(&self) -> &EsdtTokenIdentifier<SA> {
348 if let TokenMapperState::Token(token) = &self.token_state {
349 token
350 } else {
351 SA::error_api_impl().signal_error(INVALID_TOKEN_ID_ERR_MSG);
352 }
353 }
354
355 pub fn is_empty(&self) -> bool {
356 storage_get_len(self.get_storage_key()) == 0
357 }
358
359 pub fn require_issued_or_set(&self) {
360 if self.is_empty() {
361 SA::error_api_impl().signal_error(MUST_SET_TOKEN_ID_ERR_MSG);
362 }
363 }
364
365 pub fn require_same_token(&self, expected_token_id: &EsdtTokenIdentifier<SA>) {
366 let actual_token_id = self.get_token_id_ref();
367 if actual_token_id != expected_token_id {
368 SA::error_api_impl().signal_error(INVALID_PAYMENT_TOKEN_ERR_MSG);
369 }
370 }
371
372 pub fn require_all_same_token(&self, payments: &ManagedVec<SA, EsdtTokenPayment<SA>>) {
373 let actual_token_id = self.get_token_id_ref();
374 for p in payments {
375 if actual_token_id != &p.token_identifier {
376 SA::error_api_impl().signal_error(INVALID_PAYMENT_TOKEN_ERR_MSG);
377 }
378 }
379 }
380
381 pub fn default_callback_closure_obj(
382 &self,
383 initial_supply: &BigUint<SA>,
384 ) -> CallbackClosure<SA> {
385 let initial_caller = BlockchainWrapper::<SA>::new().get_caller();
386 let cb_name = if initial_supply > &0 {
387 DEFAULT_ISSUE_WITH_INIT_SUPPLY_CALLBACK_NAME
388 } else {
389 DEFAULT_ISSUE_CALLBACK_NAME
390 };
391
392 let mut cb_closure = CallbackClosure::new(cb_name);
393 cb_closure.push_endpoint_arg(&initial_caller);
394 cb_closure.push_endpoint_arg(&self.key.buffer);
395
396 cb_closure
397 }
398
399 pub(crate) fn check_not_set(&self) {
400 let storage_value: TokenMapperState<SA> = storage_get(self.get_storage_key());
401 match storage_value {
402 TokenMapperState::NotSet => {}
403 TokenMapperState::Pending => {
404 SA::error_api_impl().signal_error(PENDING_ERR_MSG);
405 }
406 TokenMapperState::Token(_) => {
407 SA::error_api_impl().signal_error(TOKEN_ID_ALREADY_SET_ERR_MSG);
408 }
409 }
410 }
411}
412
413impl<SA> TopEncodeMulti for FungibleTokenMapper<SA>
414where
415 SA: StorageMapperApi + CallTypeApi,
416{
417 fn multi_encode_or_handle_err<O, H>(&self, output: &mut O, h: H) -> Result<(), H::HandledErr>
418 where
419 O: TopEncodeMultiOutput,
420 H: EncodeErrorHandler,
421 {
422 if self.is_empty() {
423 output.push_single_value(&ManagedBuffer::<SA>::new(), h)
424 } else {
425 output.push_single_value(&self.get_token_id(), h)
426 }
427 }
428}
429
430impl<SA> TypeAbiFrom<FungibleTokenMapper<SA>> for EsdtTokenIdentifier<SA> where
431 SA: StorageMapperApi + CallTypeApi
432{
433}
434
435impl<SA> TypeAbiFrom<Self> for FungibleTokenMapper<SA> where SA: StorageMapperApi + CallTypeApi {}
436
437impl<SA> TypeAbi for FungibleTokenMapper<SA>
438where
439 SA: StorageMapperApi + CallTypeApi,
440{
441 type Unmanaged = Self;
442
443 fn type_name() -> TypeName {
444 EsdtTokenIdentifier::<SA>::type_name()
445 }
446
447 fn type_name_rust() -> TypeName {
448 EsdtTokenIdentifier::<SA>::type_name_rust()
449 }
450
451 fn provide_type_descriptions<TDC: crate::abi::TypeDescriptionContainer>(accumulator: &mut TDC) {
452 EsdtTokenIdentifier::<SA>::provide_type_descriptions(accumulator);
453 }
454
455 fn is_variadic() -> bool {
456 false
457 }
458}