Calculated from recorded token losses using historical USD prices at the incident time.
0x6f1af27d08b10caa7e96ec3d580bf39e29fd5ece00abda7d8955715403bf34a80x6bc9b4976ba6f8c9574326375204ee469993d038BSCOn BSC block 23467516, transaction 0x6f1af27d08b10caa7e96ec3d580bf39e29fd5ece00abda7d8955715403bf34a8 used a public DODO flash loan to buy GVC, then repeatedly sold 1 wei of SEAMAN into the SEAMAN/USDT pair to force SEAMAN’s token contract to dump its own fee inventory through SEAMAN -> USDT -> GVC. Those victim-funded buys pumped the GVC price, letting the attacker unwind the pre-bought GVC position for net USDT profit after loan repayment.
The root cause is that SEAMAN embeds treasury-spending swap logic directly inside permissionless sell flow. Any non-excluded seller sending SEAMAN to the pair can reach swapAndLiquifyV3() and swapAndLiquifyV1() before their own sell is processed, so an unprivileged adversary can choose when the protocol spends contract-held SEAMAN inventory into external markets.
SEAMAN is a token on BSC at 0x6bc9b4976ba6f8c9574326375204ee469993d038. Its verified source shows that transfer-time fee handling accumulates inventory on the token contract itself and tracks convertible balances through lpAmount and hAmount. That accumulated inventory is later swapped through PancakeSwap.
Two PancakeSwap pools matter for this incident:
SEAMAN/USDT at 0x6637914482670f91f43025802b6755f27050b0a6GVC/USDT at 0x84d26ad5f7d12ae783fee433fd0d2b2b1a7e9260The adversary also used public DODO liquidity and the Pancake router. No privileged role, admin method, or private artifact was required; the opportunity depended only on public state and public contract entrypoints.
The vulnerability class is an application-layer attack caused by exposing a price-impacting treasury action through a public transfer hook. In the SEAMAN override of _transfer, any qualifying sell to uniswapV2Pair checks whether the contract balance exceeds a threshold derived from pair inventory, and if so it runs swapAndLiquifyV3() and swapAndLiquifyV1(). swapAndLiquifyV1() converts contract-held SEAMAN into lpToken, which in this deployment is GVC, through the path SEAMAN -> USDT -> GVC.
That means a seller does not merely sell their own token balance. They can also force the protocol to spend treasury-held SEAMAN inventory into the market immediately before their sell completes. Because this path predictably removes USDT from the SEAMAN/USDT pool and buys GVC in the GVC/USDT pool, an attacker can first accumulate GVC, then trigger the treasury spend many times, then sell GVC into the induced price increase. The violated invariant is that unprivileged users must not control the timing and direction of protocol treasury inventory spending in external liquidity venues.
The verified SEAMAN source shows the exact permissionless breakpoint:
if (
uniswapV2Pair.totalSupply() > 0 &&
balanceOf(address(this)) > balanceOf(address(uniswapV2Pair)).div(10000) &&
to == address(uniswapV2Pair)
) {
if (!swapping && _tokenOwner != from && _tokenOwner != to && !ammPairs[from] && !(from == address(uniswapV2Router) && !ammPairs[to]) && swapAndLiquifyEnabled) {
swapping = true;
swapAndLiquifyV3();
swapAndLiquifyV1();
swapping = false;
}
}
swapAndLiquifyV1() then spends tracked fee inventory:
function swapAndLiquifyV1() public {
uint256 canlpAmount = lpAmount.sub(lpTokenAmount);
uint256 amountT = balanceOf(address(uniswapV2Pair)).div(10000);
if (balanceOf(address(this)) >= canlpAmount && canlpAmount >= amountT) {
if (canlpAmount >= amountT.mul(5)) canlpAmount = amountT.mul(5);
lpTokenAmount = lpTokenAmount.add(canlpAmount);
swapTokensFor(canlpAmount, address(lpToken), address(this));
}
}
And swapTokensFor hard-codes the market-buy route:
path[0] = address(this);
path[1] = address(usdt);
path[2] = address(token);
uniswapV2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(tokenAmount, 0, path, to, block.timestamp);
The seed trace confirms the exploit sequence. First, the attacker contract bought 485907716270510929330243 raw GVC through the GVC/USDT pair. Immediately after, the trace shows repeated calls to SEAMAN::transfer(PancakePair, 1). On the first such call, the victim contract performed a router swap of 9480988980507858741121000 raw SEAMAN into the SEAMAN -> USDT -> GVC path, emitted a Transfer of 1 token into the pair from the attacker contract, and then repeated this pattern across the trigger loop.
The balance diff matches the mechanism:
172148930281759729054127355 -> 11158620000010000000000000, delta -160990310281749729054127355-85857538524611356327120x49fac69c51a303b4597d09c18bc5e7bf38ecf89c USDT balance: delta +7781777413037475413078These state changes are exactly what the root cause predicts: public dust sells force treasury inventory into market buys, the SEAMAN pool loses USDT, GVC is bought, and the adversary exits with profit.
The adversary-controlled EOA 0x4b1f47be1f678076f447585beba025e3a046a9fa sent the seed transaction to contract 0x0e647d34c4caf61d9e377a059a01b5c85ab1d82a, which executed the exploit logic and later forwarded profit to helper 0x49fac69c51a303b4597d09c18bc5e7bf38ecf89c.
The end-to-end flow was:
805584574837416580124510 raw USDT from the public DODO pool.transfer(pair, 1).SEAMAN::transfer(SEAMAN_USDT_PAIR, 1) forty times.SEAMAN -> USDT -> GVC, which depletes USDT from the SEAMAN pool and buys GVC.The trace contains many consecutive entries of the form below, evidencing the public trigger loop:
SEAMAN::transfer(PancakePair: [0x6637914482670f91F43025802b6755F27050b0a6], 1)
SEAMAN::transfer(PancakePair: [0x6637914482670f91F43025802b6755F27050b0a6], 1)
SEAMAN::transfer(PancakePair: [0x6637914482670f91F43025802b6755F27050b0a6], 1)
The attacker cluster realized 7781777413037475413078 raw USDT profit at the helper contract after flash-loan repayment. The manipulated flow also removed 8585753852461135632712 raw USDT from the SEAMAN/USDT pool and forced the SEAMAN token contract to spend 160990310281749729054127355 raw SEAMAN from its own inventory.
The measurable loss reported for the incident is:
{
"token_symbol": "USDT",
"amount": "8585753852461135632712",
"decimal": 18
}
0x6f1af27d08b10caa7e96ec3d580bf39e29fd5ece00abda7d8955715403bf34a8/workspace/session/artifacts/collector/seed/56/0x6f1af27d08b10caa7e96ec3d580bf39e29fd5ece00abda7d8955715403bf34a8/metadata.json/workspace/session/artifacts/collector/seed/56/0x6f1af27d08b10caa7e96ec3d580bf39e29fd5ece00abda7d8955715403bf34a8/trace.cast.log/workspace/session/artifacts/collector/seed/56/0x6f1af27d08b10caa7e96ec3d580bf39e29fd5ece00abda7d8955715403bf34a8/balance_diff.json/workspace/session/artifacts/collector/seed/56/0x6bc9b4976ba6f8c9574326375204ee469993d038/src/Contract.sol/workspace/session/artifacts/collector/seed/56/0xdb95fbc5532eeb43deed56c8dc050c930e31017e/src/Contract.sol