The SAND OFT's own approveAndCall let an attacker name themselves LayerZero delegate, forge inbound verification, mint 329 trillion unbacked SAND on Base and empty the Ethereum adapter.
On August 21, 2026 at 23:41:41 UTC, an attacker took over the LayerZero delegate of The Sandbox's SAND OFT on Base by routing a crafted payload through the token's own approveAndCall function, then used that delegate position to forge inbound cross-chain verification and mint SAND with no matching burn on any source chain. Minting ran for five hours and three minutes, producing 329,243,083,813,776 SAND across 703 mint events to 173 addresses against a legitimate maximum supply of 3 billion. The unbacked supply was worthless in aggregate, but it was fungible with real SAND at the margin, and that is where the money was: the attacker sold 93,415,334 SAND into Base liquidity for about $824,000 starting four seconds after the first mint, then bridged a claim back to Ethereum and emptied the OFT adapter of 14,095,483.66 real SAND in six transfers spanning 24 seconds, swapping it for 78.24 WETH. The Ethereum adapter that backs every bridged SAND now holds 0.0056 SAND. The Sandbox disabled bridge transfers on Base and BNB Smart Chain and removed the LayerZero peer settings at 05:09:19 UTC, and described the impact as "less than 0.01% of the total SAND token supply."
The vulnerability is not in LayerZero's verification and not in the OFT accounting. It is in an ERC-20 convenience extension that predates both, kept on the token for backward compatibility and never reconsidered once the same contract became a cross-chain endpoint owner.
approveAndCall(address spender, uint256 value, bytes data) is a 2017-era pattern: set an allowance and, in the same transaction, call the spender with a caller-supplied payload so a user does not need two transactions to fund a contract. The critical property is that the token contract makes that second call, so from the callee's perspective msg.sender is the token. On a plain ERC-20 that is harmless, because nothing grants the token contract authority. On the SAND OFT at 0xac531Eb2 it was not harmless, because the OFT is its own LayerZero OApp, and the endpoint identifies an OApp by exactly that address:
// LayerZero EndpointV2, 0x1a44076050125825900e736c501f859c50fe728c
mapping(address oapp => address delegate) public delegates;
function setDelegate(address _delegate) external {
delegates[msg.sender] = _delegate; // msg.sender IS the OApp
emit DelegateSet(msg.sender, _delegate);
}The delegate is the account permitted to configure an OApp's messaging library, its send and receive settings, and to skip, nilify or clear inbound payloads. It is a privileged position by design, normally held by the protocol's multisig.
Both sides of that pairing are ordinary. Put together, the token's approveAndCall lets any caller make the OFT contract call an arbitrary address with arbitrary calldata, and the endpoint's setDelegate trusts whoever calls it to be the OApp naming its own delegate. The attacker supplied spender = LayerZero EndpointV2 and data = setDelegate(attacker), and the token dutifully made the call as itself:
23:41:41 UTC 0x149eb0ee 0x67624bfa… → 0xac531Eb2… approveAndCall(0xcae9ca51) 40,170 gas
└─ SAND OFT calls EndpointV2.setDelegate(attacker)
delegates[0xac531Eb2…] = attacker
Forty thousand gas, one transaction, no signature forged and no key compromised. The transaction is unremarkable to look at, which is the point.
Delegate authority is configuration authority, and configuration is what decides whether an inbound message is believed. With it the attacker could point the OFT's receive library and its verification settings at infrastructure they controlled, so that a message they authored would arrive already verified. From that moment the OFT's mint path was reachable without any corresponding burn on Ethereum, Polygon or BNB Smart Chain. Twenty-four seconds later the first forged message landed:
23:42:05 UTC 0x67624bfa… → EndpointV2 0x1a440760… lzReceive(0x0c0c389e)
└─ SAND OFT credits the message · mint to attacker, no source burn
The OFT itself never had to be tricked about accounting. lzReceive on a mint-and-burn OFT is supposed to mint exactly what a peer chain burned, and it does; the guarantee that a peer actually burned anything is delegated entirely to whether the endpoint considers the message verified. Own the configuration that decides verification and the mint function becomes a faucet that reports itself as a legitimate bridge delivery. This is the false-deposit pattern that recurs across bridge exploits, arriving here one layer higher than usual: not a contract mistaking a message for value, but an attacker who got to define what counts as a valid message.
The selling started four seconds after the first mint, at 23:42:09 UTC, and ran in parallel with the minting for the next two hours and thirty-nine minutes. The attacker did not attempt to sell the full balance, which would have been pointless; the Base pools were the constraint, and 93,415,334 SAND across 26 trades was roughly what they would absorb, for about $824,000.
The larger prize was on Ethereum. Every bridged SAND is supposed to be backed one-for-one by SAND locked in an adapter, and on Ethereum that adapter sits at the same address, 0xac531Eb2. A bridge transfer out of Base burns the OFT balance and instructs the adapter to release the real token. Holding an arbitrary Base balance and control of message verification, the attacker sent themselves the adapter's entire contents in six transfers inside 24 seconds:
00:32:11 0x3617940c… 2,400,000.000000 SAND → 0x53eDa2e8…
00:32:11 0x2fbde0f4… 2,400,000.000000 SAND → 0x53eDa2e8…
00:32:11 0x468d7438… 2,400,000.000000 SAND → 0x53eDa2e8…
00:32:23 0x6abe0895… 2,400,000.000000 SAND → 0x53eDa2e8…
00:32:23 0xa3ec04ae… 2,400,000.000000 SAND → 0x53eDa2e8…
00:32:35 0xb4a661df… 2,095,483.660582 SAND → 0x53eDa2e8…
─────────────────────
14,095,483.660582 SAND
The final transfer is not a round number because it is the remainder: the adapter had nothing left to give. It holds 0.005559718748018388 SAND today.
Liquidation followed three minutes later in two Uniswap V2 swaps, the size of each chosen against what the pool could take: 12,685,935.29 SAND for 58.3486 WETH at 00:35:23, and the remaining 1,409,548.366 SAND for 19.8882 WETH at 00:58:11. Together, 78.2369 WETH against 14,095,483.66 SAND, an average of 0.00000555 ETH per SAND, well under the market price the same tokens carried an hour earlier. Dumping a bridge's entire reserve through the pool that prices it is self-defeating past a certain size, and the ratio between the two swaps shows the attacker knew it.
A second wave followed once the technique was public. Between 03:08:19 and 04:45:21 UTC, 0xAbE09907 ran 376 mint transactions, 0xFF7cE6fB 50, and 0x638Ccb18 a further set including 0x76ed0384, a contract-creation transaction whose constructor runs the exploit and mints in the same breath. Deploying and executing from creation code means the drainer has no deployed bytecode for anyone to read in advance, the same exploit_in_initcode shape seen in the Allbridge CCTP exploit two days earlier. These later addresses accounted for the bulk of the raw token count, including 317.7 trillion SAND minted in 48 transactions between 04:12:25 and 04:17:47, which is why the headline supply figure is so much larger than anything that was ever sellable.
The realized loss and the face value differ by four orders of magnitude, and only one of them is a real number.
The verifiable loss is what the attacker converted: about $824,000 from selling 93,415,334 SAND into Base liquidity, plus 78.2369 WETH from the Ethereum adapter's 14,095,483.66 SAND, roughly $196,000 at the price ETH traded at during those swaps. That is approximately $1.02 million realized by the first exploiter address, and it is a floor rather than a total, because the second-wave addresses also sold and the attack was still producing transactions when this was written.
The 329.24 trillion SAND minted on Base, and the roughly $49 billion of face value widely quoted for it, are arithmetic rather than loss. Multiplying an unbacked supply by the price of the backed token assumes a buyer for all of it at that price; there was liquidity for about a thousandth of a percent of it. The Base OFT's totalSupply currently reads 327,574,531,179,831 SAND against a legitimate cap of 3 billion, so the ratio is a useful measure of how thoroughly the accounting broke and a useless measure of what was stolen.
The loss that has not yet been priced falls on holders of bridged SAND. Every SAND on Base or BNB Smart Chain was a claim on the Ethereum adapter, and the adapter is empty. The Sandbox stated during the incident that "all bridged SAND funds are backed by SAND locked on Ethereum, which remains entirely secure," which was accurate about the Ethereum token contract and not about the adapter, whose 14,095,483.66 SAND had been withdrawn roughly nine hours earlier. Making bridged holders whole means replacing that balance from treasury. The characterization of the impact as "less than 0.01% of the total SAND token supply" is also arithmetically correct against a 3 billion supply and describes the reserve drawdown, not the 329 trillion tokens now circulating on Base.
Secondary damage landed on Base liquidity providers, who ended the night holding SAND that no longer redeems for anything, and on holders across venues. SAND traded down 5.5% to 10% depending on venue, futures open interest rose 16% within an hour, and funding flipped negative. Bithumb and Upbit halted SAND deposits and withdrawals. Ethereum and Polygon SAND were unaffected as tokens, though the Ethereum adapter's reserve was the thing taken.
The Sandbox's response was containment at the configuration layer, which is the same layer the attack was fought on. At 05:09:19 UTC on August 22, twenty-four minutes after the last mint and five hours twenty-eight minutes after the delegate was hijacked, the protocol multisig disabled bridge transfers for Base and BNB Smart Chain and removed the LayerZero peer settings, which stops the OFT from accepting or emitting cross-chain messages regardless of who holds the delegate. Ethereum and Polygon were left running. A snapshot was announced for compensating eligible liquidity providers, with no restoration timeline given.
Removing the peers closes the door without repairing the lock. The delegate takeover is a property of approveAndCall coexisting with LayerZero OApp ownership on one address, and any redeployment that keeps both reopens it. A correct fix removes approveAndCall from any contract that is also an OApp, or splits the OApp into a separate contract so that the token can never be the msg.sender the endpoint trusts. Restricting which spenders approveAndCall may target would also work and is the smaller change, but it leaves a deny-list where an architectural separation belongs.
The five and a half hour window is the number worth dwelling on. The delegate hijack was a single 40,170-gas transaction and the first unbacked mint followed 24 seconds later; from that point the exploit was public, self-describing, and reproducible, and by 03:08 UTC three further addresses were running it. Detection was never the hard part, since a mint with no matching source-chain burn is one of the loudest signatures on-chain and Defimon's structured WebSocket alerts exist to put exactly that in front of an integrator within a second of the transaction confirming. What the timeline measures is the distance between an alert firing and a multisig executing, and here that distance was long enough for the reserve to be emptied, liquidated, and copied three times over.
On August 21, 2026 at 23:41:41 UTC, an attacker hijacked the LayerZero delegate of The Sandbox's SAND OFT on Base by routing a setDelegate call through the token's own approveAndCall function, which made the token contract issue the call as itself. Delegate authority let the attacker control how inbound cross-chain messages are verified, so forged messages minted SAND with no matching burn on any source chain. Minting ran five hours and three minutes and produced 329.24 trillion SAND across 703 events, and the attacker emptied the Ethereum adapter of 14,095,483.66 real SAND.
About $1.02 million was realized by the first exploiter address: roughly $824,000 from selling 93,415,334 SAND into Base liquidity, plus 78.2369 WETH, around $196,000, from liquidating the 14,095,483.66 SAND drained out of the Ethereum OFT adapter. That figure is a floor, because later exploiter addresses also sold. The widely quoted $49 billion is face value, the minted supply multiplied by the price of backed SAND, and there was never liquidity for any meaningful fraction of it.
The SAND token contracts on Ethereum and Polygon were not touched, and The Sandbox confirmed both were unaffected. The Ethereum OFT adapter is a different matter: it holds the real SAND that backs every bridged token, and it was drained to 0.0056 SAND. So Ethereum and Polygon SAND remain ordinary tokens, while SAND on Base and BNB Smart Chain is a claim on a reserve that is now empty until The Sandbox replaces it.
approveAndCall sets an allowance and then has the token contract call a spender with caller-supplied data, so the callee sees msg.sender as the token. LayerZero's endpoint lets an OApp name its own delegate with setDelegate, identifying the OApp by msg.sender. When one address is both the token and the OApp, anyone can pass the endpoint as the spender and setDelegate(attacker) as the data, and the token names the attacker as its delegate. Neither function is broken on its own; the flaw is that a single contract holds both roles.
The Sandbox disabled bridge transfers for Base and BNB Smart Chain and removed the LayerZero peer settings at 05:09:19 UTC on August 22, which stops the OFT from sending or accepting cross-chain messages. That is containment, not a fix: the underlying pairing of approveAndCall with OApp ownership on one address is unchanged, and any redeployment carrying both reopens it. A snapshot was announced for compensating eligible liquidity providers, with no restoration timeline given.
Defimon detects exploits on major chains the moment they execute and streams them to you: human-readable alerts in Telegram, or structured JSON over WebSocket for your own systems.
// signals $50/mo · websocket $200/mo · channel free
@DefimonAlerts