Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save iFrostizz/bbbadb3d93229f60c94f01b6626c056d to your computer and use it in GitHub Desktop.
Save iFrostizz/bbbadb3d93229f60c94f01b6626c056d to your computer and use it in GitHub Desktop.

Report

Gas Optimizations

Issue Instances
GAS-1 Use assembly to check for address(0) 9
GAS-2 array[index] += amount is cheaper than array[index] = array[index] + amount (or related variants) 8
GAS-3 Using bools for storage incurs overhead 7
GAS-4 Cache array length outside of loop 4
GAS-5 State variables should be cached in stack variables rather than re-reading them from storage 20
GAS-6 Use calldata instead of memory for function arguments that do not get mutated 19
GAS-7 Use Custom Errors 49
GAS-8 Don't initialize variables with default value 14
GAS-9 Long revert strings 11
GAS-10 Functions guaranteed to revert when called by normal users can be marked payable 7
GAS-11 ++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too) 2
GAS-12 Using private rather than public for constants, saves gas 27
GAS-13 Use != 0 instead of > 0 for unsigned integer comparison 6
GAS-14 internal functions not called by the contract should be removed 20

[GAS-1] Use assembly to check for address(0)

Saves 6 gas per instance

Instances (9):

File: contracts/cash/CashManager.sol

161:     _grantRole(PAUSER_ADMIN, pauser);

165:     feeRecipient = _feeRecipient;

167:     assetSender = _assetSender;

170:     mintLimit = _mintLimit;

174:     currentEpochStartTimestamp =
File: contracts/cash/kyc/KYCRegistryClient.sol

57:     emit KYCRequirementGroupSet(oldKYCLevel, _kycRequirementGroup);
File: contracts/cash/token/CashKYCSender.sol

77: 
File: contracts/cash/token/CashKYCSenderReceiver.sol

85: 

85: 

[GAS-2] array[index] += amount is cheaper than array[index] = array[index] + amount (or related variants)

When updating a value in an array with arithmetic, using array[index] += amount is cheaper than array[index] = array[index] + amount. This is because you avoid an additonal mload when the array is stored in memory, and an sload when the array is stored in storage. This can be applied for any arithmetic operation including +=, -=,/=,*=,^=,&=, %=, <<=,>>=, and >>>=. This optimization can be particularly significant if the pattern occurs during a loop.

Saves 28 gas for a storage array, 38 for a memory array

Instances (8):

File: contracts/lending/tokens/cCash/CTokenCash.sol

536:     accountTokens[minter] = accountTokens[minter] + mintTokens;

642:     accountTokens[redeemer] = accountTokens[redeemer] - redeemTokens;

1041:     accountTokens[borrower] = accountTokens[borrower] - seizeTokens;

1042:     accountTokens[liquidator] =
File: contracts/lending/tokens/cToken/CTokenModified.sol

536:     accountTokens[minter] = accountTokens[minter] + mintTokens;

642:     accountTokens[redeemer] = accountTokens[redeemer] - redeemTokens;

1044:     accountTokens[borrower] = accountTokens[borrower] - seizeTokens;

1045:     accountTokens[liquidator] =

[GAS-3] Using bools for storage incurs overhead

Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas), and to avoid Gsset (20000 gas) when changing from ‘false’ to ‘true’, after having been ‘true’ in the past. See source.

Instances (7):

File: contracts/cash/kyc/KYCRegistry.sol

39:   mapping(uint256 => mapping(address => bool)) public kycState;
File: contracts/lending/tokens/cCash/CTokenInterfacesModifiedCash.sol

17:   bool internal _notEntered;

184:   bool public constant isCToken = true;
File: contracts/lending/tokens/cErc20ModifiedDelegator.sol

187:   bool internal _notEntered;

368:   bool public constant isCToken = true;
File: contracts/lending/tokens/cToken/CTokenInterfacesModified.sol

15:   bool internal _notEntered;

182:   bool public constant isCToken = true;

[GAS-4] Cache array length outside of loop

If not cached, the solidity compiler will always read the length of the array during each iteration. That is, if it is a storage array, this is an extra sload operation (100 additional extra gas for each iteration except for the first) and if it is a memory array, this is an extra mload operation (3 additional gas for each iteration except for the first).

Instances (4):

File: contracts/cash/CashManager.sol

