MicDao Mixed-Price Sale Exploit
Exploit Transactions
0x24a2fbb27d433d91372525954f0d7d1af7509547b9ada29cc6c078e732c6d075Victim Addresses
0x19345233ea7486c1d5d780a19f0e303597e480b5BSC0x928902c2499bd6d123f455b1de93f1a139ef9b00BSCLoss Breakdown
Similar Incidents
DKP Exchange Flash-Price Exploit
38%MaraToken mispriced sale drains WBNB from liquidity pool
35%Local Traders Price Takeover
34%SHIBA Lock Bypass
33%CS Pair Balance Burn Drain
33%UN Burn-Skim Exploit
33%Root Cause Analysis
MicDao Mixed-Price Sale Exploit
1. Incident Overview TL;DR
On BSC block 32711748, transaction 0x24a2fbb27d433d91372525954f0d7d1af7509547b9ada29cc6c078e732c6d075 used a public DODO flash loan to manipulate the MIC/USDT Pancake spot price and then abused MicDao sale contract 0x19345233ea7486c1d5d780a19f0e303597e480b5. The attacker pumped the pair with 500000 USDT, deployed 80 short-lived helper contracts, and had each helper buy 20000 MIC for 2000 USDT through swap(uint256,address). Because the sale contract used internal sale pricing for the buyer payout but live Pancake reserves for the contract's own liquidity contribution, each helper drained far more MIC than the sale contract economically priced. The attacker then sold the accumulated MIC back into the same pair, repaid the flash loan, and left the adversary profit path with approximately 12235.153972865412371137 USDT net of gas.
The root cause is a mixed-pricing bug in a public sale path. MicDao's sale contract priced the buyer leg with fixed sale math, but priced the contract-side liquidity leg with a flash-loan-manipulable Pancake spot price. That mismatch let an unprivileged actor extract MIC from the sale inventory while contributing only a tiny reserve-derived MIC amount to liquidity on each purchase.
2. Key Background
MicDao token 0xf6876f6ab2637774804b85aecc17b434a2b57168 has an additional transfer rule for sells into listed pairs. Its verified source shows that when the recipient is in pairList and the sender is not in isDelivers, 45% of the transferred MIC is burned before the pair receives the remainder:
function _transfer(address sender, address recipient, uint256 amount) internal override {
if (pairList[recipient] && !isDelivers[sender]) {
uint256 toBurn = amount.mul(45).div(100);
super._transfer(sender, address(1), toBurn);
amount = amount.sub(toBurn);
}
super._transfer(sender, recipient, amount);
}
This matters because the attacker's final dump burned 797280.896553005568769367 MIC and delivered only 974454.429120340139607005 MIC into the pair, exactly matching the balance diff.
The sale contract is unverified, but the trace exposes the relevant public selectors and behavior. The sale path exposes swap(uint256,address), tracks per-caller usage through users(address), and enforces a public maxUSDT() cap of 2000e18. The cap is keyed only by caller address, so a permissionless attacker can bypass it by deploying fresh helper contracts.
The venue used for the manipulated price was Pancake pair 0x928902c2499bd6d123f455b1de93f1a139ef9b00. Its verified source exposes getReserves() and uses the reserve balances as the active price basis for swaps and liquidity operations:
function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
_reserve0 = reserve0;
_reserve1 = reserve1;
_blockTimestampLast = blockTimestampLast;
}
Because those reserves can be moved inside the same transaction, any contract that trusts the instantaneous pair ratio as an oracle is exposed to flash-loan manipulation.
3. Vulnerability Analysis & Root Cause Summary
This is an ATTACK-category ACT exploit against MicDao's public sale path. The vulnerability is not a privileged-access issue and does not rely on private keys or hidden state. It arises because swap(uint256,address) uses two inconsistent price sources for one economic action.
The buyer-facing leg is fixed: a 2000 USDT purchase transfers 20000 MIC to the caller. The contract-facing leg is floating: before routing the same 2000 USDT into Pancake liquidity, the sale path reads router.factory(), factory.getPair(token, usdt), pair.token0(), and pair.getReserves(), then computes how much MIC the sale contract should contribute from the live pair ratio. After the attacker pumps the pair, that reserve ratio implies that 2000 USDT only requires 23.6904520522176055 MIC from the sale contract.
The broken invariant is straightforward: the MIC sent to the buyer and the MIC contributed by the sale contract for the same purchase must be priced from the same trusted basis. MicDao instead paid out MIC using fixed sale math while settling the contract-side liquidity leg using a manipulable external spot. That is the code-level breakpoint that makes the exploit profitable.
4. Detailed Root Cause Analysis
The seed metadata shows that tx 0x24a2fbb27d433d91372525954f0d7d1af7509547b9ada29cc6c078e732c6d075 was sent by EOA 0xcd03ed98868a6cd78096f116a4b56a5f2c67757d to orchestrator 0x502b4a51ca7900f391d474268c907b110a277d6f in block 32711748. The trace then shows a public DODO flash loan from pool 0x26d0c625e5f5d6de034495fbde1f6e9377185618:
0x26d0c625e5F5D6de034495fbDe1F6e9377185618::flashLoan(
0,
670900962208306651912728,
0x502b4A51ca7900F391d474268C907B110a277d6F,
0x30783030
)
Inside the callback, the attacker spent 500000e18 USDT to buy MIC on Pancake. The trace shows the pair paying out 171735.325673345708376372 MIC to the orchestrator, which moved the pair from its normal state to the manipulated reserve state used later by the sale contract.
The first helper purchase shows the bug clearly:
0x19345233ea7486c1D5d780A19F0e303597E480b5::swap(2000000000000000000000, 0x502b4A51...)
MicDao::transfer(0x8124974bA43E..., 20000000000000000000000)
PancakeFactory::getPair(MicDao, BEP20USDT)
PancakePair::getReserves() -> 647813106380890190341074, 7673492667757310999365
PancakeRouter::addLiquidity(
MicDao,
BEP20USDT,
23690452052217605500,
2000000000000000000000,
0,
0,
0x3f1AF63823e2FEa40c94Bd016d5a6637c66cae44,
1697634564
)
That one call transferred 20000e18 MIC to the helper but only paired 23.6904520522176055 MIC with the helper's 2000e18 USDT on the liquidity side. The sale contract therefore lost almost the full buyer allocation on every call. The same pattern repeated 80 times; the trace contains 80 swap(2000e18, ...) calls and 80 helper SELFDESTRUCT events, confirming that the attacker bypassed the per-address cap with one fresh helper per purchase.
The accounting closes exactly against the balance diff:
- Sale contract MIC delta:
-1601895236164177408440000, or-1601895.236164177408440000MIC. - Pair USDT delta:
-12260251676860937689229, matching the adversary extraction from the pool. - Sale contract USDT dust:
+4800, which equals80 * 60raw USDT units, matching the per-call liquidity-rounding remainder.
The MIC loss also matches the mixed-pricing math: 80 * 20000 MIC paid to buyers plus 80 * 23.6904520522176055 MIC paired into liquidity equals 1601895.23616417740844 MIC. This is the explicit invariant break. The sale contract kept issuing the buyer's MIC on fixed sale logic while its own liquidity contribution collapsed to a reserve-derived amount that the attacker had already manipulated upward with the flash loan.
5. Adversary Flow Analysis
The adversary flow is a single-transaction ACT sequence:
- The gas-paying EOA
0xcd03ed98868a6cd78096f116a4b56a5f2c67757dcalls orchestrator0x502b4a51ca7900f391d474268c907b110a277d6f. - The orchestrator borrows
670900.962208306651912728USDT from the public DODO pool. - The orchestrator spends
500000USDT on Pancake to push the MIC/USDT spot price sharply upward. - The orchestrator funds
80ephemeral helper contracts with2000USDT each. Each helper:- approves the sale contract,
- calls
swap(2000e18, ...), - receives
20000MIC, - returns the MIC to the orchestrator,
- self-destructs.
- During each
swap, the sale contract re-reads the manipulated Pancake reserves and adds liquidity with only23.6904520522176055MIC plus roughly2000USDT, leaving the buyer-side payout unchanged. - After all helpers finish, the orchestrator holds
1771735.325673345708376372MIC. - The orchestrator dumps that MIC back into Pancake. Because of MicDao's sell-to-pair burn rule,
797280.896553005568769367MIC is burned and974454.429120340139607005MIC reaches the pair. - Pancake pays out
672260.251676860937684429USDT. The orchestrator repays the DODO principal and transfers the residual12260.251676860937684429USDT to immediate profit recipient0xa5b92a7abebf701b5570db57c5d396622b6ed348.
Representative trace evidence for the liquidation leg is:
MicDao::transferFrom(0x502b4A51..., PancakePair, 1771735325673345708376372)
emit Transfer(... to 0x0000000000000000000000000000000000000001, 797280896553005568769367)
emit Transfer(... to PancakePair, 974454429120340139607005)
PancakePair::swap(672260251676860937684429, 0, 0x502b4A51..., 0x)
Everything in this sequence is permissionless and publicly callable, which is why the case is correctly classified as ACT.
6. Impact & Losses
The direct protocol-side inventory loss was 1601895.236164177408440000 MIC from sale contract 0x19345233ea7486c1d5d780a19f0e303597e480b5. The public MIC/USDT Pancake liquidity lost 12260.251676860937684429 USDT to the adversary cluster. The attacker also triggered a collateral token burn of 797280.896553005568769367 MIC during the terminal liquidation because MicDao burns 45% of sells into listed pairs from non-deliver addresses.
The loss summary used for session metadata is:
MIC: raw amount"1601895236164177408440000", decimal18USDT: raw amount"12260251676860937684429", decimal18
The measurable profit predicate is also satisfied. The immediate profit-recipient address gained 12260.251676860937684429 USDT, and after converting the seed transaction's 0.118065926125134867 BNB gas cost into USDT using same-block Pancake reserves, the adversary cluster remained net positive by approximately 12235.153972865412371137 USDT.
7. References
- Seed transaction metadata:
0x24a2fbb27d433d91372525954f0d7d1af7509547b9ada29cc6c078e732c6d075, block32711748, sender0xcd03ed98868a6cd78096f116a4b56a5f2c67757d, recipient0x502b4a51ca7900f391d474268c907b110a277d6f. - Seed opcode trace for the full exploit sequence, including the DODO flash loan, Pancake buy, repeated
swap(2000e18, ...)calls, reserve reads,addLiquidity, helper self-destruction, final dump, and repayment. - Seed balance diff showing the sale contract MIC loss, pair USDT loss, burn-address MIC gain, sale-contract USDT dust, and profit-recipient USDT gain.
- Verified MicDao token source at
0xf6876f6ab2637774804b85aecc17b434a2b57168, especially_transfer,pairList, andisDelivers. - Verified PancakePair source at
0x928902c2499bd6d123f455b1de93f1a139ef9b00, especiallygetReserves()and liquidity accounting. - Public DODO pool
0x26d0c625e5f5d6de034495fbde1f6e9377185618, which exposes the flash-loan primitive used in the seed transaction. - MicDao sale contract
0x19345233ea7486c1d5d780a19f0e303597e480b5, whose trace-visibleswap(uint256,address)path mixed fixed sale pricing with manipulated reserve pricing.