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 Jun 2, 2024. It is now read-only.
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelHighA valid High severity issueRewardA payout will be made for this issue
In case if reservedUntilTokenId > 99, then owner will never get token vested
Summary
In case if reservedUntilTokenId > 99, then _getNextTokenId function will return early without doing modulo operation and as result tokenRecipient will be set with value bigger than 99 for owner, which means that this tokens will not be never vested.
Vulnerability Detail
In case if dao decided to move from l1 to l2, then they will deploy new dao on l2 using L2MigrationDeployer and that they will provide reservedUntilTokenId param to the Token contract as the amount of already minted tokens on l1, to not allow minting with same ids.
During Token initialization _addFounders function will be called, which will set vesting positions of owners to the tokenRecipient mapping.
uint256 baseTokenId = reservedUntilTokenId;
// For each token to vest:for (uint256 j; j < founderPct; ++j) {
// Get the available token id
baseTokenId =_getNextTokenId(baseTokenId);
// Store the founder as the recipient
tokenRecipient[baseTokenId] = newFounder;
emitMintScheduled(baseTokenId, founderId, newFounder);
// Update the base token id
baseTokenId = (baseTokenId + schedule) %100;
}
}
Here, we need to note that baseTokenId == reservedUntilTokenId and we need to look how next baseTokenId is calculated in the _getNextTokenId function.
function _getNextTokenId(uint256_tokenId) internalviewreturns (uint256) {
unchecked {
while (tokenRecipient[_tokenId].wallet !=address(0)) {
_tokenId = (++_tokenId) %100;
}
return _tokenId;
}
}
The purpose of this function is to check if tokenRecipient[_tokenId] is empty. In case if it is, then this means that this position doesn't belong to anyone else and can be used, otherwise _tokenId is incremented and moduled as we work with positions 0-99 only.
The problem is that in case if reservedUntilTokenId is bigger than 99, which should be the case almost 100% of migrations, then _tokenId inside _getNextTokenId will not be increased and moduled for the first founder, it will be returned as it is(for example 1500). And then this 1500 position will be stored into tokenRecipient.
This means that founder have lost his vesting position, as when tokens are minted, then they are moduled to know if token should belong to founder.
So in case if i am first owner with 1% share that means that i will have nothing vested to me after migration, in case if reservedUntilTokenId > 99.
Note, this will affect only first founder and only 1 out of his total percents. All other founders will get correct postions.
Impact
Founder loses vesting nfts
Code Snippet
Provided above
Tool used
Manual Review
Recommendation
Change to this code.
- uint256 baseTokenId = reservedUntilTokenId;+ uint256 baseTokenId = reservedUntilTokenId % 100;
// For each token to vest:
for (uint256 j; j < founderPct; ++j) {
// Get the available token id
baseTokenId = _getNextTokenId(baseTokenId);
// Store the founder as the recipient
tokenRecipient[baseTokenId] = newFounder;
emit MintScheduled(baseTokenId, founderId, newFounder);
// Update the base token id
baseTokenId = (baseTokenId + schedule) % 100;
}
}
1 comment(s) were left on this issue during the judging contest.
Aamirusmani1552 commented:
(Important:)I have created this class as I have couldn't focus on Migration Part of the audit. I joined the actual audit as well and there also I couldn't focus on it. I am not sure which part of it could make the actual problem. So all issues related to migration are put in this class. I marked the one that I think could be the actual issue as a best report. Please go through each one of them properly. Thanks.
sherlock-admin
changed the title
Uneven Clay Rook - In case if reservedUntilTokenId > 99, then owner will never get token vested
rvierdiiev - In case if reservedUntilTokenId > 99, then owner will never get token vested
Dec 13, 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` labelHighA valid High severity issueRewardA payout will be made for this issue
rvierdiiev
high
In case if reservedUntilTokenId > 99, then owner will never get token vested
Summary
In case if reservedUntilTokenId > 99, then
_getNextTokenId
function will return early without doing modulo operation and as resulttokenRecipient
will be set with value bigger than 99 for owner, which means that this tokens will not be never vested.Vulnerability Detail
In case if dao decided to move from l1 to l2, then they will deploy new dao on l2 using
L2MigrationDeployer
and that they will providereservedUntilTokenId
param to the Token contract as the amount of already minted tokens on l1, to not allow minting with same ids.During
Token
initialization_addFounders
function will be called, which will set vesting positions of owners to thetokenRecipient
mapping.https://github.com/sherlock-audit/2023-09-nounsbuilder/blob/main/nouns-protocol/src/token/Token.sol#L161-L176
Here, we need to note that
baseTokenId == reservedUntilTokenId
and we need to look how nextbaseTokenId
is calculated in the_getNextTokenId
function.https://github.com/sherlock-audit/2023-09-nounsbuilder/blob/main/nouns-protocol/src/token/Token.sol#L186-L194
The purpose of this function is to check if
tokenRecipient[_tokenId]
is empty. In case if it is, then this means that this position doesn't belong to anyone else and can be used, otherwise_tokenId
is incremented and moduled as we work with positions 0-99 only.The problem is that in case if
reservedUntilTokenId
is bigger than 99, which should be the case almost 100% of migrations, then_tokenId
inside_getNextTokenId
will not be increased and moduled for the first founder, it will be returned as it is(for example 1500). And then this 1500 position will be stored intotokenRecipient
.This means that founder have lost his vesting position, as when tokens are minted, then they are moduled to know if token should belong to founder.
So in case if i am first owner with 1% share that means that i will have nothing vested to me after migration, in case if
reservedUntilTokenId > 99
.Note, this will affect only first founder and only 1 out of his total percents. All other founders will get correct postions.
Impact
Founder loses vesting nfts
Code Snippet
Provided above
Tool used
Manual Review
Recommendation
Change to this code.
Duplicate of #42
The text was updated successfully, but these errors were encountered: