Platform: Code4rena
Start Date: 31/03/2022
Pot Size: $75,000 USDC
Total HM: 7
Participants: 42
Period: 7 days
Judge: Jack the Pug
Total Solo HM: 5
Id: 102
League: ETH
Rank: 5/42
Findings: 3
Award: $2,210.90
🌟 Selected for report: 0
🚀 Solo Findings: 0
1940.0195 USDC - $1,940.02
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/RateLimited.sol#L141
_setBufferCap() function sets the bufferCap. It first calls the _updateBufferStored() function which updates the bufferStored variable. However, calculation of bufferStored depends on bufferCap value: Math.min(bufferStored + (rateLimitPerSecond * elapsed), bufferCap); Therefore, first bufferCap should be set to newBufferCap and then _updateBufferStored() should be called.
Let's say _setBufferCap() is called with newBufferCap=8000 and oldBufferCap was 10000. Also let's suppose _updateBufferStored() at line 142 sets bufferStored to 900. In this case bufferStored > bufferCap, which should not happen.
Manual analysis
Call _updateBufferStored() after bufferCap is set with the new value:
function _setBufferCap(uint256 newBufferCap) internal { uint256 oldBufferCap = bufferCap; bufferCap = newBufferCap; _updateBufferStored(); emit BufferCapUpdate(oldBufferCap, newBufferCap); }
#0 - ElliotFriedman
2022-04-07T17:01:35Z
This assumes the admin or governor is malicious which is not a correct assumption to make as if the governor is malicious, the whole system is compromised. This is low severity, low risk.
#1 - jack-the-pug
2022-04-21T03:17:25Z
I believe this is a valid issue and it does not assume the admin is malicious. It's just saying the fact the implementation will not work as expected in certain circumstances.
I tend to keep it as Med
.
#2 - jack-the-pug
2022-04-21T03:39:05Z
#29 had this issue explained better.
🌟 Selected for report: rayn
Also found by: 0xDjango, 0xkatana, 0xkowloon, BouSalman, CertoraInc, Dravee, Funen, Hawkeye, IllIllI, Jujic, Kenshin, Kthere, Meta0xNull, Sleepy, TerrierLover, async, aysha, berndartmueller, catchup, cccz, cmichel, csanuragjain, danb, defsec, georgypetrov, hake, hubble, kenta, kyliek, pauliax, rfa, robee, sahar, shenwilly, teryanarmen
125.7841 USDC - $125.78
C4 finding submitted: (risk = 1 (Low Risk)) Missing non-zero address check
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/core/Permissions.sol#L58 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/core/Permissions.sol#L64 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/core/Permissions.sol#L70 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/core/Permissions.sol#L80
Zero address check should be included especially when changing critical addresses.
Manual analysis
Add zero address check
C4 finding submitted: (risk = 1 (Low Risk)) Init function is susceptible to front-running
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/core/Core.sol#L20
Init function is susceptible to front-running by an attacker, in which case the contract deployment would be useless.
https://github.com/code-423n4/2022-01-livepeer-findings/issues/112
Manual analysis
Add a control to init() so that only the deployer can call it.
C4 finding submitted: (risk = 1 (Low Risk)) Modifier may not work as expected
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/refs/CoreRef.sol#L29
If a modifier does not execute _ or revert, the function using that modifier will return the default value causing unexpected behaviour. Therefore, in the case of if condition executes false, a function using this modifier would return the default value of the return variable type.
https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-modifier
Manual analysis
All the paths in a modifier must execute _ or revert.
C4 finding submitted: (risk = 0 (Non-critical)) Function return data type different than the underlying variable data type
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L230 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L240 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L250
Functions return values are uint256, whereas the actual underlying data types are uint112 or uint32.
Manual analysis
Change the return values to match the underlying data types.
C4 finding submitted: (risk = 0 (Non-critical)) Missing non-zero amount check
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/GlobalRateLimitedMinter.sol#L47 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/RateLimited.sol#L95 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/volt/Volt.sol#L47 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/volt/Volt.sol#L53 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/volt/Volt.sol#L63
It is a good practice to apply non-zero check for amount parameter in functions such as mint, burn, etc. These functions emit events, so if the amount is zero emitted event would be unnecessary. Also, the whole execution can be avoided in such cases which would save gas.
Manual analysis
Apply non-zero amount check where necessary
C4 finding submitted: (risk = 0 (Non-critical)) _replenishBuffer() is missing an event
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/RateLimited.sol#L117
_replenishBuffer() should emit an event since _depleteBuffer() does
Manual analysis
Add the appropriate event
C4 finding submitted: (risk = 0 (Non-critical)) Potential approve race condition
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/volt/Volt.sol#L94
Controlling allowances through approve() function can be susceptible to front-running attacks.
https://swcregistry.io/docs/SWC-114
Manual analysis
Consider using increaseAllowance() and decreaseAllowance()
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0xNazgul, 0xkatana, 0xkowloon, CertoraInc, Dravee, Funen, Hawkeye, Jujic, Kenshin, Meta0xNull, Sleepy, TerrierLover, catchup, csanuragjain, defsec, georgypetrov, kenta, okkothejawa, rayn, rfa, robee, saian, samruna
145.0996 USDC - $145.10
Error messages longer than 32 bytes
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/refs/CoreRef.sol#L48 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/refs/CoreRef.sol#L56 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/refs/CoreRef.sol#L64 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/refs/CoreRef.sol#L72 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/refs/CoreRef.sol#L82 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/pcv/compound/ERC20CompoundPCVDeposit.sol#L36 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L58 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L68 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L85 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L107 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L146 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L150 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L155 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L268 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L272 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L299 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L303 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L307 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L337 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/peg/NonCustodialPSM.sol#L117 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/peg/NonCustodialPSM.sol#L123 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/peg/NonCustodialPSM.sol#L239 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/peg/NonCustodialPSM.sol#L277 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/peg/NonCustodialPSM.sol#L402 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/peg/NonCustodialPSM.sol#L415 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/peg/NonCustodialPSM.sol#L428 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/peg/NonCustodialPSM.sol#L441 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/peg/NonCustodialPSM.sol#L445 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/core/Permissions.sol#L31 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/core/Permissions.sol#L39 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/core/Permissions.sol#L31 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/RateLimited.sol#L48 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/RateLimited.sol#L64 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/RateLimited.sol#L103 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/oracle/ScalingPriceOracle.sol#L141 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/oracle/ScalingPriceOracle.sol#L177
Error strings longer than 32 bytes are more expensive.
Manual analysis
Limit the error strings to 32 bytes max.
Operations can be made unchecked
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/MultiRateLimited.sol#L340 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/RateLimited.sol#L106 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/Timed.sol#L57
These operations can not underflow, so they can be made unchecked to save gas
Manual analysis
Make these operations unchecked
Public state variables can be made private
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/volt/Volt.sol#L11 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/volt/Volt.sol#L13 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/volt/Volt.sol#L15
DOMAIN_SEPARATOR, PERMIT_TYPEHASH and nonces are used only in Volt.sol, hence can be made private. Saves ~4K gas.
Remix
Change their visibility to private.
Move the require check up
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/RateLimited.sol#L103
newBuffer non-zero check can be made right after reading buffer, so that if require fails the operations in between would be avoided.
Manual analysis
Move the require to line 97.
Public state variables can be made internal
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/RateLimited.sol#L11 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/RateLimited.sol#L14 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/RateLimited.sol#L17 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/RateLimited.sol#L20 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/RateLimited.sol#L23
MAX_RATE_LIMIT_PER_SECOND, rateLimitPerSecond, lastBufferUsedTime, bufferCap, doPartialAction can be changed to internal.
Manual analysis
Change their visibility to internal.
Constant keccak variables can be changed to immutable
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/core/Permissions.sol#L10 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/core/Permissions.sol#L11 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/core/Permissions.sol#L12 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/core/Permissions.sol#L15
If these variables are constant, keccak operation will be performed whenever the variable is used. However, if they are immutable, keccak hashing would be performed just once within the constructor.
https://github.com/code-423n4/2021-10-slingshot-findings/issues/3
Manual analysis
Change the code as: bytes32 public immutable override BURNER_ROLE; bytes32 public immutable override MINTER_ROLE; bytes32 public immutable override PCV_CONTROLLER_ROLE; bytes32 public constant override GOVERN_ROLE = keccak256("GOVERN_ROLE"); bytes32 public immutable override GUARDIAN_ROLE;
constructor() { // Appointed as a governor so guardian can have indirect access to revoke ability _setupGovernor(address(this)); _setRoleAdmin(MINTER_ROLE = keccak256("MINTER_ROLE"), GOVERN_ROLE); _setRoleAdmin(BURNER_ROLE = keccak256("BURNER_ROLE"), GOVERN_ROLE); _setRoleAdmin(PCV_CONTROLLER_ROLE = keccak256("PCV_CONTROLLER_ROLE"), GOVERN_ROLE); _setRoleAdmin(GOVERN_ROLE, GOVERN_ROLE); _setRoleAdmin(GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE"), GOVERN_ROLE); }
Bool can be changed to uint256
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/utils/RateLimited.sol#L23 https://github.com/code-423n4/2022-03-volt/blob/main/contracts/refs/OracleRef.sol#L25
Booleans are more expensive than uint256 or any type that takes up a full word because each write operation emits an extra SLOAD to first read the slot's contents, replace the bits taken up by the boolean, and then write back. This is the compiler's defense against contract upgrades and pointer aliasing, and it cannot be disabled.
Manual analysis
Consider changing bools to uint256
Unused modifier
https://github.com/code-423n4/2022-03-volt/blob/main/contracts/refs/CoreRef.sol#L29
This modifier is used nowhere.
Manual analysis
Delete the modifier if it is not going to be used.