PearlFi NLAMM unchecked multiplication overflow enables underpriced mint-and-drain
Exploit Transactions
0xb4a29409cbd018956746f90d285f427175070c735c36ff3bc2f3c4a4bbaae705Victim Addresses
0x5340a7278848ee51d35c30693d6fbff06d1a0d73BSC0x1903d672c821bdf7cabfde1fb4dc9ebff0494563BSC0x73a0ba8bac1b6ae00de1e9cf767cdb98075ab92eBSCLoss Breakdown
Similar Incidents
LockedDeal Overflow Drain
39%Public Mint Drains USDT Pair
34%Public mint flaw drains USDT from c3b1 token pool
33%LPMine WTO overvaluation bug enables LP double-counting drain
32%NeverFallToken LP Drain
32%VistaFinance oracle mispricing enables VISTA flash-loan arbitrage drain
32%Root Cause Analysis
PearlFi NLAMM unchecked multiplication overflow enables underpriced mint-and-drain
1. Incident Overview TL;DR
On 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.
2. Key Background
PearlFi’s NLAMM allows public buy(tokenAddress, tokenIndex, amount) calls. During the incident, that path performed economic multiplications in an unchecked block.
Dex 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:
- seed metadata for tx identity and block context
- verbose execution trace for call-level behavior
- tx balance diffs for native and ERC20 deltas
- verified source for NLAMM and DexFactory implementations
Additional protocol facts used:
- registry verification enables privileged token operations (mint/verifiedTransfer), but user entrypoints (
buy, swaps) remain public - constant-product pool math means overflow-minted token supply can be monetized by selling into existing USDT liquidity
3. Vulnerability Analysis & Root Cause Summary
Root 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:
- NLAMM buy path in incident implementation
0x1903d672c821bdf7cabfde1fb4dc9ebff0494563 - DexFactory swap routing (
0x55ab5af53852bfc5f933ab4d7441b4b6c68ea52e) and DexLibrary output math
Exploit preconditions:
- token listing exists and is publicly purchasable through NLAMM
buy - attacker can choose
amountvalues that overflowamount * currentPriceandamount * 1e18 - liquid swap route from minted token outputs to USDT is available
Security principles violated:
- avoid unchecked arithmetic on attacker-controlled economic inputs without strict bounds
- preserve overflow-safe accounting before transfers and mint operations
- fail closed on arithmetic overflow in pricing and mint logic
4. Detailed Root Cause Analysis
4.1 Code-level breakpoint
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.
4.2 Trace evidence of wrapped payment + wrapped mint
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
4.3 Conversion to USDT and payout
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)
5. Adversary Flow Analysis
Adversary strategy summary: single-transaction multi-leg overflow mint-and-dump extraction.
Adversary-related accounts:
- EOA:
0xdbca72816b83a60f5ca7cf93a1420c6e7b215aca(origin sender, final USDT recipient) - Helper contract:
0x23e5de4a390702b1ff6da7fd0b0f17b79f8eee1a(created and used in the exploit transaction)
Victim/protocol components involved:
- NLAMM proxy:
0x5340a7278848ee51d35c30693d6fbff06d1a0d73 - NLAMM incident implementation:
0x1903d672c821bdf7cabfde1fb4dc9ebff0494563 - DexFactory proxy:
0x73a0ba8bac1b6ae00de1e9cf767cdb98075ab92e
Execution stages:
- Initial funding and setup: attacker sends
0.01BNB in the create transaction; helper swaps BNB->USDT and approves NLAMM. - Overflowed buy execution: helper performs five
buycalls with hugeamount, triggering wrapped charge/mint math. - Swap and realization: helper swaps minted tokens through five USDT pairs and transfers resulting USDT to attacker EOA.
Sequence details from ACT definition:
- sequence index
1, chain56, tx0xb4a29409cbd018956746f90d285f427175070c735c36ff3bc2f3c4a4bbaae705 - type: adversary-crafted create transaction
- inclusion feasibility: unprivileged EOA can submit the same public call path
- note: exploit is fully realized in one transaction
ACT feasibility statement: the full path is callable by any unprivileged actor and does not require owner roles, compromised keys, or private orderflow.
6. Impact & Losses
Measured USDT depletion from five pools (balance diffs):
0x9849e6828022e8b5161cd5c70f4bf38eaf78cfde:-78055582243905635814430x3119b2f98693a333394c2e68c0b31dcde7183dae:-83371442576731055346130xb38d61552658bacdb382bb3074c104e0a2060ed0:-95416916137866652706660xf5784cbdd3d64dbdf882fd9f5b89109793e9f7e6:-64681348884723753076070x9095d1083a19be8e390536e9acaf5d4080ce87aa:-8182972560290464373034
Total 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:
- reference asset: USDT (18 decimals on BSC)
- attacker USDT before:
0 - attacker USDT after:
40341541995032481116169 - tx fee + funding outflow:
0.000145295420868144BNB gas plus0.01BNB value - same-tx conversion anchor:
0.01WBNB ->6050459719766201456USDT equivalent - net interpretation: approximately
+40335.40USDT after BNB-cost equivalence
Impact 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.
7. References
Root-cause evidence references:
- [1] Seed metadata and tx identity:
/workspace/session/artifacts/collector/seed/56/0xb4a29409cbd018956746f90d285f427175070c735c36ff3bc2f3c4a4bbaae705/metadata.json - [2] Verbose tx trace:
/workspace/session/artifacts/collector/iter_1/tx/56/0xb4a29409cbd018956746f90d285f427175070c735c36ff3bc2f3c4a4bbaae705/trace.cast.log - [3] Balance diffs:
/workspace/session/artifacts/collector/iter_1/tx/56/0xb4a29409cbd018956746f90d285f427175070c735c36ff3bc2f3c4a4bbaae705/balance_diff.json - [4] NLAMM incident implementation source:
/workspace/session/artifacts/collector/iter_1/contract/56/0x1903d672c821bdf7cabfde1fb4dc9ebff0494563/source/forge_clone/src/nlamm/NLAMM.sol - [5] DexFactory source:
/workspace/session/artifacts/collector/iter_1/contract/56/0x55ab5af53852bfc5f933ab4d7441b4b6c68ea52e/source/forge_clone/src/dex/DexFactory.sol - [6] DexLibrary source:
/workspace/session/artifacts/collector/iter_1/contract/56/0x55ab5af53852bfc5f933ab4d7441b4b6c68ea52e/source/forge_clone/src/dex/libraries/DexLibrary.sol
All relevant transactions in scope:
- seed:
0xb4a29409cbd018956746f90d285f427175070c735c36ff3bc2f3c4a4bbaae705 - related:
0x886f130770173ab7e6a2dd12dc35656408d0983528382a00b78be2986117447e - related:
0x8731f3d5a2e9d88ad671d3a162420434d9476ba3537b87a46e78a49cc58eb8f9