Platform: Code4rena
Start Date: 25/10/2022
Pot Size: $50,000 USDC
Total HM: 18
Participants: 127
Period: 5 days
Judge: 0xean
Total Solo HM: 9
Id: 175
League: ETH
Rank: 14/127
Findings: 2
Award: $691.21
๐ Selected for report: 0
๐ Solo Findings: 0
๐ Selected for report: 0x1f8b
Also found by: 0xNazgul, 0xSmartContract, Aymen0909, B2, Bnke0x0, Deivitto, Diana, Dinesh11G, ElKu, JC, Josiah, Rahoz, RaymondFam, ReyAdmirado, Rolezn, Waze, __141345__, adriro, aphak5010, brgltd, c3phas, c7e7eff, carlitox477, cducrest, ch0bu, chrisdior4, cryptonue, cryptostellar5, cylzxje, d3e4, delfin454000, enckrish, evmwanderer, fatherOfBlocks, gogo, hansfriese, horsefacts, immeas, leosathya, lukris02, neumo, oyc_109, pedr02b2, rbserver, robee, rotcivegaf, rvierdiiev, sakshamguruji, shark, simon135, tnevler, trustindistrust, wagmi
398.8207 USDC - $398.82
Number | Issues Details | Context |
---|---|---|
[L-01] | Use safeSetgov instead of setGov function | 1 |
[L-02] | In Market.sol, gov address value, critical 0 address control is missing | 1 |
[L-03] | Very critical gov privileges can cause complete destruction of the project in a possible privateKey exploit | 1 |
[L-04] | Signature Malleability of EVM'sย ecrecover() | 1 |
[L-05] | Add updatable dola address function | 1 |
Total 5 issues
Number | Issues Details | Context |
---|---|---|
[NC-01 ] | Insufficient coverage | |
[NC-02] | 0 address check | 10 |
[NC-03] | Function writing that does not comply with the Solidity Style Guide | All contracts |
[NC-04] | Compliance with Solidity Style rules in Constant expressions | 1 |
[NC-05] | Omissions in Events | 2 |
[NC-06] | Need Fuzzing test | 7 |
[NC-07] | Use a more recent version of Solidity | All contracts |
[NC-08] | Solidity compiler optimizations can be problematic | All contracts |
[NC-09] | Avoid whitespaces while declaring mapping variables | 4 |
[NC-10] | Require revert cause should be known | 1 |
[NC-11] | NatSpec is missing | 38 |
[NC-12] | Floating Pragma | All contracts |
[NC-13] | For functions, follow Solidity standard naming conventions | 9 |
[NC-14] | Missing Event for Critical Parameters Change | 16 |
[NC-15] | Open TODOS | 1 |
[NC-16] | Add NatSpec Mapping comment | 6 |
Total 16 issues
Number | Suggestion Details |
---|---|
[S-01] | Add to blacklist function |
[S-02] | Generate perfect code headers every time |
[S-03] | Add NatSpec comments to the variables defined in Storage |
Total 3 suggestions
safeSetgov
instead of setGov
functionContext:
src/Market.sol: 129 */ 130: function setGov(address _gov) public onlyGov { gov = _gov; } 131: 132 /**
Description:
Use a 2 structure setGov
which is safer.
safeSetgov
, use it is more secure due to 2-stage ownership transfer.
Recommendation:
Use Ownable2Step.sol
Ownable2Step.sol
/** * @dev The new owner accepts the gov transfer. */ function acceptGov() external { address sender = _msgSender(); require(pendingGov() == sender, "Ownable2Step: caller is not the new gov"); _transferGov(sender); } }
gov
address value, critical 0 address control is missinghttps://github.com/code-423n4/2022-10-inverse/blob/main/src/Market.sol#L77
With the constructor in the market.sol
file, the initial address of gov
is set with an argument of type address, but there is no check that prevents this address from starting with 0.
There is a risk that the gov
variable is accidentally initialized to 0
Passing the address variable blank causes it to be set as 0 address.
The address initialized with 0 by mistake or forgetting cant be changed.
The value of gov
is the value of the onlyGov
modifier and should be msg.sender = gov, any function with this modifier will not work as this is not possible at 0 addresses
The biggest risk is; Until this value is found to be incorrect, the project can be used, users can trade, use the platform
src/Market.sol: 60 61: constructor ( 62: address _gov, 63: address _lender, 64: address _pauseGuardian, 65: address _escrowImplementation, 66: IDolaBorrowingRights _dbr, 67: IERC20 _collateral, 68: IOracle _oracle, 69: uint _collateralFactorBps, 70: uint _replenishmentIncentiveBps, 71: uint _liquidationIncentiveBps, 72: bool _callOnDepositCallback 73: ) { 74: require(_collateralFactorBps < 10000, "Invalid collateral factor"); 75: require(_liquidationIncentiveBps > 0 && _liquidationIncentiveBps < 10000, "Invalid liquidation incentive"); 76: require(_replenishmentIncentiveBps < 10000, "Replenishment incentive must be less than 100%"); 77: gov = _gov; 78: lender = _lender; 79: pauseGuardian = _pauseGuardian; 80: escrowImplementation = _escrowImplementation; 81: dbr = _dbr; 82: collateral = _collateral; 83: oracle = _oracle; 84: collateralFactorBps = _collateralFactorBps; 85: replenishmentIncentiveBps = _replenishmentIncentiveBps; 86: liquidationIncentiveBps = _liquidationIncentiveBps; 87: callOnDepositCallback = _callOnDepositCallback; 88: INITIAL_CHAIN_ID = block.chainid; 89: INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); 90: }
Similar issue is also present in below function;
src/Market.sol: 129 */ 130: function setGov(address _gov) public onlyGov { gov = _gov; } 131: 132 /**
1- Platform is accidentally initialized with 0 gov
2- Users start using the platform, invest capital
3- It is impossible to update the gov
value and the project has been started, there will be a serious reputation problem and users may lose money
Manual Code Review
Add an if block to the constructor ;
error ZeroAddressError(); if(_ gov = address(0)) ZeroAddressError(); }
or add a constructor ;
_gov = msg.sender;
gov
privileges can cause complete destruction of the project in a possible privateKey exploithttps://github.com/code-423n4/2022-10-inverse/blob/main/src/Market.sol#L92-L95
The contractโs gov
is most impartant power role in this project. the gov is able to perform certain privileged activities.
However, gov
privileges are numerous and there is no timelock structure in the process of using these privileges.
The gov
is assumed to be an EOA, since the documents do not provide information on whether the gov
will be a multisign structure.
In parallel with the private key thefts of the project owners, which have increased recently, this vulnerability has been stated as medium.
Similar vulnerability; Private keys stolen:
Hackers have stolen cryptocurrency worth around โฌ552 million from a blockchain project linked to the popular online game Axie Infinity, in one of the largest cryptocurrency heists on record. Security issue : PrivateKey of the project officer was stolen: https://www.euronews.com/next/2022/03/30/blockchain-network-ronin-hit-by-552-million-crypto-heist
This vulnerability can also be considered as a Rugpull risk.
gov
powers;
10 results - 1 file src/Market.sol: 117 //sets the oracle to a new oracle. Only callable by governance. 118: function setOracle(IOracle _oracle) public onlyGov { oracle = _oracle; } 123 //sets the borrow controller to a new borrow controller. Only callable by governance. 124: function setBorrowController(IBorrowController _borrowController) public onlyGov 129 //sets the address of governance. Only callable by governance. 130: function setGov(address _gov) public onlyGov { gov = _gov; } 135 //sets the lender to a new lender. The lender is allowed to recall dola from the contract. 136: function setLender(address _lender) public onlyGov { lender = _lender; } 141 //sets the pause guardian. The pause guardian can pause borrowing. Only callable by governance 142: function setPauseGuardian(address _pauseGuardian) public onlyGov 148 //sets the Collateral Factor requirement of the market as measured in basis points 149: function setCollateralFactorBps(uint _collateralFactorBps) public onlyGov { 160 //sets the Liquidation Factor of the market as denoted in basis points 161: function setLiquidationFactorBps(uint _liquidationFactorBps) public onlyGov { 171 //sets the Replenishment Incentive of the market as denoted in basis points 172: function setReplenismentIncentiveBps(uint _replenishmentIncentiveBps) public onlyGov { 182 //sets the Liquidation Incentive of the market as denoted in basis points 183: function setLiquidationIncentiveBps(uint _liquidationIncentiveBps) public onlyGov { 193 //sets the Liquidation Fee of the market as denoted in basis points. 194: function setLiquidationFeeBps(uint _liquidationFeeBps) public onlyGov {
1- A timelock contract should be added to use gov
privileges. In this way, users can be warned in case of a possible security weakness.
2- gov
can be a Multisig wallet and this part is specified in the documentation
Context:
4 results - 3 files src/DBR.sol: 225 unchecked { 226: address recoveredAddress = ecrecover( 227 keccak256( src/Market.sol: 424 unchecked { 425: address recoveredAddress = ecrecover( 426 keccak256( 488 unchecked { 489: address recoveredAddress = ecrecover( 490 keccak256(
Description: Description: The function calls the Solidity ecrecover() function directly to verify the given signatures. However, the ecrecover() EVM opcode allows malleable (non-unique) signatures and thus is susceptible to replay attacks.
Although a replay attack seems not possible for this contract, I recommend using the battle-tested OpenZeppelin's ECDSA library.
Recommendation:* Use the ecrecover function from OpenZeppelin's ECDSA library for signature verification. (Ensure using a version > 4.7.3 for there was a critical bug >= 4.1.0 < 4.7.3)
dola
address functionDescription:
The dola
address may change in the future, so add a function that ensures that this address can be changed safely.
src/Market.sol: 36: contract Market { 44: IERC20 public immutable dola = IERC20(0x865377367054516e17014CcdED1e7d814EDC9ce4);
Description: The test coverage rate of the project is 89%. Testing all functions is best practice in terms of security criteria.
+-----------------------------------+------------------+------------------+------------------+-----------------+ | File | % Lines | % Statements | % Branches | % Funcs | +==============================================================================================================+ | src/DBR.sol | 98.77% (80/81) | 98.92% (92/93) | 94.74% (36/38) | 96.00% (24/25) | |-----------------------------------+------------------+------------------+------------------+-----------------| | src/Fed.sol | 93.94% (31/33) | 94.44% (34/36) | 84.62% (22/26) | 87.50% (7/8) | |-----------------------------------+------------------+------------------+------------------+-----------------| | src/Market.sol | 96.45% (136/141) | 95.53% (171/179) | 85.00% (68/80) | 92.11% (35/38) | |-----------------------------------+------------------+------------------+------------------+-----------------| | src/escrows/GovTokenEscrow.sol | 0.00% (0/10) | 0.00% (0/10) | 0.00% (0/6) | 0.00% (0/4) | |-----------------------------------+------------------+------------------+------------------+-----------------| | src/escrows/INVEscrow.sol | 0.00% (0/20) | 0.00% (0/25) | 0.00% (0/10) | 0.00% (0/5) | |-----------------------------------+------------------+------------------+------------------+-----------------|
Due to its capacity, test coverage is expected to be 100%
0 address
checkContext: BorrowController.sol#L14 BorrowController.sol#L26 BorrowController.sol#L32 BorrowController.sol#L38 DBR.sol#L36 DBR.sol#L36 DBR.sol#L36 DBR.sol#L36 Fed.sol#L39 Market.sol#L77-L80
Description: Also check of the address to protect the code from 0x0 address problem just in case. This is best practice or instead of suggesting that they verify address != 0x0, you could add some good NatSpec comments explaining what is valid and what is invalid and what are the implications of accidentally using an invalid address.
Recommendation:
like this;
if (oracle == address(0)) revert ADDRESS_ZERO();
Function writing
that does not comply with the Solidity Style Guide
Context: All Contracts
Description: Order of Functions; ordering helps readers identify which functions they can call and to find the constructor and fallback definitions easier. But there are contracts in the project that do not comply with this.
https://docs.soliditylang.org/en/v0.8.17/style-guide.html
Functions should be grouped according to their visibility and ordered:
constructor receive function (if exists) fallback function (if exists) external public internal private within a grouping, place theย viewย andย pureย functions last
Context:
src/DBR.sol: 13: uint8 public constant decimals = 18;
Recommendation: Variables are declared asย constantย utilize theย UPPER_CASE_WITH_UNDERSCORESย format.
Throughout the codebase, events are generally emitted when sensitive changes are made to the contracts. However, some events are missing important parameters
The events should include the new value and old value where possible:
Events with no old value;
src/DBR.sol: 69 */ 70: function claimOperator() public { 71: require(msg.sender == pendingOperator, "ONLY PENDING OPERATOR"); 72: operator = pendingOperator; 73: pendingOperator = address(0); 74: emit ChangeOperator(operator); 75: } 76 src/Oracle.sol: 65 */ 66: function claimOperator() public { 67: require(msg.sender == pendingOperator, "ONLY PENDING OPERATOR"); 68: operator = pendingOperator; 69: pendingOperator = address(0); 70: emit ChangeOperator(operator); 71: }
Context: 35 results - 9 files Project uncheckeds list:
7 results - 2 files src/DBR.sol: 172 balances[msg.sender] -= amount; 173: unchecked { 174 balances[to] += amount; 196 balances[from] -= amount; 197: unchecked { 198 balances[to] += amount; 236 require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); 237: unchecked { 238 address recoveredAddress = ecrecover( 373 _totalSupply += amount; 374: unchecked { 375 balances[to] += amount; 387 balances[from] -= amount; 388: unchecked { 389 _totalSupply -= amount; src/Market.sol: 423 require(deadline >= block.timestamp, "DEADLINE_EXPIRED"); 424: unchecked { 425 address recoveredAddress = ecrecover( 487 require(deadline >= block.timestamp, "DEADLINE_EXPIRED"); 488: unchecked { 489 address recoveredAddress = ecrecover(
Description: In total 2 contracts, 7 unchecked are used, the functions used are critical. For this reason, there must be fuzzing tests in the tests of the project. Not seen in tests.
Recommendation: Use should fuzzing test like Echidna.
As Alberto Cuesta Canada said: Fuzzing is not easy, the tools are rough, and the math is hard, but it is worth it. Fuzzing gives me a level of confidence in my smart contracts that I didnโt have before. Relying just on unit testing anymore and poking around in a testnet seems reckless now.
https://medium.com/coinmonks/smart-contract-fuzzing-d9b88e0b0a05
Context: All contracts
Description: For security, it is best practice to use the latest Solidity version. For the security fix list in the versions; https://github.com/ethereum/solidity/blob/develop/Changelog.md
Recommendation:
Old version of Solidity is used (0.8.13)
, newer version can be used (0.8.17)
Context: foundry.toml#L6
"src/Market.sol": { "lastModificationDate": 1666728684104, "contentHash": "6cf290b0486fc2d9b12c7c36d9303d9f", "sourceName": "src/Market.sol", "solcConfig": { "settings": { "optimizer": { "enabled": true, "runs": 200
Description: Protocol has enabled optional compiler optimizations in Solidity. There have been several optimization bugs with security implications. Moreover, optimizations are actively being developed. Solidity compiler optimizations are disabled by default, and it is unclear how many contracts in the wild actually use them.
Therefore, it is unclear how well they are being tested and exercised. High-severity security issues due to optimization bugs have occurred in the past. A high-severity bug in the emscripten-generated solc-js compiler used by Truffle and Remix persisted until late 2018. The fix for this bug was not reported in the Solidity CHANGELOG.
Another high-severity optimization bug resulting in incorrect bit shift results was patched in Solidity 0.5.6. More recently, another bug due to the incorrect caching of keccak256 was reported. A compiler audit of Solidity from November 2018 concluded that the optional optimizations may not be safe. It is likely that there are latent bugs related to optimization and that new bugs will be introduced due to future optimizations.
Exploit Scenario A latent or future bug in Solidity compiler optimizationsโor in the Emscripten transpilation to solc-jsโcauses a security vulnerability in the contracts.
Recommendation: Short term, measure the gas savings from optimizations and carefully weigh them against the possibility of an optimization-related bug. Long term, monitor the development and adoption of Solidity compiler optimizations to assess their maturity.
Style Guide helps to maintain code layout consistent and make code more readable
Mappingsย : Avoid whitespaces while declaring mapping variables.
src/DBR.sol: 9: contract DolaBorrowingRights { 24: mapping (address => bool) public minters; 25: mapping (address => bool) public markets; 26: mapping (address => uint) public debts; 27: mapping (address => uint) public dueTokensAccrued; 28: mapping (address => uint) public lastUpdated;
Context:
src/Fed.sol: 85 */ 86: function expansion(IMarket market, uint amount) public { 93: require(globalSupply <= supplyCeiling);
Description: Vulnerability related to description is not written to require, it must be written so that the user knows why the process is reverted.
This is an important debug analysis for programs and users that analyze transaction revert details such as Tenderly.
Recommendation:
src/Fed.sol: 85 */ 86: function expansion(IMarket market, uint amount) public { 93: require(globalSupply <= supplyCeiling, "Should not exceed globalSupply ");
Description: NatSpec is missing for the following functions , constructor and modifier:
Context:
src/Market.sol: 35 36: contract Market { 37: 38: address public gov; 39: address public lender; 40: address public pauseGuardian; 41: address public immutable escrowImplementation; 42: IDolaBorrowingRights public immutable dbr; 43: IBorrowController public borrowController; 44: IERC20 public immutable dola = IERC20(0x865377367054516e17014CcdED1e7d814EDC9ce4); 45: IERC20 public immutable collateral; 46: IOracle public oracle; 47: uint public collateralFactorBps; 48: uint public replenishmentIncentiveBps; 49: uint public liquidationIncentiveBps; 50: uint public liquidationFeeBps; 51: uint public liquidationFactorBps = 5000; // 50% by default 52: bool immutable callOnDepositCallback; 53: bool public borrowPaused; 54: uint public totalDebt; 55: uint256 internal immutable INITIAL_CHAIN_ID; 56: bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; 57: mapping (address => IEscrow) public escrows; // user => escrow 58: mapping (address => uint) public debts; // user => debt 59: mapping(address => uint256) public nonces; // user => nonce 60: 61: constructor ( 62: address _gov, 63: address _lender, 64: address _pauseGuardian, 65: address _escrowImplementation, 66: IDolaBorrowingRights _dbr, 67: IERC20 _collateral, 68: IOracle _oracle, 69: uint _collateralFactorBps, 70: uint _replenishmentIncentiveBps, 71: uint _liquidationIncentiveBps, 72: bool _callOnDepositCallback 73: ) { 74: require(_collateralFactorBps < 10000, "Invalid collateral factor"); 75: require(_liquidationIncentiveBps > 0 && _liquidationIncentiveBps < 10000, "Invalid liquidation incentive"); 76: require(_replenishmentIncentiveBps < 10000, "Replenishment incentive must be less than 100%"); 77: gov = _gov; 78: lender = _lender; 79: pauseGuardian = _pauseGuardian; 80: escrowImplementation = _escrowImplementation; 81: dbr = _dbr; 82: collateral = _collateral; 83: oracle = _oracle; 84: collateralFactorBps = _collateralFactorBps; 85: replenishmentIncentiveBps = _replenishmentIncentiveBps; 86: liquidationIncentiveBps = _liquidationIncentiveBps; 87: callOnDepositCallback = _callOnDepositCallback; 88: INITIAL_CHAIN_ID = block.chainid; 89: INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); 90: } 91: 92: modifier onlyGov { 93: require(msg.sender == gov, "Only gov can call this function"); 94: _; 95: } 96: 97: function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { 98: return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); 99: } 100: 101: function computeDomainSeparator() internal view virtual returns (bytes32) { 102: return 103: keccak256( 104: abi.encode( 105: keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), 106: keccak256(bytes("DBR MARKET")), 107: keccak256("1"), 108: block.chainid, 109: address(this) 110: ) 111: ); 112: }
Description:Contracts should be deployed with the same compiler version and flags that they have been tested with thoroughly. Locking the pragma helps to ensure that contracts do not accidentally get deployed using, for example, an outdated compiler version that might introduce bugs that affect the contract system negatively. https://swcregistry.io/docs/SWC-103
Context:
8 results - 8 files src/BorrowController.sol: 1 // SPDX-License-Identifier: UNLICENSED 2: pragma solidity ^0.8.13; 3 src/DBR.sol: 1 // SPDX-License-Identifier: UNLICENSED 2: pragma solidity ^0.8.13; 3 src/Fed.sol: 1 // SPDX-License-Identifier: UNLICENSED 2: pragma solidity ^0.8.13; 3 src/Market.sol: 1 // SPDX-License-Identifier: UNLICENSED 2: pragma solidity ^0.8.13; 3 src/Oracle.sol: 1 // SPDX-License-Identifier: UNLICENSED 2: pragma solidity ^0.8.13; 3 src/escrows/GovTokenEscrow.sol: 1 // SPDX-License-Identifier: UNLICENSED 2: pragma solidity ^0.8.13; 3 src/escrows/INVEscrow.sol: 1 // SPDX-License-Identifier: UNLICENSED 2: pragma solidity ^0.8.13; 3 src/escrows/SimpleERC20Escrow.sol: 1 // SPDX-License-Identifier: UNLICENSED 2: pragma solidity ^0.8.13; 3
Recommendation: Lock the pragma version and also consider known bugs (https://github.com/ethereum/solidity/releases) for the compiler version that is chosen.
Description:
The above codes don't follow Solidity's standard naming convention, internal and private functions : the mixedCase format starting with an underscore (_mixedCase starting with an underscore) public and external functions : only mixedCase constant variable : UPPER_CASE_WITH_UNDERSCORES (separated by uppercase and underscore)
Context:
9 results - 2 files src/DBR.sol: 278 279: function computeDomainSeparator() internal view virtual returns (bytes32) { 280 return src/Market.sol: 100 101: function computeDomainSeparator() internal view virtual returns (bytes32) { 102 return 225 */ 226: function createEscrow(address user) internal returns (IEscrow instance) { 227 address implementation = escrowImplementation; 244 */ 245: function getEscrow(address user) internal returns (IEscrow) { 246 if(escrows[user] != IEscrow(address(0))) return escrows[user]; 329 */ 330: function getCollateralValueInternal(address user) internal returns (uint) { 331 IEscrow escrow = predictEscrow(user); 350 */ 351: function getCreditLimitInternal(address user) internal returns (uint) { 352 uint collateralValue = getCollateralValueInternal(user); 359 */ 360: function getWithdrawalLimitInternal(address user) internal returns (uint) { 361 IEscrow escrow = predictEscrow(user); 395 */ 396: function borrowInternal(address borrower, address to, uint amount) internal { 397 require(!borrowPaused, "Borrowing is paused"); 466 */ 467: function withdrawInternal(address from, address to, uint amount) internal { 468 uint limit = getWithdrawalLimitInternal(from);
Description: Events help non-contract tools to track changes, and events prevent users from being surprised by changes
Context:
22 results - 10 files src/BorrowController.sol: 25 */ 26: function setOperator(address _operator) public onlyOperator { operator = _operator; } 27 src/DBR.sol: 52 */ 53: function setPendingOperator(address newOperator_) public onlyOperator { 54 pendingOperator = newOperator_; 61 */ 62: function setReplenishmentPriceBps(uint newReplenishmentPriceBps_) public onlyOperator { 63 require(newReplenishmentPriceBps_ > 0, "replenishment price must be over 0"); src/Market.sol: 117 */ 118: function setOracle(IOracle _oracle) public onlyGov { oracle = _oracle; } 119 123 */ 124: function setBorrowController(IBorrowController _borrowController) public onlyGov { borrowController = _borrowController; } 125 129 */ 130: function setGov(address _gov) public onlyGov { gov = _gov; } 131 135 */ 136: function setLender(address _lender) public onlyGov { lender = _lender; } 137 141 */ 142: function setPauseGuardian(address _pauseGuardian) public onlyGov { pauseGuardian = _pauseGuardian; } 143 148 */ 149: function setCollateralFactorBps(uint _collateralFactorBps) public onlyGov { 150 require(_collateralFactorBps < 10000, "Invalid collateral factor"); 160 */ 161: function setLiquidationFactorBps(uint _liquidationFactorBps) public onlyGov { 162 require(_liquidationFactorBps > 0 && _liquidationFactorBps <= 10000, "Invalid liquidation factor"); 171 */ 172: function setReplenismentIncentiveBps(uint _replenishmentIncentiveBps) public onlyGov { 173 require(_replenishmentIncentiveBps > 0 && _replenishmentIncentiveBps < 10000, "Invalid replenishment incentive"); 182 */ 183: function setLiquidationIncentiveBps(uint _liquidationIncentiveBps) public onlyGov { 184 require(_liquidationIncentiveBps > 0 && _liquidationIncentiveBps + liquidationFeeBps < 10000, "Invalid liquidation incentive"); 193 */ 194: function setLiquidationFeeBps(uint _liquidationFeeBps) public onlyGov { 195 require(_liquidationFeeBps > 0 && _liquidationFeeBps + liquidationIncentiveBps < 10000, "Invalid liquidation fee"); src/Oracle.sol: 43 */ 44: function setPendingOperator(address newOperator_) public onlyOperator { pendingOperator = newOperator_; } 45 52 */ 53: function setFeed(address token, IChainlinkFeed feed, uint8 tokenDecimals) public onlyOperator { feeds[token] = FeedData(feed, tokenDecimals); } 54 60 */ 61: function setFixedPrice(address token, uint price) public onlyOperator { fixedPrices[token] = price; }
Recommendation: Add Event-Emit
Context:
src/escrows/INVEscrow.sol: 34 constructor(IXINV _xINV) { 35: xINV = _xINV; // TODO: Test whether an immutable variable will persist across proxies 36 }
Recommendation: Use temporary TODOs as you work on a feature, but make sure to treat them before merging. Either add a link to a proper issue in your TODO, or remove it from the code.
Description: Add NatSpec comments describing mapping keys and values
Context:
6 results - 3 files src/BorrowController.sol: 11: mapping(address => bool) public contractAllowlist; src/DBR.sol: 19: mapping(address => uint256) public balances; 20: mapping(address => mapping(address => uint256)) public allowance; 23: mapping(address => uint256) public nonces; 24 mapping (address => bool) public minters; src/Oracle.sol: 26 mapping (address => uint) public fixedPrices;
Recommendation:
/// @dev address(user) -> address(ERC0 Contract Address) -> uint256(allowance amount from user) mapping(address => mapping(address => uint256)) public allowance;
Description:
Cryptocurrency mixing service, Tornado Cash, has been blacklisted in the OFAC.
A lot of blockchain companies, token projects, NFT Projects have blacklisted
all Ethereum addresses owned by Tornado Cash listed in the US Treasury Department's sanction against the protocol.
https://home.treasury.gov/policy-issues/financial-sanctions/recent-actions/20220808
Some of these Projects; USDC Aave Uniswap Balancer Infura Alchemy Opensea dYdX
For this reason, every project in the Ethereum network must have a blacklist function, this is a good method to avoid legal problems in the future, apart from the current need.
Transactions from the project by an account funded by Tonadocash or banned by OFAC can lead to legal problems.Especially American citizens may want to get addresses to the blacklist legally, this is not an obligation
If you think that such legal prohibitions should be made directly by validators, this may not be possible: https://www.paradigm.xyz/2022/09/base-layer-neutrality
The ban on Tornado Cash makes little sense, because in the end, no one can prevent people from using other mixer smart contracts, or forking the existing ones. It neither hinders cybercrime, nor privacy.
Here is the most beautiful and close to the project example; Manifold
Manifold Contract https://etherscan.io/address/0xe4e4003afe3765aca8149a82fc064c0b125b9e5a#code
modifier nonBlacklistRequired(address extension) { require(!_blacklistedExtensions.contains(extension), "Extension blacklisted"); _; }
Recommended Mitigation Steps add to Blacklist function and modifier.
Description: I recommend using header for Solidity code layout and readability
https://github.com/transmissions11/headers
/*////////////////////////////////////////////////////////////// TESTING 123 //////////////////////////////////////////////////////////////*/
Description: I recommend adding NatSpec comments explaining the variables defined in Storage, their slots, their contents and definitions.
This improves code readability and control quality
Current Code;
src/DBR.sol: 8 */ 9: contract DolaBorrowingRights { 10: 11: string public name; 12: string public symbol; 13: uint8 public constant decimals = 18; 14: uint256 public _totalSupply; 15: address public operator; 16: address public pendingOperator; 17: uint public totalDueTokensAccrued; 18: uint public replenishmentPriceBps; 19: mapping(address => uint256) public balances; 20: mapping(address => mapping(address => uint256)) public allowance; 21: uint256 internal immutable INITIAL_CHAIN_ID; 22: bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; 23: mapping(address => uint256) public nonces; 24: mapping (address => bool) public minters; 25: mapping (address => bool) public markets; 26: mapping (address => uint) public debts; // user => debt across all tracked markets 27: mapping (address => uint) public dueTokensAccrued; // user => amount of due tokens accrued 28: mapping (address => uint) public lastUpdated; // user => last update timestamp
Recommendation Code;
/****** Slot 0 ******/ /** * @notice Project name * @dev Value is defined in the constructor */ string public name; /****** Slot 1 ******/ /** * @notice Project symbol * @dev Value is defined in the constructor */ string public symbol /****** Slot 2 ******/ /** * @notice decimal */ uint8 public constant decimals = 18; ... /****** End of storage ******/
#0 - c4-judge
2022-11-08T00:48:48Z
0xean marked the issue as grade-a
๐ Selected for report: pfapostol
Also found by: 0x1f8b, 0xRoxas, 0xSmartContract, Amithuddar, Aymen0909, B2, Bnke0x0, Chandr, CloudX, Deivitto, Diana, Dinesh11G, ElKu, HardlyCodeMan, JC, JrNet, KoKo, Mathieu, Ozy42, Rahoz, RaymondFam, ReyAdmirado, Rolezn, Shinchan, __141345__, adriro, ajtra, aphak5010, ballx, c3phas, carlitox477, ch0bu, chaduke, cryptostellar5, djxploit, durianSausage, enckrish, exolorkistis, fatherOfBlocks, gogo, horsefacts, kaden, karanctf, leosathya, martin, mcwildy, oyc_109, ret2basic, robee, sakman, sakshamguruji, shark, skyle, tnevler
292.3851 USDC - $292.39
Number | Optimization Details | Context |
---|---|---|
[G-01] | Packing Storage | 1 |
[G-02] | Multiple address mappings can be combined into a single mapping of an address to a struct | 2 |
[G-03] | No need return keyword for gas efficient | 5 |
[G-04] | Usage of uints/ints smaller than 32 bytes (256 bits) saves gas | 1 |
[G-05] | Functions guaranteed to revert_ when callled by normal users can be marked payable | 11 |
[G-06] | x -= y (x += y) costs more gas than x = x โ y (x = x + y) for state variables | 27 |
[G-07] | Optimize names to save gas [22 gas per instance]ย | All contracts |
[G-08] | Use assembly to check for address(0) | 4 |
[G-09] | Use assembly to write address storage values | 18 |
[G-10] | The solady Library's Ownable contract is significantly gas-optimized, which can be used | 1 |
[G-11] | Setting The Constructor To Payable | 5 |
[G-12] | Reduce the size of error messages (Long revert Strings) | 5 |
[G-13] | Using double require instead of && consumes less gas | 11 |
[G-14] | Multiple address mappings can be combined into a single mapping of an address to a struct | 9 |
[G-15] | Use external instead of public for functions not called from contract | 16 |
Total 15 issues
Number | Suggestion Details |
---|---|
[S-01] | Missing zero-address check in constructor |
Storing state variables packaging is more gas efficient
12: string public symbol; - 13: uint8 public constant decimals = 18; + 13: uint8 public constant decimals = 18; 15: address public operator; 16 address public pendingOperator;
Context:
src/DBR.sol: 23 mapping(address => uint256) public nonces; - 24: mapping (address => bool) public minters; - 25: mapping (address => bool) public markets; + 24: mapping (address => uint256) public markets_minters; + // address => uint256 => 1 (minters true) + // address => uint256 => 2 (minters false) + // address => uint256 => 3 (markets true) + // address => uint256 => 4 (markets false)
Description: Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot.
Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations.
return
keyword for gas efficientContext:
src/BorrowController.sol: 46 function borrowAllowed(address msgSender, address, uint) public view returns (bool) { 48 return contractAllowlist[msgSender]; src/DBR.sol: 160 emit Approval(msg.sender, spender, amount); 161: return true; 162 } 176 emit Transfer(msg.sender, to, amount); 177: return true; 178 } 200 emit Transfer(from, to, amount); 201: return true; 202 }
Recommendation:
You should remove the return
keyword from the specified contexts.
Proof Of Concept:
src/DBR.sol: 157 */ 158: function approve(address spender, uint256 amount) public virtual returns (bool) { 159: allowance[msg.sender][spender] = amount; 160: emit Approval(msg.sender, spender, amount); 161: return true; 162: }
Gas Report:
without `return` keyword; โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโฎ โ src/DBR.sol:DolaBorrowingRights contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโชโโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ 1414004 โ 8019 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ approve โ 22463 โ 24038 โ 24563 โ 24563 โ 4 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโดโโโโโโโโดโโโโโโโโโโฏ
with `return` keyword; โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโฎ โ src/DBR.sol:DolaBorrowingRights contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโชโโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ 1411204 โ 8005 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ approve โ 22469 โ 24044 โ 24569 โ 24569 โ 4 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโดโโโโโโโโดโโโโโโโโโโฏ
Context:
src/Oracle.sol: 17 18: struct FeedData { 19: IChainlinkFeed feed; - 20: uint8 tokenDecimals; + 20: uint256 tokenDecimals; 21: }
Description: When using elements that are smaller than 32 bytes, your contractโs gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.
payable
Context:ย
11 results - 3 files src/BorrowController.sol: 26: function setOperator(address _operator) public onlyOperator { operator = _operator; } 32: function allow(address allowedContract) public onlyOperator { contractAllowlist[allowedContract] = true; } 38: function deny(address deniedContract) public onlyOperator { contractAllowlist[deniedContract] = false; } src/DBR.sol: 53: function setPendingOperator(address newOperator_) public onlyOperator { 62: function setReplenishmentPriceBps(uint newReplenishmentPriceBps_) public onlyOperator { 81: function addMinter(address minter_) public onlyOperator { 90: function removeMinter(address minter_) public onlyOperator { 99: function addMarket(address market_) public onlyOperator { src/Oracle.sol: 44: function setPendingOperator(address newOperator_) public onlyOperator { pendingOperator = newOperator_; } 53: function setFeed(address token, IChainlinkFeed feed, uint8 tokenDecimals) public onlyOperator { feeds[token] = FeedData(feed, tokenDecimals); } 61: function setFixedPrice(address token, uint price) public onlyOperator { fixedPrices[token] = price; }
Description: If a function modifier or require such as onlyOwner-admin 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. The extra opcodes avoided are CALLVALUE(2), DUP1(3), ISZERO(3), PUSH2(3), JUMPI(10), PUSH1(3), DUP1(3), REVERT(0), JUMPDEST(1), POP(2) which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost.
Recommendation:
Functions guaranteed to revert when called by normal users can be marked payableย (for only onlyowner or admin
functions)
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.foo(); c1.foo(); } } contract Contract0 { uint256inverse; function foo() external { inverse++; } } contract Contract1 { uint256inverse; function foo() external payable { inverse++; } }
Gas Report:
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract0 contract โ ย ย ย ย ย ย ย ย โ ย ย ย โย ย ย ย โ ย ย ย โ ย ย ย ย โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโชโโโโโโโโชโโโโโโโโโโก โ Deployment Cost ย ย ย ย ย ย ย ย ย ย ย ย ย โ Deployment Size โ ย ย ย โย ย ย ย โ ย ย ย โ ย ย ย ย โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ 44293 ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย โ 252 ย ย ย ย ย ย โ ย ย ย โย ย ย ย โ ย ย ย โ ย ย ย ย โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ Function Name ย ย ย ย ย ย ย ย ย ย ย ย ย ย โ min ย ย ย ย ย ย โ avg ย โ median โ max ย โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ foo ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย โ 22308 ย ย ย ย ย โ 22308 โ 22308ย โ 22308 โ 1 ย ย ย โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโดโโโโโโโโดโโโโโโโโโโฏ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract1 contract โ ย ย ย ย ย ย ย ย โ ย ย ย โย ย ย ย โ ย ย ย โ ย ย ย ย โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโชโโโโโโโโชโโโโโโโโโโก โ Deployment Cost ย ย ย ย ย ย ย ย ย ย ย ย ย โ Deployment Size โ ย ย ย โย ย ย ย โ ย ย ย โ ย ย ย ย โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ 41893 ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย โ 240 ย ย ย ย ย ย โ ย ย ย โย ย ย ย โ ย ย ย โ ย ย ย ย โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ Function Name ย ย ย ย ย ย ย ย ย ย ย ย ย ย โ min ย ย ย ย ย ย โ avg ย โ median โ max ย โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ foo ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย โ 22284 ย ย ย ย ย โ 22284 โ 22284ย โ 22284 โ 1 ย ย ย โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโดโโโโโโโโดโโโโโโโโโโฏ
x -= y (x += y)
costs more gas than x = x โ y (x = x + y)
for state variablesDescription:
x -= y
costs more gas than x = x โ y
for state variables.
Context:
src/DBR.sol: 173 unchecked { 174: balances[to] += amount; 175 } 197 unchecked { 198: balances[to] += amount; 199 } 300 uint accrued = (block.timestamp - lastUpdated[user]) * debt / 365 days; 301: dueTokensAccrued[user] += accrued; 302: totalDueTokensAccrued += accrued; 303 lastUpdated[user] = block.timestamp; 316 require(deficitOf(user) == 0, "DBR Deficit"); 317: debts[user] += additionalDebt; 318 } 344 accrueDueTokens(user); 345: debts[user] += replenishmentCost; 346 _mint(user, amount); 372 function _mint(address to, uint256 amount) internal virtual { 373: _totalSupply += amount; 374 unchecked { 375: balances[to] += amount; 376 } src/Fed.sol: 90 dola.mint(address(market), amount); 91: supplies[market] += amount; 92: globalSupply += amount; 93 require(globalSupply <= supplyCeiling); src/Market.sol: 401 uint credit = getCreditLimitInternal(borrower); 402: debts[borrower] += amount; 403 require(credit >= debts[borrower], "Exceeded credit limit"); 404: totalDebt += amount; 405 dbr.onBorrow(borrower, amount); 571 uint replenisherReward = replenishmentCost * replenishmentIncentiveBps / 10000; 572: debts[user] += replenishmentCost; 573 uint collateralValue = getCollateralValueInternal(user); 574 require(collateralValue >= debts[user], "Exceeded collateral value"); 575: totalDebt += replenishmentCost; 576 dbr.onForceReplenish(user, amount); 604 uint liquidatorReward = repaidDebt * 1 ether / price; 605: liquidatorReward += liquidatorReward * liquidationIncentiveBps / 10000; 606 debts[user] -= repaidDebt;
src/DBR.sol: 171 require(balanceOf(msg.sender) >= amount, "Insufficient balance"); 172: balances[msg.sender] -= amount; 173 unchecked { 195 require(balanceOf(from) >= amount, "Insufficient balance"); 196: balances[from] -= amount; 197 unchecked { 328 accrueDueTokens(user); 329: debts[user] -= repaidDebt; 330 } 386 require(balanceOf(from) >= amount, "Insufficient balance"); 387: balances[from] -= amount; 388 unchecked { 389: _totalSupply -= amount; 390 } src/Fed.sol: 109 dola.burn(amount); 110: supplies[market] -= amount; 111: globalSupply -= amount; 112 emit Contraction(market, amount); src/Market.sol: 540 require(debt >= amount, "Insufficient debt"); 541: debts[user] -= amount; 542: totalDebt -= amount; 543 dbr.onRepay(user, amount); 605 liquidatorReward += liquidatorReward * liquidationIncentiveBps / 10000; 606: debts[user] -= repaidDebt; 607: totalDebt -= repaidDebt; 608 dbr.onRepay(user, repaidDebt);
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.swap(2,3); c1.swap1(2,3); } } contract Contract0 { uint256 public amountIn = 10; function swap(uint256 amountInToBin, uint256 fee) external returns (uint256 ) { return amountIn -= amountInToBin + fee; } } contract Contract1 { uint256 public amountIn = 10; function swap1(uint256 amountInToBin, uint256 fee) external returns (uint256 result1) { return (amountIn = amountIn - (amountInToBin + fee)); } }
Gas Report:
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโฌโโโโโโโฌโโโโโโโโโโฎ โ src/test/test.sol:Contract0 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโชโโโโโโโโโชโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ 83017 โ 341 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ swap โ 5454 โ 5454 โ 5454 โ 5454 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโฌโโโโโโโฌโโโโโโโโโโฎ โ src/test/test.sol:Contract1 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโชโโโโโโโโโชโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ 83017 โ 341 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ swap1 โ 5431 โ 5431 โ 5431 โ 5431 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ
Context:ย All Contracts
Description:ย
Contracts most called functions could simply save gas by function ordering via Method ID
. Calling a function at runtime will be cheaper if the function is positioned earlier in the order (has a relatively lower Method ID) because 22 gas
are added to the cost of a function for every position that came before it. The caller can save on gas if you prioritize most called functions.ย
Recommendation:ย
Find a lower method ID
name for the most called functions for exampleย Call()ย vs.ย Call1()ย is cheaper by 22 gas
For example, the function IDs in the Market.sol
contract will be the most used; A lower method ID may be given.
Proof of Consept: https://medium.com/joyso/solidity-how-does-function-name-affect-gas-consumption-in-smart-contract-47d270d8ac92
Market.sol function names can be named and sorted according to METHOD ID
Sighash | Function Signature ======================== a9059cbb => transfer(address,uint256) 23b872dd => transferFrom(address,address,uint256) 70a08231 => balanceOf(address) 449e815d => getPrice(address,uint256) 949c4fa3 => viewPrice(address,uint256) ca786762 => initialize(IERC20,address) 12c93f59 => onDeposit() c4076876 => pay(address,uint256) b69ef8a8 => balance() f7f11fb7 => onBorrow(address,uint256) d8d2c648 => onRepay(address,uint256) e94d28fc => onForceReplenish(address,uint256) 36459f04 => deficitOf(address) a10f84cb => replenishmentPriceBps() da3d454c => borrowAllowed(address,address,uint256) 3644e515 => DOMAIN_SEPARATOR() 6d5a0e99 => computeDomainSeparator() cb06209e => setOracle(IOracle) a9674c87 => setBorrowController(IBorrowController) cfad57a2 => setGov(address) 46e368d4 => setLender(address) 48bde20c => setPauseGuardian(address) 7164695a => setCollateralFactorBps(uint256) d1220a3c => setLiquidationFactorBps(uint256) 6f48fbb6 => setReplenismentIncentiveBps(uint256) 34734dd3 => setLiquidationIncentiveBps(uint256) 8951b054 => setLiquidationFeeBps(uint256) 7d32e793 => recall(uint256) c8018619 => pauseBorrows(bool) f6a8419e => createEscrow(address) a26d494d => getEscrow(address) b6b55f25 => deposit(uint256) b75061bb => depositAndBorrow(uint256,uint256) 47e7ef24 => deposit(address,uint256) 4ef64ee7 => predictEscrow(address) 97904e42 => getCollateralValue(address) 48b6a3ba => getCollateralValueInternal(address) 2c333e25 => getCreditLimit(address) 136144d0 => getCreditLimitInternal(address) 12d65ec6 => getWithdrawalLimitInternal(address) c74e6d80 => getWithdrawalLimit(address) 2b49b869 => borrowInternal(address,address,uint256) c5ebeaec => borrow(uint256) 1ef08b75 => borrowOnBehalf(address,uint256,uint256,uint8,bytes32,bytes32) 42422a87 => withdrawInternal(address,address,uint256) 2e1a7d4d => withdraw(uint256) 3525f591 => withdrawOnBehalf(address,uint256,uint256,uint8,bytes32,bytes32) 5a57b46f => invalidateNonce() 22867d78 => repay(address,uint256) ebc9b94d => repayAndWithdraw(uint256,uint256) 651afe83 => forceReplenish(address,uint256) 02197952 => getLiquidatableDebt(address) bcbaf487 => liquidate(address,uint256)
address(0)
Context:ย
4 results - 3 files src/DBR.sol: 260 ); 261: require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); 262 allowance[recoveredAddress][spender] = value; src/Market.sol: 454 ); 455: require(recoveredAddress != address(0) && recoveredAddress == from, "INVALID_SIGNER"); 456 borrowInternal(from, msg.sender, amount); 518 ); 519: require(recoveredAddress != address(0) && recoveredAddress == from, "INVALID_SIGNER"); 520 withdrawInternal(from, msg.sender, amount); src/test/mocks/ERC20.sol: 178 require( 179: recoveredAddress != address(0) && recoveredAddress == owner, 180 "INVALID_SIGNATURE"
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public view { c0.setOperator(address(this)); c1.setOperator(address(this)); } } contract Contract0 { function setOperator(address operator_) public pure { require(operator_) != address(0), "INVALID_RECIPIENT"); } } contract Contract1 { function setOperator(address operator_) public pure { assembly { if iszero(operator_) { mstore(0x00, "Callback_InvalidParams") revert(0x00, 0x20) } } } }
Gas Report:
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโฌโโโโโโโโโฌโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract0 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโชโโโโโโโโโชโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โ 50899 โ 285 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โ setOperator โ 258 โ 258 โ 258 โ 258 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโดโโโโโโโโโดโโโโโโดโโโโโโโโโโฏ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโฌโโโโโโโโโฌโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract1 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโชโโโโโโโโโชโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โ 44893 โ 255 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โ setOperator โ 252 โ 252 โ 252 โ 252 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโดโโโโโโโโโดโโโโโโดโโโโโโโโโโฏ
assembly
to write address storage valuesContext:
18 results - 5 files:
src/BorrowController.sol: 12 13: constructor(address _operator) { 14 operator = _operator; src/Fed.sol: 35 36: constructor (IDBR _dbr) { 37 dbr = _dbr; src/BorrowController.sol: 25 */ 26: function setOperator(address _operator) public onlyOperator { operator = _operator; } 27 src/DBR.sol: 52 */ 53: function setPendingOperator(address newOperator_) public onlyOperator { 54 pendingOperator = newOperator_; 61 */ 62: function setReplenishmentPriceBps(uint newReplenishmentPriceBps_) public onlyOperator { 63 require(newReplenishmentPriceBps_ > 0, "replenishment price must be over 0"); src/Market.sol: 117 */ 118: function setOracle(IOracle _oracle) public onlyGov { oracle = _oracle; } 119 123 */ 124: function setBorrowController(IBorrowController _borrowController) public onlyGov { borrowController = _borrowController; } 125 129 */ 130: function setGov(address _gov) public onlyGov { gov = _gov; } 131 135 */ 136: function setLender(address _lender) public onlyGov { lender = _lender; } 137 141 */ 142: function setPauseGuardian(address _pauseGuardian) public onlyGov { pauseGuardian = _pauseGuardian; } 143 148 */ 149: function setCollateralFactorBps(uint _collateralFactorBps) public onlyGov { 150 require(_collateralFactorBps < 10000, "Invalid collateral factor"); 160 */ 161: function setLiquidationFactorBps(uint _liquidationFactorBps) public onlyGov { 162 require(_liquidationFactorBps > 0 && _liquidationFactorBps <= 10000, "Invalid liquidation factor"); 171 */ 172: function setReplenismentIncentiveBps(uint _replenishmentIncentiveBps) public onlyGov { 173 require(_replenishmentIncentiveBps > 0 && _replenishmentIncentiveBps < 10000, "Invalid replenishment incentive"); 182 */ 183: function setLiquidationIncentiveBps(uint _liquidationIncentiveBps) public onlyGov { 184 require(_liquidationIncentiveBps > 0 && _liquidationIncentiveBps + liquidationFeeBps < 10000, "Invalid liquidation incentive"); 193 */ 194: function setLiquidationFeeBps(uint _liquidationFeeBps) public onlyGov { 195 require(_liquidationFeeBps > 0 && _liquidationFeeBps + liquidationIncentiveBps < 10000, "Invalid liquidation fee"); src/Oracle.sol: 43 */ 44: function setPendingOperator(address newOperator_) public onlyOperator { pendingOperator = newOperator_; } 45 52 */ 53: function setFeed(address token, IChainlinkFeed feed, uint8 tokenDecimals) public onlyOperator { feeds[token] = FeedData(feed, tokenDecimals); } 54 60 */ 61: function setFixedPrice(address token, uint price) public onlyOperator { fixedPrices[token] = price; } 62
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
contract GasTestFoundry is DSTest { Contract1 c1; Contract2 c2; function setUp() public { c1 = new Contract1(); c2 = new Contract2(); } function testGas() public { c1.setRewardTokenAndAmount(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4,356); c2.setRewardTokenAndAmount(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4,356); } } contract Contract1 { address rewardToken ; uint256 reward; function setRewardTokenAndAmount(address token_, uint256 reward_) external { rewardToken = token_; reward = reward_; } } contract Contract2 { address rewardToken ; uint256 reward; function setRewardTokenAndAmount(address token_, uint256 reward_) external { assembly { sstore(rewardToken.slot, token_) sstore(reward.slot, reward_) } } }
Gas Report:
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract1 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโชโโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ 50899 โ 285 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ setRewardTokenAndAmount โ 44490 โ 44490 โ 44490 โ 44490 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโดโโโโโโโโดโโโโโโโโโโฏ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract2 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโชโโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ 38087 โ 221 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโค โ setRewardTokenAndAmount โ 44457 โ 44457 โ 44457 โ 44457 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโดโโโโโโโโดโโโโโโโโโโฏ
Description:
The project uses the onlyOperator
authorization model with the PendingOwnable.sol
contract. I recommend using Solady's highly gas optimized contract.
https://github.com/Vectorized/solady/blob/main/src/auth/OwnableRoles.sol
Description:
You can cut out 10 opcodes in the creation-time EVM bytecode if you declare a constructor payable. Making the constructor payable eliminates the need for an initial check ofย msg.value == 0
ย and saves 13 gas
on deployment with no security risks.
Context:
5 results - 5 files src/BorrowController.sol: 12 13: constructor(address _operator) { 14 operator = _operator; src/DBR.sol: 29 30: constructor( 31 uint _replenishmentPriceBps, src/Fed.sol: 35 36: constructor (IDBR _dbr, IDola _dola, address _gov, address _chair, uint _supplyCeiling) { 37 dbr = _dbr; src/Market.sol: 60 61: constructor ( 62 address _gov, src/Oracle.sol: 28 29: constructor( 30 address _operator
Recommendation:
Set the constructor to payable
Proof Of Concept: https://forum.openzeppelin.com/t/a-collection-of-gas-optimisation-tricks/19966/5?u=pcaversaccio
The optimizer was turned on and set to 10000 runs
contract GasTestFoundry is DSTest { Contract1 c1; Contract2 c2; function setUp() public { c1 = new Contract1(); c2 = new Contract2(); } function testGas() public { c1.x(); c2.x(); } } contract Contract1 { uint256 public dummy; constructor() payable { dummy = 1; } function x() public { } } contract Contract2 { uint256 public dummy; constructor() { dummy = 1; } function x() public { } }
Gas Report:
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโฌโโโโโโโโโฌโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract1 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโชโโโโโโโโโชโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โ 49563 โ 159 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโฌโโโโโโโโโฌโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract2 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโชโโโโโโโโโชโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โ 49587 โ 172 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค
Description:
Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met.
Revert strings that are longer than 32 bytes require at least one additional mstore
, along with additional overhead for computing memory offset, etc.
5 results - 2 files src/BorrowController.sol: 62 function setReplenishmentPriceBps(uint newReplenishmentPriceBps_) public onlyOperator { 63: require(newReplenishmentPriceBps_ > 0, "replenishment price must be over 0"); 313 function onBorrow(address user, uint additionalDebt) public { 314: require(markets[msg.sender], "Only markets can call onBorrow"); 326 function onRepay(address user, uint repaidDebt) public { 327: require(markets[msg.sender], "Only markets can call onRepay"); 338 function onForceReplenish(address user, uint amount) public { 339: require(markets[msg.sender], "Only markets can call onForceReplenish"); src/Market.sol: 213 if(_value) { 214: require(msg.sender == pauseGuardian || msg.sender == gov, "Only pause guardian or governance can pause");
Recommendation: Revert strings > 32 bytes or use Custom Error()
Proof Of Concept: The optimizer was turned on and set to 10000 runs
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.foo(); c1.foo(); } } contract Contract0 { uint256 test1; function foo() external { require(test1 != 0, "This is Error"); } } contract Contract1 { uint256 test1; function foo() external { require(test1 != 0, "Your Project Drop List Has Error with ID and other parameters"); } }
Gas Report:
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโฌโโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract0 contract โ ย ย ย ย ย ย ย ย โย ย ย โย ย ย ย โย ย ย โ ย ย ย ย โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโชโโโโโโโโโชโโโโโโโชโโโโโโโโโโก โ Deployment Cost ย ย ย ย ย ย ย ย ย ย ย ย ย โ Deployment Size โย ย ย โย ย ย ย โย ย ย โ ย ย ย ย โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ 44093 ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย โ 251 ย ย ย ย ย ย โย ย ย โย ย ย ย โย ย ย โ ย ย ย ย โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ Function Name ย ย ย ย ย ย ย ย ย ย ย ย ย ย โ min ย ย ย ย ย ย โ avgย โ median โ maxย โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ foo ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย โ 2334ย ย ย ย ย ย โ 2334 โ 2334 ย โ 2334 โ 1 ย ย ย โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโฌโโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract1 contract โ ย ย ย ย ย ย ย ย โย ย ย โย ย ย ย โย ย ย โ ย ย ย ย โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโชโโโโโโโโโชโโโโโโโชโโโโโโโโโโก โ Deployment Cost ย ย ย ย ย ย ย ย ย ย ย ย ย โ Deployment Size โย ย ย โย ย ย ย โย ย ย โ ย ย ย ย โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ 51705 ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย โ 290 ย ย ย ย ย ย โย ย ย โย ย ย ย โย ย ย โ ย ย ย ย โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ Function Name ย ย ย ย ย ย ย ย ย ย ย ย ย ย โ min ย ย ย ย ย ย โ avgย โ median โ maxย โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ foo ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย โ 2352ย ย ย ย ย ย โ 2352 โ 2352 ย โ 2352 โ 1 ย ย ย โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ
Context:
11 results - 3 files src/DBR.sol: 261: require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); src/Market.sol: 75: require(_liquidationIncentiveBps > 0 && _liquidationIncentiveBps < 10000, "Invalid liquidation incentive"); 162: require(_liquidationFactorBps > 0 && _liquidationFactorBps <= 10000, "Invalid liquidation factor"); 173: require(_replenishmentIncentiveBps > 0 && _replenishmentIncentiveBps < 10000, "Invalid replenishment incentive"); 184: require(_liquidationIncentiveBps > 0 && _liquidationIncentiveBps + liquidationFeeBps < 10000, "Invalid liquidation incentive"); 195: require(_liquidationFeeBps > 0 && _liquidationFeeBps + liquidationIncentiveBps < 10000, "Invalid liquidation fee"); 455: require(recoveredAddress != address(0) && recoveredAddress == from, "INVALID_SIGNER"); 519: require(recoveredAddress != address(0) && recoveredAddress == from, "INVALID_SIGNER"); src/Oracle.sol: 96: uint twoDayLow = todaysLow > yesterdaysLow && yesterdaysLow > 0 ? yesterdaysLow : todaysLow; 135: uint twoDayLow = todaysLow > yesterdaysLow && yesterdaysLow > 0 ? yesterdaysLow : todaysLow; 136: if(twoDayLow > 0 && newBorrowingPower > twoDayLow) {
Context:
9: contract DolaBorrowingRights { 19: mapping(address => uint256) public balances; 23: mapping(address => uint256) public nonces; 24: mapping (address => bool) public minters; 25: mapping (address => bool) public markets; 26: mapping (address => uint) public debts; // user => debt across all tracked markets 27: mapping (address => uint) public dueTokensAccrued; // user => amount of due tokens accrued 28: mapping (address => uint) public lastUpdated; // user => last update timestamp
Description: Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot.
Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations.
external
instead of public
for functions not called from contractContext:
16 results - 4 files src/BorrowController.sol: 26: function setOperator(address _operator) public onlyOperator { operator = _operator; } src/DBR.sol: 53: function setPendingOperator(address newOperator_) public onlyOperator { 62: function setReplenishmentPriceBps(uint newReplenishmentPriceBps_) public onlyOperator { src/Market.sol: 118: function setOracle(IOracle _oracle) public onlyGov { oracle = _oracle; } 124: function setBorrowController(IBorrowController _borrowController) public onlyGov 130: function setGov(address _gov) public onlyGov { gov = _gov; } 136: function setLender(address _lender) public onlyGov { lender = _lender; } 142: function setPauseGuardian(address _pauseGuardian) public onlyGov { pauseGuardian = _pauseGuardian; } 149: function setCollateralFactorBps(uint _collateralFactorBps) public onlyGov { 161: function setLiquidationFactorBps(uint _liquidationFactorBps) public onlyGov { 172: function setReplenismentIncentiveBps(uint _replenishmentIncentiveBps) public onlyGov { 183: function setLiquidationIncentiveBps(uint _liquidationIncentiveBps) public onlyGov { 194: function setLiquidationFeeBps(uint _liquidationFeeBps) public onlyGov { src/Oracle.sol: 44: function setPendingOperator(address newOperator_) public onlyOperator { pendingOperator = newOperator_; } 53: function setFeed(address token, IChainlinkFeed feed, uint8 tokenDecimals) public onlyOperator { 61: function setFixedPrice(address token, uint price) public onlyOperator { fixedPrices[token] = price; }
Description: Use the external function visibility for gas optimization because the public visibility modifier is equivalent to using the external and internal visibility modifier, meaning both public and external can be called from outside of your contract, which requires more gas.
Remember that of these two visibility modifiers, only the public modifier can be called from other functions inside of your contract.
zero-address
check in constructor
Description: Missing checks for zero-addresses may lead to infunctional protocol, if the variable addresses are updated incorrectly. It also wast gas as it requires the redeployment of the contract.
5 results - 5 files src/BorrowController.sol: 12 13: constructor(address _operator) { 14 operator = _operator; src/DBR.sol: 29 30: constructor( 31 uint _replenishmentPriceBps, src/Fed.sol: 35 36: constructor (IDBR _dbr, IDola _dola, address _gov, address _chair, uint _supplyCeiling) { 37 dbr = _dbr; src/Market.sol: 60 61: constructor ( 62 address _gov, src/Oracle.sol: 28 29: constructor( 30 address _operator
#0 - c4-judge
2022-11-05T23:44:40Z
0xean marked the issue as grade-a