Platform: Code4rena
Start Date: 08/05/2023
Pot Size: $90,500 USDC
Total HM: 17
Participants: 102
Period: 7 days
Judge: 0xean
Total Solo HM: 4
Id: 236
League: ETH
Rank: 29/102
Findings: 2
Award: $688.35
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: brgltd
Also found by: 0x73696d616f, 0xAce, 0xSmartContract, 0xWaitress, 0xkazim, 0xnev, Aymen0909, BGSecurity, Bauchibred, Cayo, ChrisTina, Franfran, IceBear, Infect3d, Kose, Lilyjjo, PNS, RaymondFam, Sathish9098, Team_Rocket, Udsen, YakuzaKiawe, YoungWolves, berlin-101, bin2chen, btk, codeslide, fatherOfBlocks, frazerch, kodyvim, koxuan, lfzkoala, lukris02, matrix_0wl, nadin, naman1778, sashik_eth, tnevler, volodya, wonjun, yjrwkk
56.6347 USDC - $56.63
There are 6 instances of this issue in 5 files:
File: Rewards/RewardsDistributor.sol 26: mapping(address => mapping(address => uint256)) public rewardTokenSupplierIndex; 50: mapping(address => mapping(address => uint256)) public rewardTokenBorrowerIndex;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Rewards/RewardsDistributor.sol
File: Pool/PoolRegistry.sol 103: mapping(address => mapping(address => address)) private _vTokens;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Pool/PoolRegistry.sol
File: VTokenInterfaces.sol 110: mapping(address => mapping(address => uint256)) internal transferAllowances;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/VTokenInterfaces.sol
File: ComptrollerStorage.sol 95: mapping(address => mapping(Action => bool)) internal _actionPaused;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/ComptrollerStorage.sol
File: RiskFund/ReserveHelpers.sol 17: mapping(address => mapping(address => uint256)) internal poolsAssetsReserves;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/RiskFund/ReserveHelpers.sol
The package.json configuration file says that the project is using 4.8.0 of OpenZeppelin which has a vulnerability.
Although there is no security vulnerability covering the project, it is recommended to use the latest version 4.8.3.
There is 1 instance of this issue in 1 file:
File: package.json 51: "@openzeppelin/contracts": "^4.8.0",
https://github.com/code-423n4/2023-05-venus/blob/main/package.json#L51
Consider using a modifier to implement access control instead of inlining the condition/requirement in the function’s body.
There are 6 instances of this issue in 3 files:
File: Comptroller.sol 802: _checkSenderIs(poolRegistry);
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Comptroller.sol
File: VToken.sol 395: if (msg.sender != address(comptroller)) { 396: revert HealBorrowUnauthorized(); 397: } 456: if (msg.sender != address(comptroller)) { 457: revert ForceLiquidateBorrowUnauthorized(); 458: } 489: require(msg.sender == shortfall, "only shortfall contract can update bad debt"); 525: require(msg.sender == owner(), "VToken::sweepToken: only admin can sweep tokens");
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/VToken.sol
File: RiskFund/RiskFund.sol 191: require(msg.sender == shortfall, "Risk fund: Only callable by Shortfall contract");
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/RiskFund/RiskFund.sol
While the compiler knows to optimize away the exponentiation, it’s still better coding practice to use idioms that do not require compiler optimization, if they exist
There is 1 instance of this issue in 1 file:
File: Pool/PoolRegistry.sol 290: 10**(underlyingDecimals + 18 - input.decimals),
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Pool/PoolRegistry.sol
You can use the delete keyword instead of setting the variable as zero.
There are 7 instances of this issue in 2 files:
File: Comptroller.sol 812: newMarket.collateralFactorMantissa = 0; 813: newMarket.liquidationThresholdMantissa = 0; 1353: snapshot.liquidity = 0; 1355: snapshot.liquidity = 0;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Comptroller.sol
File: Shortfall/Shortfall.sol 370: auction.highestBidBps = 0; 371: auction.highestBidBlock = 0; 376: auction.marketDebt[vToken] = 0;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Shortfall/Shortfall.sol
#0 - c4-judge
2023-05-18T18:11:22Z
0xean marked the issue as grade-b
🌟 Selected for report: JCN
Also found by: 0xAce, 0xSmartContract, Aymen0909, K42, Rageur, Raihan, ReyAdmirado, SAAJ, SM3_SS, Sathish9098, Team_Rocket, Udsen, c3phas, codeslide, descharre, fatherOfBlocks, hunter_w3b, j4ld1na, lllu_23, matrix_0wl, naman1778, petrichor, pontifex, rapha, souilos, wahedtalash77
631.723 USDC - $631.72
The variables that are only set at the time of construction of contract should be set as immutable to save gas cost.
There is 1 instance of this issue in 1 file:
File: BaseJumpRateModelV2.sol 74: accessControlManager = accessControlManager_;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/BaseJumpRateModelV2.sol
contract GasTest is DSTest { Contract0 c0; Contract_Optimized c1; function setUp() public { c0 = new Contract0(); c1 = new Contract_Optimized(); } function testGas() public { c0.getNum(); c1.getNum(); } } contract Contract0 { uint256 num; constructor(){ num = 10; } function getNum() public returns(uint256){ return num; } } contract Contract_Optimized { uint256 immutable num; constructor(){ num = 10; } function getNum() public returns(uint256){ return num; } }
Contract0 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
46181 | 154 | ||||
Function Name | min | avg | median | max | # calls |
getNum | 2246 | 2246 | 2246 | 2246 | 1 |
Contract_Optimized contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
30108 | 195 | ||||
Function Name | min | avg | median | max | # calls |
getNum | 146 | 146 | 146 | 146 | 1 |
Counting down is more gas efficient than counting up because neither we are making zero variable to non-zero variable and also we will get gas refund in the last transaction when making non-zero to zero variable.
There are 14 instances of this issue in 6 files:
File: Comptroller.sol 217: for (uint256 i; i < len; ++i) { 274: for (uint256 i; i < rewardDistributorsCount; ++i) { 304: for (uint256 i; i < rewardDistributorsCount; ++i) { 376: for (uint256 i; i < rewardDistributorsCount; ++i) { 402: for (uint256 i; i < rewardDistributorsCount; ++i) { 521: for (uint256 i; i < rewardDistributorsCount; ++i) { 558: for (uint256 i; i < rewardDistributorsCount; ++i) { 584: for (uint256 i; i < userAssetsCount; ++i) { 611: for (uint256 i; i < userAssetsCount; ++i) { 669: for (uint256 i; i < ordersCount; ++i) { 690: for (uint256 i; i < marketsCount; ++i) { 819: for (uint256 i; i < rewardDistributorsCount; ++i) { 846: for (uint256 i; i < numMarkets; ++i) { 871: for (uint256 i; i < vTokensCount; ++i) { 932: for (uint256 i; i < rewardsDistributorsLength; ++i) { 948: for (uint256 i; i < marketsCount; ++i) { 1122: for (uint256 i; i < rewardsDistributorsLength; ++i) { 1208: for (uint256 i; i < marketsCount; ++i) { 1307: for (uint256 i; i < assetsCount; ++i) {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Comptroller.sol
File: Lens/PoolLens.sol 127: for (uint256 i; i < vTokenCount; ++i) { 145: for (uint256 i; i < poolLength; ++i) { 208: for (uint256 i; i < vTokenCount; ++i) { 229: for (uint256 i; i < rewardsDistributors.length; ++i) { 263: for (uint256 i; i < markets.length; ++i) { 391: for (uint256 i; i < vTokenCount; ++i) { 418: for (uint256 i; i < markets.length; ++i) {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Lens/PoolLens.sol
File: Shortfall/Shortfall.sol 175: for (uint256 i; i < marketsCount; ++i) { 223: for (uint256 i; i < marketsCount; ++i) { 374: for (uint256 i; i < marketsCount; ++i) { 389: for (uint256 i; i < marketsCount; ++i) {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Shortfall/Shortfall.sol
File: Rewards/RewardsDistributor.sol 209: for (uint256 i; i < numTokens; ++i) { 282: for (uint256 i; i < vTokensCount; ++i) {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Rewards/RewardsDistributor.sol
File: Pool/PoolRegistry.sol 356: for (uint256 i = 1; i <= _numberOfPools; ++i) {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Pool/PoolRegistry.sol
File: RiskFund/RiskFund.sol 166: for (uint256 i; i < marketsCount; ++i) {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/RiskFund/RiskFund.sol
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.AddNum(); c1.AddNum(); } } contract Contract0 { uint256 num = 3; function AddNum() public { uint256 _num = num; for(uint i=0;i<=9;i++){ _num = _num +1; } num = _num; } } contract Contract1 { uint256 num = 3; function AddNum() public { uint256 _num = num; for(uint i=9;i>=0;i--){ _num = _num +1; } num = _num; } }
Contract0 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
77011 | 311 | ||||
Function Name | min | avg | median | max | # calls |
AddNum | 7040 | 7040 | 7040 | 7040 | 1 |
Contract1 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
73811 | 295 | ||||
Function Name | min | avg | median | max | # calls |
AddNum | 3819 | 3819 | 3819 | 3819 | 1 |
Using > 0 costs more gas than != 0 when used on a uint in a require() statement.
There are 4 instances of this issue in 2 files:
File: VToken.sol 1369: require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero.");
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/VToken.sol
File: RiskFund/RiskFund.sol 82: require(minAmountToConvert_ > 0, "Risk Fund: Invalid min amount to convert"); 83: require(loopsLimit_ > 0, "Risk Fund: Loops limit can not be zero"); 139: require(minAmountToConvert_ > 0, "Risk Fund: Invalid min amount to convert");
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/RiskFund/RiskFund.sol
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.not_optimized(5); c1.optimized(5); } } contract Contract0 { function not_optimized(uint8 x) public returns(uint8){ require(x>0,"x is greater than 0"); } } contract Contract1 { function optimized(uint8 x) public returns(uint8){ require(x!=0,"x is greater than 0"); } }
Contract0 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
58911 | 326 | ||||
Function Name | min | avg | median | max | # calls |
not_optimized | 327 | 327 | 327 | 327 | 1 |
Contract1 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
59111 | 327 | ||||
Function Name | min | avg | median | max | # calls |
optimized | 327 | 327 | 327 | 327 | 1 |
Checking zero address can be improved by replacing the require statement with Assembly.Solidity has a lot of guardrails that can be removed (with care) for optimization purposes, especially for simple functionality like checking if an address is zero.
There are 41 instances of this issue in 8 files:
File: Comptroller.sol 128: require(poolRegistry_ != address(0), "invalid pool registry address"); 962: require(address(newOracle) != address(0), "invalid price oracle address");
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Comptroller.sol
File: VToken.sol 72: require(admin_ != address(0), "invalid admin address"); 134: require(spender != address(0), "invalid spender address"); 196: require(minter != address(0), "invalid minter address"); 626: require(spender != address(0), "invalid spender address"); 646: require(spender != address(0), "invalid spender address"); 1399: if (shortfall_ == address(0)) { 1408: if (protocolShareReserve_ == address(0)) {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/VToken.sol
File: Shortfall/Shortfall.sol 137: require(convertibleBaseAsset_ != address(0), "invalid base asset address"); 138: require(address(riskFund_) != address(0), "invalid risk fund address"); 166: ((auction.highestBidder != address(0) && bidBps > auction.highestBidBps) || 167: (auction.highestBidder == address(0) && bidBps >= auction.startBidBps))) || 169: ((auction.highestBidder != address(0) && bidBps < auction.highestBidBps) || 170: (auction.highestBidder == address(0) && bidBps <= auction.startBidBps))), 180: if (auction.highestBidder != address(0)) { 189: if (auction.highestBidder != address(0)) { 214: block.number > auction.highestBidBlock + nextBidderBlockLimit && auction.highestBidder != address(0), 349: require(_poolRegistry != address(0), "invalid address");
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Shortfall/Shortfall.sol
File: Pool/PoolRegistry.sol 225: require(beaconAddress != address(0), "PoolRegistry: Invalid Comptroller beacon address."); 226: require(priceOracle != address(0), "PoolRegistry: Invalid PriceOracle address."); 257: require(input.comptroller != address(0), "PoolRegistry: Invalid comptroller address"); 258: require(input.asset != address(0), "PoolRegistry: Invalid asset address"); 259: require(input.beaconAddress != address(0), "PoolRegistry: Invalid beacon address"); 260: require(input.vTokenReceiver != address(0), "PoolRegistry: Invalid vTokenReceiver address"); 264: _vTokens[input.comptroller][input.asset] == address(0), 396: require(venusPool.creator == address(0), "PoolRegistry: Pool already exists in the directory."); 422: if (address(shortfall_) == address(0)) { 431: if (protocolShareReserve_ == address(0)) {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Pool/PoolRegistry.sol
File: RiskFund/RiskFund.sol 80: require(pancakeSwapRouter_ != address(0), "Risk Fund: Pancake swap address invalid"); 81: require(convertibleBaseAsset_ != address(0), "Risk Fund: Base asset address invalid"); 100: require(_poolRegistry != address(0), "Risk Fund: Pool registry address invalid"); 111: require(shortfallContractAddress_ != address(0), "Risk Fund: Shortfall contract address invalid"); 127: require(pancakeSwapRouter_ != address(0), "Risk Fund: PancakeSwap address invalid"); 157: require(poolRegistry != address(0), "Risk fund: Invalid pool registry.");
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/RiskFund/RiskFund.sol
File: BaseJumpRateModelV2.sol 72: require(address(accessControlManager_) != address(0), "invalid ACM address");
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/BaseJumpRateModelV2.sol
File: RiskFund/ProtocolShareReserve.sol 40: require(_protocolIncome != address(0), "ProtocolShareReserve: Protocol Income address invalid"); 41:require(_riskFund != address(0), "ProtocolShareReserve: Risk Fund address invalid"); 54: require(_poolRegistry != address(0), "ProtocolShareReserve: Pool registry address invalid"); 71: require(asset != address(0), "ProtocolShareReserve: Asset address invalid");
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/RiskFund/ProtocolShareReserve.sol
File: Proxy/UpgradeableBeacon.sol 8: require(implementation_ != address(0), "Invalid implementation address");
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Proxy/UpgradeableBeacon.sol
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.not_optimized(0x0000000000000000000000000000000000000001); c1.optimized(0x0000000000000000000000000000000000000001); } } contract Contract0 { function not_optimized(address addr) public pure{ if(addr == address(0)){ revert(); } } } contract Contract1 { function optimized(address addr) public pure{ assembly { if iszero(addr) { revert(0x00, 0x20) } } } }
Contract0 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
41893 | 240 | ||||
Function Name | min | avg | median | max | # calls |
not_optimized | 258 | 258 | 258 | 258 | 1 |
Contract1 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
37687 | 219 | ||||
Function Name | min | avg | median | max | # calls |
optimized | 252 | 252 | 252 | 252 | 1 |
When writing value for variables whose type is address, make use of assembly code instead of solidity code.
There are 15 instances of this issue in 6 files:
File: Comptroller.sol 130: poolRegistry = poolRegistry_;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Comptroller.sol
File: VToken.sol 1390: underlying = underlying_; 1403: shortfall = shortfall_; 1412: protocolShareReserve = protocolShareReserve_;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/VToken.sol
File: Shortfall/Shortfall.sol 145: convertibleBaseAsset = convertibleBaseAsset_; 351: poolRegistry = _poolRegistry;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Shortfall/Shortfall.sol
File: Pool/PoolRegistry.sol 435: protocolShareReserve = protocolShareReserve_;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Pool/PoolRegistry.sol
File: RiskFund/RiskFund.sol 88: pancakeSwapRouter = pancakeSwapRouter_; 89: minAmountToConvert = minAmountToConvert_; 90: convertibleBaseAsset = convertibleBaseAsset_; 118: shortfall = shortfallContractAddress_; 129: pancakeSwapRouter = pancakeSwapRouter_; 141: minAmountToConvert = minAmountToConvert_;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/RiskFund/RiskFund.sol
File: RiskFund/ProtocolShareReserve.sol 45: protocolIncome = _protocolIncome; 46: riskFund = _riskFund;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/RiskFund/ProtocolShareReserve.sol
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.setOwnerAssembly(0xFD2dabe9DFcc4d88a12A9D0D40D834E81217Cccf); c1.setOwner(0xFD2dabe9DFcc4d88a12A9D0D40D834E81217Cccf); } } contract Contract0 { address owner; function setOwnerAssembly(address _owner) public { assembly{ sstore(owner.slot,_owner) } } } contract Contract1 { address owner; function setOwner(address _owner) public { owner = _owner; } }
Contract0 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
35287 | 207 | ||||
Function Name | min | avg | median | max | # calls |
setOwnerAssembly | 22324 | 22324 | 22324 | 22324 | 1 |
Contract1 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
48499 | 273 | ||||
Function Name | min | avg | median | max | # calls |
setOwner | 22363 | 22363 | 22363 | 22363 | 1 |
Using nested is cheaper than using && multiple check combinations. There are more advantages, such as easier to read code and better coverage reports.
There are 12 instances of this issue in 4 files:
File: Comptroller.sol 130: if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(address(vToken)) == 0) {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Comptroller.sol
File: VToken.sol 837: if (redeemTokens == 0 && redeemAmount > 0) {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/VToken.sol
File: Lens/PoolLens.sol 462: if (deltaBlocks > 0 && borrowSpeed > 0) { 483: if (deltaBlocks > 0 && supplySpeed > 0) { 506: if (borrowerIndex.mantissa == 0 && borrowIndex.mantissa > 0) { 526: if (supplierIndex.mantissa == 0 && supplyIndex.mantissa > 0) {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Lens/PoolLens.sol
File: Rewards/RewardsDistributor.sol 261: if (deltaBlocks > 0 && rewardTokenSpeed > 0) { 348: if (supplierIndex == 0 && supplyIndex >= rewardTokenInitialIndex) { 386: if (borrowerIndex == 0 && borrowIndex >= rewardTokenInitialIndex) { 418: if (amount > 0 && amount <= rewardTokenRemaining) { 435: if (deltaBlocks > 0 && supplySpeed > 0) { 463: if (deltaBlocks > 0 && borrowSpeed > 0) {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Rewards/RewardsDistributor.sol
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.checkAge(19); c1.checkAgeOptimized(19); } } contract Contract0 { function checkAge(uint8 _age) public returns(string memory){ if(_age>18 && _age<22){ return "Eligible"; } } } contract Contract1 { function checkAgeOptimized(uint8 _age) public returns(string memory){ if(_age>18){ if(_age<22){ return "Eligible"; } } } }
Contract0 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
76923 | 416 | ||||
Function Name | min | avg | median | max | # calls |
checkAge | 651 | 651 | 651 | 651 | 1 |
Contract1 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
76323 | 413 | ||||
Function Name | min | avg | median | max | # calls |
checkAgeOptimized | 645 | 645 | 645 | 645 | 1 |
When a function with a memory array is called externally, the abi.decode() step has to use a for-loop to copy each index of the calldata to the memory index. Each iteration of this for-loop costs at least 60 gas (i.e. 60 * <mem_array>.length). Using calldata directly, obliviates the need for such a loop in the contract code and runtime execution. Note that even if an interface defines a function as having memory arguments, it’s still valid for implementation contracs to use calldata arguments instead.
If the array is passed to an internal function which passes the array to another internal function where the array is modified and therefore memory is used in the external call, it’s still more gass-efficient to use calldata when the external function uses modifiers, since the modifiers may prevent the internal functions from being called. Structs have the same overhead as an array of length one
Note that I’ve also flagged instances where the function is public but can be marked as external since it’s not called by the contract, and cases where a constructor is involved
There are 8 instances of this issue in 5 files:
File: Comptroller.sol 154: function enterMarkets(address[] memory vTokens) external override returns (uint256[] memory) {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Comptroller.sol
File: VToken.sol 59: function initialize( 60: address underlying_, 61: ComptrollerInterface comptroller_, 62: InterestRateModel interestRateModel_, 63: uint256 initialExchangeRateMantissa_, 64: string memory name_, 65: string memory symbol_, 66: uint8 decimals_, 67: address admin_, 68: address accessControlManager_, 69: RiskManagementInit memory riskManagement, 70: uint256 reserveFactorMantissa_ 71:) external initializer {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/VToken.sol
163: function distributeBorrowerRewardToken( 164: address vToken, 165: address borrower, 166: Exp memory marketBorrowIndex 167: ) external onlyComptroller { 187: function updateRewardTokenBorrowIndex(address vToken, Exp memory marketBorrowIndex) external onlyComptroller { 197: function setRewardTokenSpeeds( 198: VToken[] memory vTokens, 200: uint256[] memory supplySpeeds, 201: uint256[] memory borrowSpeeds 202: ) external {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Rewards/RewardsDistributor.sol
File: Pool/PoolRegistry.sol 255: function addMarket(AddMarketInput memory input) external { 343: function updatePoolMetadata(address comptroller, VenusPoolMetaData memory _metadata) external {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Pool/PoolRegistry.sol
File: Factories/VTokenProxyFactory.sol 28: function deployVTokenProxy(VTokenArgs memory input) external returns (VToken) {
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Factories/VTokenProxyFactory.sol
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.not_optimized("Naman"); c1.optimized("Naman"); } } contract Contract0 { function not_optimized(string memory a) public returns(string memory){ return a; } } contract Contract1 { function optimized(string calldata a) public returns(string calldata){ return a; } }
Contract0 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
100747 | 535 | ||||
Function Name | min | avg | median | max | # calls |
not_optimized | 790 | 790 | 790 | 790 | 1 |
Contract1 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
66917 | 366 | ||||
Function Name | min | avg | median | max | # calls |
optimized | 556 | 556 | 556 | 556 | 1 |
Using a state variable in SetOwner emits wastes gas.
There are 4 instances of this issue in 4 files:
File: Comptroller.sol 709: emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa);
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Comptroller.sol
File: BaseJumpRateModelV2.sol 162: emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink);
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/BaseJumpRateModelV2.sol
File: WhitePaperInterestRateModel.sol 40: emit NewInterestParams(baseRatePerBlock, multiplierPerBlock);
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/WhitePaperInterestRateModel.sol
File: MaxLoopsLimitHelper.sol 31: emit MaxLoopsLimitUpdated(oldMaxLoopsLimit, maxLoopsLimit);
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/MaxLoopsLimitHelper.sol
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.not_optimized(5); c1.optimized(5); } } contract Contract0 { uint8 num; function not_optimized(uint8 x) public returns(uint8){ num = x; return num; } } contract Contract1 { uint8 num; function optimized(uint8 x) public returns(uint8){ num = x; return x; } }
src/test/GasTest.t.sol:Contract0 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
44893 | 255 | ||||
Function Name | min | avg | median | max | # calls |
not_optimized | 22417 | 22417 | 22417 | 22417 | 1 |
src/test/GasTest.t.sol:Contract1 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
44093 | 251 | ||||
Function Name | min | avg | median | max | # calls |
optimized | 22405 | 22405 | 22405 | 22405 | 1 |
Using the addition operator instead of plus-equals saves 113 gas. Subtructions act the same way.
There are 8 instances of this issue in 4 files:
File: VToken.sol 630: newAllowance += addedValue; 652: currentAllowance -= subtractedValue;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/VToken.sol
File: RiskFund/ReserveHelpers.sol 66: assetsReserves[asset] += balanceDifference; 67: poolsAssetsReserves[comptroller][asset] += balanceDifference;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/RiskFund/ReserveHelpers.sol
File: RiskFund/RiskFund.sol 249: assetsReserves[underlyingAsset] -= balanceOfUnderlyingAsset; 250: poolsAssetsReserves[comptroller][underlyingAsset] -= balanceOfUnderlyingAsset;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/RiskFund/RiskFund.sol
File: RiskFund/ProtocolShareReserve.sol 74: assetsReserves[asset] -= amount; 75: poolsAssetsReserves[comptroller][asset] -= amount;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/RiskFund/ProtocolShareReserve.sol
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.add(); c1.addOptimized(); } } contract Contract0 { uint8 num1 = 1; function add() public{ num1 += 1; } } contract Contract1 { uint8 num1 = 1; function addOptimized() public{ num1 = num1 + 1; } }
Contract0 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
67017 | 268 | ||||
Function Name | min | avg | median | max | # calls |
add | 5405 | 5405 | 5405 | 5405 | 1 |
Contract1 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
70623 | 286 | ||||
Function Name | min | avg | median | max | # calls |
addOptimized | 5363 | 5363 | 5363 | 5363 | 1 |
The reason for using the delete keyword here is to reset the struct values (set to default value) in every operation. However, the struct values do not need to be zero each time the function is run. Therefore, the delete” key word is unnecessary.
There are 2 instances of this issue in 2 files:
File: Comptroller.sol 209: delete marketToExit.accountMembership[msg.sender];
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Comptroller.sol
File: Shortfall/Shortfall.sol 379: delete auction.markets;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Shortfall/Shortfall.sol
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.not_optimized(); c1.optimized(); } } contract Contract0 { struct details{ string name; uint8 age; } function not_optimized() public returns(uint8){ details memory det = details({name:"John",age:17}); delete det.name; det.name = "Alis"; } } contract Contract1 { struct details{ string name; uint8 age; } function optimized() public returns(uint8){ details memory det = details({name:"John",age:17}); det.name = "Alias"; } }
src/test/GasTest.t.sol:Contract0 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
47699 | 268 | ||||
Function Name | min | avg | median | max | # calls |
not_optimized | 320 | 320 | 320 | 320 | 1 |
src/test/GasTest.t.sol:Contract1 contract | |||||
---|---|---|---|---|---|
Deployment Cost | Deployment Size | ||||
47099 | 265 | ||||
Function Name | min | avg | median | max | # calls |
optimized | 308 | 308 | 308 | 308 | 1 |
#0 - c4-judge
2023-05-18T17:14:35Z
0xean marked the issue as grade-a
#1 - c4-sponsor
2023-05-23T21:42:01Z
chechu marked the issue as sponsor confirmed
#2 - chechu
2023-05-23T21:42:13Z
G-01 Confirm G-02 Confirm G-03 Confirm G-04 Confirm G-05 Confirm G-06 Confirm G-07 Confirm G-08 Confirm G-09 Confirm G-10 Confirm