<?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: mehvetero</title>
    <description>The latest articles on DEV Community by mehvetero (@mehvetero).</description>
    <link>https://dev.to/mehvetero</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%2F4045014%2Fe14594a2-4649-4055-9343-47295d21cbc7.jpg</url>
      <title>DEV Community: mehvetero</title>
      <link>https://dev.to/mehvetero</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mehvetero"/>
    <language>en</language>
    <item>
      <title>The Hacker Who Lost $7.73M to a Bot — rsETH Safe Module Exploit</title>
      <dc:creator>mehvetero</dc:creator>
      <pubDate>Tue, 15 Sep 2026 23:49:46 +0000</pubDate>
      <link>https://dev.to/mehvetero/the-hacker-who-lost-773m-to-a-bot-rseth-safe-module-exploit-23gk</link>
      <guid>https://dev.to/mehvetero/the-hacker-who-lost-773m-to-a-bot-rseth-safe-module-exploit-23gk</guid>
      <description>&lt;p&gt;On September 15, 2026, someone found a bug in a custom router module attached to a Gnosis Safe holding over $7.73 million in Aave-wrapped rsETH. They built the exploit, submitted it to the mempool — and an MEV bot named Yoink copied the transaction and executed it first. The attacker showed up an hour later to scrape leftovers.&lt;/p&gt;

&lt;p&gt;This write-up covers what I was able to verify on-chain and what I had to rely on others for. The router's source code is not published on Etherscan, so the internal vulnerability mechanism comes from SlowMist's bytecode analysis, credited below. Everything else — the Safe configuration, the money flow, the attacker timeline, and the current state — I read directly from the chain.&lt;/p&gt;

&lt;p&gt;All read : &lt;a href="https://x.com/mehvetero/status/2100007912953491857" rel="noopener noreferrer"&gt;https://x.com/mehvetero/status/2100007912953491857&lt;/a&gt;&lt;/p&gt;

</description>
      <category>hacking</category>
      <category>exploit</category>
      <category>safewallet</category>
      <category>defi</category>
    </item>
    <item>
      <title>$500 vs the 1inch DAO Treasury — Anatomy of a Live Governance Attack</title>
      <dc:creator>mehvetero</dc:creator>
      <pubDate>Tue, 15 Sep 2026 11:08:50 +0000</pubDate>
      <link>https://dev.to/mehvetero/500-vs-the-1inch-dao-treasury-anatomy-of-a-live-governance-attack-4961</link>
      <guid>https://dev.to/mehvetero/500-vs-the-1inch-dao-treasury-anatomy-of-a-live-governance-attack-4961</guid>
      <description>&lt;p&gt;On September 14, 2026, a fresh wallet with six transactions submitted two governance proposals against 1inch DAO's treasury Safe. One of the two can actually execute. This post breaks down exactly what happened, what can happen next, and what the on-chain parameters say about the timeline.&lt;/p&gt;

&lt;p&gt;Every value in this post was read directly from Ethereum mainnet via &lt;code&gt;eth_call&lt;/code&gt; or transaction receipt logs. No assumptions, no docs-only claims.&lt;/p&gt;




&lt;h2&gt;
  
  
  The attacker
&lt;/h2&gt;

&lt;p&gt;Address: &lt;a href="https://etherscan.io/address/0x6f25Cc4d6bD161e7F4514948AF1aE1B8bafF5AE1" rel="noopener noreferrer"&gt;&lt;code&gt;0x6f25Cc4d6bD161e7F4514948AF1aE1B8bafF5AE1&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Total lifetime transactions: 6. All on September 14, 2026.&lt;/p&gt;

&lt;p&gt;Funding: 0.197702 ETH from &lt;a href="https://etherscan.io/address/0x3f3ee0a9cac2d01db44001eca3e8382fbe40207b" rel="noopener noreferrer"&gt;&lt;code&gt;0x3f3e...0207b&lt;/code&gt;&lt;/a&gt; (ChangeNOW deposit address — anonymous instant exchange, no KYC) plus 0.0036 ETH from a second address. Total received: 0.2013 ETH. After posting two 0.1 ETH bonds and paying gas, the wallet holds 0.000913 ETH.&lt;/p&gt;




&lt;h2&gt;
  
  
  What 1inch uses
&lt;/h2&gt;

&lt;p&gt;1inch DAO's treasury is a Gnosis Safe multisig at &lt;a href="https://etherscan.io/address/0x7951c7ef839e26F63DA87a42C9a87986507f1c07" rel="noopener noreferrer"&gt;&lt;code&gt;0x7951c7ef839e26F63DA87a42C9a87986507f1c07&lt;/code&gt;&lt;/a&gt; (7-of-12 threshold).&lt;/p&gt;

&lt;p&gt;The Safe has a Zodiac RealityModuleETH enabled as a module. This module can execute transactions on the Safe without requiring multisig signatures — that is the entire point of SafeSnap governance. Proposals go through Reality.eth as an optimistic oracle: if no one disputes the answer within the timeout, the module can execute.&lt;/p&gt;




&lt;h2&gt;
  
  
  Two proposals, one live
&lt;/h2&gt;

