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: 28/100
Findings: 5
Award: $123.15
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0x52, 0xf15ers, 0xsanson, Bludya, BondiPestControl, Czar102, GimelSec, Kumpa, _Adam, berndartmueller, catchup, crispymangoes, eccentricexit, ellahi, hake, horsefacts, pedroais, peritoflores, reassor, shenwilly, shung, smiling_heretic, sseefried, throttle
8.1655 USDC - $8.17
https://github.com/code-423n4/2022-05-cally/blob/1849f9ee12434038aa80753266ce6a2f2b082c59/contracts/src/Cally.sol#L119-L121 https://github.com/code-423n4/2022-05-cally/blob/1849f9ee12434038aa80753266ce6a2f2b082c59/contracts/src/Cally.sol#L281-L289
If protocol feeRate
is increased when there are active options, vault creators will receive lesser amount of eth than expected when creating them. In extreme case, a malicious/compromised owner can frontran an exercise
transaction and steal all funds meant for the vault creator by setting the fees to 100%.
0
.msg.value
.exercise
transaction by setting the feeRate to 1e18
.Add a feeRate parameter in Vault, and store the current feeRate when buyOption
is called. Use the stored feeRate for the fee calculation when the option is exercised.
#0 - outdoteth
2022-05-16T09:30:38Z
16.9712 USDC - $16.97
Judge has assessed an item in Issue #37 as Medium risk. The relevant finding follows:
Missing sanity check in setFeeRate There is no input validation in setFeeRate. A faulty payload could set the feeRate to a very high amount, which would cause problems when options are exercised:
Loss of fund for vault creators if feeRate is near 1e18, as the fund is fully transferred to protocol, or Buyers unable to exercise if feeRate is higher than 1e18, as fee would be higher than msg.value. Recommended Mitigation Add sanity checks when setting feeRate, such as:
require(feeRate_ <= 3e17, "Fee must not be higher than 30%");
#0 - HardlyDifficult
2022-06-06T00:13:17Z
🌟 Selected for report: 0x1337
Also found by: 0x52, 0xDjango, 0xsanson, BondiPestControl, BowTiedWardens, GimelSec, IllIllI, MaratCerby, MiloTruck, PPrieditis, TrungOre, VAD37, WatchPug, berndartmueller, cccz, dipp, hake, hickuphh3, horsefacts, hubble, minhquanym, reassor, shenwilly, smiling_heretic
10.8874 USDC - $10.89
Judge has assessed an item in Issue #95 as Medium risk. The relevant finding follows:
Incompatability with deflationary / fee-on-transfer tokens Function Cally.createVault function takes a tokenIdOrAmount parameter but this parameter is not the actual transferred amount for fee-on-transfer / deflationary (or other rebasing) tokens in case tokenType = ERC20 Impact The actual deposited amount might be lower than the specified depositAmount of the function parameter. And when users exercise or withdraw they not only receive less than expected amount but also take funds of other vaults with the same vault.token too, causes loss of funds. Proof-of-concept https://github.com/code-423n4/2022-05-cally/blob/1849f9ee12434038aa80753266ce6a2f2b082c59/contracts/src/Cally.sol#L200 https://github.com/code-423n4/2022-05-cally/blob/1849f9ee12434038aa80753266ce6a2f2b082c59/contracts/src/Cally.sol#L296 https://github.com/code-423n4/2022-05-cally/blob/1849f9ee12434038aa80753266ce6a2f2b082c59/contracts/src/Cally.sol#L345 Recommended Mitigation Steps Transfer the tokens first and compare pre-/after token balances to compute the actual amount.
#0 - HardlyDifficult
2022-06-06T00:30:27Z
16.9712 USDC - $16.97
When exercising options, transferFrom
is used to transfer the NFT to the caller. If the caller is a contract that relies on ERC721TokenReceiver
standard and not aware of incoming ERC721 tokens, the NFT could be locked.
Consider using safeTransferFrom
over transferFrom
when exercising.
#0 - outdoteth
2022-05-15T20:42:55Z
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
70.154 USDC - $70.15
setFeeRate
There is no input validation in setFeeRate. A faulty payload could set the feeRate to a very high amount, which would cause problems when options are exercised:
feeRate
is near 1e18, as the fund is fully transferred to protocol, orfeeRate
is higher than 1e18, as fee would be higher than msg.value
.Add sanity checks when setting feeRate, such as:
require(feeRate_ <= 3e17, "Fee must not be higher than 30%");
durationDays
When creating vault, there is no check to make sure that durationDays
is not too big. A faulty frontend could mistakenly set it to an unreasonable amount (years).
Add sanity checks when setting durationDays, such as limiting it to one year:
require(durationDays <= 365, "durationDays too big");
require(msg.value >= premium, "Incorrect ETH amount sent");
When buying options, the function only checks that msg.value
must not be lower than premium
. A faulty frontend can set a higher amount and the transaction would still succeed.
Unless there's a good reason to allow sending more premium than necessary, update the statement to equality check:
require(msg.value == premium, "Incorrect ETH amount sent");
InitiatedWithdrawal
event emissionThere's no check to prevent initiateWithdraw from being called repeatedly. InitiatedWithdrawal
event can keep being emitted, which could disrupt off-chain analytics that track this particular event.
Add a check to make sure it's only callable once per vault:
require(!_vaults[vaultId].isWithdrawing, "Vault withdrawal is already in progress");
require(dutchAuctionReserveStrike < strikeOptions[dutchAuctionStartingStrikeIndex], "Reserve strike too small");
The check in Cally.sol
L169 states that "Reserve strike too small" when the check fails. It should be "Reserve strike too high".
#0 - outdoteth
2022-05-16T16:12:37Z
"1. Missing sanity check in setFeeRate" and "3. Buyers can send more premium than needed" can be bumped to medium severity:
owner can set fee greater than 100%: https://github.com/code-423n4/2022-05-cally-findings/issues/48 buyer loses eth if too much eth sent: https://github.com/code-423n4/2022-05-cally-findings/issues/84
#1 - HardlyDifficult
2022-05-30T19:09:37Z