For the complete documentation index, see llms.txt. This page is also available as Markdown.

27. Upgrade a suite

Upgrading a suite replaces the code behind its contracts with a newer version, while every balance, registration, rule and role stays in place. Who triggers it depends on how the suite was deployed. A shared suite follows the network's versions: Network governance publishes a version and activates it for every shared suite at once. An isolated suite is upgraded by its Issuer, on its own beacons, when the Issuer decides.

At a glance

Triggered by

Network governance for a shared suite; Issuer for an isolated suite

Actors

Network governance, Issuer, Platform

Contracts

TREXImplementationAuthority, the beacons of the suite, TREXAccessManager, Token, TREXRegistry, IdentityRegistryStorage, ModularCompliance

Preconditions

The token suite is deployed (see 01); the new implementations are deployed and reviewed; for a shared suite, the caller holds the platform VERSION_MANAGER role; for an isolated suite, the caller holds ADMIN_ROLE on the suite's TREXAccessManager and knows the suite's beacons

Result

The contracts of the suite run the implementations of the new version, with their state unchanged

Actors and roles

Actor
Role in this flow
What they hold

Network governance

Votes each version upgrade, publishes the version, and activates it for shared suites after a delay.

The platform VERSION_MANAGER role on the TREXImplementationAuthority, with an execution delay

Issuer

For a shared suite: follows the upgrade and verifies it. For an isolated suite: performs it.

ADMIN_ROLE on its TREXAccessManager

Platform

Reads the versions, prepares the calls and verifies the result.

The key of the party it acts for

Sequence

Step by step

Step 1. Publish the version

Who: Network governance (VERSION_MANAGER)

Call: TREXImplementationAuthority.publish(version, implementations)

Before the call: The five implementations of the version are deployed: token, registry, identity storage, compliance, access manager. The version is a semantic version that was not published before. To be activated in step 3, it is higher than the active version. When the role carries an execution delay, the call is scheduled first, as in step 3.

What the contract checks:

  1. The caller holds the platform VERSION_MANAGER role. Otherwise the call reverts with AccessManagedUnauthorized.

  2. The version was not published before. Otherwise VersionAlreadyPublished.

  3. None of the five implementation addresses is zero. Otherwise EmptyImplementations.

What changes: The authority archives the implementations under the version and emits VersionPublished(version, implementations). No beacon changes. No suite is affected. The version is now readable with implementationsFor(version), and can be inspected by anyone before it is activated.

Step 2. Review the version

Who: Issuer, with the Platform

Call: Off-chain, using read calls

Before the call: VersionPublished was emitted. The upgrade was voted by Network governance, and its activation is scheduled: it executes after the delay of the VERSION_MANAGER role, which leaves Issuers the time of that delay to review.

What happens: The Platform reads TREXImplementationAuthority.implementationsFor(version) and checks the five addresses against the published source and the audit report of that version. An Issuer that wants to protect its asset during a shared upgrade can pause the token before step 3 and lift the pause after step 5.

What changes: Nothing on-chain.

Step 3. Activate the version, for shared suites

Who: Network governance (VERSION_MANAGER)

Call: schedule(target, data, when) on the access manager of the network, where target is the authority and data the call upgrade(version). Then, once the delay has elapsed, TREXImplementationAuthority.upgrade(version)

Before the call: Step 1 is done. publishAndUpgrade(version, implementations) does steps 1 and 3 in one call.

What the contract checks:

  1. The caller holds the platform VERSION_MANAGER role. Otherwise AccessManagedUnauthorized. The call was scheduled and its delay has elapsed. Otherwise the manager reverts with AccessManagerNotScheduled or AccessManagerNotReady.

  2. The version is greater than the active version. Otherwise VersionNotNewer.

  3. The version was published. Otherwise UnknownVersion.

What changes: The authority points its five beacons at the implementations of the version, in one transaction, and records it as the active version. Every shared suite runs the new code from that transaction. The authority emits SuiteUpgraded(version, implementations). Isolated suites are not affected.

Step 4. Upgrade the beacons, for an isolated suite

