Post
Share your knowledge.
What's the role of upgrade_cap in Sui packages?
I'm trying to understand this aspect of the Sui Network because I'm either building, debugging, or deploying something that touches this area. I want a detailed explanation of how this mechanism or feature works, along with relevant CLI usage, Move code structure, or architectural concepts. My goal is to gain enough clarity to apply this knowledge in a real project—whether that's a custom smart contract, an NFT system, a wallet integration, or a DeFi tool. The Sui Network has unique features compared to EVM chains, so I'm particularly interested in what sets it apart and how that affects development best practices. It would help to have sample code, command line examples, or typical errors to watch for, especially when using the Sui CLI, SDK, or deploying on localnet/testnet. Ultimately, I want to avoid common mistakes, follow the best security principles, and ensure that the functionality I’m working on behaves as expected under realistic conditions.
- Sui
- Transaction Processing
- Move
Answers
12The upgrade_cap in Sui packages is a special object that controls the authority to upgrade a published Move package. When you publish a package on Sui, the system creates an upgrade_cap object linked to that package. This object is held by the publisher or designated authority and is required to perform any future upgrades or modifications to the package code. Without the upgrade_cap, the package becomes immutable, meaning you cannot change or replace its modules. This design enforces secure, controlled upgrades and prevents unauthorized changes, which is crucial for maintaining trust and stability in deployed smart contracts.
From a CLI perspective, when you publish a package using sui client publish, you receive the package ID and the upgrade_cap object ID. To upgrade, you must sign a transaction referencing the current upgrade_cap, then the system issues a new version of the package along with a new upgrade_cap object. In Move code, you do not directly manipulate the upgrade_cap, but your deployment scripts or off-chain logic must handle it securely.
Architecturally, the upgrade_cap embodies ownership and permission control for package lifecycle management, distinguishing Sui's package model from Ethereum's immutable contracts or proxies. Best practice is to store the upgrade_cap securely offline or in a multisig wallet to prevent compromise. Also, careful version management and testing are needed before upgrading since it affects all users depending on the package.
Typical errors include losing the upgrade_cap, which locks you out of upgrades, or mismanaging the authority leading to security risks. Using the Sui CLI, you can query the current upgrade_cap for a package or submit upgrade transactions that consume and reissue it.
In summary, the upgrade_cap is the cryptographic key to package evolution on Sui, enabling controlled, permissioned upgrades while maintaining decentralization and security. Managing it properly is essential for production-ready Sui smart contract development and deployment workflows.
In Sui, upgrade_cap
is used to manage the upgradeability of smart contracts, allowing developers to update contracts without losing data. It grants permission to perform contract upgrades and is typically owned by a trusted address.
Key Points:
-
Control Upgrade:
upgrade_cap
ensures only authorized addresses can trigger upgrades. -
Move Code: Used in Move modules to handle upgrades.
-
CLI Example:
sui client publish --upgrade --package <package-id> --capability <upgrade-cap-id>
Best Practices:
- Test upgrades on localnet/testnet.
- Secure the
upgrade_cap
to prevent unauthorized access.
Role of upgrade_cap
in Sui Network
Purpose: Controls who can upgrade a smart contract or module.
- What it does: Grants upgrade permissions to specific addresses (usually admins). It ensures only authorized entities can modify deployed contracts.
Key Concepts:
- Move Language:
upgrade_cap
is implemented in Move for secure contract management. - Deployment: Specified during contract deployment to allow future upgrades by authorized addresses only.
CLI Example:
sui client publish --gas-budget 10000 --upgrade-cap <upgrade-cap-id>
Example Move Code:
public fun grant_upgrade_cap() {
let upgrade_cap = UpgradeCap::new();
// Assign cap to authorized address
}
Common Issues:
- Permission Errors: Unauthorized address trying to upgrade.
- Missing Capabilities: Ensure
upgrade_cap
is set correctly during deployment.
Best Practices:
- Restrict
upgrade_cap
: Limit access to trusted addresses. - Test in Localnet/Testnet: Ensure proper behavior before deploying to mainnet.
The upgrade_cap
in Sui serves as an authorization mechanism for package upgrades, functioning as a unique capability object that grants upgrade rights to its holder. Unlike EVM chains where contracts are immutable, Sui's upgrade system requires this object to modify published packages, providing controlled mutability while maintaining security. The holder of the upgrade_cap
can authorize upgrades via the sui client upgrade
CLI command or programmatically through Move's package::authorize_upgrade
function. Best practices involve securely storing the upgrade_cap
(often in a Versioned
or AdminCap
wrapper) and implementing proper ownership controls, as losing it permanently locks upgradeability while exposing it risks unauthorized modifications.
In the Sui network, the upgrade_cap
plays a critical role in enabling secure and controlled upgrades to Move packages after their initial deployment. When you publish a package on Sui, an object of type UpgradeCap
is created and returned. This object is the sole authority that allows future upgrades to the package, effectively acting as a key that grants permission to change its code.
You must retain ownership of this upgrade_cap
if you intend to upgrade your package later. If you transfer it to another address or burn it (e.g., by sending it to 0x0
), then no further upgrades are possible. This design enforces immutability unless explicitly opted into by the package creator.
Why UpgradeCap Matters
Sui’s object-centric model introduces this concept to distinguish between immutable packages and upgradeable ones. Packages without access to their upgrade_cap
are considered permanently immutable. This contrasts with EVM chains where contracts are mutable only if delegatecall
proxies or upgradable patterns are built manually.
Upgrade Workflow Example
When you publish a package:
sui client publish --path . --gas-budget 100000000
The output includes:
- Package ID
- UpgradeCap object ID
To upgrade later:
sui client upgrade \
--package <original_package_id> \
--module-upgrade-path <new_module_path> \
--upgrade-cap <upgrade_cap_id> \
--gas-budget 100000000
Using UpgradeCap in Move
In your Move modules, you might protect upgrades or sensitive transitions using the capability object:
public entry fun secure_upgrade(upgrade_cap: &UpgradeCap, ctx: &mut TxContext) {
// logic that uses upgrade_cap as proof of authority
}
Or destroy it if you want to finalize the package:
transfer::public_transfer(upgrade_cap, @0x0);
This permanently revokes your ability to update the package.
Best Practices
- Store the
upgrade_cap
in a secure wallet or governance module if you plan future upgrades. - Burn the
upgrade_cap
if you want your package to be immutable, which is common for financial contracts or NFTs to build user trust. - Wrap upgrade logic in governance, so that a DAO or multisig approves upgrades instead of a single private key.
- Avoid leaking the
upgrade_cap
ID, as possession equals authority in Sui.
Common Errors
- Missing
upgrade_cap
during upgrade → Results in “permission denied” errors. - Accidental transfer to null → Your package becomes locked forever.
- Trying to call upgrade on immutable packages → The system will reject the transaction.
Learn More
- Docs: https://docs.sui.io/concepts/packages
- Upgrade examples: https://docs.sui.io/build/upgrade
- CLI reference: https://docs.sui.io/reference/cli/client
Ultimately, the upgrade_cap
represents Sui's commitment to explicit authority and fine-grained control over state and code evolution. Understanding how to use, store, and revoke it is key to building secure and maintainable smart contracts in the Sui ecosystem.
Role of upgrade_cap
in Sui Packages
The upgrade_cap
is a capability object that controls package upgrade permissions in Sui's object-centric upgrade system. Unlike EVM chains that use proxy patterns, Sui handles upgrades at the package level through this dedicated capability object.
Core Mechanics
When publishing a package:
sui client publish --gas-budget 100000000 --path ./move_package
The transaction returns:
- Package ID (immutable identifier)
upgrade_cap
object (0x2::package::UpgradeCap)
This capability object:
- Is a first-class Sui object that can be owned, transferred, or shared
- Must be included as an input in any upgrade transaction
- Contains the package ID it authorizes upgrades for
Key Technical Details
-
Security Model:
- Only the owner of the
upgrade_cap
can upgrade the package - Follows Sui's object ownership model (no signer-based authorization)
- Can be managed in multisig contracts or timelocks
- Only the owner of the
-
Upgrade Process:
sui client upgrade --package [PACKAGE_ID] \ --upgrade-cap [UPGRADE_CAP_ID] \ --gas-budget 200000000 \ --path ./updated_package
-
Critical Move Patterns:
// Storing upgrade_cap in a publisher struct struct Publisher has key { id: UID, upgrade_cap: package::UpgradeCap, } // Upgrading (must use &mut reference) public entry fun upgrade( publisher: &mut Publisher, new_package: vector<u8>, new_modules: vector<vector<u8>>, ctx: &mut TxContext ) { package::upgrade_package( &mut publisher.upgrade_cap, new_package, new_modules, ctx ) }
Sui-Specific Advantages vs EVM
- No proxy patterns needed: Upgrades happen at the package level, not contract level
- Backward compatibility: Existing objects remain valid after upgrades
- Capability-based security: Uses Sui's object ownership model instead of admin roles
- Transparent governance: The
upgrade_cap
can be put in multisigs or DAO treasuries
Common Errors & Solutions
Error | Cause | Fix |
---|---|---|
UpgradeCap not found | Missing capability object | Verify you're using the correct upgrade_cap ID |
Package ID mismatch | Using wrong upgrade_cap | Check package::id(&cap) matches target package |
Insufficient gas | Upgrade requires more gas | Increase gas budget (upgrades cost 2-3x regular tx) |
Module compatibility error | Breaking changes in new version | Ensure struct layouts remain compatible |
Best Practices
- For production: Transfer
upgrade_cap
to a multisig wallet - For immutable contracts: Burn the capability after publishing
- Always validate capability ownership:
object::is_owner(&cap, tx_context::sender(ctx))
- Emit events on upgrades:
event::emit(UpgradeEvent { package_id, version })
The upgrade_cap
embodies Sui's object-oriented security model - upgrade permissions are treated as transferable assets rather than hardcoded admin roles, providing flexible governance options while maintaining security.
The upgrade_cap in Sui is a special object that represents the authority to upgrade a published Move package. When you publish a package using the Sui CLI, the system automatically creates an upgrade_cap object associated with that package. This object controls the ability to upgrade or modify the package after its initial deployment. Without holding the upgrade_cap, no one can upgrade the package, making it immutable.
For example, when publishing a package:
sui client publish --gas-budget 10000 --path ./my_package
The output includes the upgrade_cap object ID. This object must be included in upgrade transactions to prove authority. To upgrade the package, you call:
sui client upgrade --package <new_package_path> --upgrade-cap <upgrade_cap_object_id> --gas-budget 10000
This transaction consumes the old upgrade_cap and issues a new one for the upgraded package. The upgrade_cap enforces strict permission control on the package lifecycle, ensuring only authorized parties can modify the contract code.
From a Move perspective, the package code itself doesn't directly manipulate the upgrade_cap because it is handled at the protocol level. However, developers need to manage this object securely off-chain, typically storing it in secure wallets or multisig setups to prevent unauthorized upgrades.
A lost upgrade_cap means you cannot upgrade the package again, effectively locking the code forever. This emphasizes the importance of secure key management practices. Moreover, upgrading a package impacts all users relying on it, so upgrades should be thoroughly tested on testnet or localnet before deployment.
Because Sui packages are versioned, the upgrade_cap is the key to moving between versions. The system guarantees atomic upgrades by tying the package versioning to this capability.
You can query the upgrade_cap associated with a package using the Sui CLI or SDK:
sui client get-package <package_id>
which returns package details including the current upgrade_cap. Handling the upgrade_cap securely is part of best practices in Sui development workflows to avoid accidental or malicious code changes.
In contrast to Ethereum’s immutable contracts or proxy upgrade patterns, Sui's upgrade_cap is an explicit on-chain capability object controlling upgrades. This design enhances transparency and security by making upgrade authority an explicit object in the system.
Developers must incorporate the upgrade_cap management into their deployment pipelines, typically scripting the upgrade process to include the upgrade_cap object and ensuring it is signed by the correct keys.
The upgrade_cap
in Sui packages is a special object that gives its owner the exclusive right to upgrade a published Move package. When you deploy a package on the Sui network, Sui generates an UpgradeCap
object tied to your account. This capability acts as a secure access key—without it, no one (including you) can upgrade that package’s code in the future.
You use the upgrade_cap
when calling the upgrade
function provided by Sui’s system module. The CLI allows you to perform upgrades like this:
sui client upgrade \
--package-path /path/to/your/package \
--upgrade-capability <upgrade_cap_object_id> \
--gas-budget 100000000
You must pass the ID of the UpgradeCap
object to authorize the upgrade transaction. If you lose the UpgradeCap
or transfer it to an inaccessible address (such as 0x0
), the package becomes immutable because no one can initiate an upgrade.
If you want to deliberately disable future upgrades, you can include a function in your package like:
public entry fun burn_cap(cap: UpgradeCap) {
sui::package::delete_upgrade_cap(cap);
}
Calling this function deletes the capability and locks the code forever. This is recommended for packages that must be trustless or governed externally.
Best practice: Always secure your UpgradeCap
and consider transferring it to a DAO or multisig if you're working on collaborative or public infrastructure.
Role of upgrade_cap
in Sui Packages
The upgrade_cap
is a capability object that grants permission to upgrade a Move package on Sui. It’s a unique Sui feature that enables controlled mutability for published packages, unlike immutable contracts on EVM chains.
Key Concepts
-
What It Does:
- Holds the
Publisher
andUpgradePolicy
(e.g.,immutable
,compatible
,arbitrary
). - Required to authorize package upgrades (e.g., bug fixes, new features).
- Holds the
-
Why Sui Is Unique:
- EVM contracts are immutable after deployment; Sui allows upgrades with governance (via
upgrade_cap
). - Upgrades are gas-efficient (only modified modules are re-published).
- EVM contracts are immutable after deployment; Sui allows upgrades with governance (via
-
Security Implications:
- Whoever holds the
upgrade_cap
can change the package’s logic. - Best practice: Transfer it to a multisig or DAO after deployment.
- Whoever holds the
Move Code Structure
1. Defining upgrade_cap
in init
module my_pkg::my_module {
use sui::package;
use sui::transfer;
// Called once during package publish
fun init(otw: &mut TxContext) {
let (upgrade_cap, publisher) = package::claim(otw);
transfer::transfer(upgrade_cap, tx_context::sender(otw)); // Give cap to deployer
}
}
2. Upgrading a Package
module my_pkg::upgrader {
use sui::package;
// Requires the UpgradeCap
public entry fun upgrade(
upgrade_cap: &mut UpgradeCap,
new_package: vector<u8>,
otw: &mut TxContext
) {
package::upgrade(upgrade_cap, new_package, otw);
}
}
upgrade_cap
in Sui is a key object that controls package upgrades.
Key Points:
- Admin Rights – Only the
upgrade_cap
holder can upgrade the package. - Security – Prevents unauthorized changes (unlike Ethereum’s immutable contracts).
- CLI Usage – Required for
sui client upgrade
.
Move Example:
struct UpgradeCap has key, store { id: UID }
Best Practices:
✔ Store securely (e.g., multisig).
✔ Test upgrades on localnet
first.
Why Unique:
Sui allows upgrades (vs EVM immutability).
(Losing upgrade_cap
= no more upgrades!)
1. Core Concept
The upgrade_cap
is a privileged object that controls package upgrades in Sui. Unlike EVM chains where contracts are immutable, Sui allows controlled mutability through this capability-based system.
2. Key Properties
Property | Description |
---|---|
Owner | Only the holder can authorize upgrades |
Transferable | Can be sent to other addresses |
Burnable | Permanent immutability option |
2. Move Implementation
Basic Structure
module my_pkg::admin {
use sui::package;
use sui::transfer;
use sui::tx_context;
// Generated during initial publish
struct UpgradeCap has key, store {
id: UID
}
// Initialize and transfer cap
public fun init(ctx: &mut tx_context::TxContext) {
let (upgrade_cap, publisher) = package::claim_upgrade_cap(ctx);
transfer::transfer(upgrade_cap, tx_context::sender(ctx));
}
}
Upgrade Flow
module my_pkg::upgrader {
use sui::package;
use sui::upgrade_cap;
public entry fun upgrade(
cap: &mut upgrade_cap::UpgradeCap,
policy: u8,
digest: vector<u8>,
ctx: &mut tx_context::TxContext
) {
package::authorize_upgrade(cap, policy, digest);
let new_pkg = package::make_upgrade_ticket(cap, policy, digest);
// ... complete upgrade
}
}
3. CLI Workflow
Initial Publish
sui client publish --gas-budget 1000000000
# Output includes UpgradeCap object ID
Authorize Upgrade
sui client call \
--package <UPGRADE_CAP_PKG> \
--module admin \
--function authorize_upgrade \
--args <UPGRADE_CAP_ID> 1 0x<COMPILED_PACKAGE_DIGEST> \
--gas-budget 1000000000
Execute Upgrade
sui client upgrade --upgrade-capability <UPGRADE_CAP_ID> \
--gas-budget 1000000000
4. Security Patterns
Capability Nesting
struct AdminCap has key {
id: UID,
upgrade_cap: UpgradeCap // Delegatable
}
Time-Locked Upgrades
module my_pkg::timelock {
struct TimedUpgradeCap has key {
cap: UpgradeCap,
unlock_epoch: u64
}
public fun upgrade_when_ready(
cap: &mut TimedUpgradeCap,
ctx: &mut TxContext
) {
assert!(tx_context::epoch(ctx) >= cap.unlock_epoch, ELOCKED);
package::authorize_upgrade(&mut cap.cap, ...);
}
}
5. Common Pitfalls
Error | Solution |
---|---|
MissingUpgradeCap | Store cap ID in your deployment docs |
UnauthorizedUpgrade | Use transfer::freeze_object to lock caps |
DigestMismatch | Recompile with identical dependencies |
6. Upgrade Policies
// Bitflags determining upgrade flexibility
const POLICY_COMPATIBLE: u8 = 0x1; // Backwards-compatible
const POLICY_ADDITIVE: u8 = 0x2; // New functions only
const POLICY_BREAKING: u8 = 0x4; // Full changes
7. Testing Strategy
Localnet Dry-Run
sui client publish --upgrade-policy 7 --dry-run
Upgrade Simulation
#[test_only]
module test {
fun test_upgrade() {
let (cap, _) = package::test_upgrade_cap();
package::authorize_upgrade(&mut cap, ...);
assert!(package::test_is_authorized(cap), 0);
}
}
Architectural Impact
-
Decentralized Governance:
- DAOs can hold upgrade caps
- Multi-sig schemes via
shared
objects
-
Enterprise Readiness:
- Staged rollouts with policy flags
- Emergency revocation capabilities
-
DevEx Advantages:
- Fix bugs post-deployment
- Gas-efficient storage migrations
For production systems:
- Store
UpgradeCap
in cold storage - Implement upgrade voting mechanisms
- Monitor via Sui Explorer's Upgrade Tab
In Sui, the upgrade_cap
is a special object that gives you the right to upgrade a published Move package. When you deploy a package with --upgradeable
, you automatically get an UpgradeCap
object linked to it. This object acts like a permission slip—without it, you can't push upgrades to that package. You need to hold and sign with this object anytime you want to upgrade the code, which makes it a powerful access control mechanism for on-chain development.
This design makes Sui different from EVM chains, where upgrade logic is often handled through proxy contracts and separate admin roles. In Sui, the upgrade permission is baked into the object system. If you're building an NFT collection, DeFi contract, or any system that needs future updates, the UpgradeCap
ensures that only the trusted developer (or multisig) with that cap can push those changes, reducing the chance of unauthorized upgrades.
To create an upgradeable package, use the Sui CLI:
sui client publish --path . --gas-budget 100000000 --with-unpublished-dependencies --upgradeable
Once published, you’ll see an upgrade_cap
in the output like this:
"createdObjects": [
{
"objectType": "0x2::package::UpgradeCap",
"objectId": "0xabc123...",
...
}
]
To upgrade the package later, compile it to a .json
digest and run:
sui client upgrade --package-id <PACKAGE_ID> --module <PATH_TO_COMPILED_MODULE> --upgrade-capability <CAP_OBJECT_ID> --gas-budget 100000000
Make sure the object ID in --upgrade-capability
matches the one you got from the first publish. If you lose this object or forget to protect it (like by transferring to a multisig wallet), anyone who gets it can modify your contract.
Common mistakes include forgetting to store the upgrade_cap
, using an invalid digest, or trying to upgrade a non-upgradeable package. Best practice is to vault the upgrade_cap
securely, especially in production, and monitor it closely during audits.
You can read more about upgrade_cap
and secure package upgrades in Sui’s official docs:
https://docs.sui.io/build/package-upgrades
Do you know the answer?
Please log in and share it.
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.
- Why does BCS require exact field order for deserialization when Move structs have named fields?53
- Multiple Source Verification Errors" in Sui Move Module Publications - Automated Error Resolution43
- Sui Transaction Failing: Objects Reserved for Another Transaction25
- How do ability constraints interact with dynamic fields in heterogeneous collections?05