multiversx_chain_vm/builtin_functions/
builtin_func_container.rs1use super::{
2 builtin_func_trait::BuiltinFunction,
3 esdt_nft::{
4 ESDTLocalBurn, ESDTLocalMint, ESDTNftAddQuantity, ESDTNftAddUri, ESDTNftBurn,
5 ESDTNftCreate, ESDTNftUpdateAttributes,
6 },
7 general::{ChangeOwner, ClaimDeveloperRewards, DeleteUsername, SetUsername, UpgradeContract},
8 transfer::{ESDTMultiTransfer, ESDTNftTransfer, ESDTTransfer},
9 BuiltinFunctionEsdtTransferInfo,
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
110 ESDT_MULTI_TRANSFER_FUNC_NAME => self.execute_bf(ESDTMultiTransfer, f),
111 ESDT_NFT_TRANSFER_FUNC_NAME => self.execute_bf(ESDTNftTransfer, f),
112 ESDT_TRANSFER_FUNC_NAME => self.execute_bf(ESDTTransfer, f),
113 CHANGE_OWNER_BUILTIN_FUNC_NAME => self.execute_bf(ChangeOwner, f),
114 CLAIM_DEVELOPER_REWARDS_FUNC_NAME => self.execute_bf(ClaimDeveloperRewards, f),
115 SET_USERNAME_FUNC_NAME => self.execute_bf(SetUsername, f),
116 DELETE_USERNAME_FUNC_NAME => self.execute_bf(DeleteUsername, f),
117 UPGRADE_CONTRACT_FUNC_NAME => self.execute_bf(UpgradeContract, f),
118 MIGRATE_USERNAME_FUNC_NAME => {
119 panic!("builtin function {MIGRATE_USERNAME_FUNC_NAME} was dropped")
120 }
121 _ => or_else(self.tx_input, self.tx_cache, f),
122 }
123 }
124
125 fn execute_bf<B, F>(self, builtin_func: B, f: F) -> (TxResult, BlockchainUpdate)
126 where
127 B: BuiltinFunction,
128 F: RuntimeInstanceCallLambda,
129 {
130 builtin_func.execute(self.tx_input, self.tx_cache, self.runtime, f)
131 }
132
133 fn check_role_and_execute<B, F>(
134 self,
135 role: EsdtLocalRole,
136 builtin_func: B,
137 f: F,
138 ) -> (TxResult, BlockchainUpdate)
139 where
140 B: BuiltinFunction,
141 F: RuntimeInstanceCallLambda,
142 {
143 if check_allowed_to_execute(role, &self.tx_input, &self.tx_cache) {
144 self.execute_bf(builtin_func, f)
145 } else {
146 (
147 TxResult::from_vm_error("action is not allowed"),
148 BlockchainUpdate::empty(),
149 )
150 }
151 }
152}
153
154fn check_allowed_to_execute(role: EsdtLocalRole, tx_input: &TxInput, tx_cache: &TxCache) -> bool {
155 let token_identifier = tx_input.args[0].clone();
156 let available_roles = tx_cache.with_account_mut(&tx_input.to, |account| {
157 account.esdt.get_roles(&token_identifier)
158 });
159 available_roles
160 .iter()
161 .any(|available_role| available_role.as_slice() == role.name().as_bytes())
162}