> ## Documentation Index
> Fetch the complete documentation index at: https://companyname-a7d5b98e-v2-pagination.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Upgrading contracts

export const Aside = ({type = "note", title = "", icon = "", iconType = "regular", children}) => {
  const asideVariants = ["note", "tip", "caution", "danger"];
  const asideComponents = {
    note: {
      outerStyle: "border-sky-500/20 bg-sky-50/50 dark:border-sky-500/30 dark:bg-sky-500/10",
      innerStyle: "text-sky-900 dark:text-sky-200",
      calloutType: "note",
      icon: <svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg" className="w-4 h-4 text-sky-500" aria-label="Note">
          <path fill-rule="evenodd" clip-rule="evenodd" d="M7 1.3C10.14 1.3 12.7 3.86 12.7 7C12.7 10.14 10.14 12.7 7 12.7C5.48908 12.6974 4.0408 12.096 2.97241 11.0276C1.90403 9.9592 1.30264 8.51092 1.3 7C1.3 3.86 3.86 1.3 7 1.3ZM7 0C3.14 0 0 3.14 0 7C0 10.86 3.14 14 7 14C10.86 14 14 10.86 14 7C14 3.14 10.86 0 7 0ZM8 3H6V8H8V3ZM8 9H6V11H8V9Z"></path>
        </svg>
    },
    tip: {
      outerStyle: "border-emerald-500/20 bg-emerald-50/50 dark:border-emerald-500/30 dark:bg-emerald-500/10",
      innerStyle: "text-emerald-900 dark:text-emerald-200",
      calloutType: "tip",
      icon: <svg width="11" height="14" viewBox="0 0 11 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg" className="text-emerald-600 dark:text-emerald-400/80 w-3.5 h-auto" aria-label="Tip">
          <path d="M3.12794 12.4232C3.12794 12.5954 3.1776 12.7634 3.27244 12.907L3.74114 13.6095C3.88471 13.8248 4.21067 14 4.46964 14H6.15606C6.41415 14 6.74017 13.825 6.88373 13.6095L7.3508 12.9073C7.43114 12.7859 7.49705 12.569 7.49705 12.4232L7.50055 11.3513H3.12521L3.12794 12.4232ZM5.31288 0C2.52414 0.00875889 0.5 2.26889 0.5 4.78826C0.5 6.00188 0.949566 7.10829 1.69119 7.95492C2.14321 8.47011 2.84901 9.54727 3.11919 10.4557C3.12005 10.4625 3.12175 10.4698 3.12261 10.4771H7.50342C7.50427 10.4698 7.50598 10.463 7.50684 10.4557C7.77688 9.54727 8.48281 8.47011 8.93484 7.95492C9.67728 7.13181 10.1258 6.02703 10.1258 4.78826C10.1258 2.15486 7.9709 0.000106649 5.31288 0ZM7.94902 7.11267C7.52078 7.60079 6.99082 8.37878 6.6077 9.18794H4.02051C3.63739 8.37878 3.10743 7.60079 2.67947 7.11294C2.11997 6.47551 1.8126 5.63599 1.8126 4.78826C1.8126 3.09829 3.12794 1.31944 5.28827 1.3126C7.2435 1.3126 8.81315 2.88226 8.81315 4.78826C8.81315 5.63599 8.50688 6.47551 7.94902 7.11267ZM4.87534 2.18767C3.66939 2.18767 2.68767 3.16939 2.68767 4.37534C2.68767 4.61719 2.88336 4.81288 3.12521 4.81288C3.36705 4.81288 3.56274 4.61599 3.56274 4.37534C3.56274 3.6515 4.1515 3.06274 4.87534 3.06274C5.11719 3.06274 5.31288 2.86727 5.31288 2.62548C5.31288 2.38369 5.11599 2.18767 4.87534 2.18767Z"></path>
        </svg>
    },
    caution: {
      outerStyle: "border-amber-500/20 bg-amber-50/50 dark:border-amber-500/30 dark:bg-amber-500/10",
      innerStyle: "text-amber-900 dark:text-amber-200",
      calloutType: "warning",
      icon: <svg className="flex-none w-5 h-5 text-amber-400 dark:text-amber-300/80" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-label="Warning">
          <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
        </svg>
    },
    danger: {
      outerStyle: "border-red-500/20 bg-red-50/50 dark:border-red-500/30 dark:bg-red-500/10",
      innerStyle: "text-red-900 dark:text-red-200",
      calloutType: "danger",
      icon: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor" className="text-red-600 dark:text-red-400/80 w-4 h-4" aria-label="Danger">
          <path d="M17.1 292c-12.9-22.3-12.9-49.7 0-72L105.4 67.1c12.9-22.3 36.6-36 62.4-36l176.6 0c25.7 0 49.5 13.7 62.4 36L494.9 220c12.9 22.3 12.9 49.7 0 72L406.6 444.9c-12.9 22.3-36.6 36-62.4 36l-176.6 0c-25.7 0-49.5-13.7-62.4-36L17.1 292zm41.6-48c-4.3 7.4-4.3 16.6 0 24l88.3 152.9c4.3 7.4 12.2 12 20.8 12l176.6 0c8.6 0 16.5-4.6 20.8-12L453.4 268c4.3-7.4 4.3-16.6 0-24L365.1 91.1c-4.3-7.4-12.2-12-20.8-12l-176.6 0c-8.6 0-16.5 4.6-20.8 12L58.6 244zM256 128c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"></path>
        </svg>
    }
  };
  let variant = type;
  let gotInvalidVariant = false;
  if (!asideVariants.includes(type)) {
    gotInvalidVariant = true;
    variant = "danger";
  }
  const iconVariants = ["regular", "solid", "light", "thin", "sharp-solid", "duotone", "brands"];
  if (!iconVariants.includes(iconType)) {
    iconType = "regular";
  }
  return <>
      <div className={`callout my-4 px-5 py-4 overflow-hidden rounded-2xl flex gap-3 border ${asideComponents[variant].outerStyle}`} data-callout-type={asideComponents[variant].calloutType}>
        <div className="mt-0.5 w-4" data-component-part="callout-icon">
          {}
          {icon === "" ? asideComponents[variant].icon : <Icon icon={icon} iconType={iconType} size={14} />}
        </div>
        <div className={`text-sm prose min-w-0 w-full ${asideComponents[variant].innerStyle}`} data-component-part="callout-content">
          {gotInvalidVariant ? <p>
              <span className="font-bold">
                Invalid <code>type</code> passed!
              </span>
              <br />
              <span className="font-bold">Received: </span>
              {type}
              <br />
              <span className="font-bold">Expected one of: </span>
              {asideVariants.join(", ")}
            </p> : <>
              {title && <p className="font-bold">{title}</p>}
              {children}
            </>}
        </div>
      </div>
    </>;
};

