<?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>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>
