@1kpub/upgrade-control-contracts
v1.0.3
Published
Upgrade Control Contracts
Maintainers
Readme
Contract Upgrade Management System
This is a proxy-based smart contract upgrade management system that provides a secure and reliable contract upgrade mechanism, similar to a beacon proxy architecture for centrally managing multiple upgradeable contracts.
Core Features
🔧 Upgrade Manager (UpgradeManager)
- Manages the registration and deregistration of upgradeable contracts
- Supports contract implementation upgrades and rollbacks
- Maintains version history and management
- Provides access control and administrator transfer
- Owner permission required for contract registration
🌍 Open Upgrade Manager (UpgradeManagerOpen)
- Permissionless — anyone can register and manage their own upgradeable contracts
- Uses namespace isolation to prevent tag conflicts between different users
- Supports separation of registrant and administrator roles
- Provides complete upgrade, rollback, and management functionality
- Implements the
IUpgradeManagerOpeninterface
🔄 Upgrade Proxy (UpgradeProxy)
- Based on OpenZeppelin's proxy pattern
- Dynamically retrieves the latest logic contract address
- Transparently delegates all function calls
📚 Upgrade Library (UpgradeLib)
- Provides utility functions related to upgrade management
- Centralized management of storage slots and constants
- Simplifies the implementation of upgrade logic
🏗️ Upgrade Logic Base (UpgradeLogicBase)
- Provides foundational functionality for upgradeable logic contracts
- Integrates initializer and upgrade management capabilities
- Abstract base contract for inheritance and extension
System Architecture
Centralized Management Mode (UpgradeManager)
User call → UpgradeProxy → Delegate call → Logic Contract (inherits UpgradeLogicBase)
↓ ↓
UpgradeManager ← ← ← ← ← ← ← ← ← UpgradeLib (Utility Library)
(Owner permission required)Decentralized Management Mode (UpgradeManagerOpen)
User call → UpgradeProxy → Delegate call → Logic Contract (inherits UpgradeLogicBase)
↓ ↓
UpgradeManagerOpen ← ← ← ← ← ← ← ← ← UpgradeLib (Utility Library)
(Permissionless access)
↓
Namespace isolation (registrant + tag)Contract Components
1. IUpgradeManager.sol
Defines the interface for the upgrade manager, including:
ContractInfostruct: stores contract information (logic address, proxy address, administrator, version, activation status)- Event definitions: registration, upgrade, deregistration, admin transfer
- Core function interfaces: create proxy, register, upgrade, rollback, etc.
2. UpgradeManager.sol
Core implementation of the upgrade manager:
- Inherits
Ownable2Stepfor ownership management - Implements the
IUpgradeManagerinterface - Supports proxy deployment using CREATE2
- Provides version management and historical tracking
- Enforces access control (only Owner can register, only Admin can upgrade)
3. IUpgradeManagerOpen.sol
Interface definition for the open upgrade manager:
- Extends
IUpgradeManagerwith thenamespacedTagparameter - Supports permissionless registration and management
- Provides
getNamespacedTagfunction for namespace computation - Clearly distinguishes between registration and management parameters
4. UpgradeManagerOpen.sol
Implementation of the permissionless upgrade manager:
- Implements the
IUpgradeManagerOpeninterface - Anyone can register and manage their own upgradeable contracts
- Uses
keccak256(abi.encodePacked(registrant, tag))for namespace isolation - Supports separation of registrant and administrator roles
- Provides complete upgrade, rollback, and management functionality
5. UpgradeProxy.sol
Proxy contract implementation:
- Inherits OpenZeppelin's
Proxycontract - Uses
UpgradeLibto manage storage slots - Dynamically fetches the current logic contract address
- Transparently proxies all function calls
6. UpgradeLib.sol
Upgrade utility library:
- Defines constants for upgrade-related storage slots
- Provides functions to retrieve upgrade manager addresses
- Provides functions to fetch upgrade tags, registration info, and admin addresses
7. UpgradeLogicBase.sol
Base class for upgradeable logic contracts:
- Uses
UpgradeLibto retrieve upgrade-related information - Provides internal functions to access the upgrade manager, tag, registration info, and admin
- Abstract contract intended to be inherited by concrete logic contracts
Core Features
Version Management
- Automatic version increments
- Historical version tracking
- Supports rollback to specified versions
Access Control
- Centralized mode: Owner manages registration, Admin controls upgrades
- Decentralized mode: Anyone can register, Admin controls upgrades
- Supports admin role transfer
- Only the administrator can perform upgrade operations
Security Mechanisms
- Zero-address checks
- Duplicate registration prevention
- Logic contract address validation
- Namespace isolation (in
UpgradeManagerOpen)
Modular Design
- Centralized upgrade logic managed via a utility library
- Base class provides standardized upgrade functionality
- Componentized architecture for scalability and maintainability
- Dual-mode support: Centralized vs. Decentralized
Usage Examples
Centralized Mode (UpgradeManager)
Creating an Upgradeable Logic Contract
import { Initializable } from '@openzeppelin/contracts/proxy/utils/Initializable.sol';
import {
UpgradeLogicBase
} from '@kyriework/upgrade-control-contracts/contracts/UpgradeLogicBase.sol';
contract MyContract is UpgradeLogicBase, Initializable {
uint256 public value;
constructor() {
_disableInitializers();
}
function initialize(uint256 _value) public initializer {
value = _value;
}
function setValue(uint256 _value) public {
address admin = _upgradeAdminAddress();
require(msg.sender == admin, 'Only admin can set value');
value = _value;
}
function getUpgradeInfo() external view returns (address manager, bytes32 tag) {
manager = _upgradeManagerAddress();
tag = _upgradeTagBytes32();
}
}Deploying and Registering Contracts
UpgradeManager manager = new UpgradeManager(owner);
bytes32 tag = keccak256("MyContract");
address logic = address(new MyContract());
bytes memory initData = abi.encodeWithSignature("initialize(uint256)", 100);
(address proxy, bytes memory result) = manager.createProxy(tag, logic, admin, initData);
MyContract(proxy).setValue(200);Decentralized Mode (UpgradeManagerOpen)
Deploying and Registering Contracts
UpgradeManagerOpen openManager = new UpgradeManagerOpen();
bytes32 tag = keccak256("MyContract");
address logic = address(new MyContract());
bytes memory initData = abi.encodeWithSignature("initialize(uint256)", 100);
(address proxy, bytes memory result) = openManager.createProxy(tag, logic, admin, initData);
bytes32 namespacedTag = openManager.getNamespacedTag(msg.sender, tag);
address newLogic = address(new MyContractV2());
openManager.upgrade(namespacedTag, newLogic); // Only admin can callAccess Control
address registrant = msg.sender;
address admin = 0x1234...;
openManager.createProxy(tag, logic, admin, initData);
bytes32 namespacedTag = openManager.getNamespacedTag(registrant, tag);
openManager.upgrade(namespacedTag, newLogic); // Must be called by adminUpgrading a Contract
import { Initializable } from '@openzeppelin/contracts/proxy/utils/Initializable.sol';
import { UpgradeLogicBase } from '@kyriework/upgrade-control-contracts/contracts/UpgradeLogicBase.sol';
contract MyContractV2 is UpgradeLogicBase, Initializable {
uint256 public value;
uint256 public newFeature;
constructor() {
_disableInitializers();
}
function initialize(uint256 _value) public initializer {
value = _value;
}
function setNewFeature(uint256 _newFeature) public {
address admin = _upgradeAdminAddress();
require(msg.sender == admin, "Only admin");
newFeature = _newFeature;
}
}
address newLogic = address(new MyContractV2());
manager.upgrade(tag, newLogic);
bytes32 namespacedTag = openManager.getNamespacedTag(registrant, tag);
openManager.upgrade(namespacedTag, newLogic);Rolling Back a Contract
manager.rollback(tag, 1); // Roll back to version 1
openManager.rollback(namespacedTag, 1); // Roll back to version 1Development & Testing
Install Dependencies
pnpm installCompile Contracts
npx hardhat compileRun Tests
npx hardhat testDeploy Contracts
npx hardhat ignition deploy ./ignition/modules/UpgradeManager.jsGenerate Test Reports
REPORT_GAS=true npx hardhat testTech Stack
- Solidity ^0.8.20 — Smart contract programming language
- OpenZeppelin — Secure smart contract library
- Hardhat — Ethereum development environment
- Proxy Pattern — Upgradeable contract architecture
- Library Pattern — Code reuse and modularization
- Namespace Isolation — Prevents conflicts in multi-user environments
Notes & Best Practices
- Storage Layout Compatibility: Ensure storage layout compatibility between versions.
- Access Control: Assign administrator roles carefully to avoid privilege misuse.
- Version Management: Thoroughly test before performing upgrades.
- Gas Optimization: Proxy calls incur additional gas overhead.
- Base Inheritance: Logic contracts should inherit
UpgradeLogicBaseto enable full upgrade functionality. - Initializer Usage: Use the
initializermodifier to ensure initialization functions are called only once. - Namespace Management: In
UpgradeManagerOpen, always use the correctnamespacedTagfor operations. - Role Separation: Registrant and administrator roles can be distinct; allocate permissions appropriately.
Selection Guide
When to Use UpgradeManager
- Centralized control and registration required
- Enterprise-level applications with strict permission management
- Fewer contracts and simpler management needs
- Clear administrative authority structure
When to Use UpgradeManagerOpen
- Open platforms allowing public contract registration
- Decentralized applications minimizing centralized control
- Multi-user environments requiring isolation
- Flexible role management (Registrant ≠ Administrator)
License
MIT
