multiversx_chain_vm/builtin_functions/
builtin_func_container.rs1use super::{
2 BuiltinFunctionEsdtTransferInfo,
3 builtin_func_trait::BuiltinFunction,
4 esdt_nft::{
5 ESDTLocalBurn, ESDTLocalMint, ESDTMetadataRecreate, ESDTMetadataUpdate, ESDTNftAddQuantity,
6 ESDTNftAddUri, ESDTNftBurn, ESDTNftCreate, ESDTNftUpdateAttributes,
7 },
8 general::{ChangeOwner, ClaimDeveloperRewards, DeleteUsername, SetUsername, UpgradeContract},
9 transfer::{ESDTMultiTransfer, ESDTNftTransfer, ESDTTransfer},
10};
11
12use crate::{
13 host::context::{BlockchainUpdate, TxCache, TxInput, TxResult},
14 host::runtime::{RuntimeInstanceCallLambda, RuntimeRef},
15 types::EsdtLocalRole,
16};
17
18use crate::chain_core::builtin_func_names::*;
19
20#[derive(Default)]
24pub struct BuiltinFunctionContainer;
25
26impl BuiltinFunctionContainer {
27 pub fn execute_builtin_function_or_else<F, Else>(
31 &self,
32 runtime: &RuntimeRef,
33 tx_input: TxInput,
34 tx_cache: TxCache,
35 f: F,
36 or_else: Else,
37 ) -> (TxResult, BlockchainUpdate)
38 where
39 F: RuntimeInstanceCallLambda,
40 Else: FnOnce(TxInput, TxCache, F) -> (TxResult, BlockchainUpdate),
41 {
42 BuiltinFunctionCall::new(runtime, tx_input, tx_cache).execute_or_else(f, or_else)
43 }
44
45 pub fn extract_token_transfers(&self, tx_input: &TxInput) -> BuiltinFunctionEsdtTransferInfo {
47 match tx_input.func_name.as_str() {
48 ESDT_MULTI_TRANSFER_FUNC_NAME => bf_extract_transfers(ESDTMultiTransfer, tx_input),
49 ESDT_NFT_TRANSFER_FUNC_NAME => bf_extract_transfers(ESDTNftTransfer, tx_input),
50 ESDT_TRANSFER_FUNC_NAME => bf_extract_transfers(ESDTTransfer, tx_input),
51 _ => BuiltinFunctionEsdtTransferInfo::empty(tx_input),
52 }
53 }
54}
55
56fn bf_extract_transfers<B>(builtin_func: B, tx_input: &TxInput) -> BuiltinFunctionEsdtTransferInfo
57where
58 B: BuiltinFunction,
59{
60 builtin_func.extract_esdt_transfers(tx_input)
61}
62
63struct BuiltinFunctionCall<'a> {
66 runtime: &'a RuntimeRef,
67 tx_input: TxInput,
68 tx_cache: TxCache,
69}
70
71impl<'a> BuiltinFunctionCall<'a> {
72 pub fn new(runtime: &'a RuntimeRef, tx_input: TxInput, tx_cache: TxCache) -> Self {
73 BuiltinFunctionCall {
74 runtime,
75 tx_input,
76 tx_cache,
77 }
78 }
79
80 pub fn execute_or_else<F, Else>(self, f: F, or_else: Else) -> (TxResult, BlockchainUpdate)
81 where
82 F: RuntimeInstanceCallLambda,
83 Else: FnOnce(TxInput, TxCache, F) -> (TxResult, BlockchainUpdate),
84 {
85 match self.tx_input.func_name.as_str() {
86 ESDT_LOCAL_MINT_FUNC_NAME => {
87 self.check_role_and_execute(EsdtLocalRole::Mint, ESDTLocalMint, f)
88 }
89 ESDT_LOCAL_BURN_FUNC_NAME => {
90 self.check_role_and_execute(EsdtLocalRole::Burn, ESDTLocalBurn, f)
91 }
92 ESDT_NFT_CREATE_FUNC_NAME => {
93 self.check_role_and_execute(EsdtLocalRole::NftCreate, ESDTNftCreate, f)
94 }
95 ESDT_NFT_BURN_FUNC_NAME => {
96 self.check_role_and_execute(EsdtLocalRole::NftBurn, ESDTNftBurn, f)
97 }
98 ESDT_NFT_ADD_QUANTITY_FUNC_NAME => {
99 self.check_role_and_execute(EsdtLocalRole::NftAddQuantity, ESDTNftAddQuantity, f)
100 }
101 ESDT_NFT_ADD_URI_FUNC_NAME => {
102 self.check_role_and_execute(EsdtLocalRole::NftAddUri, ESDTNftAddUri, f)
103 }
104 ESDT_NFT_UPDATE_ATTRIBUTES_FUNC_NAME => self.check_role_and_execute(
105 EsdtLocalRole::NftUpdateAttributes,
106 ESDTNftUpdateAttributes,
107 f,
108 ),
109 ESDT_METADATA_RECREATE_FUNC_NAME => {
110 self.check_role_and_execute(EsdtLocalRole::NftRecreate, ESDTMetadataRecreate, f)
111 }
112 ESDT_METADATA_UPDATE_FUNC_NAME => {
113 self.check_role_and_execute(EsdtLocalRole::NftRecreate, ESDTMetadataUpdate, f)
114 }
115
116 ESDT_MULTI_TRANSFER_FUNC_NAME => self.execute_bf(ESDTMultiTransfer, f),
117 ESDT_NFT_TRANSFER_FUNC_NAME => self.execute_bf(ESDTNftTransfer, f),
118 ESDT_TRANSFER_FUNC_NAME => self.execute_bf(ESDTTransfer, f),
119 CHANGE_OWNER_BUILTIN_FUNC_NAME => self.execute_bf(ChangeOwner, f),
120 CLAIM_DEVELOPER_REWARDS_FUNC_NAME => self.execute_bf(ClaimDeveloperRewards, f),
121 SET_USERNAME_FUNC_NAME => self.execute_bf(SetUsername, f),
122 DELETE_USERNAME_FUNC_NAME => self.execute_bf(DeleteUsername, f),
123 UPGRADE_CONTRACT_FUNC_NAME => self.execute_bf(UpgradeContract, f),
124 MIGRATE_USERNAME_FUNC_NAME => {
125 panic!("builtin function {MIGRATE_USERNAME_FUNC_NAME} was dropped")
126 }
127 _ => or_else(self.tx_input, self.tx_cache, f),
128 }
129 }
130
131 fn execute_bf<B, F>(self, builtin_func: B, f: F) -> (TxResult, BlockchainUpdate)
132 where
133 B: BuiltinFunction,
134 F: RuntimeInstanceCallLambda,
135 {
136 builtin_func.execute(self.tx_input, self.tx_cache, self.runtime, f)
137 }
138
139 fn check_role_and_execute<B, F>(
140 self,
141 role: EsdtLocalRole,
142 builtin_func: B,
143 f: F,
144 ) -> (TxResult, BlockchainUpdate)
145 where
146 B: BuiltinFunction,
147 F: RuntimeInstanceCallLambda,
148 {
149 if check_allowed_to_execute(role, &self.tx_input, &self.tx_cache) {
150 self.execute_bf(builtin_func, f)
151 } else {
152 (
153 TxResult::from_vm_error("action is not allowed"),
154 BlockchainUpdate::empty(),
155 )
156 }
157 }
158}
159
160fn check_allowed_to_execute(role: EsdtLocalRole, tx_input: &TxInput, tx_cache: &TxCache) -> bool {
161 let token_identifier = tx_input.args[0].clone();
162 let available_roles = tx_cache.with_account_mut(&tx_input.to, |account| {
163 account.esdt.get_roles(&token_identifier)
164 });
165 available_roles
166 .iter()
167 .any(|available_role| available_role.as_slice() == role.name().as_bytes())
168}