multiversx_chain_vm/host/context/
tx_cache_balance_util.rs

1use multiversx_chain_core::EGLD_000000_TOKEN_IDENTIFIER;
2use num_bigint::BigUint;
3
4use crate::{
5    blockchain::state::EsdtInstanceMetadata, host::context::TxPanic,
6    system_sc::is_system_sc_address, types::VMAddress,
7};
8
9use super::TxCache;
10
11impl TxCache {
12    pub fn subtract_egld_balance(
13        &self,
14        address: &VMAddress,
15        call_value: &BigUint,
16    ) -> Result<(), TxPanic> {
17        self.with_account_mut(address, |account| {
18            if call_value > &account.egld_balance {
19                return Err(TxPanic::vm_error("failed transfer (insufficient funds)"));
20            }
21            account.egld_balance -= call_value;
22            Ok(())
23        })
24    }
25
26    pub fn subtract_tx_gas(&self, address: &VMAddress, gas_limit: u64, gas_price: u64) {
27        self.with_account_mut(address, |account| {
28            let gas_cost = BigUint::from(gas_limit) * BigUint::from(gas_price);
29            assert!(
30                account.egld_balance >= gas_cost,
31                "Not enough balance to pay gas upfront"
32            );
33            account.egld_balance -= &gas_cost;
34        });
35    }
36
37    pub fn increase_egld_balance(&self, address: &VMAddress, amount: &BigUint) {
38        self.with_account_mut(address, |account| {
39            account.egld_balance += amount;
40        });
41    }
42
43    pub fn subtract_esdt_balance(
44        &self,
45        address: &VMAddress,
46        esdt_token_identifier: &[u8],
47        nonce: u64,
48        value: &BigUint,
49    ) -> Result<EsdtInstanceMetadata, TxPanic> {
50        self.with_account_mut(address, |account| {
51            let esdt_data_map = &mut account.esdt;
52            let esdt_data = esdt_data_map
53                .get_mut_by_identifier(esdt_token_identifier)
54                .ok_or_else(err_insufficient_funds)?;
55
56            let esdt_instances = &mut esdt_data.instances;
57            let esdt_instance = esdt_instances
58                .get_mut_by_nonce(nonce)
59                .ok_or_else(err_insufficient_funds)?;
60
61            let esdt_balance = &mut esdt_instance.balance;
62            if &*esdt_balance < value {
63                return Err(err_insufficient_funds());
64            }
65
66            *esdt_balance -= value;
67
68            Ok(esdt_instance.metadata.clone())
69        })
70    }
71
72    pub fn increase_esdt_balance(
73        &self,
74        address: &VMAddress,
75        esdt_token_identifier: &[u8],
76        nonce: u64,
77        value: &BigUint,
78        esdt_metadata: EsdtInstanceMetadata,
79    ) {
80        self.with_account_mut(address, |account| {
81            account.esdt.increase_balance(
82                esdt_token_identifier.to_vec(),
83                nonce,
84                value,
85                esdt_metadata,
86            );
87        });
88    }
89
90    pub fn transfer_egld_balance(
91        &self,
92        from: &VMAddress,
93        to: &VMAddress,
94        value: &BigUint,
95    ) -> Result<(), TxPanic> {
96        if !is_system_sc_address(from) {
97            self.subtract_egld_balance(from, value)?;
98        }
99        if !is_system_sc_address(to) {
100            self.increase_egld_balance(to, value);
101        }
102        Ok(())
103    }
104
105    pub fn transfer_esdt_balance(
106        &self,
107        from: &VMAddress,
108        to: &VMAddress,
109        esdt_token_identifier: &[u8],
110        nonce: u64,
111        value: &BigUint,
112    ) -> Result<(), TxPanic> {
113        if esdt_token_identifier == EGLD_000000_TOKEN_IDENTIFIER.as_bytes() {
114            return self.transfer_egld_balance(from, to, value);
115        }
116
117        if !is_system_sc_address(from) && !is_system_sc_address(to) {
118            let metadata = self.subtract_esdt_balance(from, esdt_token_identifier, nonce, value)?;
119            self.increase_esdt_balance(to, esdt_token_identifier, nonce, value, metadata);
120        }
121        Ok(())
122    }
123}
124
125fn err_insufficient_funds() -> TxPanic {
126    TxPanic::vm_error("insufficient funds")
127}