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: 88/102
Findings: 1
Award: $44.94
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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
44.9387 USDC - $44.94
NB: Some functions have been truncated where necessary to just show affected parts of the code Throughout the report some places might be denoted with audit tags to show the actual place affected.
Using true
and false
will trigger gas-refunds, which after London are 1/5 of what they used to be, meaning using 1
and 2
(keeping the slot non-zero), will cost 5k per change (5k + 5k) vs 20k + 5k, saving you 15k gas per function which uses the modifier
File: /contracts/VToken.sol 33: modifier nonReentrant() { 34: require(_notEntered, "re-entered"); 35: _notEntered = false; 36: _; 37: _notEntered = true; // get a gas-refund post-Istanbul 38: }
Note: We could debate about the above issue being part of automated findings grouped under use of bools, however due to the huge impact it has I'm adding it here.
External calls are expensive. Consider caching the following:
File: /contracts/VToken.sol 524: function sweepToken(IERC20Upgradeable token) external override { 525: require(msg.sender == owner(), "VToken::sweepToken: only admin can sweep tokens"); 528: token.safeTransfer(owner(), balance);
In case of a revert on the first check we'd end up wasting about 3 gas used for the mstore but if we don't revert we'd save significant amount of gas
function sweepToken(IERC20Upgradeable token) external override { - require(msg.sender == owner(), "VToken::sweepToken: only admin can sweep tokens"); + address _owner = owner(); + require(msg.sender == _owner, "VToken::sweepToken: only admin can sweep tokens"); require(address(token) != underlying, "VToken::sweepToken: can not sweep underlying token"); uint256 balance = token.balanceOf(address(this)); - token.safeTransfer(owner(), balance); + token.safeTransfer(_owner, balance); emit SweepToken(address(token)); }
File: /contracts/RiskFund/RiskFund.sol 238:ComptrollerViewInterface(comptroller).oracle().updatePrice(address(vToken));//@audit: Initial call 242: uint256 underlyingAssetPrice = ComptrollerViewInterface(comptroller).oracle().getUnderlyingPrice( address(vToken) );//@audit: 2nd call
When using a for loop or any other loop, we should avoid iterating over storage variables as the loop can become very expensive. We should cache the storage variable in memory and use the cached value in the loop.
File: /contracts/Pool/PoolRegistry.sol 354: function getAllPools() external view override returns (VenusPool[] memory) { 355: VenusPool[] memory _pools = new VenusPool[](_numberOfPools); 356: for (uint256 i = 1; i <= _numberOfPools; ++i) { 357: address comptroller = _poolsByID[i]; 358: _pools[i - 1] = (_poolByComptroller[comptroller]); 359: } 360: return _pools; 361: }
function getAllPools() external view override returns (VenusPool[] memory) { - VenusPool[] memory _pools = new VenusPool[](_numberOfPools); - for (uint256 i = 1; i <= _numberOfPools; ++i) { + uint256 numberOfPools_ = _numberOfPools; + VenusPool[] memory _pools = new VenusPool[](numberOfPools_); + for (uint256 i = 1; i <= numberOfPools_; ++i) { address comptroller = _poolsByID[i]; _pools[i - 1] = (_poolByComptroller[comptroller]); }
Here, the values emitted shouldn’t be read from storage. The existing memory values should be used instead:
contributorAccrued
instead of rewardTokenAccrued[contributor]
File: /contracts/Rewards/RewardsDistributor.sol 257: function updateContributorRewards(address contributor) public { 265: rewardTokenAccrued[contributor] = contributorAccrued; 266: lastContributorBlock[contributor] = blockNumber; 268: emit ContributorRewardsUpdated(contributor, rewardTokenAccrued[contributor]); 269: } 270: }
diff --git a/contracts/Rewards/RewardsDistributor.sol b/contracts/Rewards/RewardsDistributor.sol index 434732d..d174c19 100644 --- a/contracts/Rewards/RewardsDistributor.sol +++ b/contracts/Rewards/RewardsDistributor.sol @@ -265,7 +265,7 @@ contract RewardsDistributor is ExponentialNoError, Ownable2StepUpgradeable, Acce rewardTokenAccrued[contributor] = contributorAccrued; lastContributorBlock[contributor] = blockNumber; - emit ContributorRewardsUpdated(contributor, rewardTokenAccrued[contributor]); + emit ContributorRewardsUpdated(contributor, contributorAccrued); } }
File: /contracts/BaseJumpRateModelV2.sol 151: function _updateJumpRateModel( 152: uint256 baseRatePerYear, 153: uint256 multiplierPerYear, 154: uint256 jumpMultiplierPerYear, 155: uint256 kink_ 156: ) internal { 157: baseRatePerBlock = baseRatePerYear / blocksPerYear; 158: multiplierPerBlock = (multiplierPerYear * BASE) / (blocksPerYear * kink_); 159: jumpMultiplierPerBlock = jumpMultiplierPerYear / blocksPerYear; 160: kink = kink_; 162: emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink); 163: }
diff --git a/contracts/BaseJumpRateModelV2.sol b/contracts/BaseJumpRateModelV2.sol index 68b535a..0dc196e 100644 --- a/contracts/BaseJumpRateModelV2.sol +++ b/contracts/BaseJumpRateModelV2.sol @@ -159,7 +159,7 @@ abstract contract BaseJumpRateModelV2 is InterestRateModel { jumpMultiplierPerBlock = jumpMultiplierPerYear / blocksPerYear; kink = kink_; - emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink); + emit NewInterestParams(baseRatePerYear / blocksPerYear, (multiplierPerYear * BASE) / (blocksPerYear * kink_), jumpMultiplierPerYear / blocksPerYear, kink_); }
limit
instead of maxLoopsLimit
File: /contracts/MaxLoopsLimitHelper.sol 25: function _setMaxLoopsLimit(uint256 limit) internal { 29: maxLoopsLimit = limit; 31: emit MaxLoopsLimitUpdated(oldMaxLoopsLimit, maxLoopsLimit);
diff --git a/contracts/MaxLoopsLimitHelper.sol b/contracts/MaxLoopsLimitHelper.sol index 8607d70..f2ba93d 100644 --- a/contracts/MaxLoopsLimitHelper.sol +++ b/contracts/MaxLoopsLimitHelper.sol @@ -28,7 +28,7 @@ abstract contract MaxLoopsLimitHelper { uint256 oldMaxLoopsLimit = maxLoopsLimit; maxLoopsLimit = limit; - emit MaxLoopsLimitUpdated(oldMaxLoopsLimit, maxLoopsLimit); + emit MaxLoopsLimitUpdated(oldMaxLoopsLimit, limit); }
When fetching data from a storage location, assigning the data to a memory variable causes all fields of the struct/array to be read from storage, which incurs a Gcoldsload (2100 gas) for each field of the struct/array. If the fields are read from the new memory variable, they incur an additional MLOAD rather than a cheap stack read. Instead of declearing the variable with the memory keyword, declaring the variable with the storage keyword and caching any fields that need to be re-read in stack variables, will be much cheaper, only incuring the Gcoldsload for the fields actually read.
File: /contracts/Comptroller.sol 579: VToken[] memory userAssets = accountAssets[user];
diff --git a/contracts/Comptroller.sol b/contracts/Comptroller.sol index 41dc518..24841d2 100644 --- a/contracts/Comptroller.sol +++ b/contracts/Comptroller.sol @@ -576,7 +576,7 @@ contract Comptroller is * @custom:access Not restricted */ function healAccount(address user) external { - VToken[] memory userAssets = accountAssets[user]; + VToken[] storage userAssets = accountAssets[user]; uint256 userAssetsCount = userAssets.length;
File: /contracts/Comptroller.sol 687: VToken[] memory borrowMarkets = accountAssets[borrower]; 1304: VToken[] memory assets = accountAssets[account];
File: /contracts/VToken.sol 1441: BorrowSnapshot memory borrowSnapshot = accountBorrows[account];
File: /contracts/Pool/PoolRegistry.sol 394: VenusPool memory venusPool = _poolByComptroller[comptroller];
If a cache value exists in a function, there is no need to read the storage value. Use the cached value as it would be cheaper.
File: /contracts/RiskFund/RiskFund.sol 190: function transferReserveForAuction(address comptroller, uint256 amount) external override returns (uint256) { 191: require(msg.sender == shortfall, "Risk fund: Only callable by Shortfall contract"); 194: IERC20Upgradeable(convertibleBaseAsset).safeTransfer(shortfall, amount);
Note that in the above function, we require that msg.sender == shortfall
which means the only way this function executes is if both sender and shortfail are the same. Thus whenever we need to reference shortfall
which is a storage variable we can use the variable msg.sender
as they both represent the same thing.
diff --git a/contracts/RiskFund/RiskFund.sol b/contracts/RiskFund/RiskFund.sol index 32c3d30..3424827 100644 --- a/contracts/RiskFund/RiskFund.sol +++ b/contracts/RiskFund/RiskFund.sol @@ -191,7 +191,7 @@ contract RiskFund is require(msg.sender == shortfall, "Risk fund: Only callable by Shortfall contract"); require(amount <= poolReserves[comptroller], "Risk Fund: Insufficient pool reserve."); poolReserves[comptroller] = poolReserves[comptroller] - amount; - IERC20Upgradeable(convertibleBaseAsset).safeTransfer(shortfall, amount); + IERC20Upgradeable(convertibleBaseAsset).safeTransfer(msg.sender, amount); emit TransferredReserveForAuction(comptroller, amount);
File: /contracts/VToken.sol 1368: initialExchangeRateMantissa = initialExchangeRateMantissa_; 1369: require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero.");
diff --git a/contracts/VToken.sol b/contracts/VToken.sol index 8d9a2dc..0def337 100644 --- a/contracts/VToken.sol +++ b/contracts/VToken.sol @@ -1366,7 +1366,7 @@ contract VToken is // Set initial exchange rate initialExchangeRateMantissa = initialExchangeRateMantissa_; - require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero."); + require(initialExchangeRateMantissa_ > 0, "initial exchange rate must be greater than zero.");
underlying_
instead of underlying
File: /contracts/VToken.sol 1389: // Set underlying and sanity check it 1390: underlying = underlying_; 1391: IERC20Upgradeable(underlying).totalSupply();
diff --git a/contracts/VToken.sol b/contracts/VToken.sol index 8d9a2dc..0ed239b 100644 --- a/contracts/VToken.sol +++ b/contracts/VToken.sol @@ -1388,7 +1388,7 @@ contract VToken is // Set underlying and sanity check it underlying = underlying_; - IERC20Upgradeable(underlying).totalSupply(); + IERC20Upgradeable(underlying_).totalSupply();
Checks that involve constants should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting a Gcoldsload (2100 gas) in a function that may ultimately revert in the unhappy case.
File: /contracts/Comptroller.sol 726: function setCollateralFactor( 727: VToken vToken, 728: uint256 newCollateralFactorMantissa, 729: uint256 newLiquidationThresholdMantissa 730: ) external { 731: _checkAccessAllowed("setCollateralFactor(address,uint256,uint256)"); 733: // Verify market is listed 734: Market storage market = markets[address(vToken)]; 735: if (!market.isListed) { 736: revert MarketNotListed(address(vToken)); 737: } 739: // Check collateral factor <= 0.9 740: if (newCollateralFactorMantissa > collateralFactorMaxMantissa) { 741: revert InvalidCollateralFactor(); 742: } 744: // Ensure that liquidation threshold <= 1 745: if (newLiquidationThresholdMantissa > mantissaOne) { 746: revert InvalidLiquidationThreshold(); 747: } 749: // Ensure that liquidation threshold >= CF 750: if (newLiquidationThresholdMantissa < newCollateralFactorMantissa) { 751: revert InvalidLiquidationThreshold(); 752: }
We have an if statement that checks the validity of some functional arguments. As this is cheaper compared to reading storage , we should reorder the if statements to have this check come first. In case of a revert on the functional argument checks, we wouldn't waste alot of gas
diff --git a/contracts/Comptroller.sol b/contracts/Comptroller.sol index 41dc518..b7a22ac 100644 --- a/contracts/Comptroller.sol +++ b/contracts/Comptroller.sol @@ -730,6 +730,11 @@ contract Comptroller is ) external { _checkAccessAllowed("setCollateralFactor(address,uint256,uint256)"); + // Ensure that liquidation threshold >= CF + if (newLiquidationThresholdMantissa < newCollateralFactorMantissa) { + revert InvalidLiquidationThreshold(); + } + // Verify market is listed Market storage market = markets[address(vToken)]; if (!market.isListed) { @@ -746,11 +751,6 @@ contract Comptroller is revert InvalidLiquidationThreshold(); } - // Ensure that liquidation threshold >= CF - if (newLiquidationThresholdMantissa < newCollateralFactorMantissa) { - revert InvalidLiquidationThreshold(); - } - // If collateral factor != 0, fail if price == 0 if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(address(vToken)) == 0) { revert PriceError(address(vToken));
File: /contracts/VToken.sol 1026: comptroller.preLiquidateHook( 1027: address(this), 1028: address(vTokenCollateral), 1029: borrower, 1030: repayAmount, 1031: skipLiquidityCheck 1032: ); 1034: if (accrualBlockNumber != _getBlockNumber()) { 1035: revert LiquidateFreshnessCheck(); 1036: } 1040: if (vTokenCollateral.accrualBlockNumber() != _getBlockNumber()) { 1041: revert LiquidateCollateralFreshnessCheck(); 1042: } 1045: if (borrower == liquidator) { 1046: revert LiquidateLiquidatorIsBorrower(); 1047: } 1050: if (repayAmount == 0) { 1051: revert LiquidateCloseAmountIsZero(); 1052: } 1055: if (repayAmount == type(uint256).max) { 1056: revert LiquidateCloseAmountIsUintMax(); 1057: }
We have few checks for function parameters,consider having them come first as they are cheaper compared to making external calls.
diff --git a/contracts/VToken.sol b/contracts/VToken.sol index 8d9a2dc..81658ec 100644 --- a/contracts/VToken.sol +++ b/contracts/VToken.sol @@ -1022,6 +1022,20 @@ contract VToken is VTokenInterface vTokenCollateral, bool skipLiquidityCheck ) internal { + /* Fail if borrower = liquidator */ + if (borrower == liquidator) { + revert LiquidateLiquidatorIsBorrower(); + } + + /* Fail if repayAmount = 0 */ + if (repayAmount == 0) { + revert LiquidateCloseAmountIsZero(); + } + + /* Fail if repayAmount = -1 */ + if (repayAmount == type(uint256).max) { + revert LiquidateCloseAmountIsUintMax(); + } /* Fail if liquidate not allowed */ comptroller.preLiquidateHook( address(this), @@ -1041,20 +1055,6 @@ contract VToken is revert LiquidateCollateralFreshnessCheck(); } - /* Fail if borrower = liquidator */ - if (borrower == liquidator) { - revert LiquidateLiquidatorIsBorrower(); - } - - /* Fail if repayAmount = 0 */ - if (repayAmount == 0) { - revert LiquidateCloseAmountIsZero(); - } - - /* Fail if repayAmount = -1 */ - if (repayAmount == type(uint256).max) { - revert LiquidateCloseAmountIsUintMax(); - }
File: /contracts/VToken.sol 1097: function _seize( 1104: comptroller.preSeizeHook(address(this), seizerContract, liquidator, borrower); 1107: if (borrower == liquidator) { 1108: revert LiquidateSeizeLiquidatorIsBorrower(); 1109: }
As borrower and liquidator are function arguments they are cheaper to check first. In case we revert on this check then we'd not waste a lot of gas making an external call
diff --git a/contracts/VToken.sol b/contracts/VToken.sol index 8d9a2dc..67c58c4 100644 --- a/contracts/VToken.sol +++ b/contracts/VToken.sol @@ -1100,13 +1100,14 @@ contract VToken is address borrower, uint256 seizeTokens ) internal { - /* Fail if seize not allowed */ - comptroller.preSeizeHook(address(this), seizerContract, liquidator, borrower); - /* Fail if borrower = liquidator */ if (borrower == liquidator) { revert LiquidateSeizeLiquidatorIsBorrower(); } + /* Fail if seize not allowed */ + comptroller.preSeizeHook(address(this), seizerContract, liquidator, borrower);
File: /contracts/VToken.sol 1200: function _reduceReservesFresh(uint256 reduceAmount) internal { 1201: // totalReserves - reduceAmount 1202: uint256 totalReservesNew; 1205: if (accrualBlockNumber != _getBlockNumber()) { 1206: revert ReduceReservesFreshCheck(); 1207: } 1210: if (_getCashPrior() < reduceAmount) { 1211: revert ReduceReservesCashNotAvailable(); 1212: }
As it is, the first check reads from storage and then calls an internal function. The second check also has an internal function call but instead of reading from storage we read a function parameter which is cheaper compared to the storage read. The second check is cheaper than the first and we should reorder as follows.
diff --git a/contracts/VToken.sol b/contracts/VToken.sol index 8d9a2dc..e10d762 100644 --- a/contracts/VToken.sol +++ b/contracts/VToken.sol @@ -1198,19 +1198,19 @@ contract VToken is * @param reduceAmount Amount of reduction to reserves */ function _reduceReservesFresh(uint256 reduceAmount) internal { - // totalReserves - reduceAmount - uint256 totalReservesNew; - - // We fail gracefully unless market's block number equals current block number - if (accrualBlockNumber != _getBlockNumber()) { - revert ReduceReservesFreshCheck(); - } // Fail gracefully if protocol has insufficient underlying cash if (_getCashPrior() < reduceAmount) { revert ReduceReservesCashNotAvailable(); } + // We fail gracefully unless market's block number equals current block number + if (accrualBlockNumber != _getBlockNumber()) { + revert ReduceReservesFreshCheck(); + } + // totalReserves - reduceAmount + uint256 totalReservesNew; + // Check reduceAmount ≤ reserves[n] (totalReserves) if (reduceAmount > totalReserves) { revert ReduceReservesCashValidation();
File: /contracts/VToken.sol 1297: function _transferTokens( 1304: comptroller.preTransferHook(address(this), src, dst, tokens); 1307: if (src == dst) { 1308: revert TransferNotAllowed(); 1309: }
The check for function argument should come before the external call. As we can revert if the function params don't meet some conditions , we can refactor the code as follows to have external call come after checking the function parameters
@@ -1300,13 +1300,13 @@ contract VToken is address dst, uint256 tokens ) internal { - /* Fail if transfer not allowed */ - comptroller.preTransferHook(address(this), src, dst, tokens); - /* Do not allow self-transfers */ if (src == dst) { revert TransferNotAllowed(); } + + /* Fail if transfer not allowed */ + comptroller.preTransferHook(address(this), src, dst, tokens);
File: /contracts/RiskFund/RiskFund.sol 157: require(poolRegistry != address(0), "Risk fund: Invalid pool registry."); 158: require(markets.length == amountsOutMin.length, "Risk fund: markets and amountsOutMin are unequal lengths"); 159: require(markets.length == paths.length, "Risk fund: markets and paths are unequal lengths");
The first require statement reads a variable from storage which is expensive compared to the other two require statements. Move the first require statement below the first two as those read function arguments
@@ -154,9 +154,10 @@ contract RiskFund is address[][] calldata paths ) external override returns (uint256) { _checkAccessAllowed("swapPoolsAssets(address[],uint256[],address[][])"); - require(poolRegistry != address(0), "Risk fund: Invalid pool registry."); require(markets.length == amountsOutMin.length, "Risk fund: markets and amountsOutMin are unequal lengths"); require(markets.length == paths.length, "Risk fund: markets and paths are unequal lengths"); + require(poolRegistry != address(0), "Risk fund: Invalid pool registry."); +
If a reference type function parameter is read-only, it is cheaper in gas to use calldata instead of memory. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory.
name_,symbol_ and riskManagement
File: /contracts/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 {
Other instances: check audit tags for what to use calldata on https://github.com/code-423n4/2023-05-venus/blob/8be784ed9752b80e6f1b8b781e2e6251748d0d7e/contracts/Rewards/RewardsDistributor.sol#L163-L167
File: /contracts/Rewards/RewardsDistributor.sol //@audit: Use calldata for marketBorrowIndex 163: function distributeBorrowerRewardToken( 164: address vToken, 165: address borrower, 166: Exp memory marketBorrowIndex 167: ) external onlyComptroller {
File: /contracts/Rewards/RewardsDistributor.sol //@audit: Use caldata for vTokens,supplySpeeds,borrowSpeeds 197: function setRewardTokenSpeeds( 198: VToken[] memory vTokens, 199: uint256[] memory supplySpeeds, 200: uint256[] memory borrowSpeeds 201: ) external {
File: /contracts/Pool/PoolRegistry.sol //@audit: Use calldata on input 255: function addMarket(AddMarketInput memory input) external { //@audit: Use calldata on _metadata 343: function updatePoolMetadata(address comptroller, VenusPoolMetaData memory _metadata) external {
This saves deployement gas
File: /contracts/VToken.sol 395: if (msg.sender != address(comptroller)) { 396: revert HealBorrowUnauthorized(); 397: }
The above check is also done on Line 456
Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isn’t possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked block see resource
File: /contracts/VToken.sol 1223: totalReservesNew = totalReserves - reduceAmount;
The operation totalReserves - reduceAmount
cannot underflow due to the check on Line 1215that ensures that totalReserves
is greater than reduceAmount
@@ -1220,7 +1220,9 @@ contract VToken is // EFFECTS & INTERACTIONS // (No safe failures beyond this point) - totalReservesNew = totalReserves - reduceAmount; + unchecked { + totalReservesNew = totalReserves - reduceAmount; + }
File: /contracts/RiskFund/ProtocolShareReserve.sol 75: poolsAssetsReserves[comptroller][asset] -= amount;
The operation poolsAssetsReserves[comptroller][asset] - amount
cannot underflow due to the check on Line 72 which ensures that poolsAssetsReserves[comptroller][asset]
is greater than amount
File: /contracts/Comptroller.sol 754: // If collateral factor != 0, fail if price == 0 755: if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(address(vToken)) == 0) { 756: revert PriceError(address(vToken)); 757: }
diff --git a/contracts/Comptroller.sol b/contracts/Comptroller.sol index 41dc518..848a2ca 100644 --- a/contracts/Comptroller.sol +++ b/contracts/Comptroller.sol @@ -752,8 +752,10 @@ contract Comptroller is } // If collateral factor != 0, fail if price == 0 - if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(address(vToken)) == 0) { - revert PriceError(address(vToken)); + if (newCollateralFactorMantissa != 0){ + if (oracle.getUnderlyingPrice(address(vToken)) == 0) { + revert PriceError(address(vToken)); + } }
File: /contracts/VToken.sol 837: if (redeemTokens == 0 && redeemAmount > 0) { 838: revert("redeemTokens zero"); 839: }
diff --git a/contracts/VToken.sol b/contracts/VToken.sol index 8d9a2dc..a44d059 100644 --- a/contracts/VToken.sol +++ b/contracts/VToken.sol @@ -834,8 +834,10 @@ contract VToken is } // Require tokens is zero or amount is also zero - if (redeemTokens == 0 && redeemAmount > 0) { - revert("redeemTokens zero"); + if (redeemTokens == 0){ + if (redeemAmount > 0) { + revert("redeemTokens zero"); + } }
File: /contracts/Lens/PoolLens.sol 462: if (deltaBlocks > 0 && borrowSpeed > 0) { ... 470: } else if (deltaBlocks > 0) {
File: /contracts/Lens/PoolLens.sol 483: if (deltaBlocks > 0 && supplySpeed > 0) {
File: /contracts/Lens/PoolLens.sol 506: if (borrowerIndex.mantissa == 0 && borrowIndex.mantissa > 0) { 526: if (supplierIndex.mantissa == 0 && supplyIndex.mantissa > 0) {
File: /contracts/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) {
File: /contracts/VToken.sol 133: function approve(address spender, uint256 amount) external override returns (bool) { 134: require(spender != address(0), "invalid spender address"); 136: address src = msg.sender; 137: transferAllowances[src][spender] = amount; 138: emit Approval(src, spender, amount); 139: return true; 140: }
diff --git a/contracts/VToken.sol b/contracts/VToken.sol index 8d9a2dc..7b602bc 100644 --- a/contracts/VToken.sol +++ b/contracts/VToken.sol @@ -133,9 +133,8 @@ contract VToken is function approve(address spender, uint256 amount) external override returns (bool) { require(spender != address(0), "invalid spender address"); - address src = msg.sender; - transferAllowances[src][spender] = amount; - emit Approval(src, spender, amount); + transferAllowances[msg.sender][spender] = amount; + emit Approval(msg.sender, spender, amount); return true; }
File: /contracts/VToken.sol 625: function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { 628: address src = msg.sender; 629: uint256 newAllowance = transferAllowances[src][spender]; 630: newAllowance += addedValue; 631: transferAllowances[src][spender] = newAllowance; 633: emit Approval(src, spender, newAllowance); 634: return true; 635: }
diff --git a/contracts/VToken.sol b/contracts/VToken.sol index 8d9a2dc..c7894f1 100644 --- a/contracts/VToken.sol +++ b/contracts/VToken.sol @@ -625,12 +625,11 @@ contract VToken is function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { require(spender != address(0), "invalid spender address"); - address src = msg.sender; - uint256 newAllowance = transferAllowances[src][spender]; + uint256 newAllowance = transferAllowances[msg.sender][spender]; newAllowance += addedValue; - transferAllowances[src][spender] = newAllowance; + transferAllowances[msg.sender][spender] = newAllowance; - emit Approval(src, spender, newAllowance); + emit Approval(msg.sender, spender, newAllowance); return true; }
File: /contracts/VToken.sol 648: address src = msg.sender;
File: /contracts/Lens/PoolLens.sol 453: function updateMarketBorrowIndex( 460: uint256 blockNumber = block.number; 461: uint256 deltaBlocks = sub_(blockNumber, uint256(borrowState.block)); 462: if (deltaBlocks > 0 && borrowSpeed > 0) { 469: borrowState.block = safe32(blockNumber, "block number overflows"); 470: } else if (deltaBlocks > 0) { 471: borrowState.block = safe32(blockNumber, "block number overflows"); 472: }
@@ -457,8 +457,7 @@ contract PoolLens is ExponentialNoError { Exp memory marketBorrowIndex ) internal view { uint256 borrowSpeed = rewardsDistributor.rewardTokenBorrowSpeeds(vToken); - uint256 blockNumber = block.number; - uint256 deltaBlocks = sub_(blockNumber, uint256(borrowState.block)); + uint256 deltaBlocks = sub_(block.number, uint256(borrowState.block)); if (deltaBlocks > 0 && borrowSpeed > 0) { // Remove the total earned interest rate since the opening of the market from total borrows uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex); @@ -466,9 +465,9 @@ contract PoolLens is ExponentialNoError { Double memory ratio = borrowAmount > 0 ? fraction(tokensAccrued, borrowAmount) : Double({ mantissa: 0 }); Double memory index = add_(Double({ mantissa: borrowState.index }), ratio); borrowState.index = safe224(index.mantissa, "new index overflows"); - borrowState.block = safe32(blockNumber, "block number overflows"); + borrowState.block = safe32(block.number, "block number overflows"); } else if (deltaBlocks > 0) { - borrowState.block = safe32(blockNumber, "block number overflows"); + borrowState.block = safe32(block.number, "block number overflows"); } }
File: /contracts/Lens/PoolLens.sol 481: uint256 blockNumber = block.number;
IERC20Upgradeable(underlying)
File: /contracts/VToken.sol 1284: function _doTransferOut(address to, uint256 amount) internal virtual { 1285: IERC20Upgradeable token = IERC20Upgradeable(underlying); 1286: token.safeTransfer(to, amount); 1287: }
function _doTransferOut(address to, uint256 amount) internal virtual { - IERC20Upgradeable token = IERC20Upgradeable(underlying); - token.safeTransfer(to, amount); + IERC20Upgradeable(underlying).safeTransfer(to, amount); }
File: /contracts/VToken.sol 1421: function _getCashPrior() internal view virtual returns (uint256) { 1422: IERC20Upgradeable token = IERC20Upgradeable(underlying); 1423: return token.balanceOf(address(this)); 1424: }
diff --git a/contracts/VToken.sol b/contracts/VToken.sol index 8d9a2dc..5431de1 100644 --- a/contracts/VToken.sol +++ b/contracts/VToken.sol @@ -1419,8 +1419,7 @@ contract VToken is * @return The quantity of underlying tokens owned by this contract */ function _getCashPrior() internal view virtual returns (uint256) { - IERC20Upgradeable token = IERC20Upgradeable(underlying); - return token.balanceOf(address(this)); + return IERC20Upgradeable(underlying).balanceOf(address(this)); }
#0 - c4-judge
2023-05-18T17:55:15Z
0xean marked the issue as grade-b