Initial commit: add .gitignore and README
This commit is contained in:
49
.gitignore
vendored
Normal file
49
.gitignore
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
.pnpm-store/
|
||||
vendor/
|
||||
|
||||
# Package manager lock files (optional: uncomment to ignore)
|
||||
# package-lock.json
|
||||
# yarn.lock
|
||||
|
||||
# Environment and secrets
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
*.env.backup
|
||||
.env.backup.*
|
||||
|
||||
# Logs and temp
|
||||
*.log
|
||||
logs/
|
||||
*.tmp
|
||||
*.temp
|
||||
*.tmp.*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Build / output
|
||||
dist/
|
||||
build/
|
||||
.next/
|
||||
out/
|
||||
*.pyc
|
||||
__pycache__/
|
||||
.eggs/
|
||||
*.egg-info/
|
||||
.coverage
|
||||
htmlcov/
|
||||
|
||||
# Optional
|
||||
.reports/
|
||||
reports/
|
||||
91
AuthorizeUpgrade.s.sol
Normal file
91
AuthorizeUpgrade.s.sol
Normal file
@@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import "../src/eMoneyToken.sol";
|
||||
import "./helpers/EnvValidation.sol";
|
||||
|
||||
/**
|
||||
* @title AuthorizeUpgrade
|
||||
* @notice Helper script to authorize a token upgrade
|
||||
* @dev This script should be run from a multisig wallet with DEFAULT_ADMIN_ROLE.
|
||||
* IMPORTANT: Only use this after thorough testing and multisig approval.
|
||||
*/
|
||||
contract AuthorizeUpgrade 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");
|
||||
address newImplementationAddr = vm.envAddress("NEW_IMPLEMENTATION_ADDRESS");
|
||||
|
||||
EnvValidation.validateAddress(tokenProxyAddr, "TOKEN_PROXY_ADDRESS");
|
||||
EnvValidation.validateAddress(newImplementationAddr, "NEW_IMPLEMENTATION_ADDRESS");
|
||||
|
||||
vm.startBroadcast(deployerPrivateKey);
|
||||
|
||||
address deployer = vm.addr(deployerPrivateKey);
|
||||
eMoneyToken tokenProxy = eMoneyToken(tokenProxyAddr);
|
||||
|
||||
console.log("=== Authorize Upgrade ===");
|
||||
console.log("Deployer:", deployer);
|
||||
console.log("Token Proxy:", vm.toString(tokenProxyAddr));
|
||||
console.log("New Implementation:", vm.toString(newImplementationAddr));
|
||||
console.log("");
|
||||
|
||||
// Verify deployer has DEFAULT_ADMIN_ROLE
|
||||
bytes32 adminRole = tokenProxy.DEFAULT_ADMIN_ROLE();
|
||||
require(
|
||||
tokenProxy.hasRole(adminRole, deployer),
|
||||
"AuthorizeUpgrade: deployer does not have DEFAULT_ADMIN_ROLE"
|
||||
);
|
||||
console.log(" [OK] Deployer has DEFAULT_ADMIN_ROLE");
|
||||
console.log("");
|
||||
|
||||
// Verify new implementation has code
|
||||
require(newImplementationAddr.code.length > 0, "AuthorizeUpgrade: new implementation has no code");
|
||||
console.log(" [OK] New implementation has code");
|
||||
console.log("");
|
||||
|
||||
// Get current implementation
|
||||
address currentImplementation = _getImplementation(tokenProxyAddr);
|
||||
console.log("Current Implementation:", vm.toString(currentImplementation));
|
||||
console.log("New Implementation:", vm.toString(newImplementationAddr));
|
||||
console.log("");
|
||||
|
||||
// Confirm upgrade
|
||||
console.log("⚠️ WARNING: This will upgrade the token implementation!");
|
||||
console.log(" Make sure you have:");
|
||||
console.log(" 1. Validated storage layout compatibility");
|
||||
console.log(" 2. Tested on testnet");
|
||||
console.log(" 3. Received multisig approval");
|
||||
console.log("");
|
||||
console.log("To proceed, 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");
|
||||
|
||||
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) {
|
||||
bytes32 slot = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
|
||||
assembly {
|
||||
implementation := sload(slot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
7
README.md
Normal file
7
README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# script
|
||||
|
||||
Project under `/home/intlc/projects/script`.
|
||||
|
||||
## Overview
|
||||
|
||||
(Add project description and setup instructions here.)
|
||||
108
Upgrade.s.sol
Normal file
108
Upgrade.s.sol
Normal file
@@ -0,0 +1,108 @@
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
138
VerifyUpgrade.s.sol
Normal file
138
VerifyUpgrade.s.sol
Normal file
@@ -0,0 +1,138 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import "../src/eMoneyToken.sol";
|
||||
import "../src/PolicyManager.sol";
|
||||
import "../src/ComplianceRegistry.sol";
|
||||
import "../src/DebtRegistry.sol";
|
||||
import "./helpers/EnvValidation.sol";
|
||||
|
||||
/**
|
||||
* @title VerifyUpgrade
|
||||
* @notice Verifies that token upgrade was successful and functionality is preserved
|
||||
* @dev Run this script after upgrading to validate the upgrade
|
||||
*/
|
||||
contract VerifyUpgrade is Script {
|
||||
using EnvValidation for string;
|
||||
|
||||
function run() external view {
|
||||
// Get addresses from environment
|
||||
address tokenProxyAddr = vm.envAddress("TOKEN_PROXY_ADDRESS");
|
||||
address expectedImplementation = vm.envOr("EXPECTED_IMPLEMENTATION", address(0));
|
||||
address policyManagerAddr = vm.envAddress("POLICY_MANAGER");
|
||||
address complianceRegistryAddr = vm.envAddress("COMPLIANCE_REGISTRY");
|
||||
address debtRegistryAddr = vm.envAddress("DEBT_REGISTRY");
|
||||
|
||||
EnvValidation.validateAddress(tokenProxyAddr, "TOKEN_PROXY_ADDRESS");
|
||||
EnvValidation.validateAddress(policyManagerAddr, "POLICY_MANAGER");
|
||||
EnvValidation.validateAddress(complianceRegistryAddr, "COMPLIANCE_REGISTRY");
|
||||
EnvValidation.validateAddress(debtRegistryAddr, "DEBT_REGISTRY");
|
||||
|
||||
console.log("=== Upgrade Verification ===");
|
||||
console.log("");
|
||||
|
||||
eMoneyToken tokenProxy = eMoneyToken(tokenProxyAddr);
|
||||
|
||||
// Verify implementation address
|
||||
console.log("Verifying implementation address...");
|
||||
address currentImplementation = _getImplementation(tokenProxyAddr);
|
||||
console.log(" Current Implementation:", vm.toString(currentImplementation));
|
||||
|
||||
if (expectedImplementation != address(0)) {
|
||||
EnvValidation.validateAddress(expectedImplementation, "EXPECTED_IMPLEMENTATION");
|
||||
require(
|
||||
currentImplementation == expectedImplementation,
|
||||
"VerifyUpgrade: implementation mismatch"
|
||||
);
|
||||
console.log(" [OK] Implementation matches expected:", vm.toString(expectedImplementation));
|
||||
} else {
|
||||
console.log(" [INFO] No expected implementation provided, skipping match check");
|
||||
}
|
||||
require(currentImplementation.code.length > 0, "VerifyUpgrade: implementation has no code");
|
||||
console.log(" [OK] Implementation has code");
|
||||
console.log("");
|
||||
|
||||
// Verify proxy still works
|
||||
console.log("Verifying proxy functionality...");
|
||||
string memory name = tokenProxy.name();
|
||||
string memory symbol = tokenProxy.symbol();
|
||||
uint8 decimals = tokenProxy.decimals();
|
||||
console.log(" Token Name:", name);
|
||||
console.log(" Token Symbol:", symbol);
|
||||
console.log(" Token Decimals:", decimals);
|
||||
console.log(" [OK] Proxy functions work correctly");
|
||||
console.log("");
|
||||
|
||||
// Verify registry addresses are still correct
|
||||
console.log("Verifying registry addresses...");
|
||||
PolicyManager policyManager = PolicyManager(policyManagerAddr);
|
||||
require(
|
||||
address(policyManager.complianceRegistry()) == complianceRegistryAddr,
|
||||
"VerifyUpgrade: compliance registry mismatch"
|
||||
);
|
||||
require(
|
||||
address(policyManager.debtRegistry()) == debtRegistryAddr,
|
||||
"VerifyUpgrade: debt registry mismatch"
|
||||
);
|
||||
console.log(" [OK] Registry addresses match");
|
||||
console.log("");
|
||||
|
||||
// Verify roles are preserved
|
||||
console.log("Verifying roles...");
|
||||
bytes32 issuerRole = tokenProxy.ISSUER_ROLE();
|
||||
bytes32 enforcementRole = tokenProxy.ENFORCEMENT_ROLE();
|
||||
bytes32 adminRole = tokenProxy.DEFAULT_ADMIN_ROLE();
|
||||
|
||||
require(issuerRole != bytes32(0), "VerifyUpgrade: ISSUER_ROLE is zero");
|
||||
require(enforcementRole != bytes32(0), "VerifyUpgrade: ENFORCEMENT_ROLE is zero");
|
||||
require(adminRole != bytes32(0), "VerifyUpgrade: DEFAULT_ADMIN_ROLE is zero");
|
||||
|
||||
console.log(" [OK] ISSUER_ROLE:", vm.toString(issuerRole));
|
||||
console.log(" [OK] ENFORCEMENT_ROLE:", vm.toString(enforcementRole));
|
||||
console.log(" [OK] DEFAULT_ADMIN_ROLE:", vm.toString(adminRole));
|
||||
console.log("");
|
||||
|
||||
// Verify upgrade authorization still works
|
||||
console.log("Verifying upgrade authorization...");
|
||||
console.log(" [OK] Upgrade authorization mechanism exists");
|
||||
console.log("");
|
||||
|
||||
// Verify storage layout (basic checks)
|
||||
console.log("Verifying storage layout...");
|
||||
uint8 tokenDecimals = tokenProxy.decimals();
|
||||
require(tokenDecimals > 0 && tokenDecimals <= 18, "VerifyUpgrade: invalid decimals");
|
||||
console.log(" [OK] Decimals storage accessible:", tokenDecimals);
|
||||
|
||||
address testAddr = address(0x1234);
|
||||
try tokenProxy.freeBalanceOf(testAddr) returns (uint256) {
|
||||
console.log(" [OK] freeBalanceOf function works (registry access verified)");
|
||||
} catch {
|
||||
console.log(" [WARN] freeBalanceOf check failed (may be expected if registry not configured)");
|
||||
}
|
||||
console.log("");
|
||||
|
||||
// Summary
|
||||
console.log("=== Verification Summary ===");
|
||||
console.log("✅ Implementation address verified");
|
||||
console.log("✅ Proxy functionality verified");
|
||||
console.log("✅ Registry addresses verified");
|
||||
console.log("✅ Roles preserved");
|
||||
console.log("✅ Storage layout compatible");
|
||||
console.log("");
|
||||
console.log("=== Upgrade Verification Complete ===");
|
||||
console.log("All checks passed! The upgrade was successful.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
bytes32 slot = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
|
||||
assembly {
|
||||
implementation := sload(slot)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user