-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add script to deploy locking proxy admin (#243)
- Loading branch information
1 parent
0493a31
commit 784a836
Showing
6 changed files
with
255 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
### MU09: Transfer Locking Contract Ownership | ||
|
||
#### Description | ||
|
||
Currently, Mento Governance and Locking contracts have a circular dependency - any issues in the Locking contracts could prevent governance from executing upgrades since governance requires Locking to function properly. With the upcoming Celo L2 transition bringing breaking changes to Locking contracts, we need to temporarily transfer upgradability rights to another account to ensure we can upgrade Locking contracts without depending on them being in a perfect state. | ||
|
||
#### Changes | ||
|
||
1. Deploy a new ProxyAdmin contract owned by the MentoLabs multisig | ||
2. Transfer proxy ownership of all Locking contracts to this new ProxyAdmin | ||
|
||
#### Motivation | ||
|
||
This change breaks the circular dependency between governance and Locking contracts, allowing us to: | ||
|
||
- Safely upgrade Locking contracts if issues arise | ||
- Prepare for Celo L2 transition breaking changes | ||
- Maintain ability to fix critical issues without being blocked by Locking contract state | ||
|
||
#### Security Considerations | ||
|
||
- The MentoLabs multisig will temporarily have direct upgrade rights over Locking contracts | ||
- This is a temporary security tradeoff to prevent potential system deadlock | ||
- Plan to return ownership to governance after L2 transition is complete | ||
|
||
#### Testing | ||
|
||
- Deploy ProxyAdmin with correct ownership(Mento labs multisig) | ||
- Verify successful transfer of Locking proxy ownership |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
// solhint-disable func-name-mixedcase, contract-name-camelcase, function-max-lines, var-name-mixedcase | ||
pragma solidity ^0.8; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import { GovernanceScript } from "script/utils/mento/Script.sol"; | ||
import { console } from "forge-std/console.sol"; | ||
import { Contracts } from "script/utils/mento/Contracts.sol"; | ||
import { Chain } from "script/utils/mento/Chain.sol"; | ||
|
||
import { IGovernanceFactory } from "script/interfaces/IGovernanceFactory.sol"; | ||
import { IMentoUpgrade, ICeloGovernance } from "script/interfaces/IMentoUpgrade.sol"; | ||
|
||
interface IProxyAdminLite { | ||
function getProxyAdmin(address proxy) external view returns (address); | ||
|
||
function changeProxyAdmin(address proxy, address newAdmin) external; | ||
} | ||
|
||
contract MU09 is IMentoUpgrade, GovernanceScript { | ||
using Contracts for Contracts.Cache; | ||
|
||
bool public hasChecks = true; | ||
|
||
address public mentoGovernor; | ||
address public lockingProxyAdmin; | ||
address public lockingProxy; | ||
|
||
address public oldLockingProxyAdmin; | ||
|
||
IGovernanceFactory public governanceFactory; | ||
|
||
/** | ||
* @dev Loads the contracts from previous deployments | ||
*/ | ||
function loadDeployedContracts() public { | ||
// Load load deployment with governance factory | ||
contracts.loadSilent("MUGOV-00-Create-Factory", "latest"); | ||
|
||
// Load the deployed ProxyAdmin contract | ||
contracts.loadSilent("MU09-Deploy-LockingProxyAdmin", "latest"); | ||
} | ||
|
||
function prepare() public { | ||
loadDeployedContracts(); | ||
|
||
// Get newly deployed LockingProxyAdmin address | ||
lockingProxyAdmin = contracts.deployed("ProxyAdmin"); | ||
|
||
// Get and set the governance factory | ||
address governanceFactoryAddress = contracts.deployed("GovernanceFactory"); | ||
governanceFactory = IGovernanceFactory(governanceFactoryAddress); | ||
|
||
// Get the mento governor address | ||
mentoGovernor = governanceFactory.mentoGovernor(); | ||
require(mentoGovernor != address(0), "MentoGovernor address not found"); | ||
|
||
// Get the locking proxy address | ||
lockingProxy = governanceFactory.locking(); | ||
require(lockingProxy != address(0), "LockingProxy address not found"); | ||
|
||
// Get the old locking proxy admin address | ||
oldLockingProxyAdmin = governanceFactory.proxyAdmin(); | ||
require(oldLockingProxyAdmin != address(0), "Old LockingProxyAdmin address not found"); | ||
} | ||
|
||
function run() public { | ||
prepare(); | ||
|
||
ICeloGovernance.Transaction[] memory _transactions = buildProposal(); | ||
|
||
vm.startBroadcast(Chain.deployerPrivateKey()); | ||
{ | ||
// TODO: Change this to the forum post URL | ||
createProposal(_transactions, "https://CHANGE-ME-PLEASE", mentoGovernor); | ||
} | ||
vm.stopBroadcast(); | ||
} | ||
|
||
function buildProposal() public returns (ICeloGovernance.Transaction[] memory) { | ||
ICeloGovernance.Transaction[] memory _transactions = new ICeloGovernance.Transaction[](1); | ||
|
||
// Check that the proxy admin of locking is the proxy admin from the governance factory | ||
address proxyAdminOfLocking = IProxyAdminLite(oldLockingProxyAdmin).getProxyAdmin(lockingProxy); | ||
require( | ||
proxyAdminOfLocking == oldLockingProxyAdmin, | ||
"Proxy admin of locking is not `governanceFactory.proxyAdmin()`" | ||
); | ||
|
||
// Send tx to the old proxy admin to change the proxy admin of locking to the new locking proxy admin | ||
_transactions[0] = ICeloGovernance.Transaction( | ||
0, | ||
oldLockingProxyAdmin, | ||
abi.encodeWithSelector(IProxyAdminLite.changeProxyAdmin.selector, lockingProxy, lockingProxyAdmin) | ||
); | ||
|
||
return _transactions; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
pragma solidity ^0.8; | ||
pragma experimental ABIEncoderV2; | ||
/* solhint-disable max-line-length */ | ||
|
||
import { GovernanceScript } from "script/utils/mento/Script.sol"; | ||
import { console } from "forge-std/console.sol"; | ||
import { Contracts } from "script/utils/mento/Contracts.sol"; | ||
import { Test } from "forge-std/Test.sol"; | ||
|
||
import { IGovernanceFactory } from "script/interfaces/IGovernanceFactory.sol"; | ||
import { ITransparentUpgradeableProxy } from "mento-core-2.6.0/../lib/openzeppelin-contracts-next/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; | ||
import { ProxyAdmin } from "mento-core-2.6.0/../lib/openzeppelin-contracts-next/contracts/proxy/transparent/ProxyAdmin.sol"; | ||
|
||
contract MockImplementation {} | ||
|
||
contract MU09Checks is GovernanceScript, Test { | ||
using Contracts for Contracts.Cache; | ||
|
||
address public newLockingProxyAdmin; | ||
address public lockingProxy; | ||
address public mentoLabsMultisig; | ||
|
||
event Upgraded(address indexed newImplementation); | ||
|
||
function prepare() public { | ||
// Load addresses from deployments | ||
contracts.loadSilent("MUGOV-00-Create-Factory", "latest"); | ||
contracts.loadSilent("MU09-Deploy-LockingProxyAdmin", "latest"); | ||
|
||
mentoLabsMultisig = contracts.dependency("MentoLabsMultisig"); | ||
require(mentoLabsMultisig != address(0), "MentoLabsMultisig address not found"); | ||
|
||
// Get newly deployed LockingProxyAdmin address | ||
newLockingProxyAdmin = contracts.deployed("ProxyAdmin"); | ||
require(newLockingProxyAdmin != address(0), "LockingProxyAdmin address not found"); | ||
|
||
// Get and set the governance factory | ||
address governanceFactoryAddress = contracts.deployed("GovernanceFactory"); | ||
require(governanceFactoryAddress != address(0), "GovernanceFactory address not found"); | ||
IGovernanceFactory governanceFactory = IGovernanceFactory(governanceFactoryAddress); | ||
|
||
// Get the locking proxy address | ||
lockingProxy = governanceFactory.locking(); | ||
require(lockingProxy != address(0), "LockingProxy address not found"); | ||
} | ||
|
||
function run() public { | ||
console.log("\nStarting MU09 checks:"); | ||
prepare(); | ||
|
||
verifyLockingProxyAdminOwnership(); | ||
verifyLockingProxyAdminIsNewLockingProxyAdmin(); | ||
verifyMultisigCanUpgrade(); | ||
} | ||
|
||
function verifyLockingProxyAdminOwnership() public { | ||
console.log("\n== Verifying locking proxy admin ownership: =="); | ||
|
||
address lockingProxyAdminOwner = ProxyAdmin(newLockingProxyAdmin).owner(); | ||
require(lockingProxyAdminOwner == mentoLabsMultisig, "LockingProxyAdmin owner is not MentoLabsMultisig"); | ||
console.log(unicode"🟢 LockingProxyAdmin owner is MentoLabsMultisig: %s", lockingProxyAdminOwner); | ||
} | ||
|
||
function verifyLockingProxyAdminIsNewLockingProxyAdmin() public { | ||
console.log("\n== Verifying locking proxy admin is new locking proxy admin: =="); | ||
|
||
address lockingProxyAdmin = ProxyAdmin(newLockingProxyAdmin).getProxyAdmin( | ||
ITransparentUpgradeableProxy(lockingProxy) | ||
); | ||
require(lockingProxyAdmin == newLockingProxyAdmin, "LockingProxyAdmin is not the new LockingProxyAdmin"); | ||
console.log(unicode"🟢 LockingProxyAdmin is new LockingProxyAdmin: %s", lockingProxyAdmin); | ||
} | ||
|
||
function verifyMultisigCanUpgrade() public { | ||
console.log("\n== Verifying the multisig can successfuly upgrade implementation: =="); | ||
|
||
// Deploy a mock implementation | ||
MockImplementation mockImplementation = new MockImplementation(); | ||
vm.startPrank(mentoLabsMultisig); | ||
|
||
// Verify that the upgrade event is emitted with the fake implementation | ||
vm.expectEmit(true, true, true, true); | ||
emit Upgraded(address(mockImplementation)); | ||
|
||
ProxyAdmin(newLockingProxyAdmin).upgrade(ITransparentUpgradeableProxy(lockingProxy), address(mockImplementation)); | ||
|
||
console.log(unicode"🟢 Multisig can upgrade implementation"); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
script/upgrades/MU09/deploy/MU09-Deploy-LockingProxyAdmin.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
// solhint-disable contract-name-camelcase | ||
pragma solidity ^0.8.18; | ||
|
||
import { console } from "forge-std-next/console.sol"; | ||
import { Script } from "script/utils/mento/Script.sol"; | ||
import { Chain as ChainLib } from "script/utils/mento/Chain.sol"; | ||
import { Contracts } from "script/utils/mento/Contracts.sol"; | ||
import { ProxyAdmin } from "mento-core-2.6.0-oz/contracts/proxy/transparent/ProxyAdmin.sol"; | ||
|
||
interface IOwnableLite { | ||
function transferOwnership(address newOwner) external; | ||
} | ||
|
||
contract MU09_Deploy_LockingProxyAdmin is Script { | ||
using Contracts for Contracts.Cache; | ||
|
||
function run() public { | ||
address mentoLabsMultisig = contracts.dependency("MentoLabsMultisig"); | ||
require(mentoLabsMultisig != address(0), "MentoLabsMultisig address not found"); | ||
|
||
vm.startBroadcast(ChainLib.deployerPrivateKey()); | ||
{ | ||
IOwnableLite proxyAdmin = IOwnableLite(address(new ProxyAdmin())); | ||
console.log("Deployed ProxyAdmin for Locking at: %s", address(proxyAdmin)); | ||
|
||
proxyAdmin.transferOwnership(mentoLabsMultisig); | ||
console.log("Transferred LockingProxyAdmin ownership to MentoLabsMultisig: %s", mentoLabsMultisig); | ||
} | ||
vm.stopBroadcast(); | ||
} | ||
} |