// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "forge-std/Script.sol"; import "../src/eMoneyToken.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "./helpers/EnvValidation.sol"; /** * @title UpgradeScript * @notice Script for upgrading eMoneyToken implementation * @dev Deploys new implementation and optionally authorizes upgrade. * IMPORTANT: In production, upgrade authorization should be done via multisig, not this script. */ contract UpgradeScript is Script { using EnvValidation for string; function run() external { // Validate environment variables uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); address tokenProxyAddr = vm.envAddress("TOKEN_PROXY_ADDRESS"); EnvValidation.validateAddress(tokenProxyAddr, "TOKEN_PROXY_ADDRESS"); vm.startBroadcast(deployerPrivateKey); address deployer = vm.addr(deployerPrivateKey); console.log("Deployer address:", deployer); console.log("Token Proxy Address:", vm.toString(tokenProxyAddr)); console.log(""); // Get current implementation address eMoneyToken tokenProxy = eMoneyToken(tokenProxyAddr); address currentImplementation = _getImplementation(tokenProxyAddr); console.log("Current Implementation:", vm.toString(currentImplementation)); console.log(""); // Deploy new implementation console.log("Deploying new eMoneyToken implementation..."); eMoneyToken newImplementation = new eMoneyToken(); address newImplementationAddr = address(newImplementation); console.log("New Implementation deployed at:", vm.toString(newImplementationAddr)); console.log(""); // Verify new implementation has code require(newImplementationAddr.code.length > 0, "UpgradeScript: new implementation has no code"); console.log(" [OK] New implementation has code"); console.log(""); // Check if deployer has DEFAULT_ADMIN_ROLE (required for upgrade) bytes32 adminRole = tokenProxy.DEFAULT_ADMIN_ROLE(); bool hasAdminRole = tokenProxy.hasRole(adminRole, deployer); if (!hasAdminRole) { console.log("⚠️ WARNING: Deployer does not have DEFAULT_ADMIN_ROLE"); console.log(" Upgrade authorization must be done via multisig with DEFAULT_ADMIN_ROLE"); console.log(""); console.log("=== Manual Upgrade Required ==="); console.log("Call the following function from a multisig with DEFAULT_ADMIN_ROLE:"); console.log(" tokenProxy.upgradeTo(", vm.toString(newImplementationAddr), ")"); console.log(""); } else { console.log(" [OK] Deployer has DEFAULT_ADMIN_ROLE"); console.log(""); // Ask for confirmation before upgrading console.log("⚠️ WARNING: This will upgrade the token implementation!"); console.log(" Current Implementation:", vm.toString(currentImplementation)); console.log(" New Implementation:", vm.toString(newImplementationAddr)); console.log(""); console.log("To proceed with upgrade, uncomment the following line:"); console.log(" // tokenProxy.upgradeTo(newImplementationAddr);"); console.log(""); // Uncomment the following line to actually perform the upgrade // tokenProxy.upgradeTo(newImplementationAddr); console.log(" [SKIP] Upgrade not executed (commented out for safety)"); console.log(" [INFO] To execute upgrade, uncomment the upgradeTo() call above"); } console.log(""); console.log("=== Upgrade Preparation Complete ==="); console.log("New Implementation:", vm.toString(newImplementationAddr)); console.log(""); console.log("=== Next Steps ==="); console.log("1. Verify storage layout compatibility (run tools/validate-storage-layout.sh)"); console.log("2. Test upgrade on testnet"); console.log("3. Get multisig approval"); console.log("4. Execute upgrade via multisig:"); console.log(" tokenProxy.upgradeTo(", vm.toString(newImplementationAddr), ")"); console.log("5. Run VerifyUpgrade.s.sol to verify the upgrade"); vm.stopBroadcast(); } /** * @notice Gets the implementation address from a UUPS proxy * @param proxyAddr The proxy address * @return implementation The implementation address */ function _getImplementation(address proxyAddr) internal view returns (address implementation) { // ERC1967 implementation slot: keccak256("eip1967.proxy.implementation") - 1 bytes32 slot = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; assembly { implementation := sload(slot) } } }