Abstract 1 Property-based Testing and Smart Contracts 2 Gas Analysis of Smart Contracts 3 Combining Gas Analysis and Property-based Testing 4 Related Work and Conclusions References

Towards Property-Based Testing of Smart Contracts Using Gas Analysis

Elvira Albert111Corresponding author ORCID Complutense University of Madrid, Spain    Emanuele De Angelis ORCID IASI-CNR, Rome, Italy    Marco Di Ianni ORCID DEc, University G. d’Annunzio, Chieti-Pescara, Italy    Fabio Fioravanti ORCID DEc, University G. d’Annunzio, Chieti-Pescara, Italy    Pablo Gordillo ORCID Complutense University of Madrid, Spain
Abstract

Testing has become an integral part of the software development process in order to ensure the correct and safe execution of programs. A powerful approach to testing is property-based testing that aims at generating unit tests that verify that a certain property of interest holds. However, smart contracts are also characterized by important non-functional aspects, such as the gas consumption required to execute their functions. Static gas analyzers are able to obtain parametric gas bounds – that soundly over-approximate – the gas consumption of executing each of the public functions within a smart contract. This paper discusses our ideas towards combining both formal methods, property-based testing and gas analysis, in order to generate gas-aware unit tests that can ensure the gas requirements provided by the programmers.

Keywords and phrases:
Property-based Testing, Blockchain, Ethereum, Gas
Category:
Short Paper
Copyright and License:
[Uncaptioned image] © Elvira Albert, Emanuele De Angelis, Marco Di Ianni, Fabio Fioravanti, and Pablo Gordillo; licensed under Creative Commons License CC-BY 4.0
2012 ACM Subject Classification:
Theory of computation Logic and verification
; Software and its engineering Formal methods ; Software and its engineering Software testing and debugging
Acknowledgements:
Emanuele De Angelis and Fabio Fioravanti are members of the INdAM-GNCS research group.
Funding:
This work was funded partially by the Spanish MCI, Comunidad de Madrid, AEI and FEDER (EU) projects PID2021-122830OB-C41, PID2024-157044OB-C31 and TEC-2024/COM-235, the BOVIR project PR17/24-31926 (Line A: early-career researchers, an action funded by the Community of Madrid within the framework of the 6th PRICIT 2022-2025 signed between the CAM and the UCM).
Editors:
Massimo Bartoletti and Diego Marmsoler

1 Property-based Testing and Smart Contracts

Property-Based Testing (PBT) [12] extends the classical testing paradigm to enable the specification of expected behaviors of code using a set of properties. Although the idea was proposed earlier, it is mainly known due to the QuickCheck [10] library implementation for the Haskell programming language. Over time, the same principles have been adopted in several other programming languages, including Erlang, Scala, Java, Python, and even smart contracts [21].

PBT occupies a useful middle ground between example-based unit testing and heavier-weight formal verification techniques [13]. This intermediate position is reflected in the way PBT is performed. In PBT, developers define formal specifications, called properties [13], that capture invariants or behavioral rules that a program should satisfy. For example, a property may specify that the balance of an account should never become negative, regardless of the sequence of operations applied to it. These properties are then automatically checked against a large number of inputs generated by randomized and structured generators [19, 20]. When a property violation is detected, testing frameworks typically report a counterexample, which is often minimized to a simpler failing input [10]. Such counterexamples provide concrete evidence of property violations and often offer valuable insight into the underlying causes of faulty behaviors. As a consequence, properties can be executed as specifications that both validate program behavior and document its intended semantics.

Because of having to systematically search over large input spaces, PBT has been observed to effectively discover corner cases which are less probable to be anticipated or manually expressed than other test cases [21]. This becomes even more significant when it concerns state transition behaviors, such as within smart contracts. Indeed, PBT is especially relevant in testing complex components and systems whose correctness depends on a sequence of operations [13]. Furthermore, when applied prior to verification, PBT aids in pointing out inconsistencies and errors in a system prior to start the verification process, which in turn lowers verification costs [9]. For instance, in the case of smart contracts, where vulnerabilities are widespread in complex execution paths as well as concurrent execution between contracts, traditional unit testing has been found inadequate by some studies. However, PBT has been successful in detecting vulnerabilities in smart contracts when paired with formal verification tools [17]. This naturally raises the question of whether PBT can be extended beyond functional correctness to also account for quantitative aspects of execution, such as resource usage and execution cost.

