-
Notifications
You must be signed in to change notification settings - Fork 75
!feat: Make SpokePools upgradeable #218
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: Make SpokePools upgradeable #218
Conversation
This PR makes SpokePools compatible with OpenZeppelin's upgradeable proxy pattern
ACX-520 Add features to SpokePool to make future upgrades easier
Make SpokePool compatible with OZ upgradeable proxies |
UUPS proxies allow the implementation contract to define the conditions under which an upgrade can be called, compared to a Transparent proxy in which only the owner of the proxy can upgrade. This allows the SpokePool upgrades to be triggered by the cross domain admin via the HubPool
…use no contracts use delegatecall() anymore
mrice32
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.
LGTM!
| @@ -0,0 +1,80 @@ | |||
| // SPDX-License-Identifier: AGPL-3.0-only | |||
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 discussed IRL, maybe we should just use the OZ lockable contract? Do you see any problem with 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.
Nope, I looked into Lockable. The main feature we add is the _startReentrantGuardDisabled internal function but that's not used by Across contracts so I think we're safe to use OZ lockable
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.
Awesome!
|
|
||
| /** | ||
| * @title MockSpokePool | ||
| * @notice Logic is 100% copied from "@uma/core/contracts/common/implementation/MultiCaller.sol" but one |
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 should remember to exclude this contract from the audit scope considering the contract's logic has already been audited.
| "Arbitrum_Adapter": { "address": "0x937958992799bF4A9a656E6596FD10d7Da5c2216", "blockNumber": 14704380 }, | ||
| "Arbitrum_Adapter": { "address": "0x29528780E29abb8Af95a5e5a125b94766987543F", "blockNumber": 16243583 }, | ||
| "Arbitrum_RescueAdapter": { "address": "0xC6fA0a4EBd802c01157d6E7fB1bbd2ae196ae375", "blockNumber": 16233939 }, |
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.
Seems unrelated - maybe split it out?
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 was accidentally removed so i'm just adding it back here
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's preferable from a commit hygiene standpoint to split it out and submit it separately - at least so it's not ++confusing when someone looks back at the commit history and sees it modified as part of something totally unrelated. Just my 2c.
| // Any deposit quote times greater than or less than this value to the current contract time is blocked. Forces | ||
| // caller to use an approximately "current" realized fee. Defaults to 10 minutes. | ||
| uint32 public depositQuoteTimeBuffer = 600; | ||
| uint32 public depositQuoteTimeBuffer; |
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 won't be possible to add any more state variables to the base SpokePool after deployment. Have we ruled out adding some placeholders, just in case we find a use for something else in future?
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 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.
Great call! Any reason not to make it way way bigger than we'd ever need? Like 1000? Just to be super safe
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's a thought experiment. Imagine we wanted to support bridging with arbitrary data so that someone could bridge-and-swap. Say we had to upgrade the deposit() function (or add a new depositWithData function). Would this be possible with upgradeable proxies? How much of the extra storage slots would we need?
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 depends on the implementation. If the data is just hashed into the deposit data and added to an event, no storage changes would be necessary.
If we needed to store extra info about each deposit, deposits would need extra storage, but they aren't stored continuously, so that can be done without pre-reserving slots.
In this implementation, what extra contiguous storage would you imagine needing?
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'm not sure, this was just a functionality that I discussed with Ryan about adding and wanted to make sure we're prepared for it. Basically bridge-and-swap would be a good UX addition
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 if we needed to add a new Base contract function, does that take up slots?
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.
Nope, functions don't take up slots or anything. They are just more bytecode in the implementation contract.
Signed-off-by: nicholaspai <npai.nyc@gmail.com>
Co-authored-by: Paul <108695806+pxrl@users.noreply.github.com>
Signed-off-by: nicholaspai <npai.nyc@gmail.com>
mrice32
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.
LGTM!
mrice32
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.
pxrl
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.
Nice!
|
@nicholaspai One final thought: the OVM_SpokePool is also a base contract. Not sure whether we want to reserve some slots there as well? |
Signed-off-by: nicholaspai <npai.nyc@gmail.com>
Signed-off-by: nicholaspai <npai.nyc@gmail.com>
Great call, i've added 100 slots for OVM_SpokePool and updated SpokePool's __gap to 1000 cc @mrice32 |
mrice32
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.
@nicholaspai One final thought: the OVM_SpokePool is also a base contract. Not sure whether we want to reserve some slots there as well?
Great call, i've added 100 slots for OVM_SpokePool and updated SpokePool's __gap to 1000
cc @mrice32
@nicholaspai why not just use a 1000 gap everywhere to give us tons of room and standardize? We have 2^256 slots to work with here, so there's no running out :).
mrice32
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.
LGTM!
|
@dohaki @mrice32 @pxrl I had to update this to take into account the new EIP712 contract that the SpokePool extends. This required changing that contract to be upgradeable with an initializer function instead of a constructor see this commit 22f5fbe#diff-8c305f0568345cd237252c531582ce424adc9c7c9151cfefe304fd389b874c59 ! |
mrice32
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.
LGTM!
| * variables without shifting down storage in the inheritance chain. | ||
| * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps | ||
| */ | ||
| uint256[1000] private __gap; |
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 the gap at the bottom of the file below the functions? Shouldn't it be at the end of the list of variables, like in the other contracts?
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'm not sure this is how OZ adds their gaps so I was following along https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol#L111
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.
Makes sense. Either way is totally fine! However, I think this PR has it done both ways. Ovm_SpokePool and SpokePool seem to have it at the top. As long as it's consistent, LGTM!
| * variables without shifting down storage in the inheritance chain. | ||
| * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps | ||
| */ | ||
| uint256[1000] private __gap; |
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 as above.
| * variables without shifting down storage in the inheritance chain. | ||
| * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps | ||
| */ | ||
| uint256[1000] private __gap; |
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 as above.
dohaki
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.
Awesome LGTM 👍 I mainly looked at the changes related to EIP712
mrice32
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.
Signed-off-by: nicholaspai <npai.nyc@gmail.com>
| * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.0/contracts/utils/cryptography/EIP712.sol | ||
| * https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/utils/cryptography/EIP712Upgradeable.sol | ||
| * | ||
| * NOTE: Modified version that allows to build a domain separator that relies on a different chain id than the chain 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.
| * https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/utils/cryptography/EIP712Upgradeable.sol | |
| * https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/cb0c1443f9b0aded3b08dfb3088f5ed5c9c72bea/contracts/utils/cryptography/EIP712Upgradeable.sol |
Referring to master could be misleading in future, so it probably makes sense to refer to a specific version. I expect that we're just migrating from openzeppelin-contracts v4.8.0 to openzeppelin-contracts-upgradeable v4.8.0 - is that correct? (nb. the linked version is a permalink to https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v4.8/contracts/utils/cryptography/EIP712Upgradeable.sol)
(FWIW I hate that it stretches off to infinity because the URL is so long...)
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.
Would be nice to be more specific in the comment referring to the OZ EIP712Upgradable contract, but otherwise 👍
This PR makes SpokePools compatible with OpenZeppelin's upgradeable proxy pattern. Some important changes to support upgradeability:
SpokePool's constructoradds the_disableInitializerscall to prevent it from being left uninitialized.initializefunction.SpokePoolextends such asLockableandTestablethat have constructors replaces those with upgradeable versions that replace constructors with initializers.