← All sample reports
sample-org/contracts/Vault.sol·17 LOC · scanned May 27, 2026, 11:11 AM
1 HIGH0 MED0 LOW2 INFO

Sample report — Vault.sol classic reentrancy

The canonical contract demoed on the homepage, with the full Founder Pro report attached. 1 HIGH + 2 INFO. The reentrancy is real — every dev has seen it, plenty still ship it.

Recommendation

Ship-blocker: the reentrancy in withdraw() is the DAO-era bug. Apply checks-effects-interactions (state update before external call) before mainnet. Both informational findings (pragma range and low-level call) are intentional design choices in modern Solidity 0.8.x and can be acknowledged rather than fixed — note them in the project README so the next reviewer doesn't re-flag.

  1. HIGHF-01 · reentrancy-ethVault.sol:10–16

    Reentrancy in withdraw() lets an attacker drain the vault

    withdraw() sends ETH via msg.sender.call{value: amount}('') BEFORE zeroing balances[msg.sender]. A malicious contract can re-enter withdraw() inside its fallback / receive() function, see its balance still positive, and pull funds again. Slither flags this as reentrancy-eth with cross-function reach because balances is also read by deposit(). This is the DAO 2016 bug template — still in the wild because tutorials lag the fix by a year and beginners copy-paste.

    Vault.solvulnerable
    function withdraw() public {
        uint256 amount = balances[msg.sender];
        require(amount > 0, "no balance");
        (bool ok, ) = msg.sender.call{value: amount}("");   // ⚠ external call BEFORE state update
        require(ok, "transfer failed");
        balances[msg.sender] = 0;                            // state update happens too late
    }
    suggested patch drop-in fix
    --- a/Vault.sol
    +++ b/Vault.sol
    @@ function withdraw() public {
         uint256 amount = balances[msg.sender];
         require(amount > 0, "no balance");
    -    (bool ok, ) = msg.sender.call{value: amount}("");
    +    balances[msg.sender] = 0;
    +    (bool ok, ) = msg.sender.call{value: amount}("");
         require(ok, "transfer failed");
    -    balances[msg.sender] = 0;
     }
    Fix: Apply checks-effects-interactions: zero the balance BEFORE the external call. Optionally add OpenZeppelin's ReentrancyGuard for belt-and-suspenders, but the ordering fix alone is enough for this case.
    reentrancy-ethCWE-841SWC-107slither
  2. INFOF-02 · solc-versionVault.sol:2

    Pragma allows any 0.8.x — pin the range

    pragma solidity ^0.8.0 accepts every 0.8.x release, including older versions with known compiler bugs (ABIReencodingHeadOverflowWithStaticArrayCleanup, NestedCalldataArrayAbiReencodingSizeValidation, KeccakCaching). Slither flags this as informational because nothing in this contract triggers those bugs — but pinning to a known-good version (^0.8.21 or =0.8.25) eliminates the class entirely.

    Vault.solvulnerable
    pragma solidity ^0.8.0;   // accepts 0.8.0 through 0.8.x — broad
    Fix: Pin to a recent stable: `pragma solidity 0.8.25;` (exact) for production, `^0.8.21` if you want patch flexibility. The Hardhat / Foundry compile output won't change for this contract; the pragma is purely a declaration.
    solc-versioninformationalslither
  3. INFOF-03 · low-level-callsVault.sol:10–16

    Low-level call usage — intentional but worth documenting

    msg.sender.call{value: amount}('') is a low-level call. Slither flags any low-level call because they bypass the type system and don't propagate revert reasons. In this case the call is correct (forwarding ETH to an EOA or contract, with the return value checked) — but the next reviewer will re-flag it unless you add a comment explaining why .transfer() / .send() aren't used (2300-gas stipend doesn't suffice for many proxies post-Istanbul).

    Vault.solvulnerable
    (bool ok, ) = msg.sender.call{value: amount}("");
    require(ok, "transfer failed");
    Fix: Leave the .call() as is, but add a one-line comment explaining the 2300-gas stipend issue. Optional belt: assert that the call gas forwarded is reasonable (≥ 21,000) to defend against malicious receivers that try to grief the caller.
    low-level-callsinformationalslither

Get one of these for your contract.

Free for the first scan. Founder Pro €29/mo with a 7-day free trial unlocks unlimited scans, priority queue, and a human triaging every report — rate locked for life.