-
Notifications
You must be signed in to change notification settings - Fork 26
Feat: Funding Pot #741
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat: Funding Pot #741
Conversation
| } | ||
|
|
||
| if (block.timestamp > round_.roundStart) { | ||
| revert Module__LM_PC_FundingPot__RoundAlreadyStarted(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit of a nitpick, but since this will also trigger if somebody tries to edit an already finished round, maybe we can change the name (or add an extra error if size allows for it?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't really come up with a more descriptive error since the idea is that the admin can edit as long as a round hasn't started. Imo the error exactly tells this story.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, fair. My comment came from the thought of "if a round started and ended, it's weird to revert saying 'it started' ". But it makes sense, the condition is "rounds are editable before start". Let's leavi it like that.
0xNuggan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general looks good, there are a couple of things I pointed out. I also have still an open question about the caps/accesstypes, but I wanted to get this out first
f8d3200 to
8dda04f
Compare
| } | ||
|
|
||
| if (block.timestamp > round_.roundStart) { | ||
| revert Module__LM_PC_FundingPot__RoundAlreadyStarted(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, fair. My comment came from the thought of "if a round started and ended, it's weird to revert saying 'it started' ". But it makes sense, the condition is "rounds are editable before start". Let's leavi it like that.
| // Constants | ||
|
|
||
| /// @notice The role that allows creating funding rounds. | ||
| bytes32 public constant FUNDING_POT_ADMIN_ROLE = "FUNDING_POT_ADMIN"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You will need to update it for the new authorization logic
| } | ||
|
|
||
| /// @inheritdoc ILM_PC_FundingPot_v1 | ||
| function getRoundAccessCriteria(uint32 roundId_, uint8 accessCriteriaId_) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should revert if the roundId_ or its accessCriteriaId_ don't exist
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Unless returning all empty over revert is intended)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we were trying to reduce contract size here and so adding the revert may actually push us over the limit, if it doesn't then I think changing it makes sense but if it does then we'll have to leave as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so this function si not sued internally, so there is no internal logic problem.
To me, also returning isList_ = true when the AccessCriteriaType is equal to OPEN is unexpected, but it is impossible to say if this is problematic as we do not know the use case. What is the use case?
| ) external onlyModuleRole(FUNDING_POT_ADMIN_ROLE) { | ||
| Round storage round = rounds[roundId_]; | ||
|
|
||
| if (accessCriteriaType_ > MAX_ACCESS_CRITERIA_ID) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it checks against the type not the id. According to the name and other uses of MAX_ACCESS_CRITERIA_ID, it should both check here against accessCriteriaId_ instead of accessCriteriaType_, and in if accessCriteriaId_ is 0 it should check roundIdToNextAccessCriteriaId won't be higher.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though it's a bit unclear why require it to be up to 4?
Was that meant to be limiting the type or the ID in fact?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice find, we fixed this in the fixes PR
| // Clear all existing data to prevent stale data | ||
| round.accessCriterias[criteriaId].nftContract = address(0); | ||
| round.accessCriterias[criteriaId].merkleRoot = bytes32(0); | ||
| // @note: When changing allowlists, call removeAllowlistedAddresses first to clear previous entries |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be called here then to make sure it's removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to add a note here to be more explicit but we won't be fixing this for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should not be problematic, as these data are read only if the relevant Type is set.
However, the logic is not consistent, and that can be confusing for someone who calls this function.
you could simplify this block of code (i.e. these reset lines plus the stuff on l 531f) by just these variables to the value of the parameters.
| _validateEditRoundParameters(round); | ||
|
|
||
| for (uint i = 0; i < addressesToRemove_.length; i++) { | ||
| unchecked { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this unchecked for? (there's no math operation inside here)
| Module__LM_PC_FundingPot__UnspentCapsRoundIdsNotStrictlyIncreasing( | ||
| ); | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could move lastSeenRoundId = currentProcessingRoundId; here and delete it from the 3 other places it's done below to avoid code repetition.
| { | ||
| Round storage round = rounds[roundId_]; | ||
|
|
||
| if (round.roundEnd == 0 && round.roundCap == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of this, you could just check if the round ID is lower than the current one in the counter (here and in other places this check is done)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or check roundStart != 0 as it cannot be 0 when a round is added
| uint contributorCount = contributors.length; | ||
|
|
||
| // Check batch size is not zero | ||
| if (batchSize_ == 0 || batchSize_ > contributorCount) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might make operations more convenient to set batchSize_ to contributorCount instead of reverting if it's bigger
| } | ||
|
|
||
| // --- Personal Cap Check --- | ||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should have an if here to check if personal cap exists/ should be applied
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmmm now sure this is needed here's why
- userPersonalCap will be 0 (default value)
- If a user tries to contribute any amount (let's say 100 tokens)
- The code checks: userPreviousContribution >= userPersonalCap
- Since userPersonalCap is 0, and any contribution > 0, this will be true
- The transaction will revert with PersonalCapReached
Omega Audit Fixes
| view | ||
| returns (bool isEligible, uint remainingAmountAllowedToContribute) | ||
| { | ||
| if (accessCriteriaId_ > MAX_ACCESS_CRITERIA_TYPE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so here you compare accessCriteriaId with MAX_ACCESS_CRITERIA_TYPE, while in setAccessCriteria, it is the accessCriteriaType_ argument (not the id) that is supposed to be smaller than MAX_ACCESS_CRITERIA_TYPE.
Not sure what the original intention is here, but you should decide if the constnat MAX_ACCESS_CRITERIA_TYPE limits the ids or the type (I guess the latter)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed the if check, since it is not required for accessCriteriaId
| @@ -0,0 +1,1438 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-only | |||
| pragma solidity 0.8.23; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as a general remark, it is better (easier to review, easier to understand, easier to version control) to split the logic over diffferent classes/files rather than mixing all together in one huge file.
| mapping( | ||
| uint32 roundId | ||
| => mapping(uint8 accessCriteriaId_ => AccessCriteriaPrivileges) | ||
| ) private roundIdToAccessCriteriaIdToPrivileges; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why put this in a separate mapping? is it not cleaner to have the priviliges part of the accessCriteria object instead of createing a separate mapping?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes for sure good note but we will not be fixing this time around
| Round storage round = rounds[roundId_]; | ||
|
|
||
| if (accessCriteriaType_ > MAX_ACCESS_CRITERIA_TYPE) { | ||
| revert Module__LM_PC_FundingPot__InvalidAccessCriteriaId(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps not the error you want to use here, as the id could be fine..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed the error string from Module__LM_PC_FundingPot__InvalidAccessCriteriaId to Module__LM_PC_FundingPot__InvalidAccessCriteriaType
| // Otherwise, edit the existing one | ||
| if (accessCriteriaId_ == 0) { | ||
| unchecked { | ||
| criteriaId = ++roundIdToNextAccessCriteriaId[roundId_]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you have prblems witht he contract size, a small gain is to have a global counter for teh criteriaId instead of a spearate one for each round.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks good to note but a little too late for us to change here, will keep noted for the next contract
| round.accessCriterias[criteriaId].merkleRoot = merkleRoot_; | ||
| } else if (accessCriteriaType == AccessCriteriaType.LIST) { | ||
| // For LIST type, update the allowed addresses | ||
| for (uint i = 0; i < allowedAddresses_.length; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is important to note here that you are NOT overwriting the allowedlist here, but only adding to it. This is not obvious at all. I'd recomend to adda "addressesToRemove" parameter as well to the function signature to make this explicit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the fix and supporting unit test as well!
| uint unspentPersonalCap = 0; // Initialize to 0 | ||
| uint32 lastSeenRoundId = 0; // Tracks the last seen roundId to ensure strictly increasing order | ||
|
|
||
| // Process each previous round cap that the user wants to carry over |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not see where it is enforced taht the unspentPersonalRoundCaps are "previous" to the given roundId_. This is important, as in this way, they could include the roundId_ itself (and so the personal cap would be counted twice), or even after roundId_ (if roundId_ has overrideContributionSpan set to true)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have pushed a fix for this, and added a test as well
| .accessCriteriaId]; | ||
|
|
||
| uint userContribution = | ||
| roundIdToUserToContribution[currentProcessingRoundId][user_]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
roundIdToUserToContribution does not seem to be updated for the currentPRocessingRoundId. That would mean that the personalCap of previous rounds can be used over and over again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a proposed fix in mind as to how to fix this? some pseudo code would be helpful as well @jellegerbrandy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have pushed a fix for this, and added a couple of tests.
* fix: new audit fixes 1 * fix: new audit fixes 2 * fix: new audit fixes 3 * fix: audit fixes 4 * fix: new audit fixes 4 -remove unneccessary code * fix: new audit fixes 5 * fix: new audit fixes 5: add getter function * fix: contract size fix 1 * fix: contract size fix 2: remove getter functions and make public * fix: contract size fix 3 * fix: contract size fix 3: if condition * contract resizing * chore:remove uncommented tests * chore:remove hook validation --------- Co-authored-by: Zuhaib Mohammed <zzzuhaibmohd@gmail.com> Co-authored-by: Jeffrey Owoloko <72028836+JeffreyJoel@users.noreply.github.com> Co-authored-by: JeffreyJoel <jowoloko@gmail.com>
No description provided.