An attacker may claim risk-free rewards without risking their staked capital
Description
The Example Vault aims for an APR of 20%. At the beginning of every new period (1 day), the vault distributes the daily interest and calculates the new token price. The caveat here is that users can stake capital at the end of a period and reap rewards instantly at the beginning of the next period. Depositing on the last block before the start of new period and redeeming it in the next block would essentially guarantee an instant riskless profit.
function compute () public {
uint256 currentTimestamp = block.timestamp; // solhint-disable-line not-rely-on-time
uint256 newPeriod = DateUtils.diffDays(startOfYearTimestamp, currentTimestamp);
if (newPeriod <= currentPeriod) return;
for (uint256 i = currentPeriod + 1; i <= newPeriod; i++) {
_records[i].apr = _records[i - 1].apr;
_records[i].totalDeposited = _records[i - 1].totalDeposited;
uint256 diff = uint256(_records[i - 1].apr) * USDF_DECIMAL_MULTIPLIER * uint(100)/ uint256(365);
_records[i].tokenPrice = _records[i - 1].tokenPrice + (diff / uint256(10000));
_records[i].dailyInterest = _records[i - 1].totalDeposited * uint256(_records[i - 1].apr) / uint256(365) / uint256(100);
}
currentPeriod = newPeriod;
}
Impact
An attacker can effectively siphon out money from vaults without participating in the strategies or taking on any risk. The profit is directly dependent on attackers' capital. For a concrete example: With an APR of 20% and a capital of 1 Million USDC, the attacker can freely profit 540 dollars a day (0.054%) disregarding the gas fee. The profit scales linearly and for 10 million USDC, the profit would be $5400/day.
Recommendations
There are multiple strategies that can be taken to address this:
Lock the users capital for a minimum period of time to prevent instant withdrawals.
Immediately forward funds to the
yieldReserve
, so a large deposit is not withdrawable instantly.
Remediation
The issue has been acknowledged by Fractal.