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

feat: add script to deploy locking proxy admin #243

Merged
merged 20 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ mento-core-2.1.0/=lib/mento-core-2.1.0/contracts/
mento-core-2.2.0/=lib/mento-core-2.2.0/contracts/
mento-core-2.3.1/=lib/mento-core-2.3.1/contracts/
mento-core-2.5.0/=lib/mento-core-2.5.0/contracts/
merkle-distributor/=lib/merkle-distributor/contracts/
merkle-distributor/=lib/merkle-distributor/contracts/
mento-core-2.6.0-oz/=lib/mento-core-2.6.0/lib/openzeppelin-contracts-next
mento-core-2.6.0/=lib/mento-core-2.6.0/contracts/
2 changes: 2 additions & 0 deletions script/interfaces/IGovernanceFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ interface IGovernanceFactory {
function mentoGovernor() external view returns (address);

function locking() external view returns (address);

function proxyAdmin() external view returns (address);
}
29 changes: 29 additions & 0 deletions script/upgrades/MU09/MU09.md
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
99 changes: 99 additions & 0 deletions script/upgrades/MU09/MU09.sol
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;
Andrew718PLTS marked this conversation as resolved.
Show resolved Hide resolved

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) {
Andrew718PLTS marked this conversation as resolved.
Show resolved Hide resolved
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;
}
}
90 changes: 90 additions & 0 deletions script/upgrades/MU09/MU09Checks.sol
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");
}
}
bayological marked this conversation as resolved.
Show resolved Hide resolved
32 changes: 32 additions & 0 deletions script/upgrades/MU09/deploy/MU09-Deploy-LockingProxyAdmin.sol
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();
}
}
Loading