<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: tiancaijb</title>
    <description>The latest articles on DEV Community by tiancaijb (@tiancaijb_3df5e4436e5a5fc).</description>
    <link>https://dev.to/tiancaijb_3df5e4436e5a5fc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4042873%2F2fbe66ba-9491-4e93-94a0-3d502cd58760.png</url>
      <title>DEV Community: tiancaijb</title>
      <link>https://dev.to/tiancaijb_3df5e4436e5a5fc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tiancaijb_3df5e4436e5a5fc"/>
    <language>en</language>
    <item>
      <title>I built 43 sites and made $0. Now I'm trying the opposite approach.</title>
      <dc:creator>tiancaijb</dc:creator>
      <pubDate>Thu, 23 Jul 2026 03:31:47 +0000</pubDate>
      <link>https://dev.to/tiancaijb_3df5e4436e5a5fc/i-built-43-sites-and-made-0-now-im-trying-the-opposite-approach-1g0l</link>
      <guid>https://dev.to/tiancaijb_3df5e4436e5a5fc/i-built-43-sites-and-made-0-now-im-trying-the-opposite-approach-1g0l</guid>
      <description>&lt;h1&gt;
  
  
  I built 25 websites to make $1 each. Here's what happened.
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; 25 single-page cheatsheets. $1 each in crypto. $0 revenue. 15 open PRs. Dev.to drives more traffic than Google. Here's the real math.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The idea
&lt;/h2&gt;

&lt;p&gt;Six weeks ago I had a simple theory:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build single-page cheatsheets for developer tools. Charge $1 in crypto. Let SEO do the work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The pitch writes itself: "I could Google this for free, but $1 saves me 20 minutes of searching."&lt;/p&gt;

&lt;p&gt;It's the classic convenience play. Gumroad proved people pay for well-packaged free information. I just wanted to test if the same works for dev tools, paid in Solana/USDC.&lt;/p&gt;

&lt;p&gt;No subscription. No account. No newsletter upsell. One page, one dollar, one payment.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What I built
&lt;/h2&gt;

&lt;p&gt;I picked 25 topics across a few categories and built a single-page HTML site for each one:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crypto dev tools:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solana CLI cheatsheet&lt;/li&gt;
&lt;li&gt;Anchor framework quick reference&lt;/li&gt;
&lt;li&gt;Rust CLI essentials&lt;/li&gt;
&lt;li&gt;Web3.py snippets&lt;/li&gt;
&lt;li&gt;Hardhat command reference&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;General dev:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bash one-liners (the ones you actually use)&lt;/li&gt;
&lt;li&gt;tmux survival guide&lt;/li&gt;
&lt;li&gt;Git undo commands (the most common page)&lt;/li&gt;
&lt;li&gt;sed/awk patterns&lt;/li&gt;
&lt;li&gt;jq JSON query reference&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI coding agents:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code quick commands&lt;/li&gt;
&lt;li&gt;Cursor shortcuts&lt;/li&gt;
&lt;li&gt;Aider workflow reference&lt;/li&gt;
&lt;li&gt;Prompt patterns for codegen&lt;/li&gt;
&lt;li&gt;Vibe coding anti-patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exchange comparisons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Binance vs Bybit fee tier comparison&lt;/li&gt;
&lt;li&gt;Solana DEX aggregator fees&lt;/li&gt;
&lt;li&gt;CEX withdrawal fee reference&lt;/li&gt;
&lt;li&gt;Perpetual exchange comparison matrix&lt;/li&gt;
&lt;li&gt;KYC-free exchange roundup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every site is a single &lt;code&gt;index.html&lt;/code&gt; — no framework, no build step, no JavaScript bloat. Just a clean reference, a buy button (Solana Pay / USDC address), and a permissive license so people can fork it.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sites live&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total build time&lt;/td&gt;
&lt;td&gt;~40 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open PRs to Awesome lists&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly visitors (all sites)&lt;/td&gt;
&lt;td&gt;~320&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Revenue&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That zero stings. I'll be honest — I expected at least a couple of sales by now, even if it was friends throwing a dollar to be nice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Costs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Domains: ~$90/yr (25 × $3.99 .xyz domains)&lt;/li&gt;
&lt;li&gt;Hosting: $0 (all static, all on Vercel free tier)&lt;/li&gt;
&lt;li&gt;Total sunk: ~$90&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. What worked
&lt;/h2&gt;

