Platform: Code4rena
Start Date: 29/06/2022
Pot Size: $50,000 USDC
Total HM: 20
Participants: 133
Period: 5 days
Judge: hickuphh3
Total Solo HM: 1
Id: 142
League: ETH
Rank: 101/133
Findings: 2
Award: $26.72
π Selected for report: 0
π Solo Findings: 0
π Selected for report: IllIllI
Also found by: 0x29A, 0xDjango, 0xc0ffEE, AmitN, BowTiedWardens, StErMi, auditor0517, berndartmueller, cccz, danb, dipp, dirk_y, hansfriese, horsefacts, hyh, kirk-baird, oyc_109, peritoflores, rfa, sseefried, swit, xiaoming90, zzzitron
5.5216 USDC - $5.52
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L327-L329
If User send eth but he doesn't set another baseAssets instead of weth, the fund will just lost with no revert
The condition expect that user will using weth address as order.baseAsset when he trying to send ether to the contract. If he send eth with any token address (not weth address), the eth will just lost. In current implementation, those condition wont't executed and will executed at L338 (sending order.baseAsset to recipient).
Manual review
Change the checks to:
if (msg.value > 0) { //validate weth address if msg value != 0 require(weth == order.baseAsset, "Invalid base asset"); // check enough ETH was sent to cover the premium require(msg.value == order.premium, "Incorrect ETH amount sent");
I think it's necessary to make the contracts saver by doing this way
#0 - rotcivegaf
2022-07-04T23:28:07Z
A part duplicate of #226
#1 - outdoteth
2022-07-06T19:26:04Z
Duplicate: Native ETH can be lost if itβs not utilised in exercise and fillOrder: https://github.com/code-423n4/2022-06-putty-findings/issues/226
π Selected for report: GalloDaSballo
Also found by: 0v3rf10w, 0x1f8b, 0xA5DF, 0xDjango, 0xHarry, 0xKitsune, 0xNazgul, 0xNineDec, 0xc0ffEE, 0xf15ers, 0xkatana, 0xsanson, ACai, Aymen0909, Bnke0x0, BowTiedWardens, Chom, ElKu, Fitraldys, Funen, Haruxe, Hawkeye, IllIllI, JC, JohnSmith, Kaiziron, Kenshin, Lambda, Limbooo, MadWookie, Metatron, MiloTruck, Picodes, PwnedNoMore, Randyyy, RedOneN, ReyAdmirado, Ruhum, Sm4rty, StErMi, StyxRave, TerrierLover, TomJ, Tomio, UnusualTurtle, Waze, Yiko, _Adam, __141345__, ajtra, ak1, apostle0x01, asutorufos, c3phas, cRat1st0s, catchup, codetilda, cryptphi, datapunk, defsec, delfin454000, durianSausage, exd0tpy, fatherOfBlocks, gogo, grrwahrr, hake, hansfriese, horsefacts, ignacio, jayfromthe13th, joestakey, ladboy233, m_Rassska, mektigboy, minhquanym, mrpathfindr, natzuu, oyc_109, rajatbeladiya, reassor, rfa, robee, rokinot, sach1r0, saian, sashik_eth, simon135, slywaters, swit, z3s, zeesaw, zer0dot
21.2007 USDC - $21.20
GAS
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L271 https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L657 https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L669
Using calldata to store read only array variable can save gas
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L293 https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L598-L599
Using != rather than > to validate value is not 0 for uint is more effective
i
inside for()Occurrences: https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L594 https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L611 https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L627 https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L637 https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L647 https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L658 https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L670 https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L728 https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L742
Using unchecked and prefix increment is way more effective than the current implementation
RECOMMENDED MITIGATION STEP
for (uint256 i = 0; i < orders.length;) { positionIds[i] = fillOrder(orders[i], signatures[i], floorAssetTokenIds[i]); unchecked{++i;} }
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L509
Using else
statement can avoid MLOADing and validating order.isCall
and order.isLong
. All the rest of conditions has checked before (L495). There are no more condition will occur at L509
RECOMMENDED MITIGATION CHECK
if ((order.isCall && isExercised) || (!order.isCall && !isExercised)) { // send the fee to the admin/DAO if fee is greater than 0% uint256 feeAmount = 0; if (fee > 0) { feeAmount = (order.strike * fee) / 1000; ERC20(order.baseAsset).safeTransfer(owner(), feeAmount); } ERC20(order.baseAsset).safeTransfer(msg.sender, order.strike - feeAmount); return; } // transfer assets from putty to owner if put is exercised or call is expired else{ //@audit: Use else statement here _transferERC20sOut(order.erc20Assets); _transferERC721sOut(order.erc721Assets); // for call options the floor token ids are saved in the long position in fillOrder(), // and for put options the floor tokens ids are saved in the short position in exercise() uint256 floorPositionId = order.isCall ? longPositionId : uint256(orderHash); _transferFloorsOut(order.floorTokens, positionFloorAssetTokenIds[floorPositionId]); return; }