Files
script/Upgrade.s.sol
2026-02-09 21:51:51 -08:00

109 lines
4.8 KiB
Solidity

// 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)
}
}
}