Platform: Code4rena
Start Date: 23/02/2024
Pot Size: $92,000 USDC
Total HM: 0
Participants: 47
Period: 10 days
Judge: 0xTheC0der
Id: 336
League: ETH
Rank: 7/47
Findings: 1
Award: $5,987.35
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: CodeWasp
Also found by: 0xdice91, 0xlemon, Aamir, Al-Qa-qa, AlexCzm, BAHOZ, Bauchibred, Breeje, DadeKuma, Fassi_Security, PetarTolev, Shield, SpicyMeatball, Trust, ZanyBonzy, cheatc0d3, gesha17, haxatron, imare, jesjupyter, kutugu, lsaudit, marchev, merlinboii, nnez, osmanozdemir1, peanuts, radev_sw, twicek, visualbits
5987.3522 USDC - $5,987.35
Overview
Risk Rating | Number of issues |
---|---|
Low Risk | 10 |
Informational | 2 |
Table of Contents
deadline
)address _pool
in claimFees()
stakeOnBehalf()
can be called with amount == 0
on any _beneficiary
and _delegatee
deadline == 0
not handled in permit functionsstakeOnBehalf()
's Natspec comment and behaviorsetRewardNotifier()
isn't protected like setAdmin
alterBeneficiary()
can be called with the old deposit.beneficiary
_alterDelegatee()
can be called with the old deposit.delegatee
stakeMore()
with 0 is validThe permitAndStake
function and permitAndStakeMore
functions will not work for ERC1271 smart contract signature verification since the UNI permit function only supports EOA signatures.
But the stakeOnBehalf
, stakeMoreOnBehalf
, alterDelegateeOnBehalf
, alterBeneficiaryOnBehalf
, withdrawOnBehalf
and claimRewardOnBehalf
support the ERC1271 signatures, thus enabling the smart contract wallets to work with the protocol.
Hence the users should be informed of this difference. Else a normal user with a smart contract wallet might try to call the permitAndStake
with a valid signature but the transaction will revert.
deadline
)In the stakeOnBehalf()
function, it's recommended to have a deadline to ensure that the signature hasn't expired for the transaction to execute successfully.
Currently there is no deadline and the signature is valid forever. Hence if the signer is a EOA and needs to revoke the transaction he is unable to do that until the signature is used. Hence recommended to include the deadline in the data hash to be signed.
The same issue exists in the stakeMoreOnBehalf
, alterDelegateeOnBehalf
, withdrawOnBehalf
and claimRewardOnBehalf
functions
address _pool
in claimFees()
It's possible to input any arbitrary _pool
in claimFees()
.
If it's a malicious one, the only effect seem to be losing an amount of payoutAmount
of PAYOUT_TOKEN
from the caller to the REWARD_RECEIVER
(basically, a phishing attack without profit, only making the users lose funds).
Instead of letting any caller input an arbitrary pool, it'd be great to instead change the input to the pair of tokens (token0
and token1
) so that the real pool
's address could be fetched from the Factory
stakeOnBehalf()
can be called with amount == 0
on any _beneficiary
and _delegatee
With a malicious depositor
verifying any ERC1271 signatures, it's possible to call stakeOnBehalf()
with amount == 0
and any _beneficiary
or _delegatee
.
This could either pollute them or be used as a step in a phishing attack.
Consider validating against amount == 0
deadline == 0
not handled in permit functionsIn the permitAndStake
and permitAndStakeMore
functions, the STAKE_TOKEN.permit()
function is called which would call the permit
function of the UNI
token.
But if the deadline
is set to 0
for the above function call, the transaction will revert because the UNI
token's deadline check is as follows:
require(now <= deadline, "Uni::permit: signature expired");
It should be noted that the permit
function of some of the ERC20 Tokens (like DAI) support the zero deadline as follows:
require(deadline == 0 || now <= deadline, "Uni::permit: signature expired");
But this is not the case with the UNI token. Hence, the users may be it is recommended inform the users of this behaviour and request them to use a valid deadline when they are calling the permitAndStake
and permitAndStakeMore
functions.
If not, the transactions will revert for deadline == 0
even if the signature is valid.
stakeOnBehalf()
's Natspec comment and behaviorPlease consider the following scenario:
stakeOnBehalf()
function has the following natspec comment explaining the objective of the function.
/// @notice Stake tokens to a new deposit on behalf of a user, using a signature to validate the /// user's intent. The caller must pre-approve the staking contract to spend at least the /// would-be staked amount of the token.
Notice the The caller must pre-approve
part. This indicates that the caller is depositing his tokens on behalf of the depositor address.
However, the actual stakeOnBehalf()
function implementation uses a user-input address _depositor
:
_depositId = _stake(_depositor, _amount, _delegatee, _beneficiary);
The _stake()
function calls the _stakeTokenSafeTransferFrom()
function where tokens are transferred from the depositor
address and not from the msg.sender
. If the depositor
has not pre-approved the tokens then the transaction reverts.
_stakeTokenSafeTransferFrom(_depositor, address(_surrogate), _amount);
Hence either the natspec comments is wrong or the function implementation is wrong.
If the caller
is depositing his tokens on behalf of the depositor
then the msg.sender
should also be passed into the _stake()
function in addition to the depositor
address (function overloading on _stake()
) and the _stakeTokenSafeTransferFrom()
should be called on the msg.sender
.
Otherwise, the natspec comment needs to be changed to indicate that the depositor
should approve the staking contract with the tokens. In which case caller is not transferring his own tokens on behalf of the depositor but only executing the deposit operation.
setRewardNotifier()
isn't protected like setAdmin
_setAdmin()
has a _revertIfAddressZero()
function.
Therefore, we should have a similar address(0)
protection on setRewardNotifier()
.
Indeed, isRewardNotifier[address(0)] = true;
is a reachable state right now.
alterBeneficiary()
can be called with the old deposit.beneficiary
In the _alterBeneficiary()
function, it should be checked that _newBeneficiary != deposit.beneficiary
.
Notice that, in the fuzzed tests, the input is forbidden, as it is known to be implicitly invalid:
vm.assume(_newBeneficiary != address(0) && _newBeneficiary != _firstBeneficiary);
Consider explicitly validating against the invalid input so that the function can respect the specs (actually altering the beneficiary).
_alterDelegatee()
can be called with the old deposit.delegatee
In the _alterDelegatee()
function, it should be checked that _newDelegatee != deposit.delegatee
.
Notice that, in the fuzzed tests, the input is forbidden, as it is known to be implicitly invalid:
vm.assume(_newDelegatee != address(0) && _newDelegatee != _firstDelegatee);
Consider explicitly validating against the invalid input so that the function can respect the specs (actually altering the delegatee).
stakeMore()
with 0 is validWe can call stakeMore()
with _amount == 0
(the UNI token doesn't revert on transferring 0).
Consider validating against amount == 0
so that the function can respect the specs (actually staking more).
event StakeDeposited
File: UniStaker.sol 36: event StakeDeposited( - 37: address owner, DepositIdentifier indexed depositId, uint256 amount, uint256 depositBalance + 37: address indexed owner, DepositIdentifier indexed depositId, uint256 amount, uint256 depositBalance 38: );
_revertIfNotAdmin
with a modifier_revertIfNotAdmin()
is used in setAdmin()
and setRewardNotifier()
instead of a modifier.
Consider using a real modifier instead.
#0 - c4-judge
2024-03-14T14:10:25Z
MarioPoneder marked the issue as grade-b
#1 - udsene
2024-03-16T19:27:10Z
Hi @MarioPoneder,
The QA-1.2
of the report is a duplicate of #205 and QA-1.3
seems to be a duplicate of #380. And QA-1.4
, QA-1.8
, QA-1.9
, QA-1.10
of the report are related to #388.
And all three of above issues are currently graded a
.
And if #34 (currently escalated) is downgraded to a grade-a QA
then it will add as another grade-a
finding (#66) in this QA report.
Further to the above QA-1.1
is related to #4 (mentioning here since #4 has been escalated as of now).
I wanted to bring your attention to above.
Thank You
#2 - c4-judge
2024-03-18T01:02:52Z
MarioPoneder marked the issue as grade-a