<?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>Forem: Heemin Kim</title>
    <description>The latest articles on Forem by Heemin Kim (@h33min).</description>
    <link>https://forem.com/h33min</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3868792%2F38fe00c2-ccb6-4892-9d19-b5d53e4611eb.jpeg</url>
      <title>Forem: Heemin Kim</title>
      <link>https://forem.com/h33min</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/h33min"/>
    <language>en</language>
    <item>
      <title>Anatomy of a DeFi Hack: Reentrancy Deep Dive</title>
      <dc:creator>Heemin Kim</dc:creator>
      <pubDate>Thu, 09 Apr 2026 03:57:29 +0000</pubDate>
      <link>https://forem.com/h33min/anatomy-of-a-defi-hack-reentrancy-deep-dive-adb</link>
      <guid>https://forem.com/h33min/anatomy-of-a-defi-hack-reentrancy-deep-dive-adb</guid>
      <description>&lt;p&gt;Reentrancy is the exploit that launched a thousand audits. The DAO hack of 2016 resulted in a $60M loss and a hard fork of Ethereum itself. Yet the pattern still appears in production contracts today.&lt;/p&gt;

&lt;p&gt;Let's walk through exactly how a reentrancy attack unfolds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup: A Vulnerable Vault
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract VulnerableVault {
    mapping(address =&amp;gt; uint256) public balances;

    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw() external {
        uint256 amount = balances[msg.sender];
        require(amount &amp;gt; 0, "Nothing to withdraw");

        // Step 1: send ETH — triggers attacker's receive()
        (bool ok,) = msg.sender.call{value: amount}("");
        require(ok, "Transfer failed");

        // Step 2: update state — too late!
        balances[msg.sender] = 0;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Attacker Contract
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract Attacker {
    VulnerableVault public vault;
    uint256 public count;

    constructor(address _vault) {
        vault = VulnerableVault(_vault);
    }

    function attack() external payable {
        vault.deposit{value: msg.value}();
        vault.withdraw(); // triggers the loop
    }

    receive() external payable {
        if (count &amp;lt; 10 &amp;amp;&amp;amp; address(vault).balance &amp;gt;= 1 ether) {
            count++;
            vault.withdraw(); // re-enter before state is updated
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Attack Flow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Attacker deposits 1 ETH.&lt;/li&gt;
&lt;li&gt;Attacker calls &lt;code&gt;withdraw()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Vault sends 1 ETH to attacker — &lt;code&gt;balance&lt;/code&gt; is still 1 ETH at this point.&lt;/li&gt;
&lt;li&gt;Attacker's &lt;code&gt;receive()&lt;/code&gt; fires and calls &lt;code&gt;withdraw()&lt;/code&gt; again.&lt;/li&gt;
&lt;li&gt;Vault checks &lt;code&gt;balances[attacker]&lt;/code&gt; — still 1 ETH — sends another 1 ETH.&lt;/li&gt;
&lt;li&gt;Repeat 10 times: attacker walks away with 10 ETH, vault loses 9.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Fixes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option A: Checks-Effects-Interactions Pattern
&lt;/h3&gt;

&lt;p&gt;Zero the balance &lt;strong&gt;before&lt;/strong&gt; the external call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function withdraw() external {
    uint256 amount = balances[msg.sender];
    require(amount &amp;gt; 0);
    balances[msg.sender] = 0; // ← effect first
    (bool ok,) = msg.sender.call{value: amount}(""); // ← interaction last
    require(ok);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option B: ReentrancyGuard
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract SafeVault is ReentrancyGuard {
    function withdraw() external nonReentrant {
        // ...
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option C: Pull-over-Push
&lt;/h3&gt;

&lt;p&gt;Instead of sending ETH directly, let users claim it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mapping(address =&amp;gt; uint256) public pending;

function claimFunds() external {
    uint256 amount = pending[msg.sender];
    pending[msg.sender] = 0;
    payable(msg.sender).transfer(amount);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cross-Function Reentrancy
&lt;/h2&gt;

&lt;p&gt;Don't forget that reentrancy can occur across multiple functions sharing state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function transfer(address to, uint256 amount) external {
    balances[msg.sender] -= amount;
    // ← External call in another function can re-enter here
    balances[to] += amount;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always audit functions that modify shared state alongside functions that make external calls.&lt;/p&gt;




&lt;p&gt;ContractScan's static analysis engine flags all of these patterns. &lt;a href="https://dev.to/"&gt;Upload your contract&lt;/a&gt; and check for reentrancy in seconds.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Try &lt;a href="https://contract-scanner.raccoonworld.xyz" rel="noopener noreferrer"&gt;ContractScan&lt;/a&gt; free — automated Solidity vulnerability scanning powered by Slither, Semgrep, Mythril, and AI.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>solidity</category>
      <category>security</category>
      <category>ethereum</category>
      <category>smartcontracts</category>
    </item>
    <item>
      <title>Reentrancy: From The DAO to Euler Finance</title>
      <dc:creator>Heemin Kim</dc:creator>
      <pubDate>Thu, 09 Apr 2026 03:57:25 +0000</pubDate>
      <link>https://forem.com/h33min/reentrancy-from-the-dao-to-euler-finance-eoj</link>
      <guid>https://forem.com/h33min/reentrancy-from-the-dao-to-euler-finance-eoj</guid>
      <description>&lt;h1&gt;
  
  
  Reentrancy: From The DAO to Euler Finance
&lt;/h1&gt;

&lt;p&gt;The single vulnerability class that has drained the most funds in smart contract history: &lt;strong&gt;reentrancy&lt;/strong&gt;. From the 2016 DAO hack (~$60M) to Cream Finance in 2021 ($130M) and Euler Finance in 2023 ($197M), it has accounted for billions of dollars in losses.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Reentrancy?
&lt;/h2&gt;

&lt;p&gt;When contract A makes an external call to contract B, B can call back into A before A's state has been updated — re-executing logic against stale state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vulnerable Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract VulnerableBank {
    mapping(address =&amp;gt; uint256) public balances;

    function withdraw() external {
        uint256 amount = balances[msg.sender];
        require(amount &amp;gt; 0);
        // ⚠️ ETH sent before balance update — reentrancy possible
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);
        balances[msg.sender] = 0; // Too late!
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The DAO (2016, ~$60M)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function splitDAO(uint _proposalID) returns (bool) {
    if (!msg.sender.call.value(reward)()) throw; // Transfer first
    totalSupply -= balances[msg.sender]; // State update later
    balances[msg.sender] = 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ Directly caused the ETH/ETC hard fork&lt;/p&gt;

&lt;h2&gt;
  
  
  Cream Finance (2021, ~$130M)
&lt;/h2&gt;

&lt;p&gt;The AMP token's ERC1820 callback was used as a cross-contract reentrancy vector. Collateralized AMP was repeatedly borrowed through reentrant calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Euler Finance (2023, ~$197M)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;donateToReserves()&lt;/code&gt; did not validate the health factor, allowing an artificial liquidation state to be created. Combined with a flash loan for the exploit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defense 1: CEI Pattern
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function withdraw() external {
    uint256 amount = balances[msg.sender];
    require(amount &amp;gt; 0);
    balances[msg.sender] = 0;  // Effects first
    (bool success, ) = msg.sender.call{value: amount}(""); // Interactions last
    require(success);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Defense 2: ReentrancyGuard
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract SafeBank is ReentrancyGuard {
    function withdraw() external nonReentrant {
        uint256 amount = balances[msg.sender];
        require(amount &amp;gt; 0);
        balances[msg.sender] = 0;
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Defense 3: Pull Payment
&lt;/h2&gt;

&lt;p&gt;Instead of pushing ETH directly, let the recipient pull it themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is state fully updated before any external calls? (CEI)&lt;/li&gt;
&lt;li&gt;[ ] Is the &lt;code&gt;nonReentrant&lt;/code&gt; modifier applied?&lt;/li&gt;
&lt;li&gt;[ ] Have ERC777/ERC1155 callback tokens been reviewed?&lt;/li&gt;
&lt;li&gt;[ ] Has cross-contract reentrancy been considered?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Detecting These Issues with ContractScan
&lt;/h2&gt;

&lt;p&gt;Slither + Semgrep + AI triple-layer analysis automatically detects reentrancy patterns.&lt;br&gt;
→ &lt;a href="https://raccoonworld.xyz" rel="noopener noreferrer"&gt;ContractScan Free Scan&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Try &lt;a href="https://contract-scanner.raccoonworld.xyz" rel="noopener noreferrer"&gt;ContractScan&lt;/a&gt; free — automated Solidity vulnerability scanning powered by Slither, Semgrep, Mythril, and AI.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>solidity</category>
      <category>security</category>
      <category>ethereum</category>
      <category>smartcontracts</category>
    </item>
  </channel>
</rss>