In this context, it is important to distinguish between functional properties, which describe the expected behavior of a program, and non-functional aspects, which concern quantitative characteristics of execution such as resource usage or cost [15]. In the context of blockchain applications, gas consumption represents a particularly relevant non-functional aspect.

2 Gas Analysis of Smart Contracts

Gas is a measure of the computational and persistent storage resources needed by a transaction to be executed on the Ethereum blockchain. The gas consumed by a function, invoked within a transaction, depends on the EVM opcodes it executes. The cost of each of the different opcodes is precisely specified by the Ethereum gas cost model [24, 2]. Those opcodes that are computationally more expensive or that access the replicated (persistent) storage consume more gas. For instance, storing a value to the persistent storage may consume up to 22,100 units of gas, while storing a value on the operational stack only consumes 3 units of gas. When executing a transaction, if the user provides less gas than the gas required to run the function invoked within the transaction, the execution is aborted and an out-of-gas exception is raised. Besides, the state is reverted and the user loses all gas provided. Hence, it is of utmost importance to have tools that are able to infer sound estimates on the gas consumption for each public function within a smart contract.

Gas analysis [5, 7] takes as input a smart contract and infers an upper bound for the gas consumed by its public functions. These upper bounds are parametric expressions defined in terms of the arguments of the function analyzed, the global contract variables and even certain blockchain information. Importantly, gas analyzers infer sound gas upper bounds, i.e., none execution of the public function will exceed the bound computed by the analysis. State-of-the-art gas analyzers [5, 7] compile the source level program into bytecode form and build a system of recurrence equations that define the gas consumption from the bytecode using the Ethereum cost model. After that, they use solvers [4, 22] to compute closed-form upper bounds from the recurrence equations. As an example, for a loop like for(uint i = 0; i < a; i++){ sum = sum + i; }, where a is an input parameter to the function, Gastap [5] infers 222+24276(a) as gas upper bound. While it is intuitive that the bound is linear on the value of a, the concrete constants that measure the cost of each iteration (24276) and the base case (222) require the understanding of the translation of the source level intructions into bytecode and also of the gas model of the opcodes. We refer to [5] for the technical details.

3 Combining Gas Analysis and Property-based Testing

The complementary nature of PBT and static gas analysis lies in their different focuses. PBT aims to generate concrete executions that explore the various behaviours of a program, while static gas analysis aims to produce sound static characterizations of the gas consumption associated with a smart contract’s functions. The remainder of this section describes our approach to combine these two complementary techniques within an integrated testing framework. Specifically, PBT is intended to be used to generate test cases, while gas analysis is used to associate gas-related information with the generated tests, as we will illustrate along this section.

3.1 Motivation: Correctness, Cost and Test Generation

Simply having a smart contract that functions correctly is not sufficient to guarantee an acceptable behavior once it is deployed on the blockchain. Unlike traditional software systems, smart contracts operate under a strict cost model, where every executed opcode incurs a monetary cost paid by users in the form of gas. For this reason, both developers and users must be confident not only in the functional correctness of a contract, but also in the predictability and affordability of its execution costs. Even when a smart contract behaves correctly for all possible inputs, its gas consumption may vary significantly depending on those inputs. Such variability can render the contract economically impractical or even unusable due to excessive transaction fees.

From a testing perspective, this introduces a non-trivial challenge. Relying solely on worst-case gas consumption estimates is insufficient, as developers need concrete and executable test cases that expose unexpected or problematic gas behaviors. However, traditional testing techniques are not designed to systematically generate execution scenarios that are critical from a gas-consumption standpoint. Therefore, gas usage should be treated as a first-class testable property. This paper proposes combining static gas analysis with PBT to automatically generate meaningful test cases that assess not only functional correctness, but also the economic viability and predictability of smart contract execution.

