Calculated from recorded token losses using historical USD prices at the incident time.
0xb4a29409cbd018956746f90d285f427175070c735c36ff3bc2f3c4a4bbaae7050x5340a7278848ee51d35c30693d6fbff06d1a0d73BSC0x1903d672c821bdf7cabfde1fb4dc9ebff0494563BSC0x73a0ba8bac1b6ae00de1e9cf767cdb98075ab92eBSCOn BSC block 82115373, an unprivileged EOA (0xdbca72816b83a60f5ca7cf93a1420c6e7b215aca) deployed helper contract 0x23e5de4a390702b1ff6da7fd0b0f17b79f8eee1a and executed a single exploit transaction (0xb4a29409cbd018956746f90d285f427175070c735c36ff3bc2f3c4a4bbaae705).
The core root cause is unchecked arithmetic inside NLAMM buy at incident implementation 0x1903d672c821bdf7cabfde1fb4dc9ebff0494563 (behind proxy 0x5340a7278848ee51d35c30693d6fbff06d1a0d73). Attacker-controlled amount values around 1e60 to 1e63 are multiplied by currentPrice and 1e18 under unchecked, so both payment and mint amounts wrap modulo 2^256 instead of reverting. The attacker pays tiny wrapped USDT amounts, receives huge wrapped token mints, then dumps those mints through public DexFactory routes to drain USDT pools.
Observed outcome: attacker EOA receives 40341541995032481116169 USDT in the same transaction, while five USDT pools are heavily depleted.
PearlFi’s NLAMM allows public buy(tokenAddress, tokenIndex, amount) calls. During the incident, that path performed economic multiplications in an block.
uncheckedDex routing is also public via DexFactory proxy 0x73a0ba8bac1b6ae00de1e9cf767cdb98075ab92e (implementation 0x55ab5af53852bfc5f933ab4d7441b4b6c68ea52e). Once large token balances are minted, normal AMM swap logic can convert them into USDT and drain pair reserves.
This is ACT because no privileged roles or private data are required: the exploit uses public/external entrypoints, public chain state, and standard transaction submission.
Pre-state (sigma_B) used by the analysis is BSC at block 82115373, reconstructable from:
Additional protocol facts used:
buy, swaps) remain publicRoot cause category: ATTACK.
The violated invariant is: payment and minted output for buy must be overflow-safe functions of requested amount; overflow must revert instead of wrap. In incident code, the first safety break occurs when NLAMM computes amount * currentPrice and amount * 1e18 inside unchecked. Because amount is attacker-controlled and unbounded, arithmetic wraps modulo 2^256. As a result, charged USDT is far lower than intended while minted token output is extremely large.
The trace confirms this behavior repeatedly across five buy legs with currentPrice around 1e14 to 1e16 and amount around 1e60 to 1e63. Wrapped products match traced debits/mints exactly. The attacker then swaps overflow-minted tokens through public DexFactory pairs to extract USDT.
Vulnerable components:
0x1903d672c821bdf7cabfde1fb4dc9ebff04945630x55ab5af53852bfc5f933ab4d7441b4b6c68ea52e) and DexLibrary output mathExploit preconditions:
buyamount values that overflow amount * currentPrice and amount * 1e18Security principles violated:
Snippet from the incident NLAMM implementation (buy):
function buy(address tokenAddress, uint256 tokenIndex, uint256 amount) external {
unchecked {
INLAMM.Token storage tokenRef = _checkTokenExists(tokenAddress, tokenIndex);
(uint256 basePrice, uint256 targetPrice, uint256 currentPrice) = _getPrices(tokenRef);
TransferHelper.safeTransferFrom({
token: _stableCoin,
from: msg.sender,
to: address(this),
value: amount * currentPrice,
infAllowance: false
});
// ...
if (tokenAddress == _pearl || tokenAddress == _blackPearl) {
TransferHelper.safeTransfer({ token: tokenAddress, to: msg.sender, value: amount * E18 });
} else {
TransferHelper.mintERC20({ token: tokenAddress, to: msg.sender, value: amount * E18 });
}
}
}
This unchecked multiplication is the deterministic breakpoint.
From the exploit trace (first leg):
NLAMM::getPrices(0x26C970..., 0)
<- [Return] ... currentPrice: 15013380795249012
NLAMM::buy(0x26C970..., 0, 7712592574349318455520271942948603129480035752544334327373265)
BEP20USDT::transferFrom(..., value: 5319060416824244)
0x26C970...::mint(..., value: 70314684686449557564586932375201211164216764612057100769064455477733443764224)
The same pattern appears on all five legs. Independent recomputation confirms exact modulo-2^256 equality for both payment and mint formulas:
leg 1: (amount*price)%2^256 == charged -> True ; (amount*1e18)%2^256 == minted -> True
leg 2: (amount*price)%2^256 == charged -> True ; (amount*1e18)%2^256 == minted -> True
leg 3: (amount*price)%2^256 == charged -> True ; (amount*1e18)%2^256 == minted -> True
leg 4: (amount*price)%2^256 == charged -> True ; (amount*1e18)%2^256 == minted -> True
leg 5: (amount*price)%2^256 == charged -> True ; (amount*1e18)%2^256 == minted -> True
After overflow mints, attacker helper swaps into USDT through DexFactory pairs. Trace shows large swap outputs and final transfer to attacker EOA:
... Swap(... tokenOut: USDT, amountOut: 7805558224390563581443)
... Swap(... tokenOut: USDT, amountOut: 8337144257673105534613)
... Swap(... tokenOut: USDT, amountOut: 9541691613786665270666)
... Swap(... tokenOut: USDT, amountOut: 6468134888472375307607)
... Swap(... tokenOut: USDT, amountOut: 8182972560290464373034)
BEP20USDT::transfer(0xDbCa72816b83a60f5ca7cF93a1420C6e7b215aca, 40341541995032481116169)
Adversary strategy summary: single-transaction multi-leg overflow mint-and-dump extraction.
Adversary-related accounts:
0xdbca72816b83a60f5ca7cf93a1420c6e7b215aca (origin sender, final USDT recipient)0x23e5de4a390702b1ff6da7fd0b0f17b79f8eee1a (created and used in the exploit transaction)Victim/protocol components involved:
0x5340a7278848ee51d35c30693d6fbff06d1a0d730x1903d672c821bdf7cabfde1fb4dc9ebff04945630x73a0ba8bac1b6ae00de1e9cf767cdb98075ab92eExecution stages:
0.01 BNB in the create transaction; helper swaps BNB->USDT and approves NLAMM.buy calls with huge amount, triggering wrapped charge/mint math.Sequence details from ACT definition:
1, chain 56, tx 0xb4a29409cbd018956746f90d285f427175070c735c36ff3bc2f3c4a4bbaae705ACT feasibility statement: the full path is callable by any unprivileged actor and does not require owner roles, compromised keys, or private orderflow.
Measured USDT depletion from five pools (balance diffs):
0x9849e6828022e8b5161cd5c70f4bf38eaf78cfde: -78055582243905635814430x3119b2f98693a333394c2e68c0b31dcde7183dae: -83371442576731055346130xb38d61552658bacdb382bb3074c104e0a2060ed0: -95416916137866652706660xf5784cbdd3d64dbdf882fd9f5b89109793e9f7e6: -64681348884723753076070x9095d1083a19be8e390536e9acaf5d4080ce87aa: -8182972560290464373034Total drained from those five pools: 40335501544613174067363 USDT units.
Attacker EOA USDT delta: +40341541995032481116169.
Native token delta shows attacker paid transaction costs (-10145295420868144 wei including 0.01 BNB tx value and gas), while net USDT extraction remained strongly positive.
Profit predicate context:
0403415419950324811161690.000145295420868144 BNB gas plus 0.01 BNB value0.01 WBNB -> 6050459719766201456 USDT equivalent+40335.40 USDT after BNB-cost equivalenceImpact statement from analysis: five USDT pools were drained from multi-thousand reserves to near-dust levels in one transaction, with attacker EOA realizing 40341.541995032481116169 USDT inflow.
Root-cause evidence references:
/workspace/session/artifacts/collector/seed/56/0xb4a29409cbd018956746f90d285f427175070c735c36ff3bc2f3c4a4bbaae705/metadata.json/workspace/session/artifacts/collector/iter_1/tx/56/0xb4a29409cbd018956746f90d285f427175070c735c36ff3bc2f3c4a4bbaae705/trace.cast.log/workspace/session/artifacts/collector/iter_1/tx/56/0xb4a29409cbd018956746f90d285f427175070c735c36ff3bc2f3c4a4bbaae705/balance_diff.json/workspace/session/artifacts/collector/iter_1/contract/56/0x1903d672c821bdf7cabfde1fb4dc9ebff0494563/source/forge_clone/src/nlamm/NLAMM.sol/workspace/session/artifacts/collector/iter_1/contract/56/0x55ab5af53852bfc5f933ab4d7441b4b6c68ea52e/source/forge_clone/src/dex/DexFactory.sol/workspace/session/artifacts/collector/iter_1/contract/56/0x55ab5af53852bfc5f933ab4d7441b4b6c68ea52e/source/forge_clone/src/dex/libraries/DexLibrary.solAll relevant transactions in scope:
0xb4a29409cbd018956746f90d285f427175070c735c36ff3bc2f3c4a4bbaae7050x886f130770173ab7e6a2dd12dc35656408d0983528382a00b78be2986117447e0x8731f3d5a2e9d88ad671d3a162420434d9476ba3537b87a46e78a49cc58eb8f9