0x256979ae169abb7fbbbbc14188742f4b9debf48b48ad5b5207cadcc99ccb493b0xbbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcbEthereum0xdd1778f71a4a1c6a0efebd8ae9f8848634ce1101EthereumOn Ethereum block 20956052, transaction 0x256979ae169abb7fbbbbc14188742f4b9debf48b48ad5b5207cadcc99ccb493b let attacker EOA 0x02dbe46169fdf6555f2a125eee3dce49703b13f5 deposit only 132577813003136114 wei of PAXG and borrow 230002486670 raw USDC units from Morpho Blue. The borrow succeeded through public contract EthereumBundlerV2 at 0x4095f064b8d3c3548a3bebfd0bbfd04750e30077, but the exploit did not depend on any privilege or attacker-specific contract.
The root cause was a decimal misconfiguration in Morpho oracle 0xdd1778f71a4a1c6a0efebd8ae9f8848634ce1101. The deployed MorphoChainlinkOracleV2 instance used SCALE_FACTOR = 1e36, even though the PAXG collateral token has 18 decimals and the USDC loan token has 6 decimals, so this market required 1e24. Morpho trusted that inflated oracle output inside _isHealthy, so it accepted a borrow roughly 711 times larger than the economically correct limit.
Morpho Blue market safety depends on the tuple (loanToken, collateralToken, oracle, irm, lltv). For market 0x8eaf7b29f02ba8d8c1d7aeb587403dcb16e2e943e4e2f5f94b0963c2386406c9, the relevant parameters are:
loanToken = USDC (0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48)collateralToken = PAXG (0x45804880de22913dafe09f4980848ece6ecbaf78)oracle = 0xdd1778f71a4a1c6a0efebd8ae9f8848634ce1101lltv = 915000000000000000Morpho Blue enforces collateralization by reading oracle.price() and computing a maximum borrow amount inside _isHealthy. The deployed oracle is a generic MorphoChainlinkOracleV2, which requires the deployer to supply correct token-decimal parameters when the oracle is constructed. That requirement is explicit in the verified source:
/// @dev The base asset should be the collateral token and the quote asset the loan token.
/// @dev Here is the list of assumptions that guarantees the oracle behaves as expected:
/// - Decimals passed as argument are correct.
SCALE_FACTOR = 10
** (
36 + quoteTokenDecimals + quoteFeed1.getDecimals() + quoteFeed2.getDecimals() - baseTokenDecimals
- baseFeed1.getDecimals() - baseFeed2.getDecimals()
) * quoteVaultConversionSample / baseVaultConversionSample;
Both live price feeds used by the oracle are 8-decimal USD feeds. PAXG itself has 18 decimals and USDC has 6 decimals. That decimal gap is what the deployment failed to encode.
This is an ACT exploit caused by an oracle-decimal misconfiguration, not by a privileged action. The vulnerable component is the deployed MorphoChainlinkOracleV2 instance for the PAXG/USDC market, whose SCALE_FACTOR was effectively configured as if base and quote tokens had the same decimals. With actual decimals PAXG=18 and USDC=6, the correct scale is 10 ** (36 + 6 + 8 - 18 - 8) = 1e24, but the deployed oracle returned prices using 1e36.
Morpho Blue then consumed that oracle output exactly as designed. In verified Morpho.sol, _isHealthy computes:
uint256 borrowed = uint256(position[id][borrower].borrowShares).toAssetsUp(
market[id].totalBorrowAssets, market[id].totalBorrowShares
);
uint256 maxBorrow = uint256(position[id][borrower].collateral).mulDivDown(collateralPrice, ORACLE_PRICE_SCALE)
.wMulDown(marketParams.lltv);
return maxBorrow >= borrowed;
Because collateralPrice was inflated by 1e12, maxBorrow was inflated by the same factor. The economic invariant for this market is that the maximum USDC debt must equal PAXG collateral value, correctly normalized for token decimals and feed decimals, then discounted by LLTV. The code-level breakpoint is therefore the oracle constructor's SCALE_FACTOR derivation, followed by Morpho's _isHealthy borrow-limit check that trusted the bad value.
The vulnerable components are:
0xdd1778f71a4a1c6a0efebd8ae9f8848634ce1101, where deployer-supplied decimal parameters determined SCALE_FACTOR0xbbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb, where _isHealthy trusted oracle.price()0x8eaf7b29f02ba8d8c1d7aeb587403dcb16e2e943e4e2f5f94b0963c2386406c9, which exposed protocol USDC liquidity to the bad oracleThe relevant pre-state is Ethereum mainnet immediately before block 20956052, with Morpho market 0x8eaf7b29f02ba8d8c1d7aeb587403dcb16e2e943e4e2f5f94b0963c2386406c9 active and funded, oracle 0xdd1778f71a4a1c6a0efebd8ae9f8848634ce1101 already deployed, and public bundler 0x4095f064b8d3c3548a3bebfd0bbfd04750e30077 available to any caller.
The collector evidence and verified source establish the full arithmetic chain:
SCALE_FACTOR on chain is 1000000000000000000000000000000000000 (1e36).PAXG=18 and USDC=6, the correct factor should be 1e24.oracle.price() is inflated by 1e12, modulo ordinary integer-division remainder.At block 20956051, the feeds returned 266477128041 for PAXG/USD and 99997886 for USDC/USD. Using correct normalization, the market price should be 2664827614865778262552470359. The deployed oracle instead returned 2664827614865778262552470359223393982548. That value is the same quotient computed with a 1e12 larger scale factor, up to the expected integer-rounding remainder.
Applying Morpho's own borrow-limit formula to the attacker's collateral:
132577813003136114 wei PAXG0.915e18323266770 raw USDC units230002486670 raw USDC unitsSo the position should only have supported about 323.26677 USDC, but Morpho accepted a debt of 230002.48667 USDC. The exploit condition was therefore simple and permissionless: any account able to source a small amount of PAXG and submit a standard transaction could borrow against the inflated oracle before market operators corrected the configuration.
The ACT conditions were:
The violated security principles were also straightforward:
The seed transaction shows a single-transaction exploit path through the public Morpho helper:
0x4095F064B8d3c3548A3bebfd0Bbfd04750E30077::approve2(...)
0x4095F064B8d3c3548A3bebfd0Bbfd04750E30077::transferFrom2(PAXG, 132577813003136114)
0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb::supplyCollateral(..., 132577813003136114, attacker, ...)
0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb::borrow(..., 230002486670, 0, attacker, attacker)
FiatTokenV2_2::transfer(attacker, 230002486670)
The trace proves three important facts. First, the path is permissionless: the attacker used standard Permit2 approval flow and the public Morpho bundler. Second, Morpho recorded the attacker collateral in the intended market before borrowing. Third, the final USDC transfer went directly to the attacker EOA.
The balance diff confirms the same end state:
{
"token": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"holder": "0x02dbe46169fdf6555f2a125eee3dce49703b13f5",
"before": "0",
"after": "230002486670",
"delta": "230002486670"
}
Morpho lost exactly the same amount of USDC, showing that the exploit realization was the undercollateralized borrow itself, not a later unwind.
The adversary strategy was therefore a single-transaction oracle-mispricing borrow using only permissionless infrastructure: Permit2, the public Morpho bundler, Morpho collateral supply, and Morpho borrow.
The measurable loss in the seed exploit transaction was 230002486670 raw USDC units, or 230002.48667 USDC. That amount left Morpho Blue protocol liquidity and accrued to the adversary while the posted PAXG collateral economically supported only 323266770 raw USDC units of debt under correct pricing.
This created immediate bad-debt exposure for the misconfigured market and demonstrated that the issue was an anyone-can-take opportunity rather than a one-off attacker-specific setup.
0x256979ae169abb7fbbbbc14188742f4b9debf48b48ad5b5207cadcc99ccb493b metadata and calldata.supplyCollateral, borrow, and final USDC transfer.-230002486670 USDC and attacker +230002486670 USDC.MorphoChainlinkOracleV2 source for the constructor formula and price() implementation.Morpho.sol source for borrow and _isHealthy.