From df04604f4e2f80030733e0230c117a099377ee41 Mon Sep 17 00:00:00 2001 From: detoo Date: Wed, 10 Dec 2025 14:20:31 -0800 Subject: [PATCH 1/4] wip: chore: migrate base-sepolia corps that are using dev v3 to the prod v3 --- ...migrate-base-sep-im-rm-factory-addrs.s.sol | 178 +++++++++++++++++ script/upgrade-public-rounds-base-sep.s.sol | 166 ++++++++++++++++ src/CyberCorpWithMigrationFactories.sol | 73 +++++++ src/IssuanceManagerWithFactoryMigration.sol | 58 ++++++ src/RoundManagerWithFactoryMigration.sol | 62 ++++++ test/MigrateBaseSepImRmFactoryAddrsTest.t.sol | 181 ++++++++++++++++++ 6 files changed, 718 insertions(+) create mode 100644 script/migrate-base-sep-im-rm-factory-addrs.s.sol create mode 100644 script/upgrade-public-rounds-base-sep.s.sol create mode 100644 src/CyberCorpWithMigrationFactories.sol create mode 100644 src/IssuanceManagerWithFactoryMigration.sol create mode 100644 src/RoundManagerWithFactoryMigration.sol create mode 100644 test/MigrateBaseSepImRmFactoryAddrsTest.t.sol diff --git a/script/migrate-base-sep-im-rm-factory-addrs.s.sol b/script/migrate-base-sep-im-rm-factory-addrs.s.sol new file mode 100644 index 00000000..3ec9e26f --- /dev/null +++ b/script/migrate-base-sep-im-rm-factory-addrs.s.sol @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +import {Script} from "forge-std/Script.sol"; +import {Test, console2} from "forge-std/Test.sol"; +import {CyberCorpFactory} from "../src/CyberCorpFactory.sol"; +import {CyberCertPrinter} from "../src/CyberCertPrinter.sol"; +import {IIssuanceManager} from "../src/interfaces/IIssuanceManager.sol"; +import {CyberCorpSingleFactory} from "../src/CyberCorpSingleFactory.sol"; +import {BorgAuth} from "../src/libs/auth.sol"; +import {CyberAgreementRegistry} from "../src/CyberAgreementRegistry.sol"; +import {CyberCorpSingleFactory} from "../src/CyberCorpSingleFactory.sol"; +import {IDealManager} from "../src/interfaces/IDealManager.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {CertificateDetails} from "../src/storage/CyberCertPrinterStorage.sol"; +import "../src/CyberCorpConstants.sol"; +import {CertificateUriBuilder} from "../src/CertificateUriBuilder.sol"; +import {SAFTExtension} from "../src/storage/extensions/SAFTExtension.sol"; +import {DealManagerStorage} from "../src/storage/DealManagerStorage.sol"; +import {CyberCorp} from "../src/CyberCorp.sol"; +import {ILegacyFactory} from "./interfaces/ILegacyFactory.sol"; +import {KnownAddressesLoader} from "./libs/KnownAddressesLoader.sol"; +import {IssuanceManagerFactory} from "../src/IssuanceManagerFactory.sol"; +import {IssuanceManagerWithFactoryMigration} from "../src/IssuanceManagerWithFactoryMigration.sol"; +import {DealManagerFactory} from "../src/DealManagerFactory.sol"; +import {DealManager} from "../src/DealManager.sol"; +import {RoundManagerFactory} from "../src/RoundManagerFactory.sol"; +import {RoundManagerWithFactoryMigration} from "../src/RoundManagerWithFactoryMigration.sol"; + +contract MigrateBaseSepImRmFactoryAddrsScript is Script { + mapping(address => uint256) private corpOwnerPrivateKeyLookup; + + function run() public { + runWithArgs( + vm.envUint("PRIVATE_KEY_MAIN"), // deployerPrivateKey + new uint256[](0), // corpOwnerPrivateKeys + type(uint256).max // maxCount + ); + } + + /// @dev Input argument is not private key because tests will prank the deployer for real-world simulation + function runWithArgs( + uint256 deployerPrivateKey, + uint256[] memory corpOwnerPrivateKeys, + uint256 maxCount + ) public { + address deployer = vm.addr(deployerPrivateKey); + string memory saltStr = "MetaLexCyberCorp.PublicRounds.UpgradeV3.0.1"; + bytes32 salt = bytes32(keccak256(bytes(saltStr))); + + // Compile a lookup table of corp owners (for accepting upgrades) + for (uint256 i = 0; i < corpOwnerPrivateKeys.length; i++) { + address corpOwner = vm.addr(corpOwnerPrivateKeys[i]); + corpOwnerPrivateKeyLookup[corpOwner] = corpOwnerPrivateKeys[i]; + } + + console2.log("deployer: %s", deployer); + console2.log("salt string: %s", saltStr); + console2.log("loaded corp owners: %d", corpOwnerPrivateKeys.length); + + CyberCorpFactory cyberCorpFactory = CyberCorpFactory(0x51413048f3Dfc4516e95BC8e249341B1D53B6cB2); + RoundManagerFactory deprecatingRmFactory = RoundManagerFactory(0x9E2A3a07711Ce4b5A2F4D62a5c8f8B5307Af9C34); // deprecated develop-version of v3 RoundManagerFactory + + // Get the current factory addresses + CyberCorpSingleFactory cyberCorpSingleFactory = CyberCorpSingleFactory(cyberCorpFactory.cyberCorpSingleFactory()); + IssuanceManagerFactory imFactory = IssuanceManagerFactory(cyberCorpFactory.issuanceManagerFactory()); + DealManagerFactory dmFactory = DealManagerFactory(cyberCorpFactory.dealManagerFactory()); + RoundManagerFactory rmFactory = RoundManagerFactory(cyberCorpFactory.roundManagerFactory()); + + ILegacyFactory legacyImFactory = ILegacyFactory(0xA32547aAdAA4975082D729c79e79dBaE4385EBCf); + + // Load all known cyber corps + address[] memory knownLegacyCyberCorps = KnownAddressesLoader.load(block.chainid, "/script/res/known-cyber-corps.json", maxCount); + // TODO WIP +// address[] memory knownDevV3CyberCorps = KnownAddressesLoader.load(block.chainid, "/script/res/known-dev-v3-cyber-corps.json", maxCount); + + vm.startBroadcast(deployerPrivateKey); + + // Deploy temporary contracts for migration + + IssuanceManagerWithFactoryMigration imWithMigrationImpl = new IssuanceManagerWithFactoryMigration(); + RoundManagerWithFactoryMigration rmWithMigrationImpl = new RoundManagerWithFactoryMigration(); + + // Sanity check: the hard-coded factory addresses should match the current factories + vm.assertEq( + imWithMigrationImpl.NEW_UPGRADE_FACTORY(), + address(imFactory), + string(abi.encodePacked("IssuanceManagerWithFactoryMigration.NEW_UPGRADE_FACTORY should point to the current factory")) + ); + vm.assertEq( + rmWithMigrationImpl.NEW_UPGRADE_FACTORY(), + address(rmFactory), + string(abi.encodePacked("RoundManagerWithFactoryMigration.NEW_UPGRADE_FACTORY should point to the current factory")) + ); + + // Upgrade issuance manager beacon to a special implementation with migration features + legacyImFactory.upgradeImplementation(address(imWithMigrationImpl)); + vm.assertEq(legacyImFactory.getBeaconImplementation(), address(imWithMigrationImpl), "beacon implementation should be upgraded with migration features by now"); + console2.log("Set new beacon implementation (with migration features): %s for legacy IssuanceManagerFactory: %s", address(imWithMigrationImpl), address(legacyImFactory)); + + // Set the reference implementation on the deprecating RoundManagerFactory (so the corps can accept it) + deprecatingRmFactory.setRefImplementation(address(rmWithMigrationImpl)); + + vm.stopBroadcast(); + + // Migrate each legacy corp one-by-one + for (uint256 i = 0; i < knownLegacyCyberCorps.length; i++) { + CyberCorp corp = CyberCorp(knownLegacyCyberCorps[i]); + + // Sanity check: all other factories should match + vm.assertEq( + corp.upgradeFactory(), + address(cyberCorpSingleFactory), + string(abi.encodePacked("legacy cyberCorp: ", vm.toString(address(corp)), " should point to the current CyberCorpSingleFactory")) + ); + vm.assertEq( + _getDealManagerUpgradeFactory(corp.dealManager()), + address(dmFactory), + string(abi.encodePacked("legacy cyberCorp: ", vm.toString(address(corp)), " should point to the current DealManagerFactory")) + ); + + // Migrate legacy corp's IssuanceManager (beacon-based) + vm.startBroadcast(deployerPrivateKey); + + IssuanceManagerWithFactoryMigration im = IssuanceManagerWithFactoryMigration(corp.issuanceManager()); + im.migrateUpgradeFactory(); + vm.assertEq( + im.getUpgradeFactory(), + address(imFactory), + string(abi.encodePacked("legacy cyberCorp: ", vm.toString(address(corp)), " should point to the current IssuanceManagerFactory after migration")) + ); + + vm.stopBroadcast(); + + // Migrate legacy corp's RoundManager (UUPSUpgradeable-based, need co-approval) + + // TODO WIP: simulate co-approval for now, in production we should use `corpOwnerPrivateKeyLookup` +// vm.startBroadcast(corpOwnerPrivateKeyLookup[corp.companyPayable()]); + vm.startPrank(corp.companyPayable()); + + // Accept round manager upgrade to the temporary implementation with migration feature + RoundManagerWithFactoryMigration rm = RoundManagerWithFactoryMigration(corp.roundManager()); + rm.upgradeToAndCall( + address(rmWithMigrationImpl), + abi.encodeWithSelector(rm.migrateUpgradeFactory.selector) // perform migration atomically + ); + vm.assertEq( + rm.getUpgradeFactory(), + address(rmFactory), + string(abi.encodePacked("legacy cyberCorp: ", vm.toString(address(corp)), " should point to the current RoundManagerFactory after migration")) + ); + +// vm.stopBroadcast(); + vm.stopPrank(); + + console2.log("Migrated legacy CyberCorp: %s", address(corp)); + } + + // Revert to the normal implementation since migration is done + vm.startBroadcast(deployerPrivateKey); + + address imRefImpl = imFactory.getRefImplementation(); + legacyImFactory.upgradeImplementation(imRefImpl); + vm.assertEq(legacyImFactory.getBeaconImplementation(), imRefImpl, "beacon implementation should be upgraded without migration features by now"); + console2.log("Set new beacon implementation (without migration features): %s for legacy CyberCorpSingleFactory: %s", address(imRefImpl), address(legacyImFactory)); + + // No need to revert deprecatingRmFactory.refImplementation() since it is deprecating + + vm.stopBroadcast(); + } + + function _getDealManagerUpgradeFactory(address target) internal view returns (address) { + // `upgradeFactory` is at slot 1 of `DealManagerStorage.STORAGE_POSITION` + bytes32 slotData = vm.load(target, bytes32(uint256(DealManagerStorage.STORAGE_POSITION) + 1)); + return address(uint160(uint256(slotData))); + } +} diff --git a/script/upgrade-public-rounds-base-sep.s.sol b/script/upgrade-public-rounds-base-sep.s.sol new file mode 100644 index 00000000..8cc45373 --- /dev/null +++ b/script/upgrade-public-rounds-base-sep.s.sol @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {CompanyOfficer, SecurityClass, SecuritySeries} from "../src/CyberCorpConstants.sol"; +import {CyberAgreementRegistry} from "../src/CyberAgreementRegistry.sol"; +import {CyberCorpFactory} from "../src/CyberCorpFactory.sol"; +import {IssuanceManagerFactory} from "../src/IssuanceManagerFactory.sol"; +import {IssuanceManager} from "../src/IssuanceManager.sol"; +import {CyberCorpSingleFactory} from "../src/CyberCorpSingleFactory.sol"; +import {DealManagerFactory} from "../src/DealManagerFactory.sol"; +import {DealManager} from "../src/DealManager.sol"; +import {RoundManagerFactory} from "../src/RoundManagerFactory.sol"; +import {RoundManager} from "../src/RoundManager.sol"; +import {CyberCorp} from "../src/CyberCorp.sol"; +import {BorgAuth} from "../src/libs/auth.sol"; +import "openzeppelin-contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {CyberCertPrinter} from "../src/CyberCertPrinter.sol"; +import {CyberScrip} from "../src/CyberScrip.sol"; +import {ILegacyFactory} from "../script/interfaces/ILegacyFactory.sol"; + +interface IUUPS { + function upgradeTo(address newImplementation) external; + function upgradeToAndCall( + address newImplementation, + bytes calldata data + ) external payable; +} + +contract UpgradePublicRoundsBaseSepoliaScript is Script { + function run() public returns ( + IssuanceManagerFactory newImFactory, + RoundManagerFactory roundManagerFactory + ) { + return runWithArgs(vm.envUint("PRIVATE_KEY_MAIN")); + } + + function runWithArgs(uint256 deployerPrivateKey) public returns ( + IssuanceManagerFactory newImFactory, + RoundManagerFactory roundManagerFactory + ) { + address deployer = vm.addr(deployerPrivateKey); + // Config + bytes32 salt = bytes32( + keccak256("MetaLexCyberCorp.PublicRounds.UpgradeV3.0.1") + ); + + // Required existing addresses + address cyberCorpFactoryProxyAddr = 0x51413048f3Dfc4516e95BC8e249341B1D53B6cB2; + address legacyCyberCorpSingleFactoryAddr = 0xc8e084D3f8B3b326FCc894C7afD28F4904196406; + address legacyIssuanceManagerFactoryAddr = 0xA32547aAdAA4975082D729c79e79dBaE4385EBCf; + address registry = 0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134; + address deployedLexChexAddrAuth = 0xeAdeaD5C4A6747D4959489742c143bCDb95a01c2; + address multisig = 0x68Ab3F79622cBe74C9683aA54D7E1BBdCAE8003C; + + address stable; + uint256 currentChainId = block.chainid; + if (currentChainId == 1) { + stable = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // Mainnet + } else if (currentChainId == 42161) { + stable = 0xaf88d065e77c8cC2239327C5EDb3A432268e5831; // Arbitrum + } else if (currentChainId == 8453) { + stable = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913; // Base + } else if (currentChainId == 84532) { + stable = 0x036CbD53842c5426634e7929541eC2318f3dCF7e; // Base Sepolia + } else if (currentChainId == 11155111) { + stable = 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238; // Sepolia + } else { + revert("Unsupported chain ID"); // Handle unsupported chains + } + + CyberCorpFactory factoryProxy = CyberCorpFactory( + cyberCorpFactoryProxyAddr + ); + + // Uses existing AUTH from factory + address auth = address( + CyberCorpFactory(cyberCorpFactoryProxyAddr).AUTH() + ); + vm.assertEq(address(auth), 0x033012a1eDA6e2E00D12CD37c5b63B9440ef5E01, "should match universal AUTH address"); + + console.log("Deployer:", deployer); + uint256 role = BorgAuth(auth).userRoles(deployer); + console.log("Upgrader role:", role); + if (role < BorgAuth(auth).OWNER_ROLE()) { + revert( + "Deployer is not AUTH owner; use the AUTH owner key to upgrade" + ); + } + + vm.startBroadcast(deployerPrivateKey); + + // 1) Deploy RoundManagerFactory + roundManagerFactory = RoundManagerFactory(address( + new ERC1967Proxy{salt: salt}( + address(new RoundManagerFactory{salt: salt}()), + abi.encodeWithSelector( + RoundManagerFactory.initialize.selector, + address(auth), + address(new RoundManager{salt: salt}()) + ) + ) + )); + console.log( + "RoundManagerFactory deployed:", + address(roundManagerFactory) + ); + + roundManagerFactory.setWhitelistedToken(stable, true); + + // 2) Set the RoundManagerFactory address in CyberCorpFactory + factoryProxy.setRoundManagerFactory(address(roundManagerFactory)); + console.log( + "CyberCorpFactory.roundManagerFactory set to:", + address(roundManagerFactory) + ); + + // 3) Deploy new IssuanceManagerFactory + // Deploy new reference implementations + IssuanceManager refIm = new IssuanceManager{salt: salt}(); + CyberCertPrinter refCertPrinter = new CyberCertPrinter{salt: salt}(); + CyberScrip refScrip = new CyberScrip{salt: salt}(); + console.log( + "New IssuanceManager implementation:", + address(refIm) + ); + console.log( + "New CyberCertPrinter implementation:", + address(refCertPrinter) + ); + console.log( + "New CyberScrip implementation:", + address(refScrip) + ); + // Deploy new UUPSUpgradeable + newImFactory = IssuanceManagerFactory( + address( + new ERC1967Proxy{salt: salt}( + address(new IssuanceManagerFactory{salt: salt}()), + abi.encodeWithSelector( + IssuanceManagerFactory.initialize.selector, + address(auth), + address(refIm), + address(refCertPrinter), + address(refScrip) + ) + ) + ) + ); + console.log( + "IssuanceManagerFactory deployed:", + address(newImFactory) + ); + // Replace the old one in CyberCorpFactory + factoryProxy.setIssuanceManagerFactory(address(newImFactory)); + // Verify the upgrade was successful + vm.assertEq(newImFactory.getRefImplementation(), address(refIm), "unexpected IssuanceManager reference implementation"); + console.log( + "CyberCorpFactory.issuanceManagerFactory set to:", + address(newImFactory) + ); + + vm.stopBroadcast(); + } +} diff --git a/src/CyberCorpWithMigrationFactories.sol b/src/CyberCorpWithMigrationFactories.sol new file mode 100644 index 00000000..38f745ea --- /dev/null +++ b/src/CyberCorpWithMigrationFactories.sol @@ -0,0 +1,73 @@ +/* .o. + .888. + .8"888. + .8' `888. + .88ooo8888. + .8' `888. +o88o o8888o + + + +ooo ooooo . ooooo ooooooo ooooo +`88. .888' .o8 `888' `8888 d8' + 888b d'888 .ooooo. .o888oo .oooo. 888 .ooooo. Y888..8P + 8 Y88. .P 888 d88' `88b 888 `P )88b 888 d88' `88b `8888' + 8 `888' 888 888ooo888 888 .oP"888 888 888ooo888 .8PY888. + 8 Y 888 888 .o 888 . d8( 888 888 o 888 .o d8' `888b +o8o o888o `Y8bod8P' "888" `Y888""8o o888ooooood8 `Y8bod8P' o888o o88888o + + + + .oooooo. .o8 .oooooo. + d8P' `Y8b "888 d8P' `Y8b +888 oooo ooo 888oooo. .ooooo. oooo d8b 888 .ooooo. oooo d8b oo.ooooo. +888 `88. .8' d88' `88b d88' `88b `888""8P 888 d88' `88b `888""8P 888' `88b +888 `88..8' 888 888 888ooo888 888 888 888 888 888 888 888 +`88b ooo `888' 888 888 888 .o 888 `88b ooo 888 888 888 888 888 .o. + `Y8bood8P' .8' `Y8bod8P' `Y8bod8P' d888b `Y8bood8P' `Y8bod8P' d888b 888bod8P' Y8P + .o..P' 888 + `Y8P' o888o +_______________________________________________________________________________________________________ + +All software, documentation and other files and information in this repository (collectively, the "Software") +are copyright MetaLeX Labs, Inc., a Delaware corporation. + +All rights reserved. + +The Software is proprietary and shall not, in part or in whole, be used, copied, modified, merged, published, +distributed, transmitted, sublicensed, sold, or otherwise used in any form or by any means, electronic or +mechanical, including photocopying, recording, or by any information storage and retrieval system, +except with the express prior written permission of the copyright holder.*/ + +pragma solidity 0.8.28; + +import {CyberCorp} from "./CyberCorp.sol"; +import {CyberCorpFactory} from "./CyberCorpFactory.sol"; +import {BorgAuth} from "./libs/auth.sol"; +import {IRoundManagerFactory} from "./interfaces/IRoundManagerFactory.sol"; +import {IRoundManagerInit} from "./helpers/RoundManagerUpgradeHelper.sol"; + +contract CyberCorpWithMigrationFactories is CyberCorp { + + // TODO WIP + + address public constant NEW_UPGRADE_FACTORY = 0x50C26cd5750aBbb81A97276A521D70606f4bAFee; // TODO TBD + CyberCorpFactory public constant CYBER_CORP_FACTORY = CyberCorpFactory(0x51413048f3Dfc4516e95BC8e249341B1D53B6cB2); + + /// @notice Migrate legacy contracts and set upgradeFactory to the known new contract (for reference implementation lookup) + /// @dev Since the migration target is predefined, it doesn't matter who called it or when it is called + function migrateUpgradeFactory() public { + // Update to the new permanent address of CyberCorpSingleFactory + upgradeFactory = NEW_UPGRADE_FACTORY; + + // Deploy a new RoundManager if not setup yet + if (roundManager == address(0)) { + // Ask CyberCorpFactory to prepare one for us + bytes32 salt = keccak256(abi.encodePacked("MetaLexCyberCorp.PublicRounds.migration.", address(this))); + roundManager = CYBER_CORP_FACTORY.deployAndInitializeRoundManager(salt, address(this)); + + // Authorize the round manager + AUTH.updateRole(roundManager, 99); + } + } +} diff --git a/src/IssuanceManagerWithFactoryMigration.sol b/src/IssuanceManagerWithFactoryMigration.sol new file mode 100644 index 00000000..74453db9 --- /dev/null +++ b/src/IssuanceManagerWithFactoryMigration.sol @@ -0,0 +1,58 @@ +/* .o. + .888. + .8"888. + .8' `888. + .88ooo8888. + .8' `888. +o88o o8888o + + + +ooo ooooo . ooooo ooooooo ooooo +`88. .888' .o8 `888' `8888 d8' + 888b d'888 .ooooo. .o888oo .oooo. 888 .ooooo. Y888..8P + 8 Y88. .P 888 d88' `88b 888 `P )88b 888 d88' `88b `8888' + 8 `888' 888 888ooo888 888 .oP"888 888 888ooo888 .8PY888. + 8 Y 888 888 .o 888 . d8( 888 888 o 888 .o d8' `888b +o8o o888o `Y8bod8P' "888" `Y888""8o o888ooooood8 `Y8bod8P' o888o o88888o + + + + .oooooo. .o8 .oooooo. + d8P' `Y8b "888 d8P' `Y8b +888 oooo ooo 888oooo. .ooooo. oooo d8b 888 .ooooo. oooo d8b oo.ooooo. +888 `88. .8' d88' `88b d88' `88b `888""8P 888 d88' `88b `888""8P 888' `88b +888 `88..8' 888 888 888ooo888 888 888 888 888 888 888 888 +`88b ooo `888' 888 888 888 .o 888 `88b ooo 888 888 888 888 888 .o. + `Y8bood8P' .8' `Y8bod8P' `Y8bod8P' d888b `Y8bood8P' `Y8bod8P' d888b 888bod8P' Y8P + .o..P' 888 + `Y8P' o888o +_______________________________________________________________________________________________________ + +All software, documentation and other files and information in this repository (collectively, the "Software") +are copyright MetaLeX Labs, Inc., a Delaware corporation. + +All rights reserved. + +The Software is proprietary and shall not, in part or in whole, be used, copied, modified, merged, published, +distributed, transmitted, sublicensed, sold, or otherwise used in any form or by any means, electronic or +mechanical, including photocopying, recording, or by any information storage and retrieval system, +except with the express prior written permission of the copyright holder.*/ + +pragma solidity 0.8.28; + +import {UpgradeableBeacon} from "openzeppelin-contracts/proxy/beacon/UpgradeableBeacon.sol"; +import {IssuanceManager} from "./IssuanceManager.sol"; +import {IssuanceManagerStorage} from "./storage/IssuanceManagerStorage.sol"; +import {IIssuanceManagerFactory} from "./interfaces/IIssuanceManagerFactory.sol"; + +contract IssuanceManagerWithFactoryMigration is IssuanceManager { + + address public constant NEW_UPGRADE_FACTORY = 0xfC115b93116fc501ceDD8C9CfcD816a77d7558d7; // TODO TBD + + /// @notice Migrate legacy contracts and set upgradeFactory to the known new contract (for reference implementation lookup) + /// @dev Since the migration target is predefined, it doesn't matter who called it or when it is called + function migrateUpgradeFactory() public { + IssuanceManagerStorage.setUpgradeFactory(NEW_UPGRADE_FACTORY); + } +} diff --git a/src/RoundManagerWithFactoryMigration.sol b/src/RoundManagerWithFactoryMigration.sol new file mode 100644 index 00000000..1cd1ab37 --- /dev/null +++ b/src/RoundManagerWithFactoryMigration.sol @@ -0,0 +1,62 @@ +/* .o. + .888. + .8"888. + .8' `888. + .88ooo8888. + .8' `888. +o88o o8888o + + + +ooo ooooo . ooooo ooooooo ooooo +`88. .888' .o8 `888' `8888 d8' + 888b d'888 .ooooo. .o888oo .oooo. 888 .ooooo. Y888..8P + 8 Y88. .P 888 d88' `88b 888 `P )88b 888 d88' `88b `8888' + 8 `888' 888 888ooo888 888 .oP"888 888 888ooo888 .8PY888. + 8 Y 888 888 .o 888 . d8( 888 888 o 888 .o d8' `888b +o8o o888o `Y8bod8P' "888" `Y888""8o o888ooooood8 `Y8bod8P' o888o o88888o + + + + .oooooo. .o8 .oooooo. + d8P' `Y8b "888 d8P' `Y8b +888 oooo ooo 888oooo. .ooooo. oooo d8b 888 .ooooo. oooo d8b oo.ooooo. +888 `88. .8' d88' `88b d88' `88b `888""8P 888 d88' `88b `888""8P 888' `88b +888 `88..8' 888 888 888ooo888 888 888 888 888 888 888 888 +`88b ooo `888' 888 888 888 .o 888 `88b ooo 888 888 888 888 888 .o. + `Y8bood8P' .8' `Y8bod8P' `Y8bod8P' d888b `Y8bood8P' `Y8bod8P' d888b 888bod8P' Y8P + .o..P' 888 + `Y8P' o888o +_______________________________________________________________________________________________________ + +All software, documentation and other files and information in this repository (collectively, the "Software") +are copyright MetaLeX Labs, Inc., a Delaware corporation. + +All rights reserved. + +The Software is proprietary and shall not, in part or in whole, be used, copied, modified, merged, published, +distributed, transmitted, sublicensed, sold, or otherwise used in any form or by any means, electronic or +mechanical, including photocopying, recording, or by any information storage and retrieval system, +except with the express prior written permission of the copyright holder.*/ + +pragma solidity 0.8.28; + +import {UpgradeableBeacon} from "openzeppelin-contracts/proxy/beacon/UpgradeableBeacon.sol"; +import {RoundManager} from "./RoundManager.sol"; +import {RoundManagerStorage} from "./storage/RoundManagerStorage.sol"; +import {IRoundManagerFactory} from "./interfaces/IRoundManagerFactory.sol"; + +contract RoundManagerWithFactoryMigration is RoundManager { + + address public constant NEW_UPGRADE_FACTORY = 0xb1914126c0a559F03e321C2d7Ad452D502808285; // TODO TBD + + /// @notice Migrate legacy contracts and set upgradeFactory to the known new contract (for reference implementation lookup) + /// @dev Since the migration target is predefined, it doesn't matter who called it or when it is called + function migrateUpgradeFactory() public { + RoundManagerStorage.setUpgradeFactory(NEW_UPGRADE_FACTORY); + } + + function getUpgradeFactory() public view returns (address) { + return RoundManagerStorage.getUpgradeFactory(); + } +} diff --git a/test/MigrateBaseSepImRmFactoryAddrsTest.t.sol b/test/MigrateBaseSepImRmFactoryAddrsTest.t.sol new file mode 100644 index 00000000..42af8ce1 --- /dev/null +++ b/test/MigrateBaseSepImRmFactoryAddrsTest.t.sol @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {Test, console2} from "forge-std/Test.sol"; +import {ERC20} from "openzeppelin-contracts/token/ERC20/ERC20.sol"; +import {ERC1967Proxy} from "openzeppelin-contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {CyberCorpHelper} from "../test/RoundManagerTest.t.sol"; +import {CyberAgreementUtils} from "../test/libs/CyberAgreementUtils.sol"; +import {UpgradePublicRoundsBaseSepoliaScript} from "../script/upgrade-public-rounds-base-sep.s.sol"; +import {MigrateBaseSepImRmFactoryAddrsScript} from "../script/migrate-base-sep-im-rm-factory-addrs.s.sol"; +import {GnosisTransaction} from "../script/libs/safe.sol"; +import {ILegacyFactory} from "../script/interfaces/ILegacyFactory.sol"; +import {CompanyOfficer, SecurityClass, SecuritySeries} from "../src/CyberCorpConstants.sol"; +import {CyberAgreementRegistry} from "../src/CyberAgreementRegistry.sol"; +import {CyberCorpFactory} from "../src/CyberCorpFactory.sol"; +import {CyberCorpSingleFactory} from "../src/CyberCorpSingleFactory.sol"; +import {IssuanceManager} from "../src/IssuanceManager.sol"; +import {IssuanceManagerFactory} from "../src/IssuanceManagerFactory.sol"; +import {DealManagerFactory} from "../src/DealManagerFactory.sol"; +import {RoundManager} from "../src/RoundManager.sol"; +import {RoundManagerFactory} from "../src/RoundManagerFactory.sol"; +import {LeXcheX} from "../src/creds/lexchex.sol"; +import {CyberCertData, RoundType} from "../src/interfaces/IRoundManager.sol"; +import {EOI, LexChexDetails, MintRequest} from "../src/storage/RoundManagerStorage.sol"; +import {Accreditation} from "../src/creds/storage/lexchexStorage.sol"; +import {LeXcheXMinter} from "../src/creds/lexchexMinter.sol"; +import {BorgAuth} from "../src/libs/auth.sol"; +import {CyberCertPrinter} from "../src/CyberCertPrinter.sol"; +import {CyberScrip} from "../src/CyberScrip.sol"; + +contract MigrateBaseSepImRmFactoryAddrsTest is Test { + address metalexSafe = 0x68Ab3F79622cBe74C9683aA54D7E1BBdCAE8003C; + + // Assume Base-sepolia + ERC20 usdc = ERC20(0x036CbD53842c5426634e7929541eC2318f3dCF7e); + CyberCorpFactory cyberCorpFactory = CyberCorpFactory(0x51413048f3Dfc4516e95BC8e249341B1D53B6cB2); + CyberAgreementRegistry registry = CyberAgreementRegistry(0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134); + BorgAuth lexChexAuth = BorgAuth(0xeAdeaD5C4A6747D4959489742c143bCDb95a01c2); + LeXcheX lexchex = LeXcheX(0xc8db0c3f47656aee725b0AD1835F9A3FbD0a0b62); + LeXcheXMinter leXcheXMinter = LeXcheXMinter(0x0dD1a2a89eC172ac322B6a7a6c869180CBD0F960); + address lexchexConditionAddr = 0x4a08547d57C8d01e59bA8F884aB90CEe0d6d5b42; + + address deployer; + uint256 deployerPrivateKey; + + function setUp() public { + vm.label(address(cyberCorpFactory), "CyberCorpFactory"); + vm.label(address(usdc), "USDC"); + vm.label(address(registry), "CyberAgreementRegistry"); + vm.label(address(lexChexAuth), "lexChexAuth"); + vm.label(address(lexChexAuth), "lexChexAuth"); + vm.label(address(lexchex), "LeXcheX"); + vm.label(address(leXcheXMinter), "LeXcheXMinter"); + vm.label(address(lexchexConditionAddr), "lexchexCondition"); + + (deployer, deployerPrivateKey) = makeAddrAndKey("deployer"); + + // Simulate granting the test deployer admin access so it can perform upgrades + vm.startPrank(metalexSafe); + CyberAgreementRegistry(registry).AUTH().updateRole( + deployer, + CyberAgreementRegistry(registry).AUTH().OWNER_ROLE() + ); + lexChexAuth.updateRole( + deployer, + lexChexAuth.OWNER_ROLE() + ); // so deployer can grant cyberCorpFactory permissions to it + vm.stopPrank(); + + // simulate real deployer deploys IssuanceManagerFactory and RoundManagerFactory on base-sepolia + ( + IssuanceManagerFactory newImFactory, + RoundManagerFactory newRmFactory + ) = (new UpgradePublicRoundsBaseSepoliaScript()).runWithArgs(deployerPrivateKey); + + console2.log("newImFactory:", address(newImFactory)); + console2.log("newRmFactory:", address(newRmFactory)); + + // simulate migrations + (new MigrateBaseSepImRmFactoryAddrsScript()).runWithArgs( + deployerPrivateKey, + new uint256[](0), // TODO WIP + 3 + ); + + // Revoke deployer admin access + vm.startPrank(metalexSafe); + CyberAgreementRegistry(registry).AUTH().updateRole(deployer, 0); + lexChexAuth.updateRole(deployer, 0); + vm.stopPrank(); + } + + function test_SanityCheck() public { + } + + // TODO deprecated +// function _simulateRealBaseSepoliaRedeployment() internal { +// address realDeployer = 0x341Da9fb8F9bD9a775f6bD641091b24Dd9aA459B; +// vm.label(realDeployer, "realDeployer"); +// +// bytes32 salt = bytes32( +// keccak256("MetaLexCyberCorp.PublicRounds.UpgradeV3.0.1") +// ); +// +// address auth = address(cyberCorpFactory.AUTH()); +// +// vm.startPrank(realDeployer); +// +// // 1) Deploy RoundManagerFactory +// RoundManagerFactory roundManagerFactory = RoundManagerFactory(address( +// new ERC1967Proxy{salt: salt}( +// address(new RoundManagerFactory{salt: salt}()), +// abi.encodeWithSelector( +// RoundManagerFactory.initialize.selector, +// address(auth), +// address(new RoundManager{salt: salt}()) +// ) +// ) +// )); +// console2.log( +// "RoundManagerFactory deployed:", +// address(roundManagerFactory) +// ); +// +// roundManagerFactory.setWhitelistedToken(address(usdc), true); +// +// // 2) Set the RoundManagerFactory address in CyberCorpFactory +// cyberCorpFactory.setRoundManagerFactory(address(roundManagerFactory)); +// console2.log( +// "CyberCorpFactory.roundManagerFactory set to:", +// address(roundManagerFactory) +// ); +// +// // 3) Deploy new IssuanceManagerFactory +// // Deploy new reference implementations +// IssuanceManager refIm = new IssuanceManager{salt: salt}(); +// CyberCertPrinter refCertPrinter = new CyberCertPrinter{salt: salt}(); +// CyberScrip refScrip = new CyberScrip{salt: salt}(); +// console2.log( +// "New IssuanceManager implementation:", +// address(refIm) +// ); +// console2.log( +// "New CyberCertPrinter implementation:", +// address(refCertPrinter) +// ); +// console2.log( +// "New CyberScrip implementation:", +// address(refScrip) +// ); +// // Deploy new UUPSUpgradeable +// IssuanceManagerFactory newImFactory = IssuanceManagerFactory( +// address( +// new ERC1967Proxy{salt: salt}( +// address(new IssuanceManagerFactory{salt: salt}()), +// abi.encodeWithSelector( +// IssuanceManagerFactory.initialize.selector, +// address(auth), +// address(refIm), +// address(refCertPrinter), +// address(refScrip) +// ) +// ) +// ) +// ); +// console2.log( +// "IssuanceManagerFactory deployed:", +// address(newImFactory) +// ); +// // Replace the old one in CyberCorpFactory +// cyberCorpFactory.setIssuanceManagerFactory(address(newImFactory)); +// // Verify the upgrade was successful +// vm.assertEq(newImFactory.getRefImplementation(), address(refIm), "unexpected IssuanceManager reference implementation"); +// console2.log( +// "CyberCorpFactory.issuanceManagerFactory set to:", +// address(newImFactory) +// ); +// +// vm.stopPrank(); +// } +} From ec29fd1e2dd8de9e3e0aef04147a716f2ef911c3 Mon Sep 17 00:00:00 2001 From: detoo Date: Wed, 10 Dec 2025 15:05:50 -0800 Subject: [PATCH 2/4] wip: chore: add scripts to replace imFactory and rmFactory on base-sepolia before performing migration --- ...igrate-base-sep-im-rm-factory-addrs.s.sol} | 34 ++++++++++++++----- src/IssuanceManagerWithFactoryMigration.sol | 2 +- src/RoundManagerWithFactoryMigration.sol | 2 +- test/MigrateBaseSepImRmFactoryAddrsTest.t.sol | 4 +-- 4 files changed, 30 insertions(+), 12 deletions(-) rename script/{migrate-base-sep-im-rm-factory-addrs.s.sol => upgrade-and-migrate-base-sep-im-rm-factory-addrs.s.sol} (83%) diff --git a/script/migrate-base-sep-im-rm-factory-addrs.s.sol b/script/upgrade-and-migrate-base-sep-im-rm-factory-addrs.s.sol similarity index 83% rename from script/migrate-base-sep-im-rm-factory-addrs.s.sol rename to script/upgrade-and-migrate-base-sep-im-rm-factory-addrs.s.sol index 3ec9e26f..fe09540f 100644 --- a/script/migrate-base-sep-im-rm-factory-addrs.s.sol +++ b/script/upgrade-and-migrate-base-sep-im-rm-factory-addrs.s.sol @@ -28,7 +28,7 @@ import {DealManager} from "../src/DealManager.sol"; import {RoundManagerFactory} from "../src/RoundManagerFactory.sol"; import {RoundManagerWithFactoryMigration} from "../src/RoundManagerWithFactoryMigration.sol"; -contract MigrateBaseSepImRmFactoryAddrsScript is Script { +contract UpgradeAndMigrateBaseSepImRmFactoryAddrsScript is Script { mapping(address => uint256) private corpOwnerPrivateKeyLookup; function run() public { @@ -60,23 +60,41 @@ contract MigrateBaseSepImRmFactoryAddrsScript is Script { console2.log("loaded corp owners: %d", corpOwnerPrivateKeys.length); CyberCorpFactory cyberCorpFactory = CyberCorpFactory(0x51413048f3Dfc4516e95BC8e249341B1D53B6cB2); - RoundManagerFactory deprecatingRmFactory = RoundManagerFactory(0x9E2A3a07711Ce4b5A2F4D62a5c8f8B5307Af9C34); // deprecated develop-version of v3 RoundManagerFactory - - // Get the current factory addresses CyberCorpSingleFactory cyberCorpSingleFactory = CyberCorpSingleFactory(cyberCorpFactory.cyberCorpSingleFactory()); - IssuanceManagerFactory imFactory = IssuanceManagerFactory(cyberCorpFactory.issuanceManagerFactory()); DealManagerFactory dmFactory = DealManagerFactory(cyberCorpFactory.dealManagerFactory()); - RoundManagerFactory rmFactory = RoundManagerFactory(cyberCorpFactory.roundManagerFactory()); + // Legacy corp's IssuanceManagerFactory (for upgrading beacons) ILegacyFactory legacyImFactory = ILegacyFactory(0xA32547aAdAA4975082D729c79e79dBaE4385EBCf); + // Deprecated develop-version of v3 RoundManagerFactory (we need it later for providing reference implementation with migration features) + IssuanceManagerFactory deprecatingImFactory = IssuanceManagerFactory(0xbbD386D237f3b407E6511A52488850b1Da0cCad2); + RoundManagerFactory deprecatingRmFactory = RoundManagerFactory(0x9E2A3a07711Ce4b5A2F4D62a5c8f8B5307Af9C34); + + // Newly deployed factories that we want to use + IssuanceManagerFactory imFactory = IssuanceManagerFactory(0xD353972D7955F421d94d0eA8c42c88c417F7155A); + RoundManagerFactory rmFactory = RoundManagerFactory(0xc9d5d0DeDD124f9351E5880469f25AB41869aeb9); + + vm.startBroadcast(deployerPrivateKey); + + // Replace CyberCorpFactory's IssuanceManagerFactory with the newly deployed ones + // Set new IssuanceManager's reference implementation to the old one since they are functionally identical + address refIm = deprecatingImFactory.getRefImplementation(); + imFactory.setRefImplementation(refIm); + cyberCorpFactory.setIssuanceManagerFactory(address(imFactory)); + vm.assertEq(cyberCorpFactory.issuanceManagerFactory(), address(imFactory), "unexpected IssuanceManagerFactory"); + vm.assertEq(imFactory.getRefImplementation(), refIm, "unexpected IssuanceManager reference implementation"); + console2.log("CyberCorpFactory.issuanceManagerFactory set to: %s", address(imFactory)); + + // Replace CyberCorpFactory's RoundManagerFactory with the newly deployed ones + cyberCorpFactory.setRoundManagerFactory(address(rmFactory)); + vm.assertEq(cyberCorpFactory.roundManagerFactory(), address(rmFactory), "unexpected RoundManagerFactory"); + console2.log("CyberCorpFactory.roundManagerFactory set to: %s", address(rmFactory)); + // Load all known cyber corps address[] memory knownLegacyCyberCorps = KnownAddressesLoader.load(block.chainid, "/script/res/known-cyber-corps.json", maxCount); // TODO WIP // address[] memory knownDevV3CyberCorps = KnownAddressesLoader.load(block.chainid, "/script/res/known-dev-v3-cyber-corps.json", maxCount); - vm.startBroadcast(deployerPrivateKey); - // Deploy temporary contracts for migration IssuanceManagerWithFactoryMigration imWithMigrationImpl = new IssuanceManagerWithFactoryMigration(); diff --git a/src/IssuanceManagerWithFactoryMigration.sol b/src/IssuanceManagerWithFactoryMigration.sol index 74453db9..f2a4f06f 100644 --- a/src/IssuanceManagerWithFactoryMigration.sol +++ b/src/IssuanceManagerWithFactoryMigration.sol @@ -48,7 +48,7 @@ import {IIssuanceManagerFactory} from "./interfaces/IIssuanceManagerFactory.sol" contract IssuanceManagerWithFactoryMigration is IssuanceManager { - address public constant NEW_UPGRADE_FACTORY = 0xfC115b93116fc501ceDD8C9CfcD816a77d7558d7; // TODO TBD + address public constant NEW_UPGRADE_FACTORY = 0xD353972D7955F421d94d0eA8c42c88c417F7155A; /// @notice Migrate legacy contracts and set upgradeFactory to the known new contract (for reference implementation lookup) /// @dev Since the migration target is predefined, it doesn't matter who called it or when it is called diff --git a/src/RoundManagerWithFactoryMigration.sol b/src/RoundManagerWithFactoryMigration.sol index 1cd1ab37..e9d6e7bc 100644 --- a/src/RoundManagerWithFactoryMigration.sol +++ b/src/RoundManagerWithFactoryMigration.sol @@ -48,7 +48,7 @@ import {IRoundManagerFactory} from "./interfaces/IRoundManagerFactory.sol"; contract RoundManagerWithFactoryMigration is RoundManager { - address public constant NEW_UPGRADE_FACTORY = 0xb1914126c0a559F03e321C2d7Ad452D502808285; // TODO TBD + address public constant NEW_UPGRADE_FACTORY = 0xc9d5d0DeDD124f9351E5880469f25AB41869aeb9; /// @notice Migrate legacy contracts and set upgradeFactory to the known new contract (for reference implementation lookup) /// @dev Since the migration target is predefined, it doesn't matter who called it or when it is called diff --git a/test/MigrateBaseSepImRmFactoryAddrsTest.t.sol b/test/MigrateBaseSepImRmFactoryAddrsTest.t.sol index 42af8ce1..bb202182 100644 --- a/test/MigrateBaseSepImRmFactoryAddrsTest.t.sol +++ b/test/MigrateBaseSepImRmFactoryAddrsTest.t.sol @@ -7,7 +7,7 @@ import {ERC1967Proxy} from "openzeppelin-contracts/proxy/ERC1967/ERC1967Proxy.so import {CyberCorpHelper} from "../test/RoundManagerTest.t.sol"; import {CyberAgreementUtils} from "../test/libs/CyberAgreementUtils.sol"; import {UpgradePublicRoundsBaseSepoliaScript} from "../script/upgrade-public-rounds-base-sep.s.sol"; -import {MigrateBaseSepImRmFactoryAddrsScript} from "../script/migrate-base-sep-im-rm-factory-addrs.s.sol"; +import {UpgradeAndMigrateBaseSepImRmFactoryAddrsScript} from "../script/upgrade-and-migrate-base-sep-im-rm-factory-addrs.s.sol"; import {GnosisTransaction} from "../script/libs/safe.sol"; import {ILegacyFactory} from "../script/interfaces/ILegacyFactory.sol"; import {CompanyOfficer, SecurityClass, SecuritySeries} from "../src/CyberCorpConstants.sol"; @@ -77,7 +77,7 @@ contract MigrateBaseSepImRmFactoryAddrsTest is Test { console2.log("newRmFactory:", address(newRmFactory)); // simulate migrations - (new MigrateBaseSepImRmFactoryAddrsScript()).runWithArgs( + (new UpgradeAndMigrateBaseSepImRmFactoryAddrsScript()).runWithArgs( deployerPrivateKey, new uint256[](0), // TODO WIP 3 From 7f2edb051f11cf685c6f9b15c50d1ade4bd822eb Mon Sep 17 00:00:00 2001 From: detoo Date: Wed, 10 Dec 2025 15:32:23 -0800 Subject: [PATCH 3/4] chore: add scripts to migrate dev-v3 corps --- script/res/known-cyber-corps-v3-dev.json | 96 +++++++++++++++++++ ...migrate-base-sep-im-rm-factory-addrs.s.sol | 77 ++++++++++++--- 2 files changed, 162 insertions(+), 11 deletions(-) create mode 100644 script/res/known-cyber-corps-v3-dev.json diff --git a/script/res/known-cyber-corps-v3-dev.json b/script/res/known-cyber-corps-v3-dev.json new file mode 100644 index 00000000..78169c52 --- /dev/null +++ b/script/res/known-cyber-corps-v3-dev.json @@ -0,0 +1,96 @@ +{ + "data": [ + { + "chainId": 84532, + "address": "0x738B533fB2a4183b69E38c14E359a2FcaB5Fbc87" + }, + { + "chainId": 84532, + "address": "0x6e71076A1396E468490ed5E0F427b895E8F01E09" + }, + { + "chainId": 84532, + "address": "0x274c7B5edc5f088D880839a0d1287e5DA7Df3504" + }, + { + "chainId": 84532, + "address": "0x08D471C689e8aaB6dF2ea6860daa039C036810c2" + }, + { + "chainId": 84532, + "address": "0x1f1592BEAa33AF86C34cA6AB42b03E812e331e2D" + }, + { + "chainId": 84532, + "address": "0x62F767A4538dFF18c43EAAeA84ec721B7f289a1d" + }, + { + "chainId": 84532, + "address": "0xa2611914370b8ff3973573833C1cAe2D4bc754e6" + }, + { + "chainId": 84532, + "address": "0xFC870033B8DD920aE0227F2dbBE984EFE62F850C" + }, + { + "chainId": 84532, + "address": "0x3Aa81226f2E952fEdf6055c8c909F620b89B68aF" + }, + { + "chainId": 84532, + "address": "0xc2Bb4af9b2d65Ec277F813FCF0b17cbc5bD9DDa1" + }, + { + "chainId": 84532, + "address": "0x170a7b6225992a3605C6DcB5E0E46396f4E4E3B6" + }, + { + "chainId": 84532, + "address": "0x085073B6e8cbDAE2E24668FCfE212bBD56Cc2dB3" + }, + { + "chainId": 84532, + "address": "0x0423Aa59402624D6f17685DF388B682be515A978" + }, + { + "chainId": 84532, + "address": "0x9C33033f1D7E7316c936aBb30A368F93c1D02CCf" + }, + { + "chainId": 84532, + "address": "0x8412329B3CB87D125Adfc14904DAE896424b8d01" + }, + { + "chainId": 84532, + "address": "0x71E5249396811547d9C654A29153eF3Cc0AA045d" + }, + { + "chainId": 84532, + "address": "0xB49c65A5626f266a735cb319Af4b623D5A8dAC5e" + }, + { + "chainId": 84532, + "address": "0xD2cD7416F07381203b4e5096667E7483cb371A37" + }, + { + "chainId": 84532, + "address": "0x3231DF81Db0F50680067b627C4335509A3D74613" + }, + { + "chainId": 84532, + "address": "0xD8e877608a71A1d79181178d84C76989D7ea1DfB" + }, + { + "chainId": 84532, + "address": "0x074fb622a4A2CCB6dB81b2871Bd49f379b6E20c8" + }, + { + "chainId": 84532, + "address": "0x89812667009AA0B51b19a4Ed0228b0920d8F5d91" + }, + { + "chainId": 84532, + "address": "0x04812316D6a64B32d5DdEe21F8E12d2a87bbF32A" + } + ] +} diff --git a/script/upgrade-and-migrate-base-sep-im-rm-factory-addrs.s.sol b/script/upgrade-and-migrate-base-sep-im-rm-factory-addrs.s.sol index fe09540f..2d0f3e63 100644 --- a/script/upgrade-and-migrate-base-sep-im-rm-factory-addrs.s.sol +++ b/script/upgrade-and-migrate-base-sep-im-rm-factory-addrs.s.sol @@ -76,7 +76,7 @@ contract UpgradeAndMigrateBaseSepImRmFactoryAddrsScript is Script { vm.startBroadcast(deployerPrivateKey); - // Replace CyberCorpFactory's IssuanceManagerFactory with the newly deployed ones + // 1) Replace CyberCorpFactory's IssuanceManagerFactory with the newly deployed ones // Set new IssuanceManager's reference implementation to the old one since they are functionally identical address refIm = deprecatingImFactory.getRefImplementation(); imFactory.setRefImplementation(refIm); @@ -85,17 +85,16 @@ contract UpgradeAndMigrateBaseSepImRmFactoryAddrsScript is Script { vm.assertEq(imFactory.getRefImplementation(), refIm, "unexpected IssuanceManager reference implementation"); console2.log("CyberCorpFactory.issuanceManagerFactory set to: %s", address(imFactory)); - // Replace CyberCorpFactory's RoundManagerFactory with the newly deployed ones + // 2) Replace CyberCorpFactory's RoundManagerFactory with the newly deployed ones cyberCorpFactory.setRoundManagerFactory(address(rmFactory)); vm.assertEq(cyberCorpFactory.roundManagerFactory(), address(rmFactory), "unexpected RoundManagerFactory"); console2.log("CyberCorpFactory.roundManagerFactory set to: %s", address(rmFactory)); // Load all known cyber corps address[] memory knownLegacyCyberCorps = KnownAddressesLoader.load(block.chainid, "/script/res/known-cyber-corps.json", maxCount); - // TODO WIP -// address[] memory knownDevV3CyberCorps = KnownAddressesLoader.load(block.chainid, "/script/res/known-dev-v3-cyber-corps.json", maxCount); + address[] memory knownDevV3CyberCorps = KnownAddressesLoader.load(block.chainid, "/script/res/known-cyber-corps-v3-dev.json", maxCount); - // Deploy temporary contracts for migration + // 3) Deploy temporary contracts for migration IssuanceManagerWithFactoryMigration imWithMigrationImpl = new IssuanceManagerWithFactoryMigration(); RoundManagerWithFactoryMigration rmWithMigrationImpl = new RoundManagerWithFactoryMigration(); @@ -112,17 +111,20 @@ contract UpgradeAndMigrateBaseSepImRmFactoryAddrsScript is Script { string(abi.encodePacked("RoundManagerWithFactoryMigration.NEW_UPGRADE_FACTORY should point to the current factory")) ); - // Upgrade issuance manager beacon to a special implementation with migration features + // 4a) Upgrade issuance manager beacon to a special implementation with migration features legacyImFactory.upgradeImplementation(address(imWithMigrationImpl)); vm.assertEq(legacyImFactory.getBeaconImplementation(), address(imWithMigrationImpl), "beacon implementation should be upgraded with migration features by now"); console2.log("Set new beacon implementation (with migration features): %s for legacy IssuanceManagerFactory: %s", address(imWithMigrationImpl), address(legacyImFactory)); - // Set the reference implementation on the deprecating RoundManagerFactory (so the corps can accept it) + // 4b) Set the reference implementation on the deprecating IssuanceManagerFactory (so the dev-v3 corps can accept it) + deprecatingImFactory.setRefImplementation(address(imWithMigrationImpl)); + + // 4c) Set the reference implementation on the deprecating RoundManagerFactory (so the corps can accept it) deprecatingRmFactory.setRefImplementation(address(rmWithMigrationImpl)); vm.stopBroadcast(); - // Migrate each legacy corp one-by-one + // 5) Migrate each legacy corp one-by-one for (uint256 i = 0; i < knownLegacyCyberCorps.length; i++) { CyberCorp corp = CyberCorp(knownLegacyCyberCorps[i]); @@ -175,15 +177,68 @@ contract UpgradeAndMigrateBaseSepImRmFactoryAddrsScript is Script { console2.log("Migrated legacy CyberCorp: %s", address(corp)); } - // Revert to the normal implementation since migration is done + // 6) Migrate each dev-v3 corp one-by-one + for (uint256 i = 0; i < knownDevV3CyberCorps.length; i++) { + CyberCorp corp = CyberCorp(knownDevV3CyberCorps[i]); + + // Sanity check: all other factories should match + vm.assertEq( + corp.upgradeFactory(), + address(cyberCorpSingleFactory), + string(abi.encodePacked("legacy cyberCorp: ", vm.toString(address(corp)), " should point to the current CyberCorpSingleFactory")) + ); + vm.assertEq( + _getDealManagerUpgradeFactory(corp.dealManager()), + address(dmFactory), + string(abi.encodePacked("legacy cyberCorp: ", vm.toString(address(corp)), " should point to the current DealManagerFactory")) + ); + + // TODO WIP: simulate co-approval for now, in production we should use `corpOwnerPrivateKeyLookup` +// vm.startBroadcast(corpOwnerPrivateKeyLookup[corp.companyPayable()]); + vm.startPrank(corp.companyPayable()); + + // Migrate legacy corp's IssuanceManager (UUPSUpgradeable-based, need co-approval) + // Accept round manager upgrade to the temporary implementation with migration feature + IssuanceManagerWithFactoryMigration im = IssuanceManagerWithFactoryMigration(corp.issuanceManager()); + im.upgradeToAndCall( + address(imWithMigrationImpl), + abi.encodeWithSelector(im.migrateUpgradeFactory.selector) // perform migration atomically + ); + vm.assertEq( + im.getUpgradeFactory(), + address(imFactory), + string(abi.encodePacked("legacy cyberCorp: ", vm.toString(address(corp)), " should point to the current IssuanceManagerFactory after migration")) + ); + + // Migrate legacy corp's RoundManager (UUPSUpgradeable-based, need co-approval) + // Accept round manager upgrade to the temporary implementation with migration feature + RoundManagerWithFactoryMigration rm = RoundManagerWithFactoryMigration(corp.roundManager()); + rm.upgradeToAndCall( + address(rmWithMigrationImpl), + abi.encodeWithSelector(rm.migrateUpgradeFactory.selector) // perform migration atomically + ); + vm.assertEq( + rm.getUpgradeFactory(), + address(rmFactory), + string(abi.encodePacked("legacy cyberCorp: ", vm.toString(address(corp)), " should point to the current RoundManagerFactory after migration")) + ); + +// vm.stopBroadcast(); + vm.stopPrank(); + + console2.log("Migrated dev-v3 CyberCorp: %s", address(corp)); + } + + // 7a) Revert to the normal implementation since migration is done vm.startBroadcast(deployerPrivateKey); address imRefImpl = imFactory.getRefImplementation(); legacyImFactory.upgradeImplementation(imRefImpl); vm.assertEq(legacyImFactory.getBeaconImplementation(), imRefImpl, "beacon implementation should be upgraded without migration features by now"); - console2.log("Set new beacon implementation (without migration features): %s for legacy CyberCorpSingleFactory: %s", address(imRefImpl), address(legacyImFactory)); + console2.log("Set new beacon implementation (without migration features): %s for legacy IssuanceManagerFactory: %s", address(imRefImpl), address(legacyImFactory)); - // No need to revert deprecatingRmFactory.refImplementation() since it is deprecating + // 7b) No need to revert deprecatingImFactory.refImplementation() since it is deprecating + // 7c) No need to revert deprecatingRmFactory.refImplementation() since it is deprecating vm.stopBroadcast(); } From af4980e0957e4b9ecc4a2f6025f3ed2cf2d58a44 Mon Sep 17 00:00:00 2001 From: detoo Date: Wed, 10 Dec 2025 15:52:22 -0800 Subject: [PATCH 4/4] chore: scripts to load test corp owner private keys to accept upgrades for them --- ...migrate-base-sep-im-rm-factory-addrs.s.sol | 33 ++++--- test/MigrateBaseSepImRmFactoryAddrsTest.t.sol | 99 +------------------ 2 files changed, 22 insertions(+), 110 deletions(-) diff --git a/script/upgrade-and-migrate-base-sep-im-rm-factory-addrs.s.sol b/script/upgrade-and-migrate-base-sep-im-rm-factory-addrs.s.sol index 2d0f3e63..739be3d8 100644 --- a/script/upgrade-and-migrate-base-sep-im-rm-factory-addrs.s.sol +++ b/script/upgrade-and-migrate-base-sep-im-rm-factory-addrs.s.sol @@ -34,7 +34,7 @@ contract UpgradeAndMigrateBaseSepImRmFactoryAddrsScript is Script { function run() public { runWithArgs( vm.envUint("PRIVATE_KEY_MAIN"), // deployerPrivateKey - new uint256[](0), // corpOwnerPrivateKeys + vm.envUint("CORP_OWNER_PKS", ","), // corpOwnerPrivateKeys type(uint256).max // maxCount ); } @@ -128,6 +128,13 @@ contract UpgradeAndMigrateBaseSepImRmFactoryAddrsScript is Script { for (uint256 i = 0; i < knownLegacyCyberCorps.length; i++) { CyberCorp corp = CyberCorp(knownLegacyCyberCorps[i]); + // If we don't have the corp owner's private key, we will skip migrating the corp completely so it does not stuck in an intermediate state + uint256 corpOwnerPrivateKey = corpOwnerPrivateKeyLookup[corp.companyPayable()]; + if (corpOwnerPrivateKey == 0) { + console2.log("private key not found for legacy corp owner: %s, skipping corp: %s", corp.companyPayable(), address(corp)); + continue; + } + // Sanity check: all other factories should match vm.assertEq( corp.upgradeFactory(), @@ -154,10 +161,7 @@ contract UpgradeAndMigrateBaseSepImRmFactoryAddrsScript is Script { vm.stopBroadcast(); // Migrate legacy corp's RoundManager (UUPSUpgradeable-based, need co-approval) - - // TODO WIP: simulate co-approval for now, in production we should use `corpOwnerPrivateKeyLookup` -// vm.startBroadcast(corpOwnerPrivateKeyLookup[corp.companyPayable()]); - vm.startPrank(corp.companyPayable()); + vm.startBroadcast(corpOwnerPrivateKey); // Accept round manager upgrade to the temporary implementation with migration feature RoundManagerWithFactoryMigration rm = RoundManagerWithFactoryMigration(corp.roundManager()); @@ -171,8 +175,7 @@ contract UpgradeAndMigrateBaseSepImRmFactoryAddrsScript is Script { string(abi.encodePacked("legacy cyberCorp: ", vm.toString(address(corp)), " should point to the current RoundManagerFactory after migration")) ); -// vm.stopBroadcast(); - vm.stopPrank(); + vm.stopBroadcast(); console2.log("Migrated legacy CyberCorp: %s", address(corp)); } @@ -181,6 +184,13 @@ contract UpgradeAndMigrateBaseSepImRmFactoryAddrsScript is Script { for (uint256 i = 0; i < knownDevV3CyberCorps.length; i++) { CyberCorp corp = CyberCorp(knownDevV3CyberCorps[i]); + // If we don't have the corp owner's private key, we will skip migrating the corp completely so it does not stuck in an intermediate state + uint256 corpOwnerPrivateKey = corpOwnerPrivateKeyLookup[corp.companyPayable()]; + if (corpOwnerPrivateKey == 0) { + console2.log("private key not found for dev-v3 corp owner: %s, skipping corp: %s", corp.companyPayable(), address(corp)); + continue; + } + // Sanity check: all other factories should match vm.assertEq( corp.upgradeFactory(), @@ -192,10 +202,8 @@ contract UpgradeAndMigrateBaseSepImRmFactoryAddrsScript is Script { address(dmFactory), string(abi.encodePacked("legacy cyberCorp: ", vm.toString(address(corp)), " should point to the current DealManagerFactory")) ); - - // TODO WIP: simulate co-approval for now, in production we should use `corpOwnerPrivateKeyLookup` -// vm.startBroadcast(corpOwnerPrivateKeyLookup[corp.companyPayable()]); - vm.startPrank(corp.companyPayable()); + + vm.startBroadcast(corpOwnerPrivateKey); // Migrate legacy corp's IssuanceManager (UUPSUpgradeable-based, need co-approval) // Accept round manager upgrade to the temporary implementation with migration feature @@ -223,8 +231,7 @@ contract UpgradeAndMigrateBaseSepImRmFactoryAddrsScript is Script { string(abi.encodePacked("legacy cyberCorp: ", vm.toString(address(corp)), " should point to the current RoundManagerFactory after migration")) ); -// vm.stopBroadcast(); - vm.stopPrank(); + vm.stopBroadcast(); console2.log("Migrated dev-v3 CyberCorp: %s", address(corp)); } diff --git a/test/MigrateBaseSepImRmFactoryAddrsTest.t.sol b/test/MigrateBaseSepImRmFactoryAddrsTest.t.sol index bb202182..d4424ba4 100644 --- a/test/MigrateBaseSepImRmFactoryAddrsTest.t.sol +++ b/test/MigrateBaseSepImRmFactoryAddrsTest.t.sol @@ -67,20 +67,11 @@ contract MigrateBaseSepImRmFactoryAddrsTest is Test { ); // so deployer can grant cyberCorpFactory permissions to it vm.stopPrank(); - // simulate real deployer deploys IssuanceManagerFactory and RoundManagerFactory on base-sepolia - ( - IssuanceManagerFactory newImFactory, - RoundManagerFactory newRmFactory - ) = (new UpgradePublicRoundsBaseSepoliaScript()).runWithArgs(deployerPrivateKey); - - console2.log("newImFactory:", address(newImFactory)); - console2.log("newRmFactory:", address(newRmFactory)); - // simulate migrations (new UpgradeAndMigrateBaseSepImRmFactoryAddrsScript()).runWithArgs( deployerPrivateKey, - new uint256[](0), // TODO WIP - 3 + vm.envUint("CORP_OWNER_PKS", ","), // deployer is also the test corp owner + 4 ); // Revoke deployer admin access @@ -92,90 +83,4 @@ contract MigrateBaseSepImRmFactoryAddrsTest is Test { function test_SanityCheck() public { } - - // TODO deprecated -// function _simulateRealBaseSepoliaRedeployment() internal { -// address realDeployer = 0x341Da9fb8F9bD9a775f6bD641091b24Dd9aA459B; -// vm.label(realDeployer, "realDeployer"); -// -// bytes32 salt = bytes32( -// keccak256("MetaLexCyberCorp.PublicRounds.UpgradeV3.0.1") -// ); -// -// address auth = address(cyberCorpFactory.AUTH()); -// -// vm.startPrank(realDeployer); -// -// // 1) Deploy RoundManagerFactory -// RoundManagerFactory roundManagerFactory = RoundManagerFactory(address( -// new ERC1967Proxy{salt: salt}( -// address(new RoundManagerFactory{salt: salt}()), -// abi.encodeWithSelector( -// RoundManagerFactory.initialize.selector, -// address(auth), -// address(new RoundManager{salt: salt}()) -// ) -// ) -// )); -// console2.log( -// "RoundManagerFactory deployed:", -// address(roundManagerFactory) -// ); -// -// roundManagerFactory.setWhitelistedToken(address(usdc), true); -// -// // 2) Set the RoundManagerFactory address in CyberCorpFactory -// cyberCorpFactory.setRoundManagerFactory(address(roundManagerFactory)); -// console2.log( -// "CyberCorpFactory.roundManagerFactory set to:", -// address(roundManagerFactory) -// ); -// -// // 3) Deploy new IssuanceManagerFactory -// // Deploy new reference implementations -// IssuanceManager refIm = new IssuanceManager{salt: salt}(); -// CyberCertPrinter refCertPrinter = new CyberCertPrinter{salt: salt}(); -// CyberScrip refScrip = new CyberScrip{salt: salt}(); -// console2.log( -// "New IssuanceManager implementation:", -// address(refIm) -// ); -// console2.log( -// "New CyberCertPrinter implementation:", -// address(refCertPrinter) -// ); -// console2.log( -// "New CyberScrip implementation:", -// address(refScrip) -// ); -// // Deploy new UUPSUpgradeable -// IssuanceManagerFactory newImFactory = IssuanceManagerFactory( -// address( -// new ERC1967Proxy{salt: salt}( -// address(new IssuanceManagerFactory{salt: salt}()), -// abi.encodeWithSelector( -// IssuanceManagerFactory.initialize.selector, -// address(auth), -// address(refIm), -// address(refCertPrinter), -// address(refScrip) -// ) -// ) -// ) -// ); -// console2.log( -// "IssuanceManagerFactory deployed:", -// address(newImFactory) -// ); -// // Replace the old one in CyberCorpFactory -// cyberCorpFactory.setIssuanceManagerFactory(address(newImFactory)); -// // Verify the upgrade was successful -// vm.assertEq(newImFactory.getRefImplementation(), address(refIm), "unexpected IssuanceManager reference implementation"); -// console2.log( -// "CyberCorpFactory.issuanceManagerFactory set to:", -// address(newImFactory) -// ); -// -// vm.stopPrank(); -// } }