Skip to main content

Introduction

Upgrading a smart contract allows you to improve or modify your contract without changing its address. This guide will walk you through the process of upgrading a WebAssembly (Wasm) bytecode contract using the Soroban SDK.

Prerequisites:

Download the upgradeable contract example

The upgradeable contract example demonstrates how to upgrade a Wasm contract. Open in Codespaces Open in Codeanywhere

Code

The example contains both an “old” and “new” contract, where we upgrade from “old” to “new”. The code below is for the “old” contract.
upgradeable_contract/old_contract/src/lib.rs
Source: https://github.com/stellar/soroban-examples/blob/main/upgradeable_contract/old_contract/src/lib.rs

How it works

When upgrading a contract, the key function used is env.deployer().update_current_contract, which takes a ContractExecutable wrapping the Wasm hash of the new contract as a parameter. Here’s a step-by-step breakdown of how this process works:
  1. No change in contract address: The contract’s address remains the same after the upgrade. This ensures that all references to the contract stay intact.
  2. The Wasm executable must already be uploaded: The upgrade depends on the compiled executable (identified by the new_wasm_hash) being uploaded and available on the blockchain. This must be done prior to invoking the contract’s upgrade(...) function.
  3. Admin authorization: Before upgrading, the contract checks if the action is authorized by the Admin address. This is crucial to prevent unauthorized upgrades. Only someone with admin rights can perform the upgrade.
  4. The upgrade function: Below is the function that handles the upgrade process:
    • env: Env: The environment object representing the current state of the blockchain.
    • new_wasm_hash: BytesN<32>: The hash of the new Wasm code for the contract. The Wasm bytecode must already be installed/present on the ledger.
    • The function first retrieves the admin’s address from the contract’s storage.
    • It then requires the admin’s authorization (admin.require_auth()) to proceed.
    • Finally, it updates the contract with the new Wasm bytecode (env.deployer().update_current_contract(ContractExecutable::Wasm(new_wasm_hash))).
  5. update_current_contract will also emit a SYSTEM contract event that contains the old and new ContractExecutable, allowing downstream users to be notified when a contract they use is updated. The event structure will have topics = ["executable_update", old_executable: ContractExecutable, new_executable: ContractExecutable] and data = [].

Tests

Open the upgradeable_contract/old_contract/src/test.rs file to follow along.
upgradeable_contract/old_contract/src/test.rs
Source: https://github.com/stellar/soroban-examples/blob/main/upgradeable_contract/old_contract/src/test.rs We first import the compiled Wasm file for the new contract:
We register the old contract, initialize it with an admin, and verify the version it returns. The note in the code below is important:
We install the new contract’s Wasm:
Then we run the upgrade, and verify that the upgrade worked:

Build the contract

To build the contract .wasm files, run stellar contract build in both upgradeable_contract/old_contract and upgradeable_contract/new_contract in that order. Both .wasm files should be found in both contract target directories after building both contracts:

Run the contract

If you have stellar-cli installed, you can invoke contract functions. Deploy the old contract and install the Wasm for the new contract. First, navigate to the upgradeable_contract/old_contract directory, and deploy an instance of the old contract. We’re providing the alice identity to the __constructor function, so it will be the contract’s Admin address. Create and provide your own identities, where necessary. This command will output the contract address it was deployed to.
Then, navigate to upgradeable_contract/new_contract and upload the compiled executable file for the new contract. This command will output the Sha256 hash of the executable, which will be used later for the new_wasm_hash parameter.
Our deployed old_contract address is CAS6FKBXGVXFGU2SPPPJJOIULJNPMPR6NVKWLOQP24SZJPMB76TGH7Y3. You may need to replace this value with your own. Invoke the version function of the contract, to see the current deployed version.
Now upgrade the contract. Notice the --source-account should be the identity name matching the address passed to the __constructor function, when the contract was deployed.
Invoke the version function again. Now that the contract was upgraded, you’ll see a new version.
Hooray, our contract has been upgraded!