<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>horsefacts</title>
        <link>https://paragraph.com/@horsefacts</link>
        <description>I’m eth newbie..just learning</description>
        <lastBuildDate>Fri, 25 Sep 2026 00:10:06 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>horsefacts</title>
            <url>https://storage.googleapis.com/papyrus_images/e081f68697dc76cc51522e74ee82b72c92d0ca5548d1659ef7cf3c4786d3e4ae.png</url>
            <link>https://paragraph.com/@horsefacts</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Invariant Testing WETH With Foundry]]></title>
            <link>https://paragraph.com/@horsefacts/invariant-testing-weth-with-foundry</link>
            <guid>KDV94EkKTtFo0BJEit1k</guid>
            <pubDate>Tue, 14 Feb 2023 15:45:16 GMT</pubDate>
            <description><![CDATA[There&apos;s been a lot of interest recently in invariant testing, a new feature in the Foundry toolkit, but until recently, there hasn&apos;t been much documentation on getting started with this advanced testing technique. The Maple Finance invariant test repo, this example repo from Lucas Manuel, and a new chapter in the Foundry book are all great resources, but it&apos;s still tough to get up and running. In this short guide, we&apos;ll write invariant tests from the ground up for Wrapped ...]]></description>
            <content:encoded><![CDATA[<p>There&apos;s been a lot of interest recently in <em>invariant testing</em>, a new feature in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/foundry-rs/foundry">Foundry</a> toolkit, but until recently, there hasn&apos;t been much documentation on getting started with this advanced testing technique.</p><p>The Maple Finance <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/maple-labs/maple-core-v2/tree/main/tests/invariants">invariant test repo</a>, this <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/lucas-manuel/invariant-examples">example repo</a> from <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/lucasmanuel_eth">Lucas Manuel</a>, and a new chapter in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.getfoundry.sh/forge/invariant-testing">Foundry book</a> are all great resources, but it&apos;s still tough to get up and running.</p><p>In this short guide, we&apos;ll write invariant tests from the ground up for Wrapped Ether, one of the most important contracts on mainnet.</p><p>You can find all the code examples from this guide on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/horsefacts/weth-invariant-testing">Github</a>:</p><div data-type="embedly" src="https://github.com/horsefacts/weth-invariant-testing" data="{&quot;provider_url&quot;:&quot;https://github.com&quot;,&quot;description&quot;:&quot;Contribute to horsefacts/weth-invariant-testing development by creating an account on GitHub.&quot;,&quot;title&quot;:&quot;GitHub - horsefacts/weth-invariant-testing&quot;,&quot;author_name&quot;:&quot;horsefacts&quot;,&quot;thumbnail_width&quot;:1200,&quot;url&quot;:&quot;https://github.com/horsefacts/weth-invariant-testing&quot;,&quot;thumbnail_url&quot;:&quot;https://storage.googleapis.com/papyrus_images/35bd7524f227864c8a16f4ba9b3302c4f7212cbe1be86bb6989447c14302e2e4.png&quot;,&quot;author_url&quot;:&quot;https://github.com/horsefacts&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;GitHub&quot;,&quot;type&quot;:&quot;link&quot;,&quot;thumbnail_height&quot;:600,&quot;image&quot;:{&quot;img&quot;:{&quot;width&quot;:1200,&quot;height&quot;:600,&quot;src&quot;:&quot;https://storage.googleapis.com/papyrus_images/35bd7524f227864c8a16f4ba9b3302c4f7212cbe1be86bb6989447c14302e2e4.png&quot;}}}" format="small"><link rel="preload" as="image" href="https://storage.googleapis.com/papyrus_images/35bd7524f227864c8a16f4ba9b3302c4f7212cbe1be86bb6989447c14302e2e4.png"/><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://github.com/horsefacts/weth-invariant-testing" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>GitHub - horsefacts/weth-invariant-testing</h2><p>Contribute to horsefacts/weth-invariant-testing development by creating an account on GitHub.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://github.com</span></div><img src="https://storage.googleapis.com/papyrus_images/35bd7524f227864c8a16f4ba9b3302c4f7212cbe1be86bb6989447c14302e2e4.png"/></div></a></div></div><h2 id="h-how-invariant-tests-work" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How invariant tests work</h2><p>I like to think of invariant testing as a kind of super-fuzzing. If you&apos;ve written Forge <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.getfoundry.sh/forge/fuzz-testing">fuzz tests</a> before, the core concepts are similar. You might write a Forge fuzz test like the following one to test a property about a given function, like <code>a + b == b + a</code>:</p><pre data-type="codeBlock" text="    function test_fuzz_addition(uint256 a, uint256 b) public {
        assertEq(math.add(a, b), math.add(b, a));
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_fuzz_addition</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> a, <span class="hljs-keyword">uint256</span> b</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        assertEq(math.add(a, b), math.add(b, a));
    }
</code></pre><p>During a fuzz test run, the fuzzer will call this test with many randomly generated values for <code>a</code> and <code>b</code>, and verify that our assertion holds for each one. This lets us test a specific property of a specific function in a specific contract.</p><p>Invariant tests apply the same idea to the <em>system as a whole</em>. Rather than defining properties of specific functions, we define &quot;invariant properties&quot; about a specific contract or system of contracts that should always hold. These may be things like &quot;this vault contract always holds enough tokens to cover all withdrawals,&quot; &quot;x * y always equals k in a Uniswap pool,&quot; or &quot;this ERC20 token&apos;s total supply always equals the sum of all individual balances.&quot;</p><p>During an invariant test run, the fuzzer goes ham, running against <em>all</em> functions in <em>all</em> contracts (at least until we choose to constrain it). The fuzzer generates random call sequences with random calldata, and checks our defined invariants after every call. If any call sequence breaks a defined invariant, the tests fail.</p><p>Invariant tests can be great tools for shaking out invalid assumptions, complex edge cases, and unexpected interactions in a smart contract system. But it can also be challenging to channel the fuzzer&apos;s unconstrained chaos into a suite of meaningful, reliable tests.</p><h2 id="h-the-weth-contract" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The WETH contract</h2><p>We&apos;ll be testing the Wrapped Ether contract in this guide.</p><p>The Wrapped Ether token allows users to <code>deposit</code> and &quot;wrap&quot; native Ether, which is represented as <code>WETH</code>, an ERC20 token. Users who own <code>WETH</code> can <code>withdraw</code> native Ether by exchanging their <code>WETH</code> tokens for Ether at a 1:1 exchange rate.</p><p>Wrapped Ether is a simple but critical primitive in the Ethereum application layer. It enables applications designed to be composable with ERC20 tokens to use a representation of native Ether, and it mitigates the security risks to end users and smart contract systems associated with native Ether transfers.</p><p>The canonical <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/gnosis/canonical-weth/blob/0dd1ea3e295eef916d0c6223ec63141137d22d67/contracts/WETH9.sol">wrapped Ether contract</a>, known as <code>WETH9</code>, is only a little over 50 lines of code:</p><pre data-type="codeBlock" text="contract WETH9 {
    string public name = &quot;Wrapped Ether&quot;;
    string public symbol = &quot;WETH&quot;;
    uint8 public decimals = 18;

    event Approval(
        address indexed src, 
        address indexed guy, 
        uint256 wad
    );
    event Transfer(
        address indexed src, 
        address indexed dst, 
        uint256 wad
    );
    event Deposit(address indexed dst, uint256 wad);
    event Withdrawal(address indexed src, uint256 wad);

    mapping(address =&gt; uint256) public balanceOf;
    mapping(address =&gt; 
        mapping(address =&gt; uint256)) public allowance;

    fallback() external payable {
        deposit();
    }

    function deposit() public payable {
        balanceOf[msg.sender] += msg.value;
        emit Deposit(msg.sender, msg.value);
    }

    function withdraw(uint256 wad) public {
        require(balanceOf[msg.sender] &gt;= wad);
        balanceOf[msg.sender] -= wad;
        payable(msg.sender).transfer(wad);
        emit Withdrawal(msg.sender, wad);
    }

    function totalSupply() public view returns (uint256) {
        return address(this).balance;
    }

    function approve(
        address guy, 
        uint256 wad
    ) public returns (bool) {
        allowance[msg.sender][guy] = wad;
        emit Approval(msg.sender, guy, wad);
        return true;
    }

    function transfer(
        address dst, 
        uint256 wad
    ) public returns (bool) {
        return transferFrom(msg.sender, dst, wad);
    }

    function transferFrom(
        address src,
        address dst,
        uint256 wad
    ) public returns (bool) {
        require(balanceOf[src] &gt;= wad);

        if (
            src != msg.sender &amp;&amp;
            allowance[src][msg.sender] != type(uint256).max
        ) {
            require(allowance[src][msg.sender] &gt;= wad);
            allowance[src][msg.sender] -= wad;
        }

        balanceOf[src] -= wad;
        balanceOf[dst] += wad;

        emit Transfer(src, dst, wad);

        return true;
    }
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">WETH9</span> </span>{
    <span class="hljs-keyword">string</span> <span class="hljs-keyword">public</span> name <span class="hljs-operator">=</span> <span class="hljs-string">"Wrapped Ether"</span>;
    <span class="hljs-keyword">string</span> <span class="hljs-keyword">public</span> symbol <span class="hljs-operator">=</span> <span class="hljs-string">"WETH"</span>;
    <span class="hljs-keyword">uint8</span> <span class="hljs-keyword">public</span> decimals <span class="hljs-operator">=</span> <span class="hljs-number">18</span>;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">Approval</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> src, 
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> guy, 
        <span class="hljs-keyword">uint256</span> wad
    </span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">Transfer</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> src, 
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> dst, 
        <span class="hljs-keyword">uint256</span> wad
    </span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">Deposit</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> dst, <span class="hljs-keyword">uint256</span> wad</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">Withdrawal</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> src, <span class="hljs-keyword">uint256</span> wad</span>)</span>;

    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint256</span>) <span class="hljs-keyword">public</span> balanceOf;
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> 
        <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint256</span>)) <span class="hljs-keyword">public</span> allowance;

    <span class="hljs-function"><span class="hljs-keyword">fallback</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
        deposit();
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
        balanceOf[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>;
        <span class="hljs-keyword">emit</span> Deposit(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> wad</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(balanceOf[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">></span><span class="hljs-operator">=</span> wad);
        balanceOf[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> wad;
        <span class="hljs-keyword">payable</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>).<span class="hljs-built_in">transfer</span>(wad);
        <span class="hljs-keyword">emit</span> Withdrawal(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, wad);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">totalSupply</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">approve</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> guy, 
        <span class="hljs-keyword">uint256</span> wad
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        allowance[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>][guy] <span class="hljs-operator">=</span> wad;
        <span class="hljs-keyword">emit</span> Approval(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, guy, wad);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transfer</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> dst, 
        <span class="hljs-keyword">uint256</span> wad
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-keyword">return</span> transferFrom(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, dst, wad);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transferFrom</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> src,
        <span class="hljs-keyword">address</span> dst,
        <span class="hljs-keyword">uint256</span> wad
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-built_in">require</span>(balanceOf[src] <span class="hljs-operator">></span><span class="hljs-operator">=</span> wad);

        <span class="hljs-keyword">if</span> (
            src <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
            allowance[src][<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">type</span>(<span class="hljs-keyword">uint256</span>).<span class="hljs-built_in">max</span>
        ) {
            <span class="hljs-built_in">require</span>(allowance[src][<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">></span><span class="hljs-operator">=</span> wad);
            allowance[src][<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> wad;
        }

        balanceOf[src] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> wad;
        balanceOf[dst] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> wad;

        <span class="hljs-keyword">emit</span> Transfer(src, dst, wad);

        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }
}
</code></pre><p>(I&apos;ve slightly modified the version above to compile in Solidity 0.8.x).</p><p>The <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2">mainnet WETH contract</a> currently holds over 3.9 million Ether, worth over $6.5 billion USD. Any bugs in WETH would be a very big deal. So let&apos;s write some invariant tests that verify that the most important properties of WETH really do hold.</p><h2 id="h-getting-started" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Getting started</h2><p>Invariant test features in Foundry and <code>forge-std</code> have been under active development lately, so before we start, make sure to run <code>foundryup</code> to install the latest version of <code>forge</code>:</p><pre data-type="codeBlock" text="$ foundryup
"><code><span class="hljs-variable">$ </span>foundryup
</code></pre><p>Next, let&apos;s spin up a new Foundry project, remove the example <code>Counter.sol</code> and <code>Counter.t.sol</code>, and add the <code>WETH9</code> contract as <code>src/WETH9.sol</code>:</p><pre data-type="codeBlock" text="$ tree src
src
└── WETH9.sol
"><code>$ tree <span class="hljs-attribute">src</span>
<span class="hljs-attribute">src</span>
└── WETH9<span class="hljs-selector-class">.sol</span>
</code></pre><h2 id="h-invariant-test-setup" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Invariant test setup</h2><p>An invariant test contract looks just like the <code>Test</code> contracts you already know and love from <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.getfoundry.sh/forge/writing-tests">unit</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.getfoundry.sh/forge/fuzz-testing">fuzz</a> testing with Foundry. In the latest version of <code>forge-std</code>, all the helpers we&apos;ll need in order to define and test invariants are now included in the base <code>forge-std/Test.sol</code> contract. (If you&apos;re using <code>1.3.0</code>, the latest stable release, you may also need to import and inherit from <code>forge-std/InvariantTest.sol</code>).</p><p>Let&apos;s create a test contract in <code>test/WETH9.invariants.t.sol</code>:</p><pre data-type="codeBlock" text="pragma solidity ^0.8.13;

import {Test} from &quot;forge-std/Test.sol&quot;;
import {InvariantTest} from &quot;forge-std/InvariantTest.sol&quot;;
import {WETH9} from &quot;../src/WETH9.sol&quot;;

contract WETH9Invariants is Test, InvariantTest {
    WETH9 public weth;

    function setUp() public {
        weth = new WETH9();
    }

    function invariant_badInvariantThisShouldFail() public {
        assertEq(1, weth.totalSupply());
    }
}
"><code><span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.13;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Test</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/Test.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">InvariantTest</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/InvariantTest.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">WETH9</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../src/WETH9.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">WETH9Invariants</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span>, <span class="hljs-title">InvariantTest</span> </span>{
    WETH9 <span class="hljs-keyword">public</span> weth;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        weth <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> WETH9();
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invariant_badInvariantThisShouldFail</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        assertEq(<span class="hljs-number">1</span>, weth.totalSupply());
    }
}
</code></pre><p>I like to include <code>invariants</code> in the name of my invariant test files to distinguish them from others, but that&apos;s just a convention, not required by the test runner.</p><p>Hopefully this looks familiar if you&apos;ve used Foundry: we declare our contract under test as a state variable, create an instance of the contract in <code>setUp</code>, and write test functions using helpers like <code>assertEq</code> that make assertions about the state of the system.</p><p>Unlike unit and fuzz tests, invariants must start with the <code>invariant_</code> prefix, but otherwise this looks a lot like an everyday Forge unit test.</p><p>In case the name wasn&apos;t clear, our example invariant should fail:</p><pre data-type="codeBlock" text="    function invariant_badInvariantThisShouldFail() public {
        assertEq(1, weth.totalSupply());
    }
"><code>    function invariant_badInvariantThisShouldFail() public {
        assertE<span class="hljs-string">q(1, weth.totalSupply()</span>);
    }
</code></pre><p>Let&apos;s give it a try and see what happens. The test runner picks up invariant tests alongside normal unit and fuzz tests automatically. Just run <code>forge test</code>:</p><pre data-type="codeBlock" text="$ forge test -vvv
[⠢] Compiling...
No files changed, compilation skipped

Running 1 test for test/WETH9.invariants.t.sol:WETH9Invariants
[FAIL. Reason: Assertion failed.] invariant_badInvariantThisShouldFail()
(runs: 1, calls: 0, reverts: 0)
Test result: FAILED. 0 passed; 1 failed; finished in 2.11ms

Failing tests:
Encountered 1 failing test in test/WETH9.invariants.t.sol:WETH9Invariants
[FAIL. Reason: Assertion failed.] invariant_badInvariantThisShouldFail()
(runs: 1, calls: 0, reverts: 0)

Encountered a total of 1 failing tests, 0 tests succeeded
"><code>$ forge test <span class="hljs-operator">-</span>vvv
[⠢] Compiling...
No files changed, compilation skipped

Running <span class="hljs-number">1</span> test <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[FAIL. Reason: Assertion failed.] invariant_badInvariantThisShouldFail()
(runs: <span class="hljs-number">1</span>, calls: <span class="hljs-number">0</span>, reverts: <span class="hljs-number">0</span>)
Test result: FAILED. 0 passed; <span class="hljs-number">1</span> failed; finished in <span class="hljs-number">2</span>.11ms

Failing tests:
Encountered <span class="hljs-number">1</span> failing test in test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[FAIL. Reason: Assertion failed.] invariant_badInvariantThisShouldFail()
(runs: <span class="hljs-number">1</span>, calls: <span class="hljs-number">0</span>, reverts: <span class="hljs-number">0</span>)

Encountered a total of <span class="hljs-number">1</span> failing tests, <span class="hljs-number">0</span> tests succeeded
</code></pre><p>The test fails right away. In fact, it&apos;s failing the <em>very first time</em> it checks the invariants, immediately after <code>setUp</code>, and before the fuzzer even makes any calls to the contract under test. Since the WETH <code>totalSupply()</code> starts at zero, the assertion fails. (Good, that&apos;s what we expected).</p><p>Let&apos;s make a change and try again. We&apos;ll change the name, too, since our invariant should actually pass now:</p><pre data-type="codeBlock" text="    function invariant_wethSupplyIsAlwaysZero() public {
        assertEq(0, weth.totalSupply());
    }
"><code>    function invariant_wethSupplyIsAlwaysZero() public {
        assertE<span class="hljs-string">q(0, weth.totalSupply()</span>);
    }
</code></pre><p>It&apos;s not a particularly useful or realistic assertion, but hey, it works!</p><pre data-type="codeBlock" text="$ forge test
Running 1 test for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_wethSupplyIsAlwaysZero()
(runs: 1000, calls: 15000, reverts: 8671)
Test result: ok. 1 passed; 0 failed; finished in 873.42ms
"><code>$ forge test
Running <span class="hljs-number">1</span> test <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_wethSupplyIsAlwaysZero()
(runs: <span class="hljs-number">1000</span>, calls: <span class="hljs-number">15000</span>, reverts: <span class="hljs-number">8671</span>)
Test result: ok. 1 passed; <span class="hljs-number">0</span> failed; finished in <span class="hljs-number">873</span>.42ms
</code></pre><p>This time, we can see that the fuzzer made some actual calls to the contract under test: <code>1000</code> runs, <code>15000</code> calls, and <code>8671</code> reverts. This feedback from the fuzzer is very useful diagnostic information as you write and refine invariant tests:</p><ul><li><p>Runs: the total number of random call sequences generated by the fuzzer.</p></li><li><p>Calls: the total number of <em>calls</em> the fuzzer made to our contract under test during this test run. This is equal to the number of <code>runs</code> times the <code>depth</code> of each call sequence <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/foundry-rs/foundry/tree/master/config">defined</a> in <code>foundry.toml</code>.</p></li><li><p>Reverts: the number of calls that reverted in this test run. In this case, around 58% of the randomly generated calls to our contract reverted.</p></li></ul><p>Of course, in the real world the WETH supply is not always zero. So why does our test pass? A unit test might help clarify.</p><p>A nice feature of Foundry is that it&apos;s possible to define unit and invariant tests in the same test class. This can be useful for quick explorations like this, or for concretizing and testing a failed fuzz/invariant result to understand why it failed. Add this right after our invariant test function:</p><pre data-type="codeBlock" text="    function test_zeroDeposit() public {
        weth.deposit{ value: 0 }();
        assertEq(0, weth.balanceOf(address(this)));
        assertEq(0, weth.totalSupply());
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_zeroDeposit</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        weth.deposit{ <span class="hljs-built_in">value</span>: <span class="hljs-number">0</span> }();
        assertEq(<span class="hljs-number">0</span>, weth.balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)));
        assertEq(<span class="hljs-number">0</span>, weth.totalSupply());
    }
</code></pre><pre data-type="codeBlock" text="$ forge test -m test_zeroDeposit

Running 1 test for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] test_zeroDeposit() (gas: 11071)
Test result: ok. 1 passed; 0 failed; finished in 2.07ms
"><code>$ forge test <span class="hljs-operator">-</span>m test_zeroDeposit

Running <span class="hljs-number">1</span> test <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[PASS] test_zeroDeposit() (<span class="hljs-built_in">gas</span>: <span class="hljs-number">11071</span>)
Test result: ok. 1 passed; <span class="hljs-number">0</span> failed; finished in <span class="hljs-number">2</span>.07ms
</code></pre><p>Turns out, the WETH contract allows callers to &quot;deposit&quot; zero ETH in exchange for zero WETH!</p><p>So here&apos;s what&apos;s happening when we run our tests:</p><ul><li><p>The fuzzer is examining all the public functions on the <code>WETH9</code> contract and calling them with random arguments.</p></li><li><p>Many of these calls will revert. For example, <code>withdraw</code>, <code>transfer</code>, and <code>transferFrom</code> should all revert in most cases since there are no balances or tokens to transfer.</p></li><li><p>Some of these calls will succeed, but do nothing, like calling <code>deposit</code> with zero <code>msg.value</code>.</p></li><li><p>The fuzzer generates random call sequences and calldata, but does not fuzz <code>msg.value</code>, so all calls to the WETH contract have zero value. Since WETH is only created when a caller deposits native ETH, no ETH enters the WETH contract and no WETH tokens are ever created.</p></li><li><p>The WETH balance remains zero and our invariant holds.</p></li></ul><p>This kind of &quot;open testing&quot;—allowing the fuzzer to wreak havoc on all contracts, all methods, and all arguments at once—can be useful in some scenarios, and it&apos;s usually a good place to start when building up an invariant test suite. But you&apos;ll often want to simulate specific conditions, like a caller sending along native ETH to make a WETH <code>deposit</code>, more precisely.</p><p>There is also a probabilistic trade-off between exploring more random call sequences and finding &quot;meaningful&quot; sequences that actually test our invariants. Exposing more contracts and functions to the fuzzer generates much more &quot;surface area&quot; to fuzz that <em>could</em> expose interesting ways to break the invariants. But if 99% of those sequences revert because their arguments or ordering are unrealistic, we might not really be testing our invariants in a useful way at all.</p><p>In order to simulate native Ether transfers and test the conditions we really care about, we need to introduce a new concept and another contract: a <em>handler</em>.</p><h2 id="h-handlers" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Handlers</h2><p>A <em>handler</em> is a wrapper contract that we&apos;ll use to manage interactions with our contract under test. Rather than expose the <code>WETH9</code> functions directly to the fuzzer, we&apos;ll instead point the fuzzer at our <em>handler</em> contract and add functions to the handler that delegate to <code>WETH9</code>. This lets us use standard Forge <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.getfoundry.sh/forge/cheatcodes">cheatcodes</a> and helpers like <code>vm.prank</code> and <code>deal</code> to set up tests with the conditions we care about.</p><p>A handler is just another helper contract. Typically, I pass in the contract under test as a constructor argument. Let&apos;s add the following in <code>test/handlers/Handler.sol</code>:</p><pre data-type="codeBlock" text="import {WETH9} from &quot;../../src/WETH9.sol&quot;;

contract Handler {
    WETH9 public weth;

    constructor(WETH9 _weth) {
        weth = _weth;
    }
}
"><code><span class="hljs-keyword">import</span> {<span class="hljs-title">WETH9</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../../src/WETH9.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Handler</span> </span>{
    WETH9 <span class="hljs-keyword">public</span> weth;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params">WETH9 _weth</span>) </span>{
        weth <span class="hljs-operator">=</span> _weth;
    }
}
</code></pre><p>A word of warning: as soon as we introduce a handler, we are starting to introduce assumptions about the system under test. It&apos;s necessary to constrain the system in order to test it meaningfully, but it&apos;s also important to stop and consider the assumptions we&apos;re making along the way, lest we end up testing a system that&apos;s nothing like the real world at all. As we build out our tests, we should make sure we think about each assumption that we add along the way.</p><p>With that caveat in mind, let&apos;s start building out our handler contract. We&apos;ll start with a <code>deposit</code> function that calls through to <code>weth.deposit()</code> and passes on a fuzzed <code>amount</code> as <code>msg.value</code>:</p><pre data-type="codeBlock" text="import {WETH9} from &quot;../../src/WETH9.sol&quot;;

contract Handler {
    WETH9 public weth;

    constructor(WETH9 _weth) {
        weth = _weth;
    }

    function deposit(uint256 amount) public {
        weth.deposit{ value: amount }();
    }
}
"><code><span class="hljs-keyword">import</span> {<span class="hljs-title">WETH9</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../../src/WETH9.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Handler</span> </span>{
    WETH9 <span class="hljs-keyword">public</span> weth;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params">WETH9 _weth</span>) </span>{
        weth <span class="hljs-operator">=</span> _weth;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        weth.deposit{ <span class="hljs-built_in">value</span>: amount }();
    }
}
</code></pre><p>Remember, the fuzzer will now generate random calls with random values to the functions we define on the <em>handler</em>. It&apos;s up to us to pass through these arguments to the contract under test, or constrain them if necessary.</p><p>Of course, we&apos;ll need an ETH balance in order to make a deposit. Let&apos;s <code>deal</code> ourselves some Ether in the constructor:</p><pre data-type="codeBlock" text="import {CommonBase} from &quot;forge-std/Base.sol&quot;;
import {StdCheats} from &quot;forge-std/StdCheats.sol&quot;;
import {StdUtils} from &quot;forge-std/StdUtils.sol&quot;;

contract Handler is CommonBase, StdCheats, StdUtils {
    WETH9 public weth;

    constructor(WETH9 _weth) {
        weth = _weth;
        deal(address(this), 10 ether);
    }

    function deposit(uint256 amount) public {
        weth.deposit{ value: amount }();
    }
}
"><code><span class="hljs-keyword">import</span> {<span class="hljs-title">CommonBase</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/Base.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">StdCheats</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/StdCheats.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">StdUtils</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/StdUtils.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Handler</span> <span class="hljs-keyword">is</span> <span class="hljs-title">CommonBase</span>, <span class="hljs-title">StdCheats</span>, <span class="hljs-title">StdUtils</span> </span>{
    WETH9 <span class="hljs-keyword">public</span> weth;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params">WETH9 _weth</span>) </span>{
        weth <span class="hljs-operator">=</span> _weth;
        deal(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), <span class="hljs-number">10</span> <span class="hljs-literal">ether</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        weth.deposit{ <span class="hljs-built_in">value</span>: amount }();
    }
}
</code></pre><p>Over in the tests, we&apos;ll need to create our handler contract in <code>setUp</code> and configure the fuzzer to test its functions rather than call <code>WETH9</code> directly. The helper functions <code>targetContract(address)</code> and <code>excludeContract(address)</code> in<code>forge-std/StdInvariant</code> allow us to include and exclude contracts from invariant fuzzing.</p><pre data-type="codeBlock" text="import {Handler} from &quot;./handlers/Handler.sol&quot;;

contract WETH9Invariants is Test {
    WETH9 public weth;
    Handler public handler;

    function setUp() public {
        weth = new WETH9();
        handler = new Handler(weth);

        targetContract(address(handler));
    }

    function invariant_wethSupplyIsAlwaysZero() public {
        assertEq(0, weth.totalSupply());
    }
}
"><code><span class="hljs-keyword">import</span> {<span class="hljs-title">Handler</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./handlers/Handler.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">WETH9Invariants</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    WETH9 <span class="hljs-keyword">public</span> weth;
    Handler <span class="hljs-keyword">public</span> handler;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        weth <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> WETH9();
        handler <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Handler(weth);

        targetContract(<span class="hljs-keyword">address</span>(handler));
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invariant_wethSupplyIsAlwaysZero</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        assertEq(<span class="hljs-number">0</span>, weth.totalSupply());
    }
}
</code></pre><p>Don&apos;t forget to call <code>targetContract</code>! If we don&apos;t configure the fuzzer to explicitly filter for a given contract, it will implicitly fuzz all methods on all contracts created during <code>setUp</code>.</p><p>Let&apos;s give our new, wrapped tests a run:</p><pre data-type="codeBlock" text="$ forge test
Running 1 test for test/WETH9.invariants.t.sol:WETH9Invariants
Test result: FAILED. 0 passed; 1 failed; finished in 3.89ms

Failing tests:
Encountered 1 failing test in test/WETH9.invariants.t.sol:WETH9Invariants
[FAIL. Reason: Assertion failed.]
        [Sequence]
                sender=0x00000000000000000000000000000000000000a4
                addr=[test/handlers/Handler.sol:Handler]
                     0x2e234dae75c793f67a35089c9d99245e1c58470b
                calldata=deposit(uint256),
                args=[65]

 invariant_wethSupplyIsAlwaysZero() (runs: 1, calls: 1, reverts: 0)

Encountered a total of 1 failing tests, 0 tests succeeded
"><code>$ forge test
Running <span class="hljs-number">1</span> test <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
Test result: FAILED. 0 passed; <span class="hljs-number">1</span> failed; finished in <span class="hljs-number">3</span>.89ms

Failing tests:
Encountered <span class="hljs-number">1</span> failing test in test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[FAIL. Reason: Assertion failed.]
        [Sequence]
                sender<span class="hljs-operator">=</span><span class="hljs-number">0x00000000000000000000000000000000000000a4</span>
                addr<span class="hljs-operator">=</span>[test<span class="hljs-operator">/</span>handlers<span class="hljs-operator">/</span>Handler.sol:Handler]
                     <span class="hljs-number">0x2e234dae75c793f67a35089c9d99245e1c58470b</span>
                <span class="hljs-keyword">calldata</span><span class="hljs-operator">=</span>deposit(<span class="hljs-keyword">uint256</span>),
                args<span class="hljs-operator">=</span>[<span class="hljs-number">65</span>]

 invariant_wethSupplyIsAlwaysZero() (runs: <span class="hljs-number">1</span>, calls: <span class="hljs-number">1</span>, reverts: <span class="hljs-number">0</span>)

Encountered a total of <span class="hljs-number">1</span> failing tests, <span class="hljs-number">0</span> tests succeeded
</code></pre><p>A successful failure! Unlike our very first failure, which broke right after <code>setUp</code>, this time we failed after the first <em>call</em>. The fuzzer has helpfully printed the call sequence that broke our invariant: we called <code>deposit</code> with <code>65</code> wei, which broke our (now invalid) invariant that <code>weth.totalSupply()</code> is always zero.</p><h2 id="h-conservation-of-ether" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conservation of Ether</h2><p>So then, what should our invariant condition actually be? Thinking in invariants can be very different from the way we think about the system when writing unit tests. Invariants are about properties of the system as a whole, rather than specific reactions to specific inputs. Can we define a property that should always hold for the entire system?</p><p>Here&apos;s one: in the constrained world of our tests, our <code>Handler</code> contract and <code>WETH9</code> are a closed system. ETH is only created in the <code>Handler</code> when we <code>deal</code> it to ourselves, and can only flow into <code>WETH9</code> as a <code>deposit</code>, since that&apos;s the only function we&apos;ve exposed so far. So we can test a &quot;conservation of Ether&quot; property: the <code>weth.totalSupply()</code> plus the handler&apos;s Ether balance should always equal the circulating supply of ETH in our closed system.</p><p>We gave ourselves 10 Ether when we set up our handler contract, but let&apos;s make that a little more realistic. There are currently around 120-and-a-half million ETH <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ultrasound.money/">in circulation</a>. Let&apos;s <code>deal</code> all of it to ourselves when we create the handler:</p><pre data-type="codeBlock" text="contract Handler is CommonBase, StdCheats, StdUtils {
    WETH9 public weth;

    uint256 public constant ETH_SUPPLY = 120_500_000 ether;

    constructor(WETH9 _weth) {
        weth = _weth;
        deal(address(this), ETH_SUPPLY);
    }

    function deposit(uint256 amount) public {
        weth.deposit{ value: amount }();
    }

}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Handler</span> <span class="hljs-keyword">is</span> <span class="hljs-title">CommonBase</span>, <span class="hljs-title">StdCheats</span>, <span class="hljs-title">StdUtils</span> </span>{
    WETH9 <span class="hljs-keyword">public</span> weth;

    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> ETH_SUPPLY <span class="hljs-operator">=</span> <span class="hljs-number">120_500_000</span> <span class="hljs-literal">ether</span>;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params">WETH9 _weth</span>) </span>{
        weth <span class="hljs-operator">=</span> _weth;
        deal(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), ETH_SUPPLY);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        weth.deposit{ <span class="hljs-built_in">value</span>: amount }();
    }

}
</code></pre><p>And let&apos;s update our invariant to describe this property:</p><pre data-type="codeBlock" text="    // ETH can only be wrapped into WETH, WETH can only
    // be unwrapped back into ETH. The sum of the Handler&apos;s
    // ETH balance plus the WETH totalSupply() should always
    // equal the total ETH_SUPPLY.
    function invariant_conservationOfETH() public {
        assertEq(
          handler.ETH_SUPPLY(),
          address(handler).balance + weth.totalSupply()
        );
    }
"><code>    <span class="hljs-comment">// ETH can only be wrapped into WETH, WETH can only</span>
    <span class="hljs-comment">// be unwrapped back into ETH. The sum of the Handler's</span>
    <span class="hljs-comment">// ETH balance plus the WETH totalSupply() should always</span>
    <span class="hljs-comment">// equal the total ETH_SUPPLY.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invariant_conservationOfETH</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        assertEq(
          handler.ETH_SUPPLY(),
          <span class="hljs-keyword">address</span>(handler).<span class="hljs-built_in">balance</span> <span class="hljs-operator">+</span> weth.totalSupply()
        );
    }
</code></pre><p>We&apos;ll also add one more condition to our test: we&apos;ll <code>bound</code> the deposit amount to be less than or equal to the remaining Ether balance in the handler contract, so calls don&apos;t revert when they attempt to deposit more ETH than they have available:</p><pre data-type="codeBlock" text="contract Handler is CommonBase, StdCheats, StdUtils {
    WETH9 public weth;

    uint256 public constant ETH_SUPPLY = 120_500_000;

    constructor(WETH9 _weth) {
        weth = _weth;
        deal(address(this), ETH_SUPPLY);
    }

    function deposit(uint256 amount) public {
        amount = bound(amount, 0, address(this).balance);
        weth.deposit{ value: amount }();
    }

}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Handler</span> <span class="hljs-keyword">is</span> <span class="hljs-title">CommonBase</span>, <span class="hljs-title">StdCheats</span>, <span class="hljs-title">StdUtils</span> </span>{
    WETH9 <span class="hljs-keyword">public</span> weth;

    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> ETH_SUPPLY <span class="hljs-operator">=</span> <span class="hljs-number">120_500_000</span>;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params">WETH9 _weth</span>) </span>{
        weth <span class="hljs-operator">=</span> _weth;
        deal(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), ETH_SUPPLY);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
        weth.deposit{ <span class="hljs-built_in">value</span>: amount }();
    }

}
</code></pre><p>Our tests now pass—and since we&apos;re never attempting an invalid deposit that exceeds our balance, none of our calls revert:</p><pre data-type="codeBlock" text="$ forge test
Running 1 test for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: 1000, calls: 15000, reverts: 0)
Test result: ok. 1 passed; 0 failed; finished in 1.24s
"><code>$ forge test
Running <span class="hljs-number">1</span> test <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: <span class="hljs-number">1000</span>, calls: <span class="hljs-number">15000</span>, reverts: <span class="hljs-number">0</span>)
Test result: ok. 1 passed; <span class="hljs-number">0</span> failed; finished in <span class="hljs-number">1</span>.24s
</code></pre><p>Switching back and forth between open/constrained tests and bounded/unbounded calls can be a useful technique as we write invariant tests. For now we&apos;ll constrain the values as we build up our handler and invariants, but eventually we may want to remove these assumptions and let the fuzzer run wild to shake out any invalid assumptions we&apos;ve made along the way.</p><h2 id="h-adding-handler-functions" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Adding handler functions</h2><p>OK, our tests pass, but we&apos;ve only exposed one function from <code>WETH9</code> through our handler. To test the real world behavior of WETH, we need to expose <code>withdraw</code>, <code>transfer</code>, and all the rest. Let&apos;s add <code>withdraw</code> to our handler next:</p><pre data-type="codeBlock" text="    function withdraw(uint256 amount) public {
        weth.withdraw(amount);
    }

    receive() external payable {}
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        weth.withdraw(amount);
    }

    <span class="hljs-function"><span class="hljs-keyword">receive</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{}
</code></pre><p>Since <code>withdraw</code> will transfer back native Ether to the caller, we also need to add a <code>receive()</code> function to our handler contract in order to receive it.</p><pre data-type="codeBlock" text="$ forge test
Running 1 test for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH() 
(runs: 1000, calls: 15000, reverts: 3535)
Test result: ok. 1 passed; 0 failed; finished in 1.17s
"><code>$ forge test
Running <span class="hljs-number">1</span> test <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH() 
(runs: <span class="hljs-number">1000</span>, calls: <span class="hljs-number">15000</span>, reverts: <span class="hljs-number">3535</span>)
Test result: ok. 1 passed; <span class="hljs-number">0</span> failed; finished in <span class="hljs-number">1</span>.17s
</code></pre><p>Our tests still pass and the invariant holds! Native Ether is now flowing in two directions in our closed system: from <code>Handler</code> into <code>WETH9</code> on <code>deposit</code> and from <code>WETH9</code> back to <code>Handler</code> on <code>withdraw</code>. But our &quot;conservation of ETH&quot; invariant still holds, as we should expect.</p><p>Note also that we&apos;re now seeing some reverts in the test runs: these will be the cases when the fuzzer attempts to <code>withdraw</code> more than our actual balance of WETH. If we <code>bound</code> the withdrawal amount to less than the handler&apos;s WETH balance, we should see them go away:</p><pre data-type="codeBlock" text="    function withdraw(uint256 amount) public {
        amount = bound(amount, 0, weth.balanceOf(address(this)));
        weth.withdraw(amount);
    }

    receive() external payable {}
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)));
        weth.withdraw(amount);
    }

    <span class="hljs-function"><span class="hljs-keyword">receive</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{}
</code></pre><pre data-type="codeBlock" text="$ forge test
Running 1 test for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: 1000, calls: 15000, reverts: 0)
Test result: ok. 1 passed; 0 failed; finished in 1.54s
"><code>$ forge test
Running <span class="hljs-number">1</span> test <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: <span class="hljs-number">1000</span>, calls: <span class="hljs-number">15000</span>, reverts: <span class="hljs-number">0</span>)
Test result: ok. 1 passed; <span class="hljs-number">0</span> failed; finished in <span class="hljs-number">1</span>.54s
</code></pre><p>There is one remaining way WETH can come into the world: by sending Ether directly to the <code>WETH9</code> fallback function. Let&apos;s add a handler function for this, too:</p><pre data-type="codeBlock" text="    function sendFallback(uint256 amount) public {
        amount = bound(amount, 0, address(this).balance);
        (bool success,) = address(weth).call{ value: amount }(&quot;&quot;);
        require(success, &quot;sendFallback failed&quot;);
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendFallback</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
        (<span class="hljs-keyword">bool</span> success,) <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(weth).<span class="hljs-built_in">call</span>{ <span class="hljs-built_in">value</span>: amount }(<span class="hljs-string">""</span>);
        <span class="hljs-built_in">require</span>(success, <span class="hljs-string">"sendFallback failed"</span>);
    }
</code></pre><pre data-type="codeBlock" text="$ forge test
Running 1 test for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH() (runs: 1000, calls: 15000, reverts: 0)
Test result: ok. 1 passed; 0 failed; finished in 1.66s
"><code>$ forge test
Running <span class="hljs-number">1</span> test <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH() (runs: <span class="hljs-number">1000</span>, calls: <span class="hljs-number">15000</span>, reverts: <span class="hljs-number">0</span>)
Test result: ok. 1 passed; <span class="hljs-number">0</span> failed; finished in <span class="hljs-number">1</span>.66s
</code></pre><p>Looks good—our invariant still holds.</p><p>We&apos;ll eventually want to add transfers and approvals to our handler, to ensure that WETH transfers from one account to another don&apos;t somehow create or destroy any unaccounted-for WETH. But for now, let&apos;s skip them, since we know they don&apos;t directly wrap or unwrap Ether.</p><p>Let&apos;s move on to a second invariant: <em>solvency</em>.</p><h2 id="h-solvency-and-ghost-variables" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Solvency and ghost variables</h2><p>It&apos;s pretty important that the WETH contract&apos;s native Ether balance is always enough to cover all possible withdrawals from the contract. Since WETH and native Ether are convertible 1:1, the <code>WETH9</code> contract&apos;s Ether balance should always equal the sum of all deposits.</p><p>We can test this invariant in a couple ways: at a high level, we can look at all deposits minus all withdrawals from the contract. At a lower level, we can sum up the individual balances of each WETH token owner. Let&apos;s look at each in turn. Both of these approaches will require a new technique, <em>ghost variables</em>.</p><p>We can use <em>ghost variables</em> in our handler contract to track state that is not otherwise exposed by the contract under test. For example, keeping track of the sum of all individual deposits into the <code>WETH9</code> contract using an accumulator variable.</p><p>Let&apos;s add a <code>ghost_depositSum</code> state variable to our contract, and increase it every time we make a deposit:</p><pre data-type="codeBlock" text="    uint256 public ghost_depositSum;

    function deposit(uint256 amount) public {
        amount = bound(amount, 0, address(this).balance);
        weth.deposit{ value: amount }();
        ghost_depositSum += amount;
    }
"><code>    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> ghost_depositSum;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
        weth.deposit{ <span class="hljs-built_in">value</span>: amount }();
        ghost_depositSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }
</code></pre><p>I like to prefix these variable names with <code>ghost_</code>, but that&apos;s just a convention. There&apos;s nothing special about these variables besides their purpose. Otherwise, they are standard Solidity state variables in our helper contract.</p><p>While we&apos;re at it, let&apos;s also add <code>ghost_withdrawSum</code> to track all withdrawals. We expect the native Ether balance of the WETH contract to be equal to all the deposits minus all the withdrawals.</p><pre data-type="codeBlock" text="    uint256 public ghost_depositSum;
    uint256 public ghost_withdrawSum;

    function deposit(uint256 amount) public {
        amount = bound(amount, 0, address(this).balance);
        weth.deposit{ value: amount }();
        ghost_depositSum += amount;
    }

    function withdraw(uint256 amount) public {
        amount = bound(amount, 0, weth.balanceOf(address(this)));
        weth.withdraw(amount);
        ghost_withdrawSum += amount;
    }
"><code>    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> ghost_depositSum;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> ghost_withdrawSum;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
        weth.deposit{ <span class="hljs-built_in">value</span>: amount }();
        ghost_depositSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)));
        weth.withdraw(amount);
        ghost_withdrawSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }
</code></pre><p>We could just as easily decrement <code>ghost_depositSum</code> on withdrawals, but I prefer to use separate variables for two reasons. First, it&apos;s nice to have these accounting values available as separate properties. Often, building up a good invariant test suite requires defining properties in terms of intermediate values like &quot;all deposits&quot; and &quot;all withdrawals&quot;. I find that exposing these explicitly helps me think about the &quot;building blocks&quot; available to test against when defining new invariants.</p><p>Second, I think it&apos;s good to be wary about any complex or conditional behavior in ghost variables, which makes it easy to introduce invalid assumptions about the system under test. Sometimes this can&apos;t be avoided, but if you can use a simple no-behavior accumulator, you usually should.</p><p>Let&apos;s add our new invariant:</p><pre data-type="codeBlock" text="    // The WETH contract&apos;s Ether balance should always
    // equal the sum of all the individual deposits
    // minus all the individual withdrawals
    function invariant_solvencyDeposits() public {
        assertEq(
          address(weth).balance,
          handler.ghost_depositSum() - handler.ghost_withdrawSum()
        );
    }
"><code>    <span class="hljs-comment">// The WETH contract's Ether balance should always</span>
    <span class="hljs-comment">// equal the sum of all the individual deposits</span>
    <span class="hljs-comment">// minus all the individual withdrawals</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invariant_solvencyDeposits</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        assertEq(
          <span class="hljs-keyword">address</span>(weth).<span class="hljs-built_in">balance</span>,
          handler.ghost_depositSum() <span class="hljs-operator">-</span> handler.ghost_withdrawSum()
        );
    }
</code></pre><pre data-type="codeBlock" text="$ forge test
[PASS] invariant_conservationOfETH()
(runs: 1000, calls: 14988, reverts: 0)
[FAIL. Reason: Assertion failed.]
        [Sequence]
                sender=0x0000000000000000000000000000000000000c88
                  addr=[test/handlers/Handler.sol:Handler]
                       0x2e234dae75c793f67a35089c9d99245e1c58470b
                  calldata=deposit(uint256),
                  args=[826074471]
                sender=0x0000000000000000000000000000000000000ffb
                  addr=[test/handlers/Handler.sol:Handler]
                       0x2e234dae75c793f67a35089c9d99245e1c58470b
                  calldata=deposit(uint256),
                  args=[1]
                sender=0xeaae00d9e5544c3fd4fc519f81e2a4747920f369
                  addr=[test/handlers/Handler.sol:Handler]
                       0x2e234dae75c793f67a35089c9d99245e1c58470b
                  calldata=sendFallback(uint256),
                  args=[1007]

 invariant_solvencyDeposits()
 (runs: 1000, calls: 14988, reverts: 0)
Test result: FAILED. 1 passed; 1 failed; finished in 2.06s
"><code>$ forge test
[PASS] invariant_conservationOfETH()
(runs: <span class="hljs-number">1000</span>, calls: <span class="hljs-number">14988</span>, reverts: <span class="hljs-number">0</span>)
[FAIL. Reason: Assertion failed.]
        [Sequence]
                sender<span class="hljs-operator">=</span><span class="hljs-number">0x0000000000000000000000000000000000000c88</span>
                  addr<span class="hljs-operator">=</span>[test<span class="hljs-operator">/</span>handlers<span class="hljs-operator">/</span>Handler.sol:Handler]
                       <span class="hljs-number">0x2e234dae75c793f67a35089c9d99245e1c58470b</span>
                  <span class="hljs-keyword">calldata</span><span class="hljs-operator">=</span>deposit(<span class="hljs-keyword">uint256</span>),
                  args<span class="hljs-operator">=</span>[<span class="hljs-number">826074471</span>]
                sender<span class="hljs-operator">=</span><span class="hljs-number">0x0000000000000000000000000000000000000ffb</span>
                  addr<span class="hljs-operator">=</span>[test<span class="hljs-operator">/</span>handlers<span class="hljs-operator">/</span>Handler.sol:Handler]
                       <span class="hljs-number">0x2e234dae75c793f67a35089c9d99245e1c58470b</span>
                  <span class="hljs-keyword">calldata</span><span class="hljs-operator">=</span>deposit(<span class="hljs-keyword">uint256</span>),
                  args<span class="hljs-operator">=</span>[<span class="hljs-number">1</span>]
                sender<span class="hljs-operator">=</span><span class="hljs-number">0xeaae00d9e5544c3fd4fc519f81e2a4747920f369</span>
                  addr<span class="hljs-operator">=</span>[test<span class="hljs-operator">/</span>handlers<span class="hljs-operator">/</span>Handler.sol:Handler]
                       <span class="hljs-number">0x2e234dae75c793f67a35089c9d99245e1c58470b</span>
                  <span class="hljs-keyword">calldata</span><span class="hljs-operator">=</span>sendFallback(<span class="hljs-keyword">uint256</span>),
                  args<span class="hljs-operator">=</span>[<span class="hljs-number">1007</span>]

 invariant_solvencyDeposits()
 (runs: <span class="hljs-number">1000</span>, calls: <span class="hljs-number">14988</span>, reverts: <span class="hljs-number">0</span>)
Test result: FAILED. 1 passed; <span class="hljs-number">1</span> failed; finished in <span class="hljs-number">2</span>.06s
</code></pre><p>It failed—can you see why? Learning to read failed call sequences is part of the art of invariant testing. One important clue is that the <em>last call in the sequence</em> should always be the one that caused the invariant to fail. In this case, it looks like we forgot to account for deposits into the contract through the fallback function. These need to increment our ghost variable, too:</p><pre data-type="codeBlock" text="    function sendFallback(uint256 amount) public {
        amount = bound(amount, 0, address(this).balance);
        (bool success,) = address(weth).call{ value: amount }(&quot;&quot;);
        require(success, &quot;sendFallback failed&quot;);
        ghost_depositSum += amount;
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendFallback</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
        (<span class="hljs-keyword">bool</span> success,) <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(weth).<span class="hljs-built_in">call</span>{ <span class="hljs-built_in">value</span>: amount }(<span class="hljs-string">""</span>);
        <span class="hljs-built_in">require</span>(success, <span class="hljs-string">"sendFallback failed"</span>);
        ghost_depositSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }
</code></pre><p>With this change in place, our tests should now pass:</p><pre data-type="codeBlock" text="$ forge test
Running 2 tests for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: 1000, calls: 15000, reverts: 0)
[PASS] invariant_solvencyDeposits()
(runs: 1000, calls: 15000, reverts: 0)
Test result: ok. 2 passed; 0 failed; finished in 2.18s
"><code>$ <span class="hljs-selector-tag">forge</span> <span class="hljs-selector-tag">test</span>
<span class="hljs-selector-tag">Running</span> <span class="hljs-number">2</span> <span class="hljs-selector-tag">tests</span> <span class="hljs-selector-tag">for</span> <span class="hljs-selector-tag">test</span>/<span class="hljs-selector-tag">WETH9</span><span class="hljs-selector-class">.invariants</span><span class="hljs-selector-class">.t</span><span class="hljs-selector-class">.sol</span>:<span class="hljs-selector-tag">WETH9Invariants</span>
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_conservationOfETH</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">1000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">15000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">0</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_solvencyDeposits</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">1000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">15000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">0</span>)
<span class="hljs-selector-tag">Test</span> <span class="hljs-selector-tag">result</span>: <span class="hljs-selector-tag">ok</span>. <span class="hljs-number">2</span> <span class="hljs-selector-tag">passed</span>; <span class="hljs-number">0</span> <span class="hljs-selector-tag">failed</span>; <span class="hljs-selector-tag">finished</span> <span class="hljs-selector-tag">in</span> <span class="hljs-number">2.18s</span>
</code></pre><p>Let&apos;s move on and test another solvency invariant: the Ether balance of the WETH contract should be equal to the sum of all <em>balances</em>, including balances before and after transfers. That is, even if users transfer their WETH tokens around, the sum of all balances should stay the same and remain equal to the contract&apos;s Ether balance.</p><pre data-type="codeBlock" text="    // The WETH contract&apos;s Ether balance should always be
    // at least as much as the sum of individual balances
    function invariant_solvencyBalances() public {
        uint256 sumOfBalances = ???
        assertEq(
            address(weth).balance,
            sumOfBalances
        );
    }
