# Vlad Audit Report
# 1. Critical. EthStrategy can't claim withdrawn ETH from Lido
## Description
In function `claimLidoWithdrawals()` EthStrategy expects to receive withdrawn ETH from Lido, however it doesn't have a function to receive ETH: there is no `receive() payable` or `fallback() payable`.
As a result, ETH can't be withdrawn, and withdrawal NFT can't be transferred either.
## Recommendation
Add function `receive() payable`.
Also add end-to-end tests to ensure protocol works as expected
# 2. High. Funds withdrawn via `EthStrategy.ownerWithdraw()` will stuck in StakingManager
## Description
Owner can withdraw ETH to StakingManager.sol via this function. However funds can't be retrieved from StakingManager contract, there is no such a method.
```solidity
function ownerWithdraw(uint256 amount) external onlyOwner returns (uint256 withdrawnAmount) {
(bool success, bytes memory data) = stakingManager.call{ value: amount }("");
if (!success) revert TransferFailed(data);
emit EthWithdrawn(amount);
return amount;
}
```
## Recommendation
Remove function `ownerWithdraw()`. ETH balance in `EthStrategy` belongs to WrappedEth holders, not to owner.
# 3. Medium. Incorrect role management of `address staker` and `address depositor`
## Description
Addresses `staker` and `depositor` are meant to be different: `depositor` is EdgelessDeposit, staker is EOA
However functions which are called by EdgelessDeposit have `onlyStaker` modifier, hence can't be called by EdgelessDeposit:
```solidity
function stake(address asset, uint256 amount) external payable onlyStaker {
_stakeEth(msg.value);
emit Stake(asset, amount);
}
function withdraw(uint256 amount) external onlyStaker {
_withdrawEth(amount);
}
```
## Recommendation
Refactor access control
# 4. Medium. `mintEthBasedOnStakedAmount()` can mint less than expected
## Description
According to Natspec:
```solidity
/**
* @notice Mint wrapped tokens based on the amount of Eth staked
@> * @dev The owner can only mint up to the amount of Eth deposited + Eth staking rewards from Lido
* @param to Address to mint wrapped tokens to
* @param amount Amount of wrapped tokens to mint
*/
function mintEthBasedOnStakedAmount(address to, uint256 amount) external onlyOwner {
uint256 maxMint = stakingManager.getAssetTotal(stakingManager.ETH_ADDRESS()) - wrappedEth.totalSupply();
if (maxMint < amount) revert MaxMintExceeded();
wrappedEth.mint(to, amount);
emit MintWrappedEth(to, amount);
}
```
Let's take a look on how `underlyingAssetAmount()` is calculated. It doesn't take into consideration pending withdrawals requested via `requestLidoWithdrawal()`.
```solidity
function underlyingAssetAmount() external view returns (uint256) {
return address(this).balance + LIDO.balanceOf(address(this));
}
```
## Recommendation
According to Lido documentation, withdrawn ETH amount can't be greater than requested stETH amount, however can be slightly less. Keep
# 5. Low. Bridged funds can be lost if `l2Eth = address(0)`
## Description
Soon after deployment user can call `depositEth()` when all values are set except `l2Eth`. And if `autoBridge = true`, bridged funds will be lost:
```solidity
function _bridgeToL2(WrappedToken wrappedToken, address l2WrappedToken, address to, uint256 amount) internal {
if (autoBridge) {
wrappedToken.approve(address(l1standardBridge), amount);
l1standardBridge.depositERC20To(address(wrappedToken), l2WrappedToken, to, amount, 0, "");
emit BridgeToL2(address(wrappedToken), l2WrappedToken, to, amount);
}
}
```
## Recommendation
Add check to ensure that values are correctly set:
```diff
function _bridgeToL2(WrappedToken wrappedToken, address l2WrappedToken, address to, uint256 amount) internal {
if (autoBridge) {
+ require(l2Eth != address(0));
wrappedToken.approve(address(l1standardBridge), amount);
l1standardBridge.depositERC20To(address(wrappedToken), l2WrappedToken, to, amount, 0, "");
emit BridgeToL2(address(wrappedToken), l2WrappedToken, to, amount);
}
}
```
# 6. Low. `underlyingAssetAmount()` treats stETH is equal to ETH
## Description
Here ETH balance is mixed with stETH balance:
```solidity
function underlyingAssetAmount() external view returns (uint256) {
return address(this).balance + LIDO.balanceOf(address(this));
}
```
However historically stETH had deviations from ETH:
https://dune.com/LidoAnalytical/Curve-ETHstETH
## Recommendation
Consider using Oracle to convert stETH into ETH
# 7. Info. Make sure that array `requestIds` in function `claimLidoWithdrawals` is sorted
Otherwise some `requestIds` won't be processed accroding to Lido docs:
```solidity
/// @notice Finds the list of hints for the given `_requestIds` searching among the checkpoints with indices
/// in the range `[_firstIndex, _lastIndex]`.
@> /// NB! Array of request ids should be sorted
...
function findCheckpointHints(uint256[] calldata _requestIds, uint256 _firstIndex, uint256 _lastIndex)
```