Skip to content

Latest commit

 

History

History
63 lines (38 loc) · 1.91 KB

File metadata and controls

63 lines (38 loc) · 1.91 KB

Loud Snowy Marmot

High

L0ckin7 - Reentrancy Risk in _withdraw and _deposit Functions

L0ckin7 High

Summary

The _withdraw and _deposit functions transfer tokens before updating the internal state (_totalAssets), this can lead to reentrancy attacks.

AutoCompoundingPodLp :

_deposit https://github.com/sherlock-audit/2025-01-peapods-finance/blob/main/contracts/contracts/AutoCompoundingPodLp.sol#L135

IERC20(_asset()).safeTransferFrom(_msgSender(), address(this), _assets);

This transfers tokens from the caller before updating _totalAssets.

_withdraw https://github.com/sherlock-audit/2025-01-peapods-finance/blob/main/contracts/contracts/AutoCompoundingPodLp.sol#L199

IERC20(_asset()).safeTransfer(_receiver, _assets);

This transfers tokens before reducing _totalAssets and burning shares.

Impact

Allow an attacker to manipulate the contract's state, such as withdrawing more tokens than they are entitled to.

Attack Path

If an attacker deposits a small amount to the contract then calls withdraw triggering _withdraw, a malicious fallback function in the attacker's contract can re-enters _withdraw before _totalAssets and balances are updated, the attacker calls withdraw again draining funds before state updates.

Recommendations

We can use OpenZeppelin’s ReentrancyGuard:

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract AutoCompoundingPodLp is IERC4626, ERC20, ERC20Permit, Ownable, ReentrancyGuard {

Then, add nonReentrant to _withdraw and _deposit:

function _deposit(uint256 _assets, uint256 _shares, address _receiver) internal nonReentrant {
function _withdraw(uint256 _assets, uint256 _shares, address _caller, address _owner, address _receiver) internal nonReentrant {

So we ensures state updates are completed before external calls, preventing reentrancy attacks.