&lt;p&gt;Three things actually moved the needle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dev.to articles.&lt;/strong&gt; My post about the project itself got more views in 48 hours than all 25 sites combined got in a month. Devs are on dev.to. They search dev.to. Write there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Great READMEs.&lt;/strong&gt; The GitHub repos with solid READMEs and screenshots get starred more than the sites get visited. Devs window-shop on GitHub, they don't land on raw HTML pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Niche specificity.&lt;/strong&gt; The most visited page is "Git undo commands" — a topic so specific people search it exactly. Broad topics get buried. Narrow pages get found.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. What didn't
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Awesome list PRs are glacial.&lt;/strong&gt; 15 open PRs, zero merged. Many have sat for 3+ weeks. The maintainers are volunteers and that's fine, but if your growth strategy depends on landing in &lt;code&gt;awesome-whatever&lt;/code&gt;, you need a different timeline expectation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crypto-only payment is a filter.&lt;/strong&gt; Even if someone wants the cheatsheet, sending $1 in Solana requires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Having an exchange account with SOL in it&lt;/li&gt;
&lt;li&gt;Not minding the $0.01+ transaction fee being 1% of the purchase&lt;/li&gt;
&lt;li&gt;Bothering to do it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's three barriers before the first page load. A $1 Stripe link would convert better, but Stripe isn't available in my region.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SEO takes months.&lt;/strong&gt; I knew this going in, but the reality still hits. 25 sites, 6 weeks, zero organic rankings for anything competitive. The long-tail queries get a trickle, but trickles don't make rent.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The real insight
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;People pay for convenience, not information. &lt;strong&gt;But crypto convenience isn't here yet.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The cheatsheet model isn't broken. Gumroad proved it works — for $3-5 PDFs, with Apple Pay. The friction of sending $1 in SOL for a single page is simply higher than the value it returns.&lt;/p&gt;

&lt;p&gt;What I'd do differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One domain, 25 routes.&lt;/strong&gt; Don't buy 25 domains. Use &lt;code&gt;cheatsheets.example.com/git-undo&lt;/code&gt;. Save $90/yr.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PDF + Gumroad.&lt;/strong&gt; Let Gumroad handle payment friction. They support crypto too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B2B over B2D.&lt;/strong&gt; Devs are cheap. Sell the package of 25 cheatsheets to teams for $20.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus on one niche.&lt;/strong&gt; 25 topics in 5 categories is too spread. Pick one (AI coding agents, crypto dev tools) and own it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Side hustles for devs
&lt;/h2&gt;

&lt;p&gt;This experiment is part of a bigger collection I'm building — a handbook of developer side hustles that actually work (and the ones that don't). Each chapter is one experiment, one landing page, one set of real numbers.&lt;/p&gt;

&lt;p&gt;I'm documenting everything transparently because most "side hustle" content is survivorship bias wrapped in an affiliate link. I want the real numbers — success, failure, and the 90% that sits in the messy middle.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📖 The side hustle handbook (all experiments): &lt;a href="https://side-hustle-handbook.vercel.app" rel="noopener noreferrer"&gt;side-hustle-handbook.vercel.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐙 GitHub repo (code + experiments): &lt;a href="https://github.com/tiancaijb366-pixel/awesome-developer-side-hustles" rel="noopener noreferrer"&gt;github.com/tiancaijb366-pixel/awesome-developer-side-hustles&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;If you're building something similar or have ideas to test, DM me. I'm always down to trade real numbers for real feedback.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>startup</category>
      <category>sidehustle</category>
      <category>entrepreneur</category>
      <category>indiemakers</category>
    </item>
    <item>
      <title>Foundry Fuzz &amp; Invariant Testing: A Practical Cookbook</title>
      <dc:creator>tiancaijb</dc:creator>
      <pubDate>Thu, 23 Jul 2026 02:03:08 +0000</pubDate>
      <link>https://dev.to/tiancaijb_3df5e4436e5a5fc/foundry-fuzz-invariant-testing-a-practical-cookbook-4oof</link>
      <guid>https://dev.to/tiancaijb_3df5e4436e5a5fc/foundry-fuzz-invariant-testing-a-practical-cookbook-4oof</guid>
      <description>&lt;p&gt;Most hacks happen in edge cases the dev never considered. Fuzz testing throws random inputs at your contracts until something breaks — and Foundry makes it fast enough that you run it before every commit. Here are the patterns I actually use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;forge &lt;span class="nb"&gt;install &lt;/span&gt;tiancaijb366-pixel/foundry-security-tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The companion repo has all these examples runnable. Fork it, run &lt;code&gt;forge test&lt;/code&gt;, then rip out the patterns for your own contracts.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Basic Fuzzing with &lt;code&gt;bound&lt;/code&gt; / &lt;code&gt;vm.assume&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Don't write ten &lt;code&gt;test_RevertIf_*&lt;/code&gt; functions. Give the fuzzer a range and let it find the off-by-one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";
import {Token} from "../src/Token.sol";

contract TokenFuzzTest is Test {
    Token t;

    function setUp() public {
        t = new Token(1_000_000e18);
    }

    function testFuzz_Transfer_Bounds(address sender, address to, uint256 amount) public {
        // `bound` keeps inputs practical
        amount = bound(amount, 1, t.balanceOf(sender));

        // `vm.assume` filters — use it sparingly (slows the fuzzer)
        vm.assume(sender != address(0) &amp;amp;&amp;amp; to != address(0) &amp;amp;&amp;amp; sender != to);

        vm.prank(sender);
        t.transfer(to, amount);

        assertGe(t.balanceOf(sender), 0);
        assertEq(t.totalSupply(), 1_000_000e18);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key point:&lt;/strong&gt; &lt;code&gt;bound&lt;/code&gt; is faster than &lt;code&gt;vm.assume&lt;/code&gt; — it narrows the range instead of discarding inputs. Use &lt;code&gt;assume&lt;/code&gt; only for relationships the fuzzer can't infer (e.g., &lt;code&gt;sender != to&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Invariant Testing: ERC20 &lt;code&gt;totalSupply&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Invariant tests check a property holds across random sequences of calls. They catch state-machine bugs that single-function fuzzing misses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";
import {StdInvariant} from "forge-std/StdInvariant.sol";
import {Token} from "../src/Token.sol";

contract TokenInvariantTest is StdInvariant, Test {
    Token t;

    function setUp() public {
        t = new Token(1_000_000e18);
        targetContract(address(t));
    }

    // This should _always_ be true
    function invariant_totalSupply() public {
        assertEq(t.totalSupply(), 1_000_000e18);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;forge &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--match-test&lt;/span&gt; invariant &lt;span class="nt"&gt;-vvv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;--fuzz-runs 50000&lt;/code&gt; for serious fuzzing. On a commodity laptop that runs in ~30s and catches things you'd never write a unit test for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ghost variable pattern:&lt;/strong&gt; When the invariant involves contract state over time, track a ghost variable in the test contract that mirrors expected state after every handler call.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Reentrancy Detection via Fuzz
&lt;/h2&gt;

&lt;p&gt;Foundry rolls back state after every fuzz run, so you can brute-force reentrancy paths without setting up a separate exploit contract for each scenario.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";
import {Vault} from "../src/Vault.sol";

contract ReentrancyFuzzTest is Test {
    Vault v;

    // Track the reentrancy attempt
    bool public attackAttempted;
    uint256 public balanceBefore;

    receive() external payable {
        if (attackAttempted) return; // only re-enter once
        attackAttempted = true;

        // Try to drain before the first call finishes
        v.withdraw(balanceBefore);
    }

    function testFuzz_Reentrancy(uint256 depositAmount) public {
        depositAmount = bound(depositAmount, 1 ether, 100 ether);

        // Fund victim
        v.deposit{value: depositAmount}();
        balanceBefore = depositAmount;

        // Attack from this contract
        attackAttempted = false;
        v.withdraw(depositAmount);

        // If reentrancy worked, vault balance would be &amp;lt; 0
        assertLe(address(v).balance, depositAmount);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt; Foundry isolates each fuzz run. You don't need &lt;code&gt;expectRevert&lt;/code&gt; — just check the final state. If &lt;code&gt;totalSupply&lt;/code&gt; or &lt;code&gt;balance&lt;/code&gt; diverged, the fuzzer found a path.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Access Control Fuzzing
&lt;/h2&gt;

&lt;p&gt;The most common finding in real audits: an &lt;code&gt;onlyOwner&lt;/code&gt; modifier that doesn't cover all state-changing paths. Fuzz every function from every caller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";
import {Vault} from "../src/Vault.sol";

contract AccessControlFuzzTest is Test {
    Vault v;

    address owner = makeAddr("owner");
    address attacker = makeAddr("attacker");

    function setUp() public {
        vm.prank(owner);
        v = new Vault();

        deal(attacker, 100 ether);
    }

    // Fuzz every state-changing function as an attacker
    function testFuzz_AccessControl_Withdraw(uint256 amount) public {
        vm.assume(amount &amp;gt; 0 &amp;amp;&amp;amp; amount &amp;lt;= 100 ether);

        vm.prank(attacker);
        vm.expectRevert(); // should always revert for non-owner
        v.emergencyWithdraw(amount);
    }

    function testFuzz_AccessControl_Pause(bool paused) public {
        vm.prank(attacker);
        vm.expectRevert();
        v.setPaused(paused);
    }

    function testFuzz_AccessControl_Mint(uint256 amount) public {
        amount = bound(amount, 0, 1_000_000e18);

        vm.prank(attacker);
        vm.expectRevert();
        v.mint(attacker, amount);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Build a &lt;code&gt;handler&lt;/code&gt; contract that wraps every permissioned function and call it from both privileged and unprivileged addresses in your invariant test suite. One function per access level.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Oracle Manipulation Fuzzing
&lt;/h2&gt;

&lt;p&gt;DeFi exploits almost always involve price oracles returning manipulated values. Fuzz the oracle input and check what happens to your core accounting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";
import {LendingPool} from "../src/LendingPool.sol";
import {MockOracle} from "../src/MockOracle.sol";

contract OracleManipulationFuzzTest is Test {
    LendingPool pool;
    MockOracle oracle;

    address user = makeAddr("user");

    function setUp() public {
        oracle = new MockOracle(1000e8); // ETH/USD = $1000
        pool = new LendingPool(address(oracle));
        deal(user, 100 ether);
    }

    // What happens if the oracle price swings wildly?
    function testFuzz_OracleManipulation(uint256 manipulatedPrice, uint256 collateral) public {
        collateral = bound(collateral, 1 ether, 50 ether);
        manipulatedPrice = bound(manipulatedPrice, 1e8, 100_000e8); // $1 to $100k

        // User deposits collateral
        vm.prank(user);
        pool.deposit{value: collateral}();

        // Oracle is manipulated (flash loan, sandwich, etc.)
        oracle.setPrice(manipulatedPrice);

        // User borrows against inflated collateral — or gets liquidated unfairly
        vm.prank(user);
        pool.borrow();

        // Check: can the pool cover all deposits?
        assertGe(address(pool).balance, pool.totalDeposits());
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this catches:&lt;/strong&gt; If your &lt;code&gt;borrow()&lt;/code&gt; or &lt;code&gt;liquidation&lt;/code&gt; math assumes prices stay within 5% of the previous value, the fuzzer will find the exact value that breaks it. Then you add a circuit breaker or TWAP window.&lt;/p&gt;




&lt;h2&gt;
  
  
  Running the Full Suite
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Standard fuzz (default 256 runs per test)&lt;/span&gt;
forge &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# Heavy fuzz (closer to what auditors run)&lt;/span&gt;
forge &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--fuzz-runs&lt;/span&gt; 50000 &lt;span class="nt"&gt;--ffi&lt;/span&gt;

&lt;span class="c"&gt;# Invariant tests (sequences of calls)&lt;/span&gt;
forge &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--match-test&lt;/span&gt; invariant &lt;span class="nt"&gt;--fuzz-runs&lt;/span&gt; 50000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On CI, run the light suite on every push and the heavy suite nightly.&lt;/p&gt;




&lt;p&gt;Useful? Check out these resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://foundry-cheatsheet.vercel.app" rel="noopener noreferrer"&gt;Foundry Cheatsheet&lt;/a&gt; — every forge command, cheatcode, and flag I always forget&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://contract-audit-checklist.vercel.app" rel="noopener noreferrer"&gt;Contract Audit Checklist&lt;/a&gt; — what to check before any deployment&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://solidity-snippets.vercel.app" rel="noopener noreferrer"&gt;Solidity Snippets&lt;/a&gt; — copy-paste patterns for common patterns&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>crypto</category>
      <category>security</category>
      <category>testing</category>
      <category>web3</category>
    </item>
  </channel>
</rss>
