Platform: Code4rena
Start Date: 26/05/2022
Pot Size: $75,000 USDT
Total HM: 31
Participants: 71
Period: 7 days
Judge: GalloDaSballo
Total Solo HM: 18
Id: 126
League: ETH
Rank: 21/71
Findings: 3
Award: $967.85
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: xiaoming90
562.3122 USDT - $562.31
Judge has assessed an item in Issue #9 as Medium risk. The relevant finding follows: Centralized risk The operator address can mint arbitrary amount of tokens. In addition, operator can also burn tokens from third-party accounts.
If the private key of the owner or minter address is compromised, the attacker will be able to mint an unlimited amount of tokens, or burn from arbitrary addresses.
operator can mint and burn arbitrary tokens. I believe this is unnecessary and poses a serious centralization risk.
Recomendation:
Consider reduce centralization risks with timelock contracts. Affected source code:
VE3Token.sol#L26-L36 VeToken.sol#L23-L33 DepositToken.sol#L26-L36
#0 - JeeberC4
2022-07-28T19:50:56Z
Duplicate of #202
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x29A, 0xDjango, 0xNazgul, 0xf15ers, BouSalman, Chom, Deivitto, Dravee, ElKu, FSchmoede, Funen, GimelSec, Hawkeye, MiloTruck, Picodes, SecureZeroX, SmartSek, TerrierLover, WatchPug, _Adam, asutorufos, berndartmueller, c3phas, catchup, cccz, cogitoergosumsw, cryptphi, csanuragjain, delfin454000, dipp, ellahi, gzeon, hansfriese, horsefacts, hyh, kirk-baird, minhquanym, oyc_109, pauliax, reassor, robee, sashik_eth, shenwilly, simon135, sorrynotsorry, sseefried, unforgiven, xiaoming90, z3s
100.0277 USDT - $100.03
When BaseRewardPool.addExtraReward
is called, it is not checked that the address already exists in the extraRewards
array. This would allow the token to be duplicated and affect the economics of the project.
Affected source code:
It is not good practice to hardcode values, but if you are dealing with addresses much less, these can change between implementations, networks or projects, so it is convenient to remove these addresses from the source code.
Affected source code:
The pragma version used are:
pragma solidity 0.8.7; pragma solidity >=0.4.0; pragma solidity >=0.4.22 <0.9.0; pragma solidity >=0.5.0;
But recently solidity released a new version with important Bugfixes:
The first one is related to ABI-encoding nested arrays directly from calldata. You can find more information here.
The second bug is triggered in certain inheritance structures and can cause a memory pointer to be interpreted as a calldata pointer or vice-versa. We also have a dedicated blog post about this bug.
Apart from these, there are several minor bug fixes and improvements.
The minimum required version should be 0.8.14
The following methods have a lack checks if the received argument is an address, it's good practice in order to reduce human error to check that the address specified in the constructor or initialize is different than address(0)
.
Affected source code:
address(0)
:
Less than FEE_DENOMINATOR
:
It's possible to lose the ownership under specific circumstances.
Because an human error it's possible to set a new invalid owner. When you want to change the owner's address it's better to propose a new owner, and then accept this ownership with the new wallet.
Affected source code:
owner
:
feeManager
:
setPoolManager
:
The operator
address can mint
arbitrary amount of tokens. In addition, operator
can also burn
tokens from third-party accounts.
If the private key of the owner or minter address is compromised, the attacker will be able to mint
an unlimited amount of tokens, or burn
from arbitrary addresses.
operator
can mint
and burn
arbitrary tokens. I believe this is unnecessary and poses a serious centralization risk.
Recomendation:
Affected source code:
Although the contract has an owner, this cannot be changed, so if you want to delegate or sell the project, it could not be done while keeping the same contracts.
Recomendation:
Affected source code:
operator
can't be changed:
dao
can't be changed:
The following methods take all the user's balance and send it through transferFrom
, this call may fail, since transferFrom
extracts the balance from the previously approved allowance
, it is better to use the user's allowance
in order to avoid the unnecessary failure when both amounts are not the same. It's better to use allowance
instead of balanceOf
.
Affected source code:
#0 - jetbrain10
2022-06-15T21:07:28Z
high quality.
#1 - GalloDaSballo
2022-07-06T23:21:05Z
Valid Low
NC
Not valid as the compiler is set in truffle-config
Valid Low
NC
Valid Low, may raise based on other submissions
Disagree, this gives stronger security guarantees
Disagree, it's up to the caller to ensure they have the proper allowance
Overall a pretty good short and sweet report
3L, 2 NC
#2 - GalloDaSballo
2022-07-28T17:08:29Z
2L, 2NC After bumping Centralized Risk
#3 - GalloDaSballo
2022-07-28T17:09:21Z
TODO: Bump Centralized Risk
to Med, dup of M-10
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x29A, 0xKitsune, 0xNazgul, 0xf15ers, 0xkatana, Cityscape, Dravee, ElKu, FSchmoede, Funen, GalloDaSballo, Hawkeye, Kaiziron, MiloTruck, Randyyy, RoiEvenHaim, Ruhum, SecureZeroX, SmartSek, TerrierLover, TomJ, Tomio, WatchPug, Waze, _Adam, asutorufos, c3phas, catchup, cogitoergosumsw, delfin454000, ellahi, fatherOfBlocks, gzeon, hansfriese, horsefacts, jonatascm, minhquanym, oyc_109, pauliax, reassor, robee, sach1r0, saian, sashik_eth, simon135, z3s
305.51 USDT - $305.51
The following contracts are never used. It's an excellent idea to take them out to save gas and improve the codding style.
The contract ForceSend
is not used and it's very dangerous.
Affected source code:
Use constants for static computations 2**255
:
Use constant instead of storage for totalCliffs = 1000;
:
Cache static keccack256 bytes4(keccak256("set_rewards_receiver(address)"))
:
++i
costs less gas compared to i++
or i += 1
++i
costs less gas compared to i++
or i += 1
for unsigned integer, as pre-increment is cheaper (about 5 gas per iteration). This statement is true even with the optimizer enabled.
i++
increments i
and returns the initial value of i
. Which means:
uint i = 1; i++; // == 1 but i == 2
But ++i
returns the actual incremented value:
uint i = 1; ++i; // == 2 and i == 2 too, so no need for a temporary variable
In the first case, the compiler has to create a temporary variable (when used) for returning 1
instead of 2
I suggest using ++i
instead of i++
to increment the value of an uint variable. Same thing for --i
and i--
Affected source code:
It's compared a boolean value using == true
or == false
, instead of using the boolean value. NOT
opcode, it's cheaper to use EQUAL
or NOTEQUAL
when the value it's false, or just the value without == true
when it's true, because it will use less opcodes inside the VM.
Remove == true
in:
Change == false
by !
in:
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.
I suggest shortening the revert strings to fit in 32 bytes, or that using custom errors as described next (require pragma upgrade).
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)
Starting from Solidity v0.8.4, there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");), but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.
Custom errors are defined using the error statement, which can be used inside and outside of contracts (including interfaces and libraries).
Affected source code:
There are certain contract imports that are not used and can be removed.
using SafeERC20 for IERC20; using Address for address; using SafeMath for uint256;
Affected source code:
It's possible to avoid storage access a save gas using immutable
keyword for the following variables:
Affected source code:
Changing to immutable the variables _name
and _symbol
, the methods name
and symbol
could be pure:
Use delete
instead of set to default value (false
or 0
)
Affected source code:
Having a method that always returns the same value is not correct in terms of consumption, if you want to modify a value, and the method will perform a revert
in case of failure, it is not necessary to return a true
, since it will never be false
. It is less expensive not to return anything, and it also eliminates the need to double-check the returned value by the caller.
Affected source code:
Use storage
keyword for save gas in order to cache a storage pointer.
The recurring use of the same register reading from storage is a task that can be easily optimized by using storage
or memory
, a clear example is access to rewardTokenInfo[_rewardToken]
but it can be seen throughout all contracts.
Affected source code:
rewardTokenInfo[_rewardToken]
:
rewardData[_rewardsToken]
:
#0 - GalloDaSballo
2022-07-14T01:41:42Z
Test file, will ignore
Use constants for static computations 2**255: -> Valid, saves 10 gas (EXP) Use constant instead of storage for totalCliffs = 1000;: -> Saves 2.1k (saves more but it's on the warden to demonstrate) (SLOAD) Cache static keccack256 bytes4(keccak256("set_rewards_receiver(address)")): -> Saves 30 gas (SHA)
2140
5 per instance 5 * 34 = 170
3 gas per instance 3 * 9 = 27
Cannot quantify in lack of POC
Don't believe it will save gas as the compiler get's rid of them
Huge gas savings, in lack of further details will only give it one SLOAD per instance 3+ 5 + 6 + 5 + 5 + 4 + 5 + 3 + 1 + 1 + 2 40 * 2100 = 84000
Last 3 findings are missing POC so I can't quantify
#1 - GalloDaSballo
2022-07-14T01:42:22Z
86337 gas saved
#2 - GalloDaSballo
2022-07-28T20:31:14Z
TODO: Remove Stash Findings as contracts are out of scope
#3 - GalloDaSballo
2022-07-28T22:39:51Z
Removing gas from Out of Scope Contracts
5 * 8 -40
22 * 2100
-46200
Gas Saved in Scope: 40137