BSC • Address: 0xAD94…0449
Published on June 12, 2025
A frantic builder pinged us after their MEV bot was wiped. Decompiling the bytecode told the story: wide-open msg.sender.call() calls and token transfers that trusted whoever knocked on the door. Together they let an attacker empty both ETH and tokens in a single sweep — a textbook reminder that speed without guardrails is costly.
We pulled the drained bot into EVMDecompiler and stepped through the reconstructed callbacks. The bytecode (no published source) made two facepalm-level mistakes:
msg.sender.call{value: amount}("" ) with zero validation or recipient sanity checks.msg.sender without any notion of authorization.function d3MMSwapCallback(address _token, uint256 _amount, bytes calldata) external {
IERC20(_token).transfer(msg.sender, _amount);
}
function swapX2YCallback(uint256 amountX, uint256, bytes calldata data) external {
require(amountX <= 0 || amountX == 0);
(bool success, bytes memory result) = msg.sender.call{value: amountX}("");
require(success, "SwapX2Y: ERC20 operation did not succeed");
}
function swapCallback(uint256 amount0, uint256 amount1, bytes calldata data) external override {
_swapCallback(msg.sender, amount0, amount1, data);
}msg.sender.call hands both control and value to whoever is calling. No whitelist, no invariants — just free money and a nice way to brick funds if the fallback reverts._amount straight into the caller's wallet with no checks means any token balance the bot holds is a short-lived gift.address.call for value — use guarded withdrawal patterns instead.Analysis comes straight from the decompiled bytecode; we renamed functions so the narrative is easier to follow.