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. 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, before the transaction ends.
Basic upgrade pattern
Upgradable contracts accept upgrade messages containing new code and data. Only an admin can trigger upgrades.How it works
- Send an upgrade message to the contract that contains new code, data, or both.
- Verify that the message comes from an admin address.
- If the message contains code, schedule the code replacement with
setCodePostponed(). - If the message contains data, replace the existing data with
setData(). - During the action phase, apply the scheduled code replacement.
- Process subsequent messages with the new code after the transaction completes.
Example contract
The following contract acceptsUpgradeContract messages that contain new code or data. Only admins can trigger upgrades.
Tolk
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
- An admin sends a
RequestUpgrademessage with new code, new data, or both. - The contract verifies the message came from an admin and stores the upgrade details with a timestamp.
- The contract waits for the specified timeout, before accepting approvals.
- An admin sends an
ApproveUpgrademessage after the timeout expires. - The contract checks that enough time has passed since the request.
- If the request is approved, the contract schedules new code with
setCodePostponed()and upgrades data withsetData(). - The contract removes the pending request from storage.
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 acceptsRequestUpgrade, RejectUpgrade, and ApproveUpgrade messages. Only admins can trigger these actions.
Tolk
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
- Send an upgrade message with the new code cell and optional additional data.
- Verify the message comes from an admin address.
- Call
setCodePostponed()to schedule the code replacement. - Call
setTvmRegisterC3()to activate the new code in register C3 immediately. - Call
hotUpgradeData()to run the migration with the new code.
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().
Example code
The example shows a counter contract that changes the storage structure through a hot upgrade. The original version stores onlyadminAddress and counter; the new version adds metadata and reorders fields.
The original contract code before the upgrade:
main.tolk
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:
new.tolk
hotUpgradeData() function is what is called after the code was switched with setTvmRegisterC3() and performs the migration.
The migration logic follows these steps:
- Load the storage using the old structure (e.g.,
OldStoragewithadminAddressandcounter). - Create new storage with the additional
metadatafield fromadditionalData. - Reorder the fields to move the
counterbefore theadminAddress. - Write the migrated storage immediately with
contract.setData().
When to use hot upgrades
Use hot upgrades in the following scenarios:- The contract receives frequent state updates (DEX pools, oracles, lending protocols.)
- Storage changes between preparing and applying the upgrade would cause data loss.
- All intermediate state transitions must be preserved during the upgrade.
- The contract upgrades infrequently.
- Storage state at upgrade time is predictable.
- Simpler upgrade logic would reduce risk.