3.2 Motivating Example

In order to clarify the issues discussed above, we introduce a running example presented in Fig. 1, which describes a smart contract that is functionally correct for all inputs, yet consumes gas in different ways depending on the input values. The contract RewardSystem implements a simple incentive mechanism in which users can claim rewards by invoking the function claimRewards at line 6, (L6 for short) with an input parameter points, representing the amount of points to be redeemed. The function first performs a basic validity check on the input (L7) and retrieves the current reward balance of the caller (L8). The core of the function consists of a conditional statement (L9) that separates the execution into two different paths, depending on whether a given threshold condition on the input is satisfied. In one execution scenario (L9-L11), the function executes a loop (L10) whose number of iterations depends on the value of points, incrementally updating the user’s reward balance (L11). In the alternative execution scenario (L12-L13), the function updates the user’s reward balance through a constant number of arithmetic operations (L13), without executing any loop. In this case, the computational effort – and therefore the gas consumption – remains largely independent of the input value.

Both execution paths are functionally correct and fulfill the intended behavior of the smart contract, as they correctly update the user’s reward balance. However, their gas usage patterns are vastly different, clearly illustrating that functional correctness alone is insufficient to determine the acceptability of a smart contract when gas consumption is taken into account. Identifying such gas-critical execution scenarios requires generating concrete input values that drive the execution toward specific paths, which motivates the need for automated test generation techniques that explicitly take gas consumption into account.

1
2contract RewardSystem {
3
4 mapping(address => uint256) public userBalances;
5
6 function claimRewards(uint256 points) public {
7 require(points > 0, "Points must be greater than zero");
8 uint256 currentBalance = userBalances[msg.sender];
9 if (points < 50) {
10 for (uint256 i = 0; i < points; i++) {
11 currentBalance += points;}
12 } else {
13 currentBalance = currentBalance + (points * 2);}
14 userBalances[msg.sender] = currentBalance;
15 }
16
17}
Figure 1: Running example of a reward-based smart contract with input-dependent execution paths.

3.3 Using Gas Analysis for Property-based Testing

We now present our approach towards building a method for gas-aware testing in which the gas consumption is treated as an identifiable and quantified aspect when executing smart contracts, to allow developers to generate repeatable test cases characterised with costs, and enabling them to reason about both the functional correctness and gas cost of their program. Consider again the example in Fig. 1, a first approach to gas-aware testing would be to consider the global upper bound inferred by an off-the-shelf gas analyzer such as Gastap [5] without making any changes to the gas analysis. For our example, Gastap infers 2.603+max([22.657 , 22.257+249points]) as gas bound. Let us explain that the upper bound contains two separate components: (i) the constant value 2.603 corresponds to the gas consumed until the conditional instruction (L9) is reached, and (ii) the second component that contains the maximum of two values, corresponds to the consumption of each of the branches of the conditional instruction. In particular, the value 22.657 is an upper bound of the gas consumed if the else-branch is executed, while the parametric expression 22.257+249points is an upper bound of the gas consumed by the if branch. In this last case, the bound is parametric on the variable points (the input argument of function claimRewards), as the number of times that the for loop (L10) is executed depends on the value of that variable, as seen in Sec. 2.

When the concrete inputs appearing in the generated tests are obtained, the parametric bounds computed by the gas analyzer can be instantiated to obtain gas-aware tests that include estimates of the gas costs of executing the corresponding inputs. This makes it possible to compare different test cases based on the costs associated with distinct execution paths. Fig. 2 shows two different test cases, Test 1 represents an execution in which the variable points has the value 1 and Test 2 one in which points is equals to 50. Hence, the first test case executes the if-branch (L9-L11 in Fig. 1) as points < 50. In order to produce gas-aware test cases, we can instantiate the upper bound with points = 1, obtaining 25.260 (the result of 2.603+max(22.657 , 22.506)). The second test executes the else-branch (L12-L13 in Fig. 1), obtaining 37.310 as upper bound from (2.603+max(22.657 , 34.707)). The gas-aware tests allow a user to check that a specified amount of gas could be exceeded.

