---
title: "Upgradability"
description: "Upgradability in GenVM controls Intelligent Contract code changes with Root Slot upgraders and locked storage slots"
source: https://docs.genlayer.com/developers/intelligent-contracts/features/upgradability
last_updated: 2026-06-11
---

# Upgradability

Upgradability in GenVM is a native system for modifying an Intelligent Contract after deployment while preserving security guarantees and clear access controls.

The system is built around the **Root Slot** (`gl.storage.Root`), which stores:

| Field | Description |
|---|---|
| `code` | The contract's source code |
| `locked_slots` | Storage slots that non-upgraders cannot write to |
| `upgraders` | Addresses authorized to modify locked slots (including code) |

## How It Works

1. At the start of a write transaction, GenVM reads the `upgraders` list
2. If the sender **is** in the `upgraders` list, no slot restrictions apply — the sender can modify any slot, including `code`
3. If the sender is **not** in the `upgraders` list, GenVM reads `locked_slots` and prevents writes to them

During deployment (`__init__`), after the constructor completes, the runtime automatically calls `root.lock_default()`, which locks four critical slots: the root slot, the code slot, the locked_slots slot, and the upgraders slot.

## Making a Contract Upgradable

To make a contract upgradable, you need to:

1. Add authorized upgrader addresses in `__init__`
2. Expose a method that replaces the contract code

```python
# v0.1.0
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }

from genlayer import *

class UpgradableStorage(gl.Contract):
    storage: str

    def __init__(self, initial_storage: str):
        self.storage = initial_storage

        # Set the deployer as an upgrader
        root = gl.storage.Root.get()
        root.upgraders.get().append(gl.message.sender_address)

        # lock_default() is called automatically after __init__
        # it locks: root slot, code, locked_slots, upgraders

    @gl.public.view
    def get_storage(self) -> str:
        return self.storage

    @gl.public.write
    def update_storage(self, new_storage: str) -> None:
        self.storage = new_storage

    @gl.public.write
    def upgrade(self, new_code: bytes) -> None:
        root = gl.storage.Root.get()
        code = root.code.get()
        # If sender is not in upgraders, this will raise a VMError
        code.truncate()
        code.extend(new_code)
```

## Upgrading to a New Version

The upgraded contract code must maintain the **same storage layout** for compatibility. Only the code changes — all storage data, the upgraders list, and locked slots persist across upgrades.

```python
# v0.1.0
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }

from genlayer import *

class UpgradableStorage(gl.Contract):
    # Storage layout must remain compatible with v1
    storage: str

    def __init__(self):
        pass

    @gl.public.view
    def get_storage(self) -> str:
        return self.storage

    @gl.public.write
    def update_storage(self, new_storage: str) -> None:
        self.storage = new_storage

    # New method added in v2
    @gl.public.view
    def get_storage_length(self) -> int:
        return len(self.storage)

    # Keep the upgrade method for future upgrades
    @gl.public.write
    def upgrade(self, new_code: bytes) -> None:
        root = gl.storage.Root.get()
        code = root.code.get()
        code.truncate()
        code.extend(new_code)
```

## What Happens During an Upgrade

When an authorized upgrader calls the `upgrade` method with new code:

| Component | After upgrade |
|---|---|
| `code` | **Replaced** with new code |
| Contract storage data (e.g. `storage: str`) | **Persists** unchanged |
| `locked_slots` | **Persists** unchanged |
| `upgraders` | **Persists** unchanged |

This means:
- **Upgrades are not one-shot** — since the upgraders list persists, the same addresses can push another upgrade later
- **Storage must be compatible** — the new code must understand the existing storage layout. There is no automatic migration mechanism
- **New methods can be added** — but existing storage field positions must not change
- **Upgraders can be modified** — an upgrader can add or remove addresses from the upgraders list

## Freezing a Contract

To make a contract permanently non-upgradable, either:
- Call `root.lock_default()` without adding any addresses to `upgraders` — the code and critical slots are locked and nobody can unlock them
- Remove all addresses from the `upgraders` list after locking

> **Warning:**
> Once a contract is frozen (locked slots with no upgraders), it cannot be upgraded. This is irreversible.

## Testing Upgrades

Using the [GenLayer Testing Suite](/developers/intelligent-contracts/tools/genlayer-testing-suite), you can test the full upgrade lifecycle:

```python
from pathlib import Path
from gltest import get_contract_factory
from gltest.assertions import tx_execution_succeeded

CONTRACTS_DIR = Path(__file__).parent.parent / "contracts"

def test_upgradable_storage():
    # Deploy v1
    factory = get_contract_factory(
        contract_file_path=CONTRACTS_DIR / "upgradable_storage.py"
    )
    contract = factory.deploy(args=["hello"])

    # Use v1 methods
    assert contract.get_storage(args=[]).call() == "hello"

    # Read v2 code and upgrade the contract
    v2_code = (CONTRACTS_DIR / "upgradable_storage_v2.py").read_bytes()
    tx = contract.upgrade(args=[v2_code]).transact()
    assert tx_execution_succeeded(tx)

    # Rebuild the contract proxy from the v2 schema
    v2_factory = get_contract_factory(
        contract_file_path=CONTRACTS_DIR / "upgradable_storage_v2.py"
    )
    contract_v2 = v2_factory.build_contract(contract_address=contract.address)

    # Storage persists across upgrades
    assert contract_v2.get_storage(args=[]).call() == "hello"

    # New v2 method works
    assert contract_v2.get_storage_length(args=[]).call() == 5
```

See [GenVM specification](https://sdk.genlayer.com/v0.2.7/spec/04-contract-interface/04-upgradability.html) for the full technical details.
