-
Notifications
You must be signed in to change notification settings - Fork 0
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(StakeManager): add capabilities to register vaults #71
Conversation
*/ | ||
function register() external { | ||
stakeManager.registerVault(); | ||
} |
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.
First I wanted to make register()
part of the StakeVault's constructor()
but the problem is registerVault()
has the trusted code hash modifier. We need to create a StakeVault
"template" instance first to register its codehash with the Stake manager, but the instantiation then fails due to registerVault()
reverting.
I then decided to move this out and add an onlyRegisteredVaults
modifier to the sensitive functions.
This should ensure StakeVault
will call register()
before they'll interact with the StakeManager.
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.
Since we only allow for staking once per vault, we could also make this part of stake()
instead and enforce registration implicitly there.
Let me know what you guys think.
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.
Since we only allow for staking once per vault, we could also make this part of stake() instead and enforce registration implicitly there.
I like this idea!
Otherwise we can do it in the constructor anyway since we know the bytecode already, we don't need to deploy a real one before. But anyway I like it if we do it in the stake
function
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.
Otherwise we can do it in the constructor anyway since we know the bytecode already, we don't need to deploy a real one before. But anyway I like it if we do it in the stake function
So I gave it a bit more thought.
When I started implementing it, it felt odd to me that stake()
would create a side effect (registration).
Now I'm kind of hesitant..
re: the other point: we can't do it in the constructor because we don't know the deployed byte code at this point in time. This is because of the STAKING_TOKEN
in stakevault being immutable, making it part of the deploycode.
We know the staking token is going to be SNT on mainnet/l2 but for local testing it's always a different one.
So I don't think we can make registration part of the constructor.
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.
because we don't know the deployed byte code at this point in time
@0x-r4bbit we can add a script that compute it in local. It can use a mock manager without modifier so that the vault can be deployed in local and the bytecode can be printed out. after that we know it and we can use it in production. we don't need to deploy a contract in prod to know the bytecode, wdyt?
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 don't need to deploy a contract in prod to know the bytecode, wdyt?
I think that would be idea.
If we do a minimal proxy clone pattern for StakeVault
that needs a template deployed anyways, I think we don't need to set up a script for that either.
Maybe another option worth exploring?
*/ | ||
function owner() public view override(Ownable, IStakeVault) returns (address) { | ||
return super.owner(); | ||
} |
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 StakeManager.registerVault()
we're reading StakeVault.owner()
so I added owner()
to the IStakeVault
interface.
Needed to explicitly add owner()
here to make this compile.
80ff390
to
d6abc16
Compare
4fe58c2
to
1564bd2
Compare
1564bd2
to
0bdfd2b
Compare
@gravityblast I've updated the PR according to your comments, accept for #71 (comment) |
89c702d
to
3c47f85
Compare
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 be in another contact.
3c47f85
to
db96cd7
Compare
src/RewardsStreamerMP.sol
Outdated
* @param vault The address of the vault to check | ||
* @return true if the vault is registered, false otherwise | ||
*/ | ||
function isVaultRegistered(address vault) public view returns (bool) { |
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 don't think this belongs here.
StakeManager should not care about this.
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.
Yeap, this is a leftover!
src/RewardsStreamerMP.sol
Outdated
/** | ||
* @notice Get all vaults owned by an address | ||
*/ | ||
function getUserVaults(address owner) external view returns (address[] memory) { |
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 don't think this belongs here, StakeVault should interact directly with Registry, and systems that want to read the registry should interact directly with Registry.
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.
Same here, left over! Will remove
* @param user The address of the user | ||
* @return The total maximum multiplier points for the user | ||
*/ | ||
function getUserTotalMaxMP(address user) external view returns (uint256) { |
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 be in registry as well
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.
Hm.. not sure about that...
So far we said that the staking protocol ultimately is one of the "XP providers" for the XP token contract.
Therefore, the stakemanager has to expose these. StakeManager is also where rewards are distributed.
I think it makes sense to keep the registry solely as registry for "which account owns which vaults".
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.
The StakeManagerRegistry could be the source of XP, while the StakeManager is only the logic. If forwarding of calls are needed they can be done in StakeManagerRegistry, not in StakeManager.
At least this is what makes sense for me, from a logical flow of information.
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.
There's a tradeoff:
Do users/apps have to worry about that there's a registry under the hood or not.
With the current implementation, calls always go through the stake manager and it knows whether or not it has to talk to a registry that users don't have to know about.
It also ensures that StakeVaults that interact with the system are whitelisted.
The path you're suggesting means:
- Registry has to whitelist as well, because we need to be sure only legit stakevault interact with the system
- Apps have to talk to one more contract to figure things out
I'm personally in favor of the former, but if there's majority consensus I'm happy to make the changes.
@gravityblast maybe you can leave your opinion here as well?
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 would keep the functions in the staking manager and keep the registry only as a registry of vaults
external | ||
onlyTrustedCodehash | ||
onlyNotEmergencyMode | ||
onlyRegisteredVault |
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 is a requirement to have the vault registered? Is there any security benefits on that? For me it seems is just a tool. This type of verification can be done in UI, and there should be no problem in using StakeManger without register.
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'd say, it's an enforced invariant that only registered vaults can stake.
If it's not enforced here, then any vault could stake and the vault might not be registered, meaning it would not be taken into consideration when querying the XP for an owner of vaults.
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.
Ok, but it does not pose any risk for the current system, this should be a requirement by the system which requires this.
Otherwise the dependency graph is weird.
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 be a requirement by the system which requires this.
Can you elaborate what you're looking for? Having this modifier is essentially that requirement, no?
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.
and there should be no problem in using StakeManger without register.
So, to clarify here: the reason we've introduced registration of vaults by owners, is so that we can aggregate all MP/XP/stakedBalance for a given owner.
If a StakeVault is not registered, it won't be known in the aggregation of XP.
Sure, we could say: Well, if a user ignores the rules and still stakes without registering, then it's their problem.
I think though, we shouldn't even allow that.
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 agree with keeping the registration of the vaults, it feels more consistent like this
45d4407
to
c8ea850
Compare
src/RewardsStreamerMP.sol
Outdated
function _updateAccountMP(address accountAddress) internal { | ||
Account storage account = accounts[accountAddress]; | ||
|
||
function _getAccruedMP(Account storage account) internal view returns (uint256) { |
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.
very small thing, shall we call this _getAccountAccruedMP
so that it's clear it's for the account? just in case we extract the global MP logic to something with the same name
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 agree with renaming this.
I think we should do this across the board for all other things as well #87
c8ea850
to
d61a3e3
Compare
The registry contract should have all the logic related to registry and reading balances, otherwise it does not make sense to have a separate contract. Personally I prefer systems to have a strong single responsibility logic, whenever possible, because it leads to code that is easier to maintain in the long run. I'll explore more into the both designs below. A) StakeManager with Embedded Registry Logicflowchart TD
subgraph User
StakeVault
end
StakeVault --> StakeManager
XPToken --> StakeManager
Benefits:
Drawbacks:
B) Strong Single Responsibility Principle (SRP) with Separate RegistryIn this approach, the flowchart TD
subgraph User
StakeVault
end
StakeVault --> StakeManager
StakeVault -->|register| Registry
XPToken -->|balanceOfAccount| Registry
Registry -->|balanceOfVault| StakeManager
Benefits:
Drawbacks:
Key Takeaways:
|
This commit introduces changes related to vault registrations in the stake manager. The stake manager needs to keep track of the vaults a users creates so it can aggregate accumulated MP across vaults for any given user. The `StakeVault` now comes with a `register()` function which needs to be called to register itself with the stake manager. `StakeManager` has a new `onlyRegisteredVault` modifier that ensures only registered vaults can actually `stake` and `unstake`. Closes #70
d61a3e3
to
3dd4668
Compare
As discussed offline, we're moving ahead with not having a separate registry contract. The reasoning is that, at least now, it introduces unnecessarily complexity. Once CI is green this will be merged. |
All checks successful. @3esmit I'm dismissing your review so this can be merged (as it's currently blocking otherwise). |
We've ultimately agreed on not implementing a separate registry contract.
This commit introduces changes related to vault registrations in the stake manager.
The stake manager needs to keep track of the vaults a users creates so it can aggregate accumulated MP across vaults for any given user.
The
StakeVault
now comes with aregister()
function which needs to be called to register itself with the stake manager.StakeManager
has a newonlyRegisteredVault
modifier that ensures only registered vaults can actuallystake
andunstake
.Closes #70
Checklist
Ensure you completed all of the steps below before submitting your pull request:
pnpm adorno
?pnpm verify
?