Skip to content
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

Delegation (Experimental) #378

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

l-monninger
Copy link
Collaborator

@l-monninger l-monninger commented Aug 19, 2024

Summary

Changelog

  1. Adds methods for staking and unstaking with delegation.
  2. Adjusts stake weight computation accordingly.

Testing

  1. Additional testBasicDelegation unit test covers staking and unstaking with delegation.
  2. All previous MCR unit tests unmodified and continue to pass.

Outstanding issues

  1. Naming could be clearer.
  2. Plenty of gas optimizations available.
  3. Should delegatees be able to restrict
  4. The term "delegatees" is used instead of delegates to match OpenZeppelin verbiage. "Delegate" is generally more common however.
  5. A more complete delegation sim is probably advisable.

@l-monninger l-monninger marked this pull request as ready for review August 20, 2024 08:38
// for every delegatee of the attester
for (
uint256 k = 0;
k < delegatorsByAttesterByDomain[domain][attester].length();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how good the optimizer is, but perhaps getting the delegatorsByAttesterByDomain[domain][attester] reference for the duration of the loop would save some gas.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think it will actually save some gas.

Comment on lines 135 to 143
epochStakesByDomain[domain][epoch][custodian][attester][
delegator
] -= amount;

if (
epochStakesByDomain[domain][epoch][custodian][attester][
delegator
] == 0
) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here as well: the value has just been decremented, could we hold on to the reference and get the value instead of spending gas on 5 nested lookups another time?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, good call.

Comment on lines 66 to 80
EnumerableSet.AddressSet storage attesters = attestersByDomain[domain];

for (uint256 i = 0; i < attestersByDomain[domain].length(); i++) {
address attester = attestersByDomain[domain].at(i);
for (uint256 i = 0; i < attesters; i++) {
address attester = attesters.at(i);

for (uint256 j = 0; j < custodiansByDomain[domain].length(); j++) {
address custodian = custodiansByDomain[domain].at(j);
// for every custodian
EnumerableSet.AddressSet storage custodians = custodiansByDomain[
domain
];
for (uint256 j = 0; j < custodians; j++) {
address custodian = custodians.at(j);

// for every delegatee of the attester
for (
uint256 k = 0;
k < delegatorsByAttesterByDomain[domain][attester].length();
k++
) {
// todo: can this be replaced with _rollOverAttester?
address delegatee = delegatorsByAttesterByDomain[domain][
EnumberableSet.AddressSet
storage delegatees = delegatorsByAttesterByDomain[domain][
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks much better, aesthentically and I assume also gas-wise. I'm not an expert on Solidity, though.

Comment on lines 138 to 144
uint256 stake = epochStakesByDomain[domain][epoch][custodian][attester][
delegator
];
stake -= amount; // okay to let this error if it underflows
epochStakesByDomain[domain][epoch][custodian][attester][
delegator
] -= amount;
] = stake;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could still use a storage reference here? This change only replaces one 5-step lookup for another.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is outdated, but I was doing this to save the extra stake lookups later on. You do also in theory save one instruction in the bytecode here.

Comment on lines 443 to 444
MovementStaking staking = new MovementStaking();
staking.initialize(moveToken);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would advise against building tests that initalize the implementation contract and not a proxy contract. This might lead to clash in functions or impredictable behavior. There has been recently a hack on deltaprime that was caused by this.
I'd be glad to redesign these tests.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's probably best for a separate PR as literally all of the tests use this approach.

Copy link
Collaborator Author

@l-monninger l-monninger Sep 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you provide some links here? It would seem to me you'd have to be doing something exotic with your use of the proxy pattern to actually have behavior vary in ways that tests like these wouldn't assert.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, in the least, you would not need to check all of the logic and instead access controls.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://medium.com/@Delta_Prime/deltaprime-post-mortem-report-752bd60a25e6
I think it's a bit further than what I expected but I think that anything that gets us closer to the actual implementation might prevent us from running into a critical bug in production.

delegator
];
stake -= amount; // okay to let this error if it underflows
epochStakesByDomain[domain][epoch][custodian][attester][
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

funds get stuck if it underflows, better handle it with "if amount > _stake, _stake = 0"

for (uint256 k = 0; k < delegatees.length(); k++) {
// todo: can this be replaced with _rollOverAttester?
address delegatee = delegatees.at(k);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to use _rollOverAttester, it needs to start using nextEpoch param instead of currentEpoch. This way 0 can be passed as the param. This requires a modification of the rest of the code, but definitely seems okay.
Just rolling with the current implementation basicTest passes, it requires a test to verify if the correct epoch has been settled.


// roll over the genesis stake to the current epoch
_addStake(domain, getCurrentEpoch(domain), custodian, delegatee, attester, attesterStake);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If _rollOverAttester is to be used, it might be optimized to take storage params instead

@0xPrimata 0xPrimata force-pushed the l-monninger/delegation-experimental branch from dc712ff to 19b947c Compare September 6, 2024 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants