<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[bitc0dex]]></title><description><![CDATA[bitc0dex]]></description><link>https://blog.bitcodex.tech</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1755010827784/a0f3d54e-a0c1-4699-949e-45760faf70e7.png</url><title>bitc0dex</title><link>https://blog.bitcodex.tech</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 14 May 2026 23:13:33 GMT</lastBuildDate><atom:link href="https://blog.bitcodex.tech/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Zero to Hero in Foundry - Part 6: Time Travel & Events in Tests]]></title><description><![CDATA[Recap
What’s up, fellow chain-surfers! 🏄‍♂️
Welcome back to our grand tour of Foundry. So in Part 5 we looked into what Gas is and how it can be optimized to make our contracts user friendly. We wrote a gas-intensive version of an ether batch transf...]]></description><link>https://blog.bitcodex.tech/foundry-part-6</link><guid isPermaLink="true">https://blog.bitcodex.tech/foundry-part-6</guid><category><![CDATA[Web3]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[foundry]]></category><dc:creator><![CDATA[Abhiram A]]></dc:creator><pubDate>Thu, 21 Aug 2025 11:38:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755775382771/a1c90650-4969-4be5-8591-c50ca7241783.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<h2 id="heading-recap">Recap</h2>
<p>What’s up, fellow chain-surfers! 🏄‍♂️</p>
<p>Welcome back to our grand tour of Foundry. So in Part 5 we looked into what Gas is and how it can be optimized to make our contracts user friendly. We wrote a gas-intensive version of an ether batch transfer contract and understood how we can get gas reports and snapshots. Then, we optimized the contract to remove gas intensive parts and used gas saving methods. Finally, we compared the gas used before and after and achieved ~50% reduction.</p>
<p>Please read it if you haven’t already before continuing</p>
<p>Foundry: Zero to Hero - Part 5</p>
<p><strong>Wanna learn Web3 through live and interactive challenges? You’ll love and find that</strong> <a target="_blank" href="https://www.web3compass.xyz/"><strong>Web3Compass</strong></a> <strong><em>is the best!!</em></strong></p>
</blockquote>
<h2 id="heading-todays-outcome">Today’s Outcome</h2>
<p><strong>Topic of focus: Time Travel &amp; Events in Tests</strong></p>
<blockquote>
<h3 id="heading-code-base-auction-httpsgithubcomabhiramelf15-days-of-foundrytreemainday-6auction"><strong>Code base -</strong> <a target="_blank" href="https://github.com/abhiramelf/15-days-of-foundry/tree/main/day-6/Auction"><strong>Auction ↗️</strong></a></h3>
</blockquote>
<p>We'll be dissecting a simple <code>Auction.sol</code> contract. But the real star of the show isn't the contract itself—it's how we're going to test it. We’re going to become time travelers, bending the blockchain's clock to our will with <code>vm.warp()</code>, and we'll develop advanced hearing to listen for our contract's whispers (aka events) using <code>vm.expectEmit()</code>.</p>
<p>Ready to get your hands dirty? Let's dive in!</p>
<hr />
<h2 id="heading-meet-our-specimen-the-auctionsol-contract">🧐 Meet Our Specimen: The <code>Auction.sol</code> Contract</h2>
<p>First, let's get acquainted with our auction contract. It’s a minimal, no-frills smart contract for a single-item ETH auction.</p>
<blockquote>
<h3 id="heading-heres-our-contract-auctionsol-httpsgithubcomabhiramelf15-days-of-foundryblobmainday-6auctionsrcauctionsol">Here’s our contract - <a target="_blank" href="https://github.com/abhiramelf/15-days-of-foundry/blob/main/day-6/Auction/src/Auction.sol"><strong>Auction.sol 🚀</strong></a></h3>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755774585305/70b12e67-1155-4b11-8220-09593501e0e0.gif" alt class="image--center mx-auto" /></p>
<h3 id="heading-auction-state">Auction State</h3>
<p>The contract has several state variables to keep track of everything happening:</p>
<ul>
<li><p><code>highestBidder</code> (address) &amp; <code>highestBid</code> (uint256): These do exactly what they say on the tin—they track who's currently winning and with how much.</p>
</li>
<li><p><code>auctionStartTime</code> &amp; <code>auctionEndTime</code> (uint256): These are immutable timestamps that define the auction's lifespan. Once set in the constructor, they can't be changed.</p>
</li>
<li><p><code>seller</code> (address payable): The creator of the auction who will receive the final bid.</p>
</li>
<li><p><code>isCanceled</code> &amp; <code>isFinalized</code> (bool): Simple flags to track the auction's status.</p>
</li>
<li><p><code>bids</code> (mapping): This is our refund ledger. When someone gets outbid, their bid amount is stored here, waiting for them to withdraw it. This is a crucial part of the "withdraw pattern".</p>
</li>
</ul>
<h3 id="heading-events-amp-errors">Events &amp; Errors</h3>
<p>Our contract is a chatterbox! It emits events for key actions, making it easy for off-chain applications to track what's happening:</p>
<ul>
<li><p><code>BidPlaced</code>: Fired when a new highest bid is made.</p>
</li>
<li><p><code>Withdrawn</code>: Fired when an outbid user withdraws their funds.</p>
</li>
<li><p><code>AuctionFinalized</code>: Emitted when the seller finalizes the auction and claims the prize.</p>
</li>
<li><p><code>AuctionCanceled</code>: Emitted if the seller cancels the auction.</p>
</li>
</ul>
<p>It also has a bunch of custom errors to give clear feedback when someone tries to do something they shouldn't.</p>
<h3 id="heading-core-functions">Core Functions</h3>
<ul>
<li><p><code>constructor(uint256 _endTime)</code>: When deploying, the seller specifies a duration for the auction. The constructor sets the <code>auctionStartTime</code> to the current block's timestamp and the <code>auctionEndTime</code> to <code>block.timestamp + _endTime</code>.</p>
</li>
<li><p><code>bid()</code>: This is where the magic happens. Anyone can call this payable function to place a bid. It checks if the bid is higher than the current <code>highestBid</code>. If it is, it cleverly refunds the <em>previous</em> highest bidder by adding their bid to the <code>bids</code> mapping, then updates <code>highestBidder</code> and <code>highestBid</code>.</p>
</li>
<li><p><code>withdraw()</code>: If you've been outbid, you call this function to get your ETH back. It's a pull-based system to prevent nasty re-entrancy bugs.</p>
</li>
<li><p><code>finalizeAuction()</code>: Only the seller can call this, and only after the <code>auctionEndTime</code> has passed. It marks the auction as finalized and transfers the <code>highestBid</code> to the seller.</p>
</li>
<li><p><code>cancelAuction()</code>: The seller has an escape hatch! They can cancel the auction, but only if no bids have been placed yet.</p>
</li>
</ul>
<hr />
<h2 id="heading-putting-it-to-the-test-auctiontsol">🧪 Putting It to the Test: <code>Auction.t.sol</code></h2>
<p>Alright, we know the contract. Now, let's see how we can test its every nook and corner with Foundry.</p>
<blockquote>
<h3 id="heading-heres-our-test-contract-auctiontsol-httpsgithubcomabhiramelf15-days-of-foundryblobmainday-6auctiontestauctiontsol">Here’s our test contract - <a target="_blank" href="https://github.com/abhiramelf/15-days-of-foundry/blob/main/day-6/Auction/test/Auction.t.sol"><strong>Auction.t.sol 🚀</strong></a></h3>
</blockquote>
<p>Our test setup (<code>setUp()</code> function) uses a handy address <code>0xBEEF</code> as the <code>owner</code> and gives it 10 ETH using <code>vm.deal(owner, 10 ether)</code>. We then use <code>vm.prank(owner)</code> to make sure the <code>Auction</code> contract is deployed from this address, setting it as the <code>seller</code>.</p>
<p>Now for the main event!</p>
<h3 id="heading-bending-time-with-vmwarp">⏰ Bending Time with <code>vm.warp()</code></h3>
<p>Our contract has time-sensitive logic. The <code>finalizeAuction()</code> function should only work <em>after</em> <code>auctionEndTime</code>. The <code>bid()</code> function should only work <em>before</em> <code>auctionEndTime</code>. How do we test this without sitting around waiting for a day?</p>
<p>We don't! We use <code>vm.warp()</code>. This cheat-code is our time machine; it instantly sets the blockchain's <code>block.timestamp</code> to any value we want.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755774977763/f1c4447f-3c78-4c14-a85a-60d462c2d95a.gif" alt class="image--center mx-auto" /></p>
<p>Let's look at a couple of tests.</p>
<p>First, how do we test that bidding is disabled after the auction ends? Simple:</p>
<pre><code class="lang-solidity"><span class="hljs-comment">/// @notice bidding after the auction end time should revert</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testAuctionEnded</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
    vm.warp(auction.auctionEndTime()); <span class="hljs-comment">// 1. Fast-forward time!</span>
    vm.expectRevert(Auction.AuctionEnded.<span class="hljs-built_in">selector</span>); <span class="hljs-comment">// 2. Expect an error</span>
    auction.bid{<span class="hljs-built_in">value</span>: <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>}(); <span class="hljs-comment">// 3. Try to bid</span>
}
</code></pre>
<p>In this test, we instantly jump to the <code>auctionEndTime</code> using <code>vm.warp()</code>. At this exact moment, any attempt to bid should fail. We tell Forge to expect the <code>AuctionEnded</code> error, and then we try to bid. If the bid fails with that specific error, the test passes! 🎉</p>
<p>Similarly, to test the happy path for finalizing the auction, we need to be in the future:</p>
<pre><code class="lang-solidity"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testFinalizeAuction</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
    <span class="hljs-comment">// ... users place bids ...</span>

    vm.warp(auction.auctionEndTime()); <span class="hljs-comment">// Jump to the end</span>

    vm.expectEmit(<span class="hljs-literal">true</span>, <span class="hljs-literal">true</span>, <span class="hljs-literal">true</span>, <span class="hljs-literal">true</span>);
    <span class="hljs-keyword">emit</span> AuctionFinalized(user2, <span class="hljs-number">2</span> <span class="hljs-literal">ether</span>);

    vm.prank(owner);
    auction.finalizeAuction(); <span class="hljs-comment">// Now, this should work!</span>
}
</code></pre>
<p>Without <code>vm.warp()</code>, testing time-dependent logic would be a nightmare. With it, it's a piece of cake.</p>
<hr />
<h3 id="heading-listening-for-events-with-vmexpectemit">🎧 Listening for Events with <code>vm.expectEmit()</code></h3>
<p>State changes are great, but a well-behaved contract should also communicate its actions through events. How do we test that the right events are being emitted with the right data? With <code>vm.expectEmit()</code>, of course!</p>
<p>Think of it as putting a stethoscope on your contract. You tell Forge, "I expect to hear <em>this specific sound</em>," and then you trigger the action.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755776062176/d9dd7bfd-ab83-4511-ba9c-b3d8221fed17.gif" alt class="image--center mx-auto" /></p>
<p>Let's see how we test the <code>BidPlaced</code> event:</p>
<pre><code class="lang-solidity"><span class="hljs-comment">/// @notice ensure BidPlaced event is emitted with correct values</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testBid_EmitsBidPlaced</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
    vm.expectEmit(<span class="hljs-literal">true</span>, <span class="hljs-literal">false</span>, <span class="hljs-literal">false</span>, <span class="hljs-literal">true</span>); <span class="hljs-comment">// 1. Set up our listener</span>
    <span class="hljs-keyword">emit</span> BidPlaced(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>); <span class="hljs-comment">// 2. Define the expected event</span>

    auction.bid{<span class="hljs-built_in">value</span>: <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>}(); <span class="hljs-comment">// 3. Place the bid</span>
}
</code></pre>
<p>Let's break down that <code>vm.expectEmit</code> line, as it can look a bit cryptic: <code>vm.expectEmit(checkTopic1, checkTopic2, checkTopic3, checkData)</code></p>
<ul>
<li><p>In Solidity events, parameters marked as <code>indexed</code> become <strong>topics</strong>. They are easier to search for. Non-indexed parameters are part of the <strong>data</strong> payload.</p>
</li>
<li><p>Our<code>BidPlaced</code> event is <code>event BidPlaced(address indexed bidder, uint256 amount)</code>.</p>
<ul>
<li><p><code>bidder</code> is the first topic.</p>
</li>
<li><p><code>amount</code> is the data.</p>
</li>
</ul>
</li>
<li><p>So, <code>vm.expectEmit(true, false, false, true)</code> means:</p>
<ul>
<li><p><code>checkTopic1 = true</code>: Yes, please check the first topic (the bidder's address).</p>
</li>
<li><p><code>checkTopic2 = false</code>: Ignore the second topic (there isn't one).</p>
</li>
<li><p><code>checkTopic3 = false</code>: Ignore the third topic (also doesn't exist).</p>
</li>
<li><p><code>checkData = true</code>: Yes, please check the event's data (the bid amount).</p>
</li>
</ul>
</li>
</ul>
<p>The very next line, <code>emit BidPlaced(address(this), 1 ether);</code>, tells Foundry <em>exactly</em> what to look for: an event where the <code>bidder</code> is the test contract (<code>address(this)</code>) and the <code>amount</code> is <code>1 ether</code>. When <code>auction.bid()</code> is called, Foundry checks if the emitted event matches these expectations. If it does, we get a green light. ✅</p>
<hr />
<h2 id="heading-wrapping-up">🚀 Wrapping Up</h2>
<p>And that's a wrap! We took a seemingly complex <code>Auction</code> contract and wrote powerful, precise tests for it. We saw how to:</p>
<ul>
<li><p><strong>Become a Time Lord</strong> with <code>vm.warp()</code> to test logic that depends on the passage of time.</p>
</li>
<li><p><strong>Become a Super-Listener</strong> with <code>vm.expectEmit()</code> to ensure our contract is communicating correctly with the outside world.</p>
</li>
<li><p>Use other cheat-codes like <code>vm.expectRevert</code>, <code>vm.prank</code>, and <code>vm.deal</code> to create any test scenario we can imagine.</p>
</li>
</ul>
<p>Testing doesn't have to be a chore. With tools like Foundry, it can be an incredibly powerful and, dare I say, <em>fun</em> part of the development process. Now go ahead, clone that repo, run <code>forge test</code> for yourself, and try breaking our contract! Happy forging!</p>
]]></content:encoded></item><item><title><![CDATA[Zero to Hero in Foundry - Part 5: Gas Optimization]]></title><description><![CDATA[Recap
We’re on a roll! Let’s keep going!
Part 4 we dug deep into writing fuzz and invariant tests. We wrote our token mint contracts and the 1:1 swap contract which let users swap our tokens. Then we fuzz tested our swap by using Foundry’s in-built f...]]></description><link>https://blog.bitcodex.tech/foundry-part-5</link><guid isPermaLink="true">https://blog.bitcodex.tech/foundry-part-5</guid><category><![CDATA[Web3]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[foundry]]></category><dc:creator><![CDATA[Abhiram A]]></dc:creator><pubDate>Sun, 17 Aug 2025 09:38:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755423349248/68748bd7-5e9a-4983-8054-30ae9516d806.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<h2 id="heading-recap">Recap</h2>
<p>We’re on a roll! Let’s keep going!</p>
<p>Part 4 we dug deep into writing fuzz and invariant tests. We wrote our token mint contracts and the 1:1 swap contract which let users swap our tokens. Then we fuzz tested our swap by using Foundry’s in-built fuzzer and made sure our contracts fundamental unbreakable rules remained intact in different scenarios using invariant testing.</p>
<p>Please read it if you haven't already before continuing</p>
<p><a target="_blank" href="https://blog.bitcodex.tech/foundry-part-4"><strong>Foundry: Zero to Hero - Part 4</strong></a></p>
<p><strong>Wanna learn Web3 through live and interactive challenges? You’ll love and find that</strong> <a target="_blank" href="https://www.web3compass.xyz/"><strong>Web3Compass</strong></a> <strong><em>is the best!!</em></strong></p>
</blockquote>
<h2 id="heading-todays-outcome">Today’s Outcome</h2>
<p><strong>Topic of focus: Gas optimization</strong></p>
<p><strong>Code base -</strong> <a target="_blank" href="https://github.com/abhiramelf/15-days-of-foundry/tree/main/day-5/BatchTransfer"><strong>BatchTransfer ↗️</strong></a></p>
<ul>
<li><p>What is Gas and why is it required?</p>
</li>
<li><p>Create an ether batch transferring contract that’s intentionally gas intensive</p>
</li>
<li><p>Write tests for getting gas reports and taking gas snapshots</p>
</li>
<li><p>Optimize our batch transfer contract</p>
</li>
<li><p>Compare gas consumed before and after</p>
</li>
</ul>
<hr />
<h2 id="heading-so-whats-the-deal-with-gas">So, What's the Deal with Gas? ⛽</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755418366733/ff9a30b2-44ff-4ce2-bc32-a3856e911290.gif" alt class="image--center mx-auto" /></p>
<p>Think of <strong>gas</strong> as the small fee we pay to get anything done on the Ethereum network. It’s the fuel that makes the whole system run. Every single transaction—from sending tokens to minting an NFT—requires this fuel.</p>
<p>We need gas for two simple reasons:</p>
<ol>
<li><p><strong>It stops the network from breaking.</strong> Without a cost, a single bad piece of code (like an infinite loop) could get stuck and freeze the entire network. Gas acts like a safety switch that cuts the power if a transaction uses too much fuel.</p>
</li>
<li><p><strong>It pays the people who run the network.</strong> The fees go to validators who use their computers to process our transactions and keep the blockchain secure.</p>
</li>
</ol>
<h2 id="heading-the-biggest-gas-guzzlers-to-avoid">The Biggest Gas Guzzlers to Avoid 💸</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755418716779/054204b9-3911-48fe-9e4e-64bfdfccd025.gif" alt class="image--center mx-auto" /></p>
<p>Some actions in Solidity barely sip gas, while others chug it like there's no tomorrow. Here are the main ones to watch out for:</p>
<h4 id="heading-1-writing-to-storage-sstore"><strong>1. Writing to Storage (</strong><code>SSTORE</code>)</h4>
<p>This is Public Enemy #1 for gas fees. Writing or changing data in storage is like carving something into the blockchain's permanent public record—it's a big deal and very expensive.</p>
<ul>
<li><strong>Our goal:</strong> Write to storage as little as possible. If we need to use the same piece of stored data multiple times in a function, we should copy it to a memory variable first.</li>
</ul>
<h4 id="heading-2-loops-especially-unpredictable-ones"><strong>2. Loops (Especially Unpredictable Ones)</strong></h4>
<p>A loop that runs over an array in storage can be a ticking time bomb. If that array gets too big, the gas cost to loop through it can become so massive that the function is impossible to call.</p>
<ul>
<li><strong>Our goal:</strong> Avoid, whenever possible, loops that run based on a storage array that can grow indefinitely.</li>
</ul>
<h4 id="heading-3-creating-new-contracts-create"><strong>3. Creating New Contracts (</strong><code>CREATE</code>)</h4>
<p>Deploying a new smart contract is a heavyweight operation. It's like adding a new building to the city—a complex and costly process that consumes a lot of gas.</p>
<ul>
<li><strong>Our goal:</strong> Be mindful of designs where our system needs to create tons of new contracts.</li>
</ul>
<h4 id="heading-4-external-calls-to-other-contracts"><strong>4. External Calls to Other Contracts</strong></h4>
<p>When our contract calls another contract, we have to pay the gas for everything that happens inside that other contract, too. It’s like picking up the tab for a friend at a very expensive restaurant.</p>
<ul>
<li><strong>Our goal:</strong> Be aware of what the contracts we're calling are doing, as their gas costs become our gas costs.</li>
</ul>
<hr />
<h2 id="heading-the-solidity-gas-optimization-adventure">The Solidity Gas Optimization Adventure 🚀</h2>
<p>Alright, so we've covered the "what" and "why" of gas. We know it's the fuel and we know that wasting it is like setting a pile of money on fire. 🔥 Now, let's get our hands dirty and dive into the fun part: taking a horribly inefficient smart contract and turning it into a lean, mean, gas-sipping machine.</p>
<p>We'll be using the awesome power of <strong>Foundry</strong> to not only test our contracts but also to get concrete proof of our heroic optimization efforts. Let's begin!</p>
<h3 id="heading-meet-our-villain-gassybatchtransfersol">Meet Our Villain: <code>GassyBatchTransfer.sol</code></h3>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.30;</span>

<span class="hljs-comment">// Importing Ownable to add an owner and increase storage reads.</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/access/Ownable.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">GassyBatchTransfer</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Ownable</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) <span class="hljs-title">Ownable</span>(<span class="hljs-params"><span class="hljs-built_in">msg</span>.sender</span>) </span>{}

    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> totalTransfersCompleted <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> totalEtherSent <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;


    <span class="hljs-comment">// A mapping to store data for each recipient.</span>
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> <span class="hljs-keyword">uint256</span>) <span class="hljs-keyword">public</span> recipientTransferCounts;

    <span class="hljs-comment">// A dynamic array in storage. Pushing to this array in a loop is extremely inefficient</span>
    <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">public</span> transferHistory;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">EtherTransferred</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> recipient, <span class="hljs-keyword">uint256</span> amount</span>)</span>;

    <span class="hljs-comment">// Receives Ether to fund the contract for batch transfers.</span>
    <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>{}

    <span class="hljs-comment">// Batch transfers Ether to multiple recipients in a highly inefficient way.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">batchTransfer</span>(<span class="hljs-params"><span class="hljs-keyword">address</span>[] <span class="hljs-keyword">memory</span> _recipients, <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> _amounts</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> <span class="hljs-title">onlyOwner</span> </span>{

        <span class="hljs-built_in">require</span>(_recipients.<span class="hljs-built_in">length</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> _amounts.<span class="hljs-built_in">length</span>, <span class="hljs-string">"Recipients and amounts arrays must have the same length."</span>);
        <span class="hljs-built_in">require</span>(_recipients.<span class="hljs-built_in">length</span> <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>, <span class="hljs-string">"No recipients provided."</span>);

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> _recipients.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            <span class="hljs-keyword">address</span> recipient <span class="hljs-operator">=</span> _recipients[i];
            <span class="hljs-keyword">uint256</span> amount <span class="hljs-operator">=</span> _amounts[i];

            <span class="hljs-comment">// --- Inefficient Check inside the loop ---</span>
            <span class="hljs-built_in">require</span>(recipient <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>), <span class="hljs-string">"Cannot send to the zero address."</span>);

            <span class="hljs-comment">// Reading from storage (SLOAD) in a loop costs gas every time.</span>
            <span class="hljs-comment">// We read the owner address here even though it doesn't change and isn't used.</span>
            <span class="hljs-keyword">address</span> contractOwner <span class="hljs-operator">=</span> owner();
            <span class="hljs-built_in">require</span>(contractOwner <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>)); <span class="hljs-comment">// Dummy check to prevent compiler optimization</span>

            <span class="hljs-keyword">payable</span>(recipient).<span class="hljs-built_in">transfer</span>(amount);

            totalTransfersCompleted <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
            totalEtherSent <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;

            <span class="hljs-comment">// Update the mapping for the recipient.</span>
            recipientTransferCounts[recipient] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;

            <span class="hljs-comment">// Push the recipient to the storage array. This is one of the most</span>
            <span class="hljs-comment">// gas-intensive operations you can put in a loop.</span>
            transferHistory.<span class="hljs-built_in">push</span>(recipient);

            <span class="hljs-keyword">emit</span> EtherTransferred(recipient, amount);
        }
    }

    <span class="hljs-comment">// A helper function to check the contract's current balance.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getBalance</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>We built this contract with one purpose in mind: to be an absolute gas guzzler. It performs a simple task—sending Ether to a bunch of addresses at once—but it does so in the most spectacularly inefficient way imaginable.</p>
<p>Let's look at its main crimes against gas efficiency:</p>
<ul>
<li><p><strong>The Storage Spree</strong>: The biggest issue is inside its <code>for</code> loop. On <em>every single pass</em>, it writes to four different state variables (<code>totalTransfersCompleted</code>, <code>totalEtherSent</code>, <code>recipientTransferCounts</code>, and <code>transferHistory</code>). Think of writing to storage (<code>SSTORE</code>) as carving something into a giant stone tablet. It's permanent, and it takes a <em>ton</em> of energy. Our contract is basically chiseling a new line on that tablet for every single recipient. Ouch.</p>
</li>
<li><p><strong>Pointless Peeking</strong>: Just for good measure, we added <code>address contractOwner = owner();</code> inside the loop. This reads from storage (<code>SLOAD</code>) every time. It's like re-reading the same sentence over and over again while the meter is running.</p>
</li>
<li><p><strong>The Ever-Expanding Backpack</strong>: The line <code>transferHistory.push(recipient)</code> is a classic gas-trap. Pushing to a dynamic array in storage is like stuffing another item into a backpack that might need to magically grow larger. It's a slow and costly process.</p>
</li>
<li><p><strong>Data Detour</strong>: The function takes its inputs as <code>address[] memory</code>. This tells Solidity to make a copy of all the data in memory. For data we're just reading, this is an unnecessary detour.</p>
</li>
</ul>
<h3 id="heading-the-hero-arrives-optimizedbatchtransfersol">The Hero Arrives: <code>OptimizedBatchTransfer.sol</code></h3>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.30;</span>

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">OptimizedBatchTransfer</span> </span>{

    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> totalTransfersCompleted <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> totalEtherSent <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">immutable</span> owner;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) </span>{
        owner <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
    }

    <span class="hljs-comment">// Event to log each transfer. This is an efficient way to log activity.</span>
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">EtherTransferred</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> recipient, <span class="hljs-keyword">uint256</span> amount</span>)</span>;

    <span class="hljs-comment">// Custom error definitions for better error handling and gas efficiency</span>
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">NotOwner</span>(<span class="hljs-params"></span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">RecipientAndAmountMismatch</span>(<span class="hljs-params"></span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">NoRecipients</span>(<span class="hljs-params"></span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">ZeroAddress</span>(<span class="hljs-params"></span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">InsufficientBalance</span>(<span class="hljs-params"></span>)</span>;

    <span class="hljs-comment">// Receives Ether to fund the contract for batch transfers.</span>
    <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>{}

    <span class="hljs-comment">// Batch transfers Ether to multiple recipients in a gas-efficient way.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">batchTransfer</span>(<span class="hljs-params"><span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> _recipients, <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">calldata</span> _amounts</span>) <span class="hljs-title">onlyOwner</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-keyword">if</span> (_recipients.<span class="hljs-built_in">length</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> _amounts.<span class="hljs-built_in">length</span>) <span class="hljs-keyword">revert</span> RecipientAndAmountMismatch();
        <span class="hljs-keyword">if</span> (_recipients.<span class="hljs-built_in">length</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) <span class="hljs-keyword">revert</span> NoRecipients();

        <span class="hljs-comment">// We use a local variable to sum up the total amount to be sent.</span>
        <span class="hljs-comment">// Operations in memory are much cheaper than operations in storage (i.e., updating state variables).</span>
        <span class="hljs-keyword">uint256</span> totalAmountToSend <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
        <span class="hljs-keyword">uint256</span> recipientsCount <span class="hljs-operator">=</span> _recipients.<span class="hljs-built_in">length</span>; <span class="hljs-comment">// Caching array length in a local variable saves gas on each loop access.</span>

        <span class="hljs-keyword">unchecked</span> {
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> recipientsCount; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
                <span class="hljs-comment">// This check remains inside the loop for security, ensuring no funds are sent to an invalid address.</span>
                <span class="hljs-keyword">if</span> (_recipients[i] <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>)) <span class="hljs-keyword">revert</span> ZeroAddress();
                <span class="hljs-comment">// Add the amount to our local sum variable.</span>
                totalAmountToSend <span class="hljs-operator">+</span><span class="hljs-operator">=</span> _amounts[i];
            }
        }

        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span> <span class="hljs-operator">&lt;</span> totalAmountToSend) <span class="hljs-keyword">revert</span> InsufficientBalance();

        <span class="hljs-comment">// We loop again to perform the actual transfers</span>
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> recipientsCount;) {
            <span class="hljs-keyword">payable</span>(_recipients[i]).<span class="hljs-built_in">transfer</span>(_amounts[i]);
            <span class="hljs-keyword">emit</span> EtherTransferred(_recipients[i], _amounts[i]);
            <span class="hljs-keyword">unchecked</span> {
                i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>;
            }
        }

        <span class="hljs-comment">// This is the most critical optimization. We update the state variables only ONCE,</span>
        <span class="hljs-comment">// after the loop has completed. This saves an enormous amount of gas compared to updating in every iteration.</span>
        <span class="hljs-keyword">unchecked</span> {
            totalTransfersCompleted <span class="hljs-operator">+</span><span class="hljs-operator">=</span> recipientsCount;
            totalEtherSent <span class="hljs-operator">+</span><span class="hljs-operator">=</span> totalAmountToSend;
        }
    }

    <span class="hljs-comment">// A helper function to check the contract's current balance.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getBalance</span>(<span class="hljs-params"></span>) <span class="hljs-title">onlyOwner</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">modifier</span> <span class="hljs-title">onlyOwner</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> owner) <span class="hljs-keyword">revert</span> NotOwner();
        <span class="hljs-keyword">_</span>;
    }
}
</code></pre>
<p>Fear not! We can fix this mess. Our hero contract, <code>OptimizedBatchTransfer.sol</code>, swoops in to save the day (and our Ether). It does the exact same job, but with grace and efficiency.</p>
<p>Here’s how we transformed our villain into a hero:</p>
<ol>
<li><p><strong>Thinking in Memory</strong>: Instead of carving into that stone tablet over and over, we do all our math on a temporary notepad (memory). We create a local variable <code>totalAmountToSend</code> and loop once to add everything up. Memory operations are thousands of times cheaper than storage operations!</p>
</li>
<li><p><strong>One Write to Rule Them All</strong>: After the loop is done, we update our state variables <code>totalTransfersCompleted</code> and <code>totalEtherSent</code> just <strong>once</strong>. We went from dozens of expensive storage writes down to two. That's a huge win!</p>
</li>
<li><p><strong>Ditching the Baggage</strong>: We completely removed the <code>recipientTransferCounts</code> mapping and the <code>transferHistory</code> array. Why? Because we can get the same information almost for free using <strong>events</strong>. Events are like shouting out a log of what happened for the off-chain world to hear, without needing to permanently store it on that expensive stone tablet.</p>
</li>
<li><p><strong>The</strong> <code>calldata</code> Express Lane: We changed the function arguments from <code>memory</code> to <code>calldata</code>. Think of <code>calldata</code> as a direct, read-only lane for data coming into our function. It's the most efficient way to handle external inputs that we don't need to change.</p>
</li>
<li><p><strong>Speaking in Code</strong>: We swapped our <code>require</code> statements for <strong>custom errors</strong>. Not only does this make our code cleaner, but it also saves a surprising amount of gas compared to using error strings.</p>
</li>
</ol>
<hr />
<h2 id="heading-the-moment-of-truth">The Moment of Truth</h2>
<p>Talk is cheap, but gas isn't. Let's get some hard numbers to prove our optimizations worked. This is where Foundry shines.</p>
<p>First let’s make sure we add the following lines to our <code>foundry.toml</code></p>
<pre><code class="lang-ini"><span class="hljs-attr">gas_reports</span> = [<span class="hljs-string">"*"</span>]           <span class="hljs-comment"># Enable gas reports for all contracts</span>
<span class="hljs-attr">gas_reports_ignore</span> = []       <span class="hljs-comment"># Don't ignore any contracts</span>
</code></pre>
<h3 id="heading-gas-reports">Gas Reports</h3>
<p>let’s writw a simple test in <code>BatchTransfer.t.sol</code> that calls the function for both contracts.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.30;</span>

<span class="hljs-comment">/// @title Batch Transfer Test</span>
<span class="hljs-comment">/// @notice This contract tests the gas usage of different batch transfer implementations.</span>
<span class="hljs-comment">/// Comment the GassyBatchTransfer references and uncomment the OptimizedBatchTransfer references</span>
<span class="hljs-comment">/// for getting gas reports of OptimizedBatchTransfer contract and vice versa.</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Test</span>, <span class="hljs-title">console</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">GassyBatchTransfer</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../src/GassyBatchTransfer.sol"</span>;
<span class="hljs-comment">//import {OptimizedBatchTransfer} from "../src/OptimizedBatchTransfer.sol";</span>

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">BatchTransferTest</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    GassyBatchTransfer <span class="hljs-keyword">public</span> gassyBatchTransfer;
    <span class="hljs-comment">//OptimizedBatchTransfer public optimizedBatchTransfer;</span>
    <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">public</span> recipients;
    <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">public</span> amounts;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> owner;

    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> NUM_RECIPIENTS <span class="hljs-operator">=</span> <span class="hljs-number">10</span>;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> AMOUNT_PER_RECIPIENT <span class="hljs-operator">=</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">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        gassyBatchTransfer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> GassyBatchTransfer();
        <span class="hljs-comment">//optimizedBatchTransfer = new OptimizedBatchTransfer();</span>
        owner <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>);

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> NUM_RECIPIENTS; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            <span class="hljs-comment">// Create new, unique addresses for recipients using Foundry's `makeAddr` cheatcode.</span>
            <span class="hljs-keyword">address</span> recipient <span class="hljs-operator">=</span> makeAddr(<span class="hljs-keyword">string</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(<span class="hljs-string">"recipient"</span>, vm.toString(i <span class="hljs-operator">+</span> <span class="hljs-number">1</span>))));
            recipients.<span class="hljs-built_in">push</span>(recipient);
            amounts.<span class="hljs-built_in">push</span>(AMOUNT_PER_RECIPIENT);
        }

        <span class="hljs-keyword">uint256</span> totalAmount <span class="hljs-operator">=</span> AMOUNT_PER_RECIPIENT <span class="hljs-operator">*</span> NUM_RECIPIENTS;
        vm.deal(<span class="hljs-keyword">address</span>(gassyBatchTransfer), totalAmount);
        <span class="hljs-comment">//vm.deal(address(optimizedBatchTransfer), totalAmount);</span>

        assertEq(<span class="hljs-keyword">address</span>(gassyBatchTransfer).<span class="hljs-built_in">balance</span>, totalAmount, <span class="hljs-string">"Initial balance not correct"</span>);
        <span class="hljs-comment">//assertEq(address(optimizedBatchTransfer).balance, totalAmount, "Initial balance not correct");</span>
    }

    <span class="hljs-comment">// Add your test functions here</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_gas_batchTransfer</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-comment">// Test the gas consumption of the batch transfer function</span>
        <span class="hljs-keyword">uint256</span> gasUsed <span class="hljs-operator">=</span> <span class="hljs-built_in">gasleft</span>();

        gassyBatchTransfer.batchTransfer(recipients, amounts);
        gasUsed <span class="hljs-operator">-</span><span class="hljs-operator">=</span> <span class="hljs-built_in">gasleft</span>();
        console.log(<span class="hljs-string">"Gas used for gassy batch transfer:"</span>, gasUsed);
        <span class="hljs-keyword">emit</span> log_named_uint(<span class="hljs-string">"Gas used for gassy batch transfer"</span>, gasUsed);

        <span class="hljs-comment">// optimizedBatchTransfer.batchTransfer(recipients, amounts);</span>
        <span class="hljs-comment">// gasUsed -= gasleft();</span>
        <span class="hljs-comment">// console.log("Gas used for optimized batch transfer:", gasUsed);</span>
        <span class="hljs-comment">// emit log_named_uint("Gas used for optimized batch transfer", gasUsed);</span>
    }
}
</code></pre>
<p>First, let’s see the report of out inefficient contract. To see that, we just need to run one command:</p>
<p>Bash</p>
<pre><code class="lang-bash">forge <span class="hljs-built_in">test</span> --gas-report
</code></pre>
<p>Now we’ll un-comment our optimized contract references, comment our inefficient contract ones and run the same command as above. Foundry will run our tests and spit out a beautiful table. It's like a report card for our functions, showing the average gas cost for each.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755421424512/3d96801a-9d84-45d4-a526-748e67b61bac.png" alt class="image--center mx-auto" /></p>
<p>When we look at the results, the difference is staggering. The gas cost for <code>GassyBatchTransfer</code> will be ridiculously high, while <code>OptimizedBatchTransfer</code> will be a fraction of the cost. This is the "Aha!" moment where we see just how much Ether we saved.</p>
<h3 id="heading-gas-snapshots">Gas Snapshots</h3>
<p>For an even more granular view, we can use Foundry's snapshot testing. It's like taking a "before" and "after" photo of our gas costs.</p>
<h4 id="heading-step-1-the-before-photo"><strong>Step 1: The "Before" Photo</strong></h4>
<p>First, we'll run a snapshot test on our gas-guzzling villain using the <code>BatchTransferSnapshot.t.sol</code> file.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.30;</span>

