Claude Skills Guide

Claude Code for Upgradeable Contract Workflow Guide

Upgradeable smart contracts are essential for production blockchain applications where bug fixes and feature additions must be deployed without losing state or requiring users to migrate. This guide shows you how to use Claude Code to streamline the entire upgradeable contract development lifecycle—from initial setup through deployment and subsequent upgrades.

Understanding Upgradeable Contract Architecture

Before diving into workflows, it’s crucial to understand the proxy pattern architecture that makes upgrades possible. Upgradeable contracts separate storage from logic using three main components:

  1. Proxy Contract: Holds the state and delegates calls to the implementation
  2. Implementation Contract: Contains the business logic
  3. Proxy Admin: Manages who can upgrade the implementation

The key insight is that the proxy’s storage remains intact when you point to a new implementation. This allows you to fix bugs or add features while preserving all user balances, permissions, and other state data.

Setting Up Your Project with Claude Code

Initialize your upgradeable contract project with proper tooling:

# Create project directory
mkdir my-upgradeable-token && cd my-upgradeable-token

# Initialize with Hardhat (recommended for upgradeable contracts)
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npx hardhat init  # Choose "Create a JavaScript project"

# Install OpenZeppelin contracts and upgrades plugin
npm install @openzeppelin/contracts-upgradeable @openzeppelin/hardhat-upgrades

Configure your hardhat.config.js to include the upgrades plugin:

require("@openzeppelin/hardhat-upgrades");
require("@nomicfoundation/hardhat-toolbox");

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: {
    version: "0.8.20",
    settings: {
      optimizer: {
        enabled: true,
        runs: 200
      }
    }
  }
};

Claude Code can help you scaffold these files and explain each configuration choice. Simply ask: “Set up a Hardhat project with OpenZeppelin upgrades plugin” and Claude will generate the appropriate structure.

Writing Your First Upgradeable Contract

When writing upgradeable contracts, you must follow specific rules that differ from traditional Solidity development. Claude Code can guide you through these requirements:

Initial Implementation

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";

contract MyTokenUpgradeable is Initializable, ERC20Upgradeable, OwnableUpgradeable {
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize() public initializer {
        __ERC20_init("MyToken", "MTK");
        __Ownable_init(msg.sender);
        _mint(msg.sender, 1000000 * 10 ** decimals());
    }

    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
}

Key points Claude Code will emphasize:

Deployment Workflow

The deployment process differs significantly from standard contracts. Here’s the recommended workflow:

Step 1: Deploy Proxy and Implementation

npx hardhat run scripts/deploy.js --network sepolia

Your deployment script should use the upgrades plugin:

const { ethers, upgrades } = require("hardhat");

async function main() {
  const MyToken = await ethers.getContractFactory("MyTokenUpgradeable");
  
  const proxy = await upgrades.deployProxy(MyToken, [], {
    initializer: "initialize"
  });
  
  await proxy.waitForDeployment();
  const proxyAddress = await proxy.getAddress();
  
  console.log("Proxy deployed to:", proxyAddress);
}

main().catch(console.error);

Step 2: Verify the Deployment

Always verify your proxy setup is correct:

async function verify() {
  const proxyAddress = "YOUR_PROXY_ADDRESS";
  const implAddress = await upgrades.erc1967.getImplementationAddress(proxyAddress);
  const adminAddress = await upgrades.erc1967.getProxyAdminAddress(proxyAddress);
  
  console.log("Implementation:", implAddress);
  console.log("Admin:", adminAddress);
}

Managing Upgrades

When you need to fix a bug or add features, follow this workflow:

Step 1: Create a New Implementation

Never modify the existing implementation contract. Instead, create a new version:

// MyTokenUpgradeableV2.sol
contract MyTokenUpgradeableV2 is MyTokenUpgradeable {
    // Inherit all V1 state variables first!
    
    uint256 public transferFeeBasisPoints;
    
    function initializeV2() public reinitializer(2) {
        transferFeeBasisPoints = 50; // 0.5% fee
    }
    
    function transferWithFee(address to, uint256 amount) public returns (uint256) {
        uint256 fee = (amount * transferFeeBasisPoints) / 10000;
        _transfer(_msgSender(), to, amount - fee);
        return amount - fee;
    }
}

Critical rules for upgrades:

Step 2: Deploy and Upgrade

async function upgrade() {
  const MyTokenV2 = await ethers.getContractFactory("MyTokenUpgradeableV2");
  const proxyAddress = "YOUR_PROXY_ADDRESS";
  
  await upgrades.upgradeProxy(proxyAddress, MyTokenV2);
  console.log("Upgrade complete");
}

Best Practices for Upgradeable Contract Workflows

Use Testnets First

Always deploy to testnets (Sepolia, Goerli, or Holesky) before mainnet. Test your entire upgrade flow including:

Implement Timelock Controls

For production contracts, never allow immediate upgrades. Use a timelock controller:

const { upgrades } = require("hardhat");
const { DefenderRelay } = require("@openzeppelin/defender-relay-client");

async function upgradeWithTimelock() {
  const MyTokenV2 = await ethers.getContractFactory("MyTokenUpgradeableV2");
  const proxyAddress = "YOUR_PROXY_ADDRESS";
  
  const proposal = await upgrades.upgradeProxy(proxyAddress, MyTokenV2, {
    proposalTitle: "Upgrade to V2 - Add transfer fee",
    description: "Deploys V2 implementation with 0.5% transfer fee",
    via: "defender"
  });
}

Maintain Upgrade Documentation

Create a changelog documenting:

Automating with Claude Code

Claude Code can significantly accelerate your workflow:

Ask Claude: “Review my upgradeable contract for storage layout conflicts” or “Generate a deployment script for my proxy contract” to get started.

Conclusion

Upgradeable contracts require disciplined workflows and careful attention to storage management. By using Claude Code to assist with script generation, code review, and workflow automation, you can significantly reduce the risk of costly upgrade mistakes. Start with testnets, use timelock controls for production, and always maintain thorough documentation of your upgrade history.

The initial setup overhead pays dividends through the lifetime of your contract—users trust contracts that can be improved without disruption, and proper upgradeability patterns make that possible.

Built by theluckystrike — More at zovo.one