961:     for (uint256 i = 0; i < exCallData.length; ++i) {
File: contracts/cash/factory/CashFactory.sol

127:     for (uint256 i = 0; i < exCallData.length; ++i) {
File: contracts/cash/factory/CashKYCSenderFactory.sol

137:     for (uint256 i = 0; i < exCallData.length; ++i) {
File: contracts/cash/factory/CashKYCSenderReceiverFactory.sol

137:     for (uint256 i = 0; i < exCallData.length; ++i) {

[GAS-5] State variables should be cached in stack variables rather than re-reading them from storage

The instances below point to the second+ access of a state variable within a function. Caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.

Saves 100 gas per instance

Instances (20):

File: contracts/cash/CashManager.sol

181:         (IERC20Metadata(_cash).decimals() -

183:   }

317:       emit MintExchangeRateSet(epochToSet, oldExchangeRate, exchangeRate);

738:    *                       minus those burned by users who are issued a

842:    *         amount of CASH a user has burned to kick off redemption process
File: contracts/cash/factory/CashFactory.sol

115:    * @dev All external calls made through this function will

117:    *

119:    *       1) target - contract to call

125:   ) external payable override onlyGuardian returns (bytes[] memory results) {

125:   ) external payable override onlyGuardian returns (bytes[] memory results) {
File: contracts/cash/factory/CashKYCSenderFactory.sol

117:       address(cashKYCSenderProxyAdmin),

124:    *

126:    *      msg.sender == contract address

133:   function multiexcall(

134:     ExCallData[] calldata exCallData
File: contracts/cash/factory/CashKYCSenderReceiverFactory.sol

116:       address(cashKYCSenderReceiverProxied),

118:       address(cashKYCSenderReceiverImplementation)

123:    * @notice Allows for arbitrary batched calls

131:    *       3) value - eth value to call target with

133:   function multiexcall(

[GAS-6] Use calldata instead of memory for function arguments that do not get mutated

Mark data types as calldata instead of memory where possible. This makes it so that the data is not automatically loaded into memory. If the data passed into the function does not need to be changed (like updating values in an array), it can be passed in as calldata. The one exception to this is if the argument must later be passed into another function that takes an argument that specifies memory storage.

Instances (19):

File: contracts/cash/Proxy.sol

27: 
File: contracts/cash/token/CashKYCSender.sol

70:       require(

71:         _getKYCStatus(from),
File: contracts/cash/token/CashKYCSenderReceiver.sol

70:       require(

71:         _getKYCStatus(from),
File: contracts/lending/tokens/cCash/CCash.sol

35:     string memory name_,

36:     string memory symbol_,
File: contracts/lending/tokens/cCash/CCashDelegate.sol

21:   function _becomeImplementation(bytes memory data) public virtual override {
File: contracts/lending/tokens/cCash/CTokenCash.sol

38:     string memory name_,

39:     string memory symbol_,
File: contracts/lending/tokens/cCash/CTokenInterfacesModifiedCash.sol

443:     bytes memory becomeImplementationData

453:   function _becomeImplementation(bytes memory data) external virtual;
File: contracts/lending/tokens/cToken/CErc20.sol

35:     string memory name_,

36:     string memory symbol_,
File: contracts/lending/tokens/cToken/CTokenDelegate.sol

21:   function _becomeImplementation(bytes memory data) public virtual override {
File: contracts/lending/tokens/cToken/CTokenInterfacesModified.sol

441:     bytes memory becomeImplementationData

451:   function _becomeImplementation(bytes memory data) external virtual;
File: contracts/lending/tokens/cToken/CTokenModified.sol

38:     string memory name_,

39:     string memory symbol_,

[GAS-7] Use Custom Errors

Source Instead of using error strings, to reduce deployment and runtime cost, you should use Custom Errors. This would save both deployment and runtime cost.

Instances (49):

File: contracts/cash/CashManager.sol

965:       require(success, "Call Failed");
File: contracts/cash/factory/CashFactory.sol

131:       require(success, "Call Failed");

152:     require(msg.sender == guardian, "CashFactory: You are not the Guardian");
File: contracts/cash/factory/CashKYCSenderFactory.sol

141:       require(success, "Call Failed");
File: contracts/cash/factory/CashKYCSenderReceiverFactory.sol

141:       require(success, "Call Failed");
File: contracts/cash/kyc/KYCRegistry.sol

87:     require(v == 27 || v == 28, "KYCRegistry: invalid v value in signature");

92:     require(block.timestamp <= deadline, "KYCRegistry: signature expired");
File: contracts/lending/JumpRateModelV2.sol

89:     require(msg.sender == owner, "only the owner may call this function.");
File: contracts/lending/OndoPriceOracleV2.sol

110:       revert("Oracle type not supported");

297:     require(answer >= 0, "Price cannot be negative");
File: contracts/lending/tokens/cCash/CCash.sol

220:     require(success, "TOKEN_TRANSFER_IN_FAILED");

260:     require(success, "TOKEN_TRANSFER_OUT_FAILED");
File: contracts/lending/tokens/cCash/CTokenCash.sol

44:     require(msg.sender == admin, "only admin may initialize the market");

59:     require(err == NO_ERROR, "setting comptroller failed");

67:     require(err == NO_ERROR, "setting interest rate model failed");

97:     require(_getKYCStatus(spender), "Spender not KYC'd");

98:     require(_getKYCStatus(src), "Source not KYC'd");

99:     require(_getKYCStatus(dst), "Destination not KYC'd");

493:     require(_getKYCStatus(minter), "Minter not KYC'd");

582:     require(_getKYCStatus(redeemer), "Redeemer not KYC'd");

681:     require(_getKYCStatus(borrower), "Borrower not KYC'd");

773:     require(_getKYCStatus(payer), "Payer not KYC'd");

774:     require(_getKYCStatus(borrower), "Borrower not KYC'd");

997:     require(_getKYCStatus(liquidator), "Liquidator not KYC'd");

998:     require(_getKYCStatus(borrower), "Borrower not KYC'd");

1122:     require(newComptroller.isComptroller(), "marker method returned false");

1357:     require(msg.sender == admin, "Only admin can set KYC requirement group");

1379:     require(msg.sender == admin, "Only admin can set KYC registry");

1389:     require(_kycRegistry != address(0), "KYC registry cannot be zero address");

1435:     require(_notEntered, "re-entered");
File: contracts/lending/tokens/cToken/CErc20.sol

220:     require(success, "TOKEN_TRANSFER_IN_FAILED");

260:     require(success, "TOKEN_TRANSFER_OUT_FAILED");
File: contracts/lending/tokens/cToken/CTokenModified.sol

44:     require(msg.sender == admin, "only admin may initialize the market");

59:     require(err == NO_ERROR, "setting comptroller failed");

67:     require(err == NO_ERROR, "setting interest rate model failed");

97:     require(!sanctionsList.isSanctioned(spender), "Spender is sanctioned");

98:     require(!sanctionsList.isSanctioned(src), "Source is sanctioned");

99:     require(!sanctionsList.isSanctioned(dst), "Destination is sanctioned");

493:     require(!sanctionsList.isSanctioned(minter), "Minter is sanctioned");

582:     require(!sanctionsList.isSanctioned(redeemer), "Redeemer is sanctioned");

681:     require(_getKYCStatus(borrower), "Borrower not KYC'd");

773:     require(_getKYCStatus(payer), "Payer not KYC'd");

774:     require(_getKYCStatus(borrower), "Borrower not KYC'd");

1001:     require(!sanctionsList.isSanctioned(borrower), "Borrower is sanctioned");

1125:     require(newComptroller.isComptroller(), "marker method returned false");

1360:     require(msg.sender == admin, "Only admin can set KYC requirement group");

1382:     require(msg.sender == admin, "Only admin can set KYC registry");

1392:     require(_kycRegistry != address(0), "KYC registry cannot be zero address");

1438:     require(_notEntered, "re-entered");

[GAS-8] Don't initialize variables with default value

Instances (14):

File: contracts/cash/CashManager.sol

64:   uint256 public mintFee = 0;

581:       currentMintAmount = 0;

750:     for (uint256 i = 0; i < size; ++i) {

786:     for (uint256 i = 0; i < size; ++i) {

933:     for (uint256 i = 0; i < size; ++i) {

961:     for (uint256 i = 0; i < exCallData.length; ++i) {
File: contracts/cash/factory/CashFactory.sol

127:     for (uint256 i = 0; i < exCallData.length; ++i) {
File: contracts/cash/factory/CashKYCSenderFactory.sol

137:     for (uint256 i = 0; i < exCallData.length; ++i) {
File: contracts/cash/factory/CashKYCSenderReceiverFactory.sol

137:     for (uint256 i = 0; i < exCallData.length; ++i) {
File: contracts/cash/kyc/KYCRegistry.sol

163:     for (uint256 i = 0; i < length; i++) {

180:     for (uint256 i = 0; i < length; i++) {
File: contracts/lending/tokens/cCash/CTokenCash.sol

113:     uint startingAllowance = 0;
File: contracts/lending/tokens/cCash/CTokenInterfacesModifiedCash.sol

115:   uint public constant protocolSeizeShareMantissa = 0; //0%
File: contracts/lending/tokens/cToken/CTokenModified.sol

113:     uint startingAllowance = 0;

[GAS-9] Long revert strings

Instances (11):

File: contracts/cash/factory/CashFactory.sol

152:     require(msg.sender == guardian, "CashFactory: You are not the Guardian");
File: contracts/cash/kyc/KYCRegistry.sol

87:     require(v == 27 || v == 28, "KYCRegistry: invalid v value in signature");
File: contracts/lending/JumpRateModelV2.sol

89:     require(msg.sender == owner, "only the owner may call this function.");
File: contracts/lending/tokens/cCash/CTokenCash.sol

44:     require(msg.sender == admin, "only admin may initialize the market");

67:     require(err == NO_ERROR, "setting interest rate model failed");

1357:     require(msg.sender == admin, "Only admin can set KYC requirement group");

1389:     require(_kycRegistry != address(0), "KYC registry cannot be zero address");
File: contracts/lending/tokens/cToken/CTokenModified.sol

44:     require(msg.sender == admin, "only admin may initialize the market");

67:     require(err == NO_ERROR, "setting interest rate model failed");

1360:     require(msg.sender == admin, "Only admin can set KYC requirement group");

1392:     require(_kycRegistry != address(0), "KYC registry cannot be zero address");

[GAS-10] Functions guaranteed to revert when called by normal users can be marked payable

If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided.

Instances (7):

File: contracts/cash/CashManager.sol

526:   function pause() external onlyRole(PAUSER_ADMIN) {

533:   function unpause() external onlyRole(MANAGER_ADMIN) {

596:   function setMintLimit(uint256 _mintLimit) external onlyRole(MANAGER_ADMIN) {
File: contracts/lending/OndoPriceOracle.sol

80:   function setPrice(address fToken, uint256 price) external override onlyOwner {

106:   function setOracle(address newOracle) external override onlyOwner {
File: contracts/lending/OndoPriceOracleV2.sol

163:   function setPrice(address fToken, uint256 price) external override onlyOwner {

182:   function setOracle(address newOracle) external override onlyOwner {

[GAS-11] ++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too)

Saves 5 gas per loop

Instances (2):

File: contracts/cash/kyc/KYCRegistry.sol

163:     for (uint256 i = 0; i < length; i++) {

180:     for (uint256 i = 0; i < length; i++) {

[GAS-12] Using private rather than public for constants, saves gas

If needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table

Instances (27):

File: contracts/cash/CashManager.sol

89:   uint256 public constant BPS_DENOMINATOR = 10_000;

122:   bytes32 public constant MANAGER_ADMIN = keccak256("MANAGER_ADMIN");

123:   bytes32 public constant PAUSER_ADMIN = keccak256("PAUSER_ADMIN");

124:   bytes32 public constant SETTER_ADMIN = keccak256("SETTER_ADMIN");
File: contracts/cash/factory/CashFactory.sol

44:   bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

45:   bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

46:   bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);
File: contracts/cash/factory/CashKYCSenderFactory.sol

44:   bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

45:   bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

46:   bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);
File: contracts/cash/factory/CashKYCSenderReceiverFactory.sol

44:   bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

45:   bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

46:   bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);
File: contracts/cash/kyc/KYCRegistry.sol

31:   bytes32 public constant _APPROVAL_TYPEHASH =

36:   bytes32 public constant REGISTRY_ADMIN = keccak256("REGISTRY_ADMIN");
File: contracts/cash/token/Cash.sol

22:   bytes32 public constant TRANSFER_ROLE = keccak256("TRANSFER_ROLE");
File: contracts/cash/token/CashKYCSender.sol

26:   bytes32 public constant KYC_CONFIGURER_ROLE =
File: contracts/cash/token/CashKYCSenderReceiver.sol

26:   bytes32 public constant KYC_CONFIGURER_ROLE =
File: contracts/lending/JumpRateModelV2.sol

29:   uint public constant blocksPerYear = 2628000;
File: contracts/lending/tokens/cCash/CTokenInterfacesModifiedCash.sol

115:   uint public constant protocolSeizeShareMantissa = 0; //0%

166:   ISanctionsList public constant sanctionsList =

184:   bool public constant isCToken = true;
File: contracts/lending/tokens/cErc20ModifiedDelegator.sol

350:   ISanctionsList public constant sanctionsList =

368:   bool public constant isCToken = true;
File: contracts/lending/tokens/cToken/CTokenInterfacesModified.sol

113:   uint public constant protocolSeizeShareMantissa = 1.75e16; //1.75%

164:   ISanctionsList public constant sanctionsList =

182:   bool public constant isCToken = true;

[GAS-13] Use != 0 instead of > 0 for unsigned integer comparison

Instances (6):

File: contracts/cash/CashManager.sol

579:     if (epochDifference > 0) {
File: contracts/lending/OndoPriceOracleV2.sol

114:     if (fTokenToUnderlyingPriceCap[fToken] > 0) {
File: contracts/lending/tokens/cCash/CTokenCash.sol

53:       initialExchangeRateMantissa > 0,

595:     if (redeemTokensIn > 0) {
File: contracts/lending/tokens/cToken/CTokenModified.sol

53:       initialExchangeRateMantissa > 0,

595:     if (redeemTokensIn > 0) {

[GAS-14] internal functions not called by the contract should be removed

If the functions are required by an interface, the contract should inherit from that interface and use the override keyword

Instances (20):

File: contracts/lending/tokens/cCash/CTokenCash.sol

479:   function mintInternal(uint mintAmount) internal nonReentrant {

552:   function redeemInternal(uint redeemTokens) internal nonReentrant {

563:   function redeemUnderlyingInternal(uint redeemAmount) internal nonReentrant {

669:   function borrowInternal(uint borrowAmount) internal nonReentrant {

740:   function repayBorrowInternal(uint repayAmount) internal nonReentrant {

751:   function repayBorrowBehalfInternal(

845:   function liquidateBorrowInternal(

1182:   function _addReservesInternal(
File: contracts/lending/tokens/cToken/CTokenModified.sol

479:   function mintInternal(uint mintAmount) internal nonReentrant {

552:   function redeemInternal(uint redeemTokens) internal nonReentrant {

563:   function redeemUnderlyingInternal(uint redeemAmount) internal nonReentrant {

669:   function borrowInternal(uint borrowAmount) internal nonReentrant {

740:   function repayBorrowInternal(uint repayAmount) internal nonReentrant {

751:   function repayBorrowBehalfInternal(

845:   function liquidateBorrowInternal(

1185:   function _addReservesInternal(

Non Critical Issues

Issue Instances
NC-1 Missing checks for address(0) when assigning values to address state variables 6
NC-2 Return values of approve() not checked 1
NC-3 Event is missing indexed fields 74
NC-4 Constants should be defined rather than using magic numbers 1
NC-5 Functions not used internally could be marked external 8

[NC-1] Missing checks for address(0) when assigning values to address state variables

Instances (6):

File: contracts/cash/CashManager.sol

477:    * @param collateralAmountIn Amount of `collateral` to exchange

485:    *      bringing us down to 18 decimals of precision

826:    * @notice Custom view function to return the quantity burned by
File: contracts/cash/factory/CashFactory.sol

70:    *            address specified in constructor.
File: contracts/cash/factory/CashKYCSenderFactory.sol

70:    *            address specified in constructor.
File: contracts/cash/factory/CashKYCSenderReceiverFactory.sol

70:    *            address specified in constructor.

[NC-2] Return values of approve() not checked

Not all IERC20 implementations revert() when there's a failure in approve(). The function signature has a boolean return value and they indicate errors that way instead. By not checking the return value, operations that should have marked as failed, may potentially go through without actually approving anything

Instances (1):

File: contracts/lending/tokens/cErc20ModifiedDelegator.sol

902:       abi.encodeWithSignature("approve(address,uint256)", spender, amount)

[NC-3] Event is missing indexed fields

Index event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields). Each event should use three indexed fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed.

Instances (74):

File: contracts/cash/factory/CashFactory.sol

156: 
File: contracts/cash/factory/CashKYCSenderFactory.sol

170: 
File: contracts/cash/factory/CashKYCSenderReceiverFactory.sol

170: 
File: contracts/cash/interfaces/ICashManager.sol

95:    * @notice Event emitted when minimum deposit amount is set

102:   event MinimumDepositAmountSet(uint256 oldMinimum, uint256 newMinimum);

111:   event MinimumRedeemAmountSet(uint256 oldRedeemMin, uint256 newRedeemMin);

124:    * @notice Event emitted when exchange rate is set for

133:   event MintExchangeRateSet(

143:    * @param epoch                   Epoch in which the mint exchange rate was

151:   event MintExchangeRateOverridden(

173:    * @dev Both rates are represented in 6 decimals.

186:    *

199:   event RedeemLimitSet(uint256 oldLimit, uint256 newLimit);

212:    * @notice Event emitted when redemption request is submitted

218:   event RedemptionRequested(

228:    * @param collateralAmountDeposited The total amount deposited

237:     uint256 depositAmountAfterFee,

248:    *                                 requested

265:    * @param epochClaimedFrom          The epoch in which the user requested

286:     uint256 collateralAmountOut,

306:    * @param user       The address of the user having their mint balance set

319:    * @notice Event emitted when a user redemption balance is set manually

330:     uint256 balance,

352:   error MintFeeTooLarge();
File: contracts/cash/interfaces/IKYCRegistryClient.sol

61: 

61: 
File: contracts/cash/kyc/KYCRegistry.sol

224:     address indexed sender,

244: 
File: contracts/lending/IOndoPriceOracle.sol

66:   event CTokenOracleSet(address oldOracle, address newOracle);

68: 

68: 
File: contracts/lending/IOndoPriceOracleV2.sol

66:   event CTokenOracleSet(address oldOracle, address newOracle);

80:   function setFTokenToChainlinkOracle(

92:   ) external;

126:   event MaxChainlinkOracleTimeDelaySet(

137:   event FTokenToOracleTypeSet(address indexed fToken, OracleType oracleType);

139: 

139: 
File: contracts/lending/tokens/cCash/CTokenInterfacesModifiedCash.sol

150:   event KYCRegistrySet(address oldRegistry, address newRegistry);

158:   event KYCRequirementGroupSet(

191:   event AccrueInterest(

201:   event Mint(address minter, uint mintAmount, uint mintTokens);

206:   event Redeem(address redeemer, uint redeemAmount, uint redeemTokens);

211:   event Borrow(

221:   event RepayBorrow(

232:   event LiquidateBorrow(

245:   event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);

250:   event NewAdmin(address oldAdmin, address newAdmin);

255:   event NewComptroller(

263:   event NewMarketInterestRateModel(

271:   event NewReserveFactor(

279:   event ReservesAdded(

288:   event ReservesReduced(

297:   event Transfer(address indexed from, address indexed to, uint amount);

302:   event Approval(address indexed owner, address indexed spender, uint amount);

432:   event NewImplementation(address oldImplementation, address newImplementation);
File: contracts/lending/tokens/cToken/CTokenInterfacesModified.sol

148:   event KYCRegistrySet(address oldRegistry, address newRegistry);

156:   event KYCRequirementGroupSet(

189:   event AccrueInterest(

199:   event Mint(address minter, uint mintAmount, uint mintTokens);

204:   event Redeem(address redeemer, uint redeemAmount, uint redeemTokens);

209:   event Borrow(

219:   event RepayBorrow(

230:   event LiquidateBorrow(

243:   event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);

248:   event NewAdmin(address oldAdmin, address newAdmin);

253:   event NewComptroller(

261:   event NewMarketInterestRateModel(

269:   event NewReserveFactor(

277:   event ReservesAdded(

286:   event ReservesReduced(

295:   event Transfer(address indexed from, address indexed to, uint amount);

300:   event Approval(address indexed owner, address indexed spender, uint amount);

430:   event NewImplementation(address oldImplementation, address newImplementation);

[NC-4] Constants should be defined rather than using magic numbers

Instances (1):

File: contracts/lending/OndoPriceOracleV2.sol

261:       (36 -

[NC-5] Functions not used internally could be marked external

Instances (8):

File: contracts/cash/token/CashKYCSender.sol

69:       // Only check KYC if not minting
File: contracts/cash/token/CashKYCSenderReceiver.sol

69:       // Only check KYC if not minting
File: contracts/lending/tokens/cCash/CCash.sol

30:   function initialize(
File: contracts/lending/tokens/cCash/CTokenCash.sol

34:   function initialize(

1300:   function _setInterestRateModel(
File: contracts/lending/tokens/cToken/CErc20.sol

30:   function initialize(
File: contracts/lending/tokens/cToken/CTokenModified.sol

34:   function initialize(

1303:   function _setInterestRateModel(

Low Issues

Issue Instances
L-1 Unsafe ERC20 operation(s) 6

[L-1] Unsafe ERC20 operation(s)

Instances (6):

File: contracts/lending/tokens/cCash/CCash.sol

160:     token.transfer(admin, balance);

201:     token.transferFrom(from, address(this), amount);

241:     token.transfer(to, amount);
File: contracts/lending/tokens/cToken/CErc20.sol

160:     token.transfer(admin, balance);

201:     token.transferFrom(from, address(this), amount);

241:     token.transfer(to, amount);

Medium Issues

Issue Instances
M-1 Centralization Risk for trusted owners 41

[M-1] Centralization Risk for trusted owners

Impact:

Contracts have owners with privileged rights to perform admin tasks and need to be trusted to not perform malicious updates or drain funds.

Instances (41):

File: contracts/cash/CashManager.sol

31:   AccessControlEnumerable,

284:   ) external override updateEpoch onlyRole(SETTER_ADMIN) {

341:   ) external updateEpoch onlyRole(MANAGER_ADMIN) {

370:   ) external override updateEpoch onlyRole(MANAGER_ADMIN) {

394:   ) external override onlyRole(MANAGER_ADMIN) {

412:   ) external override onlyRole(MANAGER_ADMIN) {

435:   ) external override onlyRole(MANAGER_ADMIN) {

454:   ) external override onlyRole(MANAGER_ADMIN) {

467:   ) external override onlyRole(MANAGER_ADMIN) {

526:   function pause() external onlyRole(PAUSER_ADMIN) {

533:   function unpause() external onlyRole(MANAGER_ADMIN) {

548:   ) external onlyRole(MANAGER_ADMIN) {

596:   function setMintLimit(uint256 _mintLimit) external onlyRole(MANAGER_ADMIN) {

611:   ) external onlyRole(MANAGER_ADMIN) {

713:   ) external override updateEpoch onlyRole(MANAGER_ADMIN) {

805:   ) external onlyRole(MANAGER_ADMIN) {

819:   ) external onlyRole(MANAGER_ADMIN) {

855:   ) external updateEpoch onlyRole(MANAGER_ADMIN) {

898:   ) external override onlyRole(MANAGER_ADMIN) {

909:   ) external override onlyRole(MANAGER_ADMIN) {

956:     onlyRole(MANAGER_ADMIN)
File: contracts/cash/kyc/KYCRegistry.sol

30: contract KYCRegistry is AccessControlEnumerable, IKYCRegistry, EIP712 {

147:   ) external onlyRole(REGISTRY_ADMIN) {

161:   ) external onlyRole(kycGroupRoles[kycRequirementGroup]) {

178:   ) external onlyRole(kycGroupRoles[kycRequirementGroup]) {
File: contracts/cash/token/CashKYCSender.sol

36:   ) external override onlyRole(KYC_CONFIGURER_ROLE) {

42:   ) external override onlyRole(KYC_CONFIGURER_ROLE) {
File: contracts/cash/token/CashKYCSenderReceiver.sol

36:   ) external override onlyRole(KYC_CONFIGURER_ROLE) {

42:   ) external override onlyRole(KYC_CONFIGURER_ROLE) {
File: contracts/lending/OndoPriceOracle.sol

39: contract OndoPriceOracle is IOndoPriceOracle, Ownable {

80:   function setPrice(address fToken, uint256 price) external override onlyOwner {

95:   ) external override onlyOwner {

106:   function setOracle(address newOracle) external override onlyOwner {
File: contracts/lending/OndoPriceOracleV2.sol

49: contract OndoPriceOracleV2 is IOndoPriceOracleV2, Ownable {

133:   ) external override onlyOwner {

148:   ) external override onlyOwner {

163:   function setPrice(address fToken, uint256 price) external override onlyOwner {

182:   function setOracle(address newOracle) external override onlyOwner {

197:   ) external override onlyOwner {

236:   ) external override onlyOwner {

311:   ) external override onlyOwner {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment