FIREDRAKE Reflection Drain on PancakeSwap
Exploit Transactions
0x09925028ce5d6a54801d04ff8f39e79af6c24289e84b301ddcdb6adfa51e901bVictim Addresses
0x1954b6bd198c29c3ecf2d6f6bc70a4d41ea1cc07BSC0x6db8209c3583e7cecb01d3025c472d1eddbe49f3BSCLoss Breakdown
Similar Incidents
OceanLife Reflection Drain on PancakeSwap
56%BEVO reflection accounting lets anyone manufacture PancakeSwap input and drain WBNB
48%BUNN Reflection Drain via PancakePair
47%BIGFI Burn Bug Drains PancakeSwap
43%Sheep Burn Reserve Drain
37%SOF Sell-Hook Reserve Manipulation Drains PancakeSwap V2 USDT Liquidity
35%Root Cause Analysis
FIREDRAKE Reflection Drain on PancakeSwap
1. Incident Overview TL;DR
On 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.
2. Key Background
FIREDRAKE is a reflection token. For non-excluded holders, balances are not stored directly in token units. Instead, the token stores reflected balances in _rOwned, derives a global rate from _rTotal, and computes user-visible balances as rOwned / currentRate.
The 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.
3. Vulnerability Analysis & Root Cause Summary
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.
4. Detailed Root Cause Analysis
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 attacker only needed to acquire some FIREDRAKE, which the public pair supplied.
- The pair had to remain non-excluded from reflections.
- The pair needed WBNB liquidity to drain.
- Public router, pair, DODO flash loan, and token entrypoints were sufficient; no privileged role or private order flow was required.
5. Adversary Flow Analysis
The adversary cluster consisted of the sender EOA 0xc726bd0e973722e17eb088b8fcfedaa931fa0293, main exploit contract 0xe02970bd38b283c3079720c1e71001abe001bc83, and helper contract 0x01b2c773b37483d280366ddf48097d79d68cdb37.
The on-chain flow was:
0xe029...bc83borrowed WBNB from DODO pool0xfeafe253802b77456b4627f8c2306a9cebb5d681.- The exploit contract approved PancakeRouter
0x10ed43c718714eb63d5aa57b78b54704e256024eand swapped16326701609462839506wei WBNB for49935372988746381368951FDP. - The exploit contract called
FIREDRAKE.deliver(28463162603585437380302), reducing_rTotaland inflatingbalanceOf(pair). - Using the helper contract, the adversary called
pair.swap(0, 32506065889868331731, helper, "")and received32506065889868331731wei WBNB from the pair. - The helper returned
16326701609462839506wei WBNB so the main exploit contract could complete repayment, and retained16179364280405492225wei 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.
6. Impact & Losses
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.
7. References
- Seed transaction:
0x09925028ce5d6a54801d04ff8f39e79af6c24289e84b301ddcdb6adfa51e901b - Victim token:
0x1954b6bd198c29c3ecf2d6f6bc70a4d41ea1cc07 - Victim pair:
0x6db8209c3583e7cecb01d3025c472d1eddbe49f3 - PancakeRouter:
0x10ed43c718714eb63d5aa57b78b54704e256024e - DODO flash-loan pool:
0xfeafe253802b77456b4627f8c2306a9cebb5d681 - Seed metadata:
/workspace/session/artifacts/collector/seed/56/0x09925028ce5d6a54801d04ff8f39e79af6c24289e84b301ddcdb6adfa51e901b/metadata.json - Seed trace:
/workspace/session/artifacts/collector/seed/56/0x09925028ce5d6a54801d04ff8f39e79af6c24289e84b301ddcdb6adfa51e901b/trace.cast.log - Seed balance diff:
/workspace/session/artifacts/collector/seed/56/0x09925028ce5d6a54801d04ff8f39e79af6c24289e84b301ddcdb6adfa51e901b/balance_diff.json - Verified FIREDRAKE source:
/workspace/session/artifacts/collector/seed/56/0x1954b6bd198c29c3ecf2d6f6bc70a4d41ea1cc07/src/Contract.sol