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: 49/133
Findings: 2
Award: $97.28
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Picodes
Also found by: 0xNineDec, 0xsanson, Metatron, antonttc, berndartmueller, catchup, dirk_y, sseefried, unforgiven
Admin can cause significant loss for option sellers compared to premium
it’s possible for seller to suffer a net loss even if the option doesn’t got exercised because of the admin power of setting fee. There is also no check on whether strike * fee
is smaller or greater than the premium the seller get, which means sellers can accidentally put an order that is absolutely not worth it.
e.g.: I sell a 100ETH put on an NFT for 3 days with premium of 3 eth, After expiry and before taking out the 100 ETH, owner change the fee rate to maximum (3%), i got charge another 3% which is my premium, ended in an absolute net loss that I didn’t want to agree on.
it’s better to embed a “fee” parameter in the order
structure, and when filling order, make sure order.fee == fee
, so no one can avoid paying fee after the protocol turn the switch on, and then check premium > strike * fee to make sure sellers are not doing something stupid.
while withdrawing, use the fee in the order struct to make sure this rate is agreed by both parties when filling the order. This should remove all concerns regarding fees.
#0 - outdoteth
2022-07-06T18:33:25Z
Duplicate: Admin can change fee at any time for existing orders: https://github.com/code-423n4/2022-06-putty-findings/issues/422
🌟 Selected for report: xiaoming90
Also found by: 0x1f8b, 0x29A, 0x52, 0xDjango, 0xNazgul, 0xNineDec, 0xSolus, 0xf15ers, 0xsanson, AmitN, Bnke0x0, BowTiedWardens, Chom, David_, ElKu, Funen, GalloDaSballo, GimelSec, Hawkeye, IllIllI, JC, JohnSmith, Kaiziron, Kenshin, Lambda, Limbooo, MadWookie, Metatron, MiloTruck, Nethermind, Picodes, ReyAdmirado, Sneakyninja0129, StErMi, TomJ, Treasure-Seeker, TrungOre, Waze, Yiko, _Adam, __141345__, antonttc, async, aysha, catchup, cccz, cryptphi, csanuragjain, danb, datapunk, defsec, delfin454000, dirk_y, doddle0x, durianSausage, exd0tpy, fatherOfBlocks, gogo, hake, hansfriese, horsefacts, hubble, itsmeSTYJ, joestakey, oyc_109, pedroais, peritoflores, rajatbeladiya, reassor, robee, rokinot, samruna, saneryee, sashik_eth, shenwilly, shung, simon135, sseefried, unforgiven, zer0dot, zzzitron
47.1473 USDC - $47.15
msg.value
doesn’t work with batchFillOrder
in fillOrder
while filling a long order the function check msg.value == order.strike
to move collateral to the contract, or check msg.value == premium
and pay the seller directly. This will not work with batchFill
because msg.value will be a the same value across all iterations. So if user want to fill 2 short order both with weth as premium at the same time, fillOder will fail
Consider refactor the fillOrder
and batchFillOrder
into the following:
function fillOrder( Order memory order, bytes calldata signature, uint256[] memory floorAssetTokenIds ) public payable returns (uint256 positionId) { _fillOrder(order, signature, floorAssetTokenIds, mgs.value) } function batchFillOrder( Order[] memory orders, bytes[] calldata signatures, uint256[][] memory floorAssetTokenIds uint256[] calldata values ) public returns (uint256[] memory positionIds) { require(orders.length == signatures.length, "Length mismatch in input"); require(signatures.length == floorAssetTokenIds.length, "Length mismatch in input"); positionIds = new uint256[](orders.length); uint256 totalValue; for (uint256 i = 0; i < orders.length; i++) { positionIds[i] = fillOrder(orders[i], signatures[i], floorAssetTokenIds[i], values[i]); totalValue += value[i] } require(totalValue == msg.value, "value mismatch"); } // internal _fillOrder function _fillOrder( Order memory order, bytes calldata signature, uint256[] memory floorAssetTokenIds uint256 _value; ) public payable returns (uint256 positionId) { ... {use _value instead of msg.value} ... }
order.nonce
doesn’t work as its name suggestedUsually the word nonce
refers to a increasing number that can be used as a way to “cancel old orders”. but in this case, nonce
is purely used as “salt” to produce different id for different orders. I think it’s better that either rename this to salt
, or add a check of order.nonce>last_ nonce
in order validation step.
exercise
can be marked as exeternal#0 - HickupHH3
2022-07-15T02:57:00Z
(1) is invalid: see #138