DEV Community

Cover image for One second of block time: replaying the Fence Finance TranchePool drain
Sentinelleai
Sentinelleai

Posted on Originally published at Medium

One second of block time: replaying the Fence Finance TranchePool drain

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.

The two transactions

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, 0x6ae13b00, 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.

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.

The fork replay

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:

  • at 23:59:59 the draw reverts;
  • at 00:00:00 it succeeds and the pool goes from 1,013,353.61 USDC to zero.

A fork at the real block, 00:00:03, reproduces the on-chain result to the unit.

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.

Who was right

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.

The lesson for anyone building credit or vault logic

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:

Draw capacity can never exceed the assets the contract has actually received.

Write it down, then fuzz it across the boundaries where time changes the answer:

  • the day rollover;
  • a due date equal to the current block timestamp;
  • a due date chosen by the caller.

Status

Fence has sent the attacker an on-chain message offering a 10% recovery bounty for the return of 90% of the funds.

Addresses and transactions:

  • pool 0xe25cb545bdd47a8ec2d08001cb5661b00d47621a
  • attacker 0x6ff1c3ffa622e1062168f3497a275be9cc2b1246
  • setup tx 0x19ab145e7f771549ca2f1da4b1768d7f728834015b2480ddb5be19c9769899fd
  • drain tx 0x3f6b23292a2e9c2956c7ac56d0b1f79b0271b49edec2156d2675ddf1de058c77

The pool's source is not verified on the explorers, so this analysis rests on calldata, events, balances and the fork replay.

Sentinelleai reads smart contracts before launch, fuzzes declared invariants, and watches deployed contracts after. See how it works for Web3 teams.

Top comments (1)

Collapse
 
cryptostrapon profile image
cryptostrapon •

The replay makes the point well: the flash loan only staged the position, and the real loss came from a due date being booked as a payment at the day rollover, so draw capacity grew without any USDC arriving. The invariant that draw capacity never exceeds assets actually received is the right one to fuzz. I would add one more check on the same boundary, which is whether the settlement path can be called twice in the same block around midnight, since a second booking of the same due date would stack on top of the first.