Test 1 (loop path)

Input:

  • points = 1

Execution path:

  • branch condition points < 50 holds

  • iterative update of currentBalance

  • repeated arithmetic operations proportional to points

Estimated gas:

  • 25.260

 

Test 2 (non-iterative path)

Input:

  • points = 50

Execution path:

  • branch condition points 50 holds

  • single arithmetic update of currentBalance

Estimated gas:

  • 37.310

Figure 2: Test cases with the same gas upper bound instantiated.

The precision of our gas-aware approach could be improved by feeding the gas analyzer with path constraints gathered during testing. The problem is that considering the global upper bound expression for the entire function may result in instantiated bounds that are not tight for the considered inputs. This is reflected in the test cases shown in Fig. 2. The reason is that the global upper bound does not contain information on the part of the bound that corresponds to the if and the else-branches, they are just put together using the max. As a consequence, when instantiating the expression with points = 1, as the gas consumption of the else-branch (22.657) is higher than that of the if-branch (22.506), we take such cost even if this path is not exercised by the test (the associated path constraint to this path is points 50). Similarly, in the second test, for which the else-branch should be executed, the instantiation of the global bound for points = 50 corresponds to an upper bound on the gas consumption of an unfeasible execution path in which the for-loop is executed 50 times.

Our idea is to work on a better integration in which the gas analysis exploits the path constraints of the execution used to generate each test case to obtain tighter upper bounds. For instance, when the if-branch is executed we will have the path constraint points < 50, the upper bound should correspond to 24.860+249points, disregarding the component which does not correspond to this path constraint. Similarly, for test cases in which the else-branch is executed, the corresponding bound would be the component 25.260 that matches the path constraint points 50. Figure 3 shows the bounds for each test case if the upper bounds were obtained by feeding the gas analyzer with the path constraints of each corresponding execution path. This way, the bound for Test 1 in Fig. 3 would be 25.109 when instantiated with the value 1, instead of 25.260, whereas in the case of Test 2 the bound would be 22.657 instead of 37.310.

Test 1 (loop path)

Input:

  • points = 1

Execution path:

  • branch condition points < 50 holds

  • iterative update of currentBalance

  • repeated arithmetic operations proportional to points

Estimated gas:

  • 24.860+249points

 

Test 2 (no-loop path)

Input:

  • points = 50

Execution path:

  • branch condition points 50 holds

  • single arithmetic update of currentBalance

Estimated gas:

  • 25.260

Figure 3: Test cases with the different upper bound depending on the execution path.

Overall, the gas-aware test cases outline the envisioned outcome of the proposed research: the ability to generate concrete and executable tests that are explicitly associated with distinct execution paths and enriched with gas-related annotations. The overall goal is to make gas consumption a first-class testing goal which, in turn, enables developers to systematically reason about both functional correctness and cost-related behavior through automatically generated tests.

4 Related Work and Conclusions

Smart contracts have different characteristics such as immutability, persistent state, and gas-limited execution, that make traditional testing techniques insufficient. In the last years, several tools [14, 3, 18, 8, 16, 6, 11, 23] and methods have been proposed to detect runtime errors, security vulnerabilities, and undesired behaviors. Several tools rely on property-based testing [14, 3, 16, 11]. These tools automatically generate inputs and transaction sequences to explore execution paths and find vulnerabilities. Examples include ContractFuzzer [16], Echidna [14], or Foundry [3] which allow specifying properties or invariants that must hold during execution. Although Echidna [14] and Foundry [3] allow gas consumption to be checked during the execution of test cases, they rely on executing concrete transactions on an EVM. As a result, gas is treated only at runtime, and these tools cannot exploit parametric information. SolAR [11] builds a test suite trying to maximize the path coverage. The generated test cases consist of concrete sequences of transactions executed on an EVM and do not check functional properties or reason about gas consumption. Method eth_estimateGas [1]
from Ethereum JSON-RPC API approximates the gas consumed by a transaction by performing an iterative binary search and simulating the transaction execution on the EVM. This simulation uses the blockchain state of the latest mined block. However, the gas inferred by this method may be imprecise, as the state of the blockchain can change between the time of estimation and the actual execution of the transaction.

