Platform: Code4rena
Start Date: 14/09/2022
Pot Size: $50,000 USDC
Total HM: 25
Participants: 110
Period: 5 days
Judge: hickuphh3
Total Solo HM: 9
Id: 162
League: ETH
Rank: 18/110
Findings: 4
Award: $664.72
π Selected for report: 0
π Solo Findings: 0
π Selected for report: csanuragjain
Also found by: Lambda, R2, bin2chen, datapunk, rbserver, unforgiven
300.0094 USDC - $300.01
https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/src/Controller.sol#L102 https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/src/Controller.sol#L203
since both triggerDepeg and triggerEndEpoch can be triggered when block.timestamp == epochEnd and vault.strikePrice() >= getLatestPrice(vault.tokenInsured() at the specific block, it creates a race condition, thus undetermistic outcome.
https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/src/Controller.sol#L102 https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/src/Controller.sol#L203
make one of the comparison inclusive
#0 - HickupHH3
2022-10-18T03:17:00Z
dup of #69
#1 - HickupHH3
2022-11-05T02:25:55Z
partial credit because it fails to elaborate on the consequences of the undeterministic outcome.
320.0832 USDC - $320.08
multiple markets can be created for the same tokenInsured. Unless there is some advantage to this design, it may make sense to consolidate all epochs for the same tokenInsured under one market, by checking is a market with the tokenInsured exists already first.
mapping(address => uint256) tokenToMarketIndex; function createNewMarket( ... if (tokenToMarketIndex[token]!=0) revert MarketExistsAlready(); ... )
#0 - HickupHH3
2022-11-01T10:21:02Z
weak dup of #223, where the functional weakness was pointed out.
8.0071 USDC - $8.01
priceFeed1 is called directly, instead of through getOracle1_Price, thus missing assurance checks. for example, price1<=0 is not reverted. This may result in unexpected behavior.
#0 - HickupHH3
2022-10-17T10:10:33Z
partial credit. the keyword i'm looking for is stale prices. while negative prices are a possibility, it's arguably more unlikely to happen compared to staleness.
#1 - HickupHH3
2022-10-17T10:10:41Z
dup of #61
#2 - HickupHH3
2022-10-17T10:34:45Z
realise that the <= 0 check is done in the Factory. Hence, further reducing the partial credit given (not entirely invalidating because of the <= 0 check is arguably better placed in the PegOracle).
π Selected for report: Respx
Also found by: 0x1f8b, 0xDecorativePineapple, 0xNazgul, 0xPanas, 0xSmartContract, 0xc0ffEE, 0xmuxyz, Aymen0909, Bahurum, Bnke0x0, CodingNameKiki, Deivitto, Jeiwan, Lambda, Picodes, PwnPatrol, R2, RaymondFam, Rolezn, Ruhum, Saintcode_, SooYa, Tointer, V_B, ajtra, ak1, async, auditor0517, brgltd, c3phas, carrotsmuggler, cccz, csanuragjain, datapunk, djxploit, durianSausage, eierina, erictee, gogo, imare, joestakey, jonatascm, kv, ladboy233, leosathya, lukris02, oyc_109, pashov, pauliax, rbserver, robee, rokinot, rvierdiiev, scaraven, simon135, unforgiven, wagmi, zzzitron
36.6223 USDC - $36.62
constructor of pegOracle.sol does not check if the correct oracles are used. Wrong oracle might be used without doing so.
emit oracle1/2.description() as event, which returns a description for this data feed. Usually this is an asset pair for a price feed.
Detailed description of the impact of this finding.
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
constructor of pegOracle checks if two priceFeed have the same decimal. This can be restrictive. It allows more possibilities if adjustment to 18 deciamls is done separately on each priceFeed, then compare.
run decimal adjustment on both priceFeeds individually, then compare prices.
10000 and 1000000 seem like magic numbers. In the worst case, they might be inteneded to be the same, thus have wrong nowPrice
https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/src/oracles/PegOracle.sol#L68 https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/src/oracles/PegOracle.sol#L78
check if these two numbers are intended to be the same, otherwise justify them in comments
the intention of this code snippet is not documented nor reflected in the test cases. If as designed, make it clear in the comments if (price1 > price2) { nowPrice = (price2 * 10000) / price1; } else { nowPrice = (price1 * 10000) / price2; }
https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/src/oracles/PegOracle.sol#L67 https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/test/AssertTest.t.sol#L79
Confirm it is as designed
Comment says "for example, hedgeY2K/riskY2K", yet it should be defined as hY2K/rY2K. Misname of the symbol will treat both sides as hedge
https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/src/Vault.sol#L109 https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/src/Vault.sol#L388
make sure symbol for risk is fixed in the comment.
functions only accessed externally, should be set as external instead of public
https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/src/Vault.sol#L277 https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/src/Vault.sol#L287 https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/src/Vault.sol#L295 etc ...
change public to external
oracle and token in VaultFactory's createNewMarket should match up
oracle can return a description and token can return a name/symbol at least emit these info to show correct oracle is used for the token
why use SafeMath under 0.8.15?
no epochHasNotStarted(id) modifier applied to depositETH. Although it will eventually checked by deposit. It is advisable to check early, so to save gas
function depositETH(uint256 id, address receiver) external payable epochHasNotStarted(id) returns (uint256 shares) { ... }
Given the specific Vault assigning totalSupply to totalAssets, and the convertion logic does mulDiv(totalSupply, totalAssets), to save gas, we can remove all the conversion logic and use assets directly as shares.
https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/src/Vault.sol#L251 https://github.com/code-423n4/2022-09-y2k-finance/blob/2175c044af98509261e4147edeb48e1036773771/src/SemiFungibleVault.sol#L152