You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Mar 3, 2024. It is now read-only.
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelMediumA valid Medium severity issueRewardA payout will be made for this issue
the stake flow on gpToke being mandatory on rewarder potentially brick staker's withdrawal from DestinationVault
Summary
the stake flow on gpToke being mandatory on AbstractRewarder potentially brick staker's withdrawal
Vulnerability Detail
Let's look at the withdrawal flow (withdrawUnderlying/withdrawBaseAsset)
invoke _burn(msg.sender, shares)
invoke _beforeTokenTransfer, since _burn would transfer the token from the user to address(0)
invoke _rewarder.withdraw(from, amount, true);, where claim(the last input) is set to true
Over to MainRewarder, withdraw would trigger _processRewards if claim is set to true, which is indeed the case.
_processRewards would call _getReward
Over to AbstractRewarders, _getReward would check if the rewardToken is TOKE, if so and a non-zero lcokDuration is set, the workflow would attempt to help the user to lock the TOKE reward, calling the function gptoke.stake
Over to GPToke, stake would call _stake which has 3 constraints:
A: amount cannot be smaller than 10_000
B: amount cannot be larger than 100m * 1e18
C: the GPToke cannot be paused.
GPToken::_stake
function _stake(uint256amount, uint256duration, addressto) internal whenNotPaused {
//// validation checks//if (to ==address(0)) revertZeroAddress();
if (amount < MIN_STAKE_AMOUNT) revertStakingAmountInsufficient();
if (amount > MAX_STAKE_AMOUNT) revertStakingAmountExceeded();
// duration checked inside previewPoints
(uint256points, uint256end) =previewPoints(amount, duration);
if (points +totalSupply() >type(uint192).max) {
revertStakingPointsExceeded();
}
// checkpoint rewards for caller_collectRewards(to, false);
// save information for current lockup
lockups[to].push(Lockup({ amount: uint128(amount), end: uint128(end), points: points }));
// create points for user_mint(to, points);
emitStake(to, lockups[to].length-1, amount, end, points);
// transfer staked toke in
toke.safeTransferFrom(msg.sender, address(this), amount);
}
MainRewarder
function withdraw(addressaccount, uint256amount, boolclaim) public onlyStakeTracker {
_updateReward(account);
_withdraw(account, amount);
for (uint256 i =0; i < extraRewards.length; ++i) {
IExtraRewarder(extraRewards[i]).withdraw(account, amount);
}
if (claim) {
_processRewards(account, true);
}
}
DestinationVault
function withdrawUnderlying(uint256shares, addressto) external onlyLMPVault returns (uint256amount) {
Errors.verifyNotZero(shares, "shares");
Errors.verifyNotZero(to, "to");
amount = shares;
emitUnderlyingWithdraw(amount, msg.sender, to);
// Does a balance check, will revert if trying to burn too much_burn(msg.sender, shares);
_ensureLocalUnderlyingBalance(amount);
IERC20(_underlying).safeTransfer(to, amount);
}
...
function _beforeTokenTransfer(addressfrom, addressto, uint256amount) internalvirtualoverride {
if (from == to) {
return;
}
if (from !=address(0)) {
_rewarder.withdraw(from, amount, true);
}
}
Impact
stakers who participate in LMPVault which contributes to DestinationVault where rewardToken is Toke can be bricked from withdrawal when GPToke is paused from accepting new stakes, or not enough TOKE is sent in timely as reward to reach the minimum threshold for the staker.
While the minimum requirement(10_000) is fairly negligible, it may still affect stakers who :
1.) staker who just stake for a very short period of time (1 block).
2.) staker who just have a very small amount of deposit.
3.) pausing GPToken would pause withdrawal in DestinationVault, thus GMTVault too.
sherlock-admin
changed the title
Cuddly Fern Gecko - the stake flow on gpToke being mandatory on rewarder potentially brick staker's withdrawal from DestinationVault
0xGoodess - the stake flow on gpToke being mandatory on rewarder potentially brick staker's withdrawal from DestinationVault
Oct 3, 2023
Sign up for freeto subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Labels
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelMediumA valid Medium severity issueRewardA payout will be made for this issue
0xGoodess
medium
the stake flow on gpToke being mandatory on rewarder potentially brick staker's withdrawal from DestinationVault
Summary
the stake flow on gpToke being mandatory on AbstractRewarder potentially brick staker's withdrawal
Vulnerability Detail
Let's look at the withdrawal flow (withdrawUnderlying/withdrawBaseAsset)
_burn(msg.sender, shares)
_beforeTokenTransfer
, since_burn
would transfer the token from the user to address(0)_rewarder.withdraw(from, amount, true);
, whereclaim
(the last input) is set totrue
_processRewards
if claim is set to true, which is indeed the case._processRewards
would call_getReward
_getReward
would check if the rewardToken isTOKE
, if so and a non-zero lcokDuration is set, the workflow would attempt to help the user to lock the TOKE reward, calling the functiongptoke.stake
_stake
which has 3 constraints:10_000
100m * 1e18
GPToken::_stake
MainRewarder
DestinationVault
Impact
stakers who participate in LMPVault which contributes to DestinationVault where rewardToken is
Toke
can be bricked from withdrawal when GPToke is paused from accepting new stakes, or not enough TOKE is sent in timely as reward to reach the minimum threshold for the staker.While the minimum requirement(10_000) is fairly negligible, it may still affect stakers who :
1.) staker who just stake for a very short period of time (1 block).
2.) staker who just have a very small amount of deposit.
3.) pausing GPToken would pause withdrawal in DestinationVault, thus GMTVault too.
Code Snippet
https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/DestinationVault.sol#L341
Tool used
Manual Review
Recommendation
There are 2 possible ways to work around this:
claim
input in_beforeTransfer
to be false, so that user need to claim their only claimable after withdrawal.Duplicate of #565
The text was updated successfully, but these errors were encountered: