CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-openzeppelin--contracts-upgradeable

Secure Smart Contract library for Solidity with upgradeable implementations of standards like ERC20 and ERC721, flexible role-based permissioning schemes, and reusable components for building custom contracts and complex decentralized systems.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

proxy-upgradeability.mddocs/

Proxy and Upgradeability

Core upgradeability infrastructure including the Initializable base contract and UUPS upgrade pattern for implementing upgradeable smart contracts with secure state preservation.

Capabilities

Initializable Base Contract

Foundational contract that controls the initialization of upgradeable contracts.

/**
 * @dev Helper to support initializer functions
 */
abstract contract Initializable {
    /**
     * @dev Triggered when the contract has been initialized or reinitialized
     */
    event Initialized(uint64 version);
    
    /**
     * @dev Modifier used by the initializer function of a contract
     */
    modifier initializer();
    
    /**
     * @dev Modifier used by reinitializer functions
     */
    modifier reinitializer(uint64 version);
    
    /**
     * @dev Modifier that restricts functions to the initializing phase
     */
    modifier onlyInitializing();
    
    /**
     * @dev Locks the contract, preventing any future reinitialization
     */
    function _disableInitializers() internal;
    
    /**
     * @dev Returns the highest version that has been initialized
     */
    function _getInitializedVersion() internal view returns (uint64);
    
    /**
     * @dev Returns true if the contract is currently initializing
     */
    function _isInitializing() internal view returns (bool);
}

UUPS Upgradeable Pattern

Universal Upgradeable Proxy Standard implementation that allows contracts to upgrade themselves.

/**
 * @dev Implementation of the UUPS (Universal Upgradeable Proxy Standard) pattern
 */
abstract contract UUPSUpgradeable {
    function __UUPSUpgradeable_init() internal onlyInitializing;
    
    /**
     * @dev The version of the upgrade interface of the contract
     */
    string constant UPGRADE_INTERFACE_VERSION = "5.0.0";
    
    /**
     * @dev Storage slot with the address of the current implementation
     */
    bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
    
    /**
     * @dev Upgrade the implementation of the proxy to newImplementation and call a function on the new implementation
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) external payable;
    
    /**
     * @dev Returns the current implementation address
     */
    function proxiableUUID() external view returns (bytes32);
    
    /**
     * @dev Function that should revert when msg.sender is not authorized to upgrade the contract
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;
    
    /**
     * @dev Returns the current implementation address
     */
    function _getImplementation() internal view returns (address);
    
    /**
     * @dev Stores a new address in the EIP-1967 implementation slot
     */
    function _setImplementation(address newImplementation) private;
}

// Events
event Upgraded(address indexed implementation);

Upgrade Patterns

Storage Layout Considerations

All upgradeable contracts use ERC-7201 namespaced storage to prevent storage collisions:

// Example storage pattern used in upgradeable contracts
struct ContractNameStorage {
    // Contract state variables
    mapping(address => uint256) _balances;
    uint256 _totalSupply;
    string _name;
    string _symbol;
}

// Storage location calculation
bytes32 private constant ContractNameStorageLocation = 
    keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ContractName")) - 1)) & ~bytes32(uint256(0xff));

function _getContractNameStorage() private pure returns (ContractNameStorage storage $) {
    assembly {
        $.slot := ContractNameStorageLocation
    }
}

Initialization Pattern

Standard initialization pattern for upgradeable contracts:

// Internal initialization functions
function __ContractName_init(/* parameters */) internal onlyInitializing {
    __ContractName_init_unchained(/* parameters */);
}

function __ContractName_init_unchained(/* parameters */) internal onlyInitializing {
    ContractNameStorage storage $ = _getContractNameStorage();
    // Initialize contract state
}

Usage Examples

Basic Upgradeable Contract

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract MyUpgradeableContract is Initializable, UUPSUpgradeable, OwnableUpgradeable {
    uint256 public value;
    
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }
    
    function initialize(address initialOwner, uint256 initialValue) initializer public {
        __Ownable_init(initialOwner);
        __UUPSUpgradeable_init();
        value = initialValue;
    }
    
    function setValue(uint256 newValue) public onlyOwner {
        value = newValue;
    }
    
    function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}

Upgradeable Token Contract

import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract UpgradeableToken is ERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable {
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }
    
    function initialize(
        string memory name,
        string memory symbol,
        address initialOwner,
        uint256 initialSupply
    ) initializer public {
        __ERC20_init(name, symbol);
        __Ownable_init(initialOwner);
        __UUPSUpgradeable_init();
        
        if (initialSupply > 0) {
            _mint(initialOwner, initialSupply);
        }
    }
    
    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
    
    function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}

Contract V2 with New Features

// Upgraded version of the contract with new functionality
contract MyUpgradeableContractV2 is MyUpgradeableContract {
    uint256 public newFeature;
    
    function initializeV2(uint256 newFeatureValue) reinitializer(2) public {
        newFeature = newFeatureValue;
    }
    
    function setNewFeature(uint256 newValue) public onlyOwner {
        newFeature = newValue;
    }
    
    function getVersion() public pure returns (string memory) {
        return "2.0.0";
    }
}

Deployment Script Pattern

// Example deployment script for upgradeable contracts
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract DeployUpgradeable {
    function deploy() external returns (address) {
        // Deploy implementation
        MyUpgradeableContract implementation = new MyUpgradeableContract();
        
        // Encode initializer call
        bytes memory data = abi.encodeCall(
            MyUpgradeableContract.initialize,
            (msg.sender, 100) // initialOwner, initialValue
        );
        
        // Deploy proxy
        ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), data);
        
        return address(proxy);
    }
}

Best Practices

Constructor Disabling

Always disable initializers in the constructor to prevent initialization of the implementation contract:

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
    _disableInitializers();
}

Storage Gap Pattern

For contracts that may be inherited, consider adding storage gaps:

// Reserve storage slots for future variables
uint256[50] private __gap;

Upgrade Authorization

Always implement proper authorization for upgrades:

function _authorizeUpgrade(address newImplementation) internal override onlyOwner {
    // Additional upgrade validation logic can be added here
}

Install with Tessl CLI

npx tessl i tessl/npm-openzeppelin--contracts-upgradeable

docs

access-control.md

account-abstraction.md

finance.md

governance.md

index.md

meta-transactions.md

proxy-upgradeability.md

security-utilities.md

token-standards.md

tile.json