Platform: Code4rena
Start Date: 21/06/2022
Pot Size: $30,000 USDC
Total HM: 12
Participants: 96
Period: 3 days
Judge: HardlyDifficult
Total Solo HM: 5
Id: 140
League: ETH
Rank: 57/96
Findings: 2
Award: $45.50
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x29A, 0x52, 0xNazgul, 0xNineDec, 0xc0ffEE, 0xf15ers, 0xkatana, BowTiedWardens, Chom, ElKu, Funen, GalloDaSballo, JC, JMukesh, JohnSmith, Lambda, Limbooo, MadWookie, MiloTruck, Nethermind, Noah3o6, Nyamcil, Picodes, PwnedNoMore, Randyyy, RoiEvenHaim, SmartSek, StErMi, Tadashi, TerrierLover, TomJ, Tomio, Treasure-Seeker, UnusualTurtle, Varun_Verma, Wayne, Waze, _Adam, apostle0x01, asutorufos, berndartmueller, c3phas, catchup, cccz, cloudjunky, codexploder, cryptphi, defsec, delfin454000, dipp, ellahi, exd0tpy, fatherOfBlocks, hansfriese, hyh, joestakey, kebabsec, kenta, masterchief, minhquanym, naps62, oyc_109, pashov, peritoflores, reassor, rfa, robee, sach1r0, saian, sashik_eth, shenwilly, simon135, slywaters, sorrynotsorry, sseefried, unforgiven, xiaoming90, ych18, zuhaibmohd, zzzitron
28.2781 USDC - $28.28
the withdrawERC20 function use the standard IERC20 interface from openzeppelin, which expect a returns from the interface, this works for the most ERC20 token, however for USDT token that didnt follow the ERC20 standard didnt have a return value on the transfer function, if the basket contract hold any USDT token, the USDT token would be stuck because when the user call withdrawERC20 with asset set as USDT, this transaction would revert and the token is not transferred.
consider using safeERC20 from openzepelin, to handle odd ERC20 token
Permanent freezing for USDT token.
#0 - mundhrakeshav
2022-06-26T17:23:31Z
#63
#1 - GalloDaSballo
2022-06-26T21:57:44Z
Findings Contingent on a specific token are historically Medium at best
Notice that USDT will actually reserve as the fact that the call returns nothing means the compiler will revert
#2 - HardlyDifficult
2022-07-02T21:56:33Z
Both the vault and basket are geared towards holding NFTs. Including ERC20 withdraw functions appears to be a safety measure to allow recovery of those assets if a mistake was made. I agree they should consider a change here, but since it does not impact the protocol's intended use case I'll downgrade this report to a Low risk and converting this into a QA report for the warden.
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0x29A, 0xKitsune, 0xNazgul, 0xf15ers, 0xkatana, 8olidity, ACai, BowTiedWardens, Chandr, Chom, ElKu, Fitraldys, Funen, IgnacioB, JC, Lambda, Limbooo, MiloTruck, Noah3o6, Nyamcil, Picodes, Randyyy, SmartSek, StErMi, TerrierLover, TomJ, Tomio, UnusualTurtle, Waze, _Adam, ajtra, c3phas, cRat1st0s, catchup, codexploder, cryptphi, defsec, delfin454000, ellahi, exd0tpy, fatherOfBlocks, hansfriese, joestakey, kebabsec, kenta, m_Rassska, minhquanym, oyc_109, pashov, reassor, rfa, robee, sach1r0, saian, sashik_eth, simon135, slywaters, ych18, ynnad, zuhaibmohd
17.2247 USDC - $17.22
The Swap contract uses Solidity version 0.8 which already implements overflow checks by default. At the same time, it uses the SafeMath library which is more gas expensive than the 0.8 overflow checks.
Use uint32(n) can cut off higher bit already.
https://github.com/code-423n4/2022-06-nibbl/blob/bcae7d71718c8eb744e4465d68fd6efa2bde7b06/contracts/NibblVault.sol#L303 https://github.com/code-423n4/2022-06-nibbl/blob/bcae7d71718c8eb744e4465d68fd6efa2bde7b06/contracts/NibblVault.sol#L366
uint32 blockTimestamp = uint32(block.timestamp);
Custom errors from solidity 0.8.4 are cheaper than revert strings
https://github.com/code-423n4/2022-06-nibbl/blob/bcae7d71718c8eb744e4465d68fd6efa2bde7b06/contracts/NibblVault.sol#L129 https://github.com/code-423n4/2022-06-nibbl/blob/bcae7d71718c8eb744e4465d68fd6efa2bde7b06/contracts/NibblVault.sol#L139 https://github.com/code-423n4/2022-06-nibbl/blob/bcae7d71718c8eb744e4465d68fd6efa2bde7b06/contracts/NibblVault.sol#L146 https://github.com/code-423n4/2022-06-nibbl/blob/bcae7d71718c8eb744e4465d68fd6efa2bde7b06/contracts/NibblVault.sol#L147
uint256 variable are initialized to a default value of zero. setting variable to the default value is unnecessary.
https://github.com/code-423n4/2022-06-nibbl/blob/bcae7d71718c8eb744e4465d68fd6efa2bde7b06/contracts/NibblVault.sol#L506 https://github.com/code-423n4/2022-06-nibbl/blob/bcae7d71718c8eb744e4465d68fd6efa2bde7b06/contracts/NibblVault.sol#L525
Caching array length in the stack save gas per iteration.
https://github.com/code-423n4/2022-06-nibbl/blob/bcae7d71718c8eb744e4465d68fd6efa2bde7b06/contracts/NibblVault.sol#L506 https://github.com/code-423n4/2022-06-nibbl/blob/bcae7d71718c8eb744e4465d68fd6efa2bde7b06/contracts/NibblVault.sol#L525 https://github.com/code-423n4/2022-06-nibbl/blob/bcae7d71718c8eb744e4465d68fd6efa2bde7b06/contracts/NibblVault.sol#L547
Instead of wraping _basketAddress with Basket type to call initialise, u can do this without an extra MSTORE by changing this
function createBasket(address _curator, string memory _mix) public override returns(address) { address payable _basketAddress = payable(new ProxyBasket{salt: keccak256(abi.encodePacked(_curator, _mix))}(basketImplementation)); Basket _basket = Basket(_basketAddress); _basket.initialise(_curator); emit BasketCreated(_curator, _basketAddress); return _basketAddress; }
to this
function createBasket(address _curator, string memory _mix) public override returns(address) { address payable _basketAddress = payable(new ProxyBasket{salt: keccak256(abi.encodePacked(_curator, _mix))}(basketImplementation)); Basket(_basketAddress).initialise(_curator); emit BasketCreated(_curator, _basketAddress); return _basketAddress; }