Platform: Code4rena
Start Date: 12/07/2022
Pot Size: $35,000 USDC
Total HM: 13
Participants: 78
Period: 3 days
Judge: 0xean
Total Solo HM: 6
Id: 135
League: ETH
Rank: 43/78
Findings: 2
Award: $72.25
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: joestakey
Also found by: 0x1f8b, 0x52, 0xDjango, 0xNazgul, 0xNineDec, 8olidity, Avci, Bahurum, Bnke0x0, Chom, ElKu, Funen, GimelSec, JC, Junnon, Kaiziron, Meera, PaludoX0, Picodes, ReyAdmirado, Sm4rty, Soosh, Waze, _Adam, __141345__, ak1, aysha, benbaessler, bin2chen, c3phas, cccz, cryptphi, csanuragjain, defsec, exd0tpy, fatherOfBlocks, gogo, hake, hansfriese, itsmeSTYJ, jonatascm, kyteg, mektigboy, oyc_109, pashov, rbserver, rishabh, robee, rokinot, sach1r0, sashik_eth, scaraven, simon135, slywaters
46.2978 USDC - $46.30
Some tokens, such as Tether (USDT) return false rather than reverting if the approval fails. Use OpenZeppelin’s safeApprove(), which reverts if there’s a failure, instead
There is 1 instance of this issue: Swivel/Swivel.sol:562
Swivel/Swivel.sol:562: Safe.approve(uToken, c[i], max);`
Use OpenZeppelin’s safeApprove()
Code architecture, incentives, and error handling/reporting questions/issues should be resolved before deployment
Multiple lines in at swivel.sol
Swivel/Swivel.sol:33: address public aaveAddr; // TODO immutable? Swivel/Swivel.sol:120: // TODO cheaper to assign amount here or keep the ADD? Swivel/Swivel.sol:157: // TODO assign amount or keep the ADD? Swivel/Swivel.sol:192: // TODO assign amount or keep the ADD? Swivel/Swivel.sol:221: // TODO assign amount or keep ADD? Swivel/Swivel.sol:286: // TODO assign amount or keep the ADD? Swivel/Swivel.sol:317: // TODO assign amount or keep the ADD? Swivel/Swivel.sol:347: // TODO assign amount or keep the ADD? Swivel/Swivel.sol:382: // TODO assign amount or keep the ADD? Swivel/Swivel.sol:707: // TODO as stated elsewhere, we may choose to simply return true in all and not attempt to measure against any expected return Swivel/Swivel.sol:708: if (p == uint8(Protocols.Compound)) { // TODO is Rari a drop in here? Swivel/Swivel.sol:716: // TODO explain the Aave deposit args Swivel/Swivel.sol:721: // TODO explain the 0 (primary account) Swivel/Swivel.sol:740: // TODO as stated elsewhere, we may choose to simply return true in all and not attempt to measure against any expected return Swivel/Swivel.sol:741: if (p == uint8(Protocols.Compound)) { // TODO is Rari a drop in here? Swivel/Swivel.sol:748: // TODO explain the withdraw args Swivel/Swivel.sol:752: // TODO explain the 0
Contracts should be deployed with the same compiler version and flags that they have been tested with thoroughly. Locking the pragma helps to ensure that contracts do not accidentally get deployed using, for example, an outdated compiler version that might introduce bugs that affect the contract system negatively.
Creator/Erc20.sol Creator/ZcToken.sol Creator/IRedeemer.sol Creator/IERC5095.sol Tokens/Erc20.sol:4 Tokens/ZcToken.sol:2 Tokens/IRedeemer.sol:2 Tokens/IERC5095.sol:2 Marketplace/Erc20.sol:4
Creator/Erc20.sol:4: pragma solidity ^0.8.0; Creator/ZcToken.sol:2: pragma solidity ^0.8.4; Creator/IRedeemer.sol:2:pragma solidity ^0.8.0; Creator/IERC5095.sol:2: pragma solidity ^0.8.0; Tokens/Erc20.sol:4: pragma solidity ^0.8.0; Tokens/ZcToken.sol:2: pragma solidity ^0.8.4; Tokens/IRedeemer.sol:2: pragma solidity ^0.8.0; Tokens/IERC5095.sol:2: pragma solidity ^0.8.0; Marketplace/Erc20.sol:4:pragma solidity ^0.8.0;
use fixed solidity version
_mint()
is discouraged in favor of _safeMint()
which ensures that the recipient is either an EOA or implements IERC721Receiver
. Both open OpenZeppelin and solmate have versions of this function so that NFTs aren’t lost if they’re minted to contracts that cannot transfer them back out.
Creator/ZcToken.sol:148 Tokens/ZcToken.sol:148
Creator/ZcToken.sol:148: _mint(t, a); Tokens/ZcToken.sol:148: _mint(t, a);
Use _safeMint() instead of _mint().
#0 - robrobbins
2022-08-25T21:51:05Z
we do
this is NFT specific, doesn't apply here.
🌟 Selected for report: joestakey
Also found by: 0x040, 0x1f8b, 0xDjango, 0xNazgul, 0xsam, Avci, Aymen0909, Bnke0x0, CRYP70, ElKu, Fitraldys, Funen, JC, Kaiziron, MadWookie, Meera, ReyAdmirado, Sm4rty, Soosh, TomJ, Waze, _Adam, __141345__, ajtra, benbaessler, c3phas, csanuragjain, durianSausage, exd0tpy, fatherOfBlocks, hake, ignacio, karanctf, kyteg, m_Rassska, oyc_109, rbserver, robee, rokinot, samruna, sashik_eth, simon135, slywaters
25.9459 USDC - $25.95
Use calldata instead of memory to save gas. See here for reference: https://code4rena.com/reports/2022-04-badger-citadel/#g-12-stakedcitadeldepositall-use-calldata-instead-of-memory
Link: https://github.com/code-423n4/2022-07-swivel/blob/main/Swivel/Swivel.sol#L495
Swivel/Swivel.sol:495: function setFee(uint16[] memory i, uint16[] memory d) external authorized(admin) returns (bool) {
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)
Starting from Solidity v0.8.4,there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");),but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.
Custom errors are defined using the error statement, which can be used inside and outside of contracts (including interfaces and libraries).
Links: Creator/LibFuse.sol:36 VaultTracker/LibFuse.sol:36 Tokens/LibFuse.sol:36
Creator/LibFuse.sol:36: require(borrowRateMantissa <= 0.0005e16, "RATE_TOO_HIGH"); VaultTracker/LibFuse.sol:36: require(borrowRateMantissa <= 0.0005e16, "RATE_TOO_HIGH"); Tokens/LibFuse.sol:36: require(borrowRateMantissa <= 0.0005e16, "RATE_TOO_HIGH");
https://blog.soliditylang.org/2021/04/21/custom-errors/
I suggest replacing revert strings with custom errors.
++i costs less gas as compared to i++ for unsigned integer, as per-increment is cheaper(its about 5 gas per iteration cheaper)
i++ increments i and returns initial value of i. Which means
uint i = 1; i++; // ==1 but i ==2
But ++i returns the actual incremented value:
uint i = 1; ++i; // ==2 and i ==2 , no need for temporary variable here
In the first case, the compiler has create a temporary variable (when used) for returning 1 instead of 2.
Swivel/Swivel.sol:100: Swivel/Swivel.sol:269: Swivel/Swivel.sol:418: Swivel/Swivel.sol:511: Swivel/Swivel.sol:564:
Swivel/Swivel.sol:100: unchecked {i++;} Swivel/Swivel.sol:269: unchecked {i++;} Swivel/Swivel.sol:418: i++; Swivel/Swivel.sol:511: x++; Swivel/Swivel.sol:564: i++;
#0 - robrobbins
2022-08-31T19:09:43Z
items addressed elsewhere, i'll mark as resolved for the comment about custom errors which we did but not as part of a ticket - this mentions it tho.