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: 47/100
Findings: 2
Award: $85.97
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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.8917 USDC - $54.89
require(dutchAuctionReserveStrike < strikeOptions[dutchAuctionStartingStrikeIndex], "Reserve strike too small");
- this is suppose to be "too big"
instead of "too small"
🌟 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
31.0819 USDC - $31.08
getDutchAuctionStrike
(line 417) - it won't underflow because we know that auctionEndTimestamp > block.timestamp
.
uint256 delta = auctionEndTimestamp > block.timestamp ? auctionEndTimestamp - block.timestamp : 0;
1e36
instead of 1e18 * 1e18
in the getDutchAuctionStrike
function (uint256 auctionStrike = (progress * progress * startingStrike) / (1e18 * 1e18);
)getDutchAuctionStrike
by splitting it into if statementsThese three optimizations gives us these changes: old code: ```sol function getDutchAuctionStrike( uint256 startingStrike, uint32 auctionEndTimestamp, uint256 reserveStrike ) public view returns (uint256 strike) { /* delta = max(auctionEnd - currentTimestamp, 0) progress = delta / auctionDuration auctionStrike = progress^2 * startingStrike strike = max(auctionStrike, reserveStrike) */ uint256 delta = auctionEndTimestamp > block.timestamp ? auctionEndTimestamp - block.timestamp : 0; uint256 progress = (1e18 * delta) / AUCTION_DURATION; uint256 auctionStrike = (progress * progress * startingStrike) / (1e18 * 1e18);
// max(auctionStrike, reserveStrike) strike = auctionStrike > reserveStrike ? auctionStrike : reserveStrike; } ``` new code: ```sol function getDutchAuctionStrike( uint256 startingStrike, uint32 auctionEndTimestamp, uint256 reserveStrike ) public view returns (uint256 strike) { /* delta = max(auctionEnd - currentTimestamp, 0) progress = delta / auctionDuration auctionStrike = progress^2 * startingStrike strike = max(auctionStrike, reserveStrike) */ // this is because `auctionStrike == 0` so `auctionStrike > reserveStrike == false` for every value of reserveStrike if (auctionEndTimestamp > block.timestamp) return reserveStrike; unchecked { uint256 delta = auctionEndTimestamp - block.timestamp; // it won't underflow because of the if statement } uint256 progress = (1e18 * delta) / AUCTION_DURATION; uint256 auctionStrike = (progress * progress * startingStrike) / 1e36; // instead of 1e18 * 1e18 // max(auctionStrike, reserveStrike) strike = auctionStrike > reserveStrike ? auctionStrike : reserveStrike; } ```
Inline the getPremium
function to save the gas spent of function call
Use bitwise operations to check if a number is even or odd (num & 1
instead of num % 2
)
use logic not instead of == false
(this can be done twice in the buyOption
function and also in the withdraw
function)
save an SLOAD on lines 188-190 by applying this optimization
old:
vaultIndex += 2; vaultId = vaultIndex; _vaults[vaultId] = vault;
new:
vaultId = vaultIndex; vaultId += 2; vaultIndex = vaultId; _vaults[vaultId] = vault;
Put mapping(uint256 => address) private _vaultBeneficiaries;
inside the vault struct (it won't change the size of the struct because of the 256 bits padding)
Use auctionStartTimestamp
instead of accessing vault.currentExpiration
again in the buyOption
function
uint32 auctionStartTimestamp = vault.currentExpiration; require(block.timestamp >= auctionStartTimestamp, "Auction not started"); // set new currentStrike vault.currentStrike = getDutchAuctionStrike( strikeOptions[vault.dutchAuctionStartingStrikeIndex], vault.currentExpiration + AUCTION_DURATION, vault.dutchAuctionReserveStrike );
Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met. Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc. An even better and gas efficient approach will be to use Solidity Custom Errors instead of revert strings.
Cache feeRate to save an SLOAD + redundant initialization (variables in solidity are automatically initialized to their default value)
old:
uint256 fee = 0; if (feeRate > 0) { fee = (msg.value * feeRate) / 1e18; protocolUnclaimedFees += fee; }
new:
uint256 fee; uint cachedFeeRate = feeRate; if (cachedFeeRate > 0) { fee = (msg.value * cachedFeeRate) / 1e18; protocolUnclaimedFees += fee; }