Platform: Code4rena
Start Date: 24/10/2023
Pot Size: $36,500 USDC
Total HM: 4
Participants: 147
Period: 6 days
Judge: 0xDjango
Id: 299
League: ETH
Rank: 12/147
Findings: 2
Award: $524.94
๐ Selected for report: 0
๐ Solo Findings: 0
๐ Selected for report: ayden
Also found by: 0xWaitress, Madalad, Yanchuan, cartlex_, ciphermarco, critical-or-high, d3e4, mert_eren, peanuts, pontifex, trachev, twcctop
520.4229 USDC - $520.42
Transfer usde
to an empty vault may brick valut because of the high value of _checkMinShares
after transfer. Due to the calculation logic of _checkMinShares
, a malicious user can send usde
to an empty vault and increase the amount of _checkMinShares
_checkMinShares
is used to check min share user mint.It it designed to prevent inflation attack (https://blog.openzeppelin.com/a-novel-defense-against-erc4626-inflation-attacks) .
The issue is , when malicious user transfer some usde
token to empty StakedUSDe
vault. The MIN_SHARES
become very hard to reach,it may brick the vault.
poc:
add in StakedUSDeTest
// forge test --mt test_tt_stake_transfer_dos -vv function test_tt_stake_transfer_dos() public { uint256 amount = 10000 ether; usdeToken.mint(alice, amount); usdeToken.mint(bob,1 ether); //bob send token to increase totalAssets() in stakedUSDe ๏ผthis will dos the funtion `deposit` vm.startPrank(bob); usdeToken.transfer(address(stakedUSDe),1 ether); vm.stopPrank(); vm.startPrank(alice); usdeToken.approve(address(stakedUSDe), amount); //MinSharesViolation vm.expectRevert(IStakedUSDe.MinSharesViolation.selector); stakedUSDe.deposit(amount, alice); vm.stopPrank(); }
In this poc I use 10000 ether
for example ,actually it's a very large number, and it's big enough to prove the concept.
foundry
A new method to prevent inflation attack in needed,refer to https://blog.openzeppelin.com/a-novel-defense-against-erc4626-inflation-attacks
ERC4626
#0 - c4-pre-sort
2023-10-31T19:29:34Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-10-31T19:29:47Z
raymondfam marked the issue as duplicate of #32
#2 - c4-judge
2023-11-10T20:52:19Z
fatherGoose1 changed the severity to 2 (Med Risk)
#3 - c4-judge
2023-11-10T20:59:17Z
fatherGoose1 marked the issue as satisfactory
๐ Selected for report: 0xmystery
Also found by: 0x11singh99, 0xAadi, 0xAlix2, 0xG0P1, 0xStalin, 0xWaitress, 0x_Scar, 0xhacksmithh, 0xhunter, 0xpiken, Al-Qa-qa, Arz, Avci, Bauchibred, BeliSesir, Breeje, Bughunter101, DarkTower, Eeyore, Fitro, HChang26, Imlazy0ne, J4X, JCK, Kaysoft, Kral01, Madalad, Mike_Bello90, Noro, PASCAL, PENGUN, Proxy, Rickard, Shubham, SovaSlava, Strausses, Team_Rocket, ThreeSigma, Topmark, Udsen, Walter, Yanchuan, Zach_166, ZanyBonzy, adam-idarrha, adeolu, almurhasan, arjun16, ast3ros, asui, ayden, btk, cartlex_, castle_chain, cccz, chainsnake, codynhat, critical-or-high, cryptonue, csanuragjain, deepkin, degensec, dirk_y, erebus, foxb868, ge6a, hunter_w3b, jasonxiale, kkkmmmsk, lanrebayode77, lsaudit, marchev, matrix_0wl, max10afternoon, nuthan2x, oakcobalt, oxchsyston, pavankv, peanuts, pep7siup, pipidu83, pontifex, ptsanev, qpzm, radev_sw, rokinot, rotcivegaf, rvierdiiev, sorrynotsorry, squeaky_cactus, supersizer0x, tnquanghuy0512, twcctop, twicek, young, zhaojie, ziyou-
4.5226 USDC - $4.52
QA0:
verifyRoute should revert
if ordertype differ , because if orderType is not correct the verify logic will bypass
function verifyRoute(Route calldata route, OrderType orderType) public view override returns (bool) { // routes only used to mint if (orderType == OrderType.REDEEM) { - return true; + revert(); } ...
QA1:
in function transferInRewards
, getUnvestedAmount() is always zero, no need to add again.
function transferInRewards(uint256 amount) external nonReentrant onlyRole(REWARDER_ROLE) notZero(amount) { if (getUnvestedAmount() > 0) revert StillVesting(); - uint256 newVestingAmount = amount + getUnvestedAmount(); + uint256 newVestingAmount = amount; vestingAmount = newVestingAmount; lastDistributionTimestamp = block.timestamp; // transfer assets from rewarder to this contract IERC20(asset()).safeTransferFrom(msg.sender, address(this), amount); emit RewardsReceived(amount, newVestingAmount); }
QA2๏ผ
StakedUSDeV2
# setCooldownDuration
should Check new duration differ from old one
function setCooldownDuration(uint24 duration) external onlyRole(DEFAULT_ADMIN_ROLE) { if (duration > MAX_COOLDOWN_DURATION) { revert InvalidCooldown(); } uint24 previousDuration = cooldownDuration; cooldownDuration = duration; emit CooldownDurationUpdated(previousDuration, cooldownDuration); }
QA3๏ผ
EthenaMinting
# hashOrder
better have chain id.
function hashOrder(Order calldata order) public view override returns (bytes32) { return ECDSA.toTypedDataHash(getDomainSeparator(), keccak256(encodeOrder(order))); }
#0 - c4-pre-sort
2023-11-02T02:44:53Z
raymondfam marked the issue as sufficient quality report
#1 - c4-judge
2023-11-14T17:01:05Z
fatherGoose1 marked the issue as grade-b