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: 76/110
Findings: 2
Award: $52.80
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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
The PegOracle contract expects to use two different oracles to compute the peg between two assets: https://github.com/code-423n4/2022-09-y2k-finance/blob/main/src/oracles/PegOracle.sol#L46-L83
In the contest's README, you provide stETH and ETH as an example. Because Chainlink only has an oracle for stETH/USD and ETH/USD, you compute the ratio between stETH/ETH yourself. But, this oracle combination is not present on every network. On mainnet, there's only one for stETH/ETH. The PegOracle contract won't work on mainnet.
The readme said that deployment is only planned for Arbitrum so this isn't an issue now. But it might be in the future if you decide to expand to other networks.
It allows third parties to track those changes easier.
🌟 Selected for report: pfapostol
Also found by: 0x040, 0x1f8b, 0x4non, 0xNazgul, 0xSmartContract, 0xc0ffEE, 0xkatana, Aymen0909, Bnke0x0, Deivitto, Diana, JAGADESH, KIntern_NA, Lambda, MiloTruck, R2, RaymondFam, Respx, ReyAdmirado, Rohan16, RoiEvenHaim, Rolezn, Ruhum, Saintcode_, Samatak, Sm4rty, SnowMan, Tomio, Tomo, WilliamAmbrozic, _Adam, __141345__, ajtra, ak1, async, c3phas, ch0bu, cryptostellar5, d3e4, delfin454000, dharma09, djxploit, durianSausage, eierina, erictee, fatherOfBlocks, gianganhnguyen, gogo, ignacio, imare, jag, jonatascm, leosathya, lukris02, malinariy, oyc_109, pashov, pauliax, peanuts, peiw, prasantgupta52, robee, rokinot, rotcivegaf, rvierdiiev, seyni, simon135, slowmoses, sryysryy, tnevler, zishansami
16.1756 USDC - $16.18
Vault.previewWithdraw()
Since it can't realistically overflow, you can increment it within an unchecked{}
block
./src//Vault.sol:443: for (uint256 i = 0; i < epochsLength(); i++) {
It's a little cheaper
./src//Vault.sol:443: for (uint256 i = 0; i < epochsLength(); i++) {
Instead of hashing the hardcoded "rY2K"
string in each call, you can precompute the value and save it as a constant. That should save you a lot of gas
./src//Vault.sol:389: keccak256(abi.encodePacked("rY2K"))
In the same line you also hash the vault's symbol
. You can do the same thing as above but instead, save the value as an immutable state variable. That way you don't have to pass any extra parameters when initializing the contract
./src//Vault.sol:388: keccak256(abi.encodePacked(symbol)) ==
Vault.previewWithdraw()
The previewWithdraw()
function is called in each withdraw()
tx.
function previewWithdraw(uint256 id, uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply(id); // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets(id)); }
In the Vault contract, you overwrite the totalAssets()
function to return the value of totalSupply()
.
function totalAssets(uint256 _id) public view override marketExists(_id) returns (uint256) { return totalSupply(_id); }
Thus, the previewWithdraw()
function always returns assets
. Instead of running through all that computation to just return assets
, you can overwrite the function in the Vault contract to just do that. That should save you a lot of gas:
function previewWithdraw(uint256 id, uint256 assets) public view override returns (uint256) { return assets; }