Who: Issuer (ADMIN_ROLE on the suite's manager)

Call: TREXAccessManager.execute(beacon, upgradeTo(implementation)), for the beacon of the token, of the registry, of the identity storage and of the compliance. For the beacon of the access manager itself: upgradeTo(implementation), called on that beacon by the admin account named at deployment.

Before the call: The Platform has the suite's beacon addresses, from the IsolatedSuiteDeployed event, and the implementations of the target version, from step 2. The Issuer groups the calls in one transaction, so that the suite never runs contracts of two versions.

What the contract checks:

  1. The caller may call upgradeTo on the beacon through the manager. By default that requires ADMIN_ROLE. Otherwise the call reverts with AccessManagerUnauthorizedCall.

  2. The beacon's owner is the caller: the manager, or the admin account for the manager's own beacon. Otherwise the beacon reverts.

  3. The new implementation is a contract. Otherwise the beacon reverts with BeaconInvalidImplementation.

What changes: Each beacon points at its new implementation and emits Upgraded(implementation). The contracts of the suite run the new code. The network's authority and the shared beacons are not involved.

Step 5. Verify

Who: Platform, on behalf of the Issuer

Call: Off-chain, using read calls

Before the call: Step 3 or step 4 is confirmed.

What happens: The Platform reads Token.version(), and for a shared suite TREXImplementationAuthority.currentVersion(). It reads a sample of state that must be unchanged: Token.totalSupply(), a few balances, TREXRegistry.getClaimTopics(), ModularCompliance.getModules(), the roles of the suite. It runs TREXRegistry.isVerified and ModularCompliance.canTransfer for a known holder.

What changes: Nothing on-chain.

State after the flow

  • Token.version() returns the new version.

  • For a shared suite, TREXImplementationAuthority.currentVersion() returns the new version and implementations() its five implementations.

  • The addresses of the suite have not changed. Only the code behind them has.

  • Balances, frozen amounts, bridged positions, registrations, claim topics, trusted issuers, modules and their settings, positions and roles are unchanged.

  • The token is in the pause state it was in before the upgrade.

Failure modes

Symptom
Cause
Fix

publish or upgrade reverts with AccessManagedUnauthorized

The caller does not hold the platform VERSION_MANAGER role (steps 1 and 3, check 1)

Use the governance account that holds the role

upgrade reverts with AccessManagerNotScheduled or AccessManagerNotReady

The role carries an execution delay, and the call was not scheduled or its delay has not elapsed (step 3, check 1)

Schedule the call on the access manager of the network and wait for the delay

Reverts with VersionAlreadyPublished

The version number was used before (step 1, check 2)

Publish under a new number. A published version cannot be changed

Reverts with EmptyImplementations

One of the five addresses is zero (step 1, check 3)

Give all five, even those that did not change

Reverts with VersionNotNewer

The target is not greater than the active version (step 3, check 2)

Versions only move forward. Publish a higher version

Reverts with UnknownVersion

The version was never published (step 3, check 3)

Publish it first

An isolated upgrade reverts with AccessManagerUnauthorizedCall

The caller does not hold the role that gates upgradeTo on the beacon, ADMIN_ROLE by default (step 4, check 1)

Use the admin account, or the role the Issuer mapped to this operation

An isolated upgrade reverts in the beacon

The call did not come from the beacon's owner, or the implementation has no code (step 4, checks 2 and 3)

Call through the manager, with a deployed implementation

An isolated suite did not move with the network's upgrade

Isolated suites do not follow the shared beacons

Intended. Run step 4

A module behaves as before after its own upgrade

A module is upgraded separately, and the compliance reads its types when it is bound

Call ModularCompliance.resyncModuleTypes(module)

A faulty version is active

The version was activated before a defect was found

Publish and activate a corrected version with a higher number. There is no rollback

Common misunderstandings

Wrong: "An upgrade deploys a new token."

Right: The addresses stay the same. Each contract is a proxy that reads its code from a beacon. An upgrade changes what the beacon points to. The state, held by the proxy, is untouched.

Wrong: "The Issuer upgrades its token."

Right: Only for an isolated suite. A shared suite is upgraded by the network's VERSION_MANAGER, for all shared suites at once. The Issuer chose between the two at deployment.

Wrong: "If the new version is faulty, roll back."

Right: The authority refuses any version that is not greater than the active one. The way back is forward: a corrected version with a higher number. This is why a version is published, and reviewable, before it is activated.

Wrong: "Upgrading the suite upgrades the compliance modules and the identities."

Right: A suite is the token, the registry, the storage, the compliance and the access manager. Compliance modules are upgraded by their own authority. ONCHAINIDs are upgraded through the IdentityFactory. Each has its own path and its own role.

Integration notes

  • Index VersionPublished and SuiteUpgraded on the authority. The first announces a version, the second activates it. The activation is also visible beforehand, as an operation scheduled in the access manager of the network.

  • Do not cache ABIs by address without a version. Read Token.version() and pick the ABI that matches.

  • For an isolated suite, keep the five beacon addresses from IsolatedSuiteDeployed. A proxy does not expose its beacon through a function.

  • For an isolated suite, take the implementations from the authority, with implementationsFor(version), so that the suite runs a published and reviewed set.

  • Group the beacon upgrades of an isolated suite in one transaction with the manager's multicall.

  • An Issuer can map upgradeTo on its beacons to a dedicated role with a delay, so that an upgrade is scheduled and visible before it executes.

  • After an upgrade, run the same reads as before it and compare. An upgrade changes behavior, never state.

Last updated