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

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.

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.