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: 119/147
Findings: 1
Award: $4.52
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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
As seen from the function implementation, there is not possible to return a false boolean, as mostly they will just revert except the last line returning true
.
Furthermore, if we see from the codebase, usage of verifyOrder
function call, the return value of both bool and bytes32 is not being used anywhere. Maybe the bytes32 orderhash needed on off-chain mechanism, but the boolean is not necessary since there are two possible outcome, it will just revert or return true, thus on off-chain, if it's reverted, it's false, else true, so no need for this boolean.
File: EthenaMinting.sol 339: function verifyOrder(Order calldata order, Signature calldata signature) public view override returns (bool, bytes32) { 340: bytes32 taker_order_hash = hashOrder(order); 341: address signer = ECDSA.recover(taker_order_hash, signature.signature_bytes); 342: if (!(signer == order.benefactor || delegatedSigner[signer][order.benefactor])) revert InvalidSignature(); 343: if (order.beneficiary == address(0)) revert InvalidAmount(); 344: if (order.collateral_amount == 0) revert InvalidAmount(); 345: if (order.usde_amount == 0) revert InvalidAmount(); 346: if (block.timestamp > order.expiry) revert SignatureExpired(); 347: return (true, taker_order_hash); 348: }
Clearly as seen from the line below, the check is to make sure it's not a zero address, which is not align with the revert type an InvalidAmount
File: EthenaMinting.sol 343: if (order.beneficiary == address(0)) revert InvalidAmount();
Eventhough this will eventually be called only by MINTER_ROLE or REDEEMER_ROLE, but this verifyRoute
contains several checks, the length adresses, check if address is not zero, check if address is in custodianAdresses, but it's missing check if the address of routes is duplicates.
File: EthenaMinting.sol 351: function verifyRoute(Route calldata route, OrderType orderType) public view override returns (bool) { 352: // routes only used to mint 353: if (orderType == OrderType.REDEEM) { 354: return true; 355: } 356: uint256 totalRatio = 0; 357: if (route.addresses.length != route.ratios.length) { 358: return false; 359: } 360: if (route.addresses.length == 0) { 361: return false; 362: } 363: for (uint256 i = 0; i < route.addresses.length; ++i) { 364: if (!_custodianAddresses.contains(route.addresses[i]) || route.addresses[i] == address(0) || route.ratios[i] == 0) 365: { 366: return false; 367: } 368: totalRatio += route.ratios[i]; 369: } 370: if (totalRatio != 10_000) { 371: return false; 372: } 373: return true; 374: }
#0 - c4-pre-sort
2023-11-02T02:44:00Z
raymondfam marked the issue as sufficient quality report
#1 - c4-judge
2023-11-14T17:02:32Z
fatherGoose1 marked the issue as grade-b