Calculated from recorded token losses using historical USD prices at the incident time.
0x09925028ce5d6a54801d04ff8f39e79af6c24289e84b301ddcdb6adfa51e901b0x1954b6bd198c29c3ecf2d6f6bc70a4d41ea1cc07BSC0x6db8209c3583e7cecb01d3025c472d1eddbe49f3BSCOn BNB Smart Chain block 25430419, transaction 0x09925028ce5d6a54801d04ff8f39e79af6c24289e84b301ddcdb6adfa51e901b drained the FIREDRAKE/WBNB PancakeSwap pair at 0x6db8209c3583e7cecb01d3025c472d1eddbe49f3. The adversary EOA 0xc726bd0e973722e17eb088b8fcfedaa931fa0293 called exploit contract 0xe02970bd38b283c3079720c1e71001abe001bc83, which borrowed WBNB from DODO pool 0xfeafe253802b77456b4627f8c2306a9cebb5d681, bought FIREDRAKE, called FIREDRAKE.deliver(uint256), and then used the reflection-created phantom FIREDRAKE balance to pull out nearly all WBNB from the pair.
The root cause is a reflection-accounting flaw in FIREDRAKE 0x1954b6bd198c29c3ecf2d6f6bc70a4d41ea1cc07, not the flash loan itself. deliver() is publicly callable by any non-excluded holder, directly reduces _rTotal, and therefore changes the global reflection rate. Because the PancakeSwap pair was not excluded from reflections, balanceOf(pair) increased without any transfer into the pair. PancakePair then treated that synthetic balance increase as real token input and released WBNB. The attacker cluster retained 16179364280405492225 wei of WBNB before gas and 16161900440405492225 wei net after the sender EOA's gas cost.
FIREDRAKE is a reflection token. For non-excluded holders, balances are not stored directly in token units. Instead, the token stores reflected balances in , derives a global rate from , and computes user-visible balances as .
_rOwned_rTotalrOwned / currentRateThe verified FIREDRAKE source shows the relevant mechanism:
function balanceOf(address account) public view override returns (uint256) {
if (_isExcluded[account]) return _tOwned[account];
return tokenFromReflection(_rOwned[account]);
}
function deliver(uint256 tAmount) public {
address sender = _msgSender();
require(!_isExcluded[sender], "Excluded addresses cannot call this function");
(uint256 rAmount,,,,,) = _getValues(tAmount);
_rOwned[sender] = _rOwned[sender].sub(rAmount);
_rTotal = _rTotal.sub(rAmount);
_tFeeTotal = _tFeeTotal.add(tAmount);
}
function tokenFromReflection(uint256 rAmount) public view returns(uint256) {
uint256 currentRate = _getRate();
return rAmount.div(currentRate);
}
At block 25430418, the pair was still reflection-participating and had live reserves. Independent chain queries show FIREDRAKE.isExcluded(pair) == false, FIREDRAKE.totalFees() == 0, and getReserves() for the pair returned 99995897288241500535820 FDP and 16326701609462839506 wei WBNB before the exploit tx. This matters because UniswapV2-style pairs infer how much token input arrived by comparing stored reserves with token.balanceOf(pair). That assumption fails if a token balance can change without a transfer.
The vulnerability category is ATTACK. Any FIREDRAKE holder could call deliver() and mutate the global reflection state. That operation reduced _rTotal for the whole reflected-holder set, which lowered currentRate and increased every untouched non-excluded holder's apparent token balance. The FDP/WBNB Pancake pair remained in that reflected-holder set, so its observable FIREDRAKE balance became larger even though no FIREDRAKE transfer entered the pair after the attacker bought tokens. The pair's reserve accounting invariant was therefore broken: the token balance used inside swap() no longer matched the economically received token input since the last reserve update. Once that invariant broke, PancakePair released WBNB against phantom input. The flash loan only supplied temporary capital and was not the vulnerability.
The invariant that failed is: for the FIREDRAKE/WBNB pair, the token balance observed during swap() must equal the amount of FIREDRAKE that actually arrived in the pair since the last reserve update. FIREDRAKE violated that assumption because deliver() changes _rTotal globally while the pair remained non-excluded.
The code-level breakpoint is FIREDRAKE deliver() together with reflected-balance conversion in balanceOf() and tokenFromReflection(). _getCurrentSupply() derives the reflection rate from _rTotal, so decreasing _rTotal changes every non-excluded holder's displayed balance:
function _getCurrentSupply() private view returns(uint256, uint256) {
uint256 rSupply = _rTotal;
uint256 tSupply = _tTotal;
for (uint256 i = 0; i < _excluded.length; i++) {
if (_rOwned[_excluded[i]] > rSupply || _tOwned[_excluded[i]] > tSupply) return (_rTotal, _tTotal);
rSupply = rSupply.sub(_rOwned[_excluded[i]]);
tSupply = tSupply.sub(_tOwned[_excluded[i]]);
}
if (rSupply < _rTotal.div(_tTotal)) return (_rTotal, _tTotal);
return (rSupply, tSupply);
}
The seed trace shows the exact exploit sequence. First, the adversary borrowed 1363426920555815103015 wei of WBNB from the DODO pool, but only routed 16326701609462839506 wei of WBNB through PancakeRouter to buy 49935372988746381368951 FDP. After that buy, the pair reserve snapshot was 50060579699495119166869 FDP and 32653403218925679012 wei WBNB. The attacker then called deliver(28463162603585437380302), which raised totalFees and changed the pair's observed FDP balance to 11122277578830245110611430 without any intervening FDP transfer into the pair:
0x1954...CC07::deliver(28463162603585437380302)
...
0x6db8...49F3::getReserves() -> 50060579699495119166869 FDP, 32653403218925679012 WBNB
0x1954...CC07::balanceOf(0x6db8...49F3) -> 11122277578830245110611430
0x10ED...024E::getAmountsOut(11072216999130749991444561, [FDP, WBNB]) -> 32506065889868331731
The difference between the pair's observed balance and stored reserve was 11072216999130749991444561 FDP. PancakeRouter priced that phantom balance as enough input to withdraw 32506065889868331731 wei WBNB. The final swap() emitted a Sync event showing the pair left with only 147337329057347281 wei WBNB, confirming that the pair treated the reflection-created balance jump as real input.
The exploit conditions were fully permissionless:
The adversary cluster consisted of the sender EOA 0xc726bd0e973722e17eb088b8fcfedaa931fa0293, main exploit contract 0xe02970bd38b283c3079720c1e71001abe001bc83, and helper contract 0x01b2c773b37483d280366ddf48097d79d68cdb37.
The on-chain flow was:
0xe029...bc83 borrowed WBNB from DODO pool 0xfeafe253802b77456b4627f8c2306a9cebb5d681.0x10ed43c718714eb63d5aa57b78b54704e256024e and swapped 16326701609462839506 wei WBNB for 49935372988746381368951 FDP.FIREDRAKE.deliver(28463162603585437380302), reducing _rTotal and inflating balanceOf(pair).pair.swap(0, 32506065889868331731, helper, "") and received 32506065889868331731 wei WBNB from the pair.16326701609462839506 wei WBNB so the main exploit contract could complete repayment, and retained 16179364280405492225 wei WBNB as profit before gas.The decisive trace segment is:
WBNB::transferFrom(exploit, pair, 16326701609462839506)
pair::swap(49935372988746381368951, 0, exploit, 0x)
FIREDRAKE::deliver(28463162603585437380302)
FIREDRAKE::balanceOf(pair) -> 11122277578830245110611430
pair::swap(0, 32506065889868331731, helper, 0x)
emit Sync(11122277578830245110611430, 147337329057347281)
This flow matches the ACT framing exactly: every step was adversary-crafted, every dependency was public, and the success predicate was direct WBNB profit.
The measurable loss was borne by the FDP/WBNB PancakeSwap pair. The pair lost 16179364280405492225 wei of WBNB reserve value, equivalent to 16.179364280405492225 WBNB, and was left with only 147337329057347281 wei WBNB. The sender EOA also paid 17463840000000000 wei of native gas, so the net cluster profit after gas was 16161900440405492225 wei WBNB-equivalent.
The exploit also corrupted the market's internal price integrity. After the drain, the pair held an enormous reflected FIREDRAKE balance while essentially no WBNB remained, so the market could no longer represent a sane FDP/WBNB price.
0x09925028ce5d6a54801d04ff8f39e79af6c24289e84b301ddcdb6adfa51e901b0x1954b6bd198c29c3ecf2d6f6bc70a4d41ea1cc070x6db8209c3583e7cecb01d3025c472d1eddbe49f30x10ed43c718714eb63d5aa57b78b54704e256024e0xfeafe253802b77456b4627f8c2306a9cebb5d681/workspace/session/artifacts/collector/seed/56/0x09925028ce5d6a54801d04ff8f39e79af6c24289e84b301ddcdb6adfa51e901b/metadata.json/workspace/session/artifacts/collector/seed/56/0x09925028ce5d6a54801d04ff8f39e79af6c24289e84b301ddcdb6adfa51e901b/trace.cast.log/workspace/session/artifacts/collector/seed/56/0x09925028ce5d6a54801d04ff8f39e79af6c24289e84b301ddcdb6adfa51e901b/balance_diff.json/workspace/session/artifacts/collector/seed/56/0x1954b6bd198c29c3ecf2d6f6bc70a4d41ea1cc07/src/Contract.sol