"><code>    <span class="hljs-comment">// The WETH contract's Ether balance should always be</span>
    <span class="hljs-comment">// at least as much as the sum of individual balances</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invariant_solvencyBalances</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-keyword">uint256</span> sumOfBalances <span class="hljs-operator">=</span> ???
        assertEq(
            <span class="hljs-keyword">address</span>(weth).<span class="hljs-built_in">balance</span>,
            sumOfBalances
        );
    }
</code></pre><p>So how can we track the sum of individual balances? We <em>could</em> add more complicated ghost variables to our handler, perhaps something like a mapping that tracks each caller&apos;s balance, increments on deposits, decrements on withdrawals, and updates the sender and receiver on transfers. But by adding this, we&apos;d basically be replicating the ERC20 logic included in the WETH contract!</p><p>WETH is simple enough that we could probably get away with it this time, but this is a dangerous path: if any of our assumptions are wrong in both the contract under test <em>and</em> in our ghost variable logic that replicates it, we will simply be replicating bugs in the implementation in our tests.</p><p>In general, I think it&apos;s a good principle to rely on external state from the contract under test whenever possible. And it <em>is</em> possible here: we can tally up the balance of each user by:</p><ol><li><p>Saving the address of every caller,</p></li><li><p>iterating over each address and retrieving the <code>weth.balanceOf</code> the caller, and</p></li><li><p>adding up all the balances.</p></li></ol><p>We&apos;ll need to add some helpers to do this calculation.</p><p>If you&apos;ve paid close attention to the design of the handler so far, you may notice one more thing that&apos;s a bit out of sync with reality: so far, every call to <code>WETH9</code> is originating from the address of our <em>handler</em> contract. That means only one address (the handler contract) ever has a balance in the <code>WETH9</code> contract, which is not representative of the real world, where many different callers each have an individual balance.</p><h2 id="h-introducing-actors" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introducing actors</h2><p>Although Foundry will fuzz different <code>msg.sender</code> addresses for each call to our handler, we need to pass them on using <code>vm.prank</code> if we want to propagate them to the contract under test.</p><p>Let&apos;s take a detour to add support for many different <em>actors</em>, then return to adding up their balances.</p><p>We should be able to introduce multiple actors with different <code>msg.sender</code> addresses without breaking any of our existing tests.</p><p>We can start by simply passing through <code>msg.sender</code> using <code>vm.prank</code> before we call into the WETH contract:</p><pre data-type="codeBlock" text="    function deposit(uint256 amount) public {
        amount = bound(amount, 0, address(this).balance);

        vm.prank(msg.sender);
        weth.deposit{value: amount}();

        ghost_depositSum += amount;
    }

    function withdraw(uint256 amount) public {
        amount = bound(amount, 0, weth.balanceOf(address(this)));

        vm.prank(msg.sender);
        weth.withdraw(amount);

        ghost_withdrawSum += amount;
    }

    function sendFallback(uint256 amount) public {
        amount = bound(amount, 0, address(this).balance);

        vm.prank(msg.sender);
        (bool success,) = address(weth).call{value: amount}(&quot;&quot;);

        require(success, &quot;sendFallback failed&quot;);
        ghost_depositSum += amount;
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);

        vm.prank(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        weth.deposit{<span class="hljs-built_in">value</span>: amount}();

        ghost_depositSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)));

        vm.prank(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        weth.withdraw(amount);

        ghost_withdrawSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendFallback</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);

        vm.prank(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        (<span class="hljs-keyword">bool</span> success,) <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(weth).<span class="hljs-built_in">call</span>{<span class="hljs-built_in">value</span>: amount}(<span class="hljs-string">""</span>);

        <span class="hljs-built_in">require</span>(success, <span class="hljs-string">"sendFallback failed"</span>);
        ghost_depositSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }
</code></pre><p>However, all these calls will revert if we don&apos;t first send the <code>msg.sender</code> enough ETH for their deposits. Since our tests are a closed system with a fixed amount of ETH used in our invariant properties, we&apos;ll want to actually send &quot;real&quot; Ether using <code>&lt;address&gt;.call</code> rather than using a cheatcode to &quot;print&quot; Ether, which would mess with our circulating ETH invariant.</p><p>We&apos;ll add a <code>_pay</code> helper to make transfers before tests that need them:</p><pre data-type="codeBlock" text="    function deposit(uint256 amount) public {
        amount = bound(amount, 0, address(this).balance);
        _pay(msg.sender, amount);

        vm.prank(msg.sender);
        weth.deposit{value: amount}();

        ghost_depositSum += amount;
    }

    function withdraw(uint256 amount) public {
        amount = bound(amount, 0, weth.balanceOf(address(this)));

        vm.prank(msg.sender);
        weth.withdraw(amount);

        ghost_withdrawSum += amount;
    }

    function sendFallback(uint256 amount) public {
        amount = bound(amount, 0, address(this).balance);
        _pay(msg.sender, amount);

        vm.prank(msg.sender);
        (bool success,) = address(weth).call{value: amount}(&quot;&quot;);

        require(success, &quot;sendFallback failed&quot;);
        ghost_depositSum += amount;
    }

    function _pay(address to, uint256 amount) internal {
        (bool s,) = to.call{value: amount}(&quot;&quot;);
        require(s, &quot;pay() failed&quot;);
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
        _pay(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, amount);

        vm.prank(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        weth.deposit{<span class="hljs-built_in">value</span>: amount}();

        ghost_depositSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)));

        vm.prank(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        weth.withdraw(amount);

        ghost_withdrawSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendFallback</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
        _pay(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, amount);

        vm.prank(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        (<span class="hljs-keyword">bool</span> success,) <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(weth).<span class="hljs-built_in">call</span>{<span class="hljs-built_in">value</span>: amount}(<span class="hljs-string">""</span>);

        <span class="hljs-built_in">require</span>(success, <span class="hljs-string">"sendFallback failed"</span>);
        ghost_depositSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_pay</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> to, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        (<span class="hljs-keyword">bool</span> s,) <span class="hljs-operator">=</span> to.<span class="hljs-built_in">call</span>{<span class="hljs-built_in">value</span>: amount}(<span class="hljs-string">""</span>);
        <span class="hljs-built_in">require</span>(s, <span class="hljs-string">"pay() failed"</span>);
    }
</code></pre><p>Finally, we need to make two changes in <code>withdraw</code>. First, we need to update the <code>bound</code> condition in <code>withdraw</code> not to exceed the <code>msg.sender</code>&apos;s WETH balance on withdrawals, rather than the handler contract&apos;s total balance. (Otherwise many of these calls will revert).</p><p>Second, we need to send the withdrawn amount back to the handler using <code>_pay</code>, to keep all Ether in our closed two-contract system. (Otherwise it will remain with <code>msg.sender</code>):</p><pre data-type="codeBlock" text="    function deposit(uint256 amount) public {
        amount = bound(amount, 0, address(this).balance);
        _pay(msg.sender, amount);

        vm.prank(msg.sender);
        weth.deposit{value: amount}();

        ghost_depositSum += amount;
    }

    function withdraw(uint256 amount) public {
        amount = bound(amount, 0, weth.balanceOf(msg.sender));

        vm.startPrank(msg.sender);
        weth.withdraw(amount);
        _pay(address(this), amount);
        vm.stopPrank();

        ghost_withdrawSum += amount;
    }

    function sendFallback(uint256 amount) public {
        amount = bound(amount, 0, address(this).balance);
        _pay(msg.sender, amount);

        vm.prank(msg.sender);
        (bool success,) = address(weth).call{value: amount}(&quot;&quot;);

        require(success, &quot;sendFallback failed&quot;);
        ghost_depositSum += amount;
    }

    function _pay(address to, uint256 amount) internal {
        (bool s,) = to.call{value: amount}(&quot;&quot;);
        require(s, &quot;pay() failed&quot;);
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
        _pay(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, amount);

        vm.prank(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        weth.deposit{<span class="hljs-built_in">value</span>: amount}();

        ghost_depositSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.balanceOf(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>));

        vm.startPrank(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        weth.withdraw(amount);
        _pay(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount);
        vm.stopPrank();

        ghost_withdrawSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendFallback</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
        _pay(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, amount);

        vm.prank(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        (<span class="hljs-keyword">bool</span> success,) <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(weth).<span class="hljs-built_in">call</span>{<span class="hljs-built_in">value</span>: amount}(<span class="hljs-string">""</span>);

        <span class="hljs-built_in">require</span>(success, <span class="hljs-string">"sendFallback failed"</span>);
        ghost_depositSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_pay</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> to, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        (<span class="hljs-keyword">bool</span> s,) <span class="hljs-operator">=</span> to.<span class="hljs-built_in">call</span>{<span class="hljs-built_in">value</span>: amount}(<span class="hljs-string">""</span>);
        <span class="hljs-built_in">require</span>(s, <span class="hljs-string">"pay() failed"</span>);
    }
</code></pre><pre data-type="codeBlock" text="$ forge test
Running 2 tests for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: 10000, calls: 150000, reverts: 0)
[PASS] invariant_solvencyDeposits()
(runs: 10000, calls: 150000, reverts: 0)
Test result: ok. 2 passed; 0 failed; finished in 95.41s
"><code>$ <span class="hljs-selector-tag">forge</span> <span class="hljs-selector-tag">test</span>
<span class="hljs-selector-tag">Running</span> <span class="hljs-number">2</span> <span class="hljs-selector-tag">tests</span> <span class="hljs-selector-tag">for</span> <span class="hljs-selector-tag">test</span>/<span class="hljs-selector-tag">WETH9</span><span class="hljs-selector-class">.invariants</span><span class="hljs-selector-class">.t</span><span class="hljs-selector-class">.sol</span>:<span class="hljs-selector-tag">WETH9Invariants</span>
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_conservationOfETH</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">10000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">150000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">0</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_solvencyDeposits</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">10000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">150000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">0</span>)
<span class="hljs-selector-tag">Test</span> <span class="hljs-selector-tag">result</span>: <span class="hljs-selector-tag">ok</span>. <span class="hljs-number">2</span> <span class="hljs-selector-tag">passed</span>; <span class="hljs-number">0</span> <span class="hljs-selector-tag">failed</span>; <span class="hljs-selector-tag">finished</span> <span class="hljs-selector-tag">in</span> <span class="hljs-number">95.41s</span>
</code></pre><p>So far, so good. Many different addresses are now interacting with the <code>WETH9</code> contract under test during our invariant runs, but we need to capture them in our handler in order to reconstruct their balances as part of our test assertion. Ideally, we&apos;d capture a deduplicated list of all the caller addresses we care about.</p><h2 id="h-creating-an-addressset" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Creating an <code>AddressSet</code></h2><p>If you&apos;ll forgive a short detour into Solidity data structures, one clean way to capture this is with a modifier and a simple append-only set. Let&apos;s start with an <code>AddressSet</code> struct that stores a dynamic <code>address[]</code> array and a boolean mapping to track which addresses it contains:</p><pre data-type="codeBlock" text="struct AddressSet {
    address[] addrs;
    mapping(address =&gt; bool) saved;
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">AddressSet</span> {
    <span class="hljs-keyword">address</span>[] addrs;
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">bool</span>) saved;
}
</code></pre><p>We can then define a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.soliditylang.org/en/latest/contracts.html#libraries">library</a> that adds some behavior to this data structure. <code>add(address)</code> will push an address into the <code>saved</code> array only if it has not already been seen. <code>contains(address)</code> returns whether an address is in the set, and <code>count()</code> returns the total number of addresses in the set:</p><pre data-type="codeBlock" text="library LibAddressSet {
    function add(AddressSet storage s, address addr) internal {
        if (!s.saved[addr]) {
            s.addrs.push(addr);
            s.saved[addr] = true;
        }
    }

    function contains(
      AddressSet storage s,
      address addr
    ) internal view returns (bool) {
        return s.saved[addr];
    }

    function count(
        AddressSet storage s
    ) internal view returns (uint256) {
        return s.addrs.length;
    }
}
"><code><span class="hljs-class"><span class="hljs-keyword">library</span> <span class="hljs-title">LibAddressSet</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add</span>(<span class="hljs-params">AddressSet <span class="hljs-keyword">storage</span> s, <span class="hljs-keyword">address</span> addr</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>s.saved[addr]) {
            s.addrs.<span class="hljs-built_in">push</span>(addr);
            s.saved[addr] <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">contains</span>(<span class="hljs-params">
      AddressSet <span class="hljs-keyword">storage</span> s,
      <span class="hljs-keyword">address</span> addr
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-keyword">return</span> s.saved[addr];
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">count</span>(<span class="hljs-params">
        AddressSet <span class="hljs-keyword">storage</span> s
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> s.addrs.<span class="hljs-built_in">length</span>;
    }
}
</code></pre><p>Let&apos;s use the library in our handler and create an internal <code>AddressSet</code> named <code>_actors</code>. And let&apos;s expose the array of saved actors through an external function so we can access it from our tests:</p><pre data-type="codeBlock" text="contract Handler is CommonBase, StdCheats, StdUtils {
    using LibAddressSet for AddressSet;

    AddressSet internal _actors;

    // Other handler stuff omitted here

    function actors() external returns (address[] memory) {
      return _actors.addrs;
    }
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Handler</span> <span class="hljs-keyword">is</span> <span class="hljs-title">CommonBase</span>, <span class="hljs-title">StdCheats</span>, <span class="hljs-title">StdUtils</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">LibAddressSet</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">AddressSet</span>;

    AddressSet <span class="hljs-keyword">internal</span> _actors;

    <span class="hljs-comment">// Other handler stuff omitted here</span>

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">actors</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span>[] <span class="hljs-keyword">memory</span></span>) </span>{
      <span class="hljs-keyword">return</span> _actors.addrs;
    }
}
</code></pre><p>Finally, we&apos;ll add a <code>createActor</code> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.soliditylang.org/en/v0.8.18/contracts.html#function-modifiers">modifier</a> that automatically adds <code>msg.sender</code> to our <code>_actors</code> set on every function where it&apos;s applied:</p><pre data-type="codeBlock" text="    modifier createActor() {
        _actors.add(msg.sender);
        _;
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">createActor</span>(<span class="hljs-params"></span>) </span>{
        _actors.add(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        <span class="hljs-keyword">_</span>;
    }
</code></pre><p>Now we can load the <code>actors()</code> in our test, add up their balances, and make our assertion:</p><pre data-type="codeBlock" text="    // The WETH contract&apos;s Ether balance should always be
    // at least as much as the sum of individual balances
    function invariant_solvencyBalances() public {
        uint256 sumOfBalances;
        address[] memory actors = handler.actors();
        for (uint256 i; i &lt; actors.length; ++i) {
            sumOfBalances += weth.balanceOf(actors[i]);
        }
        assertEq(
            address(weth).balance,
            sumOfBalances
        );
    }
"><code>    <span class="hljs-comment">// The WETH contract's Ether balance should always be</span>
    <span class="hljs-comment">// at least as much as the sum of individual balances</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invariant_solvencyBalances</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-keyword">uint256</span> sumOfBalances;
        <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">memory</span> actors <span class="hljs-operator">=</span> handler.actors();
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i; i <span class="hljs-operator">&#x3C;</span> actors.<span class="hljs-built_in">length</span>; <span class="hljs-operator">+</span><span class="hljs-operator">+</span>i) {
            sumOfBalances <span class="hljs-operator">+</span><span class="hljs-operator">=</span> weth.balanceOf(actors[i]);
        }
        assertEq(
            <span class="hljs-keyword">address</span>(weth).<span class="hljs-built_in">balance</span>,
            sumOfBalances
        );
    }
</code></pre><pre data-type="codeBlock" text="$ forge test
Running 3 tests for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: 10000, calls: 150000, reverts: 10)
[PASS] invariant_solvencyBalances()
(runs: 10000, calls: 150000, reverts: 10)
[PASS] invariant_solvencyDeposits()
(runs: 10000, calls: 150000, reverts: 10)
Test result: ok. 3 passed; 0 failed; finished in 134.45s
"><code>$ <span class="hljs-selector-tag">forge</span> <span class="hljs-selector-tag">test</span>
<span class="hljs-selector-tag">Running</span> <span class="hljs-number">3</span> <span class="hljs-selector-tag">tests</span> <span class="hljs-selector-tag">for</span> <span class="hljs-selector-tag">test</span>/<span class="hljs-selector-tag">WETH9</span><span class="hljs-selector-class">.invariants</span><span class="hljs-selector-class">.t</span><span class="hljs-selector-class">.sol</span>:<span class="hljs-selector-tag">WETH9Invariants</span>
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_conservationOfETH</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">10000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">150000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">10</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_solvencyBalances</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">10000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">150000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">10</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_solvencyDeposits</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">10000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">150000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">10</span>)
<span class="hljs-selector-tag">Test</span> <span class="hljs-selector-tag">result</span>: <span class="hljs-selector-tag">ok</span>. <span class="hljs-number">3</span> <span class="hljs-selector-tag">passed</span>; <span class="hljs-number">0</span> <span class="hljs-selector-tag">failed</span>; <span class="hljs-selector-tag">finished</span> <span class="hljs-selector-tag">in</span> <span class="hljs-number">134.45s</span>
</code></pre><p>To follow the lead of a pattern in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.getfoundry.sh/forge/invariant-testing#actor-management">Foundry Book</a>, let&apos;s make one more update to our modifier. We&apos;ll add a <code>currentActor</code> state variable and set it in the modifier.</p><pre data-type="codeBlock" text="    address internal currentActor;

    modifier createActor() {
        currentActor = msg.sender;
        _actors.add(msg.sender);
        _;
    }
"><code>    <span class="hljs-keyword">address</span> <span class="hljs-keyword">internal</span> currentActor;

    <span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">createActor</span>(<span class="hljs-params"></span>) </span>{
        currentActor <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        _actors.add(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        <span class="hljs-keyword">_</span>;
    }
</code></pre><p>Now we can refer to <code>currentActor</code> as the &quot;selected&quot; actor address in our tests:</p><pre data-type="codeBlock" text="    function deposit(uint256 amount) public createActor {
        amount = bound(amount, 0, address(this).balance);
        _pay(currentActor, amount);

        vm.prank(currentActor);
        weth.deposit{value: amount}();

        ghost_depositSum += amount;
    }

    function withdraw(uint256 amount) public createActor {
        amount = bound(amount, 0, weth.balanceOf(msg.sender));

        vm.startPrank(currentActor);
        weth.withdraw(amount);
        _pay(address(this), amount);
        vm.stopPrank();

        ghost_withdrawSum += amount;
    }

    function sendFallback(uint256 amount) public createActor {
        amount = bound(amount, 0, address(this).balance);
        _pay(currentActor, amount);

        vm.prank(currentActor);
        (bool success,) = address(weth).call{value: amount}(&quot;&quot;);

        require(success, &quot;sendFallback failed&quot;);
        ghost_depositSum += amount;
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">createActor</span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
        _pay(currentActor, amount);

        vm.prank(currentActor);
        weth.deposit{<span class="hljs-built_in">value</span>: amount}();

        ghost_depositSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">createActor</span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.balanceOf(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>));

        vm.startPrank(currentActor);
        weth.withdraw(amount);
        _pay(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount);
        vm.stopPrank();

        ghost_withdrawSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendFallback</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">createActor</span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
        _pay(currentActor, amount);

        vm.prank(currentActor);
        (<span class="hljs-keyword">bool</span> success,) <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(weth).<span class="hljs-built_in">call</span>{<span class="hljs-built_in">value</span>: amount}(<span class="hljs-string">""</span>);

        <span class="hljs-built_in">require</span>(success, <span class="hljs-string">"sendFallback failed"</span>);
        ghost_depositSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }
</code></pre><h2 id="h-helper-iterators" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Helper iterators</h2><p>Can we do better? Iterating over all callers to calculate a ghost variable or make some assertion is a pretty common pattern, and as we write more tests, we&apos;ll probably find ourselves repeating it. Let&apos;s flex some rarely used Solidity muscles and add one more abstraction.</p><p>Did you know you can pass <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.soliditylang.org/en/v0.8.18/types.html#function-types">function types</a> as arguments in Solidity? We can define <code>forEach</code> and <code>reduce</code>iterators for <code>AddressSet</code> that take functions as args:</p><pre data-type="codeBlock" text="library LibAddressSet {

    function forEach(
        AddressSet storage s,
        function(address) external returns (address[] memory) func
    ) internal {
        for (uint256 i; i &lt; s.addrs.length; ++i) {
            func(s.addrs[i]);
        }
    }

    function reduce(
        AddressSet storage s,
        uint256 acc,
        function(uint256,address) external returns (uint256) func
    )
        internal
        returns (uint256)
    {
        for (uint256 i; i &lt; s.addrs.length; ++i) {
            acc = func(acc, s.addrs[i]);
        }
        return acc;
    }
}
"><code><span class="hljs-class"><span class="hljs-keyword">library</span> <span class="hljs-title">LibAddressSet</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">forEach</span>(<span class="hljs-params">
        AddressSet <span class="hljs-keyword">storage</span> s,
        <span class="hljs-keyword">function</span>(<span class="hljs-params"><span class="hljs-keyword">address</span></span>) <span class="hljs-keyword">external</span> <span class="hljs-keyword">returns</span> (<span class="hljs-params"><span class="hljs-keyword">address</span>[] <span class="hljs-keyword">memory</span></span>) func
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i; i <span class="hljs-operator">&#x3C;</span> s.addrs.<span class="hljs-built_in">length</span>; <span class="hljs-operator">+</span><span class="hljs-operator">+</span>i) {
            func(s.addrs[i]);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reduce</span>(<span class="hljs-params">
        AddressSet <span class="hljs-keyword">storage</span> s,
        <span class="hljs-keyword">uint256</span> acc,
        <span class="hljs-keyword">function</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span>,<span class="hljs-keyword">address</span></span>) <span class="hljs-keyword">external</span> <span class="hljs-keyword">returns</span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) func
    </span>)
        <span class="hljs-title"><span class="hljs-keyword">internal</span></span>
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)
    </span>{
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i; i <span class="hljs-operator">&#x3C;</span> s.addrs.<span class="hljs-built_in">length</span>; <span class="hljs-operator">+</span><span class="hljs-operator">+</span>i) {
            acc <span class="hljs-operator">=</span> func(acc, s.addrs[i]);
        }
        <span class="hljs-keyword">return</span> acc;
    }
}
</code></pre><p><code>forEach</code> will call the given function for every address in our set, while <code>reduce</code> will call a given function (that must return a <code>uint256</code>) and add its result to an accumulator value.</p><p>(One thing that&apos;s kind of fun about writing tests in Solidity is getting the chance to do stuff like this that is usually gas-cost-prohibitive or otherwise ill-advised in production contracts).</p><p>To use these iterators from our tests, we can expose them from the handler:</p><pre data-type="codeBlock" text="    function forEachActor(function(address) external func) public {
        return _actors.forEach(func);
    }

    function reduceActors(
        uint256 acc,
        function(uint256,address) external returns (uint256) func
    )
        public
        returns (uint256)
    {
        return _actors.reduce(acc, func);
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">forEachActor</span>(<span class="hljs-params"><span class="hljs-keyword">function</span>(<span class="hljs-params"><span class="hljs-keyword">address</span></span>) <span class="hljs-keyword">external</span> func</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-keyword">return</span> _actors.forEach(func);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reduceActors</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> acc,
        <span class="hljs-keyword">function</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span>,<span class="hljs-keyword">address</span></span>) <span class="hljs-keyword">external</span> <span class="hljs-keyword">returns</span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) func
    </span>)
        <span class="hljs-title"><span class="hljs-keyword">public</span></span>
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)
    </span>{
        <span class="hljs-keyword">return</span> _actors.reduce(acc, func);
    }
</code></pre><p>Now, we can rewrite our test and tally up balances using a reducer:</p><pre data-type="codeBlock" text="    // The WETH contract&apos;s Ether balance should always be
    // at least as much as the sum of individual balances
    function invariant_solvencyBalances() public {
        uint256 sumOfBalances = handler.reduceActors(
          0,
          this.accumulateBalance
        );
        assertEq(
            address(weth).balance,
            sumOfBalances
        );
    }

    function accumulateBalance(
      uint256 balance,
      address caller
    ) external view returns (uint256) {
        return balance + weth.balanceOf(caller);
    }
"><code>    <span class="hljs-comment">// The WETH contract's Ether balance should always be</span>
    <span class="hljs-comment">// at least as much as the sum of individual balances</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invariant_solvencyBalances</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-keyword">uint256</span> sumOfBalances <span class="hljs-operator">=</span> handler.reduceActors(
          <span class="hljs-number">0</span>,
          <span class="hljs-built_in">this</span>.accumulateBalance
        );
        assertEq(
            <span class="hljs-keyword">address</span>(weth).<span class="hljs-built_in">balance</span>,
            sumOfBalances
        );
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">accumulateBalance</span>(<span class="hljs-params">
      <span class="hljs-keyword">uint256</span> balance,
      <span class="hljs-keyword">address</span> caller
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> balance <span class="hljs-operator">+</span> weth.balanceOf(caller);
    }
</code></pre><p>Cool trick, right?</p><h2 id="h-targeting-specific-selectors" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Targeting specific selectors</h2><p>There&apos;s one more change we need to make in the test contract to make this all work. Now that we&apos;ve added some external functions to our handler to expose our iterators, we want to exclude them from fuzzing. We need to use the more complex <code>targetSelector</code> helper from <code>forge-std/StdInvariants</code> to specify the exact selectors we want the fuzzer to target and exclude everything else:</p><pre data-type="codeBlock" text="    function setUp() public {
        weth = new WETH9();
        handler = new Handler(weth);

        bytes4[] memory selectors = new bytes4Unsupported embed;
        selectors[0] = Handler.deposit.selector;
        selectors[1] = Handler.withdraw.selector;
        selectors[2] = Handler.sendFallback.selector;

        targetSelector(FuzzSelector({
            addr: address(handler),
            selectors: selectors
        }));

        targetContract(address(handler));
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        weth <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> WETH9();
        handler <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Handler(weth);

        <span class="hljs-keyword">bytes4</span>[] <span class="hljs-keyword">memory</span> selectors <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> bytes4Unsupported embed;
        selectors[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> Handler.deposit.<span class="hljs-built_in">selector</span>;
        selectors[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> Handler.withdraw.<span class="hljs-built_in">selector</span>;
        selectors[<span class="hljs-number">2</span>] <span class="hljs-operator">=</span> Handler.sendFallback.<span class="hljs-built_in">selector</span>;

        targetSelector(FuzzSelector({
            addr: <span class="hljs-keyword">address</span>(handler),
            selectors: selectors
        }));

        targetContract(<span class="hljs-keyword">address</span>(handler));
    }
</code></pre><pre data-type="codeBlock" text="$ forge test
Running 3 tests for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: 10000, calls: 150000, reverts: 10)
[PASS] invariant_solvencyBalances()
(runs: 10000, calls: 150000, reverts: 10)
[PASS] invariant_solvencyDeposits()
(runs: 10000, calls: 150000, reverts: 10)
Test result: ok. 3 passed; 0 failed; finished in 179.86s
"><code>$ <span class="hljs-selector-tag">forge</span> <span class="hljs-selector-tag">test</span>
<span class="hljs-selector-tag">Running</span> <span class="hljs-number">3</span> <span class="hljs-selector-tag">tests</span> <span class="hljs-selector-tag">for</span> <span class="hljs-selector-tag">test</span>/<span class="hljs-selector-tag">WETH9</span><span class="hljs-selector-class">.invariants</span><span class="hljs-selector-class">.t</span><span class="hljs-selector-class">.sol</span>:<span class="hljs-selector-tag">WETH9Invariants</span>
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_conservationOfETH</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">10000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">150000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">10</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_solvencyBalances</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">10000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">150000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">10</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_solvencyDeposits</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">10000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">150000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">10</span>)
<span class="hljs-selector-tag">Test</span> <span class="hljs-selector-tag">result</span>: <span class="hljs-selector-tag">ok</span>. <span class="hljs-number">3</span> <span class="hljs-selector-tag">passed</span>; <span class="hljs-number">0</span> <span class="hljs-selector-tag">failed</span>; <span class="hljs-selector-tag">finished</span> <span class="hljs-selector-tag">in</span> <span class="hljs-number">179.86s</span>
</code></pre><h2 id="h-individual-balance-invariant" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Individual balance invariant</h2><p>Let&apos;s add one more invariant property and make use of our <code>forEach</code> iterator. This is kind of an odd one, but we&apos;ll check that <em>no individual token owner&apos;s balance</em> can exceed the <code>weth.totalSupply()</code>. An underflow in token transfer logic might be one way to violate this property:</p><pre data-type="codeBlock" text="    // No individual account balance can exceed the
    // WETH totalSupply().
    function invariant_depositorBalances() public {
        handler.forEachActor(this.assertAccountBalanceLteTotalSupply);
    }

    function assertAccountBalanceLteTotalSupply(address account) external {
        assertLe(weth.balanceOf(account), weth.totalSupply());
    }
"><code>    <span class="hljs-comment">// No individual account balance can exceed the</span>
    <span class="hljs-comment">// WETH totalSupply().</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invariant_depositorBalances</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        handler.forEachActor(<span class="hljs-built_in">this</span>.assertAccountBalanceLteTotalSupply);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">assertAccountBalanceLteTotalSupply</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> account</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        assertLe(weth.balanceOf(account), weth.totalSupply());
    }
</code></pre><pre data-type="codeBlock" text="Running 4 tests for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: 1000, calls: 15000, reverts: 11)
[PASS] invariant_depositorBalances()
(runs: 1000, calls: 15000, reverts: 11)
[PASS] invariant_solvencyBalances()
(runs: 1000, calls: 15000, reverts: 11)
[PASS] invariant_solvencyDeposits()
(runs: 1000, calls: 15000, reverts: 11)
Test result: ok. 4 passed; 0 failed; finished in 5.80s
"><code><span class="hljs-selector-tag">Running</span> <span class="hljs-number">4</span> <span class="hljs-selector-tag">tests</span> <span class="hljs-selector-tag">for</span> <span class="hljs-selector-tag">test</span>/<span class="hljs-selector-tag">WETH9</span><span class="hljs-selector-class">.invariants</span><span class="hljs-selector-class">.t</span><span class="hljs-selector-class">.sol</span>:<span class="hljs-selector-tag">WETH9Invariants</span>
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_conservationOfETH</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">1000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">15000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">11</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_depositorBalances</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">1000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">15000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">11</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_solvencyBalances</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">1000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">15000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">11</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_solvencyDeposits</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">1000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">15000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">11</span>)
<span class="hljs-selector-tag">Test</span> <span class="hljs-selector-tag">result</span>: <span class="hljs-selector-tag">ok</span>. <span class="hljs-number">4</span> <span class="hljs-selector-tag">passed</span>; <span class="hljs-number">0</span> <span class="hljs-selector-tag">failed</span>; <span class="hljs-selector-tag">finished</span> <span class="hljs-selector-tag">in</span> <span class="hljs-number">5.80s</span>
</code></pre><h2 id="h-debugging-with-call-summaries" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Debugging with call summaries</h2><p>Earlier, I mentioned the importance of double checking all our assumptions as we build out our handler contract. Every time we constrain a parameter in our test handler, we may be making changes that interact in unexpected ways. Our handler&apos;s <code>withdraw</code> function is a good example. Do you see anything that could go wrong here?</p><pre data-type="codeBlock" text="    function withdraw(uint256 amount) public {
        amount = bound(amount, 0, weth.balanceOf(msg.sender));

        vm.startPrank(msg.sender);
        weth.withdraw(amount);
        _pay(address(this), amount);
        vm.stopPrank();

        ghost_withdrawSum += amount;
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.balanceOf(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>));

        vm.startPrank(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        weth.withdraw(amount);
        _pay(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount);
        vm.stopPrank();

        ghost_withdrawSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }
</code></pre><p>There&apos;s a subtle bug here that means we&apos;re not testing what we think we are:</p><ul><li><p>Since the possibility space for a random <code>address</code> is so large, most of the time the fuzzer will choose a new, never before seen address as <code>msg.sender</code>.</p></li><li><p>Since <code>msg.sender</code> won&apos;t yet have a balance, the <code>bound</code> statement will set the withdrawal amount to zero.</p></li><li><p>A close look at the <code>WETH</code> contract shows that it will allow zero withdrawals:</p></li></ul><pre data-type="codeBlock" text="    function withdraw(uint256 wad) public {
        require(balanceOf[msg.sender] &gt;= wad);
        balanceOf[msg.sender] -= wad;
        payable(msg.sender).transfer(wad);
        emit Withdrawal(msg.sender, wad);
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> wad</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(balanceOf[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">></span><span class="hljs-operator">=</span> wad);
        balanceOf[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> wad;
        <span class="hljs-keyword">payable</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>).<span class="hljs-built_in">transfer</span>(wad);
        <span class="hljs-keyword">emit</span> Withdrawal(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, wad);
    }
</code></pre><p>It&apos;s very likely that most of the <code>withdraw</code> calls in our test are zero value withdrawals that don&apos;t really exercise the invariants we&apos;re trying to test! We may get lucky from time to time by running our tests with lots of runs and a high depth, but we should probably constrain our tests even further to ensure we’re testing the behavior we care about.</p><p>One technique we can use to explore and fix this problem is a <em>call summary</em> that counts up calls to each of our handler functions and prints them at the end of a test run.</p><p>Let&apos;s add a <code>calls</code> mapping to our handler that will count each call to our handler functions, a <code>countCall(bytes32)</code> modifier to log them, and a <code>callSummary()</code> function that will print a summary:</p><pre data-type="codeBlock" text="import {console} from &quot;forge-std/console.sol&quot;;

contract Handler is CommonBase, StdCheats, StdUtils {

    mapping(bytes32 =&gt; uint256) public calls;

    modifier countCall(bytes32 key) {
        calls[key]++;
        _;
    }

    function callSummary() external view {
        console.log(&quot;Call summary:&quot;);
        console.log(&quot;-------------------&quot;);
        console.log(&quot;deposit&quot;, calls[&quot;deposit&quot;]);
        console.log(&quot;withdraw&quot;, calls[&quot;withdraw&quot;]);
        console.log(&quot;sendFallback&quot;, calls[&quot;sendFallback&quot;]);
    }
}
"><code><span class="hljs-keyword">import</span> {<span class="hljs-title">console</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"forge-std/console.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Handler</span> <span class="hljs-keyword">is</span> <span class="hljs-title">CommonBase</span>, <span class="hljs-title">StdCheats</span>, <span class="hljs-title">StdUtils</span> </span>{

    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">bytes32</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint256</span>) <span class="hljs-keyword">public</span> calls;

    <span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">countCall</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> key</span>) </span>{
        calls[key]<span class="hljs-operator">+</span><span class="hljs-operator">+</span>;
        <span class="hljs-keyword">_</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">callSummary</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> </span>{
        console.log(<span class="hljs-string">"Call summary:"</span>);
        console.log(<span class="hljs-string">"-------------------"</span>);
        console.log(<span class="hljs-string">"deposit"</span>, calls[<span class="hljs-string">"deposit"</span>]);
        console.log(<span class="hljs-string">"withdraw"</span>, calls[<span class="hljs-string">"withdraw"</span>]);
        console.log(<span class="hljs-string">"sendFallback"</span>, calls[<span class="hljs-string">"sendFallback"</span>]);
    }
}
</code></pre><p>We&apos;ll apply the modifier to each handler function exposed to the fuzzer and pass the name of the function:</p><pre data-type="codeBlock" text="    function deposit(
        uint256 amount
    ) public createActor countCall(&quot;deposit&quot;) {
        amount = bound(amount, 0, address(this).balance);
        _pay(msg.sender, amount);

        vm.prank(msg.sender);
        weth.deposit{value: amount}();

        ghost_depositSum += amount;
    }

    function withdraw(
        uint256 amount
    ) public countCall(&quot;withdraw&quot;) {
        amount = bound(amount, 0, weth.balanceOf(msg.sender));

        vm.startPrank(msg.sender);
        weth.withdraw(amount);
        _pay(address(this), amount);
        vm.stopPrank();

        ghost_withdrawSum += amount;
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">createActor</span> <span class="hljs-title">countCall</span>(<span class="hljs-params"><span class="hljs-string">"deposit"</span></span>) </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
        _pay(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, amount);

        vm.prank(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        weth.deposit{<span class="hljs-built_in">value</span>: amount}();

        ghost_depositSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">countCall</span>(<span class="hljs-params"><span class="hljs-string">"withdraw"</span></span>) </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.balanceOf(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>));

        vm.startPrank(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        weth.withdraw(amount);
        _pay(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount);
        vm.stopPrank();

        ghost_withdrawSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }
</code></pre><p>Finally, in our invariant tests, we can add an <code>invariant_callSummary()</code> function. Since this function is prefixed with <code>invariant_</code>, it will run as an invariant test, but we won&apos;t use it to make any assertions, just to log out the data we collect:</p><pre data-type="codeBlock" text="    function invariant_callSummary() public view {
        handler.callSummary();
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invariant_callSummary</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> </span>{
        handler.callSummary();
    }
</code></pre><p>If we run our tests with the <code>-vv</code> flag, we&apos;ll see the summary result:</p><pre data-type="codeBlock" text="$ forge test -vv -m invariant_callSummary
Running 1 test for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_callSummary()
(runs: 2000, calls: 30000, reverts: 2)
Logs:
  Call summary:
  -------------------
  deposit 5
  withdraw 6
  sendFallback 4

Test result: ok. 1 passed; 0 failed; finished in 11.54s
"><code>$ forge test <span class="hljs-operator">-</span>vv <span class="hljs-operator">-</span>m invariant_callSummary
Running <span class="hljs-number">1</span> test <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_callSummary()
(runs: <span class="hljs-number">2000</span>, calls: <span class="hljs-number">30000</span>, reverts: <span class="hljs-number">2</span>)
Logs:
  Call summary:
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span>
  deposit <span class="hljs-number">5</span>
  withdraw <span class="hljs-number">6</span>
  sendFallback <span class="hljs-number">4</span>

Test result: ok. 1 passed; <span class="hljs-number">0</span> failed; finished in <span class="hljs-number">11</span>.54s
</code></pre><p>Looks like it worked, although the result might look odd. How should we interpret these results?</p><p>Although we performed 2000 runs, the summary printed here is a snapshot of calls made during the <em>final run</em>. The total number of calls in our summary should always be the same as the <code>depth</code> parameter set for invariant tests in <code>foundry.toml</code>. In our case, the depth is set to 15. So in our last run, the fuzzer made 5 calls to <code>deposit</code>, 6 calls to <code>withdraw</code>, and 4 calls to <code>sendFallback</code>, a total of 15 calls.</p><p>This kind of snapshot can be a helpful way to observe the distribution of calls during a fuzz run and help explore and debug our tests themselves.</p><p>So, are we ever actually exercising a nonzero <code>withdraw</code> in our tests? Let&apos;s find out. We can count up zero withdrawals using a ghost variable, update <code>withdraw</code> to increment our zero withdrawal counter, and add it to our call summary:</p><pre data-type="codeBlock" text="    uint256 public ghost_zeroWithdrawals;

    function withdraw(uint256 amount) public countCall(&quot;withdraw&quot;) {
        amount = bound(amount, 0, weth.balanceOf(msg.sender));
        if (amount == 0) ghost_zeroWithdrawals++;

        vm.startPrank(msg.sender);
        weth.withdraw(amount);
        _pay(address(this), amount);
        vm.stopPrank();

        ghost_withdrawSum += amount;
    }

    function callSummary() external view {
        console.log(&quot;Call summary:&quot;);
        console.log(&quot;-------------------&quot;);
        console.log(&quot;deposit&quot;, calls[&quot;deposit&quot;]);
        console.log(&quot;withdraw&quot;, calls[&quot;withdraw&quot;]);
        console.log(&quot;sendFallback&quot;, calls[&quot;sendFallback&quot;]);
        console.log(&quot;-------------------&quot;);

        console.log(&quot;Zero withdrawals:&quot;, ghost_zeroWithdrawals);
    }
"><code>    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> ghost_zeroWithdrawals;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">countCall</span>(<span class="hljs-params"><span class="hljs-string">"withdraw"</span></span>) </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.balanceOf(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>));
        <span class="hljs-keyword">if</span> (amount <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) ghost_zeroWithdrawals<span class="hljs-operator">+</span><span class="hljs-operator">+</span>;

        vm.startPrank(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        weth.withdraw(amount);
        _pay(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount);
        vm.stopPrank();

        ghost_withdrawSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">callSummary</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> </span>{
        console.log(<span class="hljs-string">"Call summary:"</span>);
        console.log(<span class="hljs-string">"-------------------"</span>);
        console.log(<span class="hljs-string">"deposit"</span>, calls[<span class="hljs-string">"deposit"</span>]);
        console.log(<span class="hljs-string">"withdraw"</span>, calls[<span class="hljs-string">"withdraw"</span>]);
        console.log(<span class="hljs-string">"sendFallback"</span>, calls[<span class="hljs-string">"sendFallback"</span>]);
        console.log(<span class="hljs-string">"-------------------"</span>);

        console.log(<span class="hljs-string">"Zero withdrawals:"</span>, ghost_zeroWithdrawals);
    }
</code></pre><p>Run our tests:</p><pre data-type="codeBlock" text="Running 1 test for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_callSummary()
(runs: 2000, calls: 30000, reverts: 13)
Logs:
  Call summary:
  -------------------
  deposit 8
  withdraw 2
  sendFallback 5
  -------------------
  Zero withdrawals: 2

Test result: ok. 1 passed; 0 failed; finished in 9.40s
"><code>Running <span class="hljs-number">1</span> test <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_callSummary()
(runs: <span class="hljs-number">2000</span>, calls: <span class="hljs-number">30000</span>, reverts: <span class="hljs-number">13</span>)
Logs:
  Call summary:
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span>
  deposit <span class="hljs-number">8</span>
  withdraw <span class="hljs-number">2</span>
  sendFallback <span class="hljs-number">5</span>
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span>
  Zero withdrawals: <span class="hljs-number">2</span>

Test result: ok. 1 passed; <span class="hljs-number">0</span> failed; finished in <span class="hljs-number">9</span>.40s
</code></pre><p>Both calls to <code>withdraw</code> were zero withdrawals. Let&apos;s crank up the <code>depth</code> to <code>100</code> calls:</p><pre data-type="codeBlock" text="Running 1 test for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_callSummary()
(runs: 2000, calls: 200000, reverts: 24)
Logs:
  Call summary:
  -------------------
  deposit 31
  withdraw 33
  sendFallback 36
  -------------------
  Zero withdrawals: 33

Test result: ok. 1 passed; 0 failed; finished in 192.91s
"><code>Running <span class="hljs-number">1</span> test <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_callSummary()
(runs: <span class="hljs-number">2000</span>, calls: <span class="hljs-number">200000</span>, reverts: <span class="hljs-number">24</span>)
Logs:
  Call summary:
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span>
  deposit <span class="hljs-number">31</span>
  withdraw <span class="hljs-number">33</span>
  sendFallback <span class="hljs-number">36</span>
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span>
  Zero withdrawals: <span class="hljs-number">33</span>

Test result: ok. 1 passed; <span class="hljs-number">0</span> failed; finished in <span class="hljs-number">192</span>.91s
</code></pre><p>All our calls were still zero withdrawals! We&apos;ll need to constrain our tests a bit further to test our invariants in a meaningful way.</p><h2 id="h-reusing-actors" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Reusing actors</h2><p>Let&apos;s update the way we&apos;re using and tracking actors. Rather than letting the fuzzer choose any random address to call <code>withdraw</code>, let&apos;s ensure that the caller of <code>withdraw</code> is an address that’s already in the <code>_actors</code> set. This should prevent zero withdrawals from random addresses and ensure that withdrawals originate from addresses that have deposited at least once.</p><p>To start, we&apos;ll add a <code>rand(uint256)</code> function to <code>LibAddressSet</code> that takes a random seed and selects an actor address from the stored set. This still gives us an element of randomness, but selects from a constrained set of addresses. (If we don&apos;t want tests to revert when we call <code>rand()</code> before an address has been saved, we can hardcode a return address for the case when the set is empty):</p><pre data-type="codeBlock" text="library LibAddressSet {

    function rand(
        AddressSet storage s, 
        uint256 seed
    ) internal view returns (address) {
        if (s.addrs.length &gt; 0) {
            return s.addrs[seed % s.addrs.length];
        } else {
            return address(0xc0ffee);
        }
    }
}
"><code><span class="hljs-class"><span class="hljs-keyword">library</span> <span class="hljs-title">LibAddressSet</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rand</span>(<span class="hljs-params">
        AddressSet <span class="hljs-keyword">storage</span> s, 
        <span class="hljs-keyword">uint256</span> seed
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>) </span>{
        <span class="hljs-keyword">if</span> (s.addrs.<span class="hljs-built_in">length</span> <span class="hljs-operator">></span> <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">return</span> s.addrs[seed <span class="hljs-operator">%</span> s.addrs.<span class="hljs-built_in">length</span>];
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0xc0ffee</span>);
        }
    }
}
</code></pre><p>Next, let&apos;s remove <code>msg.sender</code> from <code>withdraw</code> and update it to instead select a random caller from our <code>_actors</code> set. To do so, we&apos;ll add an extra <code>uint256 actorSeed</code> argument to the handler function, and pass this through to <code>rand()</code>:</p><pre data-type="codeBlock" text="    function withdraw(
        uint256 actorSeed, 
        uint256 amount
    ) public countCall(&quot;withdraw&quot;) {
        address caller = _actors.rand(actorSeed);
        amount = bound(amount, 0, weth.balanceOf(caller));
        if (amount == 0) ghost_zeroWithdrawals++;

        vm.startPrank(caller);
        weth.withdraw(amount);
        _pay(address(this), amount);
        vm.stopPrank();

        ghost_withdrawSum += amount;
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> actorSeed, 
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">countCall</span>(<span class="hljs-params"><span class="hljs-string">"withdraw"</span></span>) </span>{
        <span class="hljs-keyword">address</span> caller <span class="hljs-operator">=</span> _actors.rand(actorSeed);
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.balanceOf(caller));
        <span class="hljs-keyword">if</span> (amount <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) ghost_zeroWithdrawals<span class="hljs-operator">+</span><span class="hljs-operator">+</span>;

        vm.startPrank(caller);
        weth.withdraw(amount);
        _pay(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount);
        vm.stopPrank();

        ghost_withdrawSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }
</code></pre><p>Now, the fuzzer will generate a random <code>callerSeed</code>, we&apos;ll use it to retrieve a random, but known address from our <code>_actors</code> set, and use this address as the caller using <code>prank</code> when we call the <code>WETH</code> contract.</p><p>Let&apos;s look at the call summary:</p><pre data-type="codeBlock" text="$ forge test -vv -m invariant_callSummary

Running 1 test for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_callSummary()
(runs: 3, calls: 300, reverts: 0)
Logs:
  Call summary:
  -------------------
  deposit 31
  withdraw 35
  sendFallback 34
  -------------------
  Zero withdrawals: 9

Test result: ok. 1 passed; 0 failed; finished in 40.61ms
"><code>$ forge test <span class="hljs-operator">-</span>vv <span class="hljs-operator">-</span>m invariant_callSummary

Running <span class="hljs-number">1</span> test <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_callSummary()
(runs: <span class="hljs-number">3</span>, calls: <span class="hljs-number">300</span>, reverts: <span class="hljs-number">0</span>)
Logs:
  Call summary:
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span>
  deposit <span class="hljs-number">31</span>
  withdraw <span class="hljs-number">35</span>
  sendFallback <span class="hljs-number">34</span>
  <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span>
  Zero withdrawals: <span class="hljs-number">9</span>

Test result: ok. 1 passed; <span class="hljs-number">0</span> failed; finished in <span class="hljs-number">40</span>.61ms
</code></pre><p>Looking better! Note that there will still be <em>some</em> zero withdrawals, since it&apos;s also possible to make zero value deposits. We could choose to constrain the tests further to prevent these, too, but as long as we&apos;re regularly generating runs with mostly nonzero withdrawals, we&apos;re probably OK for now.</p><p>To wrap up, let&apos;s extract this into a modifier that selects a random actor from a seed and sets their address as <code>currentActor</code>:</p><pre data-type="codeBlock" text="    modifier useActor(uint256 actorIndexSeed) {
        currentActor = _actors.rand(actorIndexSeed);
        _;
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">useActor</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> actorIndexSeed</span>) </span>{
        currentActor <span class="hljs-operator">=</span> _actors.rand(actorIndexSeed);
        <span class="hljs-keyword">_</span>;
    }
</code></pre><p>We can use it like this to clean up our tests a bit:</p><pre data-type="codeBlock" text="    function withdraw(
        uint256 actorSeed, 
        uint256 amount
    ) public useActor(actorSeed) countCall(&quot;withdraw&quot;) {
        amount = bound(amount, 0, weth.balanceOf(currentActor));
        if (amount == 0) ghost_zeroWithdrawals++;

        vm.startPrank(currentActor);
        weth.withdraw(amount);
        _pay(address(this), amount);
        vm.stopPrank();

        ghost_withdrawSum += amount;
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> actorSeed, 
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">useActor</span>(<span class="hljs-params">actorSeed</span>) <span class="hljs-title">countCall</span>(<span class="hljs-params"><span class="hljs-string">"withdraw"</span></span>) </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.balanceOf(currentActor));
        <span class="hljs-keyword">if</span> (amount <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) ghost_zeroWithdrawals<span class="hljs-operator">+</span><span class="hljs-operator">+</span>;

        vm.startPrank(currentActor);
        weth.withdraw(amount);
        _pay(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount);
        vm.stopPrank();

        ghost_withdrawSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }
</code></pre><h2 id="h-including-transfers" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Including transfers</h2><p>We still haven&apos;t exposed <code>approve</code>, <code>transfer</code>, and <code>transferFrom</code> from our handler. Let&apos;s do that now. We&apos;ll want to constrain these to known actors like we did with <code>withdraw</code>:</p><pre data-type="codeBlock" text="    function approve(
        uint256 actorSeed,
        uint256 spenderSeed,
        uint256 amount
    ) public useActor(actorSeed) countCall(&quot;approve&quot;) {
        address spender = _actors.rand(spenderSeed);

        vm.prank(currentActor);
        weth.approve(spender, amount);
    }

    function transfer(
        uint256 actorSeed,
        uint256 toSeed,
        uint256 amount
    ) public useActor(actorSeed) countCall(&quot;transfer&quot;) {
        address to = _actors.rand(toSeed);

        amount = bound(amount, 0, weth.balanceOf(currentActor));

        vm.prank(currentActor);
        weth.transfer(to, amount);
    }

    function transferFrom(
        uint256 actorSeed,
        uint256 fromSeed,
        uint256 toSeed,
        uint256 amount
    ) public useActor(actorSeed) countCall(&quot;transferFrom&quot;)
    {
        address from = _actors.rand(fromSeed);
        address to = _actors.rand(toSeed);

        amount = bound(amount, 0, weth.balanceOf(from));
        amount = bound(amount, 0, weth.allowance(caller, from));

        vm.prank(currentActor);
        weth.transferFrom(from, to, amount);
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">approve</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> actorSeed,
        <span class="hljs-keyword">uint256</span> spenderSeed,
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">useActor</span>(<span class="hljs-params">actorSeed</span>) <span class="hljs-title">countCall</span>(<span class="hljs-params"><span class="hljs-string">"approve"</span></span>) </span>{
        <span class="hljs-keyword">address</span> spender <span class="hljs-operator">=</span> _actors.rand(spenderSeed);

        vm.prank(currentActor);
        weth.approve(spender, amount);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transfer</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> actorSeed,
        <span class="hljs-keyword">uint256</span> toSeed,
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">useActor</span>(<span class="hljs-params">actorSeed</span>) <span class="hljs-title">countCall</span>(<span class="hljs-params"><span class="hljs-string">"transfer"</span></span>) </span>{
        <span class="hljs-keyword">address</span> to <span class="hljs-operator">=</span> _actors.rand(toSeed);

        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.balanceOf(currentActor));

        vm.prank(currentActor);
        weth.<span class="hljs-built_in">transfer</span>(to, amount);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transferFrom</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> actorSeed,
        <span class="hljs-keyword">uint256</span> fromSeed,
        <span class="hljs-keyword">uint256</span> toSeed,
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">useActor</span>(<span class="hljs-params">actorSeed</span>) <span class="hljs-title">countCall</span>(<span class="hljs-params"><span class="hljs-string">"transferFrom"</span></span>)
    </span>{
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">from</span> <span class="hljs-operator">=</span> _actors.rand(fromSeed);
        <span class="hljs-keyword">address</span> to <span class="hljs-operator">=</span> _actors.rand(toSeed);

        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.balanceOf(<span class="hljs-keyword">from</span>));
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.allowance(caller, <span class="hljs-keyword">from</span>));

        vm.prank(currentActor);
        weth.transferFrom(<span class="hljs-keyword">from</span>, to, amount);
    }
</code></pre><p>Some of these require multiple seed arguments in order to select multiple actors.</p><p>Note that we call <code>bound</code> twice in <code>transferFrom</code> to ensure the transfer value is less than the <code>from</code> account&apos;s balance <em>and</em> that <code>currentActor</code> has a sufficient allowance to perform the third-party transfer. If you look carefully at this, you may notice we have a similar problem to the zero amount issue we just solved for <code>withdraw</code>: even though we&apos;re reusing known callers, most of the time <code>amount</code> will be zero, since it&apos;s unlikely the <code>caller</code> has an approval from the <code>from</code> account. (You can use the same call summary process to debug yourself if you&apos;re interested).</p><p>Let&apos;s add a branch in the handler function that ensures nonzero <code>transferFrom</code> amounts at least <em>some</em> of the time, by approving the caller before making the <code>transferFrom</code> call. We&apos;ll add one more argument to the handler function, a boolean <code>_approve</code> that will sometimes pre-approve the caller:</p><pre data-type="codeBlock" text="    function transferFrom(
        uint256 actorSeed,
        uint256 fromSeed,
        uint256 toSeed,
        bool _approve,
        uint256 amount
    ) public useActor(actorSeed) countCall(&quot;transferFrom&quot;)
    {
        address from = _actors.rand(fromSeed);
        address to = _actors.rand(toSeed);

        amount = bound(amount, 0, weth.balanceOf(from));

        if (_approve) {
            vm.prank(from);
            weth.approve(currentActor, amount);
        } else {
            amount = bound(amount, 0, weth.allowance(currentActor, from));
        }

        vm.prank(currentActor);
        weth.transferFrom(from, to, amount);
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transferFrom</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> actorSeed,
        <span class="hljs-keyword">uint256</span> fromSeed,
        <span class="hljs-keyword">uint256</span> toSeed,
        <span class="hljs-keyword">bool</span> _approve,
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">useActor</span>(<span class="hljs-params">actorSeed</span>) <span class="hljs-title">countCall</span>(<span class="hljs-params"><span class="hljs-string">"transferFrom"</span></span>)
    </span>{
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">from</span> <span class="hljs-operator">=</span> _actors.rand(fromSeed);
        <span class="hljs-keyword">address</span> to <span class="hljs-operator">=</span> _actors.rand(toSeed);

        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.balanceOf(<span class="hljs-keyword">from</span>));

        <span class="hljs-keyword">if</span> (_approve) {
            vm.prank(<span class="hljs-keyword">from</span>);
            weth.approve(currentActor, amount);
        } <span class="hljs-keyword">else</span> {
            amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, weth.allowance(currentActor, <span class="hljs-keyword">from</span>));
        }

        vm.prank(currentActor);
        weth.transferFrom(<span class="hljs-keyword">from</span>, to, amount);
    }
</code></pre><p>Don&apos;t forget to add these new selectors to our configuration in <code>setUp</code>:</p><pre data-type="codeBlock" text="    function setUp() public {
        weth = new WETH9();
        handler = new Handler(weth);

        bytes4[] memory selectors = new bytes4Unsupported embed;
        selectors[0] = Handler.deposit.selector;
        selectors[1] = Handler.withdraw.selector;
        selectors[2] = Handler.sendFallback.selector;
        selectors[3] = Handler.approve.selector;
        selectors[4] = Handler.transfer.selector;
        selectors[5] = Handler.transferFrom.selector;

        targetSelector(
          FuzzSelector({
            addr: address(handler),
            selectors: selectors
          }
        ));

        targetContract(address(handler));
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        weth <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> WETH9();
        handler <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Handler(weth);

        <span class="hljs-keyword">bytes4</span>[] <span class="hljs-keyword">memory</span> selectors <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> bytes4Unsupported embed;
        selectors[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> Handler.deposit.<span class="hljs-built_in">selector</span>;
        selectors[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> Handler.withdraw.<span class="hljs-built_in">selector</span>;
        selectors[<span class="hljs-number">2</span>] <span class="hljs-operator">=</span> Handler.sendFallback.<span class="hljs-built_in">selector</span>;
        selectors[<span class="hljs-number">3</span>] <span class="hljs-operator">=</span> Handler.approve.<span class="hljs-built_in">selector</span>;
        selectors[<span class="hljs-number">4</span>] <span class="hljs-operator">=</span> Handler.<span class="hljs-built_in">transfer</span>.<span class="hljs-built_in">selector</span>;
        selectors[<span class="hljs-number">5</span>] <span class="hljs-operator">=</span> Handler.transferFrom.<span class="hljs-built_in">selector</span>;

        targetSelector(
          FuzzSelector({
            addr: <span class="hljs-keyword">address</span>(handler),
            selectors: selectors
          }
        ));

        targetContract(<span class="hljs-keyword">address</span>(handler));
    }
</code></pre><pre data-type="codeBlock" text="Running 4 tests for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: 1000, calls: 15000, reverts: 8)
[PASS] invariant_depositorBalances()
(runs: 1000, calls: 15000, reverts: 8)
[PASS] invariant_solvencyBalances()
(runs: 1000, calls: 15000, reverts: 8)
[PASS] invariant_solvencyDeposits()
(runs: 1000, calls: 15000, reverts: 8)
Test result: ok. 4 passed; 0 failed; finished in 5.87s
"><code><span class="hljs-selector-tag">Running</span> <span class="hljs-number">4</span> <span class="hljs-selector-tag">tests</span> <span class="hljs-selector-tag">for</span> <span class="hljs-selector-tag">test</span>/<span class="hljs-selector-tag">WETH9</span><span class="hljs-selector-class">.invariants</span><span class="hljs-selector-class">.t</span><span class="hljs-selector-class">.sol</span>:<span class="hljs-selector-tag">WETH9Invariants</span>
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_conservationOfETH</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">1000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">15000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">8</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_depositorBalances</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">1000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">15000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">8</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_solvencyBalances</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">1000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">15000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">8</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_solvencyDeposits</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">1000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">15000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">8</span>)
<span class="hljs-selector-tag">Test</span> <span class="hljs-selector-tag">result</span>: <span class="hljs-selector-tag">ok</span>. <span class="hljs-number">4</span> <span class="hljs-selector-tag">passed</span>; <span class="hljs-number">0</span> <span class="hljs-selector-tag">failed</span>; <span class="hljs-selector-tag">finished</span> <span class="hljs-selector-tag">in</span> <span class="hljs-number">5.87s</span>
</code></pre><h2 id="h-testing-our-tests" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Testing our tests</h2><p>We&apos;ve changed quite a lot of supporting infrastructure and our tests still pass. But are we sure we can really trust them? Unlike unit tests, where mapping one specific input to one expected output is usually pretty clear, I find that invariant tests can sometimes be tricky and accidentally pass when we&apos;ve introduced an incorrect assumption about the system or a condition that is vacuously true.</p><p>One way to ensure our tests are really working is to <em>test our tests</em> by introducing artificial bugs. Let&apos;s intentionally break <code>deposit</code> accounting in <code>WETH9</code>, run our tests, and see if they fail. Instead of issuing <code>msg.sender</code> an amount of WETH equal to <code>msg.value</code>, let&apos;s give them just 1 wei instead:</p><pre data-type="codeBlock" text="    function deposit() public payable {
        balanceOf[msg.sender] += 1;
        emit Deposit(msg.sender, msg.value);
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
        balanceOf[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
        <span class="hljs-keyword">emit</span> Deposit(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>);
    }
</code></pre><p>If our invariant tests are any good, they should catch the bug we introduced:</p><pre data-type="codeBlock" text="$ forge test
Running 4 tests for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: 2000, calls: 29974, reverts: 3)
[FAIL. Reason: Assertion failed.]
        [Sequence]
                sender=0x849a5a123d8d365eef30374417ef4fcbba5a9781
                addr=[test/handlers/Handler.sol:Handler]
                     0x2e234dae75c793f67a35089c9d99245e1c58470b
                calldata=withdraw(uint256),
                args=[365161364]
                sender=0xcbac49e135a0340b2fca24685962c08ed3aa81c7
                addr=[test/handlers/Handler.sol:Handler]
                     0x2e234dae75c793f67a35089c9d99245e1c58470b
                calldata=sendFallback(uint256),
                args=[0]

 invariant_depositorBalances()
 (runs: 2000, calls: 29974, reverts: 3)
[FAIL. Reason: Assertion failed.]
        [Sequence]
                sender=0x000000000000000000000000000000000000006a
                addr=[test/handlers/Handler.sol:Handler]
                     0x2e234dae75c793f67a35089c9d99245e1c58470b
                calldata=approve(address,uint256),
                args=[0x6D50393ED4ed2f7A64e40bdCA11E430dC276bbf3, 26589664]
                sender=0x0f9be7012c9f187334111c3a4f6811e6132e4815
                addr=[test/handlers/Handler.sol:Handler]
                    0x2e234dae75c793f67a35089c9d99245e1c58470b
                calldata=sendFallback(uint256),
                args=[2936]

 invariant_solvencyBalances() (runs: 2000, calls: 29974, reverts: 3)
[PASS] invariant_solvencyDeposits() (runs: 2000, calls: 29974, reverts: 3)
Test result: FAILED. 2 passed; 2 failed; finished in 16.18s
"><code>$ forge test
Running <span class="hljs-number">4</span> tests <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: <span class="hljs-number">2000</span>, calls: <span class="hljs-number">29974</span>, reverts: <span class="hljs-number">3</span>)
[FAIL. Reason: Assertion failed.]
        [Sequence]
                sender<span class="hljs-operator">=</span><span class="hljs-number">0x849a5a123d8d365eef30374417ef4fcbba5a9781</span>
                addr<span class="hljs-operator">=</span>[test<span class="hljs-operator">/</span>handlers<span class="hljs-operator">/</span>Handler.sol:Handler]
                     <span class="hljs-number">0x2e234dae75c793f67a35089c9d99245e1c58470b</span>
                <span class="hljs-keyword">calldata</span><span class="hljs-operator">=</span>withdraw(<span class="hljs-keyword">uint256</span>),
                args<span class="hljs-operator">=</span>[<span class="hljs-number">365161364</span>]
                sender<span class="hljs-operator">=</span><span class="hljs-number">0xcbac49e135a0340b2fca24685962c08ed3aa81c7</span>
                addr<span class="hljs-operator">=</span>[test<span class="hljs-operator">/</span>handlers<span class="hljs-operator">/</span>Handler.sol:Handler]
                     <span class="hljs-number">0x2e234dae75c793f67a35089c9d99245e1c58470b</span>
                <span class="hljs-keyword">calldata</span><span class="hljs-operator">=</span>sendFallback(<span class="hljs-keyword">uint256</span>),
                args<span class="hljs-operator">=</span>[<span class="hljs-number">0</span>]

 invariant_depositorBalances()
 (runs: <span class="hljs-number">2000</span>, calls: <span class="hljs-number">29974</span>, reverts: <span class="hljs-number">3</span>)
[FAIL. Reason: Assertion failed.]
        [Sequence]
                sender<span class="hljs-operator">=</span><span class="hljs-number">0x000000000000000000000000000000000000006a</span>
                addr<span class="hljs-operator">=</span>[test<span class="hljs-operator">/</span>handlers<span class="hljs-operator">/</span>Handler.sol:Handler]
                     <span class="hljs-number">0x2e234dae75c793f67a35089c9d99245e1c58470b</span>
                <span class="hljs-keyword">calldata</span><span class="hljs-operator">=</span>approve(<span class="hljs-keyword">address</span>,<span class="hljs-keyword">uint256</span>),
                args<span class="hljs-operator">=</span>[<span class="hljs-number">0x6D50393ED4ed2f7A64e40bdCA11E430dC276bbf3</span>, <span class="hljs-number">26589664</span>]
                sender<span class="hljs-operator">=</span><span class="hljs-number">0x0f9be7012c9f187334111c3a4f6811e6132e4815</span>
                addr<span class="hljs-operator">=</span>[test<span class="hljs-operator">/</span>handlers<span class="hljs-operator">/</span>Handler.sol:Handler]
                    <span class="hljs-number">0x2e234dae75c793f67a35089c9d99245e1c58470b</span>
                <span class="hljs-keyword">calldata</span><span class="hljs-operator">=</span>sendFallback(<span class="hljs-keyword">uint256</span>),
                args<span class="hljs-operator">=</span>[<span class="hljs-number">2936</span>]

 invariant_solvencyBalances() (runs: <span class="hljs-number">2000</span>, calls: <span class="hljs-number">29974</span>, reverts: <span class="hljs-number">3</span>)
[PASS] invariant_solvencyDeposits() (runs: <span class="hljs-number">2000</span>, calls: <span class="hljs-number">29974</span>, reverts: <span class="hljs-number">3</span>)
Test result: FAILED. 2 passed; <span class="hljs-number">2</span> failed; finished in <span class="hljs-number">16</span>.18s
</code></pre><p>Looks like they do: we broke the &quot;depositor balances&quot; and &quot;balance solvency&quot; invariants.</p><p>A technique I like to use here is to save a few bugs in the contract under test as git <code>.patch</code> files in a <code>bugs</code> folder inside our project repo. We can then reapply them from time to time to double check that our test suite still works as expected:</p><pre data-type="codeBlock" text="$ git diff &gt; bugs/bug1.patch
$ cat bugs/bug1.patch
diff --git a/src/WETH9.sol b/src/WETH9.sol
index cd55b98..ccb40cb 100644
--- a/src/WETH9.sol
+++ b/src/WETH9.sol
@@ -33,7 +33,7 @@ contract WETH9 {
     }

     function deposit() public payable {
-        balanceOf[msg.sender] += msg.value;
+        balanceOf[msg.sender] += 1;
         emit Deposit(msg.sender, msg.value);
     }
"><code>$ git diff <span class="hljs-operator">></span> bugs<span class="hljs-operator">/</span>bug1.patch
$ cat bugs<span class="hljs-operator">/</span>bug1.patch
diff <span class="hljs-operator">-</span><span class="hljs-operator">-</span>git a<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol b<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol
index cd55b98..ccb40cb <span class="hljs-number">100644</span>
<span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span> a<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol
<span class="hljs-operator">+</span><span class="hljs-operator">+</span><span class="hljs-operator">+</span> b<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol
@@ <span class="hljs-number">-33</span>,<span class="hljs-number">7</span> <span class="hljs-operator">+</span><span class="hljs-number">33</span>,<span class="hljs-number">7</span> @@ <span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">WETH9</span> </span>{
     }

     <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
<span class="hljs-operator">-</span>        balanceOf[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>;
<span class="hljs-operator">+</span>        balanceOf[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
         <span class="hljs-keyword">emit</span> Deposit(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>);
     }
</code></pre><p>Let&apos;s add a few more bug patches. We&apos;ll alter <code>withdraw</code> to send back only 1 wei:</p><pre data-type="codeBlock" text="diff --git a/src/WETH9.sol b/src/WETH9.sol
index cd55b98..961f03b 100644
--- a/src/WETH9.sol
+++ b/src/WETH9.sol
@@ -40,7 +40,7 @@ contract WETH9 {
     function withdraw(uint256 wad) public {
         require(balanceOf[msg.sender] &gt;= wad);
         balanceOf[msg.sender] -= wad;
-        payable(msg.sender).transfer(wad);
+        payable(msg.sender).transfer(1);
         emit Withdrawal(msg.sender, wad);
     }
"><code>diff <span class="hljs-operator">-</span><span class="hljs-operator">-</span>git a<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol b<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol
index cd55b98..961f03b <span class="hljs-number">100644</span>
<span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span> a<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol
<span class="hljs-operator">+</span><span class="hljs-operator">+</span><span class="hljs-operator">+</span> b<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol
@@ <span class="hljs-number">-40</span>,<span class="hljs-number">7</span> <span class="hljs-operator">+</span><span class="hljs-number">40</span>,<span class="hljs-number">7</span> @@ <span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">WETH9</span> </span>{
     <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> wad</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
         <span class="hljs-built_in">require</span>(balanceOf[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">></span><span class="hljs-operator">=</span> wad);
         balanceOf[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> wad;
<span class="hljs-operator">-</span>        <span class="hljs-keyword">payable</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>).<span class="hljs-built_in">transfer</span>(wad);
<span class="hljs-operator">+</span>        <span class="hljs-keyword">payable</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>).<span class="hljs-built_in">transfer</span>(<span class="hljs-number">1</span>);
         <span class="hljs-keyword">emit</span> Withdrawal(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, wad);
     }
</code></pre><p>Remove the call to <code>deposit</code> in the fallback function:</p><pre data-type="codeBlock" text="diff --git a/src/WETH9.sol b/src/WETH9.sol
index cd55b98..6e74bd5 100644
--- a/src/WETH9.sol
+++ b/src/WETH9.sol
@@ -29,7 +29,6 @@ contract WETH9 {
     mapping(address =&gt; mapping(address =&gt; uint256)) public allowance;

     fallback() external payable {
-        deposit();
     }

     function deposit() public payable {
"><code>diff <span class="hljs-operator">-</span><span class="hljs-operator">-</span>git a<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol b<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol
index cd55b98..6e74bd5 <span class="hljs-number">100644</span>
<span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span> a<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol
<span class="hljs-operator">+</span><span class="hljs-operator">+</span><span class="hljs-operator">+</span> b<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol
@@ <span class="hljs-number">-29</span>,<span class="hljs-number">7</span> <span class="hljs-operator">+</span><span class="hljs-number">29</span>,<span class="hljs-number">6</span> @@ <span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">WETH9</span> </span>{
     <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint256</span>)) <span class="hljs-keyword">public</span> allowance;

     <span class="hljs-function"><span class="hljs-keyword">fallback</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
<span class="hljs-operator">-</span>        deposit();
     }

     <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
</code></pre><p>And remove auth checks and change the balance logic in <code>transferFrom</code>:</p><pre data-type="codeBlock" text="diff --git a/src/WETH9.sol b/src/WETH9.sol
index cd55b98..26cec99 100644
--- a/src/WETH9.sol
+++ b/src/WETH9.sol
@@ -59,15 +59,8 @@ contract WETH9 {
     }

     function transferFrom(address src, address dst, uint256 wad) public returns (bool) {
-        require(balanceOf[src] &gt;= wad);
-
-        if (src != msg.sender &amp;&amp; allowance[src][msg.sender] != type(uint256).max) {
-            require(allowance[src][msg.sender] &gt;= wad);
-            allowance[src][msg.sender] -= wad;
-        }
-
-        balanceOf[src] -= wad;
-        balanceOf[dst] += wad;
+        balanceOf[src] -= wad;
+        balanceOf[dst] += 1;

         emit Transfer(src, dst, wad);v
"><code>diff <span class="hljs-operator">-</span><span class="hljs-operator">-</span>git a<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol b<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol
index cd55b98..26cec99 <span class="hljs-number">100644</span>
<span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span> a<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol
<span class="hljs-operator">+</span><span class="hljs-operator">+</span><span class="hljs-operator">+</span> b<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>WETH9.sol
@@ <span class="hljs-number">-59</span>,<span class="hljs-number">15</span> <span class="hljs-operator">+</span><span class="hljs-number">59</span>,<span class="hljs-number">8</span> @@ <span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">WETH9</span> </span>{
     }

     <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transferFrom</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> src, <span class="hljs-keyword">address</span> dst, <span class="hljs-keyword">uint256</span> wad</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
<span class="hljs-operator">-</span>        <span class="hljs-built_in">require</span>(balanceOf[src] <span class="hljs-operator">></span><span class="hljs-operator">=</span> wad);
<span class="hljs-operator">-</span>
<span class="hljs-operator">-</span>        <span class="hljs-keyword">if</span> (src <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> allowance[src][<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">type</span>(<span class="hljs-keyword">uint256</span>).<span class="hljs-built_in">max</span>) {
<span class="hljs-operator">-</span>            <span class="hljs-built_in">require</span>(allowance[src][<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">></span><span class="hljs-operator">=</span> wad);
<span class="hljs-operator">-</span>            allowance[src][<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> wad;
<span class="hljs-operator">-</span>        }
<span class="hljs-operator">-</span>
<span class="hljs-operator">-</span>        balanceOf[src] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> wad;
<span class="hljs-operator">-</span>        balanceOf[dst] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> wad;
<span class="hljs-operator">+</span>        balanceOf[src] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> wad;
<span class="hljs-operator">+</span>        balanceOf[dst] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;

         <span class="hljs-keyword">emit</span> Transfer(src, dst, wad);v
</code></pre><p>If we <code>git apply</code> each patch in turn and verify that our tests really do catch each artificial bug, we can be pretty confident that they are working:</p><pre data-type="codeBlock" text="$ git apply bugs/bug2.patch
$ forge test
Running 4 tests for test/WETH9.invariants.t.sol:WETH9Invariants
[FAIL. Reason: Assertion failed.]
 invariant_conservationOfETH() (runs: 6, calls: 84, reverts: 1)
[FAIL. Reason: Assertion failed.]
 invariant_depositorBalances() (runs: 6, calls: 84, reverts: 1)
[FAIL. Reason: Assertion failed.]
 invariant_solvencyBalances() (runs: 6, calls: 84, reverts: 1)
[FAIL. Reason: Assertion failed.]
 invariant_solvencyDeposits() (runs: 6, calls: 84, reverts: 1)
Test result: FAILED. 0 passed; 4 failed; finished in 212.99ms

Encountered a total of 4 failing tests, 0 tests succeeded
"><code>$ git apply bugs<span class="hljs-operator">/</span>bug2.patch
$ forge test
Running <span class="hljs-number">4</span> tests <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[FAIL. Reason: Assertion failed.]
 invariant_conservationOfETH() (runs: <span class="hljs-number">6</span>, calls: <span class="hljs-number">84</span>, reverts: <span class="hljs-number">1</span>)
[FAIL. Reason: Assertion failed.]
 invariant_depositorBalances() (runs: <span class="hljs-number">6</span>, calls: <span class="hljs-number">84</span>, reverts: <span class="hljs-number">1</span>)
[FAIL. Reason: Assertion failed.]
 invariant_solvencyBalances() (runs: <span class="hljs-number">6</span>, calls: <span class="hljs-number">84</span>, reverts: <span class="hljs-number">1</span>)
[FAIL. Reason: Assertion failed.]
 invariant_solvencyDeposits() (runs: <span class="hljs-number">6</span>, calls: <span class="hljs-number">84</span>, reverts: <span class="hljs-number">1</span>)
Test result: FAILED. 0 passed; <span class="hljs-number">4</span> failed; finished in <span class="hljs-number">212</span>.99ms

Encountered a total of <span class="hljs-number">4</span> failing tests, <span class="hljs-number">0</span> tests succeeded
</code></pre><p>We can add a simple Makefile that will apply a given patch and run tests:</p><pre data-type="codeBlock" text="check:
    git apply &quot;bugs/$(bug).patch&quot; &amp;&amp; forge test

clean:
    git checkout src/WETH9.sol
"><code>check:
    git apply <span class="hljs-string">"bugs/$(bug).patch"</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> forge test

clean:
    git checkout src<span class="hljs-operator">/</span>WETH9.sol
</code></pre><p>To apply a patch and run tests, run:</p><pre data-type="codeBlock" text="$ make bug=bug1 check
"><code>$ make <span class="hljs-attr">bug</span>=bug1 check
</code></pre><p>To undo changes, run:</p><pre data-type="codeBlock" text="$ make clean
"><code>$ <span class="hljs-built_in">make</span> clean
</code></pre><p>This is a quick and dirty form of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://en.wikipedia.org/wiki/Mutation_testing">mutation testing</a>, which may be coming as a native Foundry feature <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/foundry-rs/foundry/issues/478">some time soon</a>.</p><h2 id="h-accounting-for-selfdestruct" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Accounting for <code>selfdestruct</code></h2><p>Our tests seem to be pretty comprehensive, but there is one final boss battle before we can call them complete.</p><p>There is one intuitive invariant that famously <em>does not hold</em> for the WETH contract. It has to do with the way <code>WETH9</code> calculates <code>totalSupply()</code>:</p><pre data-type="codeBlock" text="    function totalSupply() public view returns (uint256) {
        return address(this).balance;
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">totalSupply</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>;
    }
</code></pre><p>Rather than storing the total token balance in a separate state variable, the WETH contract uses its total Ether balance as the total token supply. This saves gas, but actually <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.zellic.io/blog/formal-verification-weth">breaks the invariant</a> that <code>weth.totalSupply()</code> equals the sum of all balances!</p><p>There is one clever way to force <code>WETH9</code> to increase <code>totalSupply()</code> without creating new WETH tokens: calling <code>selfdestruct</code> on a contract to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.soliditylang.org/en/latest/units-and-global-variables.html#contract-related">force push Ether</a> to its contract balance.</p><p>The Foundry fuzzer can do a lot of things, but it won&apos;t do that. We&apos;ll need to simulate this scenario in our handler ourselves. Let&apos;s add a <code>ForcePush</code> contract that will <code>selfdestruct</code> and send Ether to the WETH contract:</p><pre data-type="codeBlock" text="contract ForcePush {
    constructor(address dst) payable {
        selfdestruct(payable(dst));
    }
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">ForcePush</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> dst</span>) <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
        <span class="hljs-built_in">selfdestruct</span>(<span class="hljs-keyword">payable</span>(dst));
    }
}
</code></pre><p>This contract will immediately destroy itself at construction time and send any balance to the <code>dst</code> address in its constructor.</p><p>We&apos;ll add a handler function to invoke it:</p><pre data-type="codeBlock" text="    function forcePush(
        uint256 amount
    ) public countCall(&quot;forcePush&quot;) {
        amount = bound(amount, 0, address(this).balance);
        new ForcePush{ value: amount }(address(weth));
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">forcePush</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">countCall</span>(<span class="hljs-params"><span class="hljs-string">"forcePush"</span></span>) </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
        <span class="hljs-keyword">new</span> ForcePush{ <span class="hljs-built_in">value</span>: amount }(<span class="hljs-keyword">address</span>(weth));
    }
</code></pre><p>And finally, register our handler function&apos;s selector with the fuzzer:</p><pre data-type="codeBlock" text="    function setUp() public {
        weth = new WETH9();
        handler = new Handler(weth);

        bytes4[] memory selectors = new bytes4Unsupported embed;
        selectors[0] = Handler.deposit.selector;
        selectors[1] = Handler.withdraw.selector;
        selectors[2] = Handler.sendFallback.selector;
        selectors[3] = Handler.approve.selector;
        selectors[4] = Handler.transfer.selector;
        selectors[5] = Handler.transferFrom.selector;
        selectors[6] = Handler.forcePush.selector;

        targetSelector(FuzzSelector({
            addr: address(handler), 
            selectors: selectors
        }));

        targetContract(address(weth));
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        weth <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> WETH9();
        handler <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Handler(weth);

        <span class="hljs-keyword">bytes4</span>[] <span class="hljs-keyword">memory</span> selectors <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> bytes4Unsupported embed;
        selectors[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> Handler.deposit.<span class="hljs-built_in">selector</span>;
        selectors[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> Handler.withdraw.<span class="hljs-built_in">selector</span>;
        selectors[<span class="hljs-number">2</span>] <span class="hljs-operator">=</span> Handler.sendFallback.<span class="hljs-built_in">selector</span>;
        selectors[<span class="hljs-number">3</span>] <span class="hljs-operator">=</span> Handler.approve.<span class="hljs-built_in">selector</span>;
        selectors[<span class="hljs-number">4</span>] <span class="hljs-operator">=</span> Handler.<span class="hljs-built_in">transfer</span>.<span class="hljs-built_in">selector</span>;
        selectors[<span class="hljs-number">5</span>] <span class="hljs-operator">=</span> Handler.transferFrom.<span class="hljs-built_in">selector</span>;
        selectors[<span class="hljs-number">6</span>] <span class="hljs-operator">=</span> Handler.forcePush.<span class="hljs-built_in">selector</span>;

        targetSelector(FuzzSelector({
            addr: <span class="hljs-keyword">address</span>(handler), 
            selectors: selectors
        }));

        targetContract(<span class="hljs-keyword">address</span>(weth));
    }
</code></pre><p>Our tests now fail, and it looks like they&apos;re failing in the places we should expect:</p><pre data-type="codeBlock" text="Running 4 tests for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: 5000, calls: 74986, reverts: 9)
[PASS] invariant_depositorBalances()
(runs: 5000, calls: 74986, reverts: 9)
[FAIL. Reason: Assertion failed.]
        [Sequence]
                sender=0x0000000000000000000000000000000000000b69
                addr=[test/handlers/Handler.sol:Handler]
                     0x2e234dae75c793f67a35089c9d99245e1c58470b
                calldata=forcePush(uint256),
                args=[2250]

 invariant_solvencyBalances() (runs: 5000, calls: 74986, reverts: 9)
[FAIL. Reason: Assertion failed.]
        [Sequence]
                sender=0x0000000000000000000000000000000000000b69
                addr=[test/handlers/Handler.sol:Handler]
                     0x2e234dae75c793f67a35089c9d99245e1c58470b
                calldata=forcePush(uint256),
                args=[2250]

 invariant_solvencyDeposits() (runs: 5000, calls: 74986, reverts: 9)
Test result: FAILED. 2 passed; 2 failed; finished in 68.53s
"><code>Running <span class="hljs-number">4</span> tests <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: <span class="hljs-number">5000</span>, calls: <span class="hljs-number">74986</span>, reverts: <span class="hljs-number">9</span>)
[PASS] invariant_depositorBalances()
(runs: <span class="hljs-number">5000</span>, calls: <span class="hljs-number">74986</span>, reverts: <span class="hljs-number">9</span>)
[FAIL. Reason: Assertion failed.]
        [Sequence]
                sender<span class="hljs-operator">=</span><span class="hljs-number">0x0000000000000000000000000000000000000b69</span>
                addr<span class="hljs-operator">=</span>[test<span class="hljs-operator">/</span>handlers<span class="hljs-operator">/</span>Handler.sol:Handler]
                     <span class="hljs-number">0x2e234dae75c793f67a35089c9d99245e1c58470b</span>
                <span class="hljs-keyword">calldata</span><span class="hljs-operator">=</span>forcePush(<span class="hljs-keyword">uint256</span>),
                args<span class="hljs-operator">=</span>[<span class="hljs-number">2250</span>]

 invariant_solvencyBalances() (runs: <span class="hljs-number">5000</span>, calls: <span class="hljs-number">74986</span>, reverts: <span class="hljs-number">9</span>)
[FAIL. Reason: Assertion failed.]
        [Sequence]
                sender<span class="hljs-operator">=</span><span class="hljs-number">0x0000000000000000000000000000000000000b69</span>
                addr<span class="hljs-operator">=</span>[test<span class="hljs-operator">/</span>handlers<span class="hljs-operator">/</span>Handler.sol:Handler]
                     <span class="hljs-number">0x2e234dae75c793f67a35089c9d99245e1c58470b</span>
                <span class="hljs-keyword">calldata</span><span class="hljs-operator">=</span>forcePush(<span class="hljs-keyword">uint256</span>),
                args<span class="hljs-operator">=</span>[<span class="hljs-number">2250</span>]

 invariant_solvencyDeposits() (runs: <span class="hljs-number">5000</span>, calls: <span class="hljs-number">74986</span>, reverts: <span class="hljs-number">9</span>)
Test result: FAILED. 2 passed; <span class="hljs-number">2</span> failed; finished in <span class="hljs-number">68</span>.53s
</code></pre><p>&quot;Conservation of ETH&quot; still passes. Since it&apos;s a property of <code>weth.totalSupply()</code> and the handler balance, it&apos;s not affected by the balance inconsistency, as <code>weth.totalSupply()</code> accounts for the full <code>address(weth).balance</code>. So does our depositor balance invariant, since it&apos;s still the case that no depositor&apos;s balance can exceed <code>weth.totalSupply()</code>.</p><p>But our two solvency invariants will need an update. We could simply relax the invariant to check that the WETH contract&apos;s Ether balance is <em>at least as much</em> as the individual deposits/balances. This is a reasonable property, and still means the contract is solvent. But we have the ability to account for the amount of force-pushed Ether exactly, so let&apos;s do so.</p><p>We&apos;ll add one more ghost variable to our handler and increment it when we force push Ether:</p><pre data-type="codeBlock" text="    uint256 public ghost_forcePushSum;

    function forcePush(
        uint256 amount
    ) public countCall(&quot;forcePush&quot;) {
        amount = bound(amount, 0, address(this).balance);
        new ForcePush{ value: amount }(address(weth));
        ghost_forcePushSum += amount;
    }
"><code>    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> ghost_forcePushSum;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">forcePush</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">countCall</span>(<span class="hljs-params"><span class="hljs-string">"forcePush"</span></span>) </span>{
        amount <span class="hljs-operator">=</span> bound(amount, <span class="hljs-number">0</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
        <span class="hljs-keyword">new</span> ForcePush{ <span class="hljs-built_in">value</span>: amount }(<span class="hljs-keyword">address</span>(weth));
        ghost_forcePushSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    }
</code></pre><p>And update our invariants to account for this extra Ether:</p><pre data-type="codeBlock" text="    // The WETH contract&apos;s Ether balance should always be
    // equal to the sum of all individual deposits
    // minus all individual withrawals, plus any
    // force-pushed Ether in the contract
    function invariant_solvencyDeposits() public {
        assertEq(
            address(weth).balance,
            handler.ghost_depositSum() +
            handler.ghost_forcePushSum() -
            handler.ghost_withdrawSum()
        );
    }

    // The WETH contract&apos;s Ether balance should always be
    // equal to the sum of individual balances plus any
    // force-pushed Ether in the contract
    function invariant_solvencyBalances() public {
        uint256 sumOfBalances = handler.reduceActors(
            0, 
            this.accumulateBalance
        );
        assertEq(
            address(weth).balance - handler.ghost_forcePushSum(),
            sumOfBalances
        );
    }
"><code>    <span class="hljs-comment">// The WETH contract's Ether balance should always be</span>
    <span class="hljs-comment">// equal to the sum of all individual deposits</span>
    <span class="hljs-comment">// minus all individual withrawals, plus any</span>
    <span class="hljs-comment">// force-pushed Ether in the contract</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invariant_solvencyDeposits</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        assertEq(
            <span class="hljs-keyword">address</span>(weth).<span class="hljs-built_in">balance</span>,
            handler.ghost_depositSum() <span class="hljs-operator">+</span>
            handler.ghost_forcePushSum() <span class="hljs-operator">-</span>
            handler.ghost_withdrawSum()
        );
    }

    <span class="hljs-comment">// The WETH contract's Ether balance should always be</span>
    <span class="hljs-comment">// equal to the sum of individual balances plus any</span>
    <span class="hljs-comment">// force-pushed Ether in the contract</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invariant_solvencyBalances</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-keyword">uint256</span> sumOfBalances <span class="hljs-operator">=</span> handler.reduceActors(
            <span class="hljs-number">0</span>, 
            <span class="hljs-built_in">this</span>.accumulateBalance
        );
        assertEq(
            <span class="hljs-keyword">address</span>(weth).<span class="hljs-built_in">balance</span> <span class="hljs-operator">-</span> handler.ghost_forcePushSum(),
            sumOfBalances
        );
    }
</code></pre><p>Invariant tests are a powerful tool, but this case is an interesting illustration of one of their blind spots. A symbolic execution test that models Ether sends via <code>selfdestruct</code> would catch this bug pretty quickly, but we nearly missed it with the fuzzer and had to rely on our own knowledge about the WETH contract to cover it. On the other hand, invariant tests are much faster to run than tests using a prover/constraint solver, and allow us to build up a suite of reasonably high confidence invariant properties that we might want to further verify using even more powerful tools.</p><p>However, it&apos;s important to remember that all fuzz tests are probabilistic: unlike a symbolic test that explores all possible execution paths, fuzz tests are only as good as the random data they generate. This is still very, very, good most of the time! But as with all smart contract testing, we should do it all when we can: unit tests, fuzz tests, fork tests, invariant tests, and formal verification.</p><p>Let&apos;s give our final tests one good, long, run: 25000 runs with a depth of 25 calls:</p><pre data-type="codeBlock" text="Running 4 tests for test/WETH9.invariants.t.sol:WETH9Invariants
[PASS] invariant_conservationOfETH()
(runs: 25000, calls: 625000, reverts: 15)
[PASS] invariant_depositorBalances()
(runs: 25000, calls: 625000, reverts: 15)
[PASS] invariant_solvencyBalances()
(runs: 25000, calls: 625000, reverts: 15)
[PASS] invariant_solvencyDeposits()
(runs: 25000, calls: 625000, reverts: 15)
Test result: ok. 4 passed; 0 failed; finished in 6995.00s
"><code><span class="hljs-selector-tag">Running</span> <span class="hljs-number">4</span> <span class="hljs-selector-tag">tests</span> <span class="hljs-selector-tag">for</span> <span class="hljs-selector-tag">test</span>/<span class="hljs-selector-tag">WETH9</span><span class="hljs-selector-class">.invariants</span><span class="hljs-selector-class">.t</span><span class="hljs-selector-class">.sol</span>:<span class="hljs-selector-tag">WETH9Invariants</span>
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_conservationOfETH</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">25000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">625000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">15</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_depositorBalances</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">25000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">625000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">15</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_solvencyBalances</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">25000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">625000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">15</span>)
<span class="hljs-selector-attr">[PASS]</span> <span class="hljs-selector-tag">invariant_solvencyDeposits</span>()
(<span class="hljs-attribute">runs</span>: <span class="hljs-number">25000</span>, <span class="hljs-attribute">calls</span>: <span class="hljs-number">625000</span>, <span class="hljs-attribute">reverts</span>: <span class="hljs-number">15</span>)
<span class="hljs-selector-tag">Test</span> <span class="hljs-selector-tag">result</span>: <span class="hljs-selector-tag">ok</span>. <span class="hljs-number">4</span> <span class="hljs-selector-tag">passed</span>; <span class="hljs-number">0</span> <span class="hljs-selector-tag">failed</span>; <span class="hljs-selector-tag">finished</span> <span class="hljs-selector-tag">in</span> <span class="hljs-number">6995.00s</span>
</code></pre><p>Success! Next time Crypto Twitter starts spreading FUD about &quot;unbacked WETH,&quot; send them this repo and tell them to kick rocks.</p><h2 id="h-more-resources" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">More resources</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/maple-labs/maple-core-v2/tree/main/tests/invariants">Maple Finance invariant tests repo</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/lucas-manuel/invariant-examples">invariant-examples repo</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.getfoundry.sh/forge/invariant-testing">Invariant Testing in the Foundry Book</a></p></li></ul><p><em>Thanks to </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/gakonst"><em>gakonst</em></a><em>, </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/lucasmanuel_eth"><em>lucasmanuel_eth</em></a><em>, </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/msolomon44"><em>msolomon44</em></a><em>, and </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/zachobront"><em>zachobront</em></a><em> for reviewing earlier drafts of this guide.</em></p>]]></content:encoded>
            <author>horsefacts@newsletter.paragraph.com (horsefacts)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/5ecde61b0ffae965e21fcdde8ce09e4d8cece29c3298038cef3664b540e2ebeb.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Seen on C4: Storage Structs]]></title>
            <link>https://paragraph.com/@horsefacts/seen-on-c4-storage-structs</link>
            <guid>qlNiiTDQ9ut1y9SsDV3C</guid>
            <pubDate>Mon, 06 Feb 2023 15:11:26 GMT</pubDate>
            <description><![CDATA[One of the best parts of competing on Code4rena is reading code from many different projects and seeing different styles, designs, and techniques in the wild. This is an occasional series about interesting patterns I&apos;ve seen on C4. Seen in Astaria and Drips.The problemUpgradeable contracts are fragile: they have changeable bytecode, but immutable storage layouts. This means contract authors must stay keenly aware of any code changes that alter their implementation&apos;s storage layout. ...]]></description>
            <content:encoded><![CDATA[<p><em>One of the best parts of competing on </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://code4rena.com/"><em>Code4rena</em></a><em> is reading code from many different projects and seeing different styles, designs, and techniques in the wild. This is an occasional series about interesting patterns I&apos;ve seen on C4.</em></p><p>Seen in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://code4rena.com/contests/2023-01-astaria-contest">Astaria</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://code4rena.com/contests/2023-01-drips-protocol-contest">Drips</a>.</p><h2 id="h-the-problem" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The problem</h2><p>Upgradeable contracts are fragile: they have changeable bytecode, but immutable storage layouts. This means contract authors must stay keenly aware of any code changes that alter their implementation&apos;s storage layout. Accidentally introducing a storage collision in an upgrade is very easy to do, and can have <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.audius.co/article/audius-governance-takeover-post-mortem-7-23-22">disastrous consequences</a>.</p><div data-type="twitter" tweetId="1553105446180888577" tweetData="{&quot;__typename&quot;:&quot;Tweet&quot;,&quot;lang&quot;:&quot;en&quot;,&quot;favorite_count&quot;:340,&quot;possibly_sensitive&quot;:false,&quot;created_at&quot;:&quot;2022-07-29T19:49:28.000Z&quot;,&quot;display_text_range&quot;:[0,262],&quot;entities&quot;:{&quot;hashtags&quot;:[],&quot;urls&quot;:[],&quot;user_mentions&quot;:[],&quot;symbols&quot;:[],&quot;media&quot;:[{&quot;display_url&quot;:&quot;pic.x.com/gm0KP7XQ61&quot;,&quot;expanded_url&quot;:&quot;https://x.com/danielvf/status/1553105446180888577/photo/1&quot;,&quot;indices&quot;:[263,286],&quot;url&quot;:&quot;https://t.co/gm0KP7XQ61&quot;}]},&quot;id_str&quot;:&quot;1553105446180888577&quot;,&quot;text&quot;:&quot;The Audius hack this week involved two contracts that overlapped the same storage slot (for three different variables).\n\nThe end of the admin address on the proxy was used by the implementation contract as value of the initializing and initialized variables. 1/6 https://t.co/gm0KP7XQ61&quot;,&quot;user&quot;:{&quot;id_str&quot;:&quot;5678&quot;,&quot;name&quot;:&quot;Daniel Von Fange&quot;,&quot;screen_name&quot;:&quot;danielvf&quot;,&quot;is_blue_verified&quot;:true,&quot;profile_image_shape&quot;:&quot;Circle&quot;,&quot;verified&quot;:false,&quot;profile_image_url_https&quot;:&quot;https://storage.googleapis.com/papyrus_images/2cfe4ece0420cdf5b1b52459b804b6ad2bcf6432df858fb4bceb4530c714bd32.jpg&quot;},&quot;edit_control&quot;:{&quot;edit_tweet_ids&quot;:[&quot;1553105446180888577&quot;],&quot;editable_until_msecs&quot;:&quot;1659125968000&quot;,&quot;is_edit_eligible&quot;:false,&quot;edits_remaining&quot;:&quot;5&quot;},&quot;mediaDetails&quot;:[{&quot;display_url&quot;:&quot;pic.x.com/gm0KP7XQ61&quot;,&quot;expanded_url&quot;:&quot;https://x.com/danielvf/status/1553105446180888577/photo/1&quot;,&quot;ext_media_availability&quot;:{&quot;status&quot;:&quot;Available&quot;},&quot;indices&quot;:[263,286],&quot;media_url_https&quot;:&quot;https://pbs.twimg.com/media/FY29AxRXoAAmt-J.jpg&quot;,&quot;original_info&quot;:{&quot;height&quot;:1322,&quot;width&quot;:1618,&quot;focus_rects&quot;:[{&quot;x&quot;:0,&quot;y&quot;:0,&quot;w&quot;:1618,&quot;h&quot;:906},{&quot;x&quot;:269,&quot;y&quot;:0,&quot;w&quot;:1322,&quot;h&quot;:1322},{&quot;x&quot;:350,&quot;y&quot;:0,&quot;w&quot;:1160,&quot;h&quot;:1322},{&quot;x&quot;:600,&quot;y&quot;:0,&quot;w&quot;:661,&quot;h&quot;:1322},{&quot;x&quot;:0,&quot;y&quot;:0,&quot;w&quot;:1618,&quot;h&quot;:1322}]},&quot;sizes&quot;:{&quot;large&quot;:{&quot;h&quot;:1322,&quot;resize&quot;:&quot;fit&quot;,&quot;w&quot;:1618},&quot;medium&quot;:{&quot;h&quot;:980,&quot;resize&quot;:&quot;fit&quot;,&quot;w&quot;:1200},&quot;small&quot;:{&quot;h&quot;:556,&quot;resize&quot;:&quot;fit&quot;,&quot;w&quot;:680},&quot;thumb&quot;:{&quot;h&quot;:150,&quot;resize&quot;:&quot;crop&quot;,&quot;w&quot;:150}},&quot;type&quot;:&quot;photo&quot;,&quot;url&quot;:&quot;https://t.co/gm0KP7XQ61&quot;}],&quot;photos&quot;:[{&quot;backgroundColor&quot;:{&quot;red&quot;:204,&quot;green&quot;:214,&quot;blue&quot;:221},&quot;cropCandidates&quot;:[{&quot;x&quot;:0,&quot;y&quot;:0,&quot;w&quot;:1618,&quot;h&quot;:906},{&quot;x&quot;:269,&quot;y&quot;:0,&quot;w&quot;:1322,&quot;h&quot;:1322},{&quot;x&quot;:350,&quot;y&quot;:0,&quot;w&quot;:1160,&quot;h&quot;:1322},{&quot;x&quot;:600,&quot;y&quot;:0,&quot;w&quot;:661,&quot;h&quot;:1322},{&quot;x&quot;:0,&quot;y&quot;:0,&quot;w&quot;:1618,&quot;h&quot;:1322}],&quot;expandedUrl&quot;:&quot;https://x.com/danielvf/status/1553105446180888577/photo/1&quot;,&quot;url&quot;:&quot;https://storage.googleapis.com/papyrus_images/e36c95a01d1d1d971910ec9e57de1d131046cc0c4f521ee6727b2968cdf91ff0.jpg&quot;,&quot;width&quot;:1618,&quot;height&quot;:1322}],&quot;conversation_count&quot;:15,&quot;news_action_type&quot;:&quot;conversation&quot;,&quot;isEdited&quot;:false,&quot;isStaleEdit&quot;:false}"> 
  <div class="twitter-embed embed">
    <div class="twitter-header">
        <div style="display:flex">
          <a target="_blank" href="https://twitter.com/danielvf">
              <img alt="User Avatar" class="twitter-avatar" src="https://storage.googleapis.com/papyrus_images/2cfe4ece0420cdf5b1b52459b804b6ad2bcf6432df858fb4bceb4530c714bd32.jpg" />
            </a>
            <div style="margin-left:4px;margin-right:auto;line-height:1.2;">
              <a target="_blank" href="https://twitter.com/danielvf" class="twitter-displayname">Daniel Von Fange</a>
              <p><a target="_blank" href="https://twitter.com/danielvf" class="twitter-username">@danielvf</a></p>
    
            </div>
            <a href="https://twitter.com/danielvf/status/1553105446180888577" target="_blank">
              <img alt="Twitter Logo" class="twitter-logo" src="https://paragraph.com/editor/twitter/logo.png" />
            </a>
          </div>
        </div>
      
    <div class="twitter-body">
      The Audius hack this week involved two contracts that overlapped the same storage slot (for three different variables).<br /><br />The end of the admin address on the proxy was used by the implementation contract as value of the initializing and initialized variables. 1/6 
      <div class="twitter-media"><img class="twitter-image" src="https://storage.googleapis.com/papyrus_images/e36c95a01d1d1d971910ec9e57de1d131046cc0c4f521ee6727b2968cdf91ff0.jpg" /></div>
      
       
    </div>
    
     <div class="twitter-footer">
          <a target="_blank" href="https://twitter.com/danielvf/status/1553105446180888577" style="margin-right:16px; display:flex;">
            <img alt="Like Icon" class="twitter-heart" src="https://paragraph.com/editor/twitter/heart.png">
            340
          </a>
          <a target="_blank" href="https://twitter.com/danielvf/status/1553105446180888577"><p>2:49 PM • Jul 29, 2022</p></a>
        </div>
    
  </div> 
  </div><p>Techniques like <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps">storage gaps</a>, explicit <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/OpenZeppelin/openzeppelin-labs/tree/master/upgradeability_using_inherited_storage">inheritance</a> of storage-specific contracts, and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/upgrades-plugins/1.x/">external tooling</a> that validates storage layouts at build/deploy time can all help protect against errors. But it&apos;s still really easy to mess up upgrades, especially when using libraries that leave many <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable">important responsibilities</a> in your hands.</p><h2 id="h-the-pattern" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The pattern</h2><p><strong>Storage structs</strong> are a technique that can make working with storage in upgradeable contracts safer and more explicit. This pattern (or parts of it) is also known as <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/dragonfly-xyz/useful-solidity-patterns/tree/main/patterns/explicit-storage-buckets">”explicit storage buckets”</a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/1milliondevs/new-storage-layout-for-proxy-contracts-and-diamonds-98d01d0eadb">”Diamond storage”</a>, and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies">“unstructured storage”</a>.</p><p>It works like this: first, define a struct that represents your contract&apos;s storage. For example, the storage of a contract with three top level state variables like this one:</p><pre data-type="codeBlock" text="contract DefaultStorageLayout {
  address public admin;
  uint256 public number;
  bytes32 internal hash;
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">DefaultStorageLayout</span> </span>{
  <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> admin;
  <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> number;
  <span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">internal</span> hash;
}
</code></pre><p>Can be represented as a struct like this one:</p><pre data-type="codeBlock" text="struct Storage {
  address admin;
  uint256 number;
  bytes32 hash;
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">Storage</span> {
  <span class="hljs-keyword">address</span> admin;
  <span class="hljs-keyword">uint256</span> number;
  <span class="hljs-keyword">bytes32</span> hash;
}
</code></pre><p>Next, define a unique slot to store this struct in your contract. Using the hash of a unique string is one way to generate a slot that won&apos;t collide with other storage. By hashing a string and converting it to a <code>uint256</code>, we’ll get back a very big pseudorandom number in the 256-bit integer range. Since that range is <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://keys.lol/ethereum/">so large</a>, it’s extremely unlikely that our explicit slot will collide with anything else.</p><p>We can define this once, as a constant:</p><pre data-type="codeBlock" text="uint256 private constant STORAGE_SLOT = 
  uint256(keccak256(&quot;eth.horsefacts.contract.storage&quot;)) - 1;
"><code><span class="hljs-keyword">uint256</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">constant</span> STORAGE_SLOT <span class="hljs-operator">=</span> 
  <span class="hljs-keyword">uint256</span>(<span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"eth.horsefacts.contract.storage"</span>)) <span class="hljs-operator">-</span> <span class="hljs-number">1</span>;
</code></pre><p>It&apos;s common to generate a slot following the format defined in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-1967">EIP-1967</a>, which defined this method of hashing a string and subtracting one to generate specific slots for proxy configuration addresses. (This EIP was sort of the progenitor of the unstructured storage pattern, since it first stored important data in deterministic-but-unusual slots to prevent storage collisions).</p><p>EIP-1967 defined specific deterministic slots for proxy-related storage variables, like <code>eip1967.proxy.admin</code> and <code>eip1967.proxy.implementation</code>. One thing I like about this method is that you can give your slot a friendly human readable name. Any unique string works, but if you miss your old job, you can use a name that looks like an enterprise Java package, like <code>com.mydomain.mycontract.storage</code>.</p><p>It&apos;s also a good and paranoid practice to subtract 1 from the hashed value, which ensures that the selected slot is not associated with a known hash preimage. (In other words, although the <em>value</em> of the storage slot is known, nobody knows which <em>input</em> hashes to that value).</p><p>Once our slot is defined, we can add an internal helper function to load the storage struct from its defined slot. For this we&apos;ll need a dash of inline assembly. Assigning to a storage pointer&apos;s <code>.slot</code> in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.soliditylang.org/en/v0.8.18/assembly.html#access-to-external-variables-functions-and-libraries">inline assembly</a> directly sets the storage pointer to a specific slot address.</p><pre data-type="codeBlock" text="function _storage() private pure returns (Storage storage s) {
  // Since STORAGE_SLOT is a constant, we have to put a variable
  // on the stack to access it from an inline assembly block.
  uint256 slot = STORAGE_SLOT;
  assembly {
    s.slot := slot;
  }
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_storage</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">private</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">Storage <span class="hljs-keyword">storage</span> s</span>) </span>{
  <span class="hljs-comment">// Since STORAGE_SLOT is a constant, we have to put a variable</span>
  <span class="hljs-comment">// on the stack to access it from an inline assembly block.</span>
  <span class="hljs-keyword">uint256</span> slot <span class="hljs-operator">=</span> STORAGE_SLOT;
  <span class="hljs-keyword">assembly</span> {
    s.<span class="hljs-built_in">slot</span> <span class="hljs-operator">:=</span> slot;
  }
}
</code></pre><p>Our <code>_storage</code> function:</p><ol><li><p>Creates a <code>storage</code> pointer <code>s</code></p></li><li><p>sets its slot to <code>STORAGE_SLOT</code>, and</p></li><li><p>implicitly returns it.</p></li></ol><p>We can now use this internal helper to retrieve a storage pointer any time we need to read or write from storage. For example, we can set initial values in the constructor by loading the struct from storage and assigning them:</p><pre data-type="codeBlock" text="constructor(address _admin, uint256 _number, bytes32 _hash) {
  Storage storage s = _storage();
  s.admin = _admin;
  s.number = _number;
  s.hash = _hash;
}
"><code><span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _admin, <span class="hljs-keyword">uint256</span> _number, <span class="hljs-keyword">bytes32</span> _hash</span>) </span>{
  Storage <span class="hljs-keyword">storage</span> s <span class="hljs-operator">=</span> _storage();
  s.admin <span class="hljs-operator">=</span> _admin;
  s.number <span class="hljs-operator">=</span> _number;
  s.hash <span class="hljs-operator">=</span> _hash;
}
</code></pre><p>Or call the helper directly from inside another function to access individual values:</p><pre data-type="codeBlock" text="function number() external view returns (uint256) {
  return _storage().number;
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">number</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
  <span class="hljs-keyword">return</span> _storage().number;
}
</code></pre><p>Since our internal <code>_storage()</code> helper returns a <code>storage</code> pointer to our storage struct, this is just as efficient as reading <code>number</code> would be if these values were stored in top level state variables. We are just explicitly defining which of the <code>2^256-1</code> storage slots contains our contract&apos;s storage, rather than letting the Solidity compiler automatically store it starting at slot zero. Since the storage layout for any inherited contract laid out automatically by the Solidity compiler will start at slot zero, by choosing a different slot guaranteed to be very far from zero we are avoiding the likeliest location for storage collisions.</p><p>In addition to protecting upgradeable contracts against accidental storage collisions, one nice thing about this pattern is that it makes storage access explicit. It becomes very clear when a function reads or writes from storage, which is sometimes not obvious when using state variables. Since reading and writing storage is one of the most expensive <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.evm.codes/#55">operations</a> on the EVM, this can be helpful.</p><p>However, there are still a few footguns to keep in mind. Unstructured storage is a way to tell the Solidity compiler &quot;put my storage in this slot&quot;, but its layout will still follow Solidity&apos;s <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html">rules for storage variable layout</a>.</p><p>That means, among <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eip2535diamonds.substack.com/p/diamond-upgrades">other things</a>:</p><ul><li><p>Storage structs are still append-only. Adding a new variable to the beginning or middle of the struct in an upgraded implementation will still result in a storage collision. Don&apos;t alter the fields of existing storage structs once they&apos;ve been created. (But it&apos;s OK to carefully add new ones).</p></li><li><p>Since structs are packed tightly in storage, nested structs inside an unstructured storage struct cannot be changed, and adding new fields will cause storage collisions.</p></li></ul><p>If you want to use unstructured storage, it&apos;s generally a good practice to try and limit storage structs to simple value types and mappings rather than complex types like nested structs.</p><p>(If you’re interested in why mappings are safe, read up on how mappings are <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#mappings-and-dynamic-arrays">implemented in storage</a> , which also answers why it’s not possible to retrieve a mapping’s keys on chain).</p><p>Additionally, take care to avoid accidentally loading storage structs into memory, especially if they are large. Always load a storage struct with data location <code>storage</code> to ensure you&apos;re using storage pointers rather than loading the full struct into memory, which can be expensive and error prone for large storage structs. (This is a good reason to use an internal <code>_storage()</code> helper function).</p><h2 id="h-seen-on-c4" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Seen on C4</h2><p>I&apos;ve seen storage structs in two recent C4 contests.</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/radicle-dev/drips-contracts/">Radicle Drips v2</a> uses unstructured storage throughout the codebase. One good example is the central <code>DripsHub</code> contract:</p><pre data-type="codeBlock" text="struct DripsHubStorage {
  /// @notice The next driver ID that will be used when registering.
  uint32 nextDriverId;
  /// @notice Driver addresses. The key is the driver ID, 
  /// the value is the driver address.
  mapping(uint32 =&gt; address) driverAddresses;
  /// @notice The total amount currently stored in DripsHub of each token.
  mapping(IERC20 =&gt; uint256) totalBalances;
}

/// @notice The ERC-1967 storage slot holding a single `DripsHubStorage` structure.
bytes32 private immutable _dripsHubStorageSlot = 
  _erc1967Slot(&quot;eip1967.dripsHub.storage&quot;);

/// @notice Returns the DripsHub storage.
/// @return storageRef The storage.
function _dripsHubStorage() internal view returns (DripsHubStorage storage storageRef) {
  bytes32 slot = _dripsHubStorageSlot;
  assembly {
    storageRef.slot := slot
  }
}

/// @notice Calculates the ERC-1967 slot pointer.
/// @param name The name of the slot, should be globally unique
/// @return slot The slot pointer
function _erc1967Slot(string memory name) internal pure returns (bytes32 slot) {
  return bytes32(uint256(keccak256(bytes(name))) - 1);
}

/// @notice Returns the driver address.
/// @param driverId The driver ID to look up.
/// @return driverAddr The address of the driver.
/// If the driver hasn&apos;t been registered yet, returns address 0.
function driverAddress(
  uint32 driverId
) public view returns (address driverAddr) {
  return _dripsHubStorage().driverAddresses[driverId];
}

function _decreaseTotalBalance(IERC20 erc20, uint128 amt) internal {
  _dripsHubStorage().totalBalances[erc20] -= amt;
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">DripsHubStorage</span> {
  <span class="hljs-comment">/// @notice The next driver ID that will be used when registering.</span>
  <span class="hljs-keyword">uint32</span> nextDriverId;
  <span class="hljs-comment">/// @notice Driver addresses. The key is the driver ID, </span>
  <span class="hljs-comment">/// the value is the driver address.</span>
  <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint32</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">address</span>) driverAddresses;
  <span class="hljs-comment">/// @notice The total amount currently stored in DripsHub of each token.</span>
  <span class="hljs-keyword">mapping</span>(IERC20 <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint256</span>) totalBalances;
}

<span class="hljs-comment">/// @notice The ERC-1967 storage slot holding a single `DripsHubStorage` structure.</span>
<span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">immutable</span> _dripsHubStorageSlot <span class="hljs-operator">=</span> 
  _erc1967Slot(<span class="hljs-string">"eip1967.dripsHub.storage"</span>);

<span class="hljs-comment">/// @notice Returns the DripsHub storage.</span>
<span class="hljs-comment">/// @return storageRef The storage.</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_dripsHubStorage</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">DripsHubStorage <span class="hljs-keyword">storage</span> storageRef</span>) </span>{
  <span class="hljs-keyword">bytes32</span> slot <span class="hljs-operator">=</span> _dripsHubStorageSlot;
  <span class="hljs-keyword">assembly</span> {
    storageRef.<span class="hljs-built_in">slot</span> <span class="hljs-operator">:=</span> slot
  }
}

<span class="hljs-comment">/// @notice Calculates the ERC-1967 slot pointer.</span>
<span class="hljs-comment">/// @param name The name of the slot, should be globally unique</span>
<span class="hljs-comment">/// @return slot The slot pointer</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_erc1967Slot</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> name</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes32</span> slot</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">bytes32</span>(<span class="hljs-keyword">uint256</span>(<span class="hljs-built_in">keccak256</span>(<span class="hljs-keyword">bytes</span>(name))) <span class="hljs-operator">-</span> <span class="hljs-number">1</span>);
}

<span class="hljs-comment">/// @notice Returns the driver address.</span>
<span class="hljs-comment">/// @param driverId The driver ID to look up.</span>
<span class="hljs-comment">/// @return driverAddr The address of the driver.</span>
<span class="hljs-comment">/// If the driver hasn't been registered yet, returns address 0.</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">driverAddress</span>(<span class="hljs-params">
  <span class="hljs-keyword">uint32</span> driverId
</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span> driverAddr</span>) </span>{
  <span class="hljs-keyword">return</span> _dripsHubStorage().driverAddresses[driverId];
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_decreaseTotalBalance</span>(<span class="hljs-params">IERC20 erc20, <span class="hljs-keyword">uint128</span> amt</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
  _dripsHubStorage().totalBalances[erc20] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> amt;
}
</code></pre><div data-type="embedly" src="https://github.com/radicle-dev/drips-contracts/blob/aa149127572e467af4c1adf4c2a37160b39bc91b/src/DripsHub.sol" data="{&quot;provider_url&quot;:&quot;https://github.com&quot;,&quot;description&quot;:&quot;💧 Drips Protocol Smart Contracts. Contribute to drips-network/contracts development by creating an account on GitHub.&quot;,&quot;title&quot;:&quot;contracts/src/DripsHub.sol at aa149127572e467af4c1adf4c2a37160b39bc91b · drips-network/contracts&quot;,&quot;author_name&quot;:&quot;drips-network&quot;,&quot;thumbnail_width&quot;:1200,&quot;url&quot;:&quot;https://github.com/drips-network/contracts/blob/aa149127572e467af4c1adf4c2a37160b39bc91b/src/DripsHub.sol&quot;,&quot;thumbnail_url&quot;:&quot;https://storage.googleapis.com/papyrus_images/7eac60e623a588adcacba5c4a7a2385f2e259e386667c1936ab0a6c82ff2b598.png&quot;,&quot;author_url&quot;:&quot;https://github.com/drips-network&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;GitHub&quot;,&quot;type&quot;:&quot;link&quot;,&quot;thumbnail_height&quot;:600,&quot;image&quot;:{&quot;img&quot;:{&quot;width&quot;:1200,&quot;height&quot;:600,&quot;src&quot;:&quot;https://storage.googleapis.com/papyrus_images/7eac60e623a588adcacba5c4a7a2385f2e259e386667c1936ab0a6c82ff2b598.png&quot;}}}" format="small"><link rel="preload" as="image" href="https://storage.googleapis.com/papyrus_images/7eac60e623a588adcacba5c4a7a2385f2e259e386667c1936ab0a6c82ff2b598.png"/><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://github.com/radicle-dev/drips-contracts/blob/aa149127572e467af4c1adf4c2a37160b39bc91b/src/DripsHub.sol" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>contracts/src/DripsHub.sol at aa149127572e467af4c1adf4c2a37160b39bc91b · drips-network/contracts</h2><p>💧 Drips Protocol Smart Contracts. Contribute to drips-network/contracts development by creating an account on GitHub.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://github.com</span></div><img src="https://storage.googleapis.com/papyrus_images/7eac60e623a588adcacba5c4a7a2385f2e259e386667c1936ab0a6c82ff2b598.png"/></div></a></div></div><p>So does <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/AstariaXYZ/astaria-core">Astaria</a>. The entrypoint <code>AstariaRouter.sol</code> is a good example:</p><pre data-type="codeBlock" text="struct RouterStorage {
  uint32 auctionWindow;
  uint32 auctionWindowBuffer;
  uint32 liquidationFeeNumerator;
  uint32 liquidationFeeDenominator;
  uint32 maxEpochLength;
  uint32 minEpochLength;
  uint32 protocolFeeNumerator;
  uint32 protocolFeeDenominator;
  ERC20 WETH;
  ICollateralToken COLLATERAL_TOKEN;
  ILienToken LIEN_TOKEN;
  ITransferProxy TRANSFER_PROXY;
  address feeTo;
  address BEACON_PROXY_IMPLEMENTATION;
  uint88 maxInterestRate;
  uint32 minInterestBPS;
  address guardian;
  address newGuardian;
  uint32 buyoutFeeNumerator;
  uint32 buyoutFeeDenominator;
  uint32 minDurationIncrease;
  mapping(uint8 =&gt; address) strategyValidators;
  mapping(uint8 =&gt; address) implementations;
  //A strategist can have many deployed vaults
  mapping(address =&gt; bool) vaults;
}

uint256 private constant ROUTER_SLOT =
  uint256(keccak256(&quot;xyz.astaria.AstariaRouter.storage.location&quot;)) - 1;

function _loadRouterSlot() internal pure returns (RouterStorage storage rs) {
  uint256 slot = ROUTER_SLOT;
  assembly {
    rs.slot := slot
  }
}

function getAuctionWindow(bool includeBuffer) public view returns (uint256) {
  RouterStorage storage s = _loadRouterSlot();
  return s.auctionWindow + (includeBuffer ? s.auctionWindowBuffer : 0);
}

function getLiquidatorFee(uint256 amountIn) external view returns (uint256) {
  RouterStorage storage s = _loadRouterSlot();
  return amountIn.mulDivDown(
    s.liquidationFeeNumerator,
    s.liquidationFeeDenominator
  );
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">RouterStorage</span> {
  <span class="hljs-keyword">uint32</span> auctionWindow;
  <span class="hljs-keyword">uint32</span> auctionWindowBuffer;
  <span class="hljs-keyword">uint32</span> liquidationFeeNumerator;
  <span class="hljs-keyword">uint32</span> liquidationFeeDenominator;
  <span class="hljs-keyword">uint32</span> maxEpochLength;
  <span class="hljs-keyword">uint32</span> minEpochLength;
  <span class="hljs-keyword">uint32</span> protocolFeeNumerator;
  <span class="hljs-keyword">uint32</span> protocolFeeDenominator;
  ERC20 WETH;
  ICollateralToken COLLATERAL_TOKEN;
  ILienToken LIEN_TOKEN;
  ITransferProxy TRANSFER_PROXY;
  <span class="hljs-keyword">address</span> feeTo;
  <span class="hljs-keyword">address</span> BEACON_PROXY_IMPLEMENTATION;
  <span class="hljs-keyword">uint88</span> maxInterestRate;
  <span class="hljs-keyword">uint32</span> minInterestBPS;
  <span class="hljs-keyword">address</span> guardian;
  <span class="hljs-keyword">address</span> newGuardian;
  <span class="hljs-keyword">uint32</span> buyoutFeeNumerator;
  <span class="hljs-keyword">uint32</span> buyoutFeeDenominator;
  <span class="hljs-keyword">uint32</span> minDurationIncrease;
  <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint8</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">address</span>) strategyValidators;
  <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint8</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">address</span>) implementations;
  <span class="hljs-comment">//A strategist can have many deployed vaults</span>
  <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">bool</span>) vaults;
}

<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">constant</span> ROUTER_SLOT <span class="hljs-operator">=</span>
  <span class="hljs-keyword">uint256</span>(<span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"xyz.astaria.AstariaRouter.storage.location"</span>)) <span class="hljs-operator">-</span> <span class="hljs-number">1</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_loadRouterSlot</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">RouterStorage <span class="hljs-keyword">storage</span> rs</span>) </span>{
  <span class="hljs-keyword">uint256</span> slot <span class="hljs-operator">=</span> ROUTER_SLOT;
  <span class="hljs-keyword">assembly</span> {
    rs.<span class="hljs-built_in">slot</span> <span class="hljs-operator">:=</span> slot
  }
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAuctionWindow</span>(<span class="hljs-params"><span class="hljs-keyword">bool</span> includeBuffer</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
  RouterStorage <span class="hljs-keyword">storage</span> s <span class="hljs-operator">=</span> _loadRouterSlot();
  <span class="hljs-keyword">return</span> s.auctionWindow <span class="hljs-operator">+</span> (includeBuffer ? s.auctionWindowBuffer : <span class="hljs-number">0</span>);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getLiquidatorFee</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amountIn</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
  RouterStorage <span class="hljs-keyword">storage</span> s <span class="hljs-operator">=</span> _loadRouterSlot();
  <span class="hljs-keyword">return</span> amountIn.mulDivDown(
    s.liquidationFeeNumerator,
    s.liquidationFeeDenominator
  );
}
</code></pre><div data-type="embedly" src="https://github.com/AstariaXYZ/astaria-core/blob/f4be95017b09dd5b78741cdffae4e07c0b06f68b/src/AstariaRouter.sol" data="{&quot;provider_url&quot;:&quot;https://github.com&quot;,&quot;description&quot;:&quot;NFT Collateralized Lending Market leveraging our novel 3 Actor Model - astaria-core/src/AstariaRouter.sol at f4be95017b09dd5b78741cdffae4e07c0b06f68b · AstariaXYZ/astaria-core&quot;,&quot;title&quot;:&quot;astaria-core/src/AstariaRouter.sol at f4be95017b09dd5b78741cdffae4e07c0b06f68b · AstariaXYZ/astaria-core&quot;,&quot;author_name&quot;:&quot;AstariaXYZ&quot;,&quot;thumbnail_width&quot;:1200,&quot;url&quot;:&quot;https://github.com/AstariaXYZ/astaria-core/blob/f4be95017b09dd5b78741cdffae4e07c0b06f68b/src/AstariaRouter.sol&quot;,&quot;thumbnail_url&quot;:&quot;https://storage.googleapis.com/papyrus_images/2afbb63cb1875f8b7575c23ea8b2524c0cf19dfd80584834af5d3dd17b50b716.png&quot;,&quot;author_url&quot;:&quot;https://github.com/AstariaXYZ&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;GitHub&quot;,&quot;type&quot;:&quot;link&quot;,&quot;thumbnail_height&quot;:600,&quot;image&quot;:{&quot;img&quot;:{&quot;width&quot;:1200,&quot;height&quot;:600,&quot;src&quot;:&quot;https://storage.googleapis.com/papyrus_images/2afbb63cb1875f8b7575c23ea8b2524c0cf19dfd80584834af5d3dd17b50b716.png&quot;}}}" format="small"><link rel="preload" as="image" href="https://storage.googleapis.com/papyrus_images/2afbb63cb1875f8b7575c23ea8b2524c0cf19dfd80584834af5d3dd17b50b716.png"/><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://github.com/AstariaXYZ/astaria-core/blob/f4be95017b09dd5b78741cdffae4e07c0b06f68b/src/AstariaRouter.sol" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>astaria-core/src/AstariaRouter.sol at f4be95017b09dd5b78741cdffae4e07c0b06f68b · AstariaXYZ/astaria-core</h2><p>NFT Collateralized Lending Market leveraging our novel 3 Actor Model - astaria-core/src/AstariaRouter.sol at f4be95017b09dd5b78741cdffae4e07c0b06f68b · AstariaXYZ/astaria-core</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://github.com</span></div><img src="https://storage.googleapis.com/papyrus_images/2afbb63cb1875f8b7575c23ea8b2524c0cf19dfd80584834af5d3dd17b50b716.png"/></div></a></div></div><p>Note how the examples above both store simple value types and mappings in their storage structs, but avoid complex types like nested structs.</p><p>All proxy patterns are advanced techniques, and unstructured storage is no exception. It&apos;s important to have a good understanding of the EVM storage model and Solidity <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html">state variable layout</a> to use any of them safely. And even if you&apos;re using a storage struct, you should still take extreme care if you need to change it as part of a contract upgrade. However, used carefully, they can be an elegant solution to preventing some of the most common causes of storage collisions.</p><p>You can read more about this pattern and see another detailed example under <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/dragonfly-xyz/useful-solidity-patterns/tree/main/patterns/explicit-storage-buckets">&quot;Explicit Storage Buckets&quot;</a> in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/dragonfly-xyz/useful-solidity-patterns">useful-solidity-patterns</a>.</p><h2 id="h-previously" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Previously</h2><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/horsefacts.eth/R5N_Dzm2XKVvSI3cM8e8EHuFzvpPBttG2TEtyEZDa10">https://mirror.xyz/horsefacts.eth/R5N_Dzm2XKVvSI3cM8e8EHuFzvpPBttG2TEtyEZDa10</a></p><p><em>Thanks to </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/devtooligan"><em>devtooligan</em></a><em> for reviewing a draft of this post, </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/danielvf"><em>danielvf</em></a><em> for his thread explaining the Audius exploit, and </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/merklejerk"><em>merklejerk</em></a><em> for his own writeup of this pattern.</em></p>]]></content:encoded>
            <author>horsefacts@newsletter.paragraph.com (horsefacts)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/eb207a135010f417a1dafb388e7e73f71c89f13e93bf43a7cea7b21126933a71.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Seen on C4: The File Pattern]]></title>
            <link>https://paragraph.com/@horsefacts/seen-on-c4-the-file-pattern</link>
            <guid>f5ywtWU2flxdX7AC25we</guid>
            <pubDate>Sat, 04 Feb 2023 18:26:20 GMT</pubDate>
            <description><![CDATA[One of the best parts of competing on Code4rena is reading code from many different projects and seeing different styles, designs, and techniques in the wild. This is the first post in an occasional series about interesting patterns I&apos;ve seen on C4. Seen in Astaria.The problemA contract stores several configuration values, all of which should be settable only by an authorized caller. One straightforward solution is to write a separate setter with an auth modifier for each storage variabl...]]></description>
            <content:encoded><![CDATA[<p><em>One of the best parts of competing on </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://code4rena.com/"><em>Code4rena</em></a><em> is reading code from many different projects and seeing different styles, designs, and techniques in the wild. This is the first post in an occasional series about interesting patterns I&apos;ve seen on C4.</em></p><p>Seen in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://code4rena.com/contests/2023-01-astaria-contest">Astaria</a>.</p><h2 id="h-the-problem" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The problem</h2><p>A contract stores several configuration values, all of which should be settable only by an authorized caller.</p><p>One straightforward solution is to write a separate setter with an auth modifier for each storage variable:</p><pre data-type="codeBlock" text="address public token;
uint256 public min;
uint256 public max;
bytes32 public hash;

function setToken(address _token) external onlyAdmin {
  token = _token;
}

function setMin(uint256 _min) external onlyAdmin {
  min = _min;
}

function setMax(uint256 _max) external onlyAdmin {
  max = _max;
}

function setHash(bytes32 _hash) external onlyAdmin {
  hash = _hash;
}
"><code><span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> token;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> min;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> max;
<span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">public</span> hash;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setToken</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _token</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyAdmin</span> </span>{
  token <span class="hljs-operator">=</span> _token;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setMin</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> _min</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyAdmin</span> </span>{
  min <span class="hljs-operator">=</span> _min;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setMax</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> _max</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyAdmin</span> </span>{
  max <span class="hljs-operator">=</span> _max;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setHash</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> _hash</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyAdmin</span> </span>{
  hash <span class="hljs-operator">=</span> _hash;
}
</code></pre><p>The above is a good start, but in addition to setting the new value, any time a privileged caller changes a parameter, the contract should emit an event.</p><p>This is a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://consensys.github.io/smart-contract-best-practices/development-recommendations/solidity-specific/event-monitoring/">good practice</a> that enables off chain monitoring for unauthorized interactions, helps to debug in the event of accidental changes, and allows anyone to reconstruct a full history of configuration state.</p><p>Let&apos;s add events:</p><pre data-type="codeBlock" text="address public token;
uint256 public min;
uint256 public max;
bytes32 public hash;

event SetToken(address oldToken, address newToken);
event   SetMin(uint256   oldMin, uint256 newMin);
event   SetMax(uint256   oldMax, uint256 newMax);
event  SetHash(bytes32 oldToken, bytes32 newHash);

function setToken(address _token) external onlyAdmin {
  emit SetToken(token, _token);
  token = _token;
}

function setMin(uint256 _min) external onlyAdmin {
  emit SetMin(min, _min);
  min = _min;
}

function setMax(uint256 _max) external onlyAdmin {
  emit SetMax(max, _max);
  max = _max;
}

function setHash(bytes32 _hash) external onlyAdmin {
  emit SetHash(hash, _hash);
  hash = _hash;
}
"><code><span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> token;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> min;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> max;
<span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">public</span> hash;

<span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">SetToken</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> oldToken, <span class="hljs-keyword">address</span> newToken</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">event</span>   <span class="hljs-title">SetMin</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span>   oldMin, <span class="hljs-keyword">uint256</span> newMin</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">event</span>   <span class="hljs-title">SetMax</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span>   oldMax, <span class="hljs-keyword">uint256</span> newMax</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">event</span>  <span class="hljs-title">SetHash</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> oldToken, <span class="hljs-keyword">bytes32</span> newHash</span>)</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setToken</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _token</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyAdmin</span> </span>{
  <span class="hljs-keyword">emit</span> SetToken(token, _token);
  token <span class="hljs-operator">=</span> _token;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setMin</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> _min</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyAdmin</span> </span>{
  <span class="hljs-keyword">emit</span> SetMin(min, _min);
  min <span class="hljs-operator">=</span> _min;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setMax</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> _max</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyAdmin</span> </span>{
  <span class="hljs-keyword">emit</span> SetMax(max, _max);
  max <span class="hljs-operator">=</span> _max;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setHash</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> _hash</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyAdmin</span> </span>{
  <span class="hljs-keyword">emit</span> SetHash(hash, _hash);
  hash <span class="hljs-operator">=</span> _hash;
}
</code></pre><p>This is perfectly functional, but it&apos;s a bit repetitive and verbose. Imagine if our contract had ten more parameters: we&apos;d have ten more events, and ten more setters!</p><h2 id="h-the-pattern" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The pattern</h2><p>Enter the <code>file</code> pattern: a single function that dispatches multiple setters:</p><pre data-type="codeBlock" text="address public token;
uint256 public min;
uint256 public max;
bytes32 public hash;

event File(bytes32 what, bytes value);

error InvalidParameter(bytes32 what);

function file(bytes32 what, bytes calldata value) external onlyAdmin {
  if (what == &quot;token&quot;) token = abi.decode(value, (address));
  else if (what == &quot;min&quot;) min = abi.decode(value, (uint256));
  else if (what == &quot;max&quot;) max = abi.decode(value, (uint256));
  else if (what == &quot;hash&quot;) hash = abi.decode(value, (bytes32));
  else revert InvalidParameter(what);
  emit File(what, value);
}
"><code><span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> token;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> min;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> max;
<span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">public</span> hash;

<span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">File</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> what, <span class="hljs-keyword">bytes</span> value</span>)</span>;

<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">InvalidParameter</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> what</span>)</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">file</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> what, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> value</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyAdmin</span> </span>{
  <span class="hljs-keyword">if</span> (what <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"token"</span>) token <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(value, (<span class="hljs-keyword">address</span>));
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (what <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"min"</span>) min <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(value, (<span class="hljs-keyword">uint256</span>));
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (what <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"max"</span>) max <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(value, (<span class="hljs-keyword">uint256</span>));
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (what <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"hash"</span>) hash <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(value, (<span class="hljs-keyword">bytes32</span>));
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">revert</span> InvalidParameter(what);
  <span class="hljs-keyword">emit</span> File(what, value);
}
</code></pre><p>&quot;Dispatching setters&quot; may sound complicated, but it&apos;s really just a big <code>if</code> statement. We check for each supported <code>what</code> key to determine which value to set, and dynamically <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.soliditylang.org/en/latest/units-and-global-variables.html#abi-encoding-and-decoding-functions">ABI-decode</a> the <code>value</code> bytes into an <code>address</code>, <code>uint256</code>, or <code>bytes32</code> when we find a match.</p><p>We can call <code>file</code> with a key-value-ish syntax and ABI-encoded data to set specific values:</p><pre data-type="codeBlock" text="file(&quot;token&quot;, abi.encode(address(0x6B175474E89094C44Da98b954EedeAC495271d0F)));
file(&quot;min&quot;, abi.encode(uint256(10)));
file(&quot;max&quot;, abi.encode(uint256(100)));
file(&quot;hash&quot;, abi.encode(keccak256(&quot;all my apes gone!&quot;)));
"><code>file(<span class="hljs-string">"token"</span>, <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(<span class="hljs-keyword">address</span>(<span class="hljs-number">0x6B175474E89094C44Da98b954EedeAC495271d0F</span>)));
file(<span class="hljs-string">"min"</span>, <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(<span class="hljs-keyword">uint256</span>(<span class="hljs-number">10</span>)));
file(<span class="hljs-string">"max"</span>, <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(<span class="hljs-keyword">uint256</span>(<span class="hljs-number">100</span>)));
file(<span class="hljs-string">"hash"</span>, <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(<span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"all my apes gone!"</span>)));
</code></pre><p>In addition to being more concise, <code>file</code> has a few other advantages:</p><ul><li><p>The <code>onlyAdmin</code> modifier is applied just once, to a single authenticated function. It&apos;s easy to miss or delete one of these modifiers when creating multiple setter functions.</p></li><li><p>It&apos;s possible to monitor the <code>File</code> event for <em>all</em> configuration changes, and reconstruct the configuration history of the contract from one event rather than collating multiple different events.</p></li></ul><p>Alternatively, at the cost of adding a few more functions, we can use <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.soliditylang.org/en/latest/contracts.html#function-overloading">function overloading</a> to define <code>file</code> separately for each type we need to set. Rather than ABI-decoding the <code>value</code>, we can instead ABI-<em>encode</em> it at the very end in the <code>File</code> event we emit from each function:</p><pre data-type="codeBlock" text="address public token;
uint256 public min;
uint256 public max;
bytes32 public hash;

event File(bytes32 what, bytes value);

error InvalidParameter(bytes32 what);

function file(bytes32 what, address value) external onlyAdmin {
  if (what == &quot;token&quot;) token = value;
  else revert InvalidParameter(what);
  emit File(what, abi.encode(value));
}

function file(bytes32 what, uint256 value) external onlyAdmin {
  if (what == &quot;min&quot;) min = value;
  else if (what == &quot;max&quot;) max = value;
  else revert InvalidParameter(what);
  emit File(what, abi.encode(value));
}

function file(bytes32 what, bytes32 value) external onlyAdmin {
  if (what == &quot;hash&quot;) hash = value;
  else revert InvalidParameter(what);
  emit File(what, abi.encode(value));
}
"><code><span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> token;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> min;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> max;
<span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">public</span> hash;

<span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">File</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> what, <span class="hljs-keyword">bytes</span> value</span>)</span>;

<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">InvalidParameter</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> what</span>)</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">file</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> what, <span class="hljs-keyword">address</span> value</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyAdmin</span> </span>{
  <span class="hljs-keyword">if</span> (what <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"token"</span>) token <span class="hljs-operator">=</span> value;
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">revert</span> InvalidParameter(what);
  <span class="hljs-keyword">emit</span> File(what, <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(value));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">file</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> what, <span class="hljs-keyword">uint256</span> value</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyAdmin</span> </span>{
  <span class="hljs-keyword">if</span> (what <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"min"</span>) min <span class="hljs-operator">=</span> value;
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (what <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"max"</span>) max <span class="hljs-operator">=</span> value;
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">revert</span> InvalidParameter(what);
  <span class="hljs-keyword">emit</span> File(what, <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(value));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">file</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> what, <span class="hljs-keyword">bytes32</span> value</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyAdmin</span> </span>{
  <span class="hljs-keyword">if</span> (what <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"hash"</span>) hash <span class="hljs-operator">=</span> value;
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">revert</span> InvalidParameter(what);
  <span class="hljs-keyword">emit</span> File(what, <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(value));
}
</code></pre><p>Our code is a little longer, but a lot cleaner. And calling these functions is nicer, because there&apos;s no need to encode the values:</p><pre data-type="codeBlock" text="file(&quot;token&quot;, address(0x6B175474E89094C44Da98b954EedeAC495271d0F));
file(&quot;min&quot;, 10);
file(&quot;max&quot;, 100);
file(&quot;hash&quot;, keccak256(&quot;all my apes gone!&quot;));
"><code><span class="hljs-built_in">file</span>("token", address(<span class="hljs-number">0</span>x6B175474E89094C44Da98b954EedeAC495271d0F));
<span class="hljs-built_in">file</span>("min", <span class="hljs-number">10</span>);
<span class="hljs-built_in">file</span>("max", <span class="hljs-number">100</span>);
<span class="hljs-built_in">file</span>("hash", keccak256("all my apes gone!"));
</code></pre><h2 id="h-seen-on-c4" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Seen on C4</h2><p>Here&apos;s one example of the pattern from Astaria&apos;s <code>LienToken.sol</code>:</p><pre data-type="codeBlock" text="enum FileType {
  NotSupported,
  CollateralToken,
  AstariaRouter
}

struct File {
  FileType what;
  bytes data;
}

event FileUpdated(FileType what, bytes data);

function file(File calldata incoming) external requiresAuth {
  FileType what = incoming.what;
  bytes memory data = incoming.data;
  LienStorage storage s = _loadLienStorageSlot();
  if (what == FileType.CollateralToken) {
    s.COLLATERAL_TOKEN = ICollateralToken(abi.decode(data, (address)));
  } else if (what == FileType.AstariaRouter) {
    s.ASTARIA_ROUTER = IAstariaRouter(abi.decode(data, (address)));
  } else {
    revert UnsupportedFile();
  }
  emit FileUpdated(what, data);
}
"><code><span class="hljs-keyword">enum</span> <span class="hljs-title">FileType</span> {
  NotSupported,
  CollateralToken,
  AstariaRouter
}

<span class="hljs-keyword">struct</span> <span class="hljs-title">File</span> {
  FileType what;
  <span class="hljs-keyword">bytes</span> data;
}

<span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">FileUpdated</span>(<span class="hljs-params">FileType what, <span class="hljs-keyword">bytes</span> data</span>)</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">file</span>(<span class="hljs-params">File <span class="hljs-keyword">calldata</span> incoming</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">requiresAuth</span> </span>{
  FileType what <span class="hljs-operator">=</span> incoming.what;
  <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> data <span class="hljs-operator">=</span> incoming.data;
  LienStorage <span class="hljs-keyword">storage</span> s <span class="hljs-operator">=</span> _loadLienStorageSlot();
  <span class="hljs-keyword">if</span> (what <span class="hljs-operator">=</span><span class="hljs-operator">=</span> FileType.CollateralToken) {
    s.COLLATERAL_TOKEN <span class="hljs-operator">=</span> ICollateralToken(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(data, (<span class="hljs-keyword">address</span>)));
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (what <span class="hljs-operator">=</span><span class="hljs-operator">=</span> FileType.AstariaRouter) {
    s.ASTARIA_ROUTER <span class="hljs-operator">=</span> IAstariaRouter(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(data, (<span class="hljs-keyword">address</span>)));
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">revert</span> UnsupportedFile();
  }
  <span class="hljs-keyword">emit</span> FileUpdated(what, data);
}
</code></pre><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/AstariaXYZ/astaria-core/blob/f4be95017b09dd5b78741cdffae4e07c0b06f68b/src/LienToken.sol#L82">https://github.com/AstariaXYZ/astaria-core/blob/f4be95017b09dd5b78741cdffae4e07c0b06f68b/src/LienToken.sol#L82</a></p><p>(See also the much more complex <code>file</code> function <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/AstariaXYZ/astaria-core/blob/f4be95017b09dd5b78741cdffae4e07c0b06f68b/src/AstariaRouter.sol#L277">here</a> in <code>AstariaRouter.sol</code>, which does more complex decoding and uses unstructured storage).</p><div data-type="twitter" tweetId="1612628007851630592" tweetData="{&quot;__typename&quot;:&quot;Tweet&quot;,&quot;lang&quot;:&quot;en&quot;,&quot;favorite_count&quot;:55,&quot;possibly_sensitive&quot;:false,&quot;created_at&quot;:&quot;2023-01-10T01:50:52.000Z&quot;,&quot;display_text_range&quot;:[0,38],&quot;entities&quot;:{&quot;hashtags&quot;:[],&quot;urls&quot;:[],&quot;user_mentions&quot;:[],&quot;symbols&quot;:[],&quot;media&quot;:[{&quot;display_url&quot;:&quot;pic.x.com/jV3aCDfHGh&quot;,&quot;expanded_url&quot;:&quot;https://x.com/eth_call/status/1612628007851630592/photo/1&quot;,&quot;indices&quot;:[36,59],&quot;url&quot;:&quot;https://t.co/jV3aCDfHGh&quot;}]},&quot;id_str&quot;:&quot;1612628007851630592&quot;,&quot;text&quot;:&quot;you absolutely love to see it 🗄️📁🗃️ https://t.co/jV3aCDfHGh&quot;,&quot;user&quot;:{&quot;id_str&quot;:&quot;6335812&quot;,&quot;name&quot;:&quot;horsefacts&quot;,&quot;screen_name&quot;:&quot;eth_call&quot;,&quot;is_blue_verified&quot;:true,&quot;profile_image_shape&quot;:&quot;Circle&quot;,&quot;verified&quot;:false,&quot;profile_image_url_https&quot;:&quot;https://storage.googleapis.com/papyrus_images/1184480ee9b770ee98a2a52c46365f52ccf3757de691f7536a273cffa8526ad8.jpg&quot;},&quot;edit_control&quot;:{&quot;edit_tweet_ids&quot;:[&quot;1612628007851630592&quot;],&quot;editable_until_msecs&quot;:&quot;1673317252000&quot;,&quot;is_edit_eligible&quot;:true,&quot;edits_remaining&quot;:&quot;5&quot;},&quot;mediaDetails&quot;:[{&quot;display_url&quot;:&quot;pic.x.com/jV3aCDfHGh&quot;,&quot;expanded_url&quot;:&quot;https://x.com/eth_call/status/1612628007851630592/photo/1&quot;,&quot;ext_media_availability&quot;:{&quot;status&quot;:&quot;Available&quot;},&quot;indices&quot;:[36,59],&quot;media_url_https&quot;:&quot;https://pbs.twimg.com/media/FmE0c1qXEAgtXYu.jpg&quot;,&quot;original_info&quot;:{&quot;height&quot;:1116,&quot;width&quot;:1416,&quot;focus_rects&quot;:[{&quot;x&quot;:0,&quot;y&quot;:323,&quot;w&quot;:1416,&quot;h&quot;:793},{&quot;x&quot;:0,&quot;y&quot;:0,&quot;w&quot;:1116,&quot;h&quot;:1116},{&quot;x&quot;:0,&quot;y&quot;:0,&quot;w&quot;:979,&quot;h&quot;:1116},{&quot;x&quot;:110,&quot;y&quot;:0,&quot;w&quot;:558,&quot;h&quot;:1116},{&quot;x&quot;:0,&quot;y&quot;:0,&quot;w&quot;:1416,&quot;h&quot;:1116}]},&quot;sizes&quot;:{&quot;large&quot;:{&quot;h&quot;:1116,&quot;resize&quot;:&quot;fit&quot;,&quot;w&quot;:1416},&quot;medium&quot;:{&quot;h&quot;:946,&quot;resize&quot;:&quot;fit&quot;,&quot;w&quot;:1200},&quot;small&quot;:{&quot;h&quot;:536,&quot;resize&quot;:&quot;fit&quot;,&quot;w&quot;:680},&quot;thumb&quot;:{&quot;h&quot;:150,&quot;resize&quot;:&quot;crop&quot;,&quot;w&quot;:150}},&quot;type&quot;:&quot;photo&quot;,&quot;url&quot;:&quot;https://t.co/jV3aCDfHGh&quot;}],&quot;photos&quot;:[{&quot;backgroundColor&quot;:{&quot;red&quot;:204,&quot;green&quot;:214,&quot;blue&quot;:221},&quot;cropCandidates&quot;:[{&quot;x&quot;:0,&quot;y&quot;:323,&quot;w&quot;:1416,&quot;h&quot;:793},{&quot;x&quot;:0,&quot;y&quot;:0,&quot;w&quot;:1116,&quot;h&quot;:1116},{&quot;x&quot;:0,&quot;y&quot;:0,&quot;w&quot;:979,&quot;h&quot;:1116},{&quot;x&quot;:110,&quot;y&quot;:0,&quot;w&quot;:558,&quot;h&quot;:1116},{&quot;x&quot;:0,&quot;y&quot;:0,&quot;w&quot;:1416,&quot;h&quot;:1116}],&quot;expandedUrl&quot;:&quot;https://x.com/eth_call/status/1612628007851630592/photo/1&quot;,&quot;url&quot;:&quot;https://storage.googleapis.com/papyrus_images/c9c19d360a83c438fe7facb39526a789d82e7d29ee2c60abd856764ecda36f73.jpg&quot;,&quot;width&quot;:1416,&quot;height&quot;:1116}],&quot;conversation_count&quot;:3,&quot;news_action_type&quot;:&quot;conversation&quot;,&quot;isEdited&quot;:false,&quot;isStaleEdit&quot;:false}"> 
  <div class="twitter-embed embed">
    <div class="twitter-header">
        <div style="display:flex">
          <a target="_blank" href="https://twitter.com/eth_call">
              <img alt="User Avatar" class="twitter-avatar" src="https://storage.googleapis.com/papyrus_images/1184480ee9b770ee98a2a52c46365f52ccf3757de691f7536a273cffa8526ad8.jpg" />
            </a>
            <div style="margin-left:4px;margin-right:auto;line-height:1.2;">
              <a target="_blank" href="https://twitter.com/eth_call" class="twitter-displayname">horsefacts</a>
              <p><a target="_blank" href="https://twitter.com/eth_call" class="twitter-username">@eth_call</a></p>
    
            </div>
            <a href="https://twitter.com/eth_call/status/1612628007851630592" target="_blank">
              <img alt="Twitter Logo" class="twitter-logo" src="https://paragraph.com/editor/twitter/logo.png" />
            </a>
          </div>
        </div>
      
    <div class="twitter-body">
      you absolutely love to see it <img class="twitter-emoji" draggable="false" alt="🗄️" src="https://abs-0.twimg.com/emoji/v2/72x72/1f5c4.png"/><img class="twitter-emoji" draggable="false" alt="📁" src="https://abs-0.twimg.com/emoji/v2/72x72/1f4c1.png"/><img class="twitter-emoji" draggable="false" alt="🗃️" src="https://abs-0.twimg.com/emoji/v2/72x72/1f5c3.png"/> 
      <div class="twitter-media"><img class="twitter-image" src="https://storage.googleapis.com/papyrus_images/c9c19d360a83c438fe7facb39526a789d82e7d29ee2c60abd856764ecda36f73.jpg" /></div>
      
       
    </div>
    
     <div class="twitter-footer">
          <a target="_blank" href="https://twitter.com/eth_call/status/1612628007851630592" style="margin-right:16px; display:flex;">
            <img alt="Like Icon" class="twitter-heart" src="https://paragraph.com/editor/twitter/heart.png">
            55
          </a>
          <a target="_blank" href="https://twitter.com/eth_call/status/1612628007851630592"><p>7:50 PM • Jan 9, 2023</p></a>
        </div>
    
  </div> 
  </div><p>The first place I saw this pattern in the wild was in Maker&apos;s central accounting contract, the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.makerdao.com/smart-contract-modules/core-module/vat-detailed-documentation">Vat</a>. (And as far as I know, they were the first to call this <code>file</code>). Here&apos;s the original <code>file</code> function from <code>Vat.sol</code>:</p><pre data-type="codeBlock" text="// --- Data ---
struct Ilk {
    uint256 Art;   // Total Normalised Debt     [wad]
    uint256 rate;  // Accumulated Rates         [ray]
    uint256 spot;  // Price with Safety Margin  [ray]
    uint256 line;  // Debt Ceiling              [rad]
    uint256 dust;  // Urn Debt Floor            [rad]
}

mapping (bytes32 =&gt; Ilk) public ilks;

uint256 public Line;  // Total Debt Ceiling  [rad]

// --- Administration ---

function file(bytes32 what, uint data) external note auth {
  require(live == 1, &quot;Vat/not-live&quot;);
  if (what == &quot;Line&quot;) Line = data;
  else revert(&quot;Vat/file-unrecognized-param&quot;);
}

function file(bytes32 ilk, bytes32 what, uint data) external note auth {
  require(live == 1, &quot;Vat/not-live&quot;);
  if (what == &quot;spot&quot;) ilks[ilk].spot = data;
  else if (what == &quot;line&quot;) ilks[ilk].line = data;
  else if (what == &quot;dust&quot;) ilks[ilk].dust = data;
  else revert(&quot;Vat/file-unrecognized-param&quot;);
}
"><code><span class="hljs-comment">// --- Data ---</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title">Ilk</span> {
    <span class="hljs-keyword">uint256</span> Art;   <span class="hljs-comment">// Total Normalised Debt     [wad]</span>
    <span class="hljs-keyword">uint256</span> rate;  <span class="hljs-comment">// Accumulated Rates         [ray]</span>
    <span class="hljs-keyword">uint256</span> spot;  <span class="hljs-comment">// Price with Safety Margin  [ray]</span>
    <span class="hljs-keyword">uint256</span> line;  <span class="hljs-comment">// Debt Ceiling              [rad]</span>
    <span class="hljs-keyword">uint256</span> dust;  <span class="hljs-comment">// Urn Debt Floor            [rad]</span>
}

<span class="hljs-keyword">mapping</span> (<span class="hljs-keyword">bytes32</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> Ilk) <span class="hljs-keyword">public</span> ilks;

<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> Line;  <span class="hljs-comment">// Total Debt Ceiling  [rad]</span>

<span class="hljs-comment">// --- Administration ---</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">file</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> what, <span class="hljs-keyword">uint</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">note</span> <span class="hljs-title">auth</span> </span>{
  <span class="hljs-built_in">require</span>(live <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>, <span class="hljs-string">"Vat/not-live"</span>);
  <span class="hljs-keyword">if</span> (what <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"Line"</span>) Line <span class="hljs-operator">=</span> data;
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">revert</span>(<span class="hljs-string">"Vat/file-unrecognized-param"</span>);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">file</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> ilk, <span class="hljs-keyword">bytes32</span> what, <span class="hljs-keyword">uint</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">note</span> <span class="hljs-title">auth</span> </span>{
  <span class="hljs-built_in">require</span>(live <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>, <span class="hljs-string">"Vat/not-live"</span>);
  <span class="hljs-keyword">if</span> (what <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"spot"</span>) ilks[ilk].spot <span class="hljs-operator">=</span> data;
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (what <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"line"</span>) ilks[ilk].line <span class="hljs-operator">=</span> data;
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (what <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"dust"</span>) ilks[ilk].dust <span class="hljs-operator">=</span> data;
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">revert</span>(<span class="hljs-string">"Vat/file-unrecognized-param"</span>);
}
</code></pre><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/makerdao/dss/blob/fa4f6630afb0624d04a003e920b0d71a00331d98/src/vat.sol#L104">https://github.com/makerdao/dss/blob/fa4f6630afb0624d04a003e920b0d71a00331d98/src/vat.sol#L104</a></p><p>In Maker&apos;s case, the <code>note</code> modifier handles emitting the call arguments as a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/dapphub/ds-note">ds-note</a> anonymous event, leaving just two concise functions to handle all configuration setters.</p><p>Using a <code>file</code> function to replace multiple setters is a clean, concise way to manage contracts with many configuration parameters. But it can also hide complexity in your code. Before adding a <code>file</code> function to a contract with many configuration variables, consider whether they are all necessary, and whether your contract might have too many responsibilities at once. But when some complex configuration is necessary, they are a nice little technique.</p>]]></content:encoded>
            <author>horsefacts@newsletter.paragraph.com (horsefacts)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/2b6577af18c0562ecfb3cbdda6d78ba452a0e0f8517b0a6f3a8e19355017877e.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>