<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Sentinelleai Research]]></title><description><![CDATA[Real exploits, replayed on forks. What broke, the proof, and the invariant that would have held.]]></description><link>https://sentinelleai.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 31 Oct 2026 01:16:03 GMT</lastBuildDate><atom:link href="https://sentinelleai.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[One second of block time: replaying the Fence Finance TranchePool drain]]></title><description><![CDATA[Same calldata, same state: the draw reverts at 23:59:59 and empties the pool at 00:00:00.]]></description><link>https://sentinelleai.hashnode.dev/one-second-of-block-time-replaying-the-fence-finance-tranchepool-drain</link><guid isPermaLink="true">https://sentinelleai.hashnode.dev/one-second-of-block-time-replaying-the-fence-finance-tranchepool-drain</guid><category><![CDATA[Security]]></category><category><![CDATA[Web3]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[defi]]></category><dc:creator><![CDATA[Sentinelleai]]></dc:creator><pubDate>Wed, 28 Oct 2026 17:11:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae21af4b0c4d597690e7af2/98ad0718-2f2d-49d7-b93e-3f752568d9c3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>On 27–28 October 2026, a TranchePool on Avalanche associated with Fence Finance, Valinor Digital and Oatfi lost 1,013,353 USDC, its entire balance. Within hours two readings circulated. Blockaid described flash-loan minting of inflated tranche shares. SlowMist described a settlement bug at the UTC day rollover. We wanted to know which one the chain agrees with, so we replayed the attack on a local fork.</p>
<h2>The two transactions</h2>
<p>At 23:56:41 UTC the attacker's contract took a flash loan of 1,013,353.61 USDC from Aave v3. That is exactly the pool's balance. Inside the callback it made five calls to the pool. One of them carries the same amount and a timestamp, <code>0x6ae13b00</code>, which is 00:00:00 UTC on 28 October. The last call draws the full amount back out. Aave is repaid with a 506 USDC premium. After this transaction the pool still holds its 1,013,353 USDC. Nothing has been stolen yet.</p>
<p>At 00:00:03 UTC, three seconds after midnight, the attacker called the same draw again. This time the pool paid out its real USDC.</p>
<h2>The fork replay</h2>
<img src="https://cdn.hashnode.com/uploads/covers/6ae21af4b0c4d597690e7af2/5cbb81d7-48a9-4602-bf84-4499c3930611.png" alt="" style="display:block;margin:0 auto" />

<p>We forked Avalanche C-Chain at the block just before midnight and impersonated the attacker. Then we sent the second transaction's exact calldata twice, changing only the block time:</p>
<ul>
<li>at 23:59:59 the draw reverts;</li>
<li>at 00:00:00 it succeeds and the pool goes from 1,013,353.61 USDC to zero.</li>
</ul>
<p>A fork at the real block, 00:00:03, reproduces the on-chain result to the unit.</p>
<p>The same state and the same calldata were used in every run. The only variable is the clock, so the draw check depends on time. That fits SlowMist's description: at the rollover, the pool's settlement logic treated the scheduled principal payment as received and subtracted it from outstanding principal, although no USDC had arrived. The draw check, funded principal minus outstanding principal, then reported a full line of capacity again.</p>
<h2>Who was right</h2>
<p>Both readings were partly right. A flash loan was used, but only to set the position up without putting capital at risk. The theft itself is the rollover: a due date was booked as a payment.</p>
<h2>The lesson for anyone building credit or vault logic</h2>
<img src="https://cdn.hashnode.com/uploads/covers/6ae21af4b0c4d597690e7af2/72ae1814-3934-4693-9a18-91cd0a6c0508.png" alt="" style="display:block;margin:0 auto" />

<p>Real-world credit brings real-world conventions on-chain: due dates, day counts, waterfalls. Each of them is a place where state can move without money moving. The invariant that holds the line is short:</p>
<blockquote>
<p>Draw capacity can never exceed the assets the contract has actually received.</p>
</blockquote>
<p>Write it down, then fuzz it across the boundaries where time changes the answer:</p>
<ul>
<li>the day rollover;</li>
<li>a due date equal to the current block timestamp;</li>
<li>a due date chosen by the caller.</li>
</ul>
<h2>Status</h2>
<p>Fence has sent the attacker an on-chain message offering a 10% recovery bounty for the return of 90% of the funds.</p>
<p><em>Addresses and transactions:</em></p>
<ul>
<li>pool <code>0xe25cb545bdd47a8ec2d08001cb5661b00d47621a</code></li>
<li>attacker <code>0x6ff1c3ffa622e1062168f3497a275be9cc2b1246</code></li>
<li>setup tx <code>0x19ab145e7f771549ca2f1da4b1768d7f728834015b2480ddb5be19c9769899fd</code></li>
<li>drain tx <code>0x3f6b23292a2e9c2956c7ac56d0b1f79b0271b49edec2156d2675ddf1de058c77</code></li>
</ul>
<p>The pool's source is not verified on the explorers, so this analysis rests on calldata, events, balances and the fork replay.</p>
<p><em>Sentinelleai reads smart contracts before launch, fuzzes declared invariants, and watches deployed contracts after.</em> <a href="https://sentinelleai.app/for/web3?utm_source=devto&amp;utm_medium=article&amp;utm_campaign=fence"><em>See how it works for Web3 teams.</em></a></p>
]]></content:encoded></item></channel></rss>