Platform: Code4rena
Start Date: 03/05/2022
Pot Size: $50,000 USDC
Total HM: 4
Participants: 46
Period: 5 days
Judge: gzeon
Total Solo HM: 2
Id: 117
League: ETH
Rank: 21/46
Findings: 2
Award: $199.11
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: BowTiedWardens
Also found by: 0x1337, 0x1f8b, 0x4non, 0xDjango, David_, Funen, GimelSec, IllIllI, Picodes, TerrierLover, WatchPug, bobi, cryptphi, csanuragjain, delfin454000, dirk_y, ellahi, fatherOfBlocks, hyh, ilan, jayjonah8, kebabsec, leastwood, oyc_109, robee, samruna, simon135, sorrynotsorry, throttle
114.326 USDC - $114.33
CNft.sol imports non-upgradeable version of openzeppelin interfaces.
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import "@openzeppelin/contracts/interfaces/IERC1155.sol"; import "@openzeppelin/contracts/interfaces/IERC721.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
For the consistency, it can use the upgradeable version of interfaces. Here are upgradeable version of interfaces. It also needs to update codes to use upgradeable interface.
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155ReceiverUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC1155Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes calldata data ) public virtual override { // Unused. from; to; id; amount; data; revert("CNFT: Use safeBatchTransferFrom instead"); }
The indentation of safeTransferFrom seems not consistent with other codebase.
It can write like this to be consistent.
function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes calldata data ) public virtual override { // Unused. from; to; id; amount; data; revert("CNFT: Use safeBatchTransferFrom instead"); }
IERC1155.sol is imported at ERC1155Enumerable.sol.
import "@openzeppelin/contracts/interfaces/IERC1155.sol";
Instead, it can use IERC1155Upgradeable.sol which is a upgradeable counterpart.
import "@openzeppelin/contracts-upgradeable/interfaces/IERC1155Upgradeable.sol";
onlyAdmin()
seems not idealAs mentioned in the solidity document, the order of modifiers should be after state variables. https://docs.soliditylang.org/en/v0.8.13/style-guide.html#order-of-layout
Inside each contract, library or interface, use the following order: 1. Type declarations 2. State variables 3. Events 4. Modifiers 5. Functions
Modifiers is defined between admin
and uniswapV2Oracle
state variables, which seem not ideal.
https://github.com/bunkerfinance/bunker-protocol/blob/752126094691e7457d08fc62a6a5006df59bd2fe/contracts/Oracles/CNftPriceOracle.sol#L30-L33
onlyAdmin
modifier can be located after underlyingNftxTokenAddress
state variable.
address public admin; UniswapV2PriceOracle public immutable uniswapV2Oracle; address public immutable uniswapV2Factory; address public immutable baseToken; /// @dev Mapping from CNft address to underlying NFTX token address. mapping(address => address) public underlyingNftxTokenAddress; modifier onlyAdmin() { require(msg.sender == admin, "Sender must be the admin."); _; }
The newAdmin
variable passed in changeAdmin function does not have address(0) check on newAdmin
.
function changeAdmin(address newAdmin) external onlyAdmin { admin = newAdmin; }
If newAdmin = address(0) is set at the function, admin operations of CNftPriceOracle cannot be done at all.
It is worth adding address(0) check at changeAdmin function unless setting address(0) at newAdmin is expected behavior.
function changeAdmin(address newAdmin) external onlyAdmin { require(newAdmin != address(0)); admin = newAdmin; }
liquidateBorrowNftFresh
function has following input validations.
require(seizeIds.length == seizeAmounts.length, "SEIZE_IDS_SEIZE_AMOUNTS_MISMATCH");
It is worth adding several checks such as seizeIds.length != 0
or borrower != address(borrower)
to have secure code.
require(seizeIds.length != 0, "..."); require(borrower != address(borrower), "...")
🌟 Selected for report: BowTiedWardens
Also found by: 0v3rf10w, 0x1f8b, 0x4non, 0xDjango, 0xNazgul, 0xkatana, Cityscape, Fitraldys, Funen, GimelSec, IllIllI, MaratCerby, Picodes, TerrierLover, Tomio, delfin454000, ellahi, fatherOfBlocks, hansfriese, ilan, joestakey, oyc_109, rfa, robee, samruna, simon135, slywaters, throttle
84.784 USDC - $84.78
Following codes set 0 at uint256 variables.
uint256 totalAmount = 0;
vars.totalAmount = 0;
The default value of uint256 is 0, so there is no need to set 0. It can write like this to reduce gas cost.
uint256 totalAmount;
vars.totalAmount;
mintAllowedResult
variable at mint functionmintAllowedResult variable is only used once.
uint mintAllowedResult = ComptrollerInterface(comptroller).mintAllowed(address(this), msg.sender, 0); require(mintAllowedResult == 0, "CNFT: Mint is not allowed");
It can avoid defining mintAllowedResult to reduce the gas cost. It can write like this:
require(ComptrollerInterface(comptroller).mintAllowed(address(this), msg.sender, 0) == 0, "CNFT: Mint is not allowed");
siezeAllowedResult
variable at seize functionuint siezeAllowedResult = ComptrollerInterface(comptroller).seizeAllowed(address(this), msg.sender, liquidator, borrower, 0); require(siezeAllowedResult == 0, "CNFT: Seize is not allowed");
It can avoid defining siezeAllowedResult to reduce the gas cost. It can write like this:
require(ComptrollerInterface(comptroller).seizeAllowed(address(this), msg.sender, liquidator, borrower, 0) == 0, "CNFT: Seize is not allowed");
redeemAllowedResult
variable at redeem functionuint redeemAllowedResult = ComptrollerInterface(comptroller).redeemAllowed(address(this), msg.sender, totalAmount); require(redeemAllowedResult == 0, "CNFT: Redeem is not allowed");
It can avoid defining redeemAllowedResult to reduce the gas cost. It can write like this:
require(ComptrollerInterface(comptroller).redeemAllowed(address(this), msg.sender, totalAmount) == 0, "CNFT: Redeem is not allowed");
transferAllowedResult
variable at safeBatchTransferFrom functionuint transferAllowedResult = ComptrollerInterface(comptroller).transferAllowed(address(this), from, to, vars.totalAmount); require(transferAllowedResult == 0, "CNFT: Redeem is not allowed");
It can avoid defining transferAllowedResult to reduce the gas cost. It can write like this:
require(ComptrollerInterface(comptroller).transferAllowed(address(this), from, to, vars.totalAmount) == 0, "CNFT: Redeem is not allowed");
Following codebase uses > 0
on uint variables.
require( reserve1 > 0, "UniswapV2PriceOracle: Division by zero." );
The above mentioned codebase can be rewritten like this to reduce gas cost:
require( reserve1 != 0, "UniswapV2PriceOracle: Division by zero." );
The codebase sets 0 on uint256 variable.
uint256 numberUpdated = 0; for (uint256 i = 0; i < pairs.length; ++i) {
It can be rewritten like this to reduce the gas cost.
uint256 numberUpdated; for (uint256 i; i < pairs.length; ++i) {
This code uses > 1
but it can use != 1
instead.
require(length > 1, "UniswapV2PriceOracle: Only one observation.");
length
variable is uint256 so it cannot be less than 0. In addition, it has a condition: require(length > 0, "UniswapV2PriceOracle: No observations.");
, so length
variable should be more than or equal to 1. Therefore, the above code can be rewritten like this:
require(length != 1, "UniswapV2PriceOracle: Only one observation.");
cNfts.length will be uint so it can simply check != 0.
require( cNfts.length > 0 && cNfts.length == nftxTokens.length, "CNftPriceOracle: `cNfts` and `nftxTokens` must have nonzero, equal lengths." );
Rewriting the above code to use != 0 like this can reduce gas cost.
require( cNfts.length != 0 && cNfts.length == nftxTokens.length, "CNftPriceOracle: `cNfts` and `nftxTokens` must have nonzero, equal lengths." );
It sets 0 at uint256 i, but this is not needed.
for (uint256 i = 0; i < cNfts.length; ++i) {
Rewriting like this can reduce gas cost.
for (uint256 i; i < cNfts.length; ++i) {