Calculated from recorded token losses using historical USD prices at the incident time.
0x9f2eb13417190e5139d57821422fc99bced025f24452a8b31f7d68133c9b0a6c0xe8a290c6fc6fa6c0b79c9cfae1878d195aeb59afBSC0x73cb8b07ebe2b3678c8ded169c01be6f7eb8d434BSCOn BNB Chain block 37795992, a public attacker transaction exploited FIL314's ERC314 token logic to drain the token contract's BNB reserve. The attacker bought a small FIL314 position with 0.05 BNB, called hourBurn() 6000 times, then repeatedly sold through transfer(address(token), amount). The root cause is that hourBurn() is publicly callable, uses an inverted time guard, and directly burns the token-side reserve used by the pricing formula. That reserve collapse makes subsequent sells massively overpay BNB. The token contract lost 20.177026436859275769 BNB in the transaction, and the exploit sender realized a net gain of 13.323361467421079278 BNB.
FIL314 implements a custom ERC314-style AMM inside the token contract instead of using an external pool. The token contract keeps an internal BNB reserve in ethBalance and a token reserve in _balances[address(this)]; getAmountOut() prices buys and sells from those two values. The constructor allocates 120000000 * 10^9 FIL314 to the token contract, 880000000 * 10^9 to the owner, and the remaining supply to the dividend tracker at 0x73cb8b07ebe2b3678c8ded169c01be6f7eb8d434. Public sells are routed by calling transfer(address(this), amount), which enters . The sell path also interacts with the dividend tracker and market address, so broken accounting in those branches can move both FIL314 and BNB across protocol-owned balances.
sell(uint256)The vulnerability is a public reserve-manipulation flaw in FIL314's internal AMM. In the verified source, hourBurn() is callable by anyone and returns only when hourBurnTime + 3600 < block.timestamp, which is the opposite of the intended cooldown. Because the contract initializes hourBurnTime to the deployment timestamp and each successful burn adds another 3600 seconds, the condition stays false inside one transaction and the attacker can call the function repeatedly. Each call reduces _balances[address(this)], which is the exact token reserve consumed by getAmountOut(), buy(), and sell(). Once the token reserve is crushed, the sell pricing formula pays far more BNB per FIL314 than it should. The attacker then uses the public sell path to drain BNB from the token contract while the broken post-sell tracker settlement pulls FIL314 inventory from the dividend tracker back into the pool. The first decisive code breakpoint is therefore the unrestricted reserve burn in hourBurn().
The verified FIL314 source contains the decisive defect:
function hourBurn() public {
if (hourBurnTime + 3600 < block.timestamp) {
return;
}
hourBurnTime = hourBurnTime + 3600;
uint256 burnHour = _balances[address(this)] * 2500 / 1000000;
_balances[address(this)] = _balances[address(this)] - burnHour;
emit Transfer(address(this), address(0), burnHour);
}
This function should have blocked repeated burns until enough time elapsed, but the inequality is inverted. Instead of rejecting early calls, it permits them while the deadline is still in the future. Because the function also increments hourBurnTime by another hour on every successful call, a caller can keep extending the window and burn reserve tokens repeatedly inside the same transaction.
The pricing dependency is explicit in the victim code:
function getAmountOut(uint256 value, bool _buy) public view returns (uint256) {
(uint256 reserveETH, uint256 reserveToken) = getReserves();
if (_buy) {
return (value * reserveToken) / (reserveETH + value);
} else {
return (value * reserveETH) / (reserveToken + value);
}
}
reserveToken is _balances[address(this)]. Burning that balance without removing BNB from ethBalance breaks the AMM invariant that quoted payouts must be backed by a balanced reserve state. The trace confirms the exploit exercised this exact path: first a receive{value: 50000000000000000} buy, then 6000 consecutive ERC314::hourBurn() calls, and only after that the repeated getAmountOut(..., true) plus transfer(ERC314, amount) sell sequence.
The sell path then turns the manipulated price into reserve loss:
uint256 ethAmount = (swap_amount * ethBalance) / (_balances[address(this)] + swap_amount);
ethBalance -= ethAmount;
_transfer(msg.sender, address(this), swap_amount);
payable(msg.sender).transfer(amountOutput);
_transfer(address(dividendTracker), address(this), sell_amount);
Once _balances[address(this)] has been collapsed, the computed ethAmount becomes abnormally large. The post-sell transfer from the dividend tracker lets the exploit keep sourcing FIL314 inventory during the drain. The balance diff artifact shows the token contract's native balance falling from 20854000000000000000 wei to 676973563140724231 wei, a loss of 20177026436859275769 wei, while the sender gains 13323361467421079278 wei net.
The exploit is fully contained in transaction 0x9f2eb13417190e5139d57821422fc99bced025f24452a8b31f7d68133c9b0a6c on BNB Chain. The EOA 0x4645863205b47a0a3344684489e8c446a437d66c called executor 0xde521fbbbb0dbcfa57325a9896c34941f23e96a0, which created helper 0x5c01b97299b32baf75b4940fdae158656c231847 during the transaction.
The on-chain flow is:
1. Helper buys FIL314 with 0.05 BNB through ERC314 receive/fallback.
2. Helper calls ERC314::hourBurn() 6000 times.
3. Helper repeatedly queries ERC314::getAmountOut(token.ethBalance(), true).
4. Helper repeatedly sells with ERC314::transfer(address(token), amount).
5. Executor forwards proceeds and the sender ends with a positive BNB delta.
The collected trace shows the first buy, the large hourBurn() burst, and the later sell loop in order. The balance diff and trace tail also show the reserve depletion and profit realization. No privileged role, private key, or attacker-specific off-chain artifact is required; the path uses public entrypoints and public state only.
The measurable protocol loss is the BNB removed from the FIL314 token contract reserve:
{
"token_symbol": "BNB",
"amount": "20177026436859275769",
"decimal": 18
}
Before the exploit, the token contract held 20.854 BNB. After the exploit, only 0.676973563140724231 BNB remained. The exploit sender finished the transaction with a net native balance increase of 13.323361467421079278 BNB, while the tracker and market-side recipients also received BNB as part of FIL314's broken accounting flow.
0x9f2eb13417190e5139d57821422fc99bced025f24452a8b31f7d68133c9b0a6c0xe8a290c6fc6fa6c0b79c9cfae1878d195aeb59af0x73cb8b07ebe2b3678c8ded169c01be6f7eb8d434Contract.sol