1use core::marker::PhantomData;
2
3use crate::codec::Empty;
4
5use crate::types::ManagedRef;
6use crate::{
7 api::{BlockchainApi, CallTypeApi, StorageReadApi},
8 codec,
9 types::{
10 system_proxy, BigUint, ESDTSystemSCAddress, EgldOrEsdtTokenIdentifier, EsdtTokenPayment,
11 FunctionCall, GasLeft, ManagedAddress, ManagedArgBuffer, ManagedBuffer, ManagedType,
12 ManagedVec, NotPayable, OriginalResultMarker, ReturnsRawResult, ReturnsResult, ToSelf,
13 TokenIdentifier, Tx, TxScEnv,
14 },
15};
16
17use super::BlockchainWrapper;
18
19const PERCENTAGE_TOTAL: u64 = 10_000;
20
21use super::SendRawWrapper;
22
23#[derive(Default)]
27pub struct SendWrapper<A>
28where
29 A: CallTypeApi + StorageReadApi + BlockchainApi,
30{
31 _phantom: PhantomData<A>,
32}
33
34impl<A> SendWrapper<A>
35where
36 A: CallTypeApi + StorageReadApi + BlockchainApi,
37{
38 pub fn new() -> Self {
39 SendWrapper {
40 _phantom: PhantomData,
41 }
42 }
43
44 #[inline]
45 fn send_raw_wrapper(&self) -> SendRawWrapper<A> {
46 SendRawWrapper::new()
47 }
48
49 pub fn esdt_system_sc_proxy(
51 &self,
52 ) -> system_proxy::ESDTSystemSCProxyMethods<TxScEnv<A>, (), ESDTSystemSCAddress, ()> {
53 self.esdt_system_sc_tx()
54 }
55
56 pub fn esdt_system_sc_tx(
59 &self,
60 ) -> system_proxy::ESDTSystemSCProxyMethods<TxScEnv<A>, (), ESDTSystemSCAddress, ()> {
61 Tx::new_tx_from_sc()
62 .to(ESDTSystemSCAddress)
63 .typed(system_proxy::ESDTSystemSCProxy)
64 }
65
66 #[cfg(feature = "contract-call-legacy")]
70 #[deprecated(
71 since = "0.62.0",
72 note = "Please use the unified transaction syntax instead."
73 )]
74 pub fn contract_call<R>(
75 &self,
76 to: ManagedAddress<A>,
77 endpoint_name: impl Into<ManagedBuffer<A>>,
78 ) -> crate::types::ContractCallNoPayment<A, R> {
79 crate::types::ContractCallNoPayment::new(to, endpoint_name)
80 }
81
82 #[inline]
85 pub fn direct_egld(&self, to: &ManagedAddress<A>, amount: &BigUint<A>) {
86 Tx::new_tx_from_sc().to(to).egld(amount).transfer();
87 }
88
89 pub fn direct_non_zero_egld(&self, to: &ManagedAddress<A>, amount: &BigUint<A>) {
94 Tx::new_tx_from_sc()
95 .to(to)
96 .egld(amount)
97 .transfer_if_not_empty();
98 }
99
100 #[inline]
103 pub fn direct(
104 &self,
105 to: &ManagedAddress<A>,
106 token: &EgldOrEsdtTokenIdentifier<A>,
107 nonce: u64,
108 amount: &BigUint<A>,
109 ) {
110 self.direct_with_gas_limit(to, token, nonce, amount, 0, Empty, &[]);
111 }
112
113 #[inline]
118 pub fn direct_non_zero(
119 &self,
120 to: &ManagedAddress<A>,
121 token: &EgldOrEsdtTokenIdentifier<A>,
122 nonce: u64,
123 amount: &BigUint<A>,
124 ) {
125 self.direct_non_zero_with_gas_limit(to, token, nonce, amount, 0, Empty, &[]);
126 }
127
128 #[allow(clippy::too_many_arguments)]
132 pub fn direct_esdt_with_gas_limit<D>(
133 &self,
134 to: &ManagedAddress<A>,
135 token_identifier: &TokenIdentifier<A>,
136 nonce: u64,
137 amount: &BigUint<A>,
138 gas: u64,
139 endpoint_name: D,
140 arguments: &[ManagedBuffer<A>],
141 ) where
142 D: Into<ManagedBuffer<A>>,
143 {
144 if nonce == 0 {
145 self.send_raw_wrapper().transfer_esdt_execute(
146 to,
147 token_identifier,
148 amount,
149 gas,
150 &endpoint_name.into(),
151 &arguments.into(),
152 );
153 } else {
154 self.send_raw_wrapper().transfer_esdt_nft_execute(
155 to,
156 token_identifier,
157 nonce,
158 amount,
159 gas,
160 &endpoint_name.into(),
161 &arguments.into(),
162 );
163 }
164 }
165
166 #[allow(clippy::too_many_arguments)]
172 pub fn direct_non_zero_esdt_with_gas_limit<D>(
173 &self,
174 to: &ManagedAddress<A>,
175 token_identifier: &TokenIdentifier<A>,
176 nonce: u64,
177 amount: &BigUint<A>,
178 gas: u64,
179 endpoint_name: D,
180 arguments: &[ManagedBuffer<A>],
181 ) where
182 D: Into<ManagedBuffer<A>>,
183 {
184 if amount == &0 {
185 return;
186 }
187 self.direct_esdt_with_gas_limit(
188 to,
189 token_identifier,
190 nonce,
191 amount,
192 gas,
193 endpoint_name,
194 arguments,
195 );
196 }
197
198 pub fn direct_esdt(
200 &self,
201 to: &ManagedAddress<A>,
202 token_identifier: &TokenIdentifier<A>,
203 token_nonce: u64,
204 amount: &BigUint<A>,
205 ) {
206 Tx::new_tx_from_sc()
207 .to(to)
208 .single_esdt(token_identifier, token_nonce, amount)
209 .transfer();
210 }
211
212 pub fn direct_non_zero_esdt_payment(
216 &self,
217 to: &ManagedAddress<A>,
218 payment: &EsdtTokenPayment<A>,
219 ) {
220 if payment.amount == 0 {
221 return;
222 }
223
224 self.direct_esdt(
225 to,
226 &payment.token_identifier,
227 payment.token_nonce,
228 &payment.amount,
229 );
230 }
231
232 #[allow(clippy::too_many_arguments)]
238 pub fn direct_with_gas_limit<D>(
239 &self,
240 to: &ManagedAddress<A>,
241 token: &EgldOrEsdtTokenIdentifier<A>,
242 nonce: u64,
243 amount: &BigUint<A>,
244 gas: u64,
245 endpoint_name: D,
246 arguments: &[ManagedBuffer<A>],
247 ) where
248 D: Into<ManagedBuffer<A>>,
249 {
250 if let Some(esdt_token_identifier) = token.as_esdt_option() {
251 self.direct_esdt_with_gas_limit(
252 to,
253 &esdt_token_identifier,
254 nonce,
255 amount,
256 gas,
257 endpoint_name,
258 arguments,
259 );
260 } else {
261 self.send_raw_wrapper().direct_egld_execute(
262 to,
263 amount,
264 gas,
265 &endpoint_name.into(),
266 &arguments.into(),
267 );
268 }
269 }
270 #[allow(clippy::too_many_arguments)]
278 pub fn direct_non_zero_with_gas_limit<D>(
279 &self,
280 to: &ManagedAddress<A>,
281 token: &EgldOrEsdtTokenIdentifier<A>,
282 nonce: u64,
283 amount: &BigUint<A>,
284 gas: u64,
285 endpoint_name: D,
286 arguments: &[ManagedBuffer<A>],
287 ) where
288 D: Into<ManagedBuffer<A>>,
289 {
290 if amount == &0 {
291 return;
292 }
293 self.direct_with_gas_limit(to, token, nonce, amount, gas, endpoint_name, arguments);
294 }
295
296 pub fn direct_multi(
298 &self,
299 to: &ManagedAddress<A>,
300 payments: &ManagedVec<A, EsdtTokenPayment<A>>,
301 ) {
302 Tx::new_tx_from_sc().to(to).payment(payments).transfer();
303 }
304
305 pub fn transfer_esdt_via_async_call(
314 &self,
315 to: ManagedAddress<A>,
316 token: TokenIdentifier<A>,
317 nonce: u64,
318 amount: BigUint<A>,
319 ) -> ! {
320 Tx::new_tx_from_sc()
321 .to(to)
322 .esdt((token, nonce, amount))
323 .async_call_and_exit()
324 }
325
326 pub fn transfer_esdt_non_zero_via_async_call(
336 &self,
337 to: ManagedAddress<A>,
338 token: TokenIdentifier<A>,
339 nonce: u64,
340 amount: BigUint<A>,
341 ) {
342 if amount == 0 {
343 return;
344 }
345 self.transfer_esdt_via_async_call(to, token, nonce, amount)
346 }
347
348 pub fn transfer_multiple_esdt_via_async_call(
350 &self,
351 to: ManagedAddress<A>,
352 payments: ManagedVec<A, EsdtTokenPayment<A>>,
353 ) -> ! {
354 Tx::new_tx_from_sc()
355 .to(to)
356 .payment(payments)
357 .async_call_and_exit()
358 }
359
360 #[allow(clippy::type_complexity)]
362 pub fn claim_developer_rewards(
363 &self,
364 child_sc_address: ManagedAddress<A>,
365 ) -> Tx<
366 TxScEnv<A>,
367 (),
368 ManagedAddress<A>,
369 NotPayable,
370 (),
371 FunctionCall<A>,
372 OriginalResultMarker<()>,
373 > {
374 Tx::new_tx_from_sc()
375 .to(child_sc_address)
376 .typed(system_proxy::UserBuiltinProxy)
377 .claim_developer_rewards()
378 }
379
380 #[allow(clippy::type_complexity)]
382 pub fn change_owner_address(
383 &self,
384 child_sc_address: ManagedAddress<A>,
385 new_owner: &ManagedAddress<A>,
386 ) -> Tx<
387 TxScEnv<A>,
388 (),
389 ManagedAddress<A>,
390 NotPayable,
391 (),
392 FunctionCall<A>,
393 OriginalResultMarker<()>,
394 > {
395 Tx::new_tx_from_sc()
396 .to(child_sc_address)
397 .typed(system_proxy::UserBuiltinProxy)
398 .change_owner_address(new_owner)
399 }
400
401 pub fn call_local_esdt_built_in_function(
405 &self,
406 gas: u64,
407 endpoint_name: ManagedBuffer<A>,
408 arg_buffer: ManagedArgBuffer<A>,
409 ) -> ManagedVec<A, ManagedBuffer<A>> {
410 Tx::new_tx_from_sc()
411 .to(ToSelf)
412 .gas(gas)
413 .raw_call(endpoint_name)
414 .arguments_raw(arg_buffer)
415 .returns(ReturnsRawResult)
416 .sync_call()
417 }
418
419 pub fn esdt_local_mint(&self, token: &TokenIdentifier<A>, nonce: u64, amount: &BigUint<A>) {
428 Tx::new_tx_from_sc()
429 .to(ToSelf)
430 .gas(GasLeft)
431 .typed(system_proxy::UserBuiltinProxy)
432 .esdt_local_mint(token, nonce, amount)
433 .sync_call()
434 }
435
436 pub fn esdt_non_zero_local_mint(
446 &self,
447 token: &TokenIdentifier<A>,
448 nonce: u64,
449 amount: &BigUint<A>,
450 ) {
451 if amount == &0 {
452 return;
453 }
454 self.esdt_local_mint(token, nonce, amount);
455 }
456
457 pub fn esdt_local_burn(&self, token: &TokenIdentifier<A>, nonce: u64, amount: &BigUint<A>) {
462 Tx::new_tx_from_sc()
463 .to(ToSelf)
464 .gas(GasLeft)
465 .typed(system_proxy::UserBuiltinProxy)
466 .esdt_local_burn(token, nonce, amount)
467 .sync_call()
468 }
469
470 pub fn esdt_non_zero_local_burn(
477 &self,
478 token: &TokenIdentifier<A>,
479 nonce: u64,
480 amount: &BigUint<A>,
481 ) {
482 if amount == &0 {
483 return;
484 }
485 self.esdt_local_burn(token, nonce, amount);
486 }
487
488 pub fn esdt_local_burn_multi(&self, payments: &ManagedVec<A, EsdtTokenPayment<A>>) {
492 for payment in payments {
493 self.esdt_local_burn(
494 &payment.token_identifier,
495 payment.token_nonce,
496 &payment.amount,
497 );
498 }
499 }
500
501 pub fn esdt_non_zero_local_burn_multi(&self, payments: &ManagedVec<A, EsdtTokenPayment<A>>) {
507 for payment in payments {
508 self.esdt_non_zero_local_burn(
509 &payment.token_identifier,
510 payment.token_nonce,
511 &payment.amount,
512 );
513 }
514 }
515
516 #[allow(clippy::too_many_arguments)]
525 pub fn esdt_nft_create<T: codec::TopEncode>(
526 &self,
527 token: &TokenIdentifier<A>,
528 amount: &BigUint<A>,
529 name: &ManagedBuffer<A>,
530 royalties: &BigUint<A>,
531 hash: &ManagedBuffer<A>,
532 attributes: &T,
533 uris: &ManagedVec<A, ManagedBuffer<A>>,
534 ) -> u64 {
535 Tx::new_tx_from_sc()
536 .to(ToSelf)
537 .gas(GasLeft)
538 .typed(system_proxy::UserBuiltinProxy)
539 .esdt_nft_create(token, amount, name, royalties, hash, attributes, uris)
540 .returns(ReturnsResult)
541 .sync_call()
542 }
543
544 #[allow(clippy::too_many_arguments)]
555 pub fn esdt_non_zero_nft_create<T: codec::TopEncode>(
556 &self,
557 token: &TokenIdentifier<A>,
558 amount: &BigUint<A>,
559 name: &ManagedBuffer<A>,
560 royalties: &BigUint<A>,
561 hash: &ManagedBuffer<A>,
562 attributes: &T,
563 uris: &ManagedVec<A, ManagedBuffer<A>>,
564 ) -> u64 {
565 if amount == &0 {
566 0
567 } else {
568 self.esdt_nft_create(token, amount, name, royalties, hash, attributes, uris)
569 }
570 }
571
572 #[inline]
576 pub fn esdt_nft_create_compact<T: codec::TopEncode>(
577 &self,
578 token: &TokenIdentifier<A>,
579 amount: &BigUint<A>,
580 attributes: &T,
581 ) -> u64 {
582 self.esdt_nft_create_compact_named(token, amount, &ManagedBuffer::new(), attributes)
583 }
584
585 pub fn esdt_nft_create_compact_named<T: codec::TopEncode>(
589 &self,
590 token: &TokenIdentifier<A>,
591 amount: &BigUint<A>,
592 name: &ManagedBuffer<A>,
593 attributes: &T,
594 ) -> u64 {
595 let big_zero = BigUint::zero();
596 let empty_buffer = ManagedBuffer::new();
597
598 let empty_vec = unsafe { ManagedRef::wrap_handle(empty_buffer.get_handle()) };
600
601 self.esdt_nft_create(
602 token,
603 amount,
604 name,
605 &big_zero,
606 &empty_buffer,
607 attributes,
608 &empty_vec,
609 )
610 }
611
612 #[inline]
618 pub fn esdt_non_zero_nft_create_compact<T: codec::TopEncode>(
619 &self,
620 token: &TokenIdentifier<A>,
621 amount: &BigUint<A>,
622 attributes: &T,
623 ) -> u64 {
624 self.esdt_non_zero_nft_create_compact_named(
625 token,
626 amount,
627 &ManagedBuffer::new(),
628 attributes,
629 )
630 }
631
632 pub fn esdt_non_zero_nft_create_compact_named<T: codec::TopEncode>(
638 &self,
639 token: &TokenIdentifier<A>,
640 amount: &BigUint<A>,
641 name: &ManagedBuffer<A>,
642 attributes: &T,
643 ) -> u64 {
644 if amount == &0 {
645 0
646 } else {
647 self.esdt_nft_create_compact_named(token, amount, name, attributes)
648 }
649 }
650
651 #[allow(clippy::too_many_arguments)]
655 pub fn sell_nft(
656 &self,
657 nft_id: &TokenIdentifier<A>,
658 nft_nonce: u64,
659 nft_amount: &BigUint<A>,
660 buyer: &ManagedAddress<A>,
661 payment_token: &EgldOrEsdtTokenIdentifier<A>,
662 payment_nonce: u64,
663 payment_amount: &BigUint<A>,
664 ) -> BigUint<A> {
665 let nft_token_data = BlockchainWrapper::<A>::new().get_esdt_token_data(
666 &BlockchainWrapper::<A>::new().get_sc_address(),
667 nft_id,
668 nft_nonce,
669 );
670 let royalties_amount = payment_amount.clone() * nft_token_data.royalties / PERCENTAGE_TOTAL;
671
672 self.send_raw_wrapper().transfer_esdt_nft_execute(
673 buyer,
674 nft_id,
675 nft_nonce,
676 nft_amount,
677 0,
678 &ManagedBuffer::new(),
679 &ManagedArgBuffer::new(),
680 );
681
682 if royalties_amount > 0u32 {
683 self.direct(
684 &nft_token_data.creator,
685 payment_token,
686 payment_nonce,
687 &royalties_amount,
688 );
689
690 payment_amount.clone() - royalties_amount
691 } else {
692 payment_amount.clone()
693 }
694 }
695
696 #[allow(clippy::too_many_arguments)]
702 pub fn sell_nft_non_zero(
703 &self,
704 nft_id: &TokenIdentifier<A>,
705 nft_nonce: u64,
706 nft_amount: &BigUint<A>,
707 buyer: &ManagedAddress<A>,
708 payment_token: &EgldOrEsdtTokenIdentifier<A>,
709 payment_nonce: u64,
710 payment_amount: &BigUint<A>,
711 ) -> BigUint<A> {
712 if nft_amount == &0 || payment_amount == &0 {
713 payment_amount.clone()
714 } else {
715 self.sell_nft(
716 nft_id,
717 nft_nonce,
718 nft_amount,
719 buyer,
720 payment_token,
721 payment_nonce,
722 payment_amount,
723 )
724 }
725 }
726
727 pub fn nft_add_uri(
729 &self,
730 token_id: &TokenIdentifier<A>,
731 nft_nonce: u64,
732 new_uri: ManagedBuffer<A>,
733 ) {
734 self.nft_add_multiple_uri(token_id, nft_nonce, &ManagedVec::from_single_item(new_uri));
735 }
736
737 pub fn nft_add_multiple_uri(
739 &self,
740 token_id: &TokenIdentifier<A>,
741 nft_nonce: u64,
742 new_uris: &ManagedVec<A, ManagedBuffer<A>>,
743 ) {
744 if new_uris.is_empty() {
745 return;
746 }
747
748 Tx::new_tx_from_sc()
749 .to(ToSelf)
750 .gas(GasLeft)
751 .typed(system_proxy::UserBuiltinProxy)
752 .nft_add_multiple_uri(token_id, nft_nonce, new_uris)
753 .sync_call()
754 }
755
756 pub fn nft_update_attributes<T: codec::TopEncode>(
758 &self,
759 token_id: &TokenIdentifier<A>,
760 nft_nonce: u64,
761 new_attributes: &T,
762 ) {
763 Tx::new_tx_from_sc()
764 .to(ToSelf)
765 .gas(GasLeft)
766 .typed(system_proxy::UserBuiltinProxy)
767 .nft_update_attributes(token_id, nft_nonce, new_attributes)
768 .sync_call()
769 }
770
771 pub fn esdt_modify_royalties(
773 &self,
774 token_id: &TokenIdentifier<A>,
775 nonce: u64,
776 new_royalty: u64,
777 ) {
778 Tx::new_tx_from_sc()
779 .to(ToSelf)
780 .gas(GasLeft)
781 .typed(system_proxy::UserBuiltinProxy)
782 .esdt_modify_royalties(token_id, nonce, new_royalty)
783 .sync_call()
784 }
785
786 pub fn esdt_nft_set_new_uris(
788 &self,
789 token_id: &TokenIdentifier<A>,
790 nonce: u64,
791 uris: &ManagedVec<A, ManagedBuffer<A>>,
792 ) {
793 Tx::new_tx_from_sc()
794 .to(ToSelf)
795 .gas(GasLeft)
796 .typed(system_proxy::UserBuiltinProxy)
797 .esdt_nft_set_new_uris(token_id, nonce, uris)
798 .sync_call()
799 }
800
801 pub fn esdt_nft_modify_creator(&self, token_id: &TokenIdentifier<A>, nonce: u64) {
803 Tx::new_tx_from_sc()
804 .to(ToSelf)
805 .gas(GasLeft)
806 .typed(system_proxy::UserBuiltinProxy)
807 .esdt_nft_modify_creator(token_id, nonce)
808 .sync_call()
809 }
810
811 #[allow(clippy::too_many_arguments)]
813 pub fn esdt_metadata_recreate<T: codec::TopEncode>(
814 &self,
815 token_id: TokenIdentifier<A>,
816 nonce: u64,
817 name: ManagedBuffer<A>,
818 royalties: u64,
819 hash: ManagedBuffer<A>,
820 new_attributes: &T,
821 uris: ManagedVec<A, ManagedBuffer<A>>,
822 ) {
823 Tx::new_tx_from_sc()
824 .to(ToSelf)
825 .gas(GasLeft)
826 .typed(system_proxy::UserBuiltinProxy)
827 .esdt_metadata_recreate(token_id, nonce, name, royalties, hash, new_attributes, uris)
828 .sync_call()
829 }
830
831 #[allow(clippy::too_many_arguments)]
833 pub fn esdt_metadata_update<T: codec::TopEncode>(
834 &self,
835 token_id: TokenIdentifier<A>,
836 nonce: u64,
837 name: ManagedBuffer<A>,
838 royalties: u64,
839 hash: ManagedBuffer<A>,
840 new_attributes: &T,
841 uris: ManagedVec<A, ManagedBuffer<A>>,
842 ) {
843 Tx::new_tx_from_sc()
844 .to(ToSelf)
845 .gas(GasLeft)
846 .typed(system_proxy::UserBuiltinProxy)
847 .esdt_metadata_update(token_id, nonce, name, royalties, hash, new_attributes, uris)
848 .sync_call()
849 }
850}