Platform: Code4rena
Start Date: 13/05/2022
Pot Size: $30,000 USDC
Total HM: 8
Participants: 65
Period: 3 days
Judge: hickuphh3
Total Solo HM: 1
Id: 125
League: ETH
Rank: 2/65
Findings: 5
Award: $3,253.78
π Selected for report: 1
π Solo Findings: 0
π Selected for report: jonah1005
Also found by: Picodes, WatchPug, berndartmueller, sorrynotsorry
1225.2497 USDC - $1,225.25
When withdrawing ETH collateral from LidoVault
with withdrawCollateral()
, stETH
is exchanged to ETH via Curve while using a slippage value of 2% (200
, L136).
The resulting exchanged amount receivedETHAmount
is then validated to be larger than the requested withdrawal _amount
minus 1% slippage:
require(withdrawAmount >= _amount.percentMul(99_00), Errors.VT_WITHDRAW_AMOUNT_MISMATCH);
If the curve stETH
pool is unbalanced and therefore causes a slippage of ~2%, the GeneralVault.withdrawCollateral
call will revert.
Manual review
Make sure to use the same value for slippage checks within GeneralVault
and LidoVault
.
#0 - sforman2000
2022-05-18T02:28:53Z
Duplicate of https://github.com/code-423n4/2022-05-sturdy-findings/issues/133 (high risk)
π Selected for report: pedroais
Also found by: 0x4non, 0x52, 0xf15ers, 0xliumin, CertoraInc, Dravee, GimelSec, IllIllI, MaratCerby, StErMi, TerrierLover, WatchPug, berndartmueller, cccz, dipp, fatherOfBlocks, hake, hickuphh3, hyh, isamjay, mtz, oyc_109, p4st13r4, peritoflores, rotcivegaf, saian, simon135, sorrynotsorry, sseefried, tabish, z3s
14.8433 USDC - $14.84
The LidoVault
contract uses a low level Solidity .call()
to send ETH to the user, but the require
to check the sent
return value is placed after the return
, hence the sent
success return value is not checked. This could cause a silent fail transferring ETH and loss of funds for the user.
// send ETH to user (bool sent, bytes memory data) = address(_to).call{value: receivedETHAmount}(''); return receivedETHAmount; require(sent, Errors.VT_COLLATERAL_WITHDRAW_INVALID); // @audit-info unreachable code
Manual review
Move the require
check before the return
.
#0 - sforman2000
2022-05-18T01:31:40Z
Duplicate of https://github.com/code-423n4/2022-05-sturdy-findings/issues/157 (high risk)
π Selected for report: rotcivegaf
Also found by: AuditsAreUS, MaratCerby, StErMi, berndartmueller, cccz, dipp
If a borrower deposits Curve LP tokens into the ConvexCurveLPVault
contract via the payable
function GeneralVault.depositCollateral(address _asset, uint256 _amount)
and accidentally sends a non-zero Ether value with it, then the Ether value sent will be locked within the contract without the ability to retrieve it.
ConvexCurveLPVault._depositToYieldPool
Manual review
Make sure the function ConvexCurveLPVault._depositToYieldPool
reverts if msg.value > 0
.
#0 - sforman2000
2022-05-18T02:26:47Z
π Selected for report: berndartmueller
Also found by: WatchPug
1680.7266 USDC - $1,680.73
Withdrawing ETH collateral via the withdrawCollateral
function using type(uint256).max
for the _amount
parameter reverts the transaction due to _asset
being the zero-address and IERC20Detailed(_asset).decimals()
not working for native ETH.
if (_amount == type(uint256).max) { uint256 decimal = IERC20Detailed(_asset).decimals(); // @audit-info does not work for native ETH. Transaction reverts _amount = _amountToWithdraw.mul(this.pricePerShare()).div(10**decimal); }
Manual review
Check _asset
and use hard coded decimal value (18
) for native ETH
#0 - HickupHH3
2022-06-03T04:33:15Z
Good find! Stated in _asset
description that null address is interpreted as ETH, which isn't a token, and therefore reverts when attempting to fetch its decimals.
π Selected for report: IllIllI
Also found by: 0x1f8b, 0x4non, 0xNazgul, 0xf15ers, 0xkatana, 0xliumin, AlleyCat, BouSalman, Dravee, Funen, GimelSec, Hawkeye, MaratCerby, Picodes, StErMi, TerrierLover, WatchPug, Waze, berndartmueller, bobirichman, cryptphi, csanuragjain, defsec, delfin454000, dipp, fatherOfBlocks, hake, hickuphh3, hyh, joestakey, kebabsec, mics, mtz, oyc_109, p4st13r4, p_crypt0, robee, rotcivegaf, sikorico, simon135, sorrynotsorry, tintin
49.399 USDC - $49.40
When changing state variables events are not emitted. Emitting events allows monitoring activities with off-chain monitoring tools.
YieldManager.setExchangeToken()
YieldManager.registerAsset()
YieldManager.setCurvePool()
ConvexCurveLPVault.setConfiguration()
CollateralAdapter.addCollateralAsset()
Emit events for state variable changes.
Zero-address checks are a best-practice for input validation of critical address parameters. While the codebase applies this to most most cases, there are many places where this is missing in constructors and setters.
Impact: Accidental use of zero-addresses may result in exceptions, burn fees/tokens or force redeployment of contracts.
_assetsList[_assetsCount] = _asset;
curveLPToken = _lpToken;
Add zero-address checks, e.g.:
require(_asset != address(0), "Zero-address");
LidoVault
to be only from Curve exchangeNative ETH fund transfers into the LidoVault
contract are only expected from the curveExchange
contract. Hence, it would be good to restrict incoming ETH fund transfers to prevent accidental native fund transfers from other sources.
/** * @dev Receive Ether */ receive() external payable {}
Modify the receive() function to only accept transfers from the wrapped token contract:
receive() external payable { require(msg.sender == address(curveExchange), 'Only curve exchange'); }
#0 - HickupHH3
2022-06-06T05:11:02Z
Low: L03 NC: L01, L02