11"""Ethereum Virtual Machine bytecode primitives and utilities."""
22
3- from functools import cache
43from typing import Any , List , Self , SupportsBytes , Type
54
65from pydantic import GetCoreSchemaHandler
@@ -35,6 +34,14 @@ class Bytecode:
3534 _name_ : str = ""
3635 _bytes_ : bytes
3736 _keccak_256_ : Hash | None = None
37+ _gas_cost_ : int | None = None
38+ _gas_cost_fork_ : Type [ForkOpcodeInterface ] | None = None
39+ _gas_cost_block_number_ : int | None = None
40+ _gas_cost_timestamp_ : int | None = None
41+ _refund_ : int | None = None
42+ _refund_fork_ : Type [ForkOpcodeInterface ] | None = None
43+ _refund_block_number_ : int | None = None
44+ _refund_timestamp_ : int | None = None
3845
3946 popped_stack_items : int
4047 pushed_stack_items : int
@@ -275,7 +282,22 @@ def gas_cost(
275282 timestamp : int = 0 ,
276283 ) -> int :
277284 """Use a fork object to calculate the gas used by this bytecode."""
278- return _bytecode_gas_cost (self , fork , block_number , timestamp )
285+ if (
286+ self ._gas_cost_ is None
287+ or self ._gas_cost_fork_ != fork
288+ or self ._gas_cost_block_number_ != block_number
289+ or self ._gas_cost_timestamp_ != timestamp
290+ ):
291+ self ._gas_cost_fork_ = fork
292+ self ._gas_cost_block_number_ = block_number
293+ self ._gas_cost_timestamp_ = timestamp
294+ opcode_gas_calculator = fork .opcode_gas_calculator (
295+ block_number = block_number , timestamp = timestamp
296+ )
297+ self ._gas_cost_ = 0
298+ for opcode in self .opcode_list :
299+ self ._gas_cost_ += opcode_gas_calculator (opcode )
300+ return self ._gas_cost_
279301
280302 def refund (
281303 self ,
@@ -285,7 +307,22 @@ def refund(
285307 timestamp : int = 0 ,
286308 ) -> int :
287309 """Use a fork object to calculate the gas refund from this bytecode."""
288- return _bytecode_refund (self , fork , block_number , timestamp )
310+ if (
311+ self ._refund_ is None
312+ or self ._refund_fork_ != fork
313+ or self ._refund_block_number_ != block_number
314+ or self ._refund_timestamp_ != timestamp
315+ ):
316+ self ._refund_fork_ = fork
317+ self ._refund_block_number_ = block_number
318+ self ._refund_timestamp_ = timestamp
319+ opcode_refund_calculator = fork .opcode_refund_calculator (
320+ block_number = block_number , timestamp = timestamp
321+ )
322+ self ._refund_ = 0
323+ for opcode in self .opcode_list :
324+ self ._refund_ += opcode_refund_calculator (opcode )
325+ return self ._refund_
289326
290327 @classmethod
291328 def __get_pydantic_core_schema__ (
@@ -302,37 +339,3 @@ def __get_pydantic_core_schema__(
302339 info_arg = False ,
303340 ),
304341 )
305-
306-
307- @cache
308- def _bytecode_gas_cost (
309- bytecode : Bytecode ,
310- fork : Type [ForkOpcodeInterface ],
311- block_number : int ,
312- timestamp : int ,
313- ) -> int :
314- """Return cached gas cost for the given bytecode and fork parameters."""
315- opcode_gas_calculator = fork .opcode_gas_calculator (
316- block_number = block_number , timestamp = timestamp
317- )
318- total_gas = 0
319- for opcode in bytecode .opcode_list :
320- total_gas += opcode_gas_calculator (opcode )
321- return total_gas
322-
323-
324- @cache
325- def _bytecode_refund (
326- bytecode : Bytecode ,
327- fork : Type [ForkOpcodeInterface ],
328- block_number : int ,
329- timestamp : int ,
330- ) -> int :
331- """Return cached gas refund for the given bytecode and fork parameters."""
332- opcode_refund_calculator = fork .opcode_refund_calculator (
333- block_number = block_number , timestamp = timestamp
334- )
335- total_refund = 0
336- for opcode in bytecode .opcode_list :
337- total_refund += opcode_refund_calculator (opcode )
338- return total_refund
0 commit comments