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: 57/127
Findings: 2
Award: $55.74
๐ 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
36.7345 USDC - $36.73
Emmiting events is recommended each time when a state variable's value is being changed or just some critical event for the contract has occurred. It also helps off-chain monitoring of the contract's state.
There are 15 instances of this issue:
File: src/DBR.sol 53: function setPendingOperator(address newOperator_) public onlyOperator { 62: function setReplenishmentPriceBps(uint newReplenishmentPriceBps_) public onlyOperator {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/DBR.sol
File: src/Fed.sol 48: function changeGov(address _gov) public { 57: function changeSupplyCeiling(uint _supplyCeiling) public { 66: function changeChair(address _chair) public { 75: function resign() public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Fed.sol
File: src/Market.sol 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 { 212: function pauseBorrows(bool _value) public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Market.sol
File: src/escrows/GovTokenEscrow.sol 30: function initialize(IERC20 _token, address _beneficiary) public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/escrows/GovTokenEscrow.sol
File: src/escrows/INVEscrow.sol 44: function initialize(IERC20 _token, address _beneficiary) public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/escrows/INVEscrow.sol
File: src/escrows/SimpleERC20Escrow.sol 25: function initialize(IERC20 _token, address) public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/escrows/SimpleERC20Escrow.sol
public
functions not called by the contract should be declared external
insteadThere are 48 instances of this issue:
File: src/BorrowController.sol 46: function borrowAllowed(address msgSender, address, uint) public view returns (bool) {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/BorrowController.sol
File: src/DBR.sol 53: function setPendingOperator(address newOperator_) public onlyOperator { 62: function setReplenishmentPriceBps(uint newReplenishmentPriceBps_) public onlyOperator { 70: function claimOperator() public { 81: function addMinter(address minter_) public onlyOperator { 90: function removeMinter(address minter_) public onlyOperator { 99: function addMarket(address market_) public onlyOperator { 109: function totalSupply() public view returns (uint) { 146: function signedBalanceOf(address user) public view returns (int) { 158: function approve(address spender, uint256 amount) public virtual returns (bool) { 170: function transfer(address to, uint256 amount) public virtual returns (bool) { 188: function transferFrom( 215: function permit( 258: function invalidateNonce() public { 300: function onBorrow(address user, uint additionalDebt) public { 313: function onRepay(address user, uint repaidDebt) public { 325: function onForceReplenish(address user, uint amount) public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/DBR.sol
File: src/Fed.sol 48: function changeGov(address _gov) public { 57: function changeSupplyCeiling(uint _supplyCeiling) public { 66: function changeChair(address _chair) public { 75: function resign() public { 86: function expansion(IMarket market, uint amount) public { 103: function contraction(IMarket market, uint amount) public { 131: function takeProfit(IMarket market) public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Fed.sol
File: src/Market.sol 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 { 203: function recall(uint amount) public { 212: function pauseBorrows(bool _value) public { 370: function getWithdrawalLimit(address user) public view returns (uint) { 520: function invalidateNonce() public { 559: function forceReplenish(address user, uint amount) public { 575: @notice View function for getting the amount of liquidateable debt a user holds. 578: function getLiquidatableDebt(address user) public view returns (uint) { 591: function liquidate(address user, uint repaidDebt) public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Market.sol
File: src/Oracle.sol 66: function claimOperator() public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Oracle.sol
File: src/escrows/GovTokenEscrow.sol 30: function initialize(IERC20 _token, address _beneficiary) public { 43: function pay(address recipient, uint amount) public { 52: function balance() public view returns (uint) {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/escrows/GovTokenEscrow.sol
File: src/escrows/INVEscrow.sol 44: function initialize(IERC20 _token, address _beneficiary) public { 59: function pay(address recipient, uint amount) public { 70: function balance() public view returns (uint) { 79: function onDeposit() public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/escrows/INVEscrow.sol
File: src/escrows/SimpleERC20Escrow.sol 25: function initialize(IERC20 _token, address) public { 36: function pay(address recipient, uint amount) public { 45: function balance() public view returns (uint) {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/escrows/SimpleERC20Escrow.sol
There are 8 instances of this issue:
File: src/BorrowController.sol 2: pragma solidity ^0.8.13;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/BorrowController.sol
File: src/DBR.sol 2: pragma solidity ^0.8.13;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/DBR.sol
File: src/Fed.sol 2: pragma solidity ^0.8.13;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Fed.sol
File: src/Market.sol 2: pragma solidity ^0.8.13;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Market.sol
File: src/Oracle.sol 2: pragma solidity ^0.8.13;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Oracle.sol
File: src/escrows/GovTokenEscrow.sol 2: pragma solidity ^0.8.13;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/escrows/GovTokenEscrow.sol
File: src/escrows/INVEscrow.sol 2: pragma solidity ^0.8.13;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/escrows/INVEscrow.sol
File: src/escrows/SimpleERC20Escrow.sol 2: pragma solidity ^0.8.13;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/escrows/SimpleERC20Escrow.sol
indexed
fieldsThere are 6 instances of this issue:
File: src/DBR.sol 381: event Transfer(address indexed from, address indexed to, uint256 amount); 382: event Approval(address indexed owner, address indexed spender, uint256 amount);
https://github.com/code-423n4/2022-10-inverse/tree/main/src/DBR.sol
File: src/Market.sol 616: event Withdraw(address indexed account, address indexed to, uint amount); 617: event Repay(address indexed account, address indexed repayer, uint amount); 618: event ForceReplenish(address indexed account, address indexed replenisher, uint deficit, uint replenishmentCost, uint replenisherReward); 619: event Liquidate(address indexed account, address indexed liquidator, uint repaidDebt, uint liquidatorReward);
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Market.sol
#0 - c4-judge
2022-11-08T00:29:49Z
0xean marked the issue as grade-b
๐ 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
19.0072 USDC - $19.01
The instances below point to the second+ access of a state variable within a function. Caching of a state variable replace 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.
There are 3 instances of this issue:
File: src/Market.sol /// @audit Cache `dbr`. Used 3 times in `forceReplenish` 560: uint deficit = dbr.deficitOf(user); 563: uint replenishmentCost = amount * dbr.replenishmentPriceBps() / 10000; 569: dbr.onForceReplenish(user, amount);
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Market.sol
File: src/Oracle.sol /// @audit Cache `feeds`. Used 3 times in `viewPrice` 82: uint price = feeds[token].feed.latestAnswer(); 85: uint8 feedDecimals = feeds[token].feed.decimals(); 86: uint8 tokenDecimals = feeds[token].tokenDecimals; /// @audit Cache `feeds`. Used 3 times in `getPrice` 116: uint price = feeds[token].feed.latestAnswer(); 119: uint8 feedDecimals = feeds[token].feed.decimals(); 120: uint8 tokenDecimals = feeds[token].tokenDecimals;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Oracle.sol
private
rather than public
for constants, saves gasIf 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
There are 1 instances of this issue:
File: src/DBR.sol 13: uint8 public constant decimals = 18;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/DBR.sol
payable
Marking a function as payable
reduces gas cost since the compiler does not have to check whether a payment was provided or not. This change will save around 21 gas per function call.
There are 15 instances of this issue:
File: src/DBR.sol 70: function claimOperator() public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/DBR.sol
File: src/Fed.sol 48: function changeGov(address _gov) public { 57: function changeSupplyCeiling(uint _supplyCeiling) public { 66: function changeChair(address _chair) public { 75: function resign() public { 86: function expansion(IMarket market, uint amount) public { 103: function contraction(IMarket market, uint amount) public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Fed.sol
File: src/Market.sol 203: function recall(uint amount) public { 212: function pauseBorrows(bool _value) public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Market.sol
File: src/Oracle.sol 66: function claimOperator() public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Oracle.sol
File: src/escrows/GovTokenEscrow.sol 43: function pay(address recipient, uint amount) public { 66: function delegate(address delegatee) public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/escrows/GovTokenEscrow.sol
File: src/escrows/INVEscrow.sol 59: function pay(address recipient, uint amount) public { 90: function delegate(address delegatee) public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/escrows/INVEscrow.sol
File: src/escrows/SimpleERC20Escrow.sol 36: function pay(address recipient, uint amount) public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/escrows/SimpleERC20Escrow.sol
<x> += <y>
costs more gas than <x> = <x> + <y>
for state variablesThere are 25 instances of this issue:
File: src/DBR.sol 360: _totalSupply += amount; 376: _totalSupply -= amount; 289: totalDueTokensAccrued += accrued; 172: balances[msg.sender] -= amount; 174: balances[to] += amount; 196: balances[from] -= amount; 198: balances[to] += amount; 362: balances[to] += amount; 374: balances[from] -= amount; 304: debts[user] += additionalDebt; 316: debts[user] -= repaidDebt; 332: debts[user] += replenishmentCost; 288: dueTokensAccrued[user] += accrued;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/DBR.sol
File: src/Fed.sol 92: globalSupply += amount; 111: globalSupply -= amount; 91: supplies[market] += amount; 110: supplies[market] -= amount;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Fed.sol
File: src/Market.sol 397: totalDebt += amount; 535: totalDebt -= amount; 568: totalDebt += replenishmentCost; 600: totalDebt -= repaidDebt; 395: debts[borrower] += amount; 534: debts[user] -= amount; 565: debts[user] += replenishmentCost; 599: debts[user] -= repaidDebt;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Market.sol
uint
s/int
s smaller than 32 bytes (256 bits) incurs overhead'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.' \ https://docs.soliditylang.org/en/v0.8.15/internals/layout_in_storage.html \ Use a larger size then downcast where needed
There are 12 instances of this issue:
File: src/DBR.sol 13: uint8 public constant decimals = 18; 220: uint8 v,
https://github.com/code-423n4/2022-10-inverse/tree/main/src/DBR.sol
File: src/Market.sol 422: function borrowOnBehalf(address from, uint amount, uint deadline, uint8 v, bytes32 r, bytes32 s) public { 486: function withdrawOnBehalf(address from, uint amount, uint deadline, uint8 v, bytes32 r, bytes32 s) public {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Market.sol
File: src/Oracle.sol 20: uint8 tokenDecimals; 53: function setFeed(address token, IChainlinkFeed feed, uint8 tokenDecimals) public onlyOperator { feeds[token] = FeedData(feed, tokenDecimals); } 85: uint8 feedDecimals = feeds[token].feed.decimals(); 86: uint8 tokenDecimals = feeds[token].tokenDecimals; 87: uint8 decimals = 36 - feedDecimals - tokenDecimals; 119: uint8 feedDecimals = feeds[token].feed.decimals(); 120: uint8 tokenDecimals = feeds[token].tokenDecimals; 121: uint8 decimals = 36 - feedDecimals - tokenDecimals;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Oracle.sol
Use if(x)
/if(!x)
instead of if(x == true)
/if(x == false)
.
There are 1 instances of this issue:
File: src/DBR.sol 350: require(minters[msg.sender] == true || msg.sender == operator, "ONLY MINTERS OR OPERATOR");
https://github.com/code-423n4/2022-10-inverse/tree/main/src/DBR.sol
require()
statements that use &&
saves gasInstead of using &&
on single require
check using two require
checks can save gas
There are 8 instances of this issue:
File: src/DBR.sol 249: require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
https://github.com/code-423n4/2022-10-inverse/tree/main/src/DBR.sol
File: 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"); 448: require(recoveredAddress != address(0) && recoveredAddress == from, "INVALID_SIGNER"); 512: require(recoveredAddress != address(0) && recoveredAddress == from, "INVALID_SIGNER");
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Market.sol
x <= y
with x < y + 1
, and x >= y
with x > y - 1
In the EVM, there is no opcode for >=
or <=
. When using greater than or equal, two operations are performed: >
and =
. Using strict comparison operators hence saves gas
There are 21 instances of this issue:
File: src/DBR.sol 171: require(balanceOf(msg.sender) >= amount, "Insufficient balance"); 195: require(balanceOf(from) >= amount, "Insufficient balance"); 224: require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); 329: require(deficit >= amount, "Amount > deficit"); 373: require(balanceOf(from) >= amount, "Insufficient balance");
https://github.com/code-423n4/2022-10-inverse/tree/main/src/DBR.sol
File: src/Fed.sol 93: require(globalSupply <= supplyCeiling); 107: require(amount <= supply, "AMOUNT TOO BIG"); 123: if(supply >= marketValue) return 0;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Fed.sol
File: src/Market.sol 162: require(_liquidationFactorBps > 0 && _liquidationFactorBps <= 10000, "Invalid liquidation factor"); 361: if(collateralBalance <= minimumCollateral) return 0; 378: if(collateralBalance <= minimumCollateral) return 0; 396: require(credit >= debts[borrower], "Exceeded credit limit"); 423: require(deadline >= block.timestamp, "DEADLINE_EXPIRED"); 462: require(limit >= amount, "Insufficient withdrawal limit"); 487: require(deadline >= block.timestamp, "DEADLINE_EXPIRED"); 533: require(debt >= amount, "Insufficient debt"); 562: require(deficit >= amount, "Amount > deficit"); 567: require(collateralValue >= debts[user], "Exceeded collateral value"); 582: if(credit >= debt) return 0; 595: require(repaidDebt <= debt * liquidationFactorBps / 10000, "Exceeded liquidation factor"); 607: if(escrow.balance() >= liquidationFee) {
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Market.sol
Solidity contracts have contiguous 32 bytes (256 bits) slots used in storage. By arranging the variables, it is possible to minimize the number of slots used within a contractโs storage and therefore reduce deployment costs.
There are 1 instances of this issue:
File: src/Market.sol 38: /// @audit Currently storage variables are packed in 15 slots but could fit in 14
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Market.sol
immutable
& constant
for state variables that do not change their valueThere are 2 instances of this issue:
File: src/DBR.sol 11: string public name; 12: string public symbol;
https://github.com/code-423n4/2022-10-inverse/tree/main/src/DBR.sol
require()/revert()
strings longer than 32 bytes cost extra gasEach extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas
There are 4 instances of this issue:
File: src/DBR.sol 63: require(newReplenishmentPriceBps_ > 0, "replenishment price must be over 0"); 326: require(markets[msg.sender], "Only markets can call onForceReplenish");
https://github.com/code-423n4/2022-10-inverse/tree/main/src/DBR.sol
File: src/Market.sol 76: require(_replenishmentIncentiveBps < 10000, "Replenishment incentive must be less than 100%"); 214: require(msg.sender == pauseGuardian || msg.sender == gov, "Only pause guardian or governance can pause");
https://github.com/code-423n4/2022-10-inverse/tree/main/src/Market.sol
#0 - c4-judge
2022-11-05T23:50:53Z
0xean marked the issue as grade-b