Description
The deposit(), createAtoms(), createTriples(), and depositBatch() functions all accept a receiver parameter that is forwarded directly to ethMultiVault.deposit() without any validation that it matches msg.sender.
This means a dApp integrating the Fee Proxy could set the receiver to:
- The proxy contract itself
- The dApp's own wallet
- Any arbitrary address
...as long as that address has approved the proxy on the MultiVault. The user pays msg.value (deposit + fees), but the shares are minted to someone else entirely.
Attack scenario
- User interacts with a malicious/misconfigured dApp that uses the Fee Proxy
- The dApp calls
proxy.deposit{value: userFunds}(dAppWallet, termId, curveId, 0)
- The user's ETH is deposited, fees are collected, but shares go to the dApp's wallet — not the user
- The user has no recourse — the transaction was valid from the contract's perspective
Suggested fix
Option A: Force receiver == msg.sender
function deposit(...) external payable returns (uint256 shares) {
// receiver is always the caller
shares = ethMultiVault.deposit{value: assets}(
msg.sender, // hardcoded
termId, curveId, minShares
);
}
Option B: Validate receiver explicitly
if (receiver != msg.sender) {
revert Errors.IntuitionFeeProxy_ReceiverMismatch();
}
Affected functions
deposit()
createAtoms()
createTriples()
depositBatch()
Description
The
deposit(),createAtoms(),createTriples(), anddepositBatch()functions all accept areceiverparameter that is forwarded directly toethMultiVault.deposit()without any validation that it matchesmsg.sender.This means a dApp integrating the Fee Proxy could set the
receiverto:...as long as that address has approved the proxy on the MultiVault. The user pays
msg.value(deposit + fees), but the shares are minted to someone else entirely.Attack scenario
proxy.deposit{value: userFunds}(dAppWallet, termId, curveId, 0)Suggested fix
Option A: Force
receiver == msg.senderOption B: Validate receiver explicitly
Affected functions
deposit()createAtoms()createTriples()depositBatch()