Calculated from recorded token losses using historical USD prices at the incident time.
0x0be817b6a522a111e06293435c233dab6576d7437d0e148b45efcf7ab8a10de00x6844ef18012a383c14e9a76a93602616ee9d6132BSC0xffac2ed69d61cf4a92347dcd394d36e32443d9d7BSC0x1219f2699893bd05fe03559aa78e0923559cf0cfBSCOn BSC block 33916688, transaction 0x0be817b6a522a111e06293435c233dab6576d7437d0e148b45efcf7ab8a10de0 executed a fully permissionless drain against AISPACE. The attacker used a PancakeV3 USDT flash loan, marked an attacker-controlled helper as an AISPACE trading pair through the public setSwapPairs(address) entrypoint, recycled the same AIS through the public Pancake pair skim(address) function to inflate PendingMint, harvested newly minted AIS into the AISPACE market vault, took over that vault through a permissionless setAdmin(address), withdrew the harvested AIS, sold it for USDT, repaid the flash loan plus fee, and kept 60686.884783691370295991 USDT.
The root cause is an access-control failure on two separate public privilege surfaces. AISPACE allowed arbitrary callers to mutate the trusted-pair set that drives mint accounting, and the market vault allowed arbitrary callers to replace the vault admin. Together those bugs made the mint-credit path and the withdrawal path fully exploitable by any unprivileged actor.
AISPACE is not a plain ERC-20. Its transfer path branches on Pairs[address], and those branches mutate PendingMint and PendingBrun. When Pairs[from] is true, the transfer credits PendingMint by 4% of the transfer amount and sets a pending burn on the receiver. When is true, the transfer credits by and sets a pending burn on the sender. later mints directly into .
5%Pairs[to]PendingMint8%10%harvestMarket()PendingMint - MintPositionMarketAddressThe public PancakeSwap V2 pair at 0x1219f2699893bd05fe03559aa78e0923559cf0cf exposes skim(address), which transfers any token balance above recorded reserves to an arbitrary recipient. That matters because the attacker could send AIS into the pair, let AISPACE account the transfer as pair-related, and then immediately pull the same AIS back out without changing reserves.
The market vault at 0xffac2ed69d61cf4a92347dcd394d36e32443d9d7 held the harvested AIS. Pre-state permission checks in the collected RPC observations show setAdmin(address) succeeded for a random caller, while transferToken(address,address,uint256) reverted only until the caller first rewrote the mutable admin slot.
The ACT pre-state is BSC mainnet immediately before block 33916688, which is block 33916687 in the collected fork configuration. In that pre-state, the real AIS/USDT pair was already flagged in Pairs, the attacker helper was not, the flash pool 0x4f31fa980a675570939b737ebdde0471a4be40eb exposed a USDT/USDC pool with fee 500, and AISPACE's MarketAddress already pointed at the vulnerable market vault.
The exploit is an ATTACK-class ACT opportunity, not a privileged insider event. The first broken invariant is that only protocol-approved AMM pairs should be able to trigger AISPACE's pair-tax accounting. AISPACE violates that invariant because setSwapPairs(address) is public and directly flips Pairs[_address] = true.
The second broken invariant is that only protocol governance should control market-vault withdrawals. The vault violates that invariant because a public caller can replace the admin and then satisfy the only authorization check on transferToken.
Once the attacker controls both surfaces, the rest of the exploit becomes deterministic. The attacker buys AIS with public flash liquidity, loops transfer -> skim to manufacture PendingMint without consuming the AIS principal, harvests that protocol-side mint into the vault, seizes vault admin, withdraws the harvested AIS, and dumps it into the public AIS/USDT pair for profit.
The exploit conditions were also public and concrete: AISPACE had to expose setSwapPairs(address) without access control, the market vault had to keep setAdmin(address) permissionless while gating withdrawals only by the mutable admin slot, the public AIS/USDT pair had to expose skim(address) and hold enough USDT liquidity, and a public flash-liquidity venue had to exist for the initial AIS purchase. Those are exactly the conditions observed in the validated artifacts.
The violated security principles are clear: arbitrary callers must not be able to assign protocol privileges, mint-accounting logic must not trust attacker-controlled role classification, and vault-withdrawal authority must not be separable from governance through a public setter.
The relevant AISPACE code from the verified source is straightforward:
function setSwapPairs(address _address) public {
Pairs[_address] = true;
}
function harvestMarket() public {
require(PendingMint > MintPosition, "No Pending available");
_mint(MarketAddress, PendingMint - MintPosition);
MintPosition = PendingMint;
}
function _transferAIS(address from, address to, uint256 value) private returns (bool) {
if (Pairs[from]) {
_transfer(from, to, value);
PendingBrun[to] = value * 5 / 100;
PendingMint += value * 4 / 100;
IMarketVault(MarketAddress).addMarketValue(value * 4 / 100);
return true;
}
if (Pairs[to]) {
require(balanceOf(from) > value * 111 / 100, "insufficient funds for burn!");
_transfer(from, to, value);
PendingBrun[from] = value * 10 / 100;
PendingMint += value * 8 / 100;
IMarketVault(MarketAddress).addMarketValue(value * 8 / 100);
return true;
}
...
}
That source shows the full code-level breakpoint. Any caller can mark an arbitrary helper as a trusted pair, and once either side of a transfer is treated as a pair, AISPACE credits PendingMint directly from transfer volume rather than from any real economic gain.
The vault-side evidence is equally explicit in the collected pre-state checks:
"permission_checks_pre": {
"set_admin_from_random_address": "success",
"transfer_token_from_random_address": "revert Not admin",
"transfer_token_from_existing_admin": "success"
}
This means a random caller could first take the admin role and then use the vault's token-withdrawal function.
The seed transaction data shows the exploit parameters and post-state:
3000000000000000000000000 USDT.142406161283037117313874 AIS.128165545154733405582486 AIS.100.1543728293469283826814527.1389357514498347255584772.1531763675781384372898646.3001500000000000000000000 USDT.60686884783691370295991 USDT.The on-chain trace confirms the exploit sequence includes repeated pair skim calls, then market harvesting and vault seizure:
PancakeV3_USDT_USDC::flash(..., 3000000000000000000000000, ...)
PancakePair::skim(0x15FFd1D02B3918C9e56f75E30D23786D3eF2B5bc) // repeated 100 times
AISPACE::harvestMarket()
AIS_MarketVault::setAdmin(0x15FFd1D02B3918C9e56f75E30D23786D3eF2B5bc)
AIS_MarketVault::transferToken(
AISPACE,
0x15FFd1D02B3918C9e56f75E30D23786D3eF2B5bc,
1389357514498347255584772
)
The exploit therefore breaks two explicit invariants:
Because both invariants were broken in the public pre-state, the exploit was reproducible by any unprivileged actor from the same block state. The success predicate is both monetary and non-monetary: the attacker realized 60686.884783691370295991 USDT profit, and an arbitrary caller could force unauthorized AIS minting plus USDT reserve drain from the public pair.
The adversary cluster in the validated analysis has three roles:
0x7cb74265e3e2d2b707122bf45aea66137c6c8891: the EOA that sent the exploit transaction and paid the BNB gas cost.0x15ffd1d02b3918c9e56f75e30d23786d3ef2b5bc: the helper contract that executed the flash loan, pair spoofing, skim loop, vault takeover, token withdrawal, liquidation, and repayment.0x859444a27eff21b443f6213ec54fd2f1a09de346: the EOA that received the final USDT profit transfer.The full exploit flow is:
3,000,000 USDT from PancakeV3 pool 0x4f31fa980a675570939b737ebdde0471a4be40eb.0x10ed43c718714eb63d5aa57b78b54704e256024e into 142406.161283037117313874 AIS.AISPACE.setSwapPairs(helper) to whitelist itself as a trusted pair even though it is attacker-controlled.helper -> pair transfer followed by pair.skim(helper) 100 times, recycling 128165.545154733405582486 AIS per loop while repeatedly crediting PendingMint.AISPACE.harvestMarket(), which mints 1543728.293469283826814527 AIS into the market vault.setAdmin(helper), then transferToken(AIS, helper, 1389357.514498347255584772).1531763.675781384372898646 AIS back into the AIS/USDT pair, receives 3062186.884783691370295991 USDT, repays 3001500 USDT to the flash pool, and transfers 60686.884783691370295991 USDT to the profit EOA.Every step above used public contracts and public state. No private key compromise, governance privilege, or attacker-only bytecode dependency was required.
The measurable loss is the AIS/USDT pair depletion of 62186.884783691370295991 USDT, recorded in the balance diff as raw amount "62186884783691370295991" with 18 decimals. The flash pool also collected the 1500 USDT fee that explains the difference between pair loss and attacker profit.
The attack also created protocol-side inflation that should never have existed. AISPACE.harvestMarket() minted 1543728.293469283826814527 AIS into the market vault, and the attacker then withdrew 1389357.514498347255584772 AIS from that vault before dumping 1531763.675781384372898646 AIS into the public pair. That sequence both drained USDT reserves and left the market vault and pair holding distorted AIS balances.
0x0be817b6a522a111e06293435c233dab6576d7437d0e148b45efcf7ab8a10de0 on BSC block 33916688.0x6844ef18012a383c14e9a76a93602616ee9d6132.0xffac2ed69d61cf4a92347dcd394d36e32443d9d7.0x1219f2699893bd05fe03559aa78e0923559cf0cf.0x4f31fa980a675570939b737ebdde0471a4be40eb.