The _amount parameter to the withdraw and deposit functions should be checked not to be zero. Otherwise, the following if statements:
if (_amount > 0) {
Will not trigger, allowing an user to accidentially reset their deposit or withdraw request epoch and lock funds for another cycle.
This can also lead to the spam of events at the end of these functions which may cause issues with logging.
Use of msg.sender and _msgSender can result in wasted gas
Severity: Low/Gas
Depositors may look the the body of the MasterChef.sol#withdraw and MasterChef.sol#deposit function and note that it is possible to use meta transactions to interact with this contract due to the user of _msgSender. However, in the onlyDepositor modifier, msg.sender is used which would be the relayer, not the meta transaction originator. This may result in user confusion and frustration or wasted gas if the meta transaction functionality was not necessary.
function addRewards(uint256 _pid) public {
address mainPool = IRewardStaking(convexBooster)
.poolInfo(_pid)
.crvRewards;
if (rewards[_pid].length == 0) {
pids[IRewardStaking(convexBooster).poolInfo(_pid).lptoken] = _pid;
convexPool[_pid] = mainPool;
On ConvexStakingWrapper.sol#L94-85 there is a call made to RewardStaking(convexBooster).poolInfo(_pid). This makes an external call to the convexBooster contract. This same call is then performed just a couple lines down on line 98. Caching the result of this would reduce gas usage significantly for this function.
Avoid Interactions with External Contracts Where Possible
function _checkpoint(uint256 _pid, address _account) internal {
//if shutdown, no longer checkpoint in case there are problems
if (paused()) return;
uint256 supply = _getTotalSupply(_pid);
uint256 depositedBalance = _getDepositedBalance(_pid, _account);
IRewardStaking(convexPool[_pid]).getReward(address(this), true);
uint256 rewardCount = rewards[_pid].length;
for (uint256 i = 0; i < rewardCount; i++) {
_calcRewardIntegral(_pid, i, _account, depositedBalance, supply);
}
}
In the _checkpoint function, if a user does not have any depositedBalance, then they will not have any rewards that need to be issued. This is true since when a user calls the withdraw function (the only function which decreases the user's deposited balance) that function will perform a _checkpoint call which will transfer the user's rewards to be claimed. As such, the deposit amount cannot ever be 0 and there still be rewards left to be claimed.
As such, adding an if statement check whether there is a depositedBalance before reaching out to the convexPool will save the initial gas cost when first creating a deposit, which will decrease the friction of using Concur for the first time.
Unchecked Math
Severity: Gas
https://github.com/code-423n4/2022-02-concur/blob/main/contracts/ConvexStakingWrapper.sol#L261
It is possible to put ConvexStakingWrapper.sol#L261 inside the unchecked directive.
This is due to the fact that on line 259 we see that the request.amount much be greater-than or equal to the requested _amount. This request.amount is limited by the deposits[_pid][msg.sender].amount in the requestWithdraw function. The only way for the deposits[_pid][msg.sender].amount to decrease is via the withdraw function. This withdraw function also deletes the withdrawRequest on completion and is nonReentrant. As a result, it is impossible for _amount to be greater than deposits[_pid][msg.sender].amount.
However, if you do decide to make this code optimization, I'd recommend commenting around it explaining the above as it does add complexity to the code and a modification elsewhere could turn this into a vulnerability.
Missing 0 Address Check
Severity: Low https://github.com/code-423n4/2022-02-concur/blob/main/contracts/ConcurRewardPool.sol#L16
The
rewardNotifier
is a crucial parameter that should be zero address checked.Missing input validation for _amount in deposit and withdraw
Severity: Low https://github.com/code-423n4/2022-02-concur/blob/main/contracts/ConvexStakingWrapper.sol#L236 https://github.com/code-423n4/2022-02-concur/blob/main/contracts/ConvexStakingWrapper.sol#L262
The
_amount
parameter to thewithdraw
anddeposit
functions should be checked not to be zero. Otherwise, the followingif
statements:Will not trigger, allowing an user to accidentially reset their deposit or withdraw request epoch and lock funds for another cycle. This can also lead to the spam of events at the end of these functions which may cause issues with logging.
Use of msg.sender and _msgSender can result in wasted gas
Severity: Low/Gas Depositors may look the the body of the
MasterChef.sol#withdraw
andMasterChef.sol#deposit
function and note that it is possible to use meta transactions to interact with this contract due to the user of_msgSender
. However, in theonlyDepositor
modifier,msg.sender
is used which would be the relayer, not the meta transaction originator. This may result in user confusion and frustration or wasted gas if the meta transaction functionality was not necessary.Public function should be External
Severity: Gas Since the following functions are not called from within their own contract, the visibility quantifier shoudl be set to
external
to save on gas. https://github.com/code-423n4/2022-02-concur/blob/main/contracts/ConvexStakingWrapper.sol#L93Cache calls to External Contracts
Severity: Gas https://github.com/code-423n4/2022-02-concur/blob/main/contracts/ConvexStakingWrapper.sol#L93-L99
On ConvexStakingWrapper.sol#L94-85 there is a call made to
RewardStaking(convexBooster).poolInfo(_pid)
. This makes an external call to the convexBooster contract. This same call is then performed just a couple lines down on line 98. Caching the result of this would reduce gas usage significantly for this function.Avoid Interactions with External Contracts Where Possible
Severity: Gas https://github.com/code-423n4/2022-02-concur/blob/main/contracts/ConvexStakingWrapper.sol#L209-L222
In the
_checkpoint
function, if a user does not have anydepositedBalance
, then they will not have any rewards that need to be issued. This is true since when a user calls thewithdraw
function (the only function which decreases the user's deposited balance) that function will perform a_checkpoint
call which will transfer the user's rewards to be claimed. As such, the deposit amount cannot ever be 0 and there still be rewards left to be claimed.As such, adding an
if
statement check whether there is adepositedBalance
before reaching out to the convexPool will save the initial gas cost when first creating a deposit, which will decrease the friction of using Concur for the first time.Unchecked Math
Severity: Gas https://github.com/code-423n4/2022-02-concur/blob/main/contracts/ConvexStakingWrapper.sol#L261 It is possible to put ConvexStakingWrapper.sol#L261 inside the
unchecked
directive. This is due to the fact that on line 259 we see that therequest.amount
much be greater-than or equal to the requested_amount
. Thisrequest.amount
is limited by thedeposits[_pid][msg.sender].amount
in therequestWithdraw
function. The only way for thedeposits[_pid][msg.sender].amount
to decrease is via the withdraw function. This withdraw function also deletes the withdrawRequest on completion and isnonReentrant
. As a result, it is impossible for_amount
to be greater thandeposits[_pid][msg.sender].amount
.However, if you do decide to make this code optimization, I'd recommend commenting around it explaining the above as it does add complexity to the code and a modification elsewhere could turn this into a vulnerability.
External Calls in Modifiers
https://github.com/code-423n4/2022-02-concur/blob/229b6188a2c5867f2d0cb4579d0a0d49516da252/contracts/ConvexStakingWrapper.sol#L73-L79 The
whenNotInShelter
modifier makes two external calls to verify whether a_pid
is in the shelter or not. This costs a tremendous amount of gas. I'd recommend simply creating a mapping for_pid
toinShelter
and modifying it as required. This will save a lot of gas every time a function using thewhenNotInShelter
modifier is used.