The address of a contract is determined by its [initial code and state](/foundations/status).
However, with upgrades it is possible to change the code of a contract while keeping its initial address. This allows developers to fix bugs, add features, and adapt to protocol changes without migrating to a new address, which is required for contracts that are already referenced by other contracts or have users interacting with them.

For example, [NFT item contracts](/standard/tokens/nft/how-it-works) reference their collection contract. If the collection contract address changes, all item contracts would need to point to the new address, but the collection admin cannot modify existing item contracts. With upgrades the collection contract address stays the same, so all item contracts continue to reference it without any changes. A collection admin can then upgrade the collection contract code to fix bugs or add features without affecting the item contracts.

The upgrade pattern is also required for [vanity](/contract-dev/vanity) contracts and protocols such as distributed exchanges (DEXs) that are referenced by many other contracts.

## How upgrades work

Tolk provides two functions for upgrades, one for code and one for data:

* `contract.setCodePostponed(code: cell)` schedules a code replacement during the [action phase](/foundations/phases#action-phase). The new code is available *after the current transaction completes*.
* `contract.setData(data: cell)` immediately replaces the contract's persistent storage. This happens during the [compute phase](/foundations/phases#compute-phase), *before the transaction ends*.

<Aside type="caution" title="Funds at risk">
  Contract upgrades change code behavior and can affect funds or contract state. Unauthorized upgrades can cause loss of control or funds. Restrict upgrade messages to trusted admin addresses only.
</Aside>

<Aside type="caution" title="Ethics">
  Use delayed upgrades to allow users to react to compromised admin keys or unwanted updates.

  [The Trail of Bits blog](https://blog.trailofbits.com/2025/06/25/maturing-your-smart-contracts-beyond-private-key-risk/) and [Wikipedia](https://en.wikipedia.org/wiki/Exit_scam) provide additional information on this topic.
</Aside>

## Basic upgrade pattern

Contracts must define a message type that contains new code, new data, or both, to be upgradable. The contract's message handler should verify that the message comes from an admin address and then call the appropriate upgrade functions to apply the changes.

### How it works

1. Send an upgrade message to the contract that contains new code, data, or both.
2. Verify that the message comes from an admin address.
3. If the message contains code, schedule the code replacement with `setCodePostponed()`.
4. If the message contains data, replace the existing data with `setData()`.
5. During the action phase, apply the scheduled code replacement.
6. Process subsequent messages with the new code after the transaction completes.

The upgrade runs in a single transaction. New code becomes active after the transaction completes, and new data is available when the transaction ends. If the message does not provide enough Toncoin to run both the compute phase and the action phase, the entire transaction is aborted and no state changes from the upgrade are applied. Test the upgrade script to estimate gas requirements, and send enough Toncoin to execute the full upgrade transaction.

<Aside type="caution">
  Sending a transaction with the `SendIgnoreErrors` flag will ignore the insufficient funds error, and the transaction will succeed. This can leave a contract in an unusable state with a new storage structure but old code that cannot read it.

  Learn more about [execution phases](/foundations/phases) and [sending modes](/foundations/messages/modes) in the docs.
</Aside>

### Example contract

The following contract accepts `UpgradeContract` messages that contain new code or data. Only admins can trigger upgrades.

```tolk title="Tolk" expandable theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
struct (0x1111) UpgradeContract {
    data: cell?
    code: cell?
}

type AllowedMessages = UpgradeContract

fun onInternalMessage(in: InMessage) {

    val msg = lazy AllowedMessages.fromSlice(in.body);

    match (msg) {

        UpgradeContract => {
            var storage = lazy Storage.load();
            assert (in.senderAddress == storage.adminAddress) throw 2222;
            if (msg.code != null) {
                contract.setCodePostponed(msg.code!);
            }
            if (msg.data != null) {
                contract.setData(msg.data!);
            }
        }

        else => {
            // just accept Toncoin
        }
    }
}

struct Storage {
    adminAddress: address
}

fun Storage.load() {
    return Storage.fromCell(contract.getData());
}

fun Storage.save(self) {
    contract.setData(self.toCell());
}
```

## Delayed upgrade pattern

Consider using the delayed upgrade pattern for production contracts with active users. This pattern adds a time delay between requesting and approving an upgrade, providing an additional security layer. The delay allows users to withdraw funds or exit positions if they do not trust the upgrade or if an admin account is compromised. It also gives users time to review the proposed changes before they take effect.

### How it works

1. An admin sends a `RequestUpgrade` message with new code, new data, or both.
2. The contract verifies the message came from an admin and stores the upgrade details with a timestamp.
3. The contract waits for the specified timeout, before accepting approvals.
4. An admin sends an `ApproveUpgrade` message after the timeout expires.
5. The contract checks that enough time has passed since the request.
6. If the request is approved, the contract schedules new code with `setCodePostponed()` and upgrades data with `setData()`.
7. The contract removes the pending request from storage.

Admins can also send `RejectUpgrade` at any time to cancel a pending upgrade. This three-message flow (request → wait → approve or reject) gives users time to review changes and react if an admin account is compromised.

### Example contract

The following code illustrates the delayed upgrade pattern. The contract accepts `RequestUpgrade`, `RejectUpgrade`, and `ApproveUpgrade` messages. Only admins can trigger these actions.

```tolk title="Tolk" expandable theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
struct UpgradeContract {
    data: cell?
    code: cell?
}

struct CurrentRequest {
    newUpgrade: UpgradeContract
    timestamp: uint32
}

struct (0x00000001) RequestUpgrade {
    newUpgrade: UpgradeContract
}

struct (0x00000002) RejectUpgrade { }

struct (0x00000003) ApproveUpgrade { }

type AllowedMessages =
    | RequestUpgrade
    | RejectUpgrade
    | ApproveUpgrade

fun onInternalMessage(in: InMessage) {
    val msg = lazy AllowedMessages.fromSlice(in.body);

    match (msg) {

        RequestUpgrade => {
            var storage = lazy Storage.load();

            assert (in.senderAddress == storage.adminAddress) throw 2222;
            assert (storage.currentRequest == null) throw 3333;

            storage.currentRequest = {
                newUpgrade: msg.newUpgrade,
                timestamp: blockchain.now()
            };

            storage.save();
        }

        RejectUpgrade => {
            var storage = lazy Storage.load();

            assert (in.senderAddress == storage.adminAddress) throw 2222;
            assert (storage.currentRequest != null) throw 3333;

            storage.currentRequest = null;
            storage.save();
        }

        ApproveUpgrade => {
            var storage = lazy Storage.load();

            assert (in.senderAddress == storage.adminAddress) throw 2222;
            assert (storage.currentRequest != null) throw 3333;
            assert (storage.currentRequest!.timestamp + storage.timeout < blockchain.now()) throw 302;

            if (storage.currentRequest!.newUpgrade.code != null) {
                contract.setCodePostponed(storage.currentRequest!.newUpgrade.code!);
            }

            if (storage.currentRequest!.newUpgrade.data != null) {
                contract.setData(storage.currentRequest!.newUpgrade.data!);
            }
            else {
                storage.currentRequest = null;
                storage.save();
            }
        }

        else => {
            // just accepted Toncoin
        }
    }
}

get fun currentRequest() {
    var storage = lazy Storage.load();
    return storage.currentRequest;
}

struct Storage {
    adminAddress: address,
    timeout: uint32,
    currentRequest: CurrentRequest?
}

fun Storage.load() {
    return Storage.fromCell(contract.getData());
}

fun Storage.save(self) {
    contract.setData(self.toCell());
}
```

## Hot upgrade pattern

The standard upgrade methods fail when contracts receive frequent updates. For example, DEX pools that update prices every second or lending protocols that continuously adjust interest rates. The problem: it is not possible to predict what data will be in storage when the upgrade transaction executes.

Other transactions might execute before an upgrade arrives. By the time the upgrade applies, the prepared data may be stale. For a DEX pool, this can lead to outdated values, breaking the protocol.

Hot upgrades solve this by scheduling a code change and immediately calling a migration function with the new code. The migration function runs in the same transaction that applies the upgrade. It reads the old storage structure, transforms it to match the new schema, and writes the upgraded storage to preserve all state changes that happened between preparing the upgrade and executing it.

### How it works

1. Send an upgrade message with the new code cell and optional additional data.
2. Verify the message comes from an admin address.
3. Call `setCodePostponed()` to schedule the code replacement.
4. Call `setTvmRegisterC3()` to activate the new code in register [C3](/tvm/registers#c3-—-function-selector) immediately.
5. Call `hotUpgradeData()` to run the migration with the new code.

The `setTvmRegisterC3()` is the key to hot upgrades. It replaces the current code immediately so the following command (e.g., `hotUpgradeData()`) runs the new code. The migration function reads the current storage, transforms it to the new schema, and saves it. After the transaction completes, the new code becomes permanent through `setCodePostponed()`.

<Aside type="caution" title="Migration risks">
  Hot upgrades require careful migration logic. Test migrations thoroughly on testnet. If the migration function throws, the upgrade transaction aborts and no state changes are applied. If the migration succeeds but writes invalid storage, the contract can become unusable. The `hotUpgradeData()` function runs only during upgrade messages, not on regular messages, preventing accidental repeated migrations.
</Aside>

### Example code

The example shows a counter contract that changes the storage structure through a hot upgrade. The original version stores only `adminAddress` and `counter`; the new version adds `metadata` and reorders fields.

The original contract code before the upgrade:

```tolk title="main.tolk" expandable theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
import "@stdlib/tvm-lowlevel"

struct (0x00001111) HotUpgrade {
    additionalData: cell?
    code: cell
}

struct (0x00002222) IncreaseCounter {}

type AllowedMessages =
    | HotUpgrade
    | IncreaseCounter

// migration function must have method_id
@method_id(2121)
fun hotUpgradeData(additionalData: cell?) { }

fun onInternalMessage(in: InMessage) {

    val msg = lazy AllowedMessages.fromSlice(in.body);

    match (msg) {

        HotUpgrade => {
            var storage = lazy Storage.load();
            assert (in.senderAddress == storage.adminAddress) throw 1111;

            contract.setCodePostponed(msg.code);

            setTvmRegisterC3(transformSliceToContinuation(msg.code.beginParse()));
            hotUpgradeData(msg.additionalData);
        }

        IncreaseCounter => {
            var storage = lazy Storage.load();
            storage.counter += 1;
            storage.save();
        }

        else => {
            // just accept Toncoin
        }
    }
}

get fun counter() {
    var storage = lazy Storage.load();
    return storage.counter;
}

struct Storage {
    adminAddress: address
    counter: uint32
}

fun Storage.load() {
    return Storage.fromCell(contract.getData());
}

fun Storage.save(self) {
    contract.setData(self.toCell());
}
```

The contract never executes the original `hotUpgradeData()` function because it is immediately replaced by the new code during the upgrade. The new code defines the actual migration logic. That is why the migration function must have a `method_id` that is stable across versions, so the runtime can call it after the upgrade.

New contract code that applies the hot upgrade:

```tolk title="new.tolk" expandable theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
import "@stdlib/tvm-lowlevel"

struct (0x00001111) HotUpgrade {
    additionalData: cell?
    code: cell
}

struct (0x00002222) IncreaseCounter {}

type AllowedMessages =
    | HotUpgrade
    | IncreaseCounter

// migration function must have method_id
@method_id(2121)
fun hotUpgradeData(additionalData: cell?) {
    var oldStorage = lazy OldStorage.load();

    assert (additionalData != null) throw 1112;

    var storage = Storage {
        adminAddress: oldStorage.adminAddress,
        counter: oldStorage.counter,
        metadata: additionalData!
    };

    contract.setData(storage.toCell());
}

struct OldStorage {
    adminAddress: address
    counter: uint32
}

fun OldStorage.load() {
    return OldStorage.fromCell(contract.getData());
}


fun onInternalMessage(in: InMessage) {

    val msg = lazy AllowedMessages.fromSlice(in.body);

    match (msg) {

        HotUpgrade => {
            var storage = lazy Storage.load();
            assert (in.senderAddress == storage.adminAddress) throw 1111;

            contract.setCodePostponed(msg.code);

            setTvmRegisterC3(transformSliceToContinuation(msg.code.beginParse()));
            hotUpgradeData(msg.additionalData);
        }

        IncreaseCounter => {
            var storage = lazy Storage.load();
            storage.counter += 1;
            storage.save();
        }

        else => {
            // just accept Toncoin
        }
    }
}

get fun metadata() {
    var storage = lazy Storage.load();
    return storage.metadata;
}

get fun counter() {
    var storage = lazy Storage.load();
    return storage.counter;
}
struct Storage {
    counter: uint32
    adminAddress: address
    metadata: cell
}

fun Storage.load() {
    return Storage.fromCell(contract.getData());
}

fun Storage.save(self) {
    contract.setData(self.toCell());
}
```

The new version of `hotUpgradeData()` function is what is called after the code was switched with `setTvmRegisterC3()` and performs the migration.

The migration logic follows these steps:

1. Load the storage using the old structure, e.g., `OldStorage` with `adminAddress` and `counter`.
2. Create new storage with the additional `metadata` field from `additionalData`.
3. Reorder the fields to move the `counter` before the `adminAddress`.
4. Write the migrated storage immediately with `contract.setData()`.

The migration runs in the same transaction as the upgrade message. Any counter increments that happened between preparing the upgrade and executing it remain in storage because the migration reads the current state, not a pre-prepared snapshot. The migration function explicitly handles the structure change by reading fields from the old layout and writing them in the new layout.

### When to use hot upgrades

Use hot upgrades in the following scenarios:

* The contract receives frequent state updates, e.g., DEX pools, oracles, and lending protocols.
* Storage changes between preparing and applying the upgrade would cause data loss.
* All intermediate state transitions must be preserved during the upgrade.

Use standard upgrades instead when:

* The contract upgrades infrequently.
* Storage state at upgrade time is predictable.
* Simpler upgrade logic would reduce risk.

## Combining delayed and hot upgrades

Combine delayed upgrades with hot upgrades for production protocols that require both safety and structure migration. The delayed upgrade pattern provides time for users to review changes, while the hot upgrade mechanism handles storage migration without data loss.

<Aside type="tip" title="Complete example code">
  The [TON Examples GitHub repository](https://github.com/ton-org/docs-examples/tree/main/contract-dev/Upgrading) contains full working examples demonstrating all upgrade patterns. This includes implementations for basic, delayed, and hot upgrade patterns.
</Aside>
