-
Notifications
You must be signed in to change notification settings - Fork 91
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: add a Governance Rewards Splitter contract #971
base: development
Are you sure you want to change the base?
Conversation
@rndquu please evaluate the configuration mechanism. Currently, since the number of shares becomes imbalanced as a new payee is added, I have payees and shares tied to a mapping which is a configuration ID. Owner can always set a new configuration which increments the current global configuration ID. Depending on array size, it is probably cheaper to create a new array than clearing the entire configuration array, we can delete old arrays if total storage is important. In this implementation, owner can either set a whole new configuration or add a single payee. I can add functions to edit and delete a payee as well. There should be a pointer approach to it but I imagine you want to stay away from assembly. |
Current naming (
Yes, we definitely need methods for adding, editing and removing payess.
Yes.
Ok, the concept of "configurations" appeared because we somehow need to modify arrays of payees and shares. The issue with the current approach is that payee may not be able to claim Governance tokens if currentConfig has been incremented and payee removed:
To sum up:
|
PaymentSplitter already has almost all we need for distributing ERC20 rewards. We need to:
Update: one more https://github.com/immutable/contracts/blob/main/contracts/payment-splitter/PaymentSplitter.sol (not sure if it's audited, need to dig into https://github.com/immutable/contracts/tree/main/audits) |
Yes this is actually intented in this initial approach, the reason is, if config is set poorly you can change config first and then appropriately release with new config. This only assumes contract owner behaves correctly.
Yes I wanted you to take a look at the contract before testing it.
Notice that this is a direct fork of PaymentSplitter where I've removed ETH handling capabilities and made it only able to handle governance tokens. The only thing that has changed is arrays became mappings from config to array. The reason I took the mapping approach is that handling long arrays (deleting , moving) would incur in extra gas costs specially if the array was very big. Therefore just adding new arrays would actually be gas efficient. I will take a look at Enumerable approaches though, I've used it a couple times as well. |
@rndquu, I've considered EnumerableMap but I still think the native mapping approach is more efficient. The biggest gas cost in this contract happens when you insert/remove a payee, since all shares array becomes imbalanced. For example, if there are two payees, one with 70% (7 shares) and the other with 30% (3 shares), adding a third payee with 1 share will change the distribution for all payees ~(63%, 27%, 9%). Therefore just creating new arrays and forgetting the previous one when you setup a new config is definitely better than iterating through the list of payees and delete every key manually. EnumerableSet doesn't make sense to use since it's biggest offering is O(1) existence check which is not that useful in this case. We need O(1) insertion/deletion and O(n) iteration, this is achieved by both mapping and EnumerableMap, the difference is that in my mappings approach we just forget the old payees and shares and create new arrays when we set new ones instead of modifying the old ones. It's your final call and I'll add an EnumerableMap version here for you to take a look. |
Please let me know what is your preferred approach, if you want more strict arguments in favor of using native mappings please let me know, I can further elaborate why it should be more efficient. |
); | ||
|
||
uint256 oldShares = _payeesToShares.get(account); | ||
totalShares = totalShares - oldShares + newShares; |
Check notice
Code scanning / Semgrep OSS
Semgrep Finding: rules.solidity.security.basic-arithmetic-underflow Note
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.
Underflow is not possible without assembly or unchecked blocks in Solidity ^0.8.0.
); | ||
|
||
uint256 shares = _payeesToShares.get(account); | ||
totalShares -= shares; |
Check notice
Code scanning / Semgrep OSS
Semgrep Finding: rules.solidity.security.basic-arithmetic-underflow Note
@zugdev, this task has been idle for a while. Please provide an update. |
waiting on PR review |
@zugdev, this task has been idle for a while. Please provide an update. |
Resolves #831
I think GovernanceRewardsSplitter or TreasurySplitter works for the contract's name.