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: 80/100
Findings: 2
Award: $47.42
🌟 Selected for report: 0
🚀 Solo Findings: 0
16.9712 USDC - $16.97
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#L282-L289
setFee
allow the owner to see feeRate
to any value, this lead to 2 problems.
feeRate
> 1e18, user will not be able to exercise options as L289 would revert due to underflow.feeRate
to 100% to rug userfunction setFee(uint256 feeRate_) external onlyOwner { feeRate = feeRate_; }
uint256 fee = 0; if (feeRate > 0) { fee = (msg.value * feeRate) / 1e18; protocolUnclaimedFees += fee; } // increment vault beneficiary's ETH balance ethBalance[getVaultBeneficiary(vaultId)] += msg.value - fee;
Add a check to make sure feeRate
is below some constant value e.g. 10%
#0 - outdoteth
2022-05-15T19:16:25Z
owner can change fee at any time; https://github.com/code-423n4/2022-05-cally-findings/issues/47 owner can set fee greater than 100%: https://github.com/code-423n4/2022-05-cally-findings/issues/48
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0x4non, 0xDjango, 0xNazgul, 0xf15ers, 0xkatana, 0xsanson, Bludya, BowTiedWardens, CertoraInc, Cityscape, DavidGialdi, FSchmoede, Fitraldys, Funen, Hawkeye, Kenshin, MadWookie, MaratCerby, MiloTruck, Picodes, RagePit, Tadashi, TerrierLover, TomFrenchBlockchain, VAD37, WatchPug, Waze, _Adam, antonttc, bobirichman, catchup, defsec, delfin454000, djxploit, ellahi, fatherOfBlocks, gzeon, hake, hansfriese, hickuphh3, horsefacts, ignacio, joestakey, jonatascm, mics, minhquanym, oyc_109, pmerkleplant, rfa, robee, rotcivegaf, samruna, shung, sikorico, simon135, z3s
30.4458 USDC - $30.45
> 0
is less efficient than != 0
for uint in require conditionRef: https://twitter.com/GalloDaSballo/status/1485430908165443590
require(durationDays > 0, "durationDays too small");
require(owner != address(0), "ZERO_ADDRESS");
Consider to use a polynomial instead of storing premiumOptions and strikeOptions can save gas by reducing SLOADS https://github.com/code-423n4/2022-05-cally/blob/1849f9ee12434038aa80753266ce6a2f2b082c59/contracts/src/Cally.sol#L89-L93
// prettier-ignore uint256[] public premiumOptions = [0.01 ether, 0.025 ether, 0.05 ether, 0.075 ether, 0.1 ether, 0.25 ether, 0.5 ether, 0.75 ether, 1.0 ether, 2.5 ether, 5.0 ether, 7.5 ether, 10 ether, 25 ether, 50 ether, 75 ether, 100 ether]; // prettier-ignore uint256[] public strikeOptions = [1 ether, 2 ether, 3 ether, 5 ether, 8 ether, 13 ether, 21 ether, 34 ether, 55 ether, 89 ether, 144 ether, 233 ether, 377 ether, 610 ether, 987 ether, 1597 ether, 2584 ether, 4181 ether, 6765 ether];
This line will never overflow, we can use unchecked
to save gas
vaultIndex += 2;
optionId = vaultId + 1;
vaultIndex += 2; vaultId = vaultIndex; _vaults[vaultId] = vault;
change into
vaultId = (vaultIndex += 2); _vaults[vaultId] = vault;
uint256 optionId = vaultId + 1; _burn(optionId); _burn(vaultId);
change into
_burn(vaultId + 1); _burn(vaultId);
Solidity ^0.8.4 allow the use of custom errors to optimize gas usage. https://blog.soliditylang.org/2021/04/21/custom-errors/
We can use the following function to save gas on float multiplications
// out = x * y unchecked{/} z function fmul(uint256 x, uint256 y, uint256 z) internal pure returns(uint256 out){ assembly{ if iszero(eq(div(mul(x,y),x),y)) {revert(0,0)} out := div(mul(x,y),z) } }
uint256 auctionStrike = (progress * progress * startingStrike) / (1e18 * 1e18);
_vaultBeneficiaries[id] = address(0);