DeFiFoundrySolidity
16,653 OP
View results
Submission Details
Severity: medium
Invalid

Missing Shutdown Check in `StrategyOp::_harvestAndReport()`, `StrategyMainnet::_harvestAndReport()`, `StrategyArb::_harvestAndReport()`

Summary

The strategy's _harvestAndReport() function lacks implementation of the shutdown check mechanism that is standard in Yearn V3 protocols. While the function is permissioned, the absence of this safety control would impact the protocol's ability to properly handle emergency situations.

Vulnerability Details

The current implementation of `_harvestAndReport()`:

solidity
function _harvestAndReport() internal override returns (uint256 _totalAssets) {
uint256 claimable = transmuter.getClaimableBalance(address(this));
uint256 unexchanged = transmuter.getUnexchangedBalance(address(this));
uint256 underlyingBalance = underlying.balanceOf(address(this));
_totalAssets = unexchanged + asset.balanceOf(address(this)) + underlyingBalance;
}

The function only performs balance checks without:

  1. Implementing the shutdown state check

  2. Having active management logic for rewards/redeployment

  3. Including emergency withdrawal capabilities

this function is permissioned and can only be called by trusted addresses, but it deviates from the Yearn V3 standard implementation which includes critical safety controls.

while the Yean 3 implementation specifics

function _harvestAndReport() internal override returns (uint256 _totalAssets) {
// Only harvest and redeploy if the strategy is not shutdown.
if(!TokenizedStrategy.isShutdown()) {
// Claim all rewards and sell to asset.
_claimAndSellRewards();
// Check how much we can re-deploy into the yield source.
uint256 toDeploy = Math.min(
asset.balanceOf(address(this)),
availableDepositLimit(address(this))
);
// If greater than 0.
if (toDeploy > 0) {
// Deposit the sold amount back into the yield source.
_deployFunds(toDeploy)
}
}
// Return full balance no matter what.
_totalAssets = yieldSource.balanceOf(address(this)) + asset.balanceOf(address(this));
}

Impact

  1. Limited control during emergency scenarios

  2. No distinction between normal operation and shutdown states

  3. Potential inefficiencies in capital deployment

  4. Missing opportunity to implement emergency fund-securing mechanisms

Tools Used

Manual code review

Comparison with Yearn V3 implementation

https://docs.yearn.fi/developers/v3/strategy_writing_guide

Recommendations

Implement the standard shutdown check pattern with active management:

function _harvestAndReport() internal override returns (uint256 _totalAssets) {
bool isShutdown = TokenizedStrategy.isShutdown();
if (!isShutdown) {
// Active management during normal operation
uint256 claimable = transmuter.getClaimableBalance(address(this));
if (claimable > 0) {
// Implement claiming logic
transmuter.claim();
}
// Deploy idle funds if available
uint256 idleAssets = asset.balanceOf(address(this));
if (idleAssets > 0) {
// Implement deployment strategy
}
} else {
// Optional: Implement emergency withdrawal
// _emergencyWithdraw();
}
// Balance calculation remains unchanged
uint256 unexchanged = transmuter.getUnexchangedBalance(address(this));
uint256 underlyingBalance = underlying.balanceOf(address(this));
_totalAssets = unexchanged + asset.balanceOf(address(this)) + underlyingBalance;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
10 months ago

Appeal created

inallhonesty Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Design choice
inallhonesty Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.