Calculated from recorded token losses using historical USD prices at the incident time.
0x49ca5e188c538b4f2efb45552f13309cc0dd1f3592eee54decfc9da54620c2ec0x4eDda16AB4f4cc46b160aBC42763BA63885862a4BSCYiedl SpotVault on BNB Chain was drained in transaction 0x49ca5e188c538b4f2efb45552f13309cc0dd1f3592eee54decfc9da54620c2ec after a fresh attacker EOA deployed a helper contract and executed the exploit in a single transaction. The exploit path did not require any prior Y-BULL share ownership.
The root cause is that SpotVault.redeem forwards caller-supplied 1inch calldata before any meaningful ownership check and before burning shares. A caller can therefore invoke redeem(0, ...), keep share balance at zero, and still cause 1inch to spend approved vault assets to attacker-chosen recipients.
SpotVault (0x4eDda16AB4f4cc46b160aBC42763BA63885862a4) is a portfolio vault that holds multiple assets and uses external swap routers during deposit and redemption flows. At block 38126753, the vault held an 11-asset portfolio, had nonzero total supply, and had slippage tolerances of 2% for swap, AUM, and NAV checks.
{
"block": 38126753,
"total_supply": "329385422507706907980164",
"slippage_tolerances": {
"swap": "20000000000000000",
The exploit relied on two preconditions that were already present on-chain: SpotVault had standing approvals for 1inch, and the vault trusted externally supplied swap calldata during redemption. The executor address had no prior SpotVault token-transfer history before the seed transaction, which is consistent with a zero-share exploit path.
The vulnerable logic is in SpotVault.redeem. The function accepts a bytes[] dataList from the caller and iterates over the vault asset list. For non-native assets, it executes ONE_INCH_AGG_ROUTER.functionCall(dataList[i]) directly, without internally deriving the input amount or constraining the swap recipient to the vault. The accounting variable rcvTokenAccumulator only tracks the vault’s observed increase in the chosen receiving asset, not the total value that may have been routed away by 1inch. The share burn happens only after those external calls via _burn(msg.sender, sharesToRedeem). Because _burn(msg.sender, 0) succeeds, a zero-share caller can execute externalized asset movements first and pass the later burn with no prior share ownership. This breaks the core redemption invariant that asset outflows must remain bounded by the redeemed share fraction.
The relevant redeem logic is:
uint256 rcvTokenAccumulator =
(receivingAsset == NATIVE_TOKEN ? address(this).balance : ERC20(receivingAsset).balanceOf(address(this)))
* sharesToRedeem / dp.totalSupply;
for (uint256 i = 0; i < dp.assets.length; i++) {
if (dp.assets[i] == receivingAsset) continue;
uint256 rcvTokenSize = receivingAsset == NATIVE_TOKEN
? address(this).balance
: ERC20(receivingAsset).balanceOf(address(this));
if (dp.assets[i] != NATIVE_TOKEN) {
ONE_INCH_AGG_ROUTER.functionCall(dataList[i]);
} else {
uint256 sizeToSwap = address(this).balance * sharesToRedeem / dp.totalSupply;
ONE_INCH_AGG_ROUTER.functionCallWithValue(dataList[i], sizeToSwap);
}
rcvTokenAccumulator += receivingAsset == NATIVE_TOKEN
? address(this).balance - rcvTokenSize
: ERC20(receivingAsset).balanceOf(address(this)) - rcvTokenSize;
}
_burn(msg.sender, sharesToRedeem);
This design creates the exploit condition directly:
sharesToRedeem = 0, so the initial accumulator is zero and the final _burn enforces no meaningful ownership requirement.dataList[i] to 1inch.tokensToReturn remained zero while real assets left the vault.The on-chain receipt confirms this mismatch. It shows repeated zero-value burns from the helper to the zero address and repeated zero-value transfers from SpotVault to the helper in the receiving asset path, while swap-related asset transfers still moved value out of the vault:
{
"contractAddress": "0x53a3a3eb8897346864a291e6286319c9811be2dd",
"status": "0x1",
"log_count": 1050
}
Representative receipt entries include:
{
"address": "0x4edda16ab4f4cc46b160abc42763ba63885862a4",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x00000000000000000000000053a3a3eb8897346864a291e6286319c9811be2dd",
"0x0000000000000000000000000000000000000000000000000000000000000000"
],
"data": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
{
"address": "0x4edda16ab4f4cc46b160abc42763ba63885862a4",
"topics": [
"0x9f0cb550275f55b5f68ad2cfc5ed3b4df3398cb0132ebc22a7fcb1948542cc17",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x00000000000000000000000053a3a3eb8897346864a291e6286319c9811be2dd",
"0x00000000000000000000000055d398326f99059ff775485246999027b3197955"
],
"data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
}
Those zero-share and zero-return signals coexist with large vault asset losses in the balance diff, which is the decisive evidence that the root cause is an authorization and accounting failure inside redemption rather than a normal share-based withdrawal.
The adversary flow is fully permissionless and fits the ACT model.
0x322696471792440499b1979e0a440491e870667a submitted the only observed transaction in its recent history and created helper contract 0x53a3a3eb8897346864a291e6286319c9811be2dd.The executor’s SpotVault token history contains only burn events in the seed transaction and no earlier incoming Y-BULL transfers, which independently confirms that prior share acquisition was not part of the exploit.
The exploit caused unauthorized depletion of multiple SpotVault assets while no shares were redeemed. The measured asset losses recorded in the submitted root cause are:
[
{
"token_symbol": "USDC",
"amount": "75635138375994372050770",
"decimal": 18
},
{
"token_symbol": "BTCB",
"amount": "521372360835400072",
"decimal": 18
},
{
"token_symbol": "ETH",
"amount": "8333778171625678105",
"decimal": 18
},
{
"token_symbol": "BNB",
"amount": "197316358658297108656",
"decimal": 18
}
]
The native balance diff shows the attacker EOA gained 197217680633297108656 wei net, while the fee-burn address received 98678025000000000 wei. The incident therefore combined a broken share-accounting invariant with direct attacker profit.
0x49ca5e188c538b4f2efb45552f13309cc0dd1f3592eee54decfc9da54620c2ecSpotVault at 0x4eDda16AB4f4cc46b160aBC42763BA63885862a4OracleChainlink at 0x90A039797E93f2c671DE25DD24E5333b5e8F9Ab3SpotVault.sol