Safe upgrade practices for upgradeable smart contracts. Use when planning or executing contract upgrades.
This skill inherits all available tools. When active, it can use any tool Claude has access to.
Reference skill for safe upgrade practices. Detailed upgrade security checklist is in the security-audit skill.
Use this skill when:
For comprehensive upgrade security, see:
checklists/upgrade-checklist.md - Complete upgrade security checklistpatterns/upgradeable-contracts.md and examples/upgradeable-example.solNever:
Always:
Example:
// V1
contract V1 {
uint256 public value;
address public owner;
uint256[48] private __gap; // Reserve space
}
// V2 - ✅ Safe
contract V2 {
uint256 public value;
address public owner;
uint256 public newValue; // Added at end
uint256[47] private __gap; // Reduced gap
}
Always:
_disableInitializers() in constructorinitializer modifier for init functionreinitializer(version) for upgrade initExample:
contract MyContract is Initializable, UUPSUpgradeable {
constructor() {
_disableInitializers(); // ✅ Required
}
function initialize() public initializer {
__UUPSUpgradeable_init();
}
function initializeV2() public reinitializer(2) {
// V2 initialization
}
}
UUPS:
_authorizeUpgrade functionfunction _authorizeUpgrade(address) internal override onlyOwner {}
Never remove this function in UUPS upgrades!
Foundry:
# Check storage layout
forge inspect MyContract storage-layout
# Compare layouts
forge inspect MyContractV1 storage-layout > v1.json
forge inspect MyContractV2 storage-layout > v2.json
diff v1.json v2.json
Hardhat:
# Validate upgrade
npx hardhat verify-upgrade <PROXY> <NEW_IMPL>
Slither:
# Check upgradeability
slither-check-upgradeability . MyContract
function test_UpgradePreservesStorage() public {
// Deploy V1
MyContractV1 v1 = new MyContractV1();
v1.initialize(owner);
v1.setValue(42);
// Deploy V2 implementation
MyContractV2 implementation = new MyContractV2();
// Simulate upgrade (actual upgrade depends on proxy pattern)
// ...
// Cast to V2 interface
MyContractV2 v2 = MyContractV2(address(v1));
// Verify storage preserved
assertEq(v2.getValue(), 42);
assertEq(v2.owner(), owner);
}
function test_UpgradeOnFork() public {
// Fork mainnet
vm.createSelectFork(vm.envString("MAINNET_RPC_URL"));
// Get existing proxy
MyContract proxy = MyContract(PROXY_ADDRESS);
// Deploy new implementation
MyContract newImpl = new MyContract();
// Upgrade
vm.prank(OWNER);
proxy.upgradeTo(address(newImpl));
// Verify
assertEq(proxy.version(), 2);
}
// V1
contract V1 {
uint256 public value;
}
// V2 - ❌ Wrong!
contract V2 {
address public owner; // Overwrites value!
uint256 public value;
}
// ❌ Missing constructor protection
contract MyContract {
function initialize() public initializer {
// Attacker can initialize implementation!
}
}
// V1
contract V1 is UUPSUpgradeable {
function _authorizeUpgrade(address) internal override onlyOwner {}
}
// V2 - ❌ Removed authorization!
contract V2 is UUPSUpgradeable {
// Missing _authorizeUpgrade - contract is now non-upgradeable!
}
// ❌ Never use selfdestruct in upgradeable contracts
function destroy() public {
selfdestruct(payable(owner)); // Kills implementation for all proxies!
}
| Tool | Purpose | Command |
|---|---|---|
| Foundry | Storage layout | forge inspect Contract storage-layout |
| Hardhat | Verify upgrade | npx hardhat verify-upgrade |
| Slither | Check upgrade safety | slither-check-upgradeability |
| OpenZeppelin Defender | Upgrade automation | Web interface |
security-audit/checklists/upgrade-checklist.mdproxy-patterns/SKILL.mdcontract-patterns/patterns/upgradeable-contracts.mdcontract-patterns/examples/upgradeable-example.solNote: This is a reference skill. For the complete upgrade security checklist, see security-audit/checklists/upgrade-checklist.md.