multiversx_chain_vm/tx_mock/
tx_cache_balance_util.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use multiversx_chain_core::EGLD_000000_TOKEN_IDENTIFIER;
use num_bigint::BigUint;

use crate::{
    tx_execution::is_system_sc_address, tx_mock::TxPanic, types::VMAddress,
    world_mock::EsdtInstanceMetadata,
};

use super::TxCache;

impl TxCache {
    pub fn subtract_egld_balance(
        &self,
        address: &VMAddress,
        call_value: &BigUint,
    ) -> Result<(), TxPanic> {
        self.with_account_mut(address, |account| {
            if call_value > &account.egld_balance {
                return Err(TxPanic::vm_error("failed transfer (insufficient funds)"));
            }
            account.egld_balance -= call_value;
            Ok(())
        })
    }

    pub fn subtract_tx_gas(&self, address: &VMAddress, gas_limit: u64, gas_price: u64) {
        self.with_account_mut(address, |account| {
            let gas_cost = BigUint::from(gas_limit) * BigUint::from(gas_price);
            assert!(
                account.egld_balance >= gas_cost,
                "Not enough balance to pay gas upfront"
            );
            account.egld_balance -= &gas_cost;
        });
    }

    pub fn increase_egld_balance(&self, address: &VMAddress, amount: &BigUint) {
        self.with_account_mut(address, |account| {
            account.egld_balance += amount;
        });
    }

    pub fn subtract_esdt_balance(
        &self,
        address: &VMAddress,
        esdt_token_identifier: &[u8],
        nonce: u64,
        value: &BigUint,
    ) -> Result<EsdtInstanceMetadata, TxPanic> {
        self.with_account_mut(address, |account| {
            let esdt_data_map = &mut account.esdt;
            let esdt_data = esdt_data_map
                .get_mut_by_identifier(esdt_token_identifier)
                .ok_or_else(err_insufficient_funds)?;

            let esdt_instances = &mut esdt_data.instances;
            let esdt_instance = esdt_instances
                .get_mut_by_nonce(nonce)
                .ok_or_else(err_insufficient_funds)?;

            let esdt_balance = &mut esdt_instance.balance;
            if &*esdt_balance < value {
                return Err(err_insufficient_funds());
            }

            *esdt_balance -= value;

            Ok(esdt_instance.metadata.clone())
        })
    }

    pub fn increase_esdt_balance(
        &self,
        address: &VMAddress,
        esdt_token_identifier: &[u8],
        nonce: u64,
        value: &BigUint,
        esdt_metadata: EsdtInstanceMetadata,
    ) {
        self.with_account_mut(address, |account| {
            account.esdt.increase_balance(
                esdt_token_identifier.to_vec(),
                nonce,
                value,
                esdt_metadata,
            );
        });
    }

    pub fn transfer_egld_balance(
        &self,
        from: &VMAddress,
        to: &VMAddress,
        value: &BigUint,
    ) -> Result<(), TxPanic> {
        if !is_system_sc_address(from) {
            self.subtract_egld_balance(from, value)?;
        }
        if !is_system_sc_address(to) {
            self.increase_egld_balance(to, value);
        }
        Ok(())
    }

    pub fn transfer_esdt_balance(
        &self,
        from: &VMAddress,
        to: &VMAddress,
        esdt_token_identifier: &[u8],
        nonce: u64,
        value: &BigUint,
    ) -> Result<(), TxPanic> {
        if esdt_token_identifier == EGLD_000000_TOKEN_IDENTIFIER.as_bytes() {
            return self.transfer_egld_balance(from, to, value);
        }

        if !is_system_sc_address(from) && !is_system_sc_address(to) {
            let metadata = self.subtract_esdt_balance(from, esdt_token_identifier, nonce, value)?;
            self.increase_esdt_balance(to, esdt_token_identifier, nonce, value, metadata);
        }
        Ok(())
    }
}

fn err_insufficient_funds() -> TxPanic {
    TxPanic::vm_error("insufficient funds")
}