Light ModeLight
Light ModeDark

One Bug Per Day

One H/M every day from top Wardens

Checkmark

Join over 1125 wardens!

Checkmark

Receive the email at any hour!

Ad

Users cannot unstake from YiedlETHStakingEtherfi.sol, because YieldAccount.sol is incompatible with ether.fi's WithdrawRequestNFT.sol

criticalCode4rena

Lines of code

https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/yield/YieldAccount.sol#L15

Vulnerability details

Impact

Users cannot unstake from YiedlETHStakingEtherfi.sol, because YieldAccount.sol is incompatible with ether.fi's WithdrawRequestNFT.sol.

Proof of Concept

In YiedlETHStakingEtherfi::stake, users yieldBorrow WETH, convert it to ETH, and stake in Ether.fi's LiquidityPool. Ether.fi's LiquidityPool will mint eETH (protocol token) to the user's yieldAccount.

Users will unstake() by requesting withdrawal of staked ETH from ether.fi's WithdrawRequestNFT::requestWithdraw.

The problem is WithdrawRequestNFT::requestWithdraw will _safeMint an NFT token to the user's yieldAccount as proof of request withdrawal. But the user's yieldAccount is not compatible with _safeMint because the implementation YieldAcount.sol doesn't have onERC721Received method required by _safeMint.

solidity
//src/yield/YieldAccount.sol ... //@audit YieldAcount doesn't inherit openzeppelin's ERC721Holder.sol, neither does it implement onERC721Received. Not compatible with safeMint method required by ether.fi's WithdrawRequestNFT. contract YieldAccount is IYieldAccount, Initializable { ...

(https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/yield/YieldAccount.sol#L15)

Flows: YieldStakingBase::unstake → YieldEthStakingEtherfi::protocolRequestWithdrawal → liquidityPool::requestWithdraw → WithdrawRequestNFT::requestWithdraw → _safeMint(recipient, requestId)

solidity
//https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L315 function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual { _mint(to, tokenId); require( _checkOnERC721Received(address(0), to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer" ); }

(https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L315)

Because yieldAcount.sol is cloned for every user and atomically used as caller address for ether.fi interactions, no one can unstake from YiedlETHStakingEtherfi.sol. User funds, yield and nft collateral's will be locked. When users' positions become unhealthy (e.g. Nft token price drops), yieldBorrow debts compounding can also put the protocol at risks.

Note: in the unit test file for test/yield/YieldEthStakingEtherfi.t.sol, unit tests passed only because a MockEtherfiWithdrawRequestNFT.sol is used that uses _mint() instead of _safeMint().

Tools Used

Manual

Recommended Mitigation Steps

Add onERC721Received support in YieldAccount.sol.

Assessed type

Other