Calculated from recorded token losses using historical USD prices at the incident time.
0xa17dc37e1b65c65d20042212fb834974f7faaa961442e3fc05393778705f84740x7398e7e3603119d9241e45f688734436fd7b1540Ethereum0x9dce7a180c34203fee8ce8ca62f244feeb67bd30Ethereum0xd060ebd4f56be8866376a3616b6e5aef87f945d2EthereumIn Ethereum block 24538897, an unprivileged adversary executed transaction 0xa17dc37e1b65c65d20042212fb834974f7faaa961442e3fc05393778705f8474 against Ploutos Market (0x7398e7e3603119d9241e45f688734436fd7b1540) and drained most borrowable WETH by posting only 8.879192 USDC collateral.
The borrow passed because the protocol oracle (0x9dce7a180c34203fee8ce8ca62f244feeb67bd30) priced USDC using the BTC/USD feed (0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c). This mapping was set in the prior block by transaction 0xcfedf63b37a6cd45b21bc94e3de5412fee0765e7dad6b7c8561a01cebd193ab6.
Root cause: AaveOracle accepted an incompatible source assignment (USDC -> BTC/USD) and getAssetPrice returned raw latestAnswer() without semantic validation/sanity bounds, so collateral valuation became deterministically incorrect.
This is an ACT case: one public transaction by an unprivileged EOA realized the exploit predicate and netted 181.745096492453810260 ETH.
Ploutos uses an Aave v3-style valuation path where collateral and debt checks call AaveOracle.getAssetPrice(asset) and compare normalized values in a common base currency (BASE_CURRENCY_UNIT = 1e8).
Relevant components:
0x7398e7e3603119d9241e45f688734436fd7b15400x9dce7a180c34203fee8ce8ca62f244feeb67bd300xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb480xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc20xF4030086522a5bEEa4988F8cA5B36dbC97BeE88cExploit pre-state (just before block 24538897) is publicly reconstructible and already contains the wrong USDC source from tx 0xcfedf63b... in block 24538896.
Root cause category: ATTACK.
The protocol safety invariant is: each listed asset must map to a semantically correct oracle feed for that asset at the expected base-currency scale. Borrow safety inherits this assumption.
The concrete code-level breakpoint is in AaveOracle:
_setAssetsSources writes assetsSources[asset] = AggregatorInterface(source) with no asset/feed compatibility checks.getAssetPrice returns uint256(source.latestAnswer()) when positive, with no semantic sanity guard that USDC is actually mapped to a USD-stable feed.As a result, when USDC was mapped to BTC/USD, borrow logic treated 8.879192 USDC as extremely valuable collateral and allowed borrowing 187.366746326704993556 WETH.
Oracle implementation confirms the vulnerable behavior:
function _setAssetsSources(address[] memory assets, address[] memory sources) internal {
require(assets.length == sources.length, Errors.INCONSISTENT_PARAMS_LENGTH);
for (uint256 i = 0; i < assets.length; i++) {
assetsSources[assets[i]] = AggregatorInterface(sources[i]);
emit AssetSourceUpdated(assets[i], sources[i]);
}
}
function getAssetPrice(address asset) public view override returns (uint256) {
AggregatorInterface source = assetsSources[asset];
...
int256 price = source.latestAnswer();
if (price > 0) {
return uint256(price);
} else {
return _fallbackOracle.getAssetPrice(asset);
}
}
The assignment and read paths are independent of asset/feed semantic correctness.
In tx 0xcfedf63b37a6cd45b21bc94e3de5412fee0765e7dad6b7c8561a01cebd193ab6, oracle event data shows:
AssetSourceUpdated(asset=USDC, source=0xF403...E88c)Independent state calls at block 24538897 confirm:
getSourceOfAsset(USDC) = 0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88cdescription() = "BTC / USD"latestAnswer() = 6855405329514Seed trace shows the atomic flow:
UniswapV2Pair::swap(8879192, ...) -> attackerContract::uniswapV2Call(...)
Pool::deposit(USDC, 8879192, attackerContract, 0)
Pool::borrow(WETH, 187366746326704993556, 2, 0, attackerContract)
AaveOracle::getAssetPrice(USDC) -> BTC/USD latestAnswer -> 6855405329514
AaveOracle::getAssetPrice(WETH) -> ETH/USD latestAnswer -> 208047000000
Because USDC price was massively inflated relative to WETH, the health-factor and borrow checks passed.
The same transaction then:
0.004289216474598283 WETH to the pair,187.362457110230395273 WETH,5.612909926920174741 ETH to builder address 0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97,181.749547183310220532 ETH to attacker EOA 0x3885869b0f4526806b468a0c64a89bb860a18cee.Balance deltas give the deterministic net result (after gas/transfers):
+181.745096492453810260 ETH-187.362457110230395273 ETHThe opportunity exists when all of the following hold:
All conditions were satisfied at block 24538897.
Adversary-related accounts:
0x3885869b0f4526806b468a0c64a89bb860a18cee: seed tx sender and final ETH recipient.0x3e47945cca05439f99029a3d21e3166ce1a84fab: executes flash-swap callback, collateral deposit, borrow, repayment, unwrap, and payout.End-to-end lifecycle:
0xcfedf63b... remaps USDC source to BTC/USD.0xa17dc37e... executes flash swap -> deposit USDC -> borrow WETH while oracle is misconfigured.Measured impact:
187.362457110230395273 WETH181.745096492453810260 ETHAffected parties/components:
aWETH/pool liquidity path)Exposure closure:
0xee3d7556528d3ceb00681a3c7ed7be3751c83923675bc3774c77f9f4e60d20f0) remapped USDC to the intended feed.0xa17dc37e1b65c65d20042212fb834974f7faaa961442e3fc05393778705f84740xcfedf63b37a6cd45b21bc94e3de5412fee0765e7dad6b7c8561a01cebd193ab60xee3d7556528d3ceb00681a3c7ed7be3751c83923675bc3774c77f9f4e60d20f00x9dce7a180c34203fee8ce8ca62f244feeb67bd300x7398e7e3603119d9241e45f688734436fd7b15400xF4030086522a5bEEa4988F8cA5B36dbC97BeE88ctrace.cast.log)balance_diff.json)metadata.json)0x9dce....