<span class="hljs-comment">/// @title Batch Transfer Snapshot Test</span>
<span class="hljs-comment">/// @notice This contract save the gas snapshots of different batch transfer implementations.</span>
<span class="hljs-comment">/// Comment the GassyBatchTransfer references and uncomment the OptimizedBatchTransfer references</span>
<span class="hljs-comment">/// for getting gas snapshots of OptimizedBatchTransfer contract and vice versa.</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Test</span>, <span class="hljs-title">console</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">GassyBatchTransfer</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../src/GassyBatchTransfer.sol"</span>;
<span class="hljs-comment">//import {OptimizedBatchTransfer} from "../src/OptimizedBatchTransfer.sol";</span>

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">BatchTransferSnapshotTest</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    GassyBatchTransfer <span class="hljs-keyword">public</span> gassyBatchTransfer;
    <span class="hljs-comment">//OptimizedBatchTransfer public optimizedBatchTransfer;</span>
    <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">public</span> recipients;
    <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">public</span> amounts;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> owner;

    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> NUM_RECIPIENTS <span class="hljs-operator">=</span> <span class="hljs-number">10</span>;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> AMOUNT_PER_RECIPIENT <span class="hljs-operator">=</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">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        gassyBatchTransfer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> GassyBatchTransfer();
        <span class="hljs-comment">//optimizedBatchTransfer = new OptimizedBatchTransfer();</span>

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> NUM_RECIPIENTS; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            <span class="hljs-comment">// Create new, unique addresses for recipients using Foundry's `makeAddr` cheatcode.</span>
            <span class="hljs-keyword">address</span> recipient <span class="hljs-operator">=</span> makeAddr(<span class="hljs-keyword">string</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(<span class="hljs-string">"recipient"</span>, vm.toString(i <span class="hljs-operator">+</span> <span class="hljs-number">1</span>))));
            recipients.<span class="hljs-built_in">push</span>(recipient);
            amounts.<span class="hljs-built_in">push</span>(AMOUNT_PER_RECIPIENT);
        }

        <span class="hljs-keyword">uint256</span> totalAmount <span class="hljs-operator">=</span> AMOUNT_PER_RECIPIENT <span class="hljs-operator">*</span> NUM_RECIPIENTS;
        vm.deal(<span class="hljs-keyword">address</span>(gassyBatchTransfer), totalAmount);
        <span class="hljs-comment">//vm.deal(address(optimizedBatchTransfer), totalAmount);</span>
    }

    <span class="hljs-comment">// Add your test functions here</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_snapshot_batchTransfer</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{

        <span class="hljs-comment">// Perform the batch transfer</span>
        gassyBatchTransfer.batchTransfer(recipients, amounts);
        <span class="hljs-comment">//optimizedBatchTransfer.batchTransfer(recipients, amounts);</span>
    }
}
</code></pre>
<p>Bash</p>
<pre><code class="lang-bash">forge snapshot --match-path <span class="hljs-built_in">test</span>/BatchTransferSnapshot.t.sol
</code></pre>
<p>This command runs the test and saves a <code>.gas-snapshot</code> file, which is our "before" picture, detailing the gas usage.</p>
<h4 id="heading-step-2-the-after-photo-and-comparison"><strong>Step 2: The "After" Photo and Comparison</strong></h4>
<p>Next, we'll quickly edit our test file to call our optimized hero contract instead, commenting out all our inefficient contract references. Then, we run the snapshot command again, but with a magic flag:</p>
<p>Bash</p>
<pre><code class="lang-solidity">forge snapshot <span class="hljs-operator">-</span><span class="hljs-operator">-</span>diff <span class="hljs-operator">-</span><span class="hljs-operator">-</span>match<span class="hljs-operator">-</span>path test<span class="hljs-operator">/</span>BatchTransferSnapshot.t.sol
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755421767144/e6941807-0e9f-47cd-8e0d-8cba91189f55.png" alt class="image--center mx-auto" /></p>
<p>This command runs the test on our optimized code and compares it directly to the "before" photo. The output is pure gold—it will show us the exact percentage of gas we saved. Seeing a number like <strong>-49.882%</strong> isn't just satisfying; it's proof that we’ve successfully transformed our contract from a gas-guzzling monster into a lean, efficient hero. 🦸‍♂️</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755422883727/29859515-dcf7-4d72-8bfe-3b440614079d.gif" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-and-thats-a-wrap-for-now">And that's a Wrap! (For now 😬)</h2>
<p>By now we should have</p>
<ul>
<li><p>Understood what gas is and why it needs to be optimized</p>
</li>
<li><p>Our gassy and optimized batch transfer contracts</p>
</li>
<li><p>Tests for getting gas reports for both contracts</p>
</li>
<li><p>Tests for getting gas snapshots of both contracts</p>
</li>
<li><p>Understood the optimization we did to achieve the <strong>-49.882%</strong> gas reduction</p>
</li>
</ul>
<h2 id="heading-whats-next">What's Next?</h2>
<p>Part 6, we'll be</p>
<ul>
<li><p>Writing a simple auction contract where users bid and settle based on time</p>
</li>
<li><p>Focusing majorly on more advanced tests which helps us simulate time and events</p>
</li>
</ul>
<p><strong>Don't miss it! I'll see you soon!</strong> 😎</p>
]]></content:encoded></item><item><title><![CDATA[Zero to Hero in Foundry - Part 4: Fuzz & Invariant Tests]]></title><description><![CDATA[Recap
Awesome! I see you’re back!
Part 3 was a banger with some good old testing fundamentals. We wrote a pretty simple bank contract through which users could deposit and withdraw funds, as well as check their balances. Deployed the same using anvil...]]></description><link>https://blog.bitcodex.tech/foundry-part-4</link><guid isPermaLink="true">https://blog.bitcodex.tech/foundry-part-4</guid><category><![CDATA[Web3]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[Testing]]></category><category><![CDATA[foundry]]></category><category><![CDATA[fuzzing]]></category><category><![CDATA[invariant testing]]></category><dc:creator><![CDATA[Abhiram A]]></dc:creator><pubDate>Thu, 14 Aug 2025 11:52:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755172216894/7d5f2d64-c0eb-479d-bbe7-2e35573c2120.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<h2 id="heading-recap">Recap</h2>
<p>Awesome! I see you’re back!</p>
<p>Part 3 was a banger with some good old testing fundamentals. We wrote a pretty simple bank contract through which users could deposit and withdraw funds, as well as check their balances. Deployed the same using <code>anvil</code> and <code>forge</code> and wrote fundamental tests for making sure that all of the functionalities worked as intended</p>
<p>Please read it if you haven't already before continuing</p>
<p><a target="_blank" href="https://bitc0dex.hashnode.dev/foundry-part-3"><strong>Foundry: Zero to Hero - Part 3</strong></a></p>
<p><strong>Wanna learn Web3 through live and interactive challenges? You’ll love and find that</strong> <a target="_blank" href="https://www.web3compass.xyz/"><strong>Web3Compass</strong></a> <strong><em>is the best!!</em></strong></p>
</blockquote>
<h2 id="heading-todays-outcome">Today's Outcome</h2>
<p><strong>Topic of focus: Fuzz and Invariant Tests</strong></p>
<p><strong>Code base -</strong> <a target="_blank" href="https://github.com/abhiramelf/15-days-of-foundry/tree/main/day-4/TokenSwap"><strong>TokenSwap</strong> ↗️</a></p>
<ul>
<li><p><strong>Write 2 ERC20 token contracts - TokenA and TokenB</strong></p>
</li>
<li><p><strong>Write a TokenSwap.sol contract to swap our ERC20s</strong></p>
</li>
<li><p><strong>Let users add liquidity for both tokens in our swap contract</strong></p>
</li>
<li><p><strong>Let users swap our tokens making sure that there’s enough balance in user’s wallet and our contract pool</strong></p>
</li>
<li><p><strong>And the most important bit - Write extensive Fuzz and Invariant tests</strong></p>
</li>
</ul>
<p><strong>SHALL WE?!</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755166949433/b78b52ba-fd83-441f-8707-a7ba2a7445f6.gif" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-the-what">The What ⁉️</h2>
<h3 id="heading-what-is-fuzz-testing">What is Fuzz Testing? 🥴</h3>
<p><strong>Fuzz testing</strong> is what happens when we give our code an espresso, a blindfold, and a keyboard. It’s an automated technique where we let a "fuzzer" throw absolute nonsense at our functions to see what works (or, more likely, what breaks).</p>
<p>We all have that one test we forget to write. What if the input is <code>115792089237316195423570985008687907853269984665640564039457584007913129639935</code>? What if it's a negative number in a <code>uint</code>? Instead of losing sleep over it, we let Foundry's fuzzer do our dirty work. We just add parameters to a test function, and Foundry unleashes a relentless barrage of random inputs, hoping to make our code cry. When it fails, the fuzzer proudly presents the input that caused the chaos.</p>
<p><strong>Analogy:</strong> Imagine our function is a nightclub bouncer. Normal testing is showing the bouncer a few valid IDs. <strong>Fuzz testing</strong> is sending a thousand aliens, ghosts, and cartoon characters with crayon-drawn IDs to the front door, just to see if one of them somehow gets in.</p>
<h3 id="heading-what-is-invariant-testing">What is Invariant Testing? 🏛️</h3>
<p>If fuzz testing is poking a single function with a stick, <strong>invariant testing</strong> is shaking the entire building to see if the foundations are solid.</p>
<p>An <strong>invariant</strong> is a sacred, unbreakable rule of our contract. It’s a "pinky promise" that must always be true, no matter what happens. For example:</p>
<ul>
<li><p>The total supply of our precious token doesn't just invent itself out of thin air.</p>
</li>
<li><p>The contract’s piggy bank should never be empty if it still owes people money.</p>
</li>
</ul>
<p>Invariant testing lets Foundry's fuzzer go completely wild on our <em>entire</em> contract. It will call any function it wants, in any order, like a baby who found the TV remote. Its only goal is to break one of our sacred rules. If it manages to shatter a promise, it tells us exactly how it did it, so we can fix our flawed logic.</p>
<p><strong>Analogy:</strong> Let's say our contract is a smoothie stall. Our main invariant is: "The number of bananas we have must always equal to the number of bananas we started with, minus the number we've sold." An <strong>invariant test</strong> is like unleashing a horde of hyperactive squirrels to randomly buy smoothies, sell us more bananas, and try to knock over the stall for a full day. At the end, we check if our banana count is still correct. If not, we've got a squirrelly bug.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Aspect</strong></td><td><strong>Fuzz Testing 💥</strong></td><td><strong>Invariant Testing ⚖️</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Scope</strong></td><td>Tests a <strong>single function</strong> in isolation.</td><td>Tests the <strong>entire contract</strong> and its states.</td></tr>
<tr>
<td><strong>Goal</strong></td><td>Find inputs that cause an <strong>immediate failure</strong>, like a revert.</td><td>Find a <strong>sequence of calls</strong> that breaks a core rule or promise.</td></tr>
<tr>
<td><strong>What it Checks</strong></td><td>"Does this one specific action crash?"</td><td>"Can the contract's fundamental logic ever be corrupted?"</td></tr>
<tr>
<td><strong>Analogy</strong></td><td>Stress-testing a single car part, like the brakes.</td><td>Putting the entire car on a chaotic race track to see if it survives.</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-the-how">The How 🤔</h2>
<p>Let’s go ahead and see how we can implement these tests. For this we need to have a contract. Time to implement our Tokens and TokenSwap contracts</p>
<h3 id="heading-writing-the-contracts">Writing the Contracts 📝</h3>
<p>TokenA.sol</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.30;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">TokenA</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC20</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) <span class="hljs-title">ERC20</span>(<span class="hljs-params"><span class="hljs-string">"TokenA"</span>, <span class="hljs-string">"TKA"</span></span>) </span>{
        _mint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-number">10000e18</span>);
    }
}
</code></pre>
<p>This creates a basic ERC20 token called TokenA using OpenZeppelin’s secure ERC20 implementation.</p>
<ul>
<li><p><strong>Name and Symbol:</strong> The token is named "TokenA" and its symbol is "TKA".</p>
</li>
<li><p><strong>Initial Supply:</strong> When the contract is deployed, it mints 10,000 tokens (with 18 decimals) to the deployer’s address.</p>
</li>
<li><p><strong>ERC20 Standard:</strong> By inheriting from OpenZeppelin’s ERC20, TokenA supports all standard ERC20 features like transferring tokens, checking balances, and approving allowances.</p>
</li>
</ul>
<p>Similar to this let’s write TokenB.sol for our TokenB supply</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.30;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/ERC20.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">TokenB</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC20</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) <span class="hljs-title">ERC20</span>(<span class="hljs-params"><span class="hljs-string">"TokenB"</span>, <span class="hljs-string">"TKB"</span></span>) </span>{
        _mint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-number">10000e18</span>);
    }
}
</code></pre>
<p>OKAYYY!!! There are our token contracts written and ready to be tested. Time to write our swapping contract</p>
<p>TokenSwap.sol</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.30;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/IERC20.sol"</span>; <span class="hljs-comment">// Importing the ERC20 interface</span>

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">TokenSwap</span> </span>{
    IERC20 <span class="hljs-keyword">public</span> tokenA; <span class="hljs-comment">// TokenA contract instance</span>
    IERC20 <span class="hljs-keyword">public</span> tokenB; <span class="hljs-comment">// TokenB contract instance</span>

    <span class="hljs-comment">// Constructor to set the token addresses</span>
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _tokenA, <span class="hljs-keyword">address</span> _tokenB</span>) </span>{
        tokenA <span class="hljs-operator">=</span> IERC20(_tokenA);
        tokenB <span class="hljs-operator">=</span> IERC20(_tokenB);
    }

    <span class="hljs-comment">// Function to swap TokenA for TokenB</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">swapAforB</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-built_in">require</span>(amount <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>, <span class="hljs-string">"Amounts must be greater than zero"</span>);
        <span class="hljs-built_in">require</span>(tokenA.balanceOf(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>) <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> amount, <span class="hljs-string">"Insufficient TokenA balance in Wallet"</span>);
        <span class="hljs-built_in">require</span>(tokenB.balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)) <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> amount, <span class="hljs-string">"Insufficient TokenB balance in Pool"</span>);
        tokenA.transferFrom(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount);
        tokenB.<span class="hljs-built_in">transfer</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, amount);
    }

    <span class="hljs-comment">// Function to swap TokenB for TokenA</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">swapBforA</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-built_in">require</span>(amount <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>, <span class="hljs-string">"Amounts must be greater than zero"</span>);
        <span class="hljs-built_in">require</span>(tokenB.balanceOf(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>) <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> amount, <span class="hljs-string">"Insufficient TokenB balance in Wallet"</span>);
        <span class="hljs-built_in">require</span>(tokenA.balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)) <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> amount, <span class="hljs-string">"Insufficient TokenA balance in Pool"</span>);
        tokenB.transferFrom(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount);
        tokenA.<span class="hljs-built_in">transfer</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, amount);
    }

    <span class="hljs-comment">// Function to add liquidity for TokenA</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addTokenALiquidity</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-built_in">require</span>(amount <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>, <span class="hljs-string">"Amount must be greater than zero"</span>);
        <span class="hljs-built_in">require</span>(tokenA.balanceOf(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>) <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> amount, <span class="hljs-string">"Insufficient TokenA balance"</span>);
        tokenA.transferFrom(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount);
    }

    <span class="hljs-comment">// Function to add liquidity for TokenB</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addTokenBLiquidity</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-built_in">require</span>(amount <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>, <span class="hljs-string">"Amount must be greater than zero"</span>);
        <span class="hljs-built_in">require</span>(tokenB.balanceOf(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>) <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> amount, <span class="hljs-string">"Insufficient TokenB balance"</span>);
        tokenB.transferFrom(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount);
    }

}
</code></pre>
<p>The contract lets users swap between two ERC20 tokens (TokenA and TokenB) and add liquidity to the pool.</p>
<ul>
<li><p><strong>Token Setup:</strong><br />  The contract is initialized with the addresses of TokenA and TokenB. It uses the standard ERC20 interface to interact with both tokens.</p>
</li>
<li><p><strong>Swapping Tokens:</strong></p>
<ul>
<li><p><code>swapAforB(amount)</code>: Users can swap TokenA for TokenB. The contract checks that the user has enough TokenA and the pool has enough TokenB. It then transfers TokenA from the user to the contract and TokenB from the contract to the user.</p>
</li>
<li><p><code>swapBforA(amount)</code>: Users can swap TokenB for TokenA, following the same logic in reverse.</p>
</li>
</ul>
</li>
<li><p><strong>Adding Liquidity:</strong></p>
<ul>
<li><p><code>addTokenALiquidity(amount)</code>: Users can deposit TokenA into the pool, increasing the contract’s TokenA balance.</p>
</li>
<li><p><code>addTokenBLiquidity(amount)</code>: Users can deposit TokenB into the pool, increasing the contract’s TokenB balance.</p>
</li>
</ul>
</li>
<li><p><strong>Security Checks:</strong><br />  Each function checks that the amount is greater than zero and that the sender has enough balance before proceeding.</p>
</li>
</ul>
<h3 id="heading-fuzz-amp-invariant-tests">Fuzz &amp; Invariant Tests 🧪</h3>
<p>Our contracts are in place</p>
<ul>
<li><p>TokenA.sol</p>
</li>
<li><p>TokenB.sol</p>
</li>
<li><p>TokenSwap.sol</p>
</li>
</ul>
<p>They’re completed and ready to be tested inside and out. No point lollygagging anymore, let’s jump straight into out Fuzz and Invariant test code</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755171987720/75abea8b-4a17-48d1-bdf7-742297e85068.gif" alt class="image--center mx-auto" /></p>
<p>TokenSwap.t.sol</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.30;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"forge-std/Test.sol"</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">"../src/TokenSwap.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"../src/TokenA.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"../src/TokenB.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">TokenSwapTest</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    TokenA tokenA;
    TokenB tokenB;
    TokenSwap tokenSwap;

    <span class="hljs-keyword">address</span> user <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0xABCD</span>); <span class="hljs-comment">// User address</span>

    <span class="hljs-comment">/// @dev This function is called before each test</span>
    <span class="hljs-comment">/// @notice This function sets up the token contracts and initial balances</span>
    <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>{
        tokenA <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> TokenA();
        tokenB <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> TokenB();
        tokenSwap <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> TokenSwap(<span class="hljs-keyword">address</span>(tokenA), <span class="hljs-keyword">address</span>(tokenB));

        <span class="hljs-comment">// Mint tokens to user and contract for liquidity</span>
        tokenA.<span class="hljs-built_in">transfer</span>(user, <span class="hljs-number">1000e18</span>);
        tokenB.<span class="hljs-built_in">transfer</span>(user, <span class="hljs-number">1000e18</span>);
        tokenB.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(tokenSwap), <span class="hljs-number">1000e18</span>);
        tokenA.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(tokenSwap), <span class="hljs-number">1000e18</span>);

        <span class="hljs-comment">// Approve the TokenSwap contract to spend user tokens</span>
        vm.startPrank(user);
        tokenA.approve(<span class="hljs-keyword">address</span>(tokenSwap), <span class="hljs-number">1000e18</span>);
        tokenB.approve(<span class="hljs-keyword">address</span>(tokenSwap), <span class="hljs-number">1000e18</span>);
        vm.stopPrank();
    }

    <span class="hljs-comment">/// @dev Fuzz test for addTokenALiquidity</span>
    <span class="hljs-comment">/// @notice This function tests the addTokenALiquidity function with various input amounts</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testFuzz_addTokenALiquidity</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">1</span>, <span class="hljs-number">1000e18</span>); <span class="hljs-comment">// Bound the amount to a valid range</span>

        <span class="hljs-comment">// Start test for addTokenALiquidity</span>
        vm.startPrank(user);
        tokenA.approve(<span class="hljs-keyword">address</span>(tokenSwap), amount);
        tokenSwap.addTokenALiquidity(amount);
        <span class="hljs-comment">// Check liquidity added</span>
        assertEq(tokenA.balanceOf(<span class="hljs-keyword">address</span>(tokenSwap)), <span class="hljs-number">1000e18</span> <span class="hljs-operator">+</span> amount);
        assertEq(tokenA.balanceOf(user), <span class="hljs-number">1000e18</span> <span class="hljs-operator">-</span> amount);
        vm.stopPrank();
    }

    <span class="hljs-comment">/// @dev Fuzz test for addTokenBLiquidity</span>
    <span class="hljs-comment">/// @notice This function tests the addTokenBLiquidity function with various input amounts</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testFuzz_addTokenBLiquidity</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">1</span>, <span class="hljs-number">1000e18</span>);

        <span class="hljs-comment">// Start test for addTokenBLiquidity</span>
        vm.startPrank(user);
        tokenB.approve(<span class="hljs-keyword">address</span>(tokenSwap), amount);
        tokenSwap.addTokenBLiquidity(amount);
        <span class="hljs-comment">// Check liquidity added</span>
        assertEq(tokenB.balanceOf(<span class="hljs-keyword">address</span>(tokenSwap)), <span class="hljs-number">1000e18</span> <span class="hljs-operator">+</span> amount);
        assertEq(tokenB.balanceOf(user), <span class="hljs-number">1000e18</span> <span class="hljs-operator">-</span> amount);
        vm.stopPrank();
    }

    <span class="hljs-comment">/// @dev Fuzz test for swapAforB</span>
    <span class="hljs-comment">/// @notice This function tests the swapAforB function with various input amounts</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testFuzz_swapAforB</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">1</span>, <span class="hljs-number">1000e18</span>);
        vm.assume(tokenA.balanceOf(user) <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> amount); <span class="hljs-comment">// User must have enough TokenA</span>
        vm.assume(tokenB.balanceOf(<span class="hljs-keyword">address</span>(tokenSwap)) <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> amount); <span class="hljs-comment">// TokenSwap must have enough TokenB</span>

        <span class="hljs-comment">// Start test for swapAforB</span>
        vm.startPrank(user);
        tokenA.approve(<span class="hljs-keyword">address</span>(tokenSwap), amount);
        <span class="hljs-keyword">uint256</span> userTokenBBefore <span class="hljs-operator">=</span> tokenB.balanceOf(user);
        <span class="hljs-keyword">uint256</span> contractTokenBBefore <span class="hljs-operator">=</span> tokenB.balanceOf(<span class="hljs-keyword">address</span>(tokenSwap));
        tokenSwap.swapAforB(amount);
        <span class="hljs-comment">// Check balances after swap</span>
        assertEq(tokenA.balanceOf(user), <span class="hljs-number">1000e18</span> <span class="hljs-operator">-</span> amount);
        assertEq(tokenB.balanceOf(user), userTokenBBefore <span class="hljs-operator">+</span> amount);
        assertEq(tokenB.balanceOf(<span class="hljs-keyword">address</span>(tokenSwap)), contractTokenBBefore <span class="hljs-operator">-</span> amount);
        vm.stopPrank();
    }

    <span class="hljs-comment">/// @dev Fuzz test for swapBforA</span>
    <span class="hljs-comment">/// @notice This function tests the swapBforA function with various input amounts</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testFuzz_swapBforA</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">1</span>, <span class="hljs-number">1000e18</span>);
        vm.assume(tokenB.balanceOf(user) <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> amount);
        vm.assume(tokenA.balanceOf(<span class="hljs-keyword">address</span>(tokenSwap)) <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> amount);

        <span class="hljs-comment">// Start test for swapBforA</span>
        vm.startPrank(user);
        tokenB.approve(<span class="hljs-keyword">address</span>(tokenSwap), amount);
        <span class="hljs-keyword">uint256</span> userTokenABefore <span class="hljs-operator">=</span> tokenA.balanceOf(user);
        <span class="hljs-keyword">uint256</span> contractTokenABefore <span class="hljs-operator">=</span> tokenA.balanceOf(<span class="hljs-keyword">address</span>(tokenSwap));
        tokenSwap.swapBforA(amount);
        <span class="hljs-comment">// Check balances after swap</span>
        assertEq(tokenB.balanceOf(user), <span class="hljs-number">1000e18</span> <span class="hljs-operator">-</span> amount);
        assertEq(tokenA.balanceOf(user), userTokenABefore <span class="hljs-operator">+</span> amount);
        assertEq(tokenA.balanceOf(<span class="hljs-keyword">address</span>(tokenSwap)), contractTokenABefore <span class="hljs-operator">-</span> amount);
        vm.stopPrank();
    }

    <span class="hljs-comment">// Invariant: total supply of TokenA and TokenB never changes</span>
    <span class="hljs-comment">/// @notice This function checks that the total supply of both tokens remains constant</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invariant_totalSupplyConstant</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>{
        assertEq(tokenA.totalSupply(), <span class="hljs-number">10000e18</span>);
        assertEq(tokenB.totalSupply(), <span class="hljs-number">10000e18</span>);
    }

    <span class="hljs-comment">// Invariant: sum of TokenA balances between user and contract is always &lt;= 10000e18</span>
    <span class="hljs-comment">/// @notice This function checks that the sum of TokenA balances between user and contract is always &lt;= 10000e18</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invariant_tokenABalanceSum</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>{
        <span class="hljs-keyword">uint256</span> sum <span class="hljs-operator">=</span> tokenA.balanceOf(user) <span class="hljs-operator">+</span> tokenA.balanceOf(<span class="hljs-keyword">address</span>(tokenSwap));
        assertLe(sum, <span class="hljs-number">10000e18</span>);
    }

    <span class="hljs-comment">// Invariant: sum of TokenB balances between user and contract is always &lt;= 10000e18</span>
    <span class="hljs-comment">/// @notice This function checks that the sum of TokenB balances between user and contract is always &lt;= 10000e18</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invariant_tokenBBalanceSum</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>{
        <span class="hljs-keyword">uint256</span> sum <span class="hljs-operator">=</span> tokenB.balanceOf(user) <span class="hljs-operator">+</span> tokenB.balanceOf(<span class="hljs-keyword">address</span>(tokenSwap));
        assertLe(sum, <span class="hljs-number">10000e18</span>);
    }
}
</code></pre>
<p>Here the test contract rigorously checks the behavior of the <code>TokenSwap</code> smart contract. Here’s what it does:</p>
<ul>
<li><p><strong>Setup:</strong><br />  Before each test, it deploys fresh instances of TokenA, TokenB, and TokenSwap. It gives both the user and the TokenSwap contract 1,000 tokens each for liquidity and sets up the necessary approvals.</p>
</li>
<li><p><strong>Fuzz Tests:</strong><br />  These tests use random amounts to check the swap and liquidity functions:</p>
<ul>
<li><p><code>testFuzz_addTokenALiquidity</code> and <code>testFuzz_addTokenBLiquidity</code> verify that users can add liquidity in various amounts and that balances update correctly.</p>
</li>
<li><p><code>testFuzz_swapAforB</code> and <code>testFuzz_swapBforA</code> check that swapping tokens works for any valid amount, ensuring balances change as expected.</p>
</li>
</ul>
</li>
<li><p><strong>Invariant Tests:</strong><br />  These tests ensure that certain properties always hold, no matter what sequence of actions is performed:</p>
<ul>
<li><p><code>invariant_totalSupplyConstant</code> checks that the total supply of both tokens never changes.</p>
</li>
<li><p><code>invariant_tokenABalanceSum</code> and <code>invariant_tokenBBalanceSum</code> ensure that the sum of user and contract balances for each token never exceeds the original supply.</p>
</li>
</ul>
</li>
<li><p><strong>Testing Tools:</strong><br />  The contract uses Foundry’s <code>vm.startPrank</code> to simulate actions from the user’s address and <code>bound</code> to keep fuzzed amounts within a valid range.</p>
</li>
</ul>
<p>ANDDD, let’s test! Use <code>forge</code> to start testing our swap contract</p>
<pre><code class="lang-bash">forge coverage
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755170840139/f83642e7-75aa-4928-9110-6065f8acda4f.png" alt class="image--center mx-auto" /></p>
<p>There we have our test results. BOO YAA!! Amazing work!</p>
<p>We can do this to any type of contract and make sure our contracts are bullet proof. Maybe try writing a token Borrowing contract and hit it with Fuzz and Invariant tests. It’s always great to learn by doing. We now have the power of testing contracts rigorously, and like someone wise once told</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755171282520/7557f825-274a-4e6f-9db4-901558609b83.gif" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-and-thats-a-wrap-for-now">And that's a Wrap! (For now 😬)</h2>
<p>By now we should have</p>
<ul>
<li><p>Our token and swap contracts written</p>
</li>
<li><p>Can add liquidity to our swap contract and swap our tokens</p>
</li>
<li><p>We have written proper &amp; extensive Fuzz and Invariant tests for it</p>
</li>
<li><p>Checked our test coverage and got close to 100%</p>
</li>
</ul>
<h2 id="heading-whats-next">What's Next?</h2>
<p>Part 5, we'll be</p>
<ul>
<li><p>Writing a batch token sender contract</p>
</li>
<li><p>Focusing majorly on getting Gas reports and optimizations</p>
</li>
</ul>
<p><strong>Don't miss it! I'll see you soon!</strong> 😎</p>
]]></content:encoded></item><item><title><![CDATA[Zero to Hero in Foundry - Part 3: Testing Fundamentals]]></title><description><![CDATA[Recap
Welcome back folks! Part 2 took us through some pretty good stuff. We learnt how to use anvil to run a local simulation of a blockchain in our systems. Then we used a simple forge command to deploy our contract to our local chain. And finally u...]]></description><link>https://blog.bitcodex.tech/foundry-part-3</link><guid isPermaLink="true">https://blog.bitcodex.tech/foundry-part-3</guid><category><![CDATA[Web3]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[foundry]]></category><category><![CDATA[Testing]]></category><dc:creator><![CDATA[Abhiram A]]></dc:creator><pubDate>Wed, 13 Aug 2025 12:25:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755087734259/1aeb7bf3-68b1-4081-ae55-69c07c577e4e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<h2 id="heading-recap">Recap</h2>
<p>Welcome back folks! Part 2 took us through some pretty good stuff. We learnt how to use <code>anvil</code> to run a local simulation of a blockchain in our systems. Then we used a simple <code>forge</code> command to deploy our contract to our local chain. And finally used <code>cast</code> to interact with our deployed contract</p>
<p>Please read it if you haven't already before continuing</p>
<p><a target="_blank" href="https://dev.to/abhiramelf/get-from-zero-to-hero-in-foundry-part-2-deploy-interact-2ea4"><strong>Foundry: Zero to Hero - Part 2</strong></a></p>
<p><strong>Learn Web3 through live and interactive challenges at</strong> <a target="_blank" href="https://www.web3compass.xyz/"><strong>Web3Compass</strong></a></p>
</blockquote>
<h2 id="heading-todays-outcome">Today's Outcome</h2>
<p><strong>Topic of focus: Testing fundamentals</strong></p>
<ul>
<li><p><strong>Write a SimpleBank.sol contract</strong></p>
</li>
<li><p><strong>Let users deposit &amp; withdraw funds</strong></p>
</li>
<li><p><strong>Use function modifiers to restrict access to only owner of the deployed contract</strong></p>
</li>
<li><p><strong>And the most important bit - Write extensive tests for it!</strong></p>
</li>
</ul>
<p><strong>SHALL WE?!</strong></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oxi3c61bk3y1mjp3c5ol.gif" alt="Shall we" class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-writing-the-contract">📝 Writing the Contract</h2>
<p>By now we know how to initiatize our project using <code>forge</code>. So, let’s jump right into the contract code</p>
<p>Here’s the <code>SimpleBank.sol</code> contract</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.30;</span>

<span class="hljs-comment">// Simple bank contract that allows users to deposit, withdraw, and check their balances.</span>
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">SimpleBank</span> </span>{
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> owner; <span class="hljs-comment">// Owner of the contract</span>
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> <span class="hljs-keyword">uint256</span>) <span class="hljs-keyword">private</span> balances; <span class="hljs-comment">// Mapping of user addresses to their balances</span>

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _owner</span>) </span>{
        owner <span class="hljs-operator">=</span> _owner;
    }

    <span class="hljs-comment">// Set owner</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setOwner</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> newOwner</span>) <span class="hljs-title">onlyOwner</span> <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        owner <span class="hljs-operator">=</span> newOwner;
    }

    <span class="hljs-comment">// Deposit Ether into the bank</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-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span> <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>, <span class="hljs-string">"Deposit amount must be greater than zero"</span>);
        balances[<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-comment">// Withdraw Ether from the bank, only owner can call</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> amount</span>) <span class="hljs-title">onlyOwner</span> <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(balances[owner] <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> amount, <span class="hljs-string">"Insufficient balance"</span>);
        balances[owner] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> amount;
        <span class="hljs-keyword">payable</span>(owner).<span class="hljs-built_in">transfer</span>(amount);
    }

    <span class="hljs-comment">// Get the balance of the owner</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getBalance</span>(<span class="hljs-params"></span>) <span class="hljs-title">onlyOwner</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> balances[owner];
    }

    <span class="hljs-comment">// Modifier to restrict access to the owner</span>
    <span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">onlyOwner</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> owner, <span class="hljs-string">"Not the contract owner"</span>);
        <span class="hljs-keyword">_</span>;
    }
}
</code></pre>
<h3 id="heading-simplebank-contract-explained"><strong>SimpleBank Contract Explained</strong></h3>
<p>Our contract lets a single owner safely deposit and withdraw Ether. Here’s how it works:</p>
<ul>
<li><p><strong>Owner:</strong> The contract is created with an owner address. Only this owner can withdraw funds or check the balance.</p>
</li>
<li><p><strong>Deposit:</strong> Anyone can deposit Ether by calling the <code>deposit()</code> function. The contract keeps track of how much Ether each address has deposited.</p>
</li>
<li><p><strong>Withdraw:</strong> Only the owner can withdraw Ether using the <code>withdraw()</code> function. The contract checks that the owner has enough balance before allowing the withdrawal.</p>
</li>
<li><p><strong>Check Balance:</strong> The owner can check their balance with the <code>getBalance()</code> function.</p>
</li>
<li><p><strong>Change Owner:</strong> The owner can transfer ownership to another address using <code>setOwner()</code>.</p>
</li>
<li><p><strong>Security:</strong> The <code>onlyOwner</code> modifier makes sure that only the owner can perform sensitive actions like withdrawing or checking the balance.</p>
</li>
</ul>
<h3 id="heading-time-to-test"><strong><em>Time to test!!</em></strong></h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755086285086/cc90b4a2-b463-4cfb-b3d6-090fbcd619ac.gif" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-testing-the-contract">🧪 Testing the Contract</h2>
<p>Let’s extensively test our <code>SimpleBank.sol</code> contract.</p>
<p><code>SimpleBank.t.sol</code> test file</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.30;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Test</span>, <span class="hljs-title">console</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">SimpleBank</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../src/SimpleBank.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">SimpleBankTest</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    SimpleBank <span class="hljs-keyword">public</span> bank;

    <span class="hljs-comment">// Set up the contract</span>
    <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>{
        bank <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> SimpleBank(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
    }

    <span class="hljs-comment">// Test setting a new owner</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testSetOwnerByOwner</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(bank.owner());
        bank.setOwner(<span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>));
        assertEq(bank.owner(), <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>));
        vm.stopPrank();
    }

    <span class="hljs-comment">// Test setting a new owner by non-owner</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testSetOwnerByNonOwner</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.expectRevert(<span class="hljs-string">"Not the contract owner"</span>);
        bank.setOwner(<span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>));
    }

    <span class="hljs-comment">// Test deposit by owner</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testDepositByOwner</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(bank.owner());
        <span class="hljs-keyword">uint256</span> initialBalance <span class="hljs-operator">=</span> bank.getBalance();
        bank.deposit{<span class="hljs-built_in">value</span>: <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>}();
        <span class="hljs-keyword">uint256</span> newBalance <span class="hljs-operator">=</span> bank.getBalance();
        assertEq(newBalance, initialBalance <span class="hljs-operator">+</span> <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
        vm.stopPrank();
    }

    <span class="hljs-comment">// Test deposit by non-owner</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testDepositByNonOwner</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.prank(bank.owner());
        <span class="hljs-keyword">uint256</span> initialBalance <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
        bank.deposit{<span class="hljs-built_in">value</span>: <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>}();
        vm.prank(bank.owner());
        <span class="hljs-keyword">uint256</span> newBalance <span class="hljs-operator">=</span> bank.getBalance();
        assertEq(newBalance, initialBalance <span class="hljs-operator">+</span> <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
    }

    <span class="hljs-comment">// Test withdraw by owner</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testWithdrawByOwner</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(bank.owner());
        bank.deposit{<span class="hljs-built_in">value</span>: <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>}();
        <span class="hljs-keyword">uint256</span> initialBalance <span class="hljs-operator">=</span> bank.getBalance();
        bank.withdraw(<span class="hljs-number">0</span><span class="hljs-number">.5</span> <span class="hljs-literal">ether</span>);
        <span class="hljs-keyword">uint256</span> newBalance <span class="hljs-operator">=</span> bank.getBalance();
        assertEq(newBalance, initialBalance <span class="hljs-operator">-</span> <span class="hljs-number">0</span><span class="hljs-number">.5</span> <span class="hljs-literal">ether</span>);
        vm.stopPrank();
    }

    <span class="hljs-comment">// Test withdraw with insufficient balance</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testWithdrawNoBalance</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(bank.owner());
        bank.deposit{<span class="hljs-built_in">value</span>: <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>}();
        vm.expectRevert(<span class="hljs-string">"Insufficient balance"</span>);
        bank.withdraw(<span class="hljs-number">2</span> <span class="hljs-literal">ether</span>);
        vm.stopPrank();
    }

    <span class="hljs-comment">// Test withdraw by non-owner</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testWithdrawByNonOwner</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        bank.deposit{<span class="hljs-built_in">value</span>: <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>}();
        vm.expectRevert(<span class="hljs-string">"Not the contract owner"</span>);
        bank.withdraw(<span class="hljs-number">0</span><span class="hljs-number">.5</span> <span class="hljs-literal">ether</span>);
    }

    <span class="hljs-comment">// Test get balance by owner</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testGetBalanceByOwner</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(bank.owner());
        bank.deposit{<span class="hljs-built_in">value</span>: <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>}();
        <span class="hljs-keyword">uint256</span> balance <span class="hljs-operator">=</span> bank.getBalance();
        assertEq(balance, <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>);
        vm.stopPrank();
    }

    <span class="hljs-comment">// Test get balance by non-owner</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testGetBalanceByNonOwner</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.expectRevert(<span class="hljs-string">"Not the contract owner"</span>);
        bank.getBalance();
    }

    <span class="hljs-comment">// Fuzz test for withdraw</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testFuzz_Withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint8</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        vm.startPrank(bank.owner());
        bank.deposit{<span class="hljs-built_in">value</span>: <span class="hljs-number">10000</span> <span class="hljs-literal">ether</span>}();
        <span class="hljs-keyword">uint256</span> initialBalance <span class="hljs-operator">=</span> bank.getBalance();
        bank.withdraw(amount);
        <span class="hljs-keyword">uint256</span> newBalance <span class="hljs-operator">=</span> bank.getBalance();
        assertEq(newBalance, initialBalance <span class="hljs-operator">-</span> amount);
        vm.stopPrank();
    }
}
</code></pre>
<h3 id="heading-simplebanktest-explained"><strong>SimpleBankTest Explained</strong></h3>
<p>Our test contract checks that the <code>SimpleBank.sol</code> smart contract works as expected. Here’s what each part does:</p>
<ul>
<li><p><strong>Setup:</strong> Before each test, a new bank is created with the test contract as the owner.</p>
</li>
<li><p><strong>Owner Functions:</strong> Tests that only the owner can change ownership, withdraw funds, and check the balance. If anyone else tries, the contract should revert with an error.</p>
</li>
<li><p><strong>Deposits:</strong> Checks that both the owner and other users can deposit Ether, and that the balance updates correctly.</p>
</li>
<li><p><strong>Withdrawals:</strong> Verifies that the owner can withdraw funds, but not more than their balance. Also checks that non-owners cannot withdraw.</p>
</li>
<li><p><strong>Balance Checks:</strong> Ensures only the owner can see their balance.</p>
</li>
<li><p><strong>Fuzz Testing:</strong> Randomly tests withdrawals with different amounts to make sure the contract handles all cases safely.</p>
</li>
</ul>
<p>Now, instead of running the <code>forge test</code> command, let’s try something else. Try running</p>
<pre><code class="lang-bash">forge coverage
</code></pre>
<p>We’ll see that the tests all run correctly, but we also see a coverage report of the entire test suite</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755087178371/06b1551b-3864-4c8a-9e31-3f3b936f025b.png" alt class="image--center mx-auto" /></p>
<p>Coverage measures how much of our smart contract code is actually tested by our test suite. It shows which lines of code were executed during testing and which lines were missed.</p>
<ul>
<li><p><strong>Why is it useful?</strong><br />  High coverage means our tests are checking most parts of your contract, making it less likely that bugs or vulnerabilities go unnoticed.</p>
</li>
<li><p><strong>How do we check coverage?</strong><br />  In Foundry, we can run <code>forge coverage</code> to see a report. This report highlights which functions and lines were tested and gives us a percentage score.</p>
</li>
<li><p><strong>What should we aim for?</strong><br />  Aim for as close to 100% coverage as possible, but remember: coverage alone doesn’t guarantee your contract is bug-free. Good tests and thoughtful scenarios are just as important.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755086452913/14a2fc06-d5fe-419f-b3ed-61f6684d124e.gif" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-and-thats-a-wrap-for-now">And that's a Wrap! (For now 😬)</h2>
<p>By now we should have</p>
<ul>
<li><p>Our SimpleBank.sol contract written</p>
</li>
<li><p>We have written proper &amp; extensive tests for it</p>
</li>
<li><p>Checked our test coverage and got close to 100%</p>
</li>
</ul>
<h2 id="heading-whats-next">What's Next?</h2>
<p>Part 4, we'll be</p>
<ul>
<li><p>Writing a 1:1 token swap contract</p>
</li>
<li><p>Focusing majorly on writing Fuzz and Invariant tests for it</p>
</li>
</ul>
<p><strong>Don't miss it! I'll see you soon!</strong> 😎</p>
]]></content:encoded></item><item><title><![CDATA[Zero to Hero in Foundry - Part 2: Deploy & Interact]]></title><description><![CDATA[Recap
In the last part, we saw how to setup Foundry in our systems. We created a simple Counter contract project, compiled it and tested it all using the forge command
Please read it if you haven't already before continuing
Foundry: Zero to Hero - Pa...]]></description><link>https://blog.bitcodex.tech/foundry-part-2</link><guid isPermaLink="true">https://blog.bitcodex.tech/foundry-part-2</guid><category><![CDATA[Web3]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[foundry]]></category><dc:creator><![CDATA[Abhiram A]]></dc:creator><pubDate>Wed, 13 Aug 2025 11:02:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755082776840/265d3dd0-df0d-4122-b4c1-ad9daeccbd6e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<h2 id="heading-recap">Recap</h2>
<p>In the last part, we saw how to setup Foundry in our systems. We created a simple Counter contract project, compiled it and tested it all using the <code>forge</code> command</p>
<p>Please read it if you haven't already before continuing</p>
<p><a target="_blank" href="https://dev.to/abhiramelf/get-from-zero-to-hero-in-foundry-part-1-getting-started-57h8"><strong>Foundry: Zero to Hero - Part 1</strong></a></p>
<p><strong>Learn Web3 through live and interactive challenges at</strong> <a target="_blank" href="https://www.web3compass.xyz/"><strong>Web3Compass</strong></a></p>
</blockquote>
<h2 id="heading-todays-outcome">Today's Outcome</h2>
<p><strong>Tool of focus:</strong> <code>anvil</code> &amp; <code>cast</code></p>
<ul>
<li><p><strong>Write a Greeting.sol contract</strong></p>
</li>
<li><p><strong>Compile &amp; Test it using</strong> <code>forge</code></p>
</li>
<li><p><strong>Simulate a local blockchain using</strong> <code>anvil</code></p>
</li>
<li><p><strong>Deploy to the local blockchain using</strong> <code>forge</code></p>
</li>
<li><p><strong>Interact with the deployed contract using</strong> <code>cast</code></p>
</li>
</ul>
<p><strong><em>Alright, let's dive in!</em></strong></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cxqh15wnltpn9ypgxhxu.gif" alt="Dive In" /></p>
<hr />
<h2 id="heading-building-the-contract">🛠️ Building the Contract</h2>
<p>In a new folder let's use the <code>forge</code> command to initialize our Greeting contract project</p>
<pre><code class="lang-bash">forge init Greeting
</code></pre>
<p>We'll now have a folder named <code>Greeting</code> with all the necessary project files.</p>
<h3 id="heading-writing-the-contract">📝 Writing the Contract</h3>
<p>Inside the <code>src</code> folder we'll see that the contract file is corresponding to the <code>Counter</code> contract. This will always be the case when we initialize a new project using <code>forge</code>.</p>
<p>Let's go ahead and change it to our Greeting contract</p>
<p><a target="_blank" href="https://github.com/abhiramelf/15-days-of-foundry/blob/main/day-2/Greeting/src/Greeting.sol"><strong>Greeting.sol Contract</strong></a></p>
<p>The contract let's us set a state variable to whatever greeting message we desire and retrieve the stored message through a function call</p>
<h3 id="heading-compile-amp-test-the-contract">🧪 Compile &amp; Test the Contract</h3>
<p>Let's do the same with <code>test</code> folder</p>
<p><a target="_blank" href="https://github.com/abhiramelf/15-days-of-foundry/blob/main/day-2/Greeting/test/Greeting.t.sol"><strong>Greeting.t.sol Test File</strong></a></p>
<p><strong>To compile and test the contract, let's go ahead and run the command</strong></p>
<pre><code class="lang-bash">forge <span class="hljs-built_in">test</span>
</code></pre>
<p><strong><em>Our Greeting.sol contract is now compiled and tested, ready to be Deployed!</em></strong></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s3p7di1ikrfld0uy6cwp.gif" alt="Let's Do This" /></p>
<hr />
<h2 id="heading-deploying-the-contract">🚀 Deploying the Contract</h2>
<p>Deploying our contract is a two step process</p>
<ul>
<li><p>Use <code>anvil</code> to simulate a local blockchain</p>
</li>
<li><p>Deploy to it using an account <code>anvil</code> provides</p>
</li>
</ul>
<h3 id="heading-spinning-up-a-local-blockchain">Spinning up a local blockchain</h3>
<p>All we have to do here is spin up a new instance of terminal and run the command</p>
<pre><code class="lang-bash">anvil
</code></pre>
<p>Now, we have a local blockchain running in our system with sample wallets to do transactions from</p>
<pre><code class="lang-bash"> anvil


                             _   _
                            (_) | |
      __ _   _ __   __   __  _  | |
     / _` | | <span class="hljs-string">'_ \  \ \ / / | | | |
    | (_| | | | | |  \ V /  | | | |
     \__,_| |_| |_|   \_/   |_| |_|

    1.2.3-stable (a813a2cee7 2025-06-08T15:42:40.147013149Z)
    https://github.com/foundry-rs/foundry

Available Accounts
==================

(0) 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000.000000000000000000 ETH)
(1) 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000.000000000000000000 ETH)
(2) 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC (10000.000000000000000000 ETH)
(3) 0x90F79bf6EB2c4f870365E785982E1f101E93b906 (10000.000000000000000000 ETH)
(4) 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 (10000.000000000000000000 ETH)
(5) 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc (10000.000000000000000000 ETH)
(6) 0x976EA74026E726554dB657fA54763abd0C3a0aa9 (10000.000000000000000000 ETH)
(7) 0x14dC79964da2C08b23698B3D3cc7Ca32193d9955 (10000.000000000000000000 ETH)
(8) 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f (10000.000000000000000000 ETH)
(9) 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 (10000.000000000000000000 ETH)

Private Keys
==================

(0) 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
(1) 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
(2) 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
(3) 0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
(4) 0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a
(5) 0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba
(6) 0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e
(7) 0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356
(8) 0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97
(9) 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6

Wallet
==================
Mnemonic:          test test test test test test test test test test test junk
Derivation path:   m/44'</span>/60<span class="hljs-string">'/0'</span>/0/


Chain ID
==================

31337

Base Fee
==================

1000000000

Gas Limit
==================

30000000

Genesis Timestamp
==================

1754829094

Genesis Number
==================

0

Listening on 127.0.0.1:8545
</code></pre>
<h3 id="heading-lets-deploy">Let's Deploy</h3>
<p>With our local blockchain running and providing us dummy wallets with test ETH, only think left to do is deploy out contract to it</p>
<p>Each deployment is considered as a transaction on the blockchain. And for a transaction to happen it needs to be initiated from an address in the blockchain. Hence, we need to use a wallet's <strong>Private Key</strong> provided by <code>anvil</code> in the previous step</p>
<p>Cool, let's deploy</p>
<pre><code class="lang-bash">forge create Greeting --interactive --broadcast
</code></pre>
<ul>
<li><p><code>forge create</code> - initiates the deployment</p>
</li>
<li><p><code>--interactive</code> - opens interactive mode to enter wallet private key</p>
</li>
<li><p><code>--broadcast</code> - broadcasts the deployment transaction across the chain</p>
</li>
</ul>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cg4dt96pf34nd5flndfi.png" alt="Deployed" /></p>
<p>Okay, we have the address of the wallet from which the contract was deployed, the deployed contract address and the associated transaction hash!</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7lk0y0d0ruitl723xfe3.gif" alt="Enter deployed" /></p>
<p><strong><em>AANDDD... DEPLOYED!!!</em></strong></p>
<hr />
<h2 id="heading-interacting-with-the-contract">💻 Interacting with the Contract</h2>
<p>Awesome! Great getting this far! :clap:</p>
<p>When it comes to interacting with our deployed contract, <code>cast</code> is our friend</p>
<h3 id="heading-set-our-greeting-message">Set our Greeting Message</h3>
<p>Let's see the <code>cast</code> command to do this</p>
<pre><code class="lang-bash">cast send &lt;Deployed Contract Address&gt; <span class="hljs-string">"setGreeting(string)"</span> <span class="hljs-string">"Hello from cast!"</span> --private-key &lt;wallet private key&gt; --rpc-url http://127.0.0.1:8545/
</code></pre>
<p>Let's break it down</p>
<ul>
<li><p><code>cast send</code> - send a transaction</p>
</li>
<li><p>- in my case it's 0x5FbDB2315678afecb367f032d93F642f64180aa3</p>
</li>
<li><p>The name of the function to call along with any return types and arguments it has</p>
</li>
<li><p><code>--private-key</code> - follow this option with a wallet private key</p>
</li>
<li><p><code>--rpc-url</code> - <a target="_blank" href="http://127.0.0.1:8545/">http://127.0.0.1:8545/</a>, the default url to which <code>anvil</code> deploys</p>
</li>
</ul>
<p>Your <code>greeting</code> state variable should now be updated with the value "Hello from cast!". But, let's see for ourselves and not just assume</p>
<h3 id="heading-get-our-greeting-message">Get our Greeting Message</h3>
<p>The function to get the greeting message is a <code>view</code> only function. This means we are only reading from the state variable and doesn't need to initiate a transaction to do this</p>
<p>Here's the <code>cast</code> command</p>
<pre><code class="lang-bash">cast call &lt;Deployed Contract Address&gt; <span class="hljs-string">"goGreet() returns (string)"</span> -- --abi-decode
</code></pre>
<p>Let's break it down</p>
<ul>
<li><p><code>cast call</code> - calls a function without broadcasting as a transaction</p>
</li>
<li><p>- in my case it's 0x5FbDB2315678afecb367f032d93F642f64180aa3</p>
</li>
<li><p>The name of the function to call along with any return types and arguments it has</p>
</li>
<li><p><code>-- --abi-decode</code> - (IMPORTANT)The response we get while calling the function will be in hex. This flag helps decode it to a readable string</p>
</li>
</ul>
<blockquote>
<p>Read more about <a target="_blank" href="https://docs.soliditylang.org/en/develop/abi-spec.html">ABIs here</a></p>
</blockquote>
<p>This should return "Hello from cast!".</p>
<p><strong>If that's the case, well my friend, CONGRATULATIONS are in order!!</strong> 🎉 🎊</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mg0covpus3dj7cy7ut0d.gif" alt="Well Done" /></p>
<hr />
<h2 id="heading-and-thats-a-wrap-for-now">And that's a Wrap! (For now 😬)</h2>
<p>By now we should have</p>
<ul>
<li><p>Our Greeting.sol contract compiled, tested</p>
</li>
<li><p><code>anvil</code> running a local blockchain</p>
</li>
<li><p>Greeting.sol deployed to our local blockchain</p>
</li>
<li><p>Used <code>cast</code> to interact with the deployed contract</p>
</li>
</ul>
<h2 id="heading-whats-next">What's Next?</h2>
<p>Part 3, we'll be</p>
<ul>
<li><p>Writing a simple bank contract</p>
</li>
<li><p>Focusing majorly on writing proper tests for the logic while handling all edge cases</p>
</li>
</ul>
<p><strong>Don't miss it! I'll see you soon!</strong> 😎</p>
]]></content:encoded></item><item><title><![CDATA[Zero To Hero in Foundry - Part 1: Getting Started]]></title><description><![CDATA[Let's Begin
Get ready to sweat it out in style while you Forge some piping hot Smart Contracts with molten, smoldering Solidity inside blockchain Foundry
This article is the first of many in which we'll dive deep into building, testing and deploying ...]]></description><link>https://blog.bitcodex.tech/foundry-part-1</link><guid isPermaLink="true">https://blog.bitcodex.tech/foundry-part-1</guid><category><![CDATA[Web3]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Abhiram A]]></dc:creator><pubDate>Tue, 12 Aug 2025 16:14:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755015059988/a6c1cd56-176f-4093-90ef-ff0f67b11c98.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-lets-begin">Let's Begin</h2>
<p>Get ready to sweat it out in style while you Forge some piping hot Smart Contracts with molten, smoldering Solidity inside blockchain Foundry</p>
<p>This article is the first of many in which we'll dive deep into building, testing and deploying Smart Contracts using Foundry. We'll also see how to use Foundry to interact with the deployed Smart Contracts.</p>
<p>One article per day and by the end of the series you would be able to use Foundry like a PRO</p>
<p>LFG! 🚀</p>
<hr />
<h2 id="heading-todays-outcome">Today's Outcome</h2>
<ul>
<li><p><strong>What's Foundry?</strong></p>
</li>
<li><p><strong>Install Foundry on our systems</strong></p>
</li>
<li><p><strong>Build and Test a basic Counter contract</strong></p>
</li>
</ul>
<hr />
<h2 id="heading-foundry-what">Foundry, What!? 😯</h2>
<p>Imagine a beautiful Saturday night - you have a wondrous steak simmering on your pan, ready and staring back at you. What do you do next? Do you just leave it there like that? Do you just walk away? That would be ludicrous!</p>
<blockquote>
<h3 id="heading-steaks-me-like-spice-boo">Steaks, Me like - Spice Boo</h3>
</blockquote>
<p>You'll get the steak out and slice it, taste test it(I know you're drooling!), transfer the slices to a plate and use cutlery to consume that beauty!</p>
<p>Congratulations! You've become the Foundry of Steaks!</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ii0tlgg1c2qx6lh09gcy.gif" alt="Salt Bae Meme" /></p>
<h3 id="heading-enter-foundry">Enter Foundry</h3>
<p>OOOKAYY!! - this is exactly what Foundry does for your Smart Contracts</p>
<ul>
<li><p>You have your Smart Contract simmering in your IDE</p>
</li>
<li><p>Foundry lets you taste "Test" it</p>
</li>
<li><p>Helps you "Deploy" it to your blockchain plate, and</p>
</li>
<li><p>Finally let's you "Interact" with the plated Smart Contract</p>
</li>
</ul>
<p>BOOM! Everything you need in ONE SINGLE PACKAGE! And everything written in pure Solidity, no JS/TS!</p>
<p>Enough about steaks, let's start forging</p>
<hr />
<h2 id="heading-foundry-how">Foundry, How? 🔨</h2>
<p>Let's look at how we can setup Foundry in our systems. But, before we do that let's make sure our systems are prepped for installing Foundry</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a76gu3v7e2n9kw40pnqs.gif" alt="Tell me how" /></p>
<h3 id="heading-prerequisite">📦 Prerequisite</h3>
<p><strong>Rust compiler and Cargo</strong> - The easiest way to install them is to go to <a target="_blank" href="http://rustup.rs"><strong>rustup.rs</strong></a> and follow the steps.</p>
<blockquote>
<h4 id="heading-on-windows"><em>On Windows:</em></h4>
<p><em>Make sure you have a recent version of</em> <a target="_blank" href="https://visualstudio.microsoft.com/downloads/"><strong><em>Visual Studio</em></strong></a> <em>with the</em> <strong><em>"Desktop Development with C++"</em></strong> <em>workload installed.</em></p>
</blockquote>
<h3 id="heading-install-foundry">🛠️ Install Foundry</h3>
<p><strong>Foundryup</strong> is the official installer for the Foundry toolchain. 📘 Learn more <a target="_blank" href="https://github.com/foundry-rs/foundry/blob/master/foundryup/README.md">here</a></p>
<ul>
<li>Install Foundryup</li>
</ul>
<pre><code class="lang-bash">
curl -L https://foundry.paradigm.xyz | bash
</code></pre>
<p>This will install the <code>foundryup</code> command in your CLI</p>
<ul>
<li>Follow this up with running the command</li>
</ul>
<pre><code class="lang-bash">foundryup
</code></pre>
<p>This will automatically install the latest stable versions of <code>forge</code>, <code>cast</code>, <code>anvil</code> and <code>chisel</code>. Each of these have it's own role to play. We'll get to each of them as we progress in the series</p>
<h3 id="heading-start-cooking">👨‍🍳 Start Cooking</h3>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xgnj2rk1ugr8l8ze2g5a.gif" alt="Cooking" /></p>
<p>For today's lesson, we'll be looking into how <code>forge</code> helps us build and test Smart Contracts</p>
<p>Let's create a folder named <code>foundry-learn</code> and move into that. Add a simple Counter contract using a <code>forge</code> command</p>
<pre><code class="lang-bash">forge init Counter
</code></pre>
<p>This will create a new <code>Counter</code> folder within your project folder. Once you move inside this folder you'll see a whole bunch of stuff, but let's just focus on 3 folder for now</p>
<ul>
<li><p><code>lib</code> - Holds <code>forge-std</code> library, which helps us write Tests and Deploy scripts using pure Solidity</p>
</li>
<li><p><code>src</code> - Holds all your <code>.sol</code> Smart Contract files</p>
</li>
<li><p><code>test</code> - Holds all your <code>.t.sol</code> test files</p>
</li>
</ul>
<p>We have our sample contract and test file in their respective folders. Let's look at how easy it's to test and build our contract</p>
<ul>
<li>Here's our <code>Counter.sol</code> code in <code>src</code> folder</li>
</ul>
<pre><code class="lang-Solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.13;</span>

<span class="hljs-comment">// This is a simple counter contract that allows you to set a number,</span>
<span class="hljs-comment">// increment it, and decrement it, with a check to prevent negative values.</span>
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Counter</span> </span>{
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> number; <span class="hljs-comment">// The current value of the counter</span>

    <span class="hljs-comment">// Sets the initial value of the counter</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setNumber</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> newNumber</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        number <span class="hljs-operator">=</span> newNumber;
    }

    <span class="hljs-comment">// Increments the counter by 1</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">increment</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        number<span class="hljs-operator">+</span><span class="hljs-operator">+</span>;
    }
}
</code></pre>
<p>The simple contract lets us write a number to a state variable and increment it by 1 by calling a public function</p>
<ul>
<li>And here's the <code>Counter.t.sol</code> test file in the <code>test</code> folder</li>
</ul>
<pre><code class="lang-Solidity"><span class="hljs-comment">// SPDX-License-Identifier: UNLICENSED</span>
<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">console</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">Counter</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../src/Counter.sol"</span>;

<span class="hljs-comment">// This is a test contract for the Counter contract.</span>
<span class="hljs-comment">// It includes tests for incrementing, decrementing, and setting the counter value.</span>
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">CounterTest</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    Counter <span class="hljs-keyword">public</span> counter; <span class="hljs-comment">// Instance of the Counter contract</span>

    <span class="hljs-comment">// Sets up the test environment before each test</span>
    <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>{
        counter <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Counter();
        counter.setNumber(<span class="hljs-number">0</span>);
    }

    <span class="hljs-comment">// Tests if the counter increments correctly</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_Increment</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        counter.increment();
        assertEq(counter.number(), <span class="hljs-number">1</span>);
    }

    <span class="hljs-comment">// Fuzz test for setting the counter value</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testFuzz_SetNumber</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> x</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        counter.setNumber(x);
        assertEq(counter.number(), x);
    }
}
</code></pre>
<p>This tests if the state variable is being set properly and incrementing is working on function call</p>
<hr />
<h3 id="heading-build">🏗️ Build</h3>
<p>It's time to compile/build our Contract. To do that, let's run this command while in the <code>Counter</code> folder</p>
<pre><code class="lang-bash">forge build
</code></pre>
<p>Your Contract will now be compiled using the latest Solidity compiler</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3k5o9swif26xkvgkps6o.png" alt="Successful Compiler Output" /></p>
<h3 id="heading-test">🧪 Test</h3>
<p>To Test the Contract all you have to do is run this command while in the <code>Counter</code> folder</p>
<pre><code class="lang-bash">forge <span class="hljs-built_in">test</span>
</code></pre>
<p>If everything is setup properly, you'll see that the tests passing successfully</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xhv3bsavdu0gve57vpth.png" alt="Successful Test Output" /></p>
<hr />
<h2 id="heading-and-thats-a-wrap-for-now">And that's a Wrap! (For now 😬)</h2>
<p>By now you should have</p>
<ul>
<li><p>Foundry and all it's tools installed in your system.</p>
</li>
<li><p>Created Counter project using <code>forge</code></p>
</li>
<li><p>Understood important folder structure of generated project</p>
</li>
<li><p>Built and Tested the simple Counter contract using <code>forge</code></p>
</li>
</ul>
<h2 id="heading-whats-next">What's Next?</h2>
<p>Part 2, we'll dig into</p>
<ul>
<li><p>Deploying your tested Contracts</p>
</li>
<li><p>Interacting with deployed Contract using terminal commands</p>
</li>
</ul>
<p>Don't miss it! I'll see you soon! 😎</p>
]]></content:encoded></item></channel></rss>