You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Oct 1, 2023. It is now read-only.
sherlock-admin opened this issue
Mar 27, 2023
· 0 comments
Labels
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelHighA valid High severity issueRewardA payout will be made for this issue
User can bypass Carousel's depositFee using the deposit queue
Summary
The Carousel contract's deposit fee can be bypassed by using the deposit queue instead.
Vulnerability Detail
The deposit fee is only taken for direct deposits. The amount is determined by the time of the deposit. The later you deposit the more you pay:
function _deposit(
uint256 _id,
uint256 _assets,
address _receiver
) internal {
// mint logic, either in queue or direct deposit
if (_id != 0) {
uint256 assetsToDeposit = _assets;
if (depositFee > 0) {
(uint256 maxX, , uint256 minX) = getEpochConfig(_id);
// deposit fee is calcualted linearly between time of epoch creation and epoch starting (deposit window)
// this is because late depositors have an informational advantage
uint256 fee = _calculateFeePercent(int256(minX), int256(maxX));
// min minRequiredDeposit modifier ensures that _assets has high enough value to not devide by 0
// 0.5% = multiply by 10000 then divide by 50
uint256 feeAmount = _assets.mulDivDown(fee, 10000);
assetsToDeposit = _assets - feeAmount;
_asset().safeTransfer(treasury, feeAmount);
}
_mintShares(_receiver, _id, assetsToDeposit);
emit Deposit(msg.sender, _receiver, _id, _assets);
} else {
depositQueue.push(
QueueItem({assets: _assets, receiver: _receiver, epochId: _id})
);
emit DepositInQueue(msg.sender, _receiver, _id, _assets);
}
}
To bypass it, the user calls deposit() with id == 0 to deposit into the queue. Since the queue uses FILO to process deposits the user only has to process a single one to execute their deposit. Because they execute it themselves they also don't pay any fees to the relayer.
function mintDepositInQueue(uint256 _epochId, uint256 _operations)
external
epochIdExists(_epochId)
epochHasNotStarted(_epochId)
nonReentrant
{
// make sure there is already a new epoch set
// epoch has not started
QueueItem[] memory queue = depositQueue;
uint256 length = depositQueue.length;
// dont allow minting if epochId is 0
if (_epochId == 0) revert InvalidEpochId();
if (length == 0) revert OverflowQueue();
// relayers can always input a very big number to mint all deposit queues, without the need to read depostQueue length first
if (_operations > length) _operations = length;
// queue is executed from the tail to the head
// get last index of queue
uint256 i = length - 1;
while ((length - _operations) <= i) {
// this loop impelements FILO (first in last out) stack to reduce gas cost and improve code readability
// changing it to FIFO (first in first out) would require more code changes and would be more expensive
_mintShares(
queue[i].receiver,
_epochId,
queue[i].assets - relayerFee
);
emit Deposit(
msg.sender,
queue[i].receiver,
_epochId,
queue[i].assets - relayerFee
);
depositQueue.pop();
if (i == 0) break;
unchecked {
i--;
}
}
emit RelayerMinted(_epochId, _operations);
asset.safeTransfer(msg.sender, _operations * relayerFee);
}
This allows them to deposit into the Carousel at the last minute without having to pay the deposit fee for the informational advantage.
The deposit fee should also be implemented for the queue:
(uint256 maxX, , uint256 minX) = getEpochConfig(_id);
uint256 fee = _calculateFeePercent(int256(minX), int256(maxX));
while ((length - _operations) <= i) {
// this loop impelements FILO (first in last out) stack to reduce gas cost and improve code readability
// changing it to FIFO (first in first out) would require more code changes and would be more expensive
unit fee
uint amount = queue[i].assets - relayerFee;
uint256 feeAmount = amount.mulDivDown(fee, 10000);
_mintShares(
queue[i].receiver,
_epochId,
amount - feeAmount
);
emit Deposit(
msg.sender,
queue[i].receiver,
_epochId,
amount - feeAmount
);
depositQueue.pop();
if (i == 0) break;
unchecked {
i--;
}
}
Sign up for freeto subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Labels
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelHighA valid High severity issueRewardA payout will be made for this issue
Ruhum
high
User can bypass Carousel's depositFee using the deposit queue
Summary
The Carousel contract's deposit fee can be bypassed by using the deposit queue instead.
Vulnerability Detail
The deposit fee is only taken for direct deposits. The amount is determined by the time of the deposit. The later you deposit the more you pay:
To bypass it, the user calls
deposit()
withid == 0
to deposit into the queue. Since the queue uses FILO to process deposits the user only has to process a single one to execute their deposit. Because they execute it themselves they also don't pay any fees to the relayer.This allows them to deposit into the Carousel at the last minute without having to pay the deposit fee for the informational advantage.
Impact
Loss of funds for the treasury.
Code Snippet
https://github.com/sherlock-audit/2023-03-Y2K/blob/main/Earthquake/src/v2/Carousel/Carousel.sol#L310-L355
https://github.com/sherlock-audit/2023-03-Y2K/blob/main/Earthquake/src/v2/Carousel/Carousel.sol#L470-L501
Tool used
Manual Review
Recommendation
The deposit fee should also be implemented for the queue:
Duplicate of #75
The text was updated successfully, but these errors were encountered: