Conversation
|
|
||
| function _assertValidAdmin(address _admin) internal view { | ||
| if (_admin == address(0)) revert InvalidAdminAddress(); | ||
| if (_isProxyAdmin(_admin)) revert InvalidAdminProxyAdmin(_admin); |
There was a problem hiding this comment.
This error is sort of confusingly named imo. Maybe revert AddressAlreadyProxyAdmin? Do we want to have a way to deploy a proxy reusing a proxy admin contract? Like a ProxyAdmin address is technically a valid address. You can reuse ProxyAdmins for multiple proxy contracts. The script just needs to know not to redeploy a new ProxyAdmin contract with the existing ProxyAdmin contract as the owner.
There was a problem hiding this comment.
Pull request overview
This PR adds deployment guardrails to prevent nested ProxyAdmin ownership chains by validating the admin address before deployment and asserting correct proxy configuration afterward. The changes align the deployment flow with OpenZeppelin v5's TransparentUpgradeableProxy pattern where the initialOwner parameter creates an internal ProxyAdmin, replacing the previous manual ProxyAdmin creation.
Changes:
- Added pre-deployment validation to reject
address(0)and ProxyAdmin contracts as admin addresses - Added post-deployment assertions to verify proxy admin slot, ProxyAdmin ownership, and implementation slot
- Updated deployment flow to use OZ v5 pattern with initialOwner parameter instead of manually creating ProxyAdmin
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function _deployContracts(address _admin) internal returns (TransparentUpgradeableProxy) { | ||
| _assertValidAdmin(_admin); | ||
|
|
||
| SavingCircles implementation = _deploySavingCircles(); | ||
| TransparentUpgradeableProxy proxy = _deployTransparentProxy( | ||
| address(_deploySavingCircles()), | ||
| address(_deployProxyAdmin(_admin)), | ||
| abi.encodeWithSelector(SavingCircles.initialize.selector, _admin) | ||
| address(implementation), _admin, abi.encodeWithSelector(SavingCircles.initialize.selector, _admin) | ||
| ); | ||
|
|
||
| // Deploy auxiliary contracts that reference the SavingCircles proxy | ||
| new DelegatedSavingCircles(address(proxy)); | ||
| new SavingCirclesViewer(address(proxy)); | ||
|
|
||
| address proxyAdmin = _assertDeployment(address(proxy), address(implementation), _admin); | ||
|
|
||
| console2.log('Deployer', msg.sender); | ||
| console2.log('Admin', _admin); | ||
| console2.log('ProxyAdmin', proxyAdmin); | ||
| console2.log('Proxy', address(proxy)); | ||
| console2.log('Implementation', address(implementation)); | ||
|
|
||
| return proxy; | ||
| } |
There was a problem hiding this comment.
The new validation logic in _assertValidAdmin and _assertDeployment lacks test coverage. Given that this is a security-critical feature designed to prevent deployment misconfigurations (specifically nested ProxyAdmin ownership chains), comprehensive tests should be added to verify:
- Deployment fails when
_adminisaddress(0) - Deployment fails when
_adminis a ProxyAdmin contract address - Deployment succeeds with a valid EOA address
- Post-deployment assertions correctly validate the proxy state
- Edge cases in
_isProxyAdmindetection (e.g., contracts withowner()but notUPGRADE_INTERFACE_VERSION())
The repository demonstrates comprehensive test coverage for other functionality (unit and integration tests exist), so this new validation logic should follow the same pattern.
| bytes4 internal constant _OWNER_SELECTOR = bytes4(keccak256('owner()')); | ||
| bytes4 internal constant _UPGRADE_INTERFACE_VERSION_SELECTOR = bytes4(keccak256('UPGRADE_INTERFACE_VERSION()')); | ||
|
|
||
| error InvalidAdminAddress(); |
There was a problem hiding this comment.
For consistency with other error definitions in this contract, consider including the invalid address as a parameter in the InvalidAdminAddress error. While the error is only triggered when _admin == address(0), including the parameter would make the error interface more consistent and could be helpful if the validation logic is extended in the future.
Example: error InvalidAdminAddress(address admin); and revert InvalidAdminAddress(_admin);
3a017d6 to
76a2640
Compare
…n/implementation guardrails
|
Upgrade Summary (SavingCircles) - Gnosis (chainId 100) - 2026-03-09 Upgrade completed successfully with pre/post safety checks.
Viewer rollout:
Final addresses:
Checks completed:
|
…on for PRs to dev
Problem
Previous deployments could create a nested
ProxyAdminownership chain (ProxyAdmin Aowned byProxyAdmin B, then owned by deployer EOA).In practice, this made upgrade authority easy to misconfigure and operationally unsafe.
The root cause was that deployment accepted any
ADMIN_ADDRESSwithout guardrails, including aProxyAdmincontract address.What This PR Changes
ADMIN_ADDRESS:address(0)ProxyAdmin(viaowner()+UPGRADE_INTERFACE_VERSION()checks)TransparentUpgradeableProxybehavior:ADMIN_ADDRESSasinitialOwnerof the proxy’s internally createdProxyAdminProxyAdmin.owner() == ADMIN_ADDRESSAdminProxyAdminProxyImplementationValidation Performed
GNOSIS_PKequalsADMIN_ADDRESSADMIN_ADDRESShas no code (EOA in current deployment)ProxyAdminProxyAdmin.owner()equalsADMIN_ADDRESSupgradeAndCallauthorization simulation viacast callsucceeds fromADMIN_ADDRESS(no state change)Notes