&lt;p&gt;The attacker submitted two proposals to two different RealityModuleETH contracts. Only one of them is actually connected to the treasury Safe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Proposal 1 — "wave3-drain" (THE LIVE ONE)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Module&lt;/td&gt;
&lt;td&gt;&lt;a href="https://etherscan.io/address/0xa62d2a75eb39c12e908e9f6bf50f189641692f2e" rel="noopener noreferrer"&gt;&lt;code&gt;0xa62d2a75eb39c12e908e9f6bf50f189641692f2e&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Module enabled on Safe&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;YES&lt;/strong&gt; (verified via &lt;code&gt;isModuleEnabled()&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;proposalId&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wave3-drain&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;questionId&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x03b47cdd30ffa3f228f186bf7123ab6e73671c5b0c36bed8006eb84249e79f7d&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;txHashes&lt;/td&gt;
&lt;td&gt;5 batched operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Submitted&lt;/td&gt;
&lt;td&gt;Sep 14, 15:31:47 UTC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YES bond&lt;/td&gt;
&lt;td&gt;0.1 ETH at 15:34:23 UTC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proposal TX&lt;/td&gt;
&lt;td&gt;&lt;a href="https://etherscan.io/tx/0x4db430aa9f5679fa3514f5e957157257156e95637a973fd56cbd2c4fd46d2f8e" rel="noopener noreferrer"&gt;&lt;code&gt;0x4db430aa...&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bond TX&lt;/td&gt;
&lt;td&gt;&lt;a href="https://etherscan.io/tx/0x47a4c2f6c989793aea3fba8e260368b24a984ef469ecdd8a494ced86d8561145" rel="noopener noreferrer"&gt;&lt;code&gt;0x47a4c2f6...&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This module is enabled on the treasury Safe. If the Reality.eth question finalizes as "YES" and the cooldown passes, &lt;code&gt;executeProposal&lt;/code&gt; can run 5 transaction batches against the Safe.&lt;/p&gt;

&lt;p&gt;The name "wave3-drain" is not subtle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Proposal 2 — "wave5-accumulation-001" (CANNOT EXECUTE)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Module&lt;/td&gt;
&lt;td&gt;&lt;a href="https://etherscan.io/address/0xa7456392eb978a756264d54968f55baf227fcbcf" rel="noopener noreferrer"&gt;&lt;code&gt;0xa7456392eb978a756264d54968f55baf227fcbcf&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Module enabled on Safe&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;NO&lt;/strong&gt; (verified via &lt;code&gt;isModuleEnabled()&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;proposalId&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wave5-accumulation-001&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;questionId&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xf84299c435bfe991936dfdfec377cc4e700a70b7fddd465385cbe832d786dae1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;txHashes&lt;/td&gt;
&lt;td&gt;1 batched operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Submitted&lt;/td&gt;
&lt;td&gt;Sep 14, 16:37:35 UTC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YES bond&lt;/td&gt;
&lt;td&gt;0.1 ETH at 16:37:59 UTC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proposal TX&lt;/td&gt;
&lt;td&gt;&lt;a href="https://etherscan.io/tx/0xeb8e7593be0a29610a0df44270977241bfe182f06522ff8a33d5e04a8912b699" rel="noopener noreferrer"&gt;&lt;code&gt;0xeb8e7593...&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bond TX&lt;/td&gt;
&lt;td&gt;&lt;a href="https://etherscan.io/tx/0xb3ecad6ea2a4de1bd697082134a0cfb5efb154b8522281290aa5a68464549dfc" rel="noopener noreferrer"&gt;&lt;code&gt;0xb3ecad6e...&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This module is &lt;strong&gt;not enabled&lt;/strong&gt; on the treasury Safe. Even if the Reality question finalizes as "YES," &lt;code&gt;executeProposal&lt;/code&gt; will revert because the Safe will reject the call. This proposal is either a decoy, a mistake, or aimed at a different Safe.&lt;/p&gt;




&lt;h2&gt;
  
  
  On-chain parameters (RealityModuleETH 0xa62d)
&lt;/h2&gt;

&lt;p&gt;These values were read via &lt;code&gt;eth_call&lt;/code&gt; against the live contract on Ethereum mainnet, cross-verified on two independent RPC endpoints.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;questionTimeout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;259,200 seconds = &lt;strong&gt;72 hours&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Time for the Reality.eth answer to be challenged. Each dispute resets the clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;questionCooldown&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;259,200 seconds = &lt;strong&gt;72 hours&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Delay after the answer finalizes before &lt;code&gt;executeProposal&lt;/code&gt; is allowed. This is the veto window.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;answerExpiration&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;604,800 seconds = &lt;strong&gt;7 days&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Finalized answers expire after 7 days. Execution must happen within this window.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;minimumBond&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100,000,000,000,000,000 wei = &lt;strong&gt;0.1 ETH&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Minimum bond to post an answer on Reality.eth.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;oracle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x5b7dD1E86623548AF054A4985F7fc8Ccbb554E2c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;RealityETH_v3_0 on Ethereum mainnet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;avatar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x7951c7ef839e26F63DA87a42C9a87986507f1c07&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The treasury Safe this module controls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Timeline (wave3-drain, undisputed scenario)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sep 14, 15:34 UTC — Attacker posts YES bond (0.1 ETH)
                     72-hour question timeout starts

Sep 17, 15:34 UTC — If undisputed, Reality answer finalizes as YES
                     72-hour cooldown starts
                     Safe signers can veto during this window

Sep 20, 15:34 UTC — Cooldown ends, executeProposal becomes callable
                     5 transaction batches run against the treasury Safe

Sep 21, 15:34 UTC — Answer expires (7-day answerExpiration)
                     Execution no longer possible after this point
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If someone posts a "NO" bond at any time during the question timeout, the clock resets. The escalation continues until one side stops bonding. The last answer standing after 72 hours of silence wins.&lt;/p&gt;

&lt;p&gt;During the 72-hour cooldown, the 7-of-12 Safe signers can intervene — remove the module, move funds, or otherwise prevent execution.&lt;/p&gt;




&lt;h2&gt;
  
  
  The treasury
&lt;/h2&gt;

&lt;p&gt;Safe: &lt;a href="https://etherscan.io/address/0x7951c7ef839e26F63DA87a42C9a87986507f1c07" rel="noopener noreferrer"&gt;&lt;code&gt;0x7951c7ef839e26F63DA87a42C9a87986507f1c07&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Verified holdings at time of writing (Etherscan):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Asset&lt;/th&gt;
&lt;th&gt;Amount&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Aave Ethereum USDC&lt;/td&gt;
&lt;td&gt;2,712,464&lt;/td&gt;
&lt;td&gt;~$2.71M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ETH&lt;/td&gt;
&lt;td&gt;257.53&lt;/td&gt;
&lt;td&gt;~$647K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WETH&lt;/td&gt;
&lt;td&gt;243.45&lt;/td&gt;
&lt;td&gt;~$612K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1INCH&lt;/td&gt;
&lt;td&gt;4,650,219&lt;/td&gt;
&lt;td&gt;~$433K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WBTC&lt;/td&gt;
&lt;td&gt;1.92&lt;/td&gt;
&lt;td&gt;~$150K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DAI&lt;/td&gt;
&lt;td&gt;128,520&lt;/td&gt;
&lt;td&gt;~$129K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USDT&lt;/td&gt;
&lt;td&gt;81,984&lt;/td&gt;
&lt;td&gt;~$82K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Other (67 tokens)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$4.76M&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 5 transaction hashes in wave3-drain are opaque — SafeSnap commits to the hash without revealing the operations until execution. What they target (token approvals, transfers, or something else) is not publicly visible.&lt;/p&gt;




&lt;h2&gt;
  
  
  The attack economics
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ChangeNOW funding&lt;/td&gt;
&lt;td&gt;0.1977 ETH (~$497)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Second funding&lt;/td&gt;
&lt;td&gt;0.0036 ETH (~$9)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gas (4 transactions)&lt;/td&gt;
&lt;td&gt;~0.0003 ETH (~$0.75)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total spent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$507&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bonds (refundable if YES wins)&lt;/td&gt;
&lt;td&gt;0.2 ETH (~$502)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Non-refundable cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~$6&lt;/strong&gt; (gas + ChangeNOW fee)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If wave3-drain passes unchallenged, the attacker spends $6 in gas to gain execution rights over ~$4.76M.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to block wave3-drain right now
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post a "NO" bond&lt;/strong&gt; on the Reality.eth question. The question ID is &lt;code&gt;0x03b47cdd30ffa3f228f186bf7123ab6e73671c5b0c36bed8006eb84249e79f7d&lt;/code&gt; on RealityETH_v3_0 at &lt;a href="https://etherscan.io/address/0x5b7dD1E86623548AF054A4985F7fc8Ccbb554E2c" rel="noopener noreferrer"&gt;&lt;code&gt;0x5b7dD1E86623548AF054A4985F7fc8Ccbb554E2c&lt;/code&gt;&lt;/a&gt; (Ethereum mainnet). Minimum bond: 0.1 ETH. This resets the 72-hour clock.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Safe signers:&lt;/strong&gt; Even if the question finalizes, you have 72 hours of cooldown before execution is possible. During that window, you can remove the RealityModuleETH from the Safe or move assets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Longer term:&lt;/strong&gt; Raise the minimum bond. 0.1 ETH for a $4.76M treasury means the cost of an attack attempt is $6 in gas. Consider restricting who can call &lt;code&gt;addProposal&lt;/code&gt; or requiring a minimum token balance.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Pattern recognition
&lt;/h2&gt;

&lt;p&gt;This is the same vulnerability class as Term Finance ($8.5M, August 2026), which I analyzed &lt;a href="https://mehvetero.com/term-finance-governance-exploit" rel="noopener noreferrer"&gt;previously&lt;/a&gt;. Different mechanism — Term was token-weighted voting, 1inch is an optimistic oracle — but the same root cause: governance security that depends on human attention fails when nobody is paying attention.&lt;/p&gt;

&lt;p&gt;Term Finance: $951 bought 90.66% of the vote. Nobody else voted.&lt;br&gt;
1inch DAO: $6 in gas submitted a drain proposal. Nobody disputed it yet.&lt;/p&gt;

&lt;p&gt;The wave numbering tells the story. Wave 1 through wave 4 were apparently blocked. Each time, someone noticed and disputed. The attacker keeps trying because the cost is near zero and they only need to succeed once.&lt;/p&gt;

&lt;p&gt;The code is correct. The parameters are configured. The 72+72 hour design gives the signers time. The question is whether anyone is watching the clock.&lt;/p&gt;




&lt;h2&gt;
  
  
  On-chain references
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Attacker:      0x6f25Cc4d6bD161e7F4514948AF1aE1B8bafF5AE1
Treasury Safe: 0x7951c7ef839e26F63DA87a42C9a87986507f1c07
RealityModule: 0xa62d2a75eb39c12e908e9f6bf50f189641692f2e (enabled)
RealityModule: 0xa7456392eb978a756264d54968f55baf227fcbcf (NOT enabled)
Reality Oracle: 0x5b7dD1E86623548AF054A4985F7fc8Ccbb554E2c (Ethereum)

wave3-drain questionId:
0x03b47cdd30ffa3f228f186bf7123ab6e73671c5b0c36bed8006eb84249e79f7d

wave5-accumulation-001 questionId:
0xf84299c435bfe991936dfdfec377cc4e700a70b7fddd465385cbe832d786dae1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Every value in this post was read from Ethereum mainnet via eth_call or transaction receipt parsing. No transactions were submitted.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>1inch</category>
      <category>attack</category>
      <category>hacking</category>
      <category>governance</category>
    </item>
    <item>
      <title>How $951 Bought a $8.5M Vote: The Term Finance Governance Exploit</title>
      <dc:creator>mehvetero</dc:creator>
      <pubDate>Thu, 27 Aug 2026 01:04:12 +0000</pubDate>
      <link>https://dev.to/mehvetero/how-951-bought-a-85m-vote-the-term-finance-governance-exploit-41lk</link>
      <guid>https://dev.to/mehvetero/how-951-bought-a-85m-vote-the-term-finance-governance-exploit-41lk</guid>
      <description>&lt;p&gt;&lt;em&gt;Zero bugs. Zero hacks. Just a vote.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On August 23, 2026, at 06:25 UTC, an attacker executed a single parameterless function call on Ethereum and walked away with $8.5 million from Term Finance's vaults. No exploit in the traditional sense — no reentrancy, no overflow, no oracle manipulation. The smart contracts worked exactly as designed. The attacker simply voted.&lt;/p&gt;

&lt;p&gt;This is the anatomy of a governance takeover — the kind of attack that no code audit would have caught, because the code was never the problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Target
&lt;/h2&gt;

&lt;p&gt;Term Finance is an Ethereum-based fixed-rate lending protocol. Its yield products — the Meta Vaults — sit on Yearn V3 infrastructure with a custom governance layer bolted on top. Users deposit ETH and USDC into these vaults, which then deploy capital across strategies in Aave, Morpho Blue, and other lending markets.&lt;/p&gt;

&lt;p&gt;At the time of the attack, Term's vaults held roughly $12.45 million in TVL, with $8.8 million on Ethereum mainnet.&lt;/p&gt;

&lt;p&gt;The governance design: vault depositors receive a governance token (tmvETH) proportional to their stake. Token holders vote on proposals that control vault parameters — which strategies to add, what fee structures to use, where capital flows. A 7-day timelock and an LP veto mechanism were supposed to prevent hostile proposals from executing.&lt;/p&gt;

&lt;p&gt;All of this worked as intended. That was the problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Kill Chain
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Day 0 — August 17, 05:01 UTC: The $951 Coup
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Block 25,772,572.&lt;/strong&gt; A fresh wallet (&lt;code&gt;0xa908b347...&lt;/code&gt;) receives 0.9945 ETH from an intermediary funded by Tornado Cash. Two ETH total — seed money.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Block 25,772,675&lt;/strong&gt; (twenty minutes later). The wallet purchases 0.4852 tmvETH — the governance token for Term's ETH Meta Vault — for approximately 0.5 ETH.&lt;/p&gt;

&lt;p&gt;Cost: roughly $951.&lt;/p&gt;

&lt;p&gt;The total staked supply of gtmvETH at that moment: 0.5352.&lt;/p&gt;

&lt;p&gt;The attacker now controls &lt;strong&gt;90.66% of all voting power&lt;/strong&gt; in the ETH Meta Vault. Four USDC strategy vaults fall under full control as well — their governance participation was even thinner.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The entire defense model assumed the electorate would be large enough that no single buyer could reach a majority. It wasn't. The governance token market was so thin that the protocol's security reduced to a liquidity depth question — and no one was watching the depth.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 0 — Proposals Submitted
&lt;/h3&gt;

&lt;p&gt;With 90.66% voting power, the attacker submits governance proposals to the affected vaults. The proposals contain two critical actions, ordered deliberately:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action 1: Set the timelock cooldown to zero.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the move that made everything else possible. The 7-day timelock was the last line of defense — after a proposal passes, there's supposed to be a mandatory waiting period before execution. The attacker's first action upon execution removes that waiting period for all subsequent proposals. One vote to end all future delay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action 2: Add a new "strategy" contract.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Yearn V3 architecture, strategies are the modules that deploy vault capital into external protocols. The attacker's "strategy" is a contract whose sole purpose is to route withdrawn funds to the attacker's address (&lt;code&gt;0x184f2e57b4...&lt;/code&gt;). It looks like a strategy. It functions as a drain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 1–5 — Silence
&lt;/h3&gt;

&lt;p&gt;The proposals sit in the timelock queue. The 7-day waiting period ticks.&lt;/p&gt;

&lt;p&gt;Term Finance had an LP veto mechanism — liquidity providers could reject hostile proposals during this window. Nobody vetoed. The governance forum showed no discussion of these proposals. The monitoring infrastructure either didn't exist or didn't trigger.&lt;/p&gt;

&lt;p&gt;Five days of a loaded gun sitting on the table, and nobody noticed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 6 — August 23, 06:25 UTC: Execution
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Block 25,816,049.&lt;/strong&gt; The attacker calls &lt;code&gt;0x373058b8&lt;/code&gt; on the governance executor contract (&lt;code&gt;0x64e47780...&lt;/code&gt;). A single function, no parameters. Execute.&lt;/p&gt;

&lt;p&gt;What happens inside this transaction — 78 log events in total:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The timelock cooldown drops to zero (Action 1 fires)&lt;/li&gt;
&lt;li&gt;The malicious strategy is added to the vault roster&lt;/li&gt;
&lt;li&gt;The Meta Vault begins withdrawing from its real strategies:

&lt;ul&gt;
&lt;li&gt;Aave WETH pool: two sub-vaults redeemed&lt;/li&gt;
&lt;li&gt;Morpho Blue (&lt;code&gt;0xbbbbbbbb...&lt;/code&gt;): two withdrawals via Yearn V3 wrapper&lt;/li&gt;
&lt;li&gt;Each redemption flows back through the Meta Vault aggregator (&lt;code&gt;0x26fcb50e...&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Capital is routed to the attacker's strategy (&lt;code&gt;0x184f2e57b4...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The strategy forwards &lt;strong&gt;2,841.74 WETH&lt;/strong&gt; to the attacker wallet&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Transaction:&lt;/strong&gt; &lt;a href="https://etherscan.io/tx/0xd354a15b15cb73d30908f411aee3f795ec86737a4d080e9a818ac4d6d3014129" rel="noopener noreferrer"&gt;&lt;code&gt;0xd354a15b...&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A separate execution drains the USDC vaults — approximately &lt;strong&gt;1.68 million USDC&lt;/strong&gt;, immediately swapped to DAI through on-chain DEX routes.&lt;/p&gt;

&lt;p&gt;Total extraction: &lt;strong&gt;~$8.5 million&lt;/strong&gt; from a $951 investment.&lt;/p&gt;

&lt;p&gt;Return on capital: &lt;strong&gt;8,938x&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Money Trail
&lt;/h2&gt;

&lt;p&gt;Post-drain, the attacker moves quickly but not completely.&lt;/p&gt;

&lt;p&gt;The 2,841 WETH is unwrapped to ETH and forwarded through a series of splitter contracts, all pointing to variations of &lt;code&gt;0xd518...&lt;/code&gt; addresses. Address poisoning tactics are visible in the transaction log — fake "EṬH" transfers (note the modified T character: Ṭ vs T) sent to similar-looking addresses, a social engineering technique targeting anyone monitoring the flow who might copy-paste the wrong destination.&lt;/p&gt;

&lt;p&gt;As of this analysis, the consolidation wallet &lt;strong&gt;&lt;code&gt;0xD5183d8BfC65a50863C62aF2538198A8288FFc13&lt;/code&gt;&lt;/strong&gt; holds:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Asset&lt;/th&gt;
&lt;th&gt;Amount&lt;/th&gt;
&lt;th&gt;USD Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ETH&lt;/td&gt;
&lt;td&gt;2,543.15&lt;/td&gt;
&lt;td&gt;~$6.15M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DAI&lt;/td&gt;
&lt;td&gt;1,679,642.45&lt;/td&gt;
&lt;td&gt;~$1.68M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$7.83M&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Approximately $670K has been spent on gas, forwarding, and partial exits. The bulk remains untouched — either the attacker is patient, or they're waiting for attention to die down.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Every Safeguard Failed
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Timelock Paradox
&lt;/h3&gt;

&lt;p&gt;A 7-day timelock is only useful if someone is watching. Term Finance's governance participation was so low that the proposals passed with zero opposition. But the deeper design flaw is subtler: &lt;strong&gt;the timelock itself was governed by the same vote that it was supposed to protect against.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The attacker's first action — setting cooldown to zero — is the governance equivalent of using the first wish to wish for unlimited wishes. If the entity that controls the timelock is the same entity the timelock is supposed to constrain, the timelock is decorative.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Veto That Wasn't
&lt;/h3&gt;

&lt;p&gt;LP veto rights existed on paper. No LP exercised them. This isn't an implementation failure — it's a participation failure. The mechanism assumed LPs would:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Monitor the governance forum&lt;/li&gt;
&lt;li&gt;Understand the proposals&lt;/li&gt;
&lt;li&gt;Recognize the threat&lt;/li&gt;
&lt;li&gt;Act within the window&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each assumption was individually reasonable. Together, they built a defense that required four human actions in sequence, any one of which could fail to happen. The attacker only needed one to not happen. It was all four.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Token Distribution That Nobody Checked
&lt;/h3&gt;

&lt;p&gt;The most fundamental failure: at the moment of attack, the governance token had a total staked supply of 0.5352 gtmvETH. The entire security model — the votes, the timelock, the veto — was downstream of a single assumption: that this number would be large enough to make a hostile majority prohibitively expensive.&lt;/p&gt;

&lt;p&gt;It cost $951.&lt;/p&gt;

&lt;p&gt;No monitoring system checked this number. No circuit breaker fired when a single address acquired 90% of voting power. The protocol treated governance token concentration as someone else's problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture Lesson
&lt;/h2&gt;

&lt;p&gt;This exploit belongs to a class that traditional smart contract audits cannot catch. The code functioned correctly. The governance mechanism executed as specified. The timelock enforced its rules. Every &lt;code&gt;require&lt;/code&gt; passed, every &lt;code&gt;assert&lt;/code&gt; held, every state transition was valid.&lt;/p&gt;

&lt;p&gt;The vulnerability was economic, not technical:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Governance security is a function of participation, not mechanism design.&lt;/strong&gt; A perfectly designed voting system with 0.5 participants is less secure than a flawed one with 10,000.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Timelocks must not govern themselves.&lt;/strong&gt; If a governance vote can modify the timelock parameters, the timelock provides exactly one window of protection — the first one. After that, the attacker owns the clock.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Token concentration is a security metric.&lt;/strong&gt; Protocols that expose governance power through thin markets need circuit breakers: minimum quorum, concentration alerts, automatic pause when a single address crosses a voting threshold.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The veto window is a monitoring commitment, not a safety feature.&lt;/strong&gt; A veto mechanism that nobody monitors is indistinguishable from no veto mechanism at all.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;$951 is cheaper than an audit.&lt;/strong&gt; The attacker spent less on this entire operation than most protocols spend on a single smart contract review. The audit would have found nothing — the code was clean. The governance design was the attack surface, and no auditor was looking at token distribution depth.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What Should Have Existed
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minimum quorum&lt;/strong&gt;: proposals below X% participation cannot execute, regardless of vote ratio&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concentration alert&lt;/strong&gt;: if a single address holds &amp;gt;Y% of voting power, governance enters emergency mode (pause + multisig override)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timelock immutability&lt;/strong&gt;: core safety parameters (timelock duration, veto window) cannot be modified through governance — they require a multisig or are hardcoded&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strategy whitelist with independent approval&lt;/strong&gt;: new strategy contracts require approval from a separate security council, not the same governance that benefits from the strategy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active monitoring with auto-pause&lt;/strong&gt;: governance proposal submission triggers alerts to all LPs, and proposals modifying safety parameters auto-escalate to a higher approval threshold&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are novel. All of them existed in other protocols before this attack. They just weren't implemented here.&lt;/p&gt;




&lt;h2&gt;
  
  
  Timeline
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Aug 17, 05:01 UTC&lt;/td&gt;
&lt;td&gt;Attacker wallet funded via Tornado Cash (2 ETH)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aug 17, 05:21 UTC&lt;/td&gt;
&lt;td&gt;0.4852 tmvETH purchased (~$951) → 90.66% voting power&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aug 17 (est.)&lt;/td&gt;
&lt;td&gt;Governance proposals submitted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aug 17–22&lt;/td&gt;
&lt;td&gt;6-day timelock window — zero vetoes, zero discussion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aug 23, 06:25 UTC&lt;/td&gt;
&lt;td&gt;Proposals executed: timelock zeroed, malicious strategy added, 2,841.74 WETH drained&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aug 23, 06:25+ UTC&lt;/td&gt;
&lt;td&gt;USDC vaults drained (~1.68M USDC → DAI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aug 23&lt;/td&gt;
&lt;td&gt;Term Labs shuts down all Meta Vaults, revokes DAO governance roles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aug 26 (this analysis)&lt;/td&gt;
&lt;td&gt;$7.83M still in consolidation wallet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;&lt;em&gt;On-chain data sourced via Alchemy Ethereum mainnet RPC. All addresses and transaction hashes are independently verifiable on Etherscan.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>solidity</category>
    </item>
    <item>
      <title>BlueMove Was Not an Overflow Bug — How a Cross-Version Reserve Desync Drained 714,000 SUI</title>
      <dc:creator>mehvetero</dc:creator>
      <pubDate>Tue, 11 Aug 2026 19:23:45 +0000</pubDate>
      <link>https://dev.to/mehvetero/bluemove-was-not-an-overflow-bug-how-a-cross-version-reserve-desync-drained-714000-sui-5cp9</link>
      <guid>https://dev.to/mehvetero/bluemove-was-not-an-overflow-bug-how-a-cross-version-reserve-desync-drained-714000-sui-5cp9</guid>
      <description>&lt;p&gt;On July 11, 2026, at 22:13 UTC, an attacker began draining liquidity pools on BlueMove DEX, a Sui-based AMM. Within 23 minutes, approximately 714,000 SUI — roughly $528,000 at the time — had crossed a Wormhole bridge as USDC. The contracts were immutable. The pools were empty. There was no way to patch or freeze.&lt;/p&gt;

&lt;p&gt;BlueMove called it "a long-standing arithmetic overflow bug in the legacy AMM contract." Tyler Simpson of Quantum Void Labs called it a backdoor — a function shipped in a May 31 upgrade that set the stage for the drain.&lt;/p&gt;

&lt;p&gt;Both accounts identify real pieces of the story. Neither captures the mechanism that made the drain possible.&lt;/p&gt;

&lt;p&gt;I decompiled every module of both package versions, tested Move's integer behavior directly, and traced the attack transactions command by command. To the best of my knowledge, this is the first public analysis that identifies the actual exploit path: a &lt;strong&gt;reserve desynchronization&lt;/strong&gt; between two callable versions of the same package. Both versions write &lt;code&gt;reserve_x&lt;/code&gt; after every operation, but they write &lt;em&gt;different values&lt;/em&gt; — V1 writes &lt;code&gt;pool.token_x.value()&lt;/code&gt;, V-latest writes &lt;code&gt;escrow.token_x.value()&lt;/code&gt;. When a V1 swap runs on a pool whose main liquidity lives in the escrow, &lt;code&gt;reserve_x&lt;/code&gt; drops to the small &lt;code&gt;pool.token_x&lt;/code&gt; balance, while the escrow retains the large one. The next V-latest &lt;code&gt;mint&lt;/code&gt; divides by this deflated &lt;code&gt;reserve_x&lt;/code&gt;, inflating LP tokens by the ratio between the two balances.&lt;/p&gt;

&lt;p&gt;See all- &lt;a href="https://mehvetero.com/" rel="noopener noreferrer"&gt;https://mehvetero.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>crypto</category>
      <category>web3</category>
      <category>cryptocurrency</category>
    </item>
    <item>
      <title>How One Missing Assert Drained $1.14M From Aftermath Finance — A Signed Integer Exploit on Sui</title>
      <dc:creator>mehvetero</dc:creator>
      <pubDate>Sat, 01 Aug 2026 14:17:38 +0000</pubDate>
      <link>https://dev.to/mehvetero/how-one-missing-assert-drained-114m-from-aftermath-finance-a-signed-integer-exploit-on-sui-1db3</link>
      <guid>https://dev.to/mehvetero/how-one-missing-assert-drained-114m-from-aftermath-finance-a-signed-integer-exploit-on-sui-1db3</guid>
      <description>&lt;p&gt;A technical breakdown of the Aftermath Finance perpetuals exploit (April 29, 2026). How a negative fee value bypassed validation, inflated collateral, and allowed an attacker to drain $1.14M USDC in 36 minutes — and what a single line of code would have prevented.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mehvetero.com/" rel="noopener noreferrer"&gt;https://mehvetero.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>crypto</category>
      <category>cybersecurity</category>
      <category>security</category>
    </item>
    <item>
      <title>How a Missing `assert!` Drained $3.44M From Typus Finance — and Why the Code Looked Correct</title>
      <dc:creator>mehvetero</dc:creator>
      <pubDate>Sat, 01 Aug 2026 14:15:29 +0000</pubDate>
      <link>https://dev.to/mehvetero/how-a-missing-assert-drained-344m-from-typus-finance-and-why-the-code-looked-correct-4gbb</link>
      <guid>https://dev.to/mehvetero/how-a-missing-assert-drained-344m-from-typus-finance-and-why-the-code-looked-correct-4gbb</guid>
      <description>&lt;p&gt;On October 15, 2025, at 13:05 UTC, an attacker began draining Typus Finance's TLP liquidity pool on the Sui network. Thirty-four minutes later, the contracts were paused — but the pool was already empty. Over the hours that followed, the stolen SUI, xBTC, and suiETH were swapped to USDC on-chain, then bridged to Ethereum via Circle's CCTP in 14 transactions. Total loss: $3.44 million.&lt;/p&gt;

&lt;p&gt;The root cause was a single line of Move code that looked like an authorization check — called the right function, passed the right arguments, sat in the right place — but silently discarded its result. The oracle module had been live for eleven months.&lt;/p&gt;

&lt;p&gt;I pulled the vulnerable source from GitHub, compared it against the post-incident rewrite, and traced the attack on-chain. This is what I found.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mehvetero.com/" rel="noopener noreferrer"&gt;https://mehvetero.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>crypto</category>
      <category>cybersecurity</category>
      <category>security</category>
    </item>
    <item>
      <title>Three Sui Exploits, One Disease — Why "The Math Looked Fine" Keeps Costing Hundreds of Millions</title>
      <dc:creator>mehvetero</dc:creator>
      <pubDate>Mon, 27 Jul 2026 19:36:42 +0000</pubDate>
      <link>https://dev.to/mehvetero/three-sui-exploits-one-disease-why-the-math-looked-fine-keeps-costing-hundreds-of-millions-17f3</link>
      <guid>https://dev.to/mehvetero/three-sui-exploits-one-disease-why-the-math-looked-fine-keeps-costing-hundreds-of-millions-17f3</guid>
      <description>&lt;p&gt;&lt;em&gt;Cetus lost $223M to a shift overflow. Aftermath lost $1.14M to a negative fee. Bucket Protocol ships a decimal scaling bug today. All three passed audits. All three have the same root cause.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I've spent the last month reading exploit post-mortems, scanning production Sui contracts, and building lint rules to catch what I kept seeing. After the third time the same shape showed up, I stopped thinking of these as separate incidents.&lt;/p&gt;

&lt;p&gt;They're the same bug wearing different clothes.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://mehvetero.com/" rel="noopener noreferrer"&gt;https://mehvetero.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>bug</category>
      <category>crypto</category>
      <category>security</category>
    </item>
    <item>
      <title>I Built an Open-Source Security Linter for Sui Move — Here's What It Found on Real Protocols</title>
      <dc:creator>mehvetero</dc:creator>
      <pubDate>Fri, 24 Jul 2026 14:30:38 +0000</pubDate>
      <link>https://dev.to/mehvetero/i-built-an-open-source-security-linter-for-sui-move-heres-what-it-found-on-real-protocols-230o</link>
      <guid>https://dev.to/mehvetero/i-built-an-open-source-security-linter-for-sui-move-heres-what-it-found-on-real-protocols-230o</guid>
      <description>&lt;p&gt;I've been building &lt;a href="https://github.com/mehvetero/move-test-gen" rel="noopener noreferrer"&gt;move-test-gen&lt;/a&gt;, a security tool for Sui Move smart contracts. It started as a test generator and grew into a coverage checker with mutation testing and a security linter. This post covers what it does, what it found when I pointed it at production DeFi protocols, and how to use it in your own CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Sui's built-in linter has 6 rules — all focused on object handling (self-transfer, share-owned, etc.). Nothing about arithmetic overflow, missing access control, or unsafe type casting. &lt;a href="https://movebit.xyz/" rel="noopener noreferrer"&gt;MoveScanner&lt;/a&gt; exists but is closed-source and commercial.&lt;/p&gt;

&lt;p&gt;If you're writing Move and want a &lt;code&gt;slither&lt;/code&gt;-equivalent that checks security patterns on every PR — there isn't one. Or there wasn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  What move-test-gen Does
&lt;/h2&gt;

&lt;p&gt;Three layers, each independent:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1 — Assert pairing.&lt;/strong&gt; Scans every &lt;code&gt;assert!&lt;/code&gt; and &lt;code&gt;abort&lt;/code&gt; in your source modules, every &lt;code&gt;#[expected_failure]&lt;/code&gt; in your tests, and tells you which abort paths have no test coverage. Runs in seconds, needs only Node.js.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2 — Mutation testing.&lt;/strong&gt; Injects deterministic bugs (flip a &lt;code&gt;&amp;lt;&lt;/code&gt; to &lt;code&gt;&amp;gt;=&lt;/code&gt;, comment out an &lt;code&gt;assert!&lt;/code&gt;) and checks whether your test suite catches them. If a mutant survives, the test that should have caught it is too weak. 7 operators, exhaustive mode. Needs &lt;code&gt;sui&lt;/code&gt; CLI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 3 — Security lint.&lt;/strong&gt; 4 rules that scan for common vulnerability patterns:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;th&gt;What it catches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MOV-001&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HIGH&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;public fun&lt;/code&gt; with &lt;code&gt;&amp;amp;mut&lt;/code&gt; but no capability, key, or witness — missing access control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MOV-002&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HIGH&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;u64 * u64&lt;/code&gt; without &lt;code&gt;u128&lt;/code&gt; promotion — arithmetic overflow risk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MOV-003&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MEDIUM&lt;/td&gt;
&lt;td&gt;Division by a variable without prior zero-check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MOV-004&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MEDIUM&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;(expr as u64)&lt;/code&gt; downcast from u128/u256 without overflow assert&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MOV-002 and MOV-004 use a lightweight Move parser (&lt;code&gt;scripts/move-parser.mjs&lt;/code&gt;) that tracks variable types through declarations, casts, and naming conventions. So it knows that &lt;code&gt;numerator1&lt;/code&gt; assigned from &lt;code&gt;liquidity_u256 &amp;lt;&amp;lt; RESOLUTION&lt;/code&gt; is u256 — no false positive on the multiplication.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Found on Real Protocols
&lt;/h2&gt;

&lt;p&gt;I ran the linter against four production Sui DeFi protocols:&lt;/p&gt;

&lt;h3&gt;
  
  
  Kriya DEX — 1 finding
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔴 HIGH  spot_dex.move:187  [MOV-001]
  public function modifies state without capability check:
  `update_pool` takes &amp;amp;mut but no capability parameter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the same access control gap I &lt;a href="https://github.com/efficacy-finance/kriya-dex-interface/issues/2" rel="noopener noreferrer"&gt;reported manually&lt;/a&gt; in a 6-finding security review. The rule catches it in under a second on 3 source files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scallop Lending (172 files) — 2 findings
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔴 HIGH  liquidation_evaluator.move:143  [MOV-002]
  `LIQUIDATION_CAP_DIVISOR * debt_price_raw` — overflow risk

🟡 MEDIUM  limiter.move:189  [MOV-004]
  `(i as u64)` — downcast without overflow check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initial run hit 82 false positives. Generic functions &lt;code&gt;fun name&amp;lt;X, Y&amp;gt;()&lt;/code&gt; were invisible to the regex, test modules were scanned, Sui-specific patterns like &lt;code&gt;Witness&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code&gt;Version&lt;/code&gt; params weren't recognized. 9 commits to fix all of that. After hardening: these 2 legitimate findings, zero false positives on production code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bucket Protocol — Fix PR Submitted
&lt;/h3&gt;

&lt;p&gt;MOV-002 flagged three sites in &lt;code&gt;compute_collateral_value_to_buck&lt;/code&gt; and &lt;code&gt;compute_buck_value_to_collateral&lt;/code&gt; where &lt;code&gt;mul_factor&lt;/code&gt; (which safely promotes to u128 internally) is followed by a raw u64 multiplication that undoes the protection. I &lt;a href="https://github.com/Bucket-Protocol/v1-core/pull/12" rel="noopener noreferrer"&gt;submitted a fix PR&lt;/a&gt; — three lines changed, &lt;code&gt;u128&lt;/code&gt; intermediate cast.&lt;/p&gt;

&lt;p&gt;The overflow doesn't affect current deployments (SUI is 9 decimals = BUCK decimals, so the scaling factor is 1), but adding a collateral type with higher decimals would break liquidation, redemption, and health checks. The &lt;a href="https://github.com/Bucket-Protocol/Audit" rel="noopener noreferrer"&gt;OtterSec V1 audit&lt;/a&gt; caught a related conversion issue but not the overflow in the fix itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  One-liner (no install)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx mehvetero/move-test-gen sources tests &lt;span class="nt"&gt;--lint&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  In CI — GitHub Action
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/move-security.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;move-security&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;lint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mehvetero/move-test-gen@v1.3.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sources&lt;/span&gt;
          &lt;span class="na"&gt;tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tests&lt;/span&gt;
          &lt;span class="na"&gt;lint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Layer 1 + lint runs in seconds with zero dependencies. For mutation testing, add &lt;code&gt;mutate: 'true'&lt;/code&gt; and install &lt;code&gt;sui&lt;/code&gt; — there's a &lt;a href="https://github.com/mehvetero/move-test-gen/blob/main/examples/workflows/nightly-mutation.yml" rel="noopener noreferrer"&gt;nightly schedule example&lt;/a&gt; in the repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Marketplace:&lt;/strong&gt; &lt;a href="https://github.com/marketplace/actions/move-test-gen-coverage-check" rel="noopener noreferrer"&gt;move-test-gen coverage check&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  As an Agent Skill
&lt;/h3&gt;

&lt;p&gt;The tool is also an &lt;a href="https://github.com/agentskills/agentskills" rel="noopener noreferrer"&gt;agentskills&lt;/a&gt;-compatible skill. Install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add mehvetero/move-test-gen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then ask your coding agent: &lt;em&gt;"Generate edge-case tests for sources/vault.move"&lt;/em&gt; — it produces &lt;code&gt;#[test]&lt;/code&gt; and &lt;code&gt;#[expected_failure]&lt;/code&gt; functions covering boundary values, arithmetic overflows, access control violations, and economic edge cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent Skill listing:&lt;/strong&gt; &lt;a href="https://github.com/agentskills/agentskills/discussions/451" rel="noopener noreferrer"&gt;agentskills Discussion #451&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Compares
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;move-test-gen&lt;/th&gt;
&lt;th&gt;Sui built-in linter&lt;/th&gt;
&lt;th&gt;MoveScanner&lt;/th&gt;
&lt;th&gt;Aptos move-mutation-tools&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Open source&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security rules&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4 (MOV-001~004)&lt;/td&gt;
&lt;td&gt;0 (object-handling only)&lt;/td&gt;
&lt;td&gt;Many (undisclosed)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mutation testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes (7 operators)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (Aptos only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sui support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (Aptos only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CI integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GitHub Action&lt;/td&gt;
&lt;td&gt;CLI flag&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;CLI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parser&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lightweight (function-level types)&lt;/td&gt;
&lt;td&gt;Full compiler&lt;/td&gt;
&lt;td&gt;Full compiler&lt;/td&gt;
&lt;td&gt;Full compiler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Price&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Paid&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The gap this fills: there's no open-source tool that does security-focused static analysis on Sui Move. The built-in linter handles object patterns. MoveScanner is commercial. Aptos has mutation tools but they don't work on Sui. This tool covers the middle ground — regex + parser based, not compiler-grade, but catches the patterns that actually show up in exploits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measured, Not Trusted
&lt;/h2&gt;

&lt;p&gt;The tool has an eval lab (&lt;code&gt;eval/&lt;/code&gt;) with 5 campaigns, 13 scenarios, and 47 rounds of testing. Frozen prompt templates, retirement-by-saturation protocol, dated records. The methodology is borrowed from &lt;a href="https://github.com/TheColliery" rel="noopener noreferrer"&gt;TheColliery&lt;/a&gt; — full lineage in the &lt;a href="https://github.com/mehvetero/move-test-gen/blob/main/eval/RESULTS.md" rel="noopener noreferrer"&gt;RESULTS.md&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The lint rules were validated against Kriya DEX, Scallop (172 files), Bucket Protocol, and Turbos CLMM. Every false positive fix gets a selftest pin before it ships — 11 regression cases, all enforced in CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;More rules — clock staleness, upgrade policy, shared object access patterns&lt;/li&gt;
&lt;li&gt;Parser improvements — tracking types through function calls, not just declarations&lt;/li&gt;
&lt;li&gt;More protocol scans — each scan either validates the rules or finds a new pattern to handle&lt;/li&gt;
&lt;li&gt;Contribution: if you have a Move security pattern that should be a rule, &lt;a href="https://github.com/mehvetero/move-test-gen/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt; or PR a &lt;code&gt;rules/mov-NNN-*.mjs&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/mehvetero/move-test-gen" rel="noopener noreferrer"&gt;github.com/mehvetero/move-test-gen&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Marketplace:&lt;/strong&gt; &lt;a href="https://github.com/marketplace/actions/move-test-gen-coverage-check" rel="noopener noreferrer"&gt;move-test-gen coverage check&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Blog:&lt;/strong&gt; &lt;a href="https://mehvetero.com" rel="noopener noreferrer"&gt;mehvetero.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>testing</category>
      <category>tools</category>
      <category>move</category>
    </item>
  </channel>
</rss>