Other approaches [18, 23] use symbolic execution and SMT solvers to reason about branch conditions and generate test cases where the executions are vulnerable. Mythril [18] and Solitor [23] identify security issues and control-flow errors. Mutation testing has also been applied to smart contracts [8, 6] to evaluate the quality of existing test suites. Tools like Deviant [8] introduce errors into the code and measure whether tests detect them. Other tools such as SuMo [6] define several mutation operators to modify the behavior of the contract and evaluate the quality of the code. However, they do not define any operator that takes into account the gas consumption of a smart contract.

As conclusion, we have proposed an integrated testing approach utilising both parametric gas bounds (inferred via static analysis) and automatically generated concrete test cases. Current testing approaches for smart contracts focus primarily on runtime gas usage, whereas we aim to systematically associate cost with a series of specific execution paths. By treating gas consumption as a first-class goal of testing, we are able to create a better basis for comparing equivalent but economically divergent execution paths. Future research will focus on implementing the gas-based testing framework following the proposed methods, as well as exploring its applicability to more complex smart contracts and their related costs.

References

  • [1] Ethereum JSON-RPC Specification for eth_estimateGas. https://ethereum.github.io/execution-apis/api/methods/eth_estimateGas/, 2025.
  • [2] EVM Codes: An Ethereum Virtual Machine Opcodes Interactive Reference. https://www.evm.codes/, 2025.
  • [3] Foundry. https://getfoundry.sh/, 2025.
  • [4] Elvira Albert, Puri Arenas, Samir Genaim, and Germán Puebla. Automatic inference of upper bounds for recurrence relations in cost analysis. In María Alpuente and Germán Vidal, editors, Static Analysis, 15th International Symposium, SAS 2008, Valencia, Spain, July 16-18, 2008. Proceedings, volume 5079 of LNCS, pages 221–237. Springer, 2008. doi:10.1007/978-3-540-69166-2_15.
  • [5] Elvira Albert, Jesús Correas, Pablo Gordillo, Guillermo Román-Díez, and Albert Rubio. Don’t run on fumes - parametric gas bounds for smart contracts. J. Syst. Softw., 176:110923, 2021. doi:10.1016/J.JSS.2021.110923.
  • [6] Morena Barboni, Andrea Morichetta, and Andrea Polini. Sumo: A mutation testing approach and tool for the ethereum blockchain. Journal of Systems and Software, 193:111445, 2022. doi:10.1016/j.jss.2022.111445.
  • [7] Zhuo Cai, Soroush Farokhnia, Amir Kafshdar Goharshady, and S. Hitarth. Asparagus: Automated synthesis of parametric gas upper-bounds for smart contracts. Proc. ACM Program. Lang., 7(OOPSLA2):882–911, 2023. doi:10.1145/3622829.
  • [8] Patrick Chapman, Dianxiang Xu, Lin Deng, and Yin Xiong. Deviant: A mutation testing tool for solidity smart contracts. In 2019 IEEE International Conference on Blockchain (Blockchain), pages 319–324, 2019. doi:10.1109/Blockchain.2019.00050.
  • [9] Zilin Chen, Christine Rizkallah, Liam O’Connor, Partha Susarla, Gerwin Klein, Gernot Heiser, and Gabriele Keller. Property-based testing: Climbing the stairway to verification. In Bernd Fischer, Lola Burgueño, and Walter Cazzola, editors, Proceedings of the 15th ACM SIGPLAN International Conference on Software Language Engineering, SLE 2022, Auckland, New Zealand, December 6-7, 2022, pages 84–97. ACM, 2022. doi:10.1145/3567512.3567520.
  • [10] Koen Claessen and John Hughes. Quickcheck: a lightweight tool for random testing of haskell programs. In Martin Odersky and Philip Wadler, editors, Proceedings of the Fifth ACM SIGPLAN International Conference on Functional Programming (ICFP ’00), Montreal, Canada, September 18-21, 2000, pages 268–279. ACM, 2000. doi:10.1145/351240.351266.
  • [11] S.W. Driessen, D. Di Nucci, D.A. Tamburri, and W.J. van den Heuvel. SolAR: Automated test-suite generation for solidity smart contracts. Science of Computer Programming, 232:103036, 2024. doi:10.1016/j.scico.2023.103036.
  • [12] George Fink and Karl Levitt. Property-based testing of privileged programs. In In Tenth Annual Computer Security Applications Conference. IEEE Computer Society Press, pages 154–163, 1994. doi:10.1109/CSAC.1994.367311.
  • [13] Harrison Goldstein, Joseph W. Cutler, Daniel Dickstein, Benjamin C. Pierce, and Andrew Head. Property-based testing in practice. Proceedings - International Conference on Software Engineering, pages 2307–2319, May 2024. doi:10.1145/3597503.3639581.
  • [14] Gustavo Grieco, Will Song, Artur Cygan, Josselin Feist, and Alex Groce. Echidna: effective, usable, and fast fuzzing for smart contracts. In Proceedings of the 29th ACM SIGSOFT International Symposium on Software Testing and Analysis, ISSTA 2020, pages 557–560, New York, NY, USA, 2020. Association for Computing Machinery. doi:10.1145/3395363.3404366.
  • [15] Dick Hamlet. Functional vs. non-functional properties. Composing Software Components, pages 311–318, 2010. doi:10.1007/978-1-4419-7148-7_20.
  • [16] Bo Jiang, Ye Liu, and W. K. Chan. ContractFuzzer: fuzzing smart contracts for vulnerability detection. In Marianne Huchard, Christian Kästner, and Gordon Fraser, editors, Proceedings of the 33rd ACM/IEEE International Conference on Automated Software Engineering, ASE 2018, Montpellier, France, September 3-7, 2018, pages 259–269. ACM, 2018. doi:10.1145/3238147.3238177.
  • [17] Mikkel Milo, Eske Hoy Nielsen, Danil Annenkov, and Bas Spitters. Finding smart contract vulnerabilities with concert’s property-based testing framework. In Zaynah Dargaye and Clara Schneidewind, editors, 4th International Workshop on Formal Methods for Blockchains, FMBC@CAV 2022, Haifa, Israel, August 11, 2022, volume 105 of OASIcs, pages 2:1–2:13. Schloss Dagstuhl – Leibniz-Zentrum für Informatik, 2022. doi:10.4230/OASIcs.FMBC.2022.2.
  • [18] Bernhard Mueller. Smashing Ethereum Smart Contracts for Fun and Real Profit.(2018). In The 9th annual HITB Security Conference, 2018.
  • [19] Zoe Paraskevopoulou, Catalin Hritcu, Maxime Dénès, Leonidas Lampropoulos, and Benjamin C. Pierce. Foundational property-based testing. In Christian Urban and Xingyuan Zhang, editors, Interactive Theorem Proving - 6th International Conference, ITP 2015, Nanjing, China, August 24-27, 2015, Proceedings, volume 9236 of Lecture Notes in Computer Science, pages 325–343. Springer, 2015. doi:10.1007/978-3-319-22102-1_22.
  • [20] Manuel J. C. S. Reis. Property-based testing for cybersecurity: Towards automated validation of security protocols. Computers 2025, Vol. 14,, 14, May 2025. doi:10.3390/COMPUTERS14050179.
  • [21] Célio Gil Gouveia Rodrigues. Property-based testing of erc-20 smart contracts. Master’s thesis, Universidade do Porto (Portugal), 2020.
  • [22] Roberto Sebastiani and Patrick Trentin. Optimathsat: A tool for optimization modulo theories. Journal of Automated Reasoning, 64(3):423–460, 2020. doi:10.1007/S10817-018-09508-6.
  • [23] Lars Stegeman. Solitor: runtime verification of smart contracts on the ethereum network. Master’s thesis, University of Twente, 2018.
  • [24] Gavin Wood et al. Ethereum: A secure decentralised generalised transaction ledger. Ethereum project yellow paper, (2025):1–42, 2025.