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: 23/100
Findings: 6
Award: $157.69
🌟 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
Attacker could set the feeRate
to 100% without any time delay, front run any premiums/strikes being processed and steal premiums/strike from vault owner.
function setFee(uint256 feeRate_) external onlyOwner { feeRate = feeRate_; } /// @notice Withdraws the protocol fees and sends to current owner function withdrawProtocolFees() external onlyOwner returns (uint256 amount) { amount = protocolUnclaimedFees; protocolUnclaimedFees = 0; payable(msg.sender).safeTransferETH(amount); }
No time delay for feeRate
or withdrawProtocolFees
.
withdrawProtocolFees
sends ETH to msg.sender
.
Consider implementing a multisig system.
Consider introducing a time delay in setFee
.
Consider setting withdrawProtocolFees
recipient address to a different fixed address instead of msg.sender
.
Add maxFee
parameter to accommodate vault owners tolerance.
#0 - outdoteth
2022-05-16T10:40:54Z
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: BondiPestControl
Also found by: 0xf15ers, GimelSec, IllIllI, MadWookie, MiloTruck, Ruhum, VAD37, berndartmueller, cccz, csanuragjain, dipp, hake, horsefacts, jayjonah8, m9800, pedroais, throttle
31.6149 USDC - $31.61
If option buyer sends an amount bigger than premium
the difference is not refunded to buyer.
This could lead to a big loss for the buyer who could have made a mistake like for example typing an extra zero.
require(msg.value >= premium, "Incorrect ETH amount sent");
Consider refunding the difference to the buyer (more gas expensive) or implementing a check with strict equality: msg.value == premium
(less gas expensive).
#0 - outdoteth
2022-05-15T17:01:00Z
reference issue: https://github.com/code-423n4/2022-05-cally-findings/issues/84
🌟 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 #74 as Medium risk. The relevant finding follows:
Protocol does not support fee-on-transfer tokens The tokenIdOrAmount established in createVault prevents buyers from exercise their option because address(this) holds less than tokenIdOrAmount due to the transfer fee.
That is also valid for withdraw.
I recommend making it explicitly to users that such tokens are not supported or preferably only allowing a set of whitelisted ERC20s.
#0 - HardlyDifficult
2022-06-06T00:21:48Z
16.9712 USDC - $16.97
Scenario 1:
tokenId
value.dutchAuctionReserveStrike
and dutchAuctionStartingStrikeIndex
.Scenario 2 (extremely unlikely):
tokenId
value.dutchAuctionReserveStrike
and dutchAuctionStartingStrikeIndex
.vault.tokenType == TokenType.ERC721 ? ERC721(vault.token).transferFrom( msg.sender, address(this), vault.tokenIdOrAmount ) : ERC20(vault.token).safeTransferFrom( msg.sender, address(this), vault.tokenIdOrAmount );
ERC721.transferFrom
may actually call a ERC20 transferFrom
and vice versa.
Dont allow user input to be the sole validator on ERC being an ERC20 or ERC721.
If TokenType.ERC20
implement extra inputs and require functions to ensure name
, symbol
and decimals
match inputs. Same thing for TokenType.ERC721
excluding decimals
and confirming inputs match name
, symbol
and tokenId
/id
.
Only allow whitelisted tokens to be used as collateral.
#0 - outdoteth
2022-05-15T20:44:19Z
use safeTransferFrom to prevent stuck NFTs: https://github.com/code-423n4/2022-05-cally-findings/issues/38
fundamentally this is the same issue as above.
🌟 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
59.9643 USDC - $59.96
The tokenIdOrAmount
established in createVault
prevents buyers from exercise
their option because address(this)
holds less than tokenIdOrAmount
due to the transfer fee.
That is also valid for withdraw
.
I recommend making it explicitly to users that such tokens are not supported or preferably only allowing a set of whitelisted ERC20s.
Allowing Vault Tokens to be used as collateral may lead to users accidentally giving up his NFT or its strike price by mistake.
Please implement a check in createVault
as prevention:
require(token != address(this));
transferFrom
instead of safeTransferFrom
When buyers exercise
the transferFrom
function is used instead of safeTransferFrom
. In the very unlikely event the receiving contract is somehow not aware of incoming ERC721, the token could be locked.
Consider exchanging transferFrom
for safeTransferFrom
https://github.com/code-423n4/2021-11-streaming-findings/issues/28 ----REFERENCE
uint256 progress = (1e18 * delta) / AUCTION_DURATION; uint256 auctionStrike = (progress * progress * startingStrike) / (1e18 * 1e18);
Performing multiplication before division is generally better to avoid loss of precision because Solidity integer division might truncate.
This truncation might affect auctionStrike
.
durationDays
input is not correctly enforcedrequire(durationDays > 0, "durationDays too small");
durationDays
input check leads to believe there is a minimum of at least 1 day. However, if user inputs any amount above zero the check will pass.
I recommend hardcoding in a constant the equivalent of 1 day in seconds and using that as a comparison instead of zero.
/************************* OVVERIDES FUNCTIONS **************************/
Please change to OVERRIDES.
#0 - outdoteth
2022-05-16T16:23:15Z
these can be bumped to medium
L-01: Protocol does not support fee-on-transfer tokens: https://github.com/code-423n4/2022-05-cally-findings/issues/39 L-02: Vault Tokens can be used as collateral; https://github.com/code-423n4/2022-05-cally-findings/issues/224 L-03: Use of transferFrom instead of safeTransferFrom: https://github.com/code-423n4/2022-05-cally-findings/issues/38
#1 - HardlyDifficult
2022-05-21T00:56:40Z
#2 - HardlyDifficult
2022-05-29T17:24:40Z
#3 - HardlyDifficult
2022-05-29T17:27:36Z
🌟 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.0888 USDC - $30.09
Cally.sol#L249-L251
buyOption
:
address beneficiary = getVaultBeneficiary(vaultId); ethBalance[beneficiary] += msg.value;
In the code snippet above the beneficiary is unnecessarily cached because it is only called once.
In the code snippet below we can see the same functionality without the caching.
Cally.sol#L289-L290
exercise
:
ethBalance[getVaultBeneficiary(vaultId)] += msg.value - fee;
I suggest using the same implementation from exercise
in buyOption
.