Platform: Code4rena
Start Date: 10/05/2022
Pot Size: $50,000 USDC
Total HM: 13
Participants: 100
Period: 5 days
Judge: HardlyDifficult
Total Solo HM: 1
Id: 122
League: ETH
Rank: 59/100
Findings: 2
Award: $71.88
🌟 Selected for report: 0
🚀 Solo Findings: 0
16.9712 USDC - $16.97
The function exercise is using transferFrom instead of safeTransferFrom function when transferring ERC721 function and if the exerciser is a smart contract and not aware of incoming ERC721 token, the token could be lost forever
Manual analysis
use safeTransferFrom instead of transferFrom
#0 - outdoteth
2022-05-15T20:46:08Z
use safeTransferFrom to prevent stuck NFTs: https://github.com/code-423n4/2022-05-cally-findings/issues/38
🌟 Selected for report: hubble
Also found by: 0x1337, 0x1f8b, 0x4non, 0xDjango, 0xf15ers, 0xsanson, 242, Aits, AlleyCat, Bludya, BondiPestControl, BouSalman, BowTiedWardens, CertoraInc, Cityscape, Czar102, FSchmoede, Funen, Hawkeye, IllIllI, JDeryl, Kenshin, Kumpa, MaratCerby, MiloTruck, Picodes, Ruhum, TrungOre, VAD37, WatchPug, Waze, antonttc, bobirichman, catchup, cccz, cryptphi, csanuragjain, delfin454000, dipp, dirk_y, djxploit, eccentricexit, ellahi, fatherOfBlocks, hake, hansfriese, hickuphh3, horsefacts, hyh, jah, joestakey, mics, minhquanym, pedroais, pmerkleplant, radoslav11, reassor, rfa, robee, seanamani, shenwilly, shung, sikorico, sorrynotsorry, sseefried, z3s
54.9119 USDC - $54.91
The function exercise in Cally.sol Logic for handling Fee is wrong instead of taking the fee from the exerciser it is substracting from the beneficiary strike price
ethBalance[getVaultBeneficiary(vaultId)] += msg.value - fee;
so the exercise will not pay any fee
and the beneficiary will not get the strike price amount
(As the developer says the exerciser is the one that should pay the fee)
https://github.com/code-423n4/2022-05-cally/blob/main/contracts/src/Cally.sol#L258
Manual analysis
function exercise(uint256 optionId) external payable { require(optionId % 2 == 0, "Not option type"); require(msg.sender == ownerOf(optionId), "You are not the owner"); uint256 vaultId = optionId - 1; Vault memory vault = _vaults[vaultId]; require(block.timestamp < vault.currentExpiration, "Option has expired"); _burn(optionId); vault.isExercised = true; _vaults[vaultId] = vault; uint256 fee = 0; if (feeRate > 0) { fee = (msg.value * feeRate) / 1e18; protocolUnclaimedFees += fee; } require(msg.value >= vault.currentStrike + fee, "Incorrect ETH sent for strike"); ethBalance[getVaultBeneficiary(vaultId)] += msg.value - fee; emit ExercisedOption(optionId, msg.sender); vault.tokenType == TokenType.ERC721 ? ERC721(vault.token).transferFrom(address(this), msg.sender, vault.tokenIdOrAmount) : ERC20(vault.token).safeTransfer(msg.sender, vault.tokenIdOrAmount); }
#0 - outdoteth
2022-05-15T13:48:37Z
(As the developer says the exerciser is the one that should pay the fee)
this was miscommunication in DM's and is my fault. The beneficiary is the one who should pay the fee not the exerciser so the logic is correct.
#1 - outdoteth
2022-05-16T10:24:01Z
#2 - HardlyDifficult
2022-05-21T02:01:36Z
RE my comment in https://github.com/code-423n4/2022-05-cally-findings/issues/221, lowering this to 1 (Low). Since this warden does not have a QA submission, reopening this issue to track.