1use crate::consts::*;
2use crate::Config;
3use evm_core::ExitError;
4use primitive_types::{H256, U256};
5
6pub fn call_extra_check(gas: U256, after_gas: u64, config: &Config) -> Result<(), ExitError> {
7 if config.err_on_call_with_more_gas && U256::from(after_gas) < gas {
8 Err(ExitError::OutOfGas)
9 } else {
10 Ok(())
11 }
12}
13
14pub fn suicide_refund(already_removed: bool) -> i64 {
15 if already_removed {
16 0
17 } else {
18 R_SUICIDE
19 }
20}
21
22#[allow(clippy::collapsible_else_if)]
23pub fn sstore_refund(original: H256, current: H256, new: H256, config: &Config) -> i64 {
24 if config.sstore_gas_metering {
25 if current == new {
26 0
27 } else {
28 if original == current && new == H256::default() {
29 config.refund_sstore_clears
30 } else {
31 let mut refund = 0;
32
33 if original != H256::default() {
34 if current == H256::default() {
35 refund -= config.refund_sstore_clears;
36 } else if new == H256::default() {
37 refund += config.refund_sstore_clears;
38 }
39 }
40
41 if original == new {
42 if original == H256::default() {
43 refund += (config.gas_sstore_set - config.gas_sload) as i64;
44 } else {
45 refund += (config.gas_sstore_reset - config.gas_sload) as i64;
46 }
47 }
48
49 refund
50 }
51 }
52 } else {
53 if current != H256::default() && new == H256::default() {
54 config.refund_sstore_clears
55 } else {
56 0
57 }
58 }
59}
60
61pub fn create2_cost(len: U256) -> Result<u64, ExitError> {
62 let base = U256::from(G_CREATE);
63 let sha_addup_base = len / U256::from(32)
65 + if len % U256::from(32) == U256::zero() {
66 U256::zero()
67 } else {
68 U256::one()
69 };
70 let sha_addup = U256::from(G_SHA3WORD)
71 .checked_mul(sha_addup_base)
72 .ok_or(ExitError::OutOfGas)?;
73 let gas = base.checked_add(sha_addup).ok_or(ExitError::OutOfGas)?;
74
75 if gas > U256::from(u64::MAX) {
76 return Err(ExitError::OutOfGas);
77 }
78
79 Ok(gas.as_u64())
80}
81
82pub fn exp_cost(power: U256, config: &Config) -> Result<u64, ExitError> {
83 if power == U256::zero() {
84 Ok(G_EXP)
85 } else {
86 let gas = U256::from(G_EXP)
87 .checked_add(
88 U256::from(config.gas_expbyte)
89 .checked_mul(U256::from(crate::utils::log2floor(power) / 8 + 1))
90 .ok_or(ExitError::OutOfGas)?,
91 )
92 .ok_or(ExitError::OutOfGas)?;
93
94 if gas > U256::from(u64::MAX) {
95 return Err(ExitError::OutOfGas);
96 }
97
98 Ok(gas.as_u64())
99 }
100}
101
102pub fn verylowcopy_cost(len: U256) -> Result<u64, ExitError> {
103 let wordd = len / U256::from(32);
104 let wordr = len % U256::from(32);
105
106 let gas = U256::from(G_VERYLOW)
107 .checked_add(
108 U256::from(G_COPY)
109 .checked_mul(if wordr == U256::zero() {
110 wordd
111 } else {
112 wordd + U256::one()
113 })
114 .ok_or(ExitError::OutOfGas)?,
115 )
116 .ok_or(ExitError::OutOfGas)?;
117
118 if gas > U256::from(u64::MAX) {
119 return Err(ExitError::OutOfGas);
120 }
121
122 Ok(gas.as_u64())
123}
124
125pub fn extcodecopy_cost(len: U256, is_cold: bool, config: &Config) -> Result<u64, ExitError> {
126 let wordd = len / U256::from(32);
127 let wordr = len % U256::from(32);
128 let gas = U256::from(address_access_cost(is_cold, config.gas_ext_code, config))
129 .checked_add(
130 U256::from(G_COPY)
131 .checked_mul(if wordr == U256::zero() {
132 wordd
133 } else {
134 wordd + U256::one()
135 })
136 .ok_or(ExitError::OutOfGas)?,
137 )
138 .ok_or(ExitError::OutOfGas)?;
139
140 if gas > U256::from(u64::MAX) {
141 return Err(ExitError::OutOfGas);
142 }
143
144 Ok(gas.as_u64())
145}
146
147pub fn log_cost(n: u8, len: U256) -> Result<u64, ExitError> {
148 let gas = U256::from(G_LOG)
149 .checked_add(
150 U256::from(G_LOGDATA)
151 .checked_mul(len)
152 .ok_or(ExitError::OutOfGas)?,
153 )
154 .ok_or(ExitError::OutOfGas)?
155 .checked_add(U256::from(G_LOGTOPIC * n as u64))
156 .ok_or(ExitError::OutOfGas)?;
157
158 if gas > U256::from(u64::MAX) {
159 return Err(ExitError::OutOfGas);
160 }
161
162 Ok(gas.as_u64())
163}
164
165pub fn sha3_cost(len: U256) -> Result<u64, ExitError> {
166 let wordd = len / U256::from(32);
167 let wordr = len % U256::from(32);
168
169 let gas = U256::from(G_SHA3)
170 .checked_add(
171 U256::from(G_SHA3WORD)
172 .checked_mul(if wordr == U256::zero() {
173 wordd
174 } else {
175 wordd + U256::one()
176 })
177 .ok_or(ExitError::OutOfGas)?,
178 )
179 .ok_or(ExitError::OutOfGas)?;
180
181 if gas > U256::from(u64::MAX) {
182 return Err(ExitError::OutOfGas);
183 }
184
185 Ok(gas.as_u64())
186}
187
188pub fn sload_cost(is_cold: bool, config: &Config) -> u64 {
189 if config.increase_state_access_gas {
190 if is_cold {
191 config.gas_sload_cold
192 } else {
193 config.gas_storage_read_warm
194 }
195 } else {
196 config.gas_sload
197 }
198}
199
200#[allow(clippy::collapsible_else_if)]
201pub fn sstore_cost(
202 original: H256,
203 current: H256,
204 new: H256,
205 gas: u64,
206 is_cold: bool,
207 config: &Config,
208) -> Result<u64, ExitError> {
209 let gas_cost = if config.estimate {
210 config.gas_sstore_set
211 } else {
212 if config.sstore_gas_metering {
213 if config.sstore_revert_under_stipend && gas <= config.call_stipend {
214 return Err(ExitError::OutOfGas);
215 }
216
217 if new == current {
218 config.gas_sload
219 } else {
220 if original == current {
221 if original == H256::zero() {
222 config.gas_sstore_set
223 } else {
224 config.gas_sstore_reset
225 }
226 } else {
227 config.gas_sload
228 }
229 }
230 } else {
231 if current == H256::zero() && new != H256::zero() {
232 config.gas_sstore_set
233 } else {
234 config.gas_sstore_reset
235 }
236 }
237 };
238 Ok(
239 if is_cold {
241 gas_cost + config.gas_sload_cold
242 } else {
243 gas_cost
244 },
245 )
246}
247
248pub fn tload_cost(config: &Config) -> Result<u64, ExitError> {
249 Ok(config.gas_storage_read_warm)
250}
251
252pub fn tstore_cost(config: &Config) -> Result<u64, ExitError> {
253 Ok(config.gas_storage_read_warm)
254}
255
256pub fn suicide_cost(value: U256, is_cold: bool, target_exists: bool, config: &Config) -> u64 {
257 let eip161 = !config.empty_considered_exists;
258 let should_charge_topup = if eip161 {
259 value != U256::zero() && !target_exists
260 } else {
261 !target_exists
262 };
263
264 let suicide_gas_topup = if should_charge_topup {
265 config.gas_suicide_new_account
266 } else {
267 0
268 };
269
270 let mut gas = config.gas_suicide + suicide_gas_topup;
271 if config.increase_state_access_gas && is_cold {
272 gas += config.gas_account_access_cold
273 }
274 gas
275}
276
277pub fn call_cost(
278 value: U256,
279 is_cold: bool,
280 is_call_or_callcode: bool,
281 is_call_or_staticcall: bool,
282 new_account: bool,
283 config: &Config,
284) -> u64 {
285 let transfers_value = value != U256::default();
286 address_access_cost(is_cold, config.gas_call, config)
287 + xfer_cost(is_call_or_callcode, transfers_value)
288 + new_cost(is_call_or_staticcall, new_account, transfers_value, config)
289}
290
291pub fn address_access_cost(is_cold: bool, regular_value: u64, config: &Config) -> u64 {
292 if config.increase_state_access_gas {
293 if is_cold {
294 config.gas_account_access_cold
295 } else {
296 config.gas_storage_read_warm
297 }
298 } else {
299 regular_value
300 }
301}
302
303fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 {
304 if is_call_or_callcode && transfers_value {
305 G_CALLVALUE
306 } else {
307 0
308 }
309}
310
311fn new_cost(
312 is_call_or_staticcall: bool,
313 new_account: bool,
314 transfers_value: bool,
315 config: &Config,
316) -> u64 {
317 let eip161 = !config.empty_considered_exists;
318 if is_call_or_staticcall {
319 if eip161 {
320 if transfers_value && new_account {
321 G_NEWACCOUNT
322 } else {
323 0
324 }
325 } else if new_account {
326 G_NEWACCOUNT
327 } else {
328 0
329 }
330 } else {
331 0
332 }
333}