Root Cause Analysis
The Vulnerability
The exploit was a multi-stage attack where the attacker leveraged excessive permissions within the Arcadia Asset Management architecture.
Initial Attack Base
The attacker created multiple Arcadia Accounts to act as the base for the exploit.
View Transaction on BasescanThe Attack Sequence:
- Taken three Morpho flashloans of approximately $1.5 billion.
- Linked the Asset Manager to their account, setting themselves as the initiator.
- Created a small Liquidity Provider (LP) position.
- Repaid all debt of the victim account using the flashloaned funds.
- Triggered a rebalance for their own LP position, injecting malicious custom calldata.
- Abused missing validation to call the victim's Arcadia Account from within the context of the Asset Manager.
- Withdrew the position, decomposed it, and stole the owner's funds.
- Since the target account had its debt cleared, it ended in a 'healthy' state, bypassing failsafes.
Detailed Transaction Analysis
Explore Tenderly Trace
Technical Execution
Attack Logic
The root cause of the exploit is that the attacker could hijack the msg.sender context of the Asset Manager. Since the victim account trusted the Asset Manager, the attacker's injected call was executed with full privileges.
Mitigation Strategy
A critical check should have been performed to ensure the router is not a protocol account. Ideally, the swap logic should be handled by an isolated, non-permissioned intermediate contract.

Relevant Contracts
0xC729213B9b72694F202FeB9cf40FE8ba5F5A45090x9529E5988ceD568898566782e88012cf11C3Ec990x6250DFD35ca9eee5Ea21b5837F6F21425BEe45530x87730d2c2A2D453d3E2248Fd7360D31FEf9c7f04Vulnerable Code Snippet
function _swapViaRouter(
address positionManager,
RebalancerPositionState memory position,
bool zeroToOne,
bytes memory swapData
) internal returns (uint256 balance0, uint256 balance1) {
// Decode the swap data.
(address router, uint256 amountIn, bytes memory data) = abi.decode(swapData, (address, uint256, bytes));
// Approve token to swap.
address tokenToSwap = zeroToOne ? position.token0 : position.token1;
ERC20(tokenToSwap).safeApproveWithRetry(router, amountIn);
// Execute arbitrary swap.
(bool success, bytes memory result) = router.call(data);
require(success, string(result));
}
Prerequisites
Required knowledge to understand this exploit.
Flash Loans
Borrowing assets without collateral for one transaction.
Msg.Sender Context
How context changes (or doesn't) between calls.
Arbitrary Calls
Dangers of .call() with user data.
State Management
Checks-Effects